jkf 0.2.2
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Guardfile +70 -0
- data/LICENSE.txt +21 -0
- data/README.md +42 -0
- data/Rakefile +6 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/jkf.gemspec +26 -0
- data/lib/jkf/converter/ki2.rb +249 -0
- data/lib/jkf/converter/kif.rb +271 -0
- data/lib/jkf/converter.rb +9 -0
- data/lib/jkf/parser/base.rb +61 -0
- data/lib/jkf/parser/csa.rb +988 -0
- data/lib/jkf/parser/ki2.rb +1240 -0
- data/lib/jkf/parser/kif.rb +1536 -0
- data/lib/jkf/parser.rb +10 -0
- data/lib/jkf/version.rb +3 -0
- data/lib/jkf.rb +7 -0
- metadata +122 -0
@@ -0,0 +1,271 @@
|
|
1
|
+
module Jkf::Converter
|
2
|
+
class Kif
|
3
|
+
def convert(jkf)
|
4
|
+
hash = if jkf.is_a?(Hash)
|
5
|
+
jkf
|
6
|
+
else
|
7
|
+
JSON.parse(jkf)
|
8
|
+
end
|
9
|
+
@forks = []
|
10
|
+
|
11
|
+
result = ''
|
12
|
+
result += convert_header(hash['header']) if hash['header']
|
13
|
+
result += convert_initial(hash['initial']) if hash['initial']
|
14
|
+
result += "手数----指手---------消費時間--\n"
|
15
|
+
result += convert_moves(hash['moves'])
|
16
|
+
if @forks.size > 0
|
17
|
+
result += "\n"
|
18
|
+
result += @forks.join("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def convert_header(header)
|
27
|
+
header.map { |(key, value)| "#{key}:#{value}\n" }.join
|
28
|
+
end
|
29
|
+
|
30
|
+
def convert_initial(initial)
|
31
|
+
result = ''
|
32
|
+
result += "手合割:#{preset2str(initial["preset"])}\n" if initial["preset"] != "OTHER"
|
33
|
+
|
34
|
+
data = initial["data"]
|
35
|
+
|
36
|
+
if data
|
37
|
+
if data['color'] == 0
|
38
|
+
result += "先手番\n"
|
39
|
+
elsif data['color'] == 1
|
40
|
+
result += "後手番\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
if data['hands']
|
44
|
+
if data['hands'][0]
|
45
|
+
result += '先手の持駒:'
|
46
|
+
result += convert_motigoma(data['hands'][0])
|
47
|
+
end
|
48
|
+
if data['hands'][1]
|
49
|
+
result += '後手の持駒:'
|
50
|
+
result += convert_motigoma(data['hands'][1])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if data['board']
|
55
|
+
result += " 9 8 7 6 5 4 3 2 1\n"
|
56
|
+
result += "+---------------------------+\n"
|
57
|
+
9.times { |y|
|
58
|
+
line = "|"
|
59
|
+
9.times { |x|
|
60
|
+
line += convert_board_piece(data['board'][8-x][y])
|
61
|
+
}
|
62
|
+
line += "|#{n2kan(y+1)}\n"
|
63
|
+
result += line
|
64
|
+
}
|
65
|
+
result += "+---------------------------+\n"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
def convert_moves(moves, idx=0)
|
73
|
+
result = ''
|
74
|
+
moves.each_with_index { |move, i|
|
75
|
+
if move['special']
|
76
|
+
result_move = "%4d "%(i+idx)
|
77
|
+
result_move += ljust(special2kan(move['special']), 13)
|
78
|
+
result_move += convert_time(move['time']) if move['time']
|
79
|
+
result_move += "+" if move['forks']
|
80
|
+
result_move += "\n"
|
81
|
+
result += result_move
|
82
|
+
# first_board+speical分を引く(-2)
|
83
|
+
result += convert_special(move['special'], i-2+idx) if move['special']
|
84
|
+
else
|
85
|
+
if move['move']
|
86
|
+
result_move = "%4d "%(i+idx)
|
87
|
+
result_move += convert_move(move['move'])
|
88
|
+
result_move += convert_time(move['time']) if move['time']
|
89
|
+
result_move += "+" if move['forks']
|
90
|
+
result_move += "\n"
|
91
|
+
result += result_move
|
92
|
+
end
|
93
|
+
|
94
|
+
if move['comments']
|
95
|
+
result += convert_comments(move['comments'])
|
96
|
+
end
|
97
|
+
|
98
|
+
@forks.unshift convert_forks(move['forks'], i+idx) if move['forks']
|
99
|
+
end
|
100
|
+
}
|
101
|
+
result
|
102
|
+
end
|
103
|
+
|
104
|
+
def convert_move(move)
|
105
|
+
result = if move['to']
|
106
|
+
n2zen(move['to']['x']) + n2kan(move['to']['y'])
|
107
|
+
elsif move['same']
|
108
|
+
'同 '
|
109
|
+
else
|
110
|
+
raise "error??"
|
111
|
+
end
|
112
|
+
result += csa2kind(move['piece'])
|
113
|
+
result += '成' if move['promote']
|
114
|
+
result += if move['from']
|
115
|
+
"(#{move['from']['x']}#{move['from']['y']})"
|
116
|
+
else
|
117
|
+
'打'
|
118
|
+
end
|
119
|
+
result = ljust(result,13)
|
120
|
+
result
|
121
|
+
end
|
122
|
+
|
123
|
+
def convert_time(time)
|
124
|
+
"(%2d:%02d/%02d:%02d:%02d)"%[
|
125
|
+
time['now']['m'],
|
126
|
+
time['now']['s'],
|
127
|
+
time['total']['h'],
|
128
|
+
time['total']['m'],
|
129
|
+
time['total']['s'],
|
130
|
+
]
|
131
|
+
end
|
132
|
+
|
133
|
+
def convert_special(special, index)
|
134
|
+
result = "まで#{index+1}手"
|
135
|
+
|
136
|
+
if special == 'TORYO' || special =~ /ILLEGAL/
|
137
|
+
turn = index % 2 == 0 ? '後' : '先'
|
138
|
+
result += "で#{turn}手の"
|
139
|
+
result += case special
|
140
|
+
when "TORYO" then "勝ち"
|
141
|
+
when "ILLEGAL_ACTION" then "反則勝ち"
|
142
|
+
when "ILLEGAL_MOVE" then "反則負け"
|
143
|
+
end
|
144
|
+
else
|
145
|
+
turn = index % 2 == 0 ? '先' : '後'
|
146
|
+
result += case special
|
147
|
+
when "TIME_UP" then "で時間切れにより#{turn}手の勝ち"
|
148
|
+
when "CHUDAN" then "で中断"
|
149
|
+
when "JISHOGI" then "で持将棋"
|
150
|
+
when "SENNICHITE" then "で千日手"
|
151
|
+
when "TSUMI" then "で詰み"
|
152
|
+
when "FUZUMI" then "で不詰"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
result += "\n"
|
157
|
+
result
|
158
|
+
end
|
159
|
+
|
160
|
+
def convert_comments(comments)
|
161
|
+
comments.map { |comment| "*#{comment}\n" }.join
|
162
|
+
end
|
163
|
+
|
164
|
+
def convert_forks(forks, index)
|
165
|
+
result = "\n"
|
166
|
+
result = "変化:%4d手\n"%[index]
|
167
|
+
forks.each do |moves|
|
168
|
+
result += convert_moves(moves, index)
|
169
|
+
end
|
170
|
+
result
|
171
|
+
end
|
172
|
+
|
173
|
+
def convert_board_piece(piece)
|
174
|
+
result = ''
|
175
|
+
|
176
|
+
if piece == {}
|
177
|
+
result = ' ・'
|
178
|
+
else
|
179
|
+
result += piece['color'] == 0 ? ' ' : 'v'
|
180
|
+
result += csa2kind(piece['kind'])
|
181
|
+
end
|
182
|
+
|
183
|
+
result
|
184
|
+
end
|
185
|
+
|
186
|
+
def convert_motigoma(pieces)
|
187
|
+
pieces.map do |piece, num|
|
188
|
+
if num > 0
|
189
|
+
str = csa2kind(piece)
|
190
|
+
if num > 1
|
191
|
+
str += n2kan(num/10) if num / 10 > 0
|
192
|
+
num %= 10
|
193
|
+
str += n2kan(num)
|
194
|
+
end
|
195
|
+
str
|
196
|
+
else
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
end.compact.join(' ') + " \n"
|
200
|
+
end
|
201
|
+
|
202
|
+
protected
|
203
|
+
|
204
|
+
def n2zen(n)
|
205
|
+
"0123456789"[n]
|
206
|
+
end
|
207
|
+
|
208
|
+
def n2kan(n)
|
209
|
+
"〇一二三四五六七八九"[n]
|
210
|
+
end
|
211
|
+
|
212
|
+
def csa2kind(csa)
|
213
|
+
{
|
214
|
+
"FU" => "歩",
|
215
|
+
"KY" => "香",
|
216
|
+
"KE" => "桂",
|
217
|
+
"GI" => "銀",
|
218
|
+
"KI" => "金",
|
219
|
+
"KA" => "角",
|
220
|
+
"HI" => "飛",
|
221
|
+
"OU" => "玉",
|
222
|
+
"TO" => "と",
|
223
|
+
"NY" => "成香",
|
224
|
+
"NK" => "成桂",
|
225
|
+
"NG" => "成銀",
|
226
|
+
"UM" => "馬",
|
227
|
+
"RY" => "龍",
|
228
|
+
}[csa]
|
229
|
+
end
|
230
|
+
|
231
|
+
def preset2str(preset)
|
232
|
+
{
|
233
|
+
"HIRATE" => "平手",
|
234
|
+
"KY" => "香落ち",
|
235
|
+
"KY_R" => "右香落ち",
|
236
|
+
"KA" => "角落ち",
|
237
|
+
"HI" => "飛車落ち",
|
238
|
+
"HIKY" => "飛香落ち",
|
239
|
+
"2" => "二枚落ち",
|
240
|
+
"3" => "三枚落ち",
|
241
|
+
"4" => "四枚落ち",
|
242
|
+
"5" => "五枚落ち",
|
243
|
+
"5_L" => "左五枚落ち",
|
244
|
+
"6" => "六枚落ち",
|
245
|
+
"8" => "八枚落ち",
|
246
|
+
"10" => "十枚落ち",
|
247
|
+
"OTHER" => "その他"
|
248
|
+
}[preset]
|
249
|
+
end
|
250
|
+
|
251
|
+
def special2kan(special)
|
252
|
+
case special
|
253
|
+
when "CHUDAN" then "中断"
|
254
|
+
when "TORYO" then "投了"
|
255
|
+
when "JISHOGI" then "持将棋"
|
256
|
+
when "SENNICHITE" then "千日手"
|
257
|
+
when "TSUMI" then "詰み"
|
258
|
+
when "FUZUMI" then "不詰"
|
259
|
+
when "TIME_UP" then "切れ負け"
|
260
|
+
when "ILLEGAL_ACTION" then "反則勝ち"
|
261
|
+
when "ILLEGAL_MOVE" then "反則負け"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def ljust(str, n)
|
266
|
+
len = 0
|
267
|
+
str.each_codepoint { |codepoint| len += codepoint > 255 ? 2 : 1 }
|
268
|
+
str + ' '*(n-len)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Jkf::Parser
|
2
|
+
class Base
|
3
|
+
def parse(input)
|
4
|
+
@input = input.clone
|
5
|
+
|
6
|
+
@current_pos = 0
|
7
|
+
@reported_pos = 0
|
8
|
+
@cached_pos = 0
|
9
|
+
@cached_pos_details = { line: 1, column: 1, seenCR: false }
|
10
|
+
@max_fail_pos = 0
|
11
|
+
@max_fail_expected = []
|
12
|
+
@silent_fails = 0
|
13
|
+
|
14
|
+
@result = parse_root
|
15
|
+
|
16
|
+
if @result != :failded && @current_pos == @input.length
|
17
|
+
return @result
|
18
|
+
else
|
19
|
+
fail({ type: "end", description: "end of input" }) if @result != :failed && @current_pos < input.length
|
20
|
+
raise ParseError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def match_regexp(reg)
|
27
|
+
ret = nil
|
28
|
+
if matched = reg.match(@input[@current_pos])
|
29
|
+
ret = matched.to_s
|
30
|
+
@current_pos += ret.size
|
31
|
+
else
|
32
|
+
ret = :failed
|
33
|
+
fail({ type: "class", value: reg.inspect, description: reg.inspect }) if @silent_fails == 0
|
34
|
+
end
|
35
|
+
ret
|
36
|
+
end
|
37
|
+
|
38
|
+
def match_str(str)
|
39
|
+
ret = nil
|
40
|
+
if @input[@current_pos, str.size] == str
|
41
|
+
ret = str
|
42
|
+
@current_pos += str.size
|
43
|
+
else
|
44
|
+
ret = :failed
|
45
|
+
fail({ type: "literal", value: str, description: "\"#{str}\"" }) if @slient_fails == 0
|
46
|
+
end
|
47
|
+
ret
|
48
|
+
end
|
49
|
+
|
50
|
+
def fail(expected)
|
51
|
+
return if @current_pos < @max_fail_pos
|
52
|
+
|
53
|
+
if @current_pos > @max_fail_pos
|
54
|
+
@max_fail_pos = @current_pos
|
55
|
+
@max_fail_expected = []
|
56
|
+
end
|
57
|
+
|
58
|
+
@max_fail_expected << expected
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|