chess 0.3.0 → 0.3.3

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +12 -0
  3. data/.github/workflows/ruby.yml +58 -0
  4. data/.gitignore +3 -2
  5. data/.rubocop.yml +10 -142
  6. data/Gemfile.lock +74 -0
  7. data/README.md +26 -22
  8. data/Rakefile +2 -1
  9. data/chess.gemspec +11 -7
  10. data/docs/Chess/BadNotationError.html +237 -0
  11. data/docs/Chess/Board.html +1759 -0
  12. data/docs/Chess/CGame.html +2296 -0
  13. data/docs/Chess/Game.html +1277 -0
  14. data/docs/Chess/Gnuchess.html +366 -0
  15. data/docs/Chess/IllegalMoveError.html +137 -0
  16. data/docs/Chess/InvalidFenFormatError.html +237 -0
  17. data/docs/Chess/InvalidPgnFormatError.html +217 -0
  18. data/docs/Chess/Pgn.html +1477 -0
  19. data/docs/Chess/UTF8Notation.html +270 -0
  20. data/docs/Chess.html +157 -0
  21. data/docs/_index.html +217 -0
  22. data/docs/class_list.html +51 -0
  23. data/docs/css/common.css +1 -0
  24. data/docs/css/full_list.css +58 -0
  25. data/docs/css/style.css +497 -0
  26. data/docs/file.README.html +116 -0
  27. data/docs/file_list.html +56 -0
  28. data/docs/frames.html +17 -0
  29. data/docs/index.html +116 -0
  30. data/docs/js/app.js +314 -0
  31. data/docs/js/full_list.js +216 -0
  32. data/docs/js/jquery.js +4 -0
  33. data/docs/method_list.html +531 -0
  34. data/docs/top-level-namespace.html +110 -0
  35. data/ext/chess.c +1 -1
  36. data/ext/common.h +7 -3
  37. data/ext/extconf.rb +1 -1
  38. data/lib/chess/game.rb +68 -66
  39. data/lib/chess/gnuchess.rb +49 -53
  40. data/lib/chess/pgn.rb +13 -15
  41. data/lib/chess/version.rb +1 -1
  42. data/test/test_big_pgn_collection.rb +1 -1
  43. data/test/test_checkmate.rb +4 -4
  44. data/test/test_errors.rb +22 -0
  45. data/test/test_fifty_rule_move.rb +2 -2
  46. data/test/test_game.rb +82 -0
  47. data/test/test_helper.rb +15 -0
  48. data/test/test_insufficient_material.rb +4 -4
  49. data/test/test_move_generator.rb +3 -2
  50. data/test/test_pgn.rb +35 -0
  51. data/test/test_pgn_collection.rb +2 -2
  52. data/test/test_stalemate.rb +1 -1
  53. data/test/test_threefold_repetition.rb +1 -1
  54. data/test/test_uci_castling.rb +34 -0
  55. metadata +118 -1236
data/lib/chess/game.rb CHANGED
@@ -7,6 +7,7 @@ module Chess
7
7
  # @raise [IllegalMoveError]
8
8
  # @raise [BadNotationError]
9
9
  def initialize(moves = [])
10
+ super()
10
11
  moves.each { |m| move(m) }
11
12
  end
12
13
 
@@ -20,7 +21,7 @@ module Chess
20
21
  pgn = Chess::Pgn.new(file)
21
22
  game = Chess::Game.new
22
23
  pgn.moves.each { |m| game.move(m) }
23
- if !game.over?
24
+ unless game.over?
24
25
  case pgn.result
25
26
  when '1-0'
26
27
  game.resign(:black)
@@ -39,13 +40,11 @@ module Chess
39
40
  # @raise [InvalidFenFormatError]
40
41
  # @note This game do not have history before the FEN placement.
41
42
  def self.load_fen(fen)
