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