pgn2 1.0.0 → 1.1.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.
@@ -8,7 +8,7 @@ require 'racc/parser.rb'
8
8
  module PGN
9
9
  class PgnParser < Racc::Parser
10
10
 
11
- module_eval(<<'...end pgn_parser.y/module_eval...', 'pgn_parser.y', 91)
11
+ module_eval(<<'...end pgn_parser.y/module_eval...', 'pgn_parser.y', 97)
12
12
 
13
13
  def parse(input)
14
14
  @lexer = PGN::Lexer.new(input)
@@ -19,29 +19,21 @@ module_eval(<<'...end pgn_parser.y/module_eval...', 'pgn_parser.y', 91)
19
19
  end
20
20
 
21
21
  def next_token
22
- t = @lexer.next_token
23
- return [false, false] unless t
24
- [translate_type(t.type), t.value]
22
+ pair = @lexer.next_token_pair
23
+ return [false, false] unless pair
24
+ type, value = pair
25
+ [translate_type(type), value]
25
26
  end
26
27
 
27
28
  private
28
29
 
30
+ # Punctuation tokens are racc'd by literal; everything else is racc'd by
31
+ # the upcased lexer symbol (e.g. :san_move -> :SAN_MOVE), so there's a
32
+ # single source of truth for the token vocabulary: PGN::Lexer's rules.
33
+ LITERAL_TOKENS = { lbracket: '[', rbracket: ']', lparen: '(', rparen: ')' }.freeze
34
+
29
35
  def translate_type(sym)
30
- case sym
31
- when :string then :STRING
32
- when :comment then :COMMENT
33
- when :game_termination then :GAME_TERMINATION
34
- when :san_move then :SAN_MOVE
35
- when :nag then :NAG
36
- when :move_number then :MOVE_NUMBER
37
- when :tag_name then :TAG_NAME
38
- when :lbracket then '['
39
- when :rbracket then ']'
40
- when :lparen then '('
41
- when :rparen then ')'
42
- else
43
- raise "PGN::Lexer produced an unknown token type: #{sym.inspect}"
44
- end
36
+ LITERAL_TOKENS[sym] || sym.to_s.upcase.to_sym
45
37
  end
46
38
 
47
39
  # Slice the verbatim raw PGN text per game out of the original input using
@@ -65,42 +57,42 @@ module_eval(<<'...end pgn_parser.y/module_eval...', 'pgn_parser.y', 91)
65
57
  ##### State transition tables begin ###
66
58
 
67
59
  racc_action_table = [
68
- 2, 16, 7, 17, 22, 14, 6, 24, 11, 6,
69
- 31, 16, 12, 17, 29, 14, 18, 30, 21, 25,
70
- 21, 24, 30 ]
60
+ 2, 16, 7, 17, 23, 14, 6, 25, 11, 6,
61
+ 32, 16, 12, 17, 30, 14, 18, 31, 21, 26,
62
+ 21, 25, 31 ]
71
63
 
72
64
  racc_action_check = [
73
- 1, 27, 2, 27, 17, 27, 5, 17, 6, 1,
74
- 27, 9, 9, 9, 23, 9, 11, 23, 15, 18,
75
- 20, 22, 28 ]
65
+ 1, 28, 2, 28, 17, 28, 5, 17, 6, 1,
66
+ 28, 9, 9, 9, 24, 9, 11, 24, 15, 18,
67
+ 20, 23, 29 ]
76
68
 
77
69
  racc_action_pointer = [
78
70
  nil, 0, 2, nil, nil, -3, 0, nil, nil, 8,
79
71
  nil, 14, nil, nil, nil, 7, nil, 1, 9, nil,
80
- 9, nil, 15, 11, nil, nil, nil, -2, 16, nil,
81
- nil, nil ]
72
+ 9, nil, nil, 15, 11, nil, nil, nil, -2, 16,
73
+ nil, nil, nil ]
82
74
 
83
75
  racc_action_default = [
84
- -1, -24, -24, -2, -8, -4, -24, 32, -3, -24,
85
- -5, -24, -7, -9, -10, -11, -13, -14, -24, -12,
86
- -21, -8, -15, -16, -19, -6, -22, -24, -18, -17,
87
- -20, -23 ]
76
+ -1, -25, -25, -2, -8, -4, -25, 33, -3, -25,
77
+ -5, -25, -7, -9, -10, -11, -13, -14, -25, -12,
78
+ -22, -8, -15, -16, -17, -20, -6, -23, -25, -19,
79
+ -18, -21, -24 ]
88
80
 