42
- if /^((?:[PRNBQKprnbqk1-8]{1,8}\/){7}[RNBQKPrnbqkp1-8]{1,8})\s(w|b)\s(K?Q?k?q?|-)\s([a-h][1-8]|-)\s(\d+)\s(\d+)$/.match?(fen)
43
- game = Chess::Game.new
44
- game.set_fen!(fen)
45
- return game
46
- else
47
- raise InvalidFenFormatError.new(fen)
48
- end
43
+ raise InvalidFenFormatError.new(fen) unless /^((?:[PRNBQKprnbqk1-8]{1,8}\/){7}[RNBQKPrnbqkp1-8]{1,8})\s(w|b)\s(K?Q?k?q?|-)\s([a-h][1-8]|-)\s(\d+)\s(\d+)$/.match?(fen)
44
+
45
+ game = Chess::Game.new
46
+ game.set_fen!(fen)
47
+ return game
49
48
  end
50
49
 
51
50
  # Make a move.
@@ -67,11 +66,9 @@ module Chess
67
66
  super(expand[:name], expand[:dis], expand[:to], expand[:promotion])
68
67
  end
69
68
  rescue IllegalMoveError
70
- if ENV['DEBUG']
71
- raise IllegalMoveError.new("Illegal move '#{notation}'\nStatus: #{self.status}\nPlayer turn #{self.active_player}\n#{self}")
72
- else
73
- raise IllegalMoveError.new("Illegal move '#{notation}'")
74
- end
69
+ raise IllegalMoveError.new("Illegal move '#{notation}'\nStatus: #{self.status}\nPlayer turn #{self.active_player}\n#{self}") if ENV['DEBUG']
70
+
71
+ raise IllegalMoveError.new("Illegal move '#{notation}'")
75
72
  end
76
73
  alias move= move
77
74
  alias << move
@@ -106,38 +103,28 @@ module Chess
106
103
  # * `black_won_resign`: black player has won for resign.
107
104
  # * `stalemate`: draw for stalemate.
108
105
  # * `insufficient_material`: draw for insufficient material to checkmate.
109
- # * `fifty_rule_move`: draw for fifty rule move.
110
- # * `threefold_repetition`: draw for threefold_repetition.
111
- # * `unknown`: something went wrong.
112
- # @return [String]
106
+ # * `fifty_move_rule`: draw for fifty-move rule.
107
+ # * `threefold_repetition`: draw for threefold repetition.
108
+ # @return [Symbol]
113
109
  def status
114
110
  case self.result
115
111
  when '*'
116
112
  return :in_progress
117
113
  when '1-0'
118
- if self.board.checkmate?
119
- return :white_won
120
- else
121
- return :white_won_resign
122
- end
114
+ return :white_won if self.board.checkmate?
115
+
116
+ return :white_won_resign
123
117
  when '0-1'
124
- if self.board.checkmate?
125
- return :black_won
126
- else
127
- return :black_won_resign
128
- end
118
+ return :black_won if self.board.checkmate?
119
+
120
+ return :black_won_resign
129
121
  when '1/2-1/2'
130
- if self.board.stalemate?
131
- return :stalemate
132
- elsif self.board.insufficient_material?
133
- return :insufficient_material
134
- elsif self.board.fifty_rule_move?
135
- return :fifty_rule_move
136
- elsif self.threefold_repetition?
137
- return :threefold_repetition
138
- end
122
+ return :stalemate if self.board.stalemate?
123
+ return :insufficient_material if self.board.insufficient_material?
124
+ return :fifty_move_rule if self.board.fifty_move_rule?
125
+
126
+ return :threefold_repetition if self.threefold_repetition?
139
127
  end
140
- return :unknown
141
128
  end
142
129
 
143
130
  # Returns `true` if the game is over.
@@ -146,7 +133,7 @@ module Chess
146
133
  end
147
134
 
148
135
  # Returns the PGN rappresenting the game.
149
- # @return [String]
136
+ # @return [Chess::Pgn]
150
137
  def pgn
151
138
  pgn = Chess::Pgn.new
152
139
  pgn.moves = self.moves
@@ -156,37 +143,52 @@ module Chess
156
143
 
157
144
  private
158
145
 
