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/converter/kif.rb
CHANGED
@@ -1,120 +1,126 @@
|
|
1
|
-
module Jkf
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Jkf
|
2
|
+
module Converter
|
3
|
+
# KIF Converter
|
4
|
+
class Kif < Base
|
5
|
+
protected
|
5
6
|
|
6
|
-
|
7
|
+
include Kifuable
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def convert_root(jkf)
|
10
|
+
reset!
|
11
|
+
setup_players!(jkf)
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
result = ''
|
14
|
+
result += convert_header(jkf['header'], jkf) if jkf['header']
|
15
|
+
result += convert_initial(jkf['initial']) if jkf['initial']
|
16
|
+
result += @header2.join
|
17
|
+
result += "手数----指手---------消費時間--\n"
|
18
|
+
result += convert_moves(jkf['moves'])
|
19
|
+
unless @forks.empty?
|
20
|
+
result += "\n"
|
21
|
+
result += @forks.join("\n")
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
result
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
def convert_header(header, jkf)
|
28
|
+
header.map do |(key, value)|
|
29
|
+
result = "#{key}:#{value}\n"
|
30
|
+
if key =~ /\A[先後上下]手\Z/
|
31
|
+
if key =~ /[先下]/
|
32
|
+
@header2.unshift result
|
33
|
+
else
|
34
|
+
@header2 << result
|
35
|
+
end
|
36
|
+
nil
|
37
|
+
elsif key == '手合割' && jkf['initial'] && jkf['initial']['preset'] && value == preset2str(jkf['initial']['preset'])
|
38
|
+
nil
|
32
39
|
else
|
33
|
-
|
40
|
+
result
|
34
41
|
end
|
35
|
-
|
36
|
-
|
37
|
-
nil
|
38
|
-
else
|
39
|
-
result
|
40
|
-
end
|
41
|
-
end.compact.join
|
42
|
-
end
|
42
|
+
end.compact.join
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
def convert_moves(moves, idx = 0)
|
46
|
+
result = ''
|
47
|
+
moves.each_with_index do |move, i|
|
48
|
+
if move['special']
|
49
|
+
result += convert_special_line(move, i + idx)
|
50
|
+
else
|
51
|
+
result += convert_move_line(move, i + idx) if move['move']
|
52
|
+
result += convert_comments(move['comments']) if move['comments']
|
53
|
+
@forks.unshift convert_forks(move['forks'], i + idx) if move['forks']
|
54
|
+
end
|
53
55
|
end
|
56
|
+
result
|
54
57
|
end
|
55
|
-
result
|
56
|
-
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
def convert_move_line(move, index)
|
60
|
+
result = '%4d ' % [index]
|
61
|
+
result += convert_move(move['move'])
|
62
|
+
result += convert_time(move['time']) if move['time']
|
63
|
+
result += '+' if move['forks']
|
64
|
+
result + "\n"
|
65
|
+
end
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
67
|
+
def convert_special_line(move, index)
|
68
|
+
result = '%4d ' % [index]
|
69
|
+
result += ljust(special2kan(move['special']), 13)
|
70
|
+
result += convert_time(move['time']) if move['time']
|
71
|
+
result += '+' if move['forks']
|
72
|
+
result += "\n"
|
73
|
+
# first_board+speical分を引く(-2)
|
74
|
+
result + convert_special(move['special'], index - 2)
|
75
|
+
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
77
|
+
def convert_move(move)
|
78
|
+
result = convert_piece_with_pos(move)
|
79
|
+
result += if move['from']
|
80
|
+
"(#{pos2str(move['from'])})"
|
81
|
+
else
|
82
|
+
'打'
|
83
|
+
end
|
84
|
+
ljust(result, 13)
|
85
|
+
end
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
87
|
+
def convert_time(time)
|
88
|
+
'(%2d:%02d/%02d:%02d:%02d)' % [
|
89
|
+
time['now']['m'],
|
90
|
+
time['now']['s'],
|
91
|
+
time['total']['h'],
|
92
|
+
time['total']['m'],
|
93
|
+
time['total']['s']
|
94
|
+
]
|
95
|
+
end
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
when "CHUDAN" then "中断"
|
99
|
-
when "TORYO" then "投了"
|
100
|
-
when "JISHOGI" then "持将棋"
|
101
|
-
when "SENNICHITE" then "千日手"
|
102
|
-
when "TSUMI" then "詰み"
|
103
|
-
when "FUZUMI" then "不詰"
|
104
|
-
when "TIME_UP" then "切れ負け"
|
105
|
-
when "ILLEGAL_ACTION" then "反則勝ち"
|
106
|
-
when "ILLEGAL_MOVE" then "反則負け"
|
97
|
+
def special2kan(special)
|
98
|
+
SPECIAL_NAME_TO_KIF_MAPPING[special]
|
107
99
|
end
|
108
|
-
end
|
109
100
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
101
|
+
SPECIAL_NAME_TO_KIF_MAPPING = {
|
102
|
+
'CHUDAN' => '中断',
|
103
|
+
'TORYO' => '投了',
|
104
|
+
'JISHOGI' => '持将棋',
|
105
|
+
'SENNICHITE' => '千日手',
|
106
|
+
'TSUMI' => '詰み',
|
107
|
+
'FUZUMI' => '不詰',
|
108
|
+
'TIME_UP' => '切れ負け',
|
109
|
+
'ILLEGAL_ACTION' => '反則勝ち',
|
110
|
+
'ILLEGAL_MOVE' => '反則負け'
|
111
|
+
}.freeze
|
112
|
+
|
113
|
+
private_constant :SPECIAL_NAME_TO_KIF_MAPPING
|
115
114
|
|
116
|
-
|
117
|
-
|
115
|
+
def ljust(str, n)
|
116
|
+
len = 0
|
117
|
+
str.each_codepoint { |codepoint| len += codepoint > 255 ? 2 : 1 }
|
118
|
+
str + (' ' * (n - len))
|
119
|
+
end
|
120
|
+
|
121
|
+
def pos2str(pos)
|
122
|
+
'%d%d' % [pos['x'], pos['y']]
|
123
|
+
end
|
118
124
|
end
|
119
125
|
end
|
120
126
|
end
|
@@ -1,188 +1,188 @@
|
|
1
|
-
module Jkf
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
1
|
+
module Jkf
|
2
|
+
module Converter
|
3
|
+
# Intersection of KIF and KI2
|
4
|
+
module Kifuable
|
5
|
+
protected
|
6
|
+
|
7
|
+
def convert_initial(initial)
|
8
|
+
result = convert_handicap(initial['preset'])
|
9
|
+
footer = ''
|
10
|
+
|
11
|
+
data = initial['data']
|
12
|
+
if data
|
13
|
+
result += convert_teban(data, 1)
|
14
|
+
if (hands = data['hands'])
|
15
|
+
result += convert_hands(hands, 1) if hands[1]
|
16
|
+
footer += convert_hands(hands, 0) if hands[0]
|
17
|
+
end
|
18
|
+
footer += convert_teban(data, 0)
|
18
19
|
|
19
|
-
|
20
|
+
result += convert_board(data['board']) if data['board']
|
21
|
+
end
|
22
|
+
result + footer
|
20
23
|
end
|
21
|
-
result + footer
|
22
|
-
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
def convert_handicap(preset)
|
26
|
+
preset == 'OTHER' ? '' : "手合割:#{preset2str(preset)}\n"
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def convert_teban(data, color)
|
30
|
+
data['color'] == color ? "#{@players[color]}手番\n" : ''
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def convert_hands(hands, color)
|
34
|
+
"#{@players[color]}手の持駒:" + convert_motigoma(hands[color])
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
def convert_board(board)
|
38
|
+
result = " 9 8 7 6 5 4 3 2 1\n+---------------------------+\n"
|
39
|
+
9.times do |y|
|
40
|
+
line = '|'
|
41
|
+
9.times do |x|
|
42
|
+
line += convert_board_piece(board[8 - x][y])
|
43
|
+
end
|
44
|
+
line += "|#{n2kan(y + 1)}\n"
|
45
|
+
result += line
|
42
46
|
end
|
43
|
-
|
44
|
-
result += line
|
47
|
+
result + "+---------------------------+\n"
|
45
48
|
end
|
46
|
-
result + "+---------------------------+\n"
|
47
|
-
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
def convert_comments(comments)
|
51
|
+
comments.map { |comment| "*#{comment}\n" }.join
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
def convert_motigoma(pieces)
|
55
|
+
pieces.to_a.reverse.map do |(piece, num)|
|
56
|
+
if num > 0
|
57
|
+
str = csa2kind(piece)
|
58
|
+
if num > 1
|
59
|
+
str += n2kan(num / 10) if num / 10 > 0
|
60
|
+
num %= 10
|
61
|
+
str += n2kan(num)
|
62
|
+
end
|
63
|
+
str
|
61
64
|
end
|
62
|
-
|
63
|
-
|
64
|
-
end.compact.join(" ") + " \n"
|
65
|
-
end
|
65
|
+
end.compact.join(' ') + " \n"
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
68
|
+
def convert_board_piece(piece)
|
69
|
+
result = ''
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
if piece == {}
|
72
|
+
result = ' ・'
|
73
|
+
else
|
74
|
+
result += piece['color'] == 0 ? ' ' : 'v'
|
75
|
+
result += csa2kind(piece['kind'])
|
76
|
+
end
|
77
|
+
|
78
|
+
result
|
75
79
|
end
|
76
80
|
|
77
|
-
|
78
|
-
|
81
|
+
def convert_special(special, index)
|
82
|
+
result = "まで#{index + 1}手"
|
83
|
+
|
84
|
+
if special == 'TORYO' || special =~ /ILLEGAL/
|
85
|
+
turn = @players[index % 2]
|
86
|
+
result += "で#{turn}手の"
|
87
|
+
result += { 'TORYO' => '勝ち',
|
88
|
+
'ILLEGAL_ACTION' => '反則勝ち',
|
89
|
+
'ILLEGAL_MOVE' => '反則負け' }[special]
|
90
|
+
else
|
91
|
+
turn = @players[(index + 1) % 2]
|
92
|
+
result += case special
|
93
|
+
when 'TIME_UP' then "で時間切れにより#{turn}手の勝ち"
|
94
|
+
when 'CHUDAN' then 'で中断'
|
95
|
+
when 'JISHOGI' then 'で持将棋'
|
96
|
+
when 'SENNICHITE' then 'で千日手'
|
97
|
+
when 'TSUMI' then 'で詰み'
|
98
|
+
when 'FUZUMI' then 'で不詰'
|
99
|
+
end
|
100
|
+
end
|
79
101
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
if special == "TORYO" || special =~ /ILLEGAL/
|
84
|
-
turn = @players[index % 2]
|
85
|
-
result += "で#{turn}手の"
|
86
|
-
result += case special
|
87
|
-
when "TORYO" then "勝ち"
|
88
|
-
when "ILLEGAL_ACTION" then "反則勝ち"
|
89
|
-
when "ILLEGAL_MOVE" then "反則負け"
|
90
|
-
end
|
91
|
-
else
|
92
|
-
turn = @players[(index + 1) % 2]
|
93
|
-
result += case special
|
94
|
-
when "TIME_UP" then "で時間切れにより#{turn}手の勝ち"
|
95
|
-
when "CHUDAN" then "で中断"
|
96
|
-
when "JISHOGI" then "で持将棋"
|
97
|
-
when "SENNICHITE" then "で千日手"
|
98
|
-
when "TSUMI" then "で詰み"
|
99
|
-
when "FUZUMI" then "で不詰"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
result + "\n"
|
104
|
-
end
|
102
|
+
result + "\n"
|
103
|
+
end
|
105
104
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
105
|
+
def convert_piece_with_pos(move)
|
106
|
+
result = if move['to']
|
107
|
+
n2zen(move['to']['x']) + n2kan(move['to']['y'])
|
108
|
+
elsif move['same']
|
109
|
+
'同 '
|
110
|
+
end
|
111
|
+
result += csa2kind(move['piece'])
|
112
|
+
result += '成' if move['promote']
|
113
|
+
result
|
114
|
+
end
|
116
115
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
116
|
+
def convert_forks(forks, index)
|
117
|
+
result = "\n"
|
118
|
+
result = "変化:%4d手\n" % [index] # ki2の場合\nなし
|
119
|
+
forks.each do |moves|
|
120
|
+
result += convert_moves(moves, index)
|
121
|
+
end
|
122
|
+
result
|
122
123
|
end
|
123
|
-
result
|
124
|
-
end
|
125
124
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
125
|
+
def reset!
|
126
|
+
@forks = []
|
127
|
+
@header2 = []
|
128
|
+
end
|
130
129
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
130
|
+
def setup_players!(jkf)
|
131
|
+
players_flag = :sengo
|
132
|
+
jkf['header']&.keys&.detect { |key| key =~ /[上下]手/ } && players_flag = :uwasimo
|
133
|
+
@players = if players_flag == :uwasimo
|
134
|
+
['下', '上']
|
135
|
+
else
|
136
|
+
['先', '後']
|
137
|
+
end
|
138
|
+
end
|
140
139
|
|
141
|
-
|
142
|
-
|
143
|
-
|
140
|
+
def n2zen(n)
|
141
|
+
'0123456789'[n]
|
142
|
+
end
|
144
143
|
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
def n2kan(n)
|
145
|
+
'〇一二三四五六七八九'[n]
|
146
|
+
end
|
148
147
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
148
|
+
def csa2kind(csa)
|
149
|
+
{
|
150
|
+
'FU' => '歩',
|
151
|
+
'KY' => '香',
|
152
|
+
'KE' => '桂',
|
153
|
+
'GI' => '銀',
|
154
|
+
'KI' => '金',
|
155
|
+
'KA' => '角',
|
156
|
+
'HI' => '飛',
|
157
|
+
'OU' => '玉',
|
158
|
+
'TO' => 'と',
|
159
|
+
'NY' => '成香',
|
160
|
+
'NK' => '成桂',
|
161
|
+
'NG' => '成銀',
|
162
|
+
'UM' => '馬',
|
163
|
+
'RY' => '龍'
|
164
|
+
}[csa]
|
165
|
+
end
|
167
166
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
167
|
+
def preset2str(preset)
|
168
|
+
{
|
169
|
+
'HIRATE' => '平手',
|
170
|
+
'KY' => '香落ち',
|
171
|
+
'KY_R' => '右香落ち',
|
172
|
+
'KA' => '角落ち',
|
173
|
+
'HI' => '飛車落ち',
|
174
|
+
'HIKY' => '飛香落ち',
|
175
|
+
'2' => '二枚落ち',
|
176
|
+
'3' => '三枚落ち',
|
177
|
+
'4' => '四枚落ち',
|
178
|
+
'5' => '五枚落ち',
|
179
|
+
'5_L' => '左五枚落ち',
|
180
|
+
'6' => '六枚落ち',
|
181
|
+
'8' => '八枚落ち',
|
182
|
+
'10' => '十枚落ち',
|
183
|
+
'OTHER' => 'その他'
|
184
|
+
}[preset]
|
185
|
+
end
|
186
186
|
end
|
187
187
|
end
|
188
188
|
end
|
data/lib/jkf/converter.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
module Jkf
|
2
|
-
# Define converter namespace
|
3
2
|
module Converter
|
4
|
-
# Convert error
|
5
3
|
class ConvertError < StandardError; end
|
6
4
|
end
|
7
5
|
end
|
8
6
|
|
9
|
-
require
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
require 'json'
|
8
|
+
require_relative 'converter/base'
|
9
|
+
require_relative 'converter/kifuable'
|
10
|
+
require_relative 'converter/kif'
|
11
|
+
require_relative 'converter/ki2'
|
12
|
+
require_relative 'converter/csa'
|