89
81
  racc_goto_table = [
90
- 9, 19, 23, 1, 4, 3, 26, 28, 10, 8,
91
- nil, nil, nil, nil, nil, nil, nil, 27 ]
82
+ 9, 24, 4, 19, 1, 3, 10, 29, 27, 8,
83
+ 22, nil, nil, nil, nil, nil, nil, 28 ]
92
84
 
93
85
  racc_goto_check = [
94
- 6, 9, 10, 1, 3, 2, 9, 10, 3, 4,
95
- nil, nil, nil, nil, nil, nil, nil, 6 ]
86
+ 6, 11, 3, 9, 1, 2, 3, 11, 9, 4,
87
+ 10, nil, nil, nil, nil, nil, nil, 6 ]
96
88
 
97
89
  racc_goto_pointer = [
98
- nil, 3, 4, 3, 5, nil, -4, nil, nil, -14,
99
- -15, nil ]
90
+ nil, 4, 4, 1, 5, nil, -4, nil, nil, -12,
91
+ -7, -16, nil ]
100
92
 
101
93
  racc_goto_default = [
102
94
  nil, nil, nil, nil, nil, 5, nil, 13, 15, nil,
103
- nil, 20 ]
95
+ nil, nil, 20 ]
104
96
 
105
97
  racc_reduce_table = [
106
98
  0, 0, :racc_error,
@@ -119,18 +111,19 @@ racc_reduce_table = [
119
111
  1, 20, :_reduce_13,
120
112
  1, 21, :_reduce_14,
121
113
  2, 21, :_reduce_15,
122
- 2, 21, :_reduce_16,
123
- 3, 21, :_reduce_17,
124
- 3, 21, :_reduce_18,
125
- 1, 23, :_reduce_19,
126
- 2, 23, :_reduce_20,
127
- 1, 22, :_reduce_21,
128
- 2, 22, :_reduce_22,
129
- 3, 24, :_reduce_23 ]
114
+ 1, 23, :_reduce_16,
115
+ 1, 23, :_reduce_17,
116
+ 2, 23, :_reduce_18,
117
+ 2, 23, :_reduce_19,
118
+ 1, 24, :_reduce_20,
119
+ 2, 24, :_reduce_21,
120
+ 1, 22, :_reduce_22,
121
+ 2, 22, :_reduce_23,
122
+ 3, 25, :_reduce_24 ]
130
123
 
131
- racc_reduce_n = 24
124
+ racc_reduce_n = 25
132
125
 
133
- racc_shift_n = 32
126
+ racc_shift_n = 33
134
127
 
135
128
  racc_token_table = {
136
129
  false => 0,
@@ -192,6 +185,7 @@ Racc_token_to_s_table = [
192
185
  "element",
193
186
  "san_move_annotated",
194
187
  "variation_list",
188
+ "move_trailer",
195
189
  "annotation_list",
196
190
  "variation" ]
197
191
  Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)
@@ -202,21 +196,21 @@ Racc_debug_parser = false
202
196
 
203
197
  # reduce 0 omitted
204
198
 
205
- module_eval(<<'.,.,', 'pgn_parser.y', 7)
199
+ module_eval(<<'.,.,', 'pgn_parser.y', 9)
206
200
  def _reduce_1(val, _values, result)
207
201
  result = []
208
202
  result
209
203
  end
210
204
  .,.,
211
205
 
212
- module_eval(<<'.,.,', 'pgn_parser.y', 8)
206
+ module_eval(<<'.,.,', 'pgn_parser.y', 10)
213
207
  def _reduce_2(val, _values, result)
214
208
  result = val[0] << val[1]
215
209
  result
216
210
  end
217
211
  .,.,
218
212
 
219
- module_eval(<<'.,.,', 'pgn_parser.y', 13)
213
+ module_eval(<<'.,.,', 'pgn_parser.y', 15)
220
214
  def _reduce_3(val, _values, result)
221
215
  result = val[1].pop