159
- # Expand the short algebraic chess notation string `notation` in a hash like this:
160
- #
161
- # Ngxe2 ==> { name: 'N', dis: 'g', from: nil, to: 'e2', promotion: nil }
162
- def expand_move(notation)
163
- if (match = notation.match(Chess::MOVE_REGEXP))
164
- expand = {
165
- name: match[1] || 'P', # Piece name [RNBQK]
166
- dis: match[2], # Disambiguating move
167
- to: match[3], # Move to
168
- promotion: match[4] # Promote with
169
- }
170
- expand[:from] = match[2] if match[2] && match[2].size == 2
171
- return expand
172
- elsif SHORT_CASTLING_REGEXP.match?(notation)
173
- if self.board.active_color # black king short castling
174
- return { name: 'K', dis: nil, from: 'e8', to: 'g8', promotion: nil }
175
- else # white king short castling
176
- return { name: 'K', dis: nil, from: 'e1', to: 'g1', promotion: nil }
177
- end
178
- elsif LONG_CASTLING_REGEXP.match?(notation)
179
- if self.board.active_color # black king long castling
180
- return { name: 'K', dis: nil, from: 'e8', to: 'c8', promotion: nil }
181
- else # white king long castling
182
- return { name: 'K', dis: nil, from: 'e1', to: 'c1', promotion: nil }
183
- end
146
+ # Expand the short algebraic chess notation string `notation` in a hash like this:
147
+ #
148
+ # Ngxe2 ==> { name: 'N', dis: 'g', from: nil, to: 'e2', promotion: nil }
149
+ def expand_move(notation)
150
+ if (match = notation.match(MOVE_REGEXP))
151
+ expand = {
152
+ name: match[1] || 'P', # Piece name [RNBQK]
153
+ dis: match[2], # Disambiguating move
154
+ to: match[3], # Move to
155
+ promotion: match[4] # Promote with
156
+ }
157
+ expand[:from] = match[2] if match[2] && match[2].size == 2
158
+
159
+ # Support UCI protocol (Lichess)
160
+ case expand[:from]
161
+ when 'e1'
162
+ expand[:to] = 'g1' if expand[:to] == 'h1' # UCI protocol (Lichess) white king short castling
163
+ expand[:to] = 'c1' if expand[:to] == 'a1' # UCI protocol (Lichess) white king long castling
164
+ when 'e8'
165
+ expand[:to] = 'g8' if expand[:to] == 'h8' # UCI protocol (Lichess) black king short castling
166
+ expand[:to] = 'c8' if expand[:to] == 'a8' # UCI protocol (Lichess) black king long castling
184
167
  end
185
- raise BadNotationError.new(notation)
168
+
169
+ return expand
170
+ end
171
+
172
+ # Castling notation
173
+ if SHORT_CASTLING_REGEXP.match?(notation)
174
+ return { name: 'K', dis: nil, from: 'e8', to: 'g8', promotion: nil } if self.board.active_color # black king short castling
175
+
176
+ return { name: 'K', dis: nil, from: 'e1', to: 'g1', promotion: nil } # white king short castling
177
+ end
178
+ if LONG_CASTLING_REGEXP.match?(notation)
179
+ return { name: 'K', dis: nil, from: 'e8', to: 'c8', promotion: nil } if self.board.active_color # black king long castling
180
+
181
+ return { name: 'K', dis: nil, from: 'e1', to: 'c1', promotion: nil } # white king long castling
186
182
  end
183
+
184
+ raise BadNotationError.new(notation)
185
+ end
187
186
  end
188
187
 
