jkf 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +57 -0
- data/.rubocop_todo.yml +1 -454
- data/CHANGELOG.md +17 -0
- data/Gemfile +11 -11
- data/Guardfile +3 -3
- data/README.en.md +3 -4
- data/README.md +4 -2
- data/Rakefile +4 -4
- data/bench.rb +12 -0
- data/bin/console +4 -4
- data/jkf.gemspec +12 -13
- data/lib/jkf/converter/base.rb +12 -10
- data/lib/jkf/converter/csa.rb +141 -150
- data/lib/jkf/converter/ki2.rb +93 -91
- data/lib/jkf/converter/kif.rb +105 -99
- data/lib/jkf/converter/kifuable.rb +160 -160
- data/lib/jkf/converter.rb +6 -8
- data/lib/jkf/parser/base.rb +81 -95
- data/lib/jkf/parser/csa.rb +651 -654
- data/lib/jkf/parser/ki2.rb +331 -333
- data/lib/jkf/parser/kif.rb +467 -468
- data/lib/jkf/parser/kifuable.rb +511 -517
- data/lib/jkf/parser.rb +5 -7
- data/lib/jkf/version.rb +1 -2
- data/lib/jkf.rb +6 -6
- data/manifest.scm +1 -0
- data/po/all.pot +31 -25
- data/po/en.po +36 -37
- metadata +4 -3
data/lib/jkf/parser/kif.rb
CHANGED
@@ -1,620 +1,619 @@
|
|
1
|
-
|
1
|
+
module Jkf
|
2
|
+
module Parser
|
3
|
+
# KIF Parser
|
4
|
+
class Kif < Base
|
5
|
+
include Kifuable
|
2
6
|
|
3
|
-
|
4
|
-
# KIF Parser
|
5
|
-
class Kif < Base
|
6
|
-
include Kifuable
|
7
|
+
protected
|
7
8
|
|
8
|
-
|
9
|
+
# kifu : skipline* header* initialboard? header* split? moves fork* nl?
|
10
|
+
def parse_root
|
11
|
+
@scanner << "\n" unless @scanner.string.end_with?("\n")
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
@input += "\n" unless @input.end_with?("\n")
|
13
|
-
|
14
|
-
s0 = @current_pos
|
15
|
-
s1 = []
|
16
|
-
s2 = parse_skipline
|
17
|
-
while s2 != :failed
|
18
|
-
s1 << s2
|
13
|
+
s0 = @scanner.pos
|
14
|
+
s1 = []
|
19
15
|
s2 = parse_skipline
|
20
|
-
|
16
|
+
while s2 != :failed
|
17
|
+
s1 << s2
|
18
|
+
s2 = parse_skipline
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
s3 = parse_header
|
24
|
-
while s3 != :failed
|
25
|
-
s2 << s3
|
21
|
+
s2 = []
|
26
22
|
s3 = parse_header
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
s4 = []
|
31
|
-
s5 = parse_header
|
32
|
-
while s5 != :failed
|
33
|
-
s4 << s5
|
34
|
-
s5 = parse_header
|
35
|
-
end
|
36
|
-
parse_split
|
37
|
-
s6 = parse_moves
|
38
|
-
if s6 == :failed
|
39
|
-
@current_pos = s0
|
40
|
-
s0 = :failed
|
41
|
-
else
|
42
|
-
s7 = []
|
43
|
-
s8 = parse_fork
|
44
|
-
while s8 != :failed
|
45
|
-
s7 << s8
|
46
|
-
s8 = parse_fork
|
23
|
+
while s3 != :failed
|
24
|
+
s2 << s3
|
25
|
+
s3 = parse_header
|
47
26
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# header : [^:\r\n]+ ":" nonls nl
|
57
|
-
# | turn "手番" nl
|
58
|
-
# | "盤面回転" nl
|
59
|
-
def parse_header
|
60
|
-
s0 = @current_pos
|
61
|
-
s2 = match_regexp(/^[^:\r\n]/)
|
62
|
-
if s2 == :failed
|
63
|
-
s1 = :failed
|
64
|
-
else
|
65
|
-
s1 = []
|
66
|
-
while s2 != :failed
|
67
|
-
s1 << s2
|
68
|
-
s2 = match_regexp(/^[^:\r\n]/)
|
27
|
+
s3 = parse_initialboard
|
28
|
+
s3 = nil if s3 == :failed
|
29
|
+
s4 = []
|
30
|
+
s5 = parse_header
|
31
|
+
while s5 != :failed
|
32
|
+
s4 << s5
|
33
|
+
s5 = parse_header
|
69
34
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
elsif match_str(":") != :failed
|
75
|
-
s3 = parse_nonls
|
76
|
-
if parse_nl == :failed
|
77
|
-
@current_pos = s0
|
35
|
+
parse_split
|
36
|
+
s6 = parse_moves
|
37
|
+
if s6 == :failed
|
38
|
+
@scanner.pos = s0
|
78
39
|
s0 = :failed
|
79
40
|
else
|
41
|
+
s7 = []
|
42
|
+
s8 = parse_fork
|
43
|
+
while s8 != :failed
|
44
|
+
s7 << s8
|
45
|
+
s8 = parse_fork
|
46
|
+
end
|
47
|
+
parse_nl
|
80
48
|
@reported_pos = s0
|
81
|
-
|
82
|
-
s0 = s1
|
49
|
+
s0 = transform_root(s2, s3, s4, s6, s7)
|
83
50
|
end
|
84
|
-
|
85
|
-
|
86
|
-
s0 = :failed
|
51
|
+
|
52
|
+
s0
|
87
53
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
54
|
+
|
55
|
+
# header : [^:\r\n]+ ":" nonls nl
|
56
|
+
# | turn "手番" nl
|
57
|
+
# | "盤面回転" nl
|
58
|
+
def parse_header
|
59
|
+
s0 = @scanner.pos
|
60
|
+
s2 = match_regexp(/[^:\r\n]/)
|
61
|
+
if s2 == :failed
|
62
|
+
s1 = :failed
|
63
|
+
else
|
64
|
+
s1 = []
|
65
|
+
while s2 != :failed
|
66
|
+
s1 << s2
|
67
|
+
s2 = match_regexp(/[^:\r\n]/)
|
68
|
+
end
|
69
|
+
end
|
91
70
|
if s1 == :failed
|
92
|
-
@
|
71
|
+
@scanner.pos = s0
|
93
72
|
s0 = :failed
|
94
|
-
elsif match_str(
|
73
|
+
elsif match_str(':') != :failed
|
74
|
+
s3 = parse_nonls
|
95
75
|
if parse_nl == :failed
|
96
|
-
@
|
76
|
+
@scanner.pos = s0
|
97
77
|
s0 = :failed
|
98
78
|
else
|
99
79
|
@reported_pos = s0
|
100
|
-
|
80
|
+
s1 = { 'k' => s1.join, 'v' => s3.join }
|
81
|
+
s0 = s1
|
101
82
|
end
|
102
83
|
else
|
103
|
-
@
|
84
|
+
@scanner.pos = s0
|
104
85
|
s0 = :failed
|
105
86
|
end
|
106
87
|
if s0 == :failed
|
107
|
-
s0 = @
|
108
|
-
|
109
|
-
|
88
|
+
s0 = @scanner.pos
|
89
|
+
s1 = parse_turn
|
90
|
+
if s1 == :failed
|
91
|
+
@scanner.pos = s0
|
110
92
|
s0 = :failed
|
111
|
-
elsif
|
112
|
-
|
113
|
-
|
93
|
+
elsif match_str('手番') != :failed
|
94
|
+
if parse_nl == :failed
|
95
|
+
@scanner.pos = s0
|
96
|
+
s0 = :failed
|
97
|
+
else
|
98
|
+
@reported_pos = s0
|
99
|
+
s0 = { 'k' => '手番', 'v' => s1 }
|
100
|
+
end
|
114
101
|
else
|
115
|
-
@
|
102
|
+
@scanner.pos = s0
|
116
103
|
s0 = :failed
|
117
104
|
end
|
105
|
+
if s0 == :failed
|
106
|
+
s0 = @scanner.pos
|
107
|
+
if match_str('盤面回転') == :failed
|
108
|
+
@scanner.pos = s0
|
109
|
+
s0 = :failed
|
110
|
+
elsif parse_nl != :failed
|
111
|
+
@reported_pos = s0
|
112
|
+
s0 = nil
|
113
|
+
else
|
114
|
+
@scanner.pos = s0
|
115
|
+
s0 = :failed
|
116
|
+
end
|
117
|
+
end
|
118
118
|
end
|
119
|
-
end
|
120
119
|
|
121
|
-
|
122
|
-
|
120
|
+
s0
|
121
|
+
end
|
123
122
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
# turn : [先後上下]
|
124
|
+
def parse_turn
|
125
|
+
match_regexp(/[先後上下]/)
|
126
|
+
end
|
128
127
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
s0 = :failed
|
136
|
-
else
|
137
|
-
s2 = match_str("-------消費時間--")
|
138
|
-
s2 = nil if s2 == :failed
|
139
|
-
s3 = parse_nl
|
140
|
-
if s3 == :failed
|
141
|
-
@current_pos = s0
|
128
|
+
# split : "手数----指手--" "-------消費時間--"? nl
|
129
|
+
def parse_split
|
130
|
+
s0 = @scanner.pos
|
131
|
+
s1 = match_str('手数----指手--')
|
132
|
+
if s1 == :failed
|
133
|
+
@scanner.pos = s0
|
142
134
|
s0 = :failed
|
143
135
|
else
|
144
|
-
|
136
|
+
s2 = match_str('-------消費時間--')
|
137
|
+
s2 = nil if s2 == :failed
|
138
|
+
s3 = parse_nl
|
139
|
+
if s3 == :failed
|
140
|
+
@scanner.pos = s0
|
141
|
+
s0 = :failed
|
142
|
+
else
|
143
|
+
s0 = [s1, s2, s3]
|
144
|
+
end
|
145
145
|
end
|
146
|
+
s0
|
146
147
|
end
|
147
|
-
s0
|
148
|
-
end
|
149
148
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
s3 = parse_move
|
161
|
-
while s3 != :failed
|
162
|
-
s2 << s3
|
149
|
+
# moves : firstboard split? move* result?
|
150
|
+
def parse_moves
|
151
|
+
s0 = @scanner.pos
|
152
|
+
s1 = parse_firstboard
|
153
|
+
if s1 == :failed
|
154
|
+
@scanner.pos = s0
|
155
|
+
s0 = :failed
|
156
|
+
else
|
157
|
+
parse_split
|
158
|
+
s2 = []
|
163
159
|
s3 = parse_move
|
160
|
+
while s3 != :failed
|
161
|
+
s2 << s3
|
162
|
+
s3 = parse_move
|
163
|
+
end
|
164
|
+
parse_result
|
165
|
+
@reported_pos = s0
|
166
|
+
s0 = s2.unshift(s1)
|
164
167
|
end
|
165
|
-
|
166
|
-
@reported_pos = s0
|
167
|
-
s0 = s2.unshift(s1)
|
168
|
+
s0
|
168
169
|
end
|
169
|
-
s0
|
170
|
-
end
|
171
170
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
s2 = parse_comment
|
177
|
-
while s2 != :failed
|
178
|
-
s1 << s2
|
171
|
+
# firstboard : comment* pointer?
|
172
|
+
def parse_firstboard
|
173
|
+
s0 = @scanner.pos
|
174
|
+
s1 = []
|
179
175
|
s2 = parse_comment
|
176
|
+
while s2 != :failed
|
177
|
+
s1 << s2
|
178
|
+
s2 = parse_comment
|
179
|
+
end
|
180
|
+
parse_pointer
|
181
|
+
@reported_pos = s0
|
182
|
+
s1.empty? ? {} : { 'comments' => s1 }
|
180
183
|
end
|
181
|
-
parse_pointer
|
182
|
-
@reported_pos = s0
|
183
|
-
s0 = s1.empty? ? {} : { "comments" => s1 }
|
184
|
-
s0
|
185
|
-
end
|
186
184
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
s3 = parse_comment
|
197
|
-
while s3 != :failed
|
198
|
-
s2 << s3
|
185
|
+
# move : line comment* pointer?
|
186
|
+
def parse_move
|
187
|
+
s0 = @scanner.pos
|
188
|
+
s1 = parse_line
|
189
|
+
if s1 == :failed
|
190
|
+
@scanner.pos = s0
|
191
|
+
s0 = :failed
|
192
|
+
else
|
193
|
+
s2 = []
|
199
194
|
s3 = parse_comment
|
195
|
+
while s3 != :failed
|
196
|
+
s2 << s3
|
197
|
+
s3 = parse_comment
|
198
|
+
end
|
199
|
+
parse_pointer
|
200
|
+
@reported_pos = s0
|
201
|
+
s0 = transform_move(s1, s2)
|
200
202
|
end
|
201
|
-
|
202
|
-
@reported_pos = s0
|
203
|
-
s0 = transform_move(s1, s2)
|
203
|
+
s0
|
204
204
|
end
|
205
|
-
s0
|
206
|
-
end
|
207
205
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
match_spaces
|
212
|
-
s2 = parse_te
|
213
|
-
if s2 == :failed
|
214
|
-
@current_pos = s0
|
215
|
-
s0 = :failed
|
216
|
-
else
|
206
|
+
# line : " "* te " "* (fugou from | [^\r\n ]*) " "* time? "+"? nl
|
207
|
+
def parse_line
|
208
|
+
s0 = @scanner.pos
|
217
209
|
match_spaces
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
s4 = :failed
|
210
|
+
s2 = parse_te
|
211
|
+
if s2 == :failed
|
212
|
+
@scanner.pos = s0
|
213
|
+
s0 = :failed
|
223
214
|
else
|
224
|
-
|
225
|
-
|
226
|
-
|
215
|
+
match_spaces
|
216
|
+
s4 = @scanner.pos
|
217
|
+
s5 = parse_fugou
|
218
|
+
if s5 == :failed
|
219
|
+
@scanner.pos = s4
|
227
220
|
s4 = :failed
|
228
221
|
else
|
229
|
-
|
230
|
-
|
222
|
+
s6 = parse_from
|
223
|
+
if s6 == :failed
|
224
|
+
@scanner.pos = s4
|
225
|
+
s4 = :failed
|
226
|
+
else
|
227
|
+
@reported_pos = s4
|
228
|
+
s4 = transform_teban_fugou_from(s2, s5, s6)
|
229
|
+
end
|
231
230
|
end
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
231
|
+
if s4 == :failed
|
232
|
+
s4 = @scanner.pos
|
233
|
+
s5 = []
|
234
|
+
s6 = match_regexp(/[^\r\n ]/)
|
235
|
+
while s6 != :failed
|
236
|
+
s5 << s6
|
237
|
+
s6 = match_regexp(/[^\r\n ]/)
|
238
|
+
end
|
239
|
+
@reported_pos = s4
|
240
|
+
s4 = s5.join
|
240
241
|
end
|
241
|
-
|
242
|
-
|
243
|
-
end
|
244
|
-
if s4 == :failed
|
245
|
-
@current_pos = s0
|
246
|
-
s0 = :failed
|
247
|
-
else
|
248
|
-
match_spaces
|
249
|
-
s6 = parse_time
|
250
|
-
s6 = nil if s6 == :failed
|
251
|
-
match_str("+")
|
252
|
-
if parse_nl == :failed
|
253
|
-
@current_pos = s0
|
242
|
+
if s4 == :failed
|
243
|
+
@scanner.pos = s0
|
254
244
|
s0 = :failed
|
255
245
|
else
|
256
|
-
|
257
|
-
|
246
|
+
match_spaces
|
247
|
+
s6 = parse_time
|
248
|
+
s6 = nil if s6 == :failed
|
249
|
+
match_str('+')
|
250
|
+
if parse_nl == :failed
|
251
|
+
@scanner.pos = s0
|
252
|
+
s0 = :failed
|
253
|
+
else
|
254
|
+
@reported_pos = s0
|
255
|
+
s0 = { 'move' => s4, 'time' => s6 }
|
256
|
+
end
|
258
257
|
end
|
259
258
|
end
|
259
|
+
s0
|
260
260
|
end
|
261
|
-
s0
|
262
|
-
end
|
263
261
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
262
|
+
# te : [0-9]+
|
263
|
+
def parse_te
|
264
|
+
match_digits!
|
265
|
+
end
|
268
266
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
s0 = :failed
|
276
|
-
else
|
277
|
-
s2 = parse_piece
|
278
|
-
if s2 == :failed
|
279
|
-
@current_pos = s0
|
267
|
+
# fugou : place piece "成"?
|
268
|
+
def parse_fugou
|
269
|
+
s0 = @scanner.pos
|
270
|
+
s1 = parse_place
|
271
|
+
if s1 == :failed
|
272
|
+
@scanner.pos = s0
|
280
273
|
s0 = :failed
|
281
274
|
else
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
275
|
+
s2 = parse_piece
|
276
|
+
if s2 == :failed
|
277
|
+
@scanner.pos = s0
|
278
|
+
s0 = :failed
|
279
|
+
else
|
280
|
+
s3 = match_str('成')
|
281
|
+
s3 = nil if s3 == :failed
|
282
|
+
@reported_pos = s0
|
283
|
+
s0 = { 'to' => s1, 'piece' => s2, 'promote' => !!s3 }
|
284
|
+
end
|
286
285
|
end
|
286
|
+
s0
|
287
287
|
end
|
288
|
-
s0
|
289
|
-
end
|
290
288
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
s0 = :failed
|
298
|
-
else
|
299
|
-
s2 = parse_numkan
|
300
|
-
if s2 == :failed
|
301
|
-
@current_pos = s0
|
289
|
+
# place : num numkan | "同 "
|
290
|
+
def parse_place
|
291
|
+
s0 = @scanner.pos
|
292
|
+
s1 = parse_num
|
293
|
+
if s1 == :failed
|
294
|
+
@scanner.pos = s0
|
302
295
|
s0 = :failed
|
303
296
|
else
|
304
|
-
|
305
|
-
|
297
|
+
s2 = parse_numkan
|
298
|
+
if s2 == :failed
|
299
|
+
@scanner.pos = s0
|
300
|
+
s0 = :failed
|
301
|
+
else
|
302
|
+
@reported_pos = s0
|
303
|
+
s0 = { 'x' => s1, 'y' => s2 }
|
304
|
+
end
|
306
305
|
end
|
306
|
+
if s0 == :failed
|
307
|
+
s0 = @scanner.pos
|
308
|
+
s1 = match_str('同 ')
|
309
|
+
if s1 != :failed
|
310
|
+
@reported_pos = s0
|
311
|
+
s1 = nil
|
312
|
+
end
|
313
|
+
s0 = s1
|
314
|
+
end
|
315
|
+
s0
|
307
316
|
end
|
308
|
-
|
309
|
-
|
310
|
-
|
317
|
+
|
318
|
+
# from : "打" | "(" [1-9] [1-9] ")"
|
319
|
+
def parse_from
|
320
|
+
s0 = @scanner.pos
|
321
|
+
s1 = match_str('打')
|
311
322
|
if s1 != :failed
|
312
323
|
@reported_pos = s0
|
313
324
|
s1 = nil
|
314
325
|
end
|
315
326
|
s0 = s1
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
# from : "打" | "(" [1-9] [1-9] ")"
|
321
|
-
def parse_from
|
322
|
-
s0 = @current_pos
|
323
|
-
s1 = match_str("打")
|
324
|
-
if s1 != :failed
|
325
|
-
@reported_pos = s0
|
326
|
-
s1 = nil
|
327
|
-
end
|
328
|
-
s0 = s1
|
329
|
-
if s0 == :failed
|
330
|
-
s0 = @current_pos
|
331
|
-
if match_str("(") == :failed
|
332
|
-
@current_pos = s0
|
333
|
-
s0 = :failed
|
334
|
-
else
|
335
|
-
s2 = match_regexp(/^[1-9]/)
|
336
|
-
if s2 == :failed
|
337
|
-
@current_pos = s0
|
327
|
+
if s0 == :failed
|
328
|
+
s0 = @scanner.pos
|
329
|
+
if match_str('(') == :failed
|
330
|
+
@scanner.pos = s0
|
338
331
|
s0 = :failed
|
339
332
|
else
|
340
|
-
|
341
|
-
if
|
342
|
-
@
|
333
|
+
s2 = match_regexp(/[1-9]/)
|
334
|
+
if s2 == :failed
|
335
|
+
@scanner.pos = s0
|
343
336
|
s0 = :failed
|
344
|
-
elsif match_str(")") != :failed
|
345
|
-
@reported_pos = s0
|
346
|
-
s0 = { "x" => s2.to_i, "y" => s3.to_i }
|
347
337
|
else
|
348
|
-
|
349
|
-
|
338
|
+
s3 = match_regexp(/[1-9]/)
|
339
|
+
if s3 == :failed
|
340
|
+
@scanner.pos = s0
|
341
|
+
s0 = :failed
|
342
|
+
elsif match_str(')') != :failed
|
343
|
+
@reported_pos = s0
|
344
|
+
s0 = { 'x' => s2.to_i, 'y' => s3.to_i }
|
345
|
+
else
|
346
|
+
@scanner.pos = s0
|
347
|
+
s0 = :failed
|
348
|
+
end
|
350
349
|
end
|
351
350
|
end
|
352
351
|
end
|
352
|
+
s0
|
353
353
|
end
|
354
|
-
s0
|
355
|
-
end
|
356
354
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
s0 = :failed
|
363
|
-
else
|
364
|
-
match_spaces
|
365
|
-
s3 = parse_ms
|
366
|
-
if s3 == :failed
|
367
|
-
@current_pos = s0
|
355
|
+
# time : "(" " "* ms " "* "/" " "* (hms | ms) " "* ")"
|
356
|
+
def parse_time
|
357
|
+
s0 = @scanner.pos
|
358
|
+
if match_str('(') == :failed
|
359
|
+
@scanner.pos = s0
|
368
360
|
s0 = :failed
|
369
361
|
else
|
370
362
|
match_spaces
|
371
|
-
|
372
|
-
|
363
|
+
s3 = parse_ms
|
364
|
+
if s3 == :failed
|
365
|
+
@scanner.pos = s0
|
373
366
|
s0 = :failed
|
374
367
|
else
|
375
368
|
match_spaces
|
376
|
-
|
377
|
-
|
378
|
-
if s5 == :failed
|
379
|
-
@current_pos = s0
|
369
|
+
if match_str('/') == :failed
|
370
|
+
@scanner.pos = s0
|
380
371
|
s0 = :failed
|
381
372
|
else
|
382
373
|
match_spaces
|
383
|
-
|
384
|
-
|
374
|
+
s5 = parse_hms
|
375
|
+
s5 = parse_ms(with_hour: true) if s5 == :failed
|
376
|
+
if s5 == :failed
|
377
|
+
@scanner.pos = s0
|
385
378
|
s0 = :failed
|
386
379
|
else
|
387
|
-
|
388
|
-
|
380
|
+
match_spaces
|
381
|
+
if match_str(')') == :failed
|
382
|
+
@scanner.pos = s0
|
383
|
+
s0 = :failed
|
384
|
+
else
|
385
|
+
@reported_pos = s0
|
386
|
+
s0 = { 'now' => s3, 'total' => s5 }
|
387
|
+
end
|
389
388
|
end
|
390
389
|
end
|
391
390
|
end
|
392
391
|
end
|
392
|
+
s0
|
393
393
|
end
|
394
|
-
s0
|
395
|
-
end
|
396
394
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
s0 = :failed
|
405
|
-
elsif match_str(":") != :failed
|
406
|
-
s3 = match_digits!
|
407
|
-
if s3 == :failed
|
408
|
-
@current_pos = s0
|
395
|
+
# hms : [0-9]+ ":" [0-9]+ ":" [0-9]+
|
396
|
+
def parse_hms
|
397
|
+
s0 = @scanner.pos
|
398
|
+
s1 = match_digits!
|
399
|
+
|
400
|
+
if s1 == :failed
|
401
|
+
@scanner.pos = s0
|
409
402
|
s0 = :failed
|
410
|
-
elsif match_str(
|
411
|
-
|
412
|
-
if
|
413
|
-
@
|
403
|
+
elsif match_str(':') != :failed
|
404
|
+
s3 = match_digits!
|
405
|
+
if s3 == :failed
|
406
|
+
@scanner.pos = s0
|
414
407
|
s0 = :failed
|
408
|
+
elsif match_str(':') != :failed
|
409
|
+
s5 = match_digits!
|
410
|
+
if s5 == :failed
|
411
|
+
@scanner.pos = s0
|
412
|
+
s0 = :failed
|
413
|
+
else
|
414
|
+
@reported_pos = s0
|
415
|
+
s0 = { 'h' => s1.join.to_i, 'm' => s3.join.to_i, 's' => s5.join.to_i }
|
416
|
+
end
|
415
417
|
else
|
416
|
-
@
|
417
|
-
s0 =
|
418
|
+
@scanner.pos = s0
|
419
|
+
s0 = :failed
|
418
420
|
end
|
419
421
|
else
|
420
|
-
@
|
422
|
+
@scanner.pos = s0
|
421
423
|
s0 = :failed
|
422
424
|
end
|
423
|
-
|
424
|
-
@current_pos = s0
|
425
|
-
s0 = :failed
|
425
|
+
s0
|
426
426
|
end
|
427
|
-
s0
|
428
|
-
end
|
429
427
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
s0 = :failed
|
437
|
-
elsif match_str(":") != :failed
|
438
|
-
s3 = match_digits!
|
439
|
-
if s3 == :failed
|
440
|
-
@current_pos = s0
|
428
|
+
# ms : [0-9]+ ":" [0-9]+
|
429
|
+
def parse_ms(with_hour: false)
|
430
|
+
s0 = @scanner.pos
|
431
|
+
s1 = match_digits!
|
432
|
+
if s1 == :failed
|
433
|
+
@scanner.pos = s0
|
441
434
|
s0 = :failed
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
h = m / 60
|
448
|
-
m = m % 60
|
449
|
-
s0 = { "h" => h, "m" => m, "s" => s }
|
435
|
+
elsif match_str(':') != :failed
|
436
|
+
s3 = match_digits!
|
437
|
+
if s3 == :failed
|
438
|
+
@scanner.pos = s0
|
439
|
+
s0 = :failed
|
450
440
|
else
|
451
|
-
|
441
|
+
@reported_pos = s0
|
442
|
+
m = s1.join.to_i
|
443
|
+
s = s3.join.to_i
|
444
|
+
if with_hour
|
445
|
+
h = m / 60
|
446
|
+
m = m % 60
|
447
|
+
s0 = { 'h' => h, 'm' => m, 's' => s }
|
448
|
+
else
|
449
|
+
s0 = { 'm' => m, 's' => s }
|
450
|
+
end
|
452
451
|
end
|
453
|
-
end
|
454
|
-
else
|
455
|
-
@current_pos = s0
|
456
|
-
s0 = :failed
|
457
|
-
end
|
458
|
-
s0
|
459
|
-
end
|
460
|
-
|
461
|
-
# comment : "*" nonls nl | "&" nonls nl
|
462
|
-
def parse_comment
|
463
|
-
s0 = @current_pos
|
464
|
-
if match_str("*") == :failed
|
465
|
-
@current_pos = s0
|
466
|
-
s0 = :failed
|
467
|
-
else
|
468
|
-
s2 = parse_nonls
|
469
|
-
if parse_nl == :failed
|
470
|
-
@current_pos = s0
|
471
|
-
s0 = :failed
|
472
452
|
else
|
473
|
-
@
|
474
|
-
s0 =
|
453
|
+
@scanner.pos = s0
|
454
|
+
s0 = :failed
|
475
455
|
end
|
456
|
+
s0
|
476
457
|
end
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
458
|
+
|
459
|
+
# comment : "*" nonls nl | "&" nonls nl
|
460
|
+
def parse_comment
|
461
|
+
s0 = @scanner.pos
|
462
|
+
if match_str('*') == :failed
|
463
|
+
@scanner.pos = s0
|
482
464
|
s0 = :failed
|
483
465
|
else
|
484
466
|
s2 = parse_nonls
|
485
467
|
if parse_nl == :failed
|
486
|
-
@
|
468
|
+
@scanner.pos = s0
|
487
469
|
s0 = :failed
|
488
470
|
else
|
489
471
|
@reported_pos = s0
|
490
|
-
s0 =
|
472
|
+
s0 = s2.join
|
491
473
|
end
|
492
474
|
end
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
def parse_fork
|
499
|
-
s0 = @current_pos
|
500
|
-
if match_str("変化:") == :failed
|
501
|
-
@current_pos = s0
|
502
|
-
s0 = :failed
|
503
|
-
else
|
504
|
-
match_spaces
|
505
|
-
s3 = parse_te
|
506
|
-
if s3 == :failed
|
507
|
-
@current_pos = s0
|
508
|
-
s0 = :failed
|
509
|
-
elsif match_str("手") != :failed
|
510
|
-
if parse_nl == :failed
|
511
|
-
@current_pos = s0
|
475
|
+
if s0 == :failed
|
476
|
+
s0 = @scanner.pos
|
477
|
+
s1 = match_str('&')
|
478
|
+
if s1 == :failed
|
479
|
+
@scanner.pos = s0
|
512
480
|
s0 = :failed
|
513
481
|
else
|
514
|
-
|
515
|
-
if
|
516
|
-
@
|
482
|
+
s2 = parse_nonls
|
483
|
+
if parse_nl == :failed
|
484
|
+
@scanner.pos = s0
|
517
485
|
s0 = :failed
|
518
486
|
else
|
519
487
|
@reported_pos = s0
|
520
|
-
s0 =
|
488
|
+
s0 = '&' + s2.join
|
521
489
|
end
|
522
490
|
end
|
523
|
-
|
524
|
-
|
491
|
+
end
|
492
|
+
s0
|
493
|
+
end
|
494
|
+
|
495
|
+
# fork : "変化:" " "* [0-9]+ "手" nl moves
|
496
|
+
def parse_fork
|
497
|
+
s0 = @scanner.pos
|
498
|
+
if match_str('変化:') == :failed
|
499
|
+
@scanner.pos = s0
|
525
500
|
s0 = :failed
|
501
|
+
else
|
502
|
+
match_spaces
|
503
|
+
s3 = parse_te
|
504
|
+
if s3 == :failed
|
505
|
+
@scanner.pos = s0
|
506
|
+
s0 = :failed
|
507
|
+
elsif match_str('手') != :failed
|
508
|
+
if parse_nl == :failed
|
509
|
+
@scanner.pos = s0
|
510
|
+
s0 = :failed
|
511
|
+
else
|
512
|
+
s6 = parse_moves
|
513
|
+
if s6 == :failed
|
514
|
+
@scanner.pos = s0
|
515
|
+
s0 = :failed
|
516
|
+
else
|
517
|
+
@reported_pos = s0
|
518
|
+
s0 = { 'te' => s3.join.to_i, 'moves' => s6[1..-1] }
|
519
|
+
end
|
520
|
+
end
|
521
|
+
else
|
522
|
+
@scanner.pos = s0
|
523
|
+
s0 = :failed
|
524
|
+
end
|
526
525
|
end
|
526
|
+
s0
|
527
527
|
end
|
528
|
-
s0
|
529
|
-
end
|
530
528
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
529
|
+
# transfrom to jkf
|
530
|
+
def transform_root(headers, ini, headers2, moves, forks)
|
531
|
+
ret = { 'header' => {}, 'moves' => moves }
|
532
|
+
headers.compact.each { |h| ret['header'][h['k']] = h['v'] }
|
533
|
+
headers2.compact.each { |h| ret['header'][h['k']] = h['v'] }
|
534
|
+
if ini
|
535
|
+
ret['initial'] = ini
|
536
|
+
elsif ret['header']['手合割']
|
537
|
+
preset = preset2str(ret['header']['手合割'])
|
538
|
+
ret['initial'] = { 'preset' => preset } if preset && preset != 'OTHER'
|
539
|
+
end
|
540
|
+
transform_root_header_data(ret) if ret['initial'] && ret['initial']['data']
|
541
|
+
transform_root_forks(forks, moves)
|
542
|
+
if ret['initial'] && ret['initial']['data'] && ret['initial']['data']['color'] == 1
|
543
|
+
reverse_color(ret['moves'])
|
544
|
+
end
|
545
|
+
ret
|
541
546
|
end
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
547
|
+
|
548
|
+
# transform move to jkf
|
549
|
+
def transform_move(line, c)
|
550
|
+
ret = {}
|
551
|
+
ret['comments'] = c unless c.empty?
|
552
|
+
if line['move'].is_a? Hash
|
553
|
+
ret['move'] = line['move']
|
554
|
+
else
|
555
|
+
ret['special'] = special2csa(line['move'])
|
556
|
+
end
|
557
|
+
ret['time'] = line['time'] if line['time']
|
558
|
+
ret
|
546
559
|
end
|
547
|
-
ret
|
548
|
-
end
|
549
560
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
561
|
+
# transform teban-fugou-from to jkf
|
562
|
+
def transform_teban_fugou_from(teban, fugou, from)
|
563
|
+
ret = { 'color' => teban2color(teban.join), 'piece' => fugou['piece'] }
|
564
|
+
if fugou['to']
|
565
|
+
ret['to'] = fugou['to']
|
566
|
+
else
|
567
|
+
ret['same'] = true
|
568
|
+
end
|
569
|
+
ret['promote'] = true if fugou['promote']
|
570
|
+
ret['from'] = from if from
|
571
|
+
ret
|
558
572
|
end
|
559
|
-
ret["time"] = line["time"] if line["time"]
|
560
|
-
ret
|
561
|
-
end
|
562
573
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
574
|
+
# special string to csa
|
575
|
+
def special2csa(str)
|
576
|
+
{
|
577
|
+
'中断' => 'CHUDAN',
|
578
|
+
'投了' => 'TORYO',
|
579
|
+
'持将棋' => 'JISHOGI',
|
580
|
+
'千日手' => 'SENNICHITE',
|
581
|
+
'詰み' => 'TSUMI',
|
582
|
+
'不詰' => 'FUZUMI',
|
583
|
+
'切れ負け' => 'TIME_UP',
|
584
|
+
'反則勝ち' => 'ILLEGAL_ACTION', # 直前の手が反則(先頭に+か-で反則した側の情報を含める必要が有る)
|
585
|
+
'反則負け' => 'ILLEGAL_MOVE' # ここで手番側が反則,反則の内容はコメントで表現
|
586
|
+
}[str] || (raise ParseError)
|
570
587
|
end
|
571
|
-
ret["promote"] = true if fugou["promote"]
|
572
|
-
ret["from"] = from if from
|
573
|
-
ret
|
574
|
-
end
|
575
588
|
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
"持将棋" => "JISHOGI",
|
582
|
-
"千日手" => "SENNICHITE",
|
583
|
-
"詰み" => "TSUMI",
|
584
|
-
"不詰" => "FUZUMI",
|
585
|
-
"切れ負け" => "TIME_UP",
|
586
|
-
"反則勝ち" => "ILLEGAL_ACTION", # 直前の手が反則(先頭に+か-で反則した側の情報を含める必要が有る)
|
587
|
-
"反則負け" => "ILLEGAL_MOVE" # ここで手番側が反則,反則の内容はコメントで表現
|
588
|
-
}[str] || (raise ParseError)
|
589
|
-
end
|
589
|
+
# teban to color
|
590
|
+
def teban2color(teban)
|
591
|
+
teban = teban.to_i unless teban.is_a? Integer
|
592
|
+
(teban + 1) % 2
|
593
|
+
end
|
590
594
|
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
595
|
+
# generate motigoma
|
596
|
+
def make_hand(str)
|
597
|
+
# Kifu for iPhoneは半角スペース区切り
|
598
|
+
ret = { 'FU' => 0, 'KY' => 0, 'KE' => 0, 'GI' => 0, 'KI' => 0, 'KA' => 0, 'HI' => 0 }
|
599
|
+
return ret if str.empty?
|
596
600
|
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
return ret if str.empty?
|
601
|
+
str.split(/[ ]/).each do |kind|
|
602
|
+
next if kind.empty?
|
603
|
+
ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
|
604
|
+
end
|
602
605
|
|
603
|
-
|
604
|
-
next if kind.empty?
|
605
|
-
ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
|
606
|
+
ret
|
606
607
|
end
|
607
608
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
move["move"]["color"] = (move["move"]["color"] + 1) % 2
|
609
|
+
# exchange sente gote
|
610
|
+
def reverse_color(moves)
|
611
|
+
moves.each do |move|
|
612
|
+
if move['move'] && move['move']['color']
|
613
|
+
move['move']['color'] = (move['move']['color'] + 1) % 2
|
614
|
+
end
|
615
|
+
move['forks']&.each { |fork| reverse_color(fork) }
|
616
616
|
end
|
617
|
-
move["forks"].each { |_fork| reverse_color(_fork) } if move["forks"]
|
618
617
|
end
|
619
618
|
end
|
620
619
|
end
|