222
216
  result = {
@@ -231,14 +225,14 @@ module_eval(<<'.,.,', 'pgn_parser.y', 13)
231
225
  end
232
226
  .,.,
233
227
 
234
- module_eval(<<'.,.,', 'pgn_parser.y', 24)
228
+ module_eval(<<'.,.,', 'pgn_parser.y', 26)
235
229
  def _reduce_4(val, _values, result)
236
230
  result = val[0]
237
231
  result
238
232
  end
239
233
  .,.,
240
234
 
241
- module_eval(<<'.,.,', 'pgn_parser.y', 27)
235
+ module_eval(<<'.,.,', 'pgn_parser.y', 29)
242
236
  def _reduce_5(val, _values, result)
243
237
  # Right-recursive with section.merge(pair) reproduces the legacy
244
238
  # whittle parser's tag semantics exactly: reverse source insertion
@@ -250,28 +244,28 @@ module_eval(<<'.,.,', 'pgn_parser.y', 27)
250
244
  end
251
245
  .,.,
252
246
 
253
- module_eval(<<'.,.,', 'pgn_parser.y', 35)
247
+ module_eval(<<'.,.,', 'pgn_parser.y', 37)
254
248
  def _reduce_6(val, _values, result)
255
249
  result = { val[1] => val[2][1...-1] }
256
250
  result
257
251
  end
258
252
  .,.,
259
253
 
260
- module_eval(<<'.,.,', 'pgn_parser.y', 38)
254
+ module_eval(<<'.,.,', 'pgn_parser.y', 40)
261
255
  def _reduce_7(val, _values, result)
262
256
  result = val[0] << val[1]
263
257
  result
264
258
  end
265
259
  .,.,
266
260
 
267
- module_eval(<<'.,.,', 'pgn_parser.y', 41)
261
+ module_eval(<<'.,.,', 'pgn_parser.y', 43)
268
262
  def _reduce_8(val, _values, result)
269
263
  result = []
270
264
  result
271
265
  end
272
266
  .,.,
273
267
 
274
- module_eval(<<'.,.,', 'pgn_parser.y', 44)
268
+ module_eval(<<'.,.,', 'pgn_parser.y', 46)
275
269
  def _reduce_9(val, _values, result)
276
270
  result = val[1].nil? ? val[0] : val[0] << val[1]
277
271
 
@@ -279,7 +273,7 @@ module_eval(<<'.,.,', 'pgn_parser.y', 44)
279
273
  end
280
274
  .,.,
281
275
 
282
- module_eval(<<'.,.,', 'pgn_parser.y', 48)
276
+ module_eval(<<'.,.,', 'pgn_parser.y', 50)
283
277
  def _reduce_10(val, _values, result)
284
278
  result = nil
285
279
  result
@@ -288,7 +282,7 @@ module_eval(<<'.,.,', 'pgn_parser.y', 48)
288
282
 
289
283
  # reduce 11 omitted
290
284
 
291
- module_eval(<<'.,.,', 'pgn_parser.y', 52)
285
+ module_eval(<<'.,.,', 'pgn_parser.y', 54)
292
286
  def _reduce_12(val, _values, result)
293
287
  result = val[0]
294
288
  result.variations = val[1]
@@ -298,7 +292,7 @@ module_eval(<<'.,.,', 'pgn_parser.y', 52)
298
292
  end
299
293
  .,.,
300
294
 
301
- module_eval(<<'.,.,', 'pgn_parser.y', 58)
295
+ module_eval(<<'.,.,', 'pgn_parser.y', 60)
302
296
  def _reduce_13(val, _values, result)
303
297
  # A standalone comment (not attached to a move) becomes the game
304
298
  # comment; the last such comment in the game wins.
@@ -309,64 +303,71 @@ module_eval(<<'.,.,', 'pgn_parser.y', 58)
309
303
  end
310
304
  .,.,
311
305
 
312
- module_eval(<<'.,.,', 'pgn_parser.y', 65)
306
+ module_eval(<<'.,.,', 'pgn_parser.y', 67)
313
307
  def _reduce_14(val, _values, result)
314
308
  result = MoveText.new(val[0])
315
309
  result
316
310
  end
317
311
  .,.,
318
312
 
319
- module_eval(<<'.,.,', 'pgn_parser.y', 66)
313
+ module_eval(<<'.,.,', 'pgn_parser.y', 68)
320
314
  def _reduce_15(val, _values, result)
321
- result = MoveText.new(val[0], nil, val[1])
315
+ result = MoveText.new(val[0], *val[1])
322
316
  result
323
317
  end
324
318
  .,.,
325
319
 
326
- module_eval(<<'.,.,', 'pgn_parser.y', 67)
320
+ module_eval(<<'.,.,', 'pgn_parser.y', 72)
327
321
  def _reduce_16(val, _values, result)
328
- result = MoveText.new(val[0], val[1])
322
+ result = [nil, val[0]]
329
323
  result
330
324
  end
331
325
  .,.,
332
326
 
333
- module_eval(<<'.,.,', 'pgn_parser.y', 68)
327
+ module_eval(<<'.,.,', 'pgn_parser.y', 73)
334
328
  def _reduce_17(val, _values, result)
335
- result = MoveText.new(val[0], val[1], val[2])
329
+ result = [val[0], nil]
336
330
  result
337
331
  end
338
332
  .,.,
339
333
 
340
- module_eval(<<'.,.,', 'pgn_parser.y', 69)
334
+ module_eval(<<'.,.,', 'pgn_parser.y', 74)
341
335
  def _reduce_18(val, _values, result)
342
- result = MoveText.new(val[0], val[2], val[1])
336
+ result = [val[0], val[1]]
343
337
  result
344
338
  end
345
339
  .,.,
346
340
 
347
- module_eval(<<'.,.,', 'pgn_parser.y', 72)
341
+ module_eval(<<'.,.,', 'pgn_parser.y', 75)
348
342
  def _reduce_19(val, _values, result)
349
- result = [val[0]]
343
+ result = [val[1], val[0]]
350
344
  result
351
345
  end
352
346
  .,.,
353
347
 
354
- module_eval(<<'.,.,', 'pgn_parser.y', 73)
348
+ module_eval(<<'.,.,', 'pgn_parser.y', 78)
355
349
  def _reduce_20(val, _values, result)
356
- result = val[0] << val[1]
350
+ result = [val[0]]
357
351
  result
358
352
  end
359
353
  .,.,
360
354
 
361
- module_eval(<<'.,.,', 'pgn_parser.y', 76)
355
+ module_eval(<<'.,.,', 'pgn_parser.y', 79)
362
356
  def _reduce_21(val, _values, result)
363
- result = [val[0]]
357
+ result = val[0] << val[1]
364
358
  result
365
359
  end
366
360
  .,.,
367
361
 
368
- module_eval(<<'.,.,', 'pgn_parser.y', 79)
362
+ module_eval(<<'.,.,', 'pgn_parser.y', 82)
369
363
  def _reduce_22(val, _values, result)
364
+ result = [val[0]]
365
+ result
366
+ end
367
+ .,.,
368
+
369
+ module_eval(<<'.,.,', 'pgn_parser.y', 85)
370
+ def _reduce_23(val, _values, result)
370
371
  # Right-recursive prepend reproduces the legacy whittle parser's
371
372
  # variation-order reversal on every parse. This keeps parsed-game
372
373
  # serialization byte-compatible with the legacy behavior; the quirk
@@ -377,8 +378,8 @@ module_eval(<<'.,.,', 'pgn_parser.y', 79)
377
378
  end
378
379
  .,.,
379
380
 
380
- module_eval(<<'.,.,', 'pgn_parser.y', 87)
381
- def _reduce_23(val, _values, result)
381
+ module_eval(<<'.,.,', 'pgn_parser.y', 93)
382
+ def _reduce_24(val, _values, result)
382
383
  result = val[1]
383
384
  result
384
385
  end
data/lib/pgn/pgn_parser.y CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PGN::PgnParser
2
4
 
3
5
  token STRING COMMENT GAME_TERMINATION SAN_MOVE NAG MOVE_NUMBER TAG_NAME
@@ -64,10 +66,14 @@ rule
64
66
 
65
67
  san_move_annotated:
66
68
  SAN_MOVE { result = MoveText.new(val[0]) }
67
- | SAN_MOVE COMMENT { result = MoveText.new(val[0], nil, val[1]) }
68
- | SAN_MOVE annotation_list { result = MoveText.new(val[0], val[1]) }
69
- | SAN_MOVE annotation_list COMMENT { result = MoveText.new(val[0], val[1], val[2]) }
70
- | SAN_MOVE COMMENT annotation_list { result = MoveText.new(val[0], val[2], val[1]) }
69
+ | SAN_MOVE move_trailer { result = MoveText.new(val[0], *val[1]) }
70
+
71
+ # [annotation_list, comment], in whichever order they followed the move.
72
+ move_trailer:
73
+ COMMENT { result = [nil, val[0]] }
74
+ | annotation_list { result = [val[0], nil] }
75
+ | annotation_list COMMENT { result = [val[0], val[1]] }
76
+ | COMMENT annotation_list { result = [val[1], val[0]] }
71
77
 
72
78
  annotation_list:
73
79
  NAG { result = [val[0]] }
@@ -98,29 +104,21 @@ rule
98
104
  end
99
105
 
100
106
  def next_token
101
- t = @lexer.next_token
102
- return [false, false] unless t
103
- [translate_type(t.type), t.value]
107
+ pair = @lexer.next_token_pair
108
+ return [false, false] unless pair
109
+ type, value = pair
110
+ [translate_type(type), value]
104
111
  end
105
112
 
106
113
  private
107
114
 
115
+ # Punctuation tokens are racc'd by literal; everything else is racc'd by
116
+ # the upcased lexer symbol (e.g. :san_move -> :SAN_MOVE), so there's a
117
+ # single source of truth for the token vocabulary: PGN::Lexer's rules.
118
+ LITERAL_TOKENS = { lbracket: '[', rbracket: ']', lparen: '(', rparen: ')' }.freeze
119
+
108
120
  def translate_type(sym)
109
- case sym
110
- when :string then :STRING
111
- when :comment then :COMMENT
112
- when :game_termination then :GAME_TERMINATION
113
- when :san_move then :SAN_MOVE
114
- when :nag then :NAG
115
- when :move_number then :MOVE_NUMBER
116
- when :tag_name then :TAG_NAME
117
- when :lbracket then '['
118
- when :rbracket then ']'
119
- when :lparen then '('
120
- when :rparen then ')'
121
- else
122
- raise "PGN::Lexer produced an unknown token type: #{sym.inspect}"
123
- end
121
+ LITERAL_TOKENS[sym] || sym.to_s.upcase.to_sym
124
122
  end
125
123
 
126
124
  # Slice the verbatim raw PGN text per game out of the original input using
data/lib/pgn/position.rb CHANGED
@@ -81,7 +81,8 @@ module PGN
81
81
  move = PGN::Move.new(str, player)
82
82
  calculator = PGN::MoveCalculator.new(board, move)
83
83
 
84
- new_castling = castling - calculator.castling_restrictions
84
+ restrictions = calculator.castling_restrictions
85
+ new_castling = restrictions.empty? ? castling : castling - restrictions
85
86
  new_halfmove = if calculator.increment_halfmove?
86
87
  halfmove + 1
87
88
  else
@@ -106,7 +107,7 @@ module PGN
106
107
  # @return [Symbol] the next player to move
107
108
  #
108
109
  def next_player
109
- (PLAYERS - [player]).first
110
+ player == :white ? :black : :white
110
111
  end
111
112
 
112
113
  def inspect
data/lib/pgn/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module PGN
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
data/spec/parser_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'pry'
3
5
 
@@ -25,7 +27,7 @@ describe PGN do
25
27
  games.each do |game|
26
28
  expect(game.tags['White']).to eq 'Fool'
27
29
  expect(game.result).to eq '0-1'
28
- expect(game.moves.last).to eq 'Qh4#'
30
+ expect(game.moves.last).to eq 'Qh4#'
29
31
  end
30
32
  end
31
33
 
@@ -39,7 +41,7 @@ describe PGN do
39
41
  game = games.first
40
42
  expect(game.tags['White']).to eq 'Scholar'
41
43
  expect(game.result).to eq '1-0'
42
- expect(game.moves.last).to eq 'Qxf7#'
44
+ expect(game.moves.last).to eq 'Qxf7#'
43
45
  end
44
46
 
45
47
  it 'should handle multiline comments' do
@@ -47,7 +49,7 @@ describe PGN do
47
49
  game = games.first
48
50
  expect(game.tags['White']).to eq 'Scholar'
49
51
  expect(game.result).to eq '1-0'
50
- expect(game.moves.last).to eq 'Qxf7#'
52
+ expect(game.moves.last).to eq 'Qxf7#'
51
53
  end
52
54
 
53
55
  it 'should handle nested comments' do
@@ -57,12 +59,6 @@ describe PGN do
57
59
  expect(game.moves.last).to eq 'Nf6'
58
60
  end
59
61
 
60
- it 'handles two annotations' do
61
- games = PGN.parse(File.read('./spec/pgn_files/two_annotations.pgn'))
62
- game = games.first
63
- expect(game.moves[1].annotation).to eq ['$2', '$11']
64
- end
65
-
66
62
  it 'returns empty array when no variations' do
67
63
  games = PGN.parse(File.read('./spec/pgn_files/variations.pgn'))
68
64
  game = games.first
@@ -84,13 +80,6 @@ describe PGN do
84
80
  expect(game.moves[-2].variations.first).to eq %w[f4 exf4]
85
81
  end
86
82
 
87
- it 'should handle empty variation moves' do
88
- games = PGN.parse(File.read('./spec/pgn_files/empty_variation_move.pgn'))
89
- game = games.first
90
- expect(game.result).to eq '*'
91
- expect(game.moves.last).to eq 'Ng5'
92
- end
93
-
94
83
  it 'should handle complex files' do
95
84
  games = PGN.parse(File.read('./spec/pgn_files/test.pgn'))
96
85
  game = games.first
@@ -103,7 +92,7 @@ describe PGN do
103
92
  expect(game.moves[35].variations.size).to eq 1
104
93
  variation = game.moves[35].variations[0]
105
94
  expect(variation.size).to eq 2
106
- expect(variation[0]).to eq 'exf3'
95
+ expect(variation[0]).to eq 'exf3'
107
96
  end
108
97
 
109
98
  it 'should handle files with starting position' do
@@ -133,26 +122,5 @@ describe PGN do
133
122
  expect { game.positions }.not_to raise_error
134
123
  expect(game.moves).not_to be_empty
135
124
  end
136
-
137
- it 'gets game comment' do
138
- games = PGN.parse(File.read('./spec/pgn_files/no_moves.pgn'))
139
- game = games.last
140
- expect(game.comment).to eq('{game comment}')
141
- end
142
-
143
- it 'tag_values with double quotes' do
144
- games = PGN.parse(File.read('./spec/pgn_files/doublequotes.pgn'))
145
- game = games.last
146
- expect(game.tags['Event']).to eq('IRT BLITZ "Sub Zonal"')
147
- end
148
-
149
- it 'set encoding' do
150
- file = File.read('./spec/pgn_files/specialcharacters.pgn')
151
- games = PGN.parse(file, Encoding::UTF_8)
152
- game1 = games.first
153
- game2 = games.last
154
- expect(game1.tags['WhiteTeam']).to eq('NARIÑO')
155
- expect(game2.tags['Site']).to eq('HOTEL CAFEIRA Calle 18 Nro. 5 – 38 Centro de Pereira – Risaralda')
156
- end
157
125
  end
158
126
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgn2
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stacey Touset
@@ -133,6 +133,7 @@ extra_rdoc_files: []
133
133
  files:
134
134
  - ".github/workflows/ci.yml"
135
135
  - ".github/workflows/publish.yml"
136
+ - ".github/workflows/release.yml"
136
137
  - ".gitignore"
137
138
  - ".rspec"
138
139
  - ".rubocop.yml"
@@ -145,8 +146,10 @@ files:
145
146
  - bench/.keep
146
147
  - bench/IMPROVEMENTS.md
147
148
  - bench/baseline_moves.pre-optimization.txt
149
+ - bench/baseline_moves.pre-quickwins.txt
148
150
  - bench/baseline_moves.txt
149
151
  - bench/baseline_parse.pre-optimization.txt
152
+ - bench/baseline_parse.pre-quickwins.txt
150
153
  - bench/baseline_parse.racc.txt
151
154
  - bench/baseline_parse.txt
152
155
  - bench/profile_moves.rb
@@ -156,6 +159,7 @@ files:
156
159
  - docs/superpowers/plans/2026-08-12-to-pgn-serialization.md
157
160
  - docs/superpowers/plans/2026-08-13-whittle-to-racc-migration.md
158
161
  - docs/superpowers/specs/2026-08-12-to-pgn-serialization-design.md
162
+ - docs/superpowers/specs/2026-08-13-pgn-performance-quick-wins-design.md
159
163
  - examples/immortal_game.pgn
160
164
  - lib/pgn.rb
161
165
  - lib/pgn/board.rb