189
188
  MOVE_REGEXP = /^([RNBQK])?([a-h]|[1-8]|[a-h][1-8])?(?:x)?([a-h][1-8])(?:=?([RrNnBbQq]))?(?:ep)?(?:\+|\#)?$/.freeze
190
- SHORT_CASTLING_REGEXP = /^([0O])-([0O])([\+#])?$/.freeze
191
- LONG_CASTLING_REGEXP = /^([0O])-([0O])-([0O])([\+#])?$/.freeze
189
+ SHORT_CASTLING_REGEXP = /^([0O])-([0O])([+#])?$/.freeze
190
+ LONG_CASTLING_REGEXP = /^([0O])-([0O])-([0O])([+#])?$/.freeze
191
+ private_constant :MOVE_REGEXP
192
+ private_constant :SHORT_CASTLING_REGEXP
193
+ private_constant :LONG_CASTLING_REGEXP
192
194
  end
@@ -46,69 +46,65 @@ module Chess
46
46
  class << self
47
47
  private
48
48
 
49
- def included(_mod)
50
- raise_if_gnuchess_is_not_installed
51
- end
49
+ def included(_mod)
50
+ raise_if_gnuchess_is_not_installed
51
+ end
52
52
 
53
- def extended(_mod)
54
- raise_if_gnuchess_is_not_installed
55
- end
53
+ def extended(_mod)
54
+ raise_if_gnuchess_is_not_installed
55
+ end
56
56
 
57
- # Raise an exception if Gnuchess is not installed
58
- def raise_if_gnuchess_is_not_installed
59
- unless find_executable0('gnuchess')
60
- raise 'You must install Gnuchess to use the module Chess::Gnuchess!'
61
- end
62
- end
57
+ # Raise an exception if Gnuchess is not installed
58
+ def raise_if_gnuchess_is_not_installed
59
+ raise 'You must install Gnuchess to use the module Chess::Gnuchess!' unless find_executable0('gnuchess')
60
+ end
63
61
  end
64
62
 
65
63
  private
66
64
 
67
- def gen_pgn(file_to_save, moves = [])
68
- done = false
69
- pipe = IO.popen('gnuchess', 'r+')
70
- begin
71
- pipe.write("depth 1\n")
72
- pipe.write("force\n")
73
- pipe.write("name chess.rb\n")
74
- moves.each do |move|
75
- pipe.write("#{move}\n")
76
- end
77
- until done
78
- pipe.write("go\n")
79
- while (line = pipe.gets)
80
- if /My move is : /.match?(line)
81
- break
82
- elsif / : resign/.match?(line)
83
- break
84
- elsif / : 1-0 {White mates}/.match?(line)
85
- done = :white_won
86
- break
87
- elsif / : 0-1 {Black mates}/.match?(line)
88
- done = :black_won
89
- break
90
- elsif (m = line.match(/1\/2-1\/2 {(.*?)}/))
91
- case m[1]
92
- when 'Stalemate'
93
- done = :stalemate
94
- when 'Draw by repetition'
95
- done = :repetition
96
- when 'Draw by fifty-move rule'
97
- done = :fifty_move_rule
98
- when 'Draw by insufficient material'
99
- done = :insufficient_material
100
- end
101
- break
65
+ def gen_pgn(file_to_save, moves = [])
66
+ done = false
67
+ pipe = IO.popen('gnuchess', 'r+')
68
+ begin
69
+ pipe.write("depth 1\n")
70
+ pipe.write("force\n")
71
+ pipe.write("name chess.rb\n")
72
+ moves.each do |move|
73
+ pipe.write("#{move}\n")
74
+ end
75
+ until done
76
+ pipe.write("go\n")
77
+ while (line = pipe.gets)
78
+ break if /My move is : /.match?(line) || / : resign/.match?(line)
79
+
80
+ if / : 1-0 {White mates}/.match?(line)
81
+ done = :white_won
82
+ break
83
+ elsif / : 0-1 {Black mates}/.match?(line)
84
+ done = :black_won
85
+ break
86
+ elsif (m = line.match(/1\/2-1\/2 {(.*?)}/))
87
+ case m[1]
88
+ when 'Stalemate'
89
+ done = :stalemate
90
+ when 'Draw by repetition'
91
+ done = :repetition
92
+ when 'Draw by fifty-move rule'
93
+ done = :fifty_move_rule
94
+ when 'Draw by insufficient material'
95
+ done = :insufficient_material
102
96
  end
97
+ break
103
98
  end
104
99
  end
105
- dir = File.dirname(file_to_save)
106
- name = File.basename(file_to_save)
107
- pipe.write("pgnsave #{File.join(dir, done.to_s, name)}\n")
108
- ensure
109
- pipe.write("quit\n")
110
- pipe.close
111
100
  end
101
+ dir = File.dirname(file_to_save)
102
+ name = File.basename(file_to_save)
103
+ pipe.write("pgnsave #{File.join(dir, done.to_s, name)}\n")
104
+ ensure
105
+ pipe.write("quit\n")
106
+ pipe.close
112
107
  end
108
+ end
113
109
  end
114
110
  end
data/lib/chess/pgn.rb CHANGED
@@ -40,7 +40,6 @@ module Chess
40
40
  def initialize(filename = nil, check_moves: false)
41
41
  self.load(filename, check_moves: check_moves) if filename
42
42
  @date = '??'
43
- @round = '1'
44
43
  end
45
44
 
46
45
  # Load a PGN from file.
@@ -50,7 +49,7 @@ module Chess
50
49
  # @raise [InvalidPgnFormatError]
51
50
  # @raise [IllegalMoveError]
52
51
  def load(filename, check_moves: false)
53
- str = File.open(filename, 'r').read
52
+ str = File.read(filename)
54
53
  load_from_string(str, check_moves: check_moves)
55
54
  end
56
55
 
@@ -70,12 +69,10 @@ module Chess
70
69
  raise Chess::InvalidPgnFormatError.new if game_index.nil?
71
70
 
72
71
  game = str[game_index..-1].strip
73
- @moves = game.tr("\n", ' ').split(/\d+\./).collect(&:strip)[1..-1].collect { |t| t.split(' ') }.flatten
74
- @moves.delete_at(@moves.size - 1) if @moves.last =~ /(0-1)|(1-0)|(1\/2)|(1\/2-1\/2)|(\*)/
72
+ @moves = game.tr("\n", ' ').split(/\d+\./).collect(&:strip)[1..-1].map(&:split).flatten
73
+ @moves.delete_at(@moves.size - 1) if @moves.last.match?(/(0-1)|(1-0)|(1\/2)|(1\/2-1\/2)|(\*)/)
75
74
  @moves.each do |m|
76
- if m !~ MOVE_REGEXP && m !~ SHORT_CASTLING_REGEXP && m !~ LONG_CASTLING_REGEXP
77
- raise Chess::InvalidPgnFormatError.new
78
- end
75
+ raise Chess::InvalidPgnFormatError.new if m !~ MOVE_REGEXP && m !~ SHORT_CASTLING_REGEXP && m !~ LONG_CASTLING_REGEXP
79
76
  end
80
77
  Chess::Game.new(@moves) if check_moves
81
78
  return self
@@ -85,13 +82,13 @@ module Chess
85
82
  def to_s
86
83
  s = ''
87
84
  TAGS.each do |t|
88
- tag = instance_variable_get("@#{t}")
85
+ tag = instance_variable_defined?("@#{t}") ? instance_variable_get("@#{t}") : ''
89
86
  s << "[#{t.capitalize} \"#{tag}\"]\n"
90
87
  end
91
88
  s << "\n"
92
89
  m = ''
93
90
  @moves.each_with_index do |move, i|
94
- m << "#{i / 2 + 1}. " if i.even?
91
+ m << "#{(i / 2) + 1}. " if i.even?
95
92
  m << "#{move} "
96
93
  end
97
94
  m << @result unless @result.nil?
@@ -101,7 +98,7 @@ module Chess
101
98
  # Write PGN to file.
102
99
  # @param [String] filename The path of the PGN file.
103
100
  def write(filename)
104
- File.open(filename, 'w') { |f| f.write(self.to_s) }
101
+ File.write(filename, self.to_s)
105
102
  end
106
103
 
107
104
  # # @!visibility private
@@ -109,11 +106,12 @@ module Chess
109
106
 
110
107
  # Set the date tag.
111
108
  def date=(value)
112
- if value.is_a?(Time)
113
- @date = value.strftime('%Y.%m.%d')
114
- else
115
- @date = value
116
- end
109
+ @date =
110
+ if value.is_a?(Time)
111
+ value.strftime('%Y.%m.%d')
112
+ else
113
+ value
114
+ end
117
115
  end
118
116
  end
119
117
  end
data/lib/chess/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # The Chess library module.
2
2
  module Chess
3
3
  # The library version.
4
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.3.3'.freeze
5
5
  end
@@ -11,7 +11,7 @@ class ChessTest < Minitest::Test
11
11
  define_method "test_big_pgn_#{filename}" do
12
12
  pgn = Chess::Pgn.new(path)
13
13
  game = Chess::Game.new(pgn.moves)
14
- assert(game.checkmate?) if pgn.moves.last =~ /\#$/
14
+ assert(game.checkmate?) if pgn.moves.last.match?(/\#$/)
15
15
  end
16
16
  end
17
17
  end
@@ -9,11 +9,11 @@ class ChessTest < Minitest::Test
9
9
  game = Chess::Game.new(pgn.moves)
10
10
  assert(game.board.checkmate?)
11
11
  if file.include?('white_won')
12
- assert_equal(game.result, '1-0')
13
- assert_equal(game.active_player, :black)
12
+ assert_equal('1-0', game.result)
13
+ assert_equal(:black, game.active_player)
14
14
  elsif file.include?('black_won')
15
- assert_equal(game.result, '0-1')
16
- assert_equal(game.active_player, :white)
15
+ assert_equal('0-1', game.result)
16
+ assert_equal(:white, game.active_player)
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class ChessTest < Minitest::Test
4
+ def test_bad_notation_error
5
+ game = Chess::Game.new
6
+ assert_raises(Chess::BadNotationError) do
7
+ game << 'gg'
8
+ end
9
+ end
10
+
11
+ def test_invalid_pgn_format_error
12
+ assert_raises(Chess::InvalidPgnFormatError) do
13
+ Chess::Pgn.new('test/pgn_collection/invalid/0001.pgn')
14
+ end
15
+ end
16
+
17
+ def test_invalid_fen_format_error
18
+ assert_raises(Chess::InvalidFenFormatError) do
19
+ Chess::Game.load_fen('invalid')
20
+ end
21
+ end
22
+ end
@@ -3,10 +3,10 @@ require 'test_helper'
3
3
  class ChessTest < Minitest::Test
4
4
  TestHelper.pgns('fifty_move_rule').each do |file|
5
5
  name = File.basename(file, '.pgn')
6
- define_method "test_fifty_rule_move_#{name}" do
6
+ define_method "test_fifty_move_rule_#{name}" do
7
7
  pgn = Chess::Pgn.new(file)
8
8
  game = Chess::Game.new(pgn.moves)
9
- assert(game.board.fifty_rule_move?)
9
+ assert game.board.fifty_move_rule?
10
10
  end
11
11
  end
12
12
  end
data/test/test_game.rb ADDED
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+
3
+ class ChessTest < Minitest::Test
4
+ def test_moves
5
+ game = Chess::Game.new
6
+ game.moves = %w[e4 e5]
7
+ assert_equal %w[e4 e5], game.moves
8
+ end
9
+
10
+ def test_active_player
11
+ game = Chess::Game.new
12
+ game << 'a3'
13
+ assert_equal :black, game.active_player
14
+ end
15
+
16
+ def test_inactive_player
17
+ game = Chess::Game.new
18
+ game.moves = %w[a3 f5]
19
+ assert_equal :black, game.inactive_player
20
+ end
21
+
22
+ def test_status_white_won_resign
23
+ game = Chess::Game.new
24
+ game.resign(:black)
25
+ assert_equal :white_won_resign, game.status
26
+ assert game.over?
27
+ assert_equal '1-0', game.result
28
+ end
29
+
30
+ def test_status_black_won_resign
31
+ game = Chess::Game.new
32
+ game << 'e4'
33
+ game.resign(:white)
34
+ assert_equal :black_won_resign, game.status
35
+ assert game.over?
36
+ assert_equal '0-1', game.result
37
+ end
38
+
39
+ def test_status_insufficient_material
40
+ pgn = TestHelper.pick_pgn('insufficient_material/0001.pgn')
41
+ game = Chess::Game.new(pgn.moves)
42
+ assert_equal :insufficient_material, game.status
43
+ end
44
+
45
+ def test_status_fifty_move_rule
46
+ pgn = TestHelper.pick_pgn('fifty_move_rule/0001.pgn')
47
+ game = Chess::Game.new(pgn.moves)
48
+ game.draw
49
+ assert_equal :fifty_move_rule, game.status
50
+ end
51
+
52
+ def test_status_threefold_repetition
53
+ pgn = TestHelper.pick_pgn('threefold_repetition/0001.pgn')
54
+ game = Chess::Game.new(pgn.moves)
55
+ game.draw
56
+ assert_equal :threefold_repetition, game.status
57
+ end
58
+
59
+ def test_pgn
60
+ pgn = TestHelper.pick_pgn('valid/0001.pgn')
61
+ game = Chess::Game.new(pgn.moves)
62
+ expected_pgn = <<~PGN
63
+ [Event ""]
64
+ [Site ""]
65
+ [Date "??"]
66
+ [Round ""]
67
+ [White ""]
68
+ [Black ""]
69
+ [Result "*"]
70
+
71
+ 1. e4 c5 2. Nf3 d6 3. d4 cxd4 4. Qxd4 Nc6 5. Bb5 Bd7 6. Bxc6 Bxc6 7. Bg5 Nf6
72
+ 8. Bxf6 gxf6 9. Nc3 e6 10. O-O-O Be7 11. Rhe1 Rg8 12. Qe3 Rxg2 13. Rg1 Rg6 14.
73
+ Nd4 Qb6 15. h4 O-O-O 16. h5 Rg5 17. Nd5 Bxd5 18. exd5 e5 19. Qh3+ Kb8 20. Nf5
74
+ Bf8 21. Rxg5 fxg5 22. Ne3 Qb4 23. c4 g4 24. Qxg4 Bh6 25. Kb1 Rc8 26. Rc1 Qd2
75
+ 27. Qf5 Bf4 28. Qc2 Qxc2+ 29. Rxc2 Rg8 30. b4 Rg5 31. c5 e4 32. c6 Rxh5 33.
76
+ Rc4 f5 34. b5 Rg5 35. Rc3 Be5 36. Rc1 Bd4 37. Nc4 Bc5 38. Ne5 dxe5 39. Rxc5
77
+ Rg6 40. a4 Rd6 41. a5 b6 42. axb6 axb6 43. Rc2 Rxd5 44. Rb2 h5 45. Ka2 h4 46.
78
+ Ka3 h3 47. Ka4 Rd1 *
79
+ PGN
80
+ assert_equal expected_pgn, game.pgn.to_s
81
+ end
82
+ end
data/test/test_helper.rb CHANGED
@@ -1,5 +1,15 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter 'lib/chess/gnuchess.rb'
4
+ end
5
+ if ENV['CODECOV'] == 'true'
6
+ require 'codecov'
7
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
8
+ end
9
+
1
10
  require 'chess'
2
11
  require 'minitest/autorun'
12
+ require 'byebug'
3
13
 
4
14
  module TestHelper
5
15
  PGN_COLLECTION = 'test/pgn_collection'.freeze
@@ -8,4 +18,9 @@ module TestHelper
8
18
  def self.pgns(path, prefix = PGN_COLLECTION)
9
19
  Dir[File.join(prefix, path, '**/*.pgn')]
10
20
  end
21
+
22
+ def self.pick_pgn(path, prefix = PGN_COLLECTION)
23
+ file = File.join(prefix, path)
24
+ return Chess::Pgn.new(file)
25
+ end
11
26
  end
@@ -17,15 +17,15 @@ class ChessTest < Minitest::Test
17
17
  FENS.each_with_index do |fen, i|
18
18
  define_method("test_insufficient_material_by_fen_#{i}") do
19
19
  game = Chess::Game.load_fen(fen)
20
- assert(game.board.insufficient_material?)
20
+ assert game.board.insufficient_material?
21
21
  end
22
22
  end
23
23
 
24
24
  ONLY_KINGS_FENS.each_with_index do |fen, i|
25
25
  define_method("test_only_kings_by_fen_#{i}") do
26
26
  game = Chess::Game.load_fen(fen)
27
- assert(game.board.insufficient_material?)
28
- assert(game.board.only_kings?)
27
+ assert game.board.insufficient_material?
28
+ assert game.board.only_kings?
29
29
  end
30
30
  end
31
31
 
@@ -34,7 +34,7 @@ class ChessTest < Minitest::Test
34
34
  define_method "test_insufficient_material_#{name}" do
35
35
  pgn = Chess::Pgn.new(file)
36
36
  game = Chess::Game.new(pgn.moves)
37
- assert(game.board.insufficient_material?)
37
+ assert game.board.insufficient_material?
38
38
  end
39
39
  end
40
40
  end
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class ChessTest < Minitest::Test
4
4
  GENS = {
5
- 'r2qk3/8/2n5/8/8/8/p2B4/4K2R w K - 0 1' => { 'e1' => ['Kd1', 'Ke2', 'Kf2', 'Kf1', 'O-O'] },
5
+ 'r2qk3/8/2n5/8/8/8/p2B4/4K2R w K - 0 1' => { 'e1' => %w[Kd1 Ke2 Kf2 Kf1 O-O] },
6
6
  'r2qk3/8/2n5/8/8/8/p2B4/4K2R w - - 0 1' => { 'e1' => %w[Kd1 Ke2 Kf2 Kf1] },
7
7
  'r2qk3/8/2n5/2b5/8/8/p2B4/4K2R w K - 0 1' => { 'e1' => %w[Kd1 Ke2 Kf1] },
8
8
  'r2qk3/8/2n5/2b5/8/8/p2B4/4K2R b K - 0 1' => { 'a2' => ['a1=Q'] },
@@ -12,7 +12,8 @@ class ChessTest < Minitest::Test
12
12
  '1B1k4/r3q3/2n1n3/2b2pP1/8/8/2nB4/3K3R b - - 0 1' => { 'c6' => %w[Nxb8 Ne5 Nc6d4 N6b4 Na5] },
13
13
  '1B1k4/r3q3/2n1n2P/2b5/5p2/3p1RP1/3B4/3K4 w - - 0 1' => { 'f3' => %w[Rxd3 Re3 Rxf4 Rf2 Rf1] },
14
14
  'k7/1Q6/2P5/8/8/8/8/3K4 b - - 0 1' => { 'a8' => [] },
15
- 'k7/6P1/8/8/8/3r4/3Q4/3K4 w - - 0 1' => { 'd2' => ['Qxd3'] }
15
+ 'k7/6P1/8/8/8/3r4/3Q4/3K4 w - - 0 1' => { 'd2' => ['Qxd3'] },
16
+ 'rnbqkbnr/1ppppppp/8/8/pP2P3/P7/2PP1PPP/RNBQKBNR w KQkq - 0 3' => { 'c2' => %w[c3 c4] }
16
17
  }.freeze
17
18
 
18
19
  GENS.each do |fen, generators|
data/test/test_pgn.rb CHANGED
@@ -67,4 +67,39 @@ class ChessTest < Minitest::Test
67
67
  assert_nil pgn.result
68
68
  assert_equal 'Re6', pgn.moves.last
69
69
  end
70
+
71
+ def test_write_pgn
72
+ tempfile = Tempfile.new('test_pgn')
73
+ game = Chess::Game.new
74
+ game.moves = %w[e4 e5 d3]
75
+ pgn = game.pgn
76
+ pgn.event = 'Ruby Chess Tournament'
77
+ pgn.site = 'Ruby Chess test suite'
78
+ pgn.date = '1984.10.21'
79
+ pgn.round = '2'
80
+ pgn.white = 'Pioz'
81
+ pgn.black = 'Elizabeth Harmon'
82
+ pgn.write(tempfile.path)
83
+
84
+ loaded_pgn = Chess::Pgn.new
85
+ loaded_pgn.load(tempfile.path)
86
+ tempfile.delete
87
+ assert_equal 'Ruby Chess Tournament', loaded_pgn.event
88
+ assert_equal 'Ruby Chess test suite', loaded_pgn.site
89
+ assert_equal '1984.10.21', loaded_pgn.date
90
+ assert_equal '2', loaded_pgn.round
91
+ assert_equal 'Pioz', loaded_pgn.white
92
+ assert_equal 'Elizabeth Harmon', loaded_pgn.black
93
+ assert_equal '*', loaded_pgn.result
94
+ assert_equal %w[e4 e5 d3], loaded_pgn.moves
95
+ end
96
+
97
+ def test_set_date
98
+ pgn = Chess::Pgn.new
99
+ pgn.date = Time.parse('21-10-1984')
100
+ assert_equal '1984.10.21', pgn.date
101
+
102
+ pgn.date = '1984.10.21'
103
+ assert_equal '1984.10.21', pgn.date
104
+ end
70
105
  end
@@ -8,8 +8,8 @@ class ChessTest < Minitest::Test
8
8
  game = Chess::Game.new
9
9
  pgn.moves.each do |m|
10
10
  game.move(m)
11
- assert(game.board.check?) if m =~ /\+$/
12
- assert(game.board.checkmate?) if m =~ /\#$/
11
+ assert(game.board.check?) if m.match?(/\+$/)
12
+ assert(game.board.checkmate?) if m.match?(/\#$/)
13
13
  end
14
14
  end
15
15
  define_method "test_pgn_result_#{name}" do