jkf 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,9 @@
1
+ module Jkf
2
+ module Converter
3
+ class ConvertError < StandardError; end
4
+ end
5
+ end
6
+
7
+ require 'json'
8
+ require 'jkf/converter/ki2'
9
+ require 'jkf/converter/kif'
@@ -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