chess 0.3.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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