pgn 0.1.1 → 0.2.0
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/lib/pgn/move_calculator.rb +1 -1
- data/lib/pgn/parser.rb +59 -51
- data/lib/pgn/version.rb +1 -1
- data/spec/parser_spec.rb +65 -65
- data/spec/pgn_files/empty_variation_move.pgn +11 -0
- data/spec/pgn_files/nested_comments.pgn +4 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5c9a8ca0a6c732bd3ec99585b21929c1710976e
|
4
|
+
data.tar.gz: ff812fc594259ffa25370d3e35eed074c56a3c41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05e7faa858c9ee725416c0c87021ac2b3440407b41de732ce6049f446ede8736e039c7f0a8658cba5dced9c6d27a07ad50e0c176e04afbede60cb959c2d267a9
|
7
|
+
data.tar.gz: c5d008c3d2d579dd39b3ab76889cf697252d12eac55f5cab69665dd04b2cbc03227de4487325a476a45b1fe9a435e1fb41ad775b456fd5e5b5297fafc4624cbe
|
data/lib/pgn/move_calculator.rb
CHANGED
data/lib/pgn/parser.rb
CHANGED
@@ -5,46 +5,48 @@ module PGN
|
|
5
5
|
# context free grammar.
|
6
6
|
#
|
7
7
|
class Parser < Whittle::Parser
|
8
|
-
rule(:
|
8
|
+
rule(wsp: /\s+/).skip!
|
9
9
|
|
10
|
-
rule(
|
11
|
-
rule(
|
12
|
-
rule(
|
13
|
-
rule(
|
10
|
+
rule('[')
|
11
|
+
rule(']')
|
12
|
+
rule('(')
|
13
|
+
rule(')')
|
14
14
|
|
15
15
|
start(:pgn_database)
|
16
16
|
|
17
17
|
rule(:pgn_database) do |r|
|
18
18
|
r[].as { [] }
|
19
|
-
r[:pgn_game, :pgn_database].as {|game, database| database << game }
|
19
|
+
r[:pgn_game, :pgn_database].as { |game, database| database << game }
|
20
20
|
end
|
21
21
|
|
22
22
|
rule(:pgn_game) do |r|
|
23
|
-
r[:tag_section, :movetext_section].as
|
23
|
+
r[:tag_section, :movetext_section].as do |tags, moves|
|
24
|
+
{ tags: tags, result: moves.pop, moves: moves }
|
25
|
+
end
|
24
26
|
end
|
25
27
|
|
26
28
|
rule(:tag_section) do |r|
|
27
|
-
r[:tag_pair, :tag_section].as {|pair, section| section.merge(pair) }
|
29
|
+
r[:tag_pair, :tag_section].as { |pair, section| section.merge(pair) }
|
28
30
|
r[:tag_pair]
|
29
31
|
end
|
30
32
|
|
31
33
|
rule(:tag_pair) do |r|
|
32
|
-
r[
|
34
|
+
r['[', :tag_name, :tag_value, ']'].as { |_, a, b, _| { a => b } }
|
33
35
|
end
|
34
36
|
|
35
37
|
rule(:tag_value) do |r|
|
36
|
-
r[:string].as {|value| value[1...-1] }
|
38
|
+
r[:string].as { |value| value[1...-1] }
|
37
39
|
end
|
38
40
|
|
39
41
|
rule(:movetext_section) do |r|
|
40
|
-
r[:element_sequence, :game_termination].as {|a, b| a.reverse << b }
|
42
|
+
r[:element_sequence, :game_termination].as { |a, b| a.reverse << b }
|
41
43
|
end
|
42
44
|
|
43
45
|
rule(:element_sequence) do |r|
|
44
|
-
r[:element, :element_sequence].as
|
46
|
+
r[:element, :element_sequence].as do |element, sequence|
|
47
|
+
element.nil? ? sequence : sequence << element
|
48
|
+
end
|
45
49
|
r[].as { [] }
|
46
|
-
#r[:recursive_variation, :element_sequence]
|
47
|
-
#r[:recursive_variation]
|
48
50
|
end
|
49
51
|
|
50
52
|
rule(:element) do |r|
|
@@ -56,34 +58,36 @@ module PGN
|
|
56
58
|
end
|
57
59
|
r[:comment].as { nil }
|
58
60
|
end
|
59
|
-
|
61
|
+
|
60
62
|
rule(:san_move_annotated) do |r|
|
61
63
|
r[:san_move].as { |move| MoveText.new(move) }
|
62
|
-
r[:san_move, :comment].as
|
63
|
-
|
64
|
-
|
64
|
+
r[:san_move, :comment].as do |move, comment|
|
65
|
+
MoveText.new(move, nil, comment)
|
66
|
+
end
|
67
|
+
r[:san_move, :numeric_annotation_glyph].as do |move, annotation|
|
68
|
+
MoveText.new(move, annotation)
|
69
|
+
end
|
70
|
+
r[:san_move, :numeric_annotation_glyph, :comment].as do |move, annotation, comment|
|
71
|
+
MoveText.new(move, annotation, comment)
|
72
|
+
end
|
73
|
+
r[:san_move, :comment, :numeric_annotation_glyph].as do |move, comment, annotation|
|
74
|
+
MoveText.new(move, annotation, comment)
|
75
|
+
end
|
65
76
|
end
|
66
77
|
|
67
78
|
rule(:variation_list) do |r|
|
68
|
-
|
69
|
-
|
79
|
+
r[:variation, :variation_list].as do |variation, sequence|
|
80
|
+
sequence << variation
|
81
|
+
end
|
82
|
+
r[:variation].as { |v| [v] }
|
70
83
|
end
|
71
|
-
|
84
|
+
|
72
85
|
rule(:variation) do |r|
|
73
|
-
r[
|
86
|
+
r['(', :element_sequence, ')'].as { |_, sequence, _| sequence }
|
74
87
|
end
|
75
88
|
|
76
|
-
# rule(:variation_sequence) do |r|
|
77
|
-
# r[:element, :variation_sequence]
|
78
|
-
# r[].as { [] }
|
79
|
-
# end
|
80
|
-
|
81
|
-
#rule(:recursive_variation) do |r|
|
82
|
-
#r["(", :element_sequence, ")"]
|
83
|
-
#end
|
84
|
-
|
85
89
|
rule(
|
86
|
-
:
|
90
|
+
string: /
|
87
91
|
" # beginning of string
|
88
92
|
(
|
89
93
|
[[:print:]&&[^\\"]] | # printing characters except quote and backslash
|
@@ -91,25 +95,28 @@ module PGN
|
|
91
95
|
\\" # escaped quotation marks
|
92
96
|
)* # zero or more of the above
|
93
97
|
" # end of string
|
94
|
-
|
98
|
+
/x
|
95
99
|
)
|
96
100
|
|
97
101
|
rule(
|
98
|
-
:
|
99
|
-
\{ # beginning of comment
|
102
|
+
comment: /
|
100
103
|
(
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
104
|
+
\{ # beginning of comment
|
105
|
+
(
|
106
|
+
[[:print:]&&[^\\\{\}]] | # printing characters except brace and backslash
|
107
|
+
\n |
|
108
|
+
\\\\ | # escaped backslashes
|
109
|
+
\\\{|\\\} | # escaped braces
|
110
|
+
\n | # newlines
|
111
|
+
\g<1> # recursive
|
112
|
+
)* # zero or more of the above
|
113
|
+
\} # end of comment
|
114
|
+
)
|
115
|
+
/x
|
109
116
|
)
|
110
117
|
|
111
118
|
rule(
|
112
|
-
:
|
119
|
+
game_termination: %r{
|
113
120
|
1-0 | # white wins
|
114
121
|
0-1 | # black wins
|
115
122
|
1\/2-1\/2 | # draw
|
@@ -118,14 +125,15 @@ module PGN
|
|
118
125
|
)
|
119
126
|
|
120
127
|
rule(
|
121
|
-
:
|
128
|
+
move_number_indication: /
|
122
129
|
[[:digit:]]+\.* # one or more digits followed by zero or more periods
|
123
|
-
|
130
|
+
/x
|
124
131
|
)
|
125
132
|
|
126
133
|
rule(
|
127
|
-
:
|
134
|
+
san_move: %r{
|
128
135
|
(
|
136
|
+
-- | # "don't care" move (used in variations)
|
129
137
|
[O0](-[O0]){1,2} | # castling (O-O, O-O-O)
|
130
138
|
[a-h][1-8] | # pawn moves (e4, d7)
|
131
139
|
[BKNQR][a-h1-8]?x?[a-h][1-8] | # major piece moves w/ optional specifier
|
@@ -144,16 +152,16 @@ module PGN
|
|
144
152
|
)
|
145
153
|
|
146
154
|
rule(
|
147
|
-
:
|
155
|
+
tag_name: /
|
148
156
|
[A-Za-z0-9_]+ # letters, digits and underscores only
|
149
|
-
|
157
|
+
/x
|
150
158
|
)
|
151
159
|
|
152
160
|
rule(
|
153
|
-
:
|
161
|
+
numeric_annotation_glyph: /
|
154
162
|
\$\d+ | # dollar sign followed by an integer from 0 to 255
|
155
163
|
[\?!][\?!]? # support the most used annotations directly
|
156
|
-
|
164
|
+
/x
|
157
165
|
)
|
158
166
|
end
|
159
167
|
end
|
data/lib/pgn/version.rb
CHANGED
data/spec/parser_spec.rb
CHANGED
@@ -1,84 +1,84 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe PGN do
|
4
|
-
describe
|
5
|
-
it
|
6
|
-
games = PGN.parse(File.read(
|
4
|
+
describe '.parse' do
|
5
|
+
it 'should return a list of games' do
|
6
|
+
games = PGN.parse(File.read('./examples/immortal_game.pgn'))
|
7
7
|
games.length.should == 1
|
8
8
|
game = games.first
|
9
|
-
game.result.should ==
|
10
|
-
game.tags[
|
11
|
-
game.moves.last.should ==
|
9
|
+
game.result.should == '1-0'
|
10
|
+
game.tags['White'].should == 'Adolf Anderssen'
|
11
|
+
game.moves.last.should == 'Be7#'
|
12
12
|
end
|
13
|
-
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
game.result.should == "*"
|
22
|
-
game.moves.last.should == "O-O-O"
|
23
|
-
end
|
14
|
+
it 'should handle alternate castling notation' do
|
15
|
+
games = PGN.parse(File.read('./spec/pgn_files/alternate_castling.pgn'))
|
16
|
+
game = games.first
|
17
|
+
game.tags['White'].should == 'Somebody'
|
18
|
+
game.result.should == '*'
|
19
|
+
game.moves.last.should == 'O-O-O'
|
24
20
|
end
|
25
|
-
end
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
game.result.should == "0-1"
|
34
|
-
game.moves.last.should == "Qh4#"
|
35
|
-
end
|
22
|
+
it 'should handle annotations' do
|
23
|
+
games = PGN.parse(File.read('./spec/pgn_files/annotations.pgn'))
|
24
|
+
games.each do |game|
|
25
|
+
game.tags['White'].should == 'Fool'
|
26
|
+
game.result.should == '0-1'
|
27
|
+
game.moves.last.should == 'Qh4#'
|
36
28
|
end
|
37
29
|
end
|
38
30
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
31
|
+
it 'should handle comments' do
|
32
|
+
games = PGN.parse(File.read('./spec/pgn_files/comments.pgn'))
|
33
|
+
game = games.first
|
34
|
+
game.tags['White'].should == 'Scholar'
|
35
|
+
game.result.should == '1-0'
|
36
|
+
game.moves.last.should == 'Qxf7#'
|
37
|
+
end
|
47
38
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
39
|
+
it 'should handle multiline comments' do
|
40
|
+
games = PGN.parse(File.read('./spec/pgn_files/multiline_comments.pgn'))
|
41
|
+
game = games.first
|
42
|
+
game.tags['White'].should == 'Scholar'
|
43
|
+
game.result.should == '1-0'
|
44
|
+
game.moves.last.should == 'Qxf7#'
|
55
45
|
end
|
56
46
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
game.result.should == "*"
|
63
|
-
game.moves.last.should == "Nf6"
|
64
|
-
end
|
47
|
+
it 'should handle nested comments' do
|
48
|
+
games = PGN.parse(File.read('./spec/pgn_files/nested_comments.pgn'))
|
49
|
+
game = games.first
|
50
|
+
game.result.should == '*'
|
51
|
+
game.moves.last.should == 'Nf6'
|
65
52
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
53
|
+
|
54
|
+
it 'should handle variations' do
|
55
|
+
games = PGN.parse(File.read('./spec/pgn_files/variations.pgn'))
|
56
|
+
game = games.first
|
57
|
+
game.tags['Black'].should == 'Petrov'
|
58
|
+
game.result.should == '*'
|
59
|
+
game.moves.last.should == 'Nf6'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should handle empty variation moves' do
|
63
|
+
games = PGN.parse(File.read('./spec/pgn_files/empty_variation_move.pgn'))
|
64
|
+
game = games.first
|
65
|
+
game.result.should == '*'
|
66
|
+
game.moves.last.should == 'Ng5'
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should handle complex files' do
|
70
|
+
games = PGN.parse(File.read('./spec/pgn_files/test.pgn'))
|
71
|
+
game = games.first
|
72
|
+
game.tags['Black'].should == 'Gelfand, Boris'
|
73
|
+
game.result.should == '1-0'
|
74
|
+
game.moves[13].should == 'Nfd7'
|
75
|
+
game.moves[34].should == 'f3'
|
76
|
+
game.moves[35].annotation.should == '$6'
|
77
|
+
game.moves[35].comment.should == "{Gelfand\ndecide tomar medidas.}"
|
78
|
+
game.moves[35].variations.size.should == 1
|
79
|
+
variation = game.moves[35].variations[0]
|
80
|
+
variation.size.should == 2
|
81
|
+
variation[0].should == 'Nxf3'
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
[White "Somebody"]
|
2
|
+
[Black "Somebody Else"]
|
3
|
+
|
4
|
+
1. d4 e6 2. c4 d5 3. Nc3 c6 4. Nf3 Nf6 5. Bg5 Nbd7 6. e3 Qa5 7. a3 (7. cxd5
|
5
|
+
Nxd5 8. Rc1 {Analyzer's primary variation: 8... Bb4 9. Qd2 e5 10. e4 Nxc3 11.
|
6
|
+
bxc3 Ba3 12. Rb1 h6 13. Be3 O-O 14. Bc4 exd4 15. cxd4 Qxd2+ 16. Nxd2 a5 17. O-O
|
7
|
+
b5 18. Be2 Rd8 19. d5 cxd5 20. exd5 Nc5 21. Rxb5 Rxd5 {+0.30/21} }) 7... Ne4 8.
|
8
|
+
Rc1 Nxg5 9. Nxg5 dxc4 10. Bxc4 Qxg5 11. O-O Nf6 12. Qf3 Qg4 13. Qxg4 Nxg4 14.
|
9
|
+
Ne4 Be7 15. b4 O-O 16. Rce1 Nf6 17. Ng5 ({Better is} 17. Nxf6+ gxf6 {-4.08/31}
|
10
|
+
(17... Bxf6) (17... gxf6)) (17. Nxf6+) 17... h6 18. Nf3 Ne4 ({Treatening} 18...
|
11
|
+
-- 19. Ra1 b5 {-4.05/31}) 19. Bd3 Ng5 *
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pgn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stacey Touset
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: whittle
|
@@ -97,7 +97,9 @@ files:
|
|
97
97
|
- spec/pgn_files/alternate_castling.pgn
|
98
98
|
- spec/pgn_files/annotations.pgn
|
99
99
|
- spec/pgn_files/comments.pgn
|
100
|
+
- spec/pgn_files/empty_variation_move.pgn
|
100
101
|
- spec/pgn_files/multiline_comments.pgn
|
102
|
+
- spec/pgn_files/nested_comments.pgn
|
101
103
|
- spec/pgn_files/test.pgn
|
102
104
|
- spec/pgn_files/variations.pgn
|
103
105
|
- spec/position_spec.rb
|
@@ -133,7 +135,9 @@ test_files:
|
|
133
135
|
- spec/pgn_files/alternate_castling.pgn
|
134
136
|
- spec/pgn_files/annotations.pgn
|
135
137
|
- spec/pgn_files/comments.pgn
|
138
|
+
- spec/pgn_files/empty_variation_move.pgn
|
136
139
|
- spec/pgn_files/multiline_comments.pgn
|
140
|
+
- spec/pgn_files/nested_comments.pgn
|
137
141
|
- spec/pgn_files/test.pgn
|
138
142
|
- spec/pgn_files/variations.pgn
|
139
143
|
- spec/position_spec.rb
|