jkf 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/bin/console
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'jkf'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
9
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
if require
|
10
|
+
if require 'pry'
|
11
11
|
Pry.start
|
12
12
|
else
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
15
15
|
end
|
data/jkf.gemspec
CHANGED
@@ -1,24 +1,23 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require 'jkf/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'jkf'
|
8
7
|
spec.version = Jkf::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
8
|
+
spec.authors = ['iyuuya']
|
9
|
+
spec.email = ['i.yuuya@gmail.com']
|
11
10
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
11
|
+
spec.summary = 'jkf/csa/kif/ki2 parser and converter'
|
12
|
+
spec.description = 'converter/parser of records of shogi'
|
13
|
+
spec.homepage = 'https://github.com/iyuuya/jkf'
|
14
|
+
spec.license = 'MIT'
|
16
15
|
|
17
|
-
spec.required_ruby_version = '>=
|
16
|
+
spec.required_ruby_version = '>= 3.0'
|
18
17
|
|
19
18
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
-
spec.bindir =
|
19
|
+
spec.bindir = 'exe'
|
21
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
-
spec.require_paths = [
|
21
|
+
spec.require_paths = ['lib']
|
23
22
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
24
23
|
end
|
data/lib/jkf/converter/base.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
-
module Jkf
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
module Jkf
|
2
|
+
module Converter
|
3
|
+
# Base of Converter
|
4
|
+
class Base
|
5
|
+
# start convert
|
6
|
+
#
|
7
|
+
# @param [String, Hash] jkf
|
8
|
+
# @return [String] kif or ki2 or csa text
|
9
|
+
def convert(jkf)
|
10
|
+
jkf = jkf.is_a?(Hash) ? jkf : JSON.parse(jkf)
|
11
|
+
convert_root(jkf)
|
12
|
+
end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/jkf/converter/csa.rb
CHANGED
@@ -1,174 +1,165 @@
|
|
1
|
-
module Jkf
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def convert_information(header)
|
17
|
-
result = ""
|
18
|
-
if header["先手"] || header["下手"]
|
19
|
-
result += "N+" + (header.delete("先手") || header.delete("下手") || "") + "\n"
|
20
|
-
end
|
21
|
-
if header["後手"] || header["上手"]
|
22
|
-
result += "N-" + (header.delete("後手") || header.delete("上手") || "") + "\n"
|
1
|
+
module Jkf
|
2
|
+
module Converter
|
3
|
+
# CSA v2.2 Converter
|
4
|
+
class Csa < Base
|
5
|
+
VERSION = '2.2'.freeze
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def convert_root(jkf)
|
10
|
+
result = version
|
11
|
+
result += convert_information(jkf['header']) if jkf['header']
|
12
|
+
result += convert_initial(jkf['initial']) if jkf['initial']
|
13
|
+
result += convert_moves(jkf['moves']) if jkf['moves']
|
14
|
+
result
|
23
15
|
end
|
24
|
-
header.each { |(k, v)| result += "$#{csa_header_key(k)}:#{v}\n" }
|
25
|
-
result
|
26
|
-
end
|
27
16
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
result += convert_hands(data["hands"], 0)
|
39
|
-
result += convert_hands(data["hands"], 1)
|
17
|
+
def convert_information(header)
|
18
|
+
result = ''
|
19
|
+
if header['先手'] || header['下手']
|
20
|
+
result += 'N+' + (header.delete('先手') || header.delete('下手') || '') + "\n"
|
21
|
+
end
|
22
|
+
if header['後手'] || header['上手']
|
23
|
+
result += 'N-' + (header.delete('後手') || header.delete('上手') || '') + "\n"
|
24
|
+
end
|
25
|
+
header.each { |(k, v)| result += "$#{csa_header_key(k)}:#{v}\n" }
|
26
|
+
result
|
40
27
|
end
|
41
|
-
result += csa_color(data["color"]) + "\n" if data["color"]
|
42
|
-
result
|
43
|
-
end
|
44
28
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
29
|
+
def convert_initial(initial)
|
30
|
+
result = ''
|
31
|
+
data = initial['data'] || {}
|
32
|
+
result += if initial['preset'] == 'OTHER'
|
33
|
+
convert_board(data['board'])
|
34
|
+
else
|
35
|
+
convert_preset(initial['preset'])
|
36
|
+
end
|
37
|
+
# 持駒
|
38
|
+
if data['hands']
|
39
|
+
result += convert_hands(data['hands'], 0)
|
40
|
+
result += convert_hands(data['hands'], 1)
|
41
|
+
end
|
42
|
+
result += csa_color(data['color']) + "\n" if data['color']
|
43
|
+
result
|
53
44
|
end
|
54
|
-
result
|
55
|
-
end
|
56
45
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
if move["time"]
|
65
|
-
result += "," + convert_time(move["time"])
|
66
|
-
elsif move["move"] || move["special"]
|
46
|
+
def convert_hands(hands, color)
|
47
|
+
result = ''
|
48
|
+
sum = 0
|
49
|
+
hands[color].each_value { |n| sum += n }
|
50
|
+
if sum > 0
|
51
|
+
result += "P#{csa_color(color)}"
|
52
|
+
hands[color].to_a.reverse_each { |(k, v)| v.times { result += "00#{k}" } }
|
67
53
|
result += "\n"
|
68
54
|
end
|
69
|
-
result
|
70
|
-
before_pos = move["move"]["to"] if move["move"] && move["move"]["to"]
|
55
|
+
result
|
71
56
|
end
|
72
|
-
result
|
73
|
-
end
|
74
57
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
58
|
+
def convert_moves(moves)
|
59
|
+
result = ''
|
60
|
+
before_pos = nil
|
61
|
+
moves.each do |move|
|
62
|
+
next if move == {}
|
63
|
+
result += convert_move(move['move'], before_pos) if move['move']
|
64
|
+
result += convert_special(move['special'], move['color']) if move['special']
|
65
|
+
if move['time']
|
66
|
+
result += ',' + convert_time(move['time'])
|
67
|
+
elsif move['move'] || move['special']
|
68
|
+
result += "\n"
|
69
|
+
end
|
70
|
+
result += convert_comments(move['comments']) if move['comments']
|
71
|
+
before_pos = move['move']['to'] if move['move'] && move['move']['to']
|
72
|
+
end
|
73
|
+
result
|
74
|
+
end
|
85
75
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
76
|
+
def convert_move(move, before_pos)
|
77
|
+
result = csa_color(move['color'])
|
78
|
+
result += move['from'] ? pos2str(move['from']) : '00'
|
79
|
+
result += if move['to']
|
80
|
+
pos2str(move['to']) + move['piece']
|
81
|
+
else
|
82
|
+
pos2str(before_pos) + move['piece']
|
83
|
+
end
|
84
|
+
result
|
85
|
+
end
|
91
86
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
87
|
+
def convert_special(special, color = nil)
|
88
|
+
result = '%'
|
89
|
+
result += csa_color(color) if color
|
90
|
+
result + special
|
91
|
+
end
|
96
92
|
|
97
|
-
|
98
|
-
|
99
|
-
|
93
|
+
def convert_time(time)
|
94
|
+
sec = (time['now']['m'] * 60) + time['now']['s']
|
95
|
+
"T#{sec}\n"
|
96
|
+
end
|
100
97
|
|
101
|
-
|
102
|
-
|
103
|
-
9.times do |y|
|
104
|
-
result += "P#{y + 1}"
|
105
|
-
9.times do |x|
|
106
|
-
piece = board[8 - x][y]
|
107
|
-
result += if piece == {}
|
108
|
-
" * "
|
109
|
-
else
|
110
|
-
csa_color(piece["color"]) + piece["kind"]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
result += "\n"
|
98
|
+
def convert_comments(comments)
|
99
|
+
comments.map { |comment| "'#{comment}" }.join("\n") + "\n"
|
114
100
|
end
|
115
|
-
result
|
116
|
-
end
|
117
101
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
"
|
131
|
-
when "HIKY" # 飛香落ち
|
132
|
-
"22HI11KY91KY"
|
133
|
-
when "2" # 二枚落ち
|
134
|
-
"82HI22KA"
|
135
|
-
when "3" # 三枚落ち
|
136
|
-
"82HI22KA91KY"
|
137
|
-
when "4" # 四枚落ち
|
138
|
-
"82HI22KA11KY91KY"
|
139
|
-
when "5" # 五枚落ち
|
140
|
-
"82HI22KA81KE11KY91KY"
|
141
|
-
when "5_L" # 左五枚落ち
|
142
|
-
"82HI22KA21KE11KY91KY"
|
143
|
-
when "6" # 六枚落ち
|
144
|
-
"82HI22KA21KE81KE11KY91KY"
|
145
|
-
when "8" # 八枚落ち
|
146
|
-
"82HI22KA31GI71GI21KE81KE11KY91KY"
|
147
|
-
when "10" # 十枚落ち
|
148
|
-
"82HI22KA41KI61KI31GI71GI21KE81KE11KY91KY"
|
102
|
+
def convert_board(board)
|
103
|
+
result = ''
|
104
|
+
9.times do |y|
|
105
|
+
result += "P#{y + 1}"
|
106
|
+
9.times do |x|
|
107
|
+
piece = board[8 - x][y]
|
108
|
+
result += if piece == {}
|
109
|
+
' * '
|
110
|
+
else
|
111
|
+
csa_color(piece['color']) + piece['kind']
|
112
|
+
end
|
113
|
+
end
|
114
|
+
result += "\n"
|
149
115
|
end
|
150
|
-
|
116
|
+
result
|
117
|
+
end
|
151
118
|
|
152
|
-
|
153
|
-
|
154
|
-
|
119
|
+
def convert_preset(preset)
|
120
|
+
'PI' + PRESET_NAME_TO_CSA_MAPPING[preset]
|
121
|
+
end
|
155
122
|
|
156
|
-
|
157
|
-
|
158
|
-
|
123
|
+
PRESET_NAME_TO_CSA_MAPPING = {
|
124
|
+
'HIRATE' => '', # 平手
|
125
|
+
'KY' => '11KY', # 香落ち
|
126
|
+
'KY_R' => '91KY', # 右香落ち
|
127
|
+
'KA' => '22KA', # 角落ち
|
128
|
+
'HI' => '82HI', # 飛車落ち
|
129
|
+
'HIKY' => '22HI11KY91KY', # 飛香落ち
|
130
|
+
'2' => '82HI22KA', # 二枚落ち
|
131
|
+
'3' => '82HI22KA91KY', # 三枚落ち
|
132
|
+
'4' => '82HI22KA11KY91KY', # 四枚落ち
|
133
|
+
'5' => '82HI22KA81KE11KY91KY', # 五枚落ち
|
134
|
+
'5_L' => '82HI22KA21KE11KY91KY', # 左五枚落ち
|
135
|
+
'6' => '82HI22KA21KE81KE11KY91KY', # 六枚落ち
|
136
|
+
'8' => '82HI22KA31GI71GI21KE81KE11KY91KY', # 八枚落ち
|
137
|
+
'10' => '82HI22KA41KI61KI31GI71GI21KE81KE11KY91KY' # 十枚落ち
|
138
|
+
}.freeze
|
139
|
+
|
140
|
+
private_constant :PRESET_NAME_TO_CSA_MAPPING
|
141
|
+
|
142
|
+
def csa_color(color)
|
143
|
+
color == 0 ? '+' : '-'
|
144
|
+
end
|
159
145
|
|
160
|
-
|
161
|
-
|
162
|
-
|
146
|
+
def pos2str(pos)
|
147
|
+
'%d%d' % [pos['x'], pos['y']]
|
148
|
+
end
|
149
|
+
|
150
|
+
def version
|
151
|
+
"V#{VERSION}\n"
|
152
|
+
end
|
163
153
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
154
|
+
def csa_header_key(key)
|
155
|
+
{
|
156
|
+
'棋戦' => 'EVENT',
|
157
|
+
'場所' => 'SITE',
|
158
|
+
'開始日時' => 'START_TIME',
|
159
|
+
'終了日時' => 'END_TIME',
|
160
|
+
'持ち時間' => 'TIME_LIMIT'
|
161
|
+
}[key] || key
|
162
|
+
end
|
172
163
|
end
|
173
164
|
end
|
174
165
|
end
|
data/lib/jkf/converter/ki2.rb
CHANGED
@@ -1,113 +1,115 @@
|
|
1
|
-
module Jkf
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Jkf
|
2
|
+
module Converter
|
3
|
+
# KI2 Converter
|
4
|
+
class Ki2 < Base
|
5
|
+
include Kifuable
|
5
6
|
|
6
|
-
|
7
|
+
protected
|
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
|
-
|
13
|
+
result = ''
|
14
|
+
result += convert_header(jkf['header']) if jkf['header']
|
15
|
+
result += convert_initial(jkf['initial']) if jkf['initial']
|
16
|
+
result += @header2.join + "\n"
|
17
|
+
result += convert_moves(jkf['moves']) if jkf['moves']
|
18
|
+
unless @forks.empty?
|
19
|
+
result += "\n"
|
20
|
+
result += @forks.join("\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
result
|
20
24
|
end
|
21
25
|
|
22
|
-
|
23
|
-
|
26
|
+
def convert_header(header)
|
27
|
+
header.map do |(key, value)|
|
28
|
+
result = add_header(key, value)
|
29
|
+
if key =~ /\A[先後上下]手\Z/
|
30
|
+
nil
|
31
|
+
else
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end.compact.join
|
35
|
+
end
|
24
36
|
|
25
|
-
|
26
|
-
|
27
|
-
result = add_header(key, value)
|
37
|
+
def add_header(key, value)
|
38
|
+
result = "#{key}:#{value}\n"
|
28
39
|
if key =~ /\A[先後上下]手\Z/
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def add_header(key, value)
|
37
|
-
result = "#{key}:#{value}\n"
|
38
|
-
if key =~ /\A[先後上下]手\Z/
|
39
|
-
if key =~ /[先下]/
|
40
|
-
@header2.unshift result
|
41
|
-
else
|
42
|
-
@header2 << result
|
40
|
+
if key =~ /[先下]/
|
41
|
+
@header2.unshift result
|
42
|
+
else
|
43
|
+
@header2 << result
|
44
|
+
end
|
43
45
|
end
|
46
|
+
result
|
44
47
|
end
|
45
|
-
result
|
46
|
-
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
49
|
+
def convert_moves(moves, idx = 0)
|
50
|
+
result = ''
|
51
|
+
j = 0
|
52
|
+
before_split = ''
|
53
|
+
moves.each_with_index do |move, i|
|
54
|
+
if move['special']
|
55
|
+
# first_board+speical分を引く(-2)
|
56
|
+
result += convert_special_and_split(move, i + idx - 2)
|
57
|
+
else
|
58
|
+
result += before_split
|
59
|
+
if move['move']
|
60
|
+
j += 1
|
61
|
+
result_move, before_split = convert_move_and_split(move, j)
|
62
|
+
result += result_move
|
63
|
+
end
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
if move['comments']
|
66
|
+
unless result.end_with?("\n") || result.empty?
|
67
|
+
result += "\n"
|
68
|
+
before_split = ''
|
69
|
+
j = 0
|
70
|
+
end
|
71
|
+
result += convert_comments(move['comments'])
|
69
72
|
end
|
70
|
-
result += convert_comments(move["comments"])
|
71
|
-
end
|
72
73
|
|
73
|
-
|
74
|
+
@forks.unshift convert_forks(move['forks'], i + idx) if move['forks']
|
75
|
+
end
|
74
76
|
end
|
77
|
+
result
|
75
78
|
end
|
76
|
-
result
|
77
|
-
end
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
def convert_special_and_split(hash, index)
|
81
|
+
"\n" + convert_special(hash['special'], index)
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
84
|
+
def convert_move_and_split(move, num)
|
85
|
+
result = convert_move(move['move'])
|
86
|
+
split = if num % 6 == 0
|
87
|
+
"\n"
|
88
|
+
else
|
89
|
+
result.size == 4 ? ' ' * 4 : ' ' * 2
|
90
|
+
end
|
91
|
+
[result, split]
|
92
|
+
end
|
92
93
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
def convert_move(move)
|
95
|
+
result = move['color'] == 0 ? '▲' : '△'
|
96
|
+
result += convert_piece_with_pos(move)
|
97
|
+
result += csa2relative(move['relative']) if move['relative']
|
98
|
+
result
|
99
|
+
end
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
101
|
+
def csa2relative(relative)
|
102
|
+
case relative
|
103
|
+
when 'L' then '左'
|
104
|
+
when 'C' then '直'
|
105
|
+
when 'R' then '右'
|
106
|
+
when 'U' then '上'
|
107
|
+
when 'M' then '寄'
|
108
|
+
when 'D' then '引'
|
109
|
+
when 'H' then '打'
|
110
|
+
else
|
111
|
+
''
|
112
|
+
end
|
111
113
|
end
|
112
114
|
end
|
113
115
|
end
|