pgn_parser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 901aa0e18bad4ee0e401dae4106dd7acb6bf0c27
4
+ data.tar.gz: 850dd2f3e77f87983bab5cb3ea456492fc46d9f6
5
+ SHA512:
6
+ metadata.gz: acbf926115041dcca8a14ec17cdccf38b866a40a1224df5b0210dc9e13e224fab859449234b8a32e4a60d2e09c2cdc1c57986c925eab643835577e88469e046b
7
+ data.tar.gz: f10e87e9eadb25a1049a2402a463565c92a48a8093734702d71e4c5c27b9618aa2761b146817f2d195cdd1ef5d4141bb259a3919739d2e0b649e78f19badce1c
@@ -0,0 +1,18 @@
1
+ .idea
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/11.pgn ADDED
@@ -0,0 +1,15 @@
1
+ [Event "Eurotel Trophy"]
2
+ [Site "Prague CZE"]
3
+ [Date "2002.04.30"]
4
+ [Round "2.1"]
5
+ [White "Polgar, Judit"]
6
+ [Black "Kasparov, Garri"]
7
+ [Result "0-1"]
8
+ [ECO "B33"]
9
+ [WhiteElo "2677"]
10
+ [BlackElo "2838"]
11
+ [EventDate "2002.04.28"]
12
+ [Annotator "Hathaway, Mark"]
13
+
14
+ 1. e4 c5 2. Nf3 Nc6 { In some cases this restrains Pe4 sufficient to allow ...Ng8-f6, but in some other cases Black needs more control of e5. } ( 2...d6 ) 3. d4 cxd4 4. Nxd4 Nf6 5. Nc3 { Now Black has to decide how to prevent Nxc6 and e4-e5. To that end Black has played ...d6 (Scheveningen) , ...e5 (Sveshnikov) and ...Qb6 (Benko) . All have good qualities. } 5...e5 6. Ndb5 { If White doesn't try to exploit the weakness at d6 then his pieces become too passive. } 6...d6 7. Bg5 a6 8. Na3 b5 { threatening ...b4 and preventing Na3-c4-e3 } 9. Bxf6 ( 9. Nd5 ) 9...gxf6 { It seems odd that Black would allow such a pawn structure, but he can't easily be prevented from ...f6-f5xe4 to give him an advantage in central pawns. That the position has many weaknesses gives White room to develop and fight for a win. } 10. Nd5 Bg7 11. c3 f5 12. Bd3 ( 12. exf5 Bxf5 13. Qf3 Be6 ) ( 12. Nc2 $6 fxe4 { and Nc2 doesn't exactly fit into any part of the board. With Black ready to play ...f5 the king-side is safe and Nd5 occupies the most useful central square, so that leaves b4. But, moving Nc2-b4 doesn't serve much purpose at this point; only after a2-a4 softens up the queen-side would Nc2-b4 possibly become useful. } ) 12...Ne7 { After Ne7 it appears Black might prefer ...Bc8-b7, as is played in many Sicilian Defense games, to cut through the center of the board and to aim at Pg2, where White's king might soon reside. } ( 12...Be6 ) 13. Nxe7 Qxe7 14. O-O ( 14. exf5 { is consistent with the threat established by Bf1-d3, but it's unclear whether White can hold the pawn because of Black's mobile central pawns and generally good piece } 14...d5 ( 14...e4 $2 15. Bc2 Bxf5 $4 ( 15...O-O ) 16. Qd5 ) 15. Qh5 ) 14...O-O 15. Nc2 { Seeing that the piece needed to be improved and that d5 would be a good spot for it, she moves it now. } 15...f4 { It might be a little unusual to use this move when Bg7 blocks the g-file, but Kasparov has played the King's Indian Defense many times and understands how to handle the weak d5 and the open g-file. In this case he might not aim directly for an attack on Pg2 and Kg1. Instead he might just be preventing Ne3 or exf5, so he can return to ...Bc8-b7 with either ...f5 or ...d5 soon to follow. Kasparov is very much about achieving good overall piece activity. } 16. a4 bxa4 17. Rxa4 Qg5 { threatening ...Bh3 to checkmate with ...Qxg2# or to force g2-g3, so ...Bh3xf1 will win material and continue to weaken Kg1 } 18. f3 Bf6 19. Bc4 Kh8 { These moves are very much taken from a King's Indian Defense player's repertoire. } 20. Rf2 $2 ( 20. Qd2 Rg8 21. Rfa1 { coordinates the pieces a little better. Rf2 isn't good. } ) 20...Be7 21. Nb4 d5 $5 { Kasparov is always on the lookout for tactical shots which he can use to win or confuse the opponent. } 22. Bxd5 Bc5 { offering Ra8 is just the sort of audacious thing Kasparov is noted for. But, don't think it's just a bluff. He's calculated variations to determine if his compensation is sufficient. } 23. Bxa8 Rg8 24. Kf1 ( { I'm not quite sure what Kasparov had in mind after the obvious defensive move. } 24. Qf1 Bh3 25. Kh1 Bxf2 26. Qxf2 Bxg2+ 27. Kg1 Bh3+ 28. Kh1 Qd8 ( 28...Rxa8 29. Rxa6 Rd8 ( 29...Qd8 30. Rxa8 Qxa8 31. Qg1 f5 ) 30. Nd5 ) 29. Bd5 ) 24...Bxf2 25. Ke2 ( 25. Kxf2 Qxg2+ 26. Ke1 Qxb2 { and the threat of ...Rg1# is very strong } ) 25...Qxg2 26. Kd3 { Material is nominally equal, but the exposed Kd3 could be a problem. } 26...Be3 27. Bd5 Qxh2 28. Kc4 Rg1 29. Qd3 Bh3 { It's odd to see all these Black pieces cramped into the corner, but Black threatens ...Bf1 to win a queen! } 30. Kb3 Bf1 31. Qc2 Qxc2+ { It's a little surprising, after what had gone before, to see simplification; Black has a passed Ph7 and wants to advance it, so the h-file had to be cleared. } ( 31...Rg2 32. Qb1 Bb5 33. Ra5 { and White is somewhat coordinated and threatening Nxa6 to gain more space } ) 32. Kxc2 Kg7 33. c4 Be2 { I suppose he's decided he can't just run Ph7 forward because Pc4 is also in good shape to advance and White's pieces might suddenly swarm Kg7. So, he has to continue the fight and await further simplifications when his king will be safer, then the pawns can advance. } 34. Rxa6 Bd1+ { prevents White's king from running to a2 (via b3) to get away from checks } ( 34...Rc1+ 35. Kb3 Bd1+ 36. Ka2 ) 35. Kd3 Bxf3 36. Nc6 Rd1+ 37. Kc3 Bg4 { clearing the way for ...f4-f3 and perhaps teasing Polgar into Nxe5?? Maybe Black has a won position anyway. White's pawns can't advance yet and with Be3 preventing Ra6-a7 there's no attack on Kg7. This is the moment for pawn advances. } 38. Nxe5 $4 Bd4+ 39. Kc2 Bxe5 $19 40. Ra7 Re1 41. Rxf7+ Kg6 42. c5 Re2+ 43. Kc1 f3 44. Bc4 ( 44. b4 Rxe4 ) 44...Rxe4 45. Bd5 Rf4
15
+ 0-1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pgn_parser.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Joseph Emmanuel Dayo
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # PgnParser
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'pgn_parser'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install pgn_parser
18
+
19
+ ## Usage
20
+
21
+ Pretty straightforward.
22
+
23
+ ```ruby
24
+ require 'pgn_parser'
25
+ require 'json'
26
+
27
+ @pgn_data = File.read(File.join(File.dirname(__FILE__),'11.pgn'))
28
+
29
+ pgn = PGN::Parser.new(@pgn_data)
30
+
31
+ puts pgn.parse.to_json
32
+ ```
33
+
34
+ ## Contributing
35
+
36
+ 1. Fork it ( http://github.com/<my-github-username>/pgn_parser/fork )
37
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
38
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
39
+ 4. Push to the branch (`git push origin my-new-feature`)
40
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,294 @@
1
+ require "pgn_parser/version"
2
+
3
+ module PGN
4
+ class Parser
5
+
6
+ def initialize(pgn_content)
7
+ @pgn_content = pgn_content
8
+ @headers = []
9
+ @movelist = []
10
+ @game_attributes = {}
11
+ end
12
+
13
+ def headers
14
+ @headers
15
+ end
16
+
17
+ def attributes
18
+ @game_attributes
19
+ end
20
+
21
+ def movelist
22
+ @movelist
23
+ end
24
+
25
+ def parse
26
+ current_index = 0
27
+ state = :initial
28
+ buffer = ''
29
+ while (current_index < @pgn_content.size)
30
+ current_char = @pgn_content[current_index]
31
+ current_index+=1
32
+ if state==:initial
33
+ if current_char=='['
34
+ state = :start_parse_header
35
+ next
36
+ elsif (current_char == ' ' || current_char == "\n" || current_char == "\r")
37
+ next
38
+ else
39
+ break
40
+ end
41
+ end
42
+
43
+ if state==:start_parse_header
44
+ if current_char == ']'
45
+ state = :initial
46
+ hd = parse_header(buffer)
47
+ @headers << hd
48
+ @game_attributes[hd[:type]] = hd[:value]
49
+ buffer = ''
50
+ next
51
+ else
52
+ buffer << current_char
53
+ next
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ @movelist = parse_moves(@pgn_content,current_index - 1)[0]
60
+ {moves: @movelist}.merge(@game_attributes)
61
+ end
62
+
63
+ def parse_moves(content, current_index, initial_state = :parse_moves)
64
+ state = initial_state
65
+ buffer = ''
66
+ state_before = nil
67
+ current_move = {}
68
+ current_alternative = {}
69
+ movelist = []
70
+ while (current_index < content.size)
71
+ current_char = content[current_index]
72
+ current_index+=1
73
+ if state == :parse_moves
74
+ if current_char == ' '
75
+ next
76
+ elsif current_char == '('
77
+ x = nil
78
+ x, current_index = parse_moves(content, current_index)
79
+ elsif current_char == ')'
80
+ current_index+=1
81
+ break
82
+ elsif current_char == '{'
83
+ state = :start_parse_comment_section_paren
84
+ next
85
+ elsif current_char >= '0' && current_char <= '9'
86
+ state =:parse_move_text_number
87
+ buffer << current_char
88
+ next
89
+ end
90
+ end
91
+
92
+ if state == :parse_move_text_number
93
+ if current_char == '.'
94
+ current_move[:num] = buffer.to_i
95
+ buffer = ''
96
+ state = :parse_move_first_position
97
+ next
98
+ else
99
+ buffer << current_char
100
+ next
101
+ end
102
+ end
103
+
104
+ if state == :parse_move_first_position
105
+ if (current_char == ' ')
106
+ next
107
+ elsif (current_char == '.')
108
+ state = :continuation_indicator
109
+ current_move[:cont] = true
110
+ next
111
+ else
112
+ buffer << current_char
113
+ state = :begin_parse_move_first_position
114
+ next
115
+ end
116
+ end
117
+
118
+ if state == :continuation_indicator
119
+ if (current_char == '.' )
120
+ next
121
+ elsif (current_char == ' ')
122
+ state = :parse_move_second_position
123
+ next
124
+ else
125
+ buffer << current_char
126
+ state = :parse_move_second_position
127
+ end
128
+ end
129
+
130
+ if state == :begin_parse_move_first_position
131
+ if (current_char == ' ')
132
+ current_move[:w] = buffer
133
+ buffer = ''
134
+ state = :parse_move_second_position
135
+ next
136
+ elsif (current_char == "\n")
137
+ break
138
+ else
139
+ buffer << current_char
140
+ next
141
+ end
142
+ end
143
+
144
+ if state == :parse_move_second_position
145
+ if (current_char == ' ')
146
+ next
147
+ elsif (current_char == ')')
148
+ current_index+=1
149
+ break
150
+ elsif (current_char == '$')
151
+ buf = ''
152
+ while (content[current_index] >='0') && (content[current_index]<='9') && (current_index < content.size)
153
+ buf << content[current_index]
154
+ current_index+=1
155
+ end
156
+ current_move[:w_nag] = buf
157
+ next
158
+ elsif (current_char == '{')
159
+ state = :start_parse_comment_section_paren
160
+ next
161
+ elsif (current_char == '(')
162
+ current_move[:w_alt] = [] if current_move[:w_alt].nil?
163
+ alternate, current_index = parse_moves(content, current_index + 1)
164
+ current_move[:w_alt] << alternate
165
+ next
166
+ elsif (current_char >= '0' && current_char <= '9')
167
+ buffer = ''
168
+ movelist << current_move.dup
169
+ current_move = {}
170
+ buffer << current_char
171
+ state = :parse_move_text_number
172
+ else
173
+ buffer << current_char
174
+ state = :begin_parse_move_second_position
175
+ next
176
+ end
177
+ end
178
+
179
+ if state == :begin_parse_move_second_position
180
+ if (current_char == ' ')
181
+ current_move[:b] = buffer
182
+ buffer = ''
183
+ state = :start_comment_section
184
+ next
185
+ elsif (current_char == "\n")
186
+ current_move[:b] = buffer
187
+ movelist << current_move.dup
188
+ break
189
+ else
190
+ buffer << current_char
191
+ next
192
+ end
193
+ end
194
+
195
+ if state == :start_comment_section
196
+ if (current_char == ' ')
197
+ next
198
+ elsif (current_char == '$')
199
+ buf = ''
200
+ while (content[current_index] >='0') && (content[current_index]<='9') && (current_index < content.size)
201
+ buf << content[current_index]
202
+ current_index+=1
203
+ end
204
+ current_move[:b_nag] = buf
205
+ next
206
+ elsif (current_char == '{')
207
+ state = :start_parse_comment_section_paren
208
+ next
209
+ elsif (current_char == ')')
210
+ current_index+=1
211
+ break
212
+ elsif (current_char == '(')
213
+ current_move[:b_alt] = [] if current_move[:b_alt].nil?
214
+ alternate, current_index = parse_moves(content, current_index)
215
+ current_move[:b_alt] << alternate
216
+ next
217
+ elsif current_char >= '0' && current_char <= '9'
218
+ buffer = ''
219
+ movelist << current_move.dup
220
+ current_move = {}
221
+ buffer << current_char
222
+ state =:parse_move_text_number
223
+ next
224
+ end
225
+ end
226
+
227
+ if state == :start_parse_comment_section_paren
228
+ if (current_char == '}')
229
+ current_move[:comment] = buffer.dup
230
+ buffer = ''
231
+ movelist << current_move.dup
232
+ current_move = {}
233
+ state = :parse_moves
234
+ next
235
+ else
236
+ buffer << current_char
237
+ next
238
+ end
239
+ end
240
+ end
241
+
242
+ if state == :start_comment_section
243
+ movelist << current_move.dup
244
+ elsif state == :begin_parse_move_first_position
245
+ current_move[:w] = buffer
246
+ movelist << current_move.dup
247
+ elsif state == :start_parse_comment_section
248
+ movelist << current_move.dup
249
+ elsif state == :parse_move_second_position
250
+ movelist << current_move.dup
251
+ end
252
+ [movelist, current_index]
253
+ end
254
+
255
+ def parse_header(header)
256
+ event_type = ""
257
+ event_value = ""
258
+ state = :parse_type
259
+ current_index = 0
260
+ buffer = ''
261
+ while (current_index < header.size)
262
+ current_char = header[current_index]
263
+ current_index+=1
264
+ if state==:parse_type
265
+ if current_char == ' '
266
+ event_type = buffer.dup
267
+ buffer = ''
268
+ state=:start_parse_value
269
+ next
270
+ else
271
+ buffer << current_char
272
+ next
273
+ end
274
+ elsif state==:start_parse_value
275
+ if current_char=='"'
276
+ state=:parse_value
277
+ next
278
+ else
279
+ next
280
+ end
281
+ elsif state==:parse_value
282
+ if current_char=='"'
283
+ event_value = buffer.dup
284
+ buffer = ''
285
+ else
286
+ buffer << current_char
287
+ end
288
+
289
+ end
290
+ end
291
+ {type: event_type, value: event_value}
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,3 @@
1
+ module PgnParser
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pgn_parser/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pgn_parser"
8
+ spec.version = PgnParser::VERSION
9
+ spec.authors = ["Joseph Emmanuel Dayo"]
10
+ spec.email = ["joseph.dayo@gmail.com"]
11
+ spec.summary = %q{A pure ruby parser for chess pgn files.}
12
+ spec.description = %q{A pure ruby parser for chess pgn files. Read pgn files an allow for a format easily readable for chess computers}
13
+ spec.homepage = "https://github.com/jedld/chess_pgn_parser.git"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pgn_parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joseph Emmanuel Dayo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A pure ruby parser for chess pgn files. Read pgn files an allow for a
42
+ format easily readable for chess computers
43
+ email:
44
+ - joseph.dayo@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - 11.pgn
51
+ - Gemfile
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - lib/pgn_parser.rb
56
+ - lib/pgn_parser/version.rb
57
+ - pgn_parser.gemspec
58
+ homepage: https://github.com/jedld/chess_pgn_parser.git
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.2.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A pure ruby parser for chess pgn files.
82
+ test_files: []
83
+ has_rdoc: