chess 0.3.1 → 0.3.2

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 +2 -2
  5. data/.rubocop.yml +9 -144
  6. data/Gemfile.lock +70 -0
  7. data/README.md +26 -22
  8. data/Rakefile +1 -0
  9. data/chess.gemspec +8 -5
  10. data/docs/Chess.html +157 -0
  11. data/docs/Chess/BadNotationError.html +237 -0
  12. data/docs/Chess/Board.html +1759 -0
  13. data/docs/Chess/CGame.html +2296 -0
  14. data/docs/Chess/Game.html +1277 -0
  15. data/docs/Chess/Gnuchess.html +366 -0
  16. data/docs/Chess/IllegalMoveError.html +137 -0
  17. data/docs/Chess/InvalidFenFormatError.html +237 -0
  18. data/docs/Chess/InvalidPgnFormatError.html +217 -0
  19. data/docs/Chess/Pgn.html +1477 -0
  20. data/docs/Chess/UTF8Notation.html +270 -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 +53 -67
  39. data/lib/chess/gnuchess.rb +49 -53
  40. data/lib/chess/pgn.rb +10 -12
  41. data/lib/chess/utf8_notation.rb +3 -0
  42. data/lib/chess/version.rb +1 -1
  43. data/test/test_big_pgn_collection.rb +1 -1
  44. data/test/test_checkmate.rb +4 -4
  45. data/test/test_errors.rb +22 -0
  46. data/test/test_fifty_rule_move.rb +2 -2
  47. data/test/test_game.rb +82 -0
  48. data/test/test_helper.rb +15 -0
  49. data/test/test_insufficient_material.rb +4 -4
  50. data/test/test_move_generator.rb +3 -2
  51. data/test/test_pgn.rb +35 -0
  52. data/test/test_pgn_collection.rb +2 -2
  53. data/test/test_stalemate.rb +1 -1
  54. data/test/test_threefold_repetition.rb +1 -1
  55. metadata +112 -10
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Chess
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Chess.html" title="Chess (module)">Chess</a></span>
86
+
87
+
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Thu Jan 28 17:31:40 2021 by
104
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.26 (ruby-2.6.1).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
@@ -723,7 +723,7 @@ Init_chess ()
723
723
  rb_define_method (board_klass, "stalemate?", board_stalemate, 0);
724
724
  rb_define_method (board_klass, "insufficient_material?", board_insufficient_material, 0);
725
725
  rb_define_method (board_klass, "only_kings?", board_only_kings, 0);
726
- rb_define_method (board_klass, "fifty_rule_move?", board_fifty_move_rule, 0);
726
+ rb_define_method (board_klass, "fifty_move_rule?", board_fifty_move_rule, 0);
727
727
  rb_define_method (board_klass, "active_color", board_active_color, 0);
728
728
  rb_define_method (board_klass, "halfmove_clock", board_halfmove_clock, 0);
729
729
  rb_define_method (board_klass, "fullmove_number", board_fullmove_number, 0);
@@ -13,9 +13,15 @@
13
13
  #include <string.h>
14
14
  #include <ctype.h>
15
15
 
16
- // Macros
16
+ // Boolean definition
17
+
18
+ #ifndef RUBY_BACKWARD2_BOOL_H
17
19
  #define FALSE 0
18
20
  #define TRUE 1
21
+ typedef unsigned short bool;
22
+ #endif
23
+
24
+ // Macros
19
25
 
20
26
  #define A1 0
21
27
  #define B1 1
@@ -95,8 +101,6 @@
95
101
  #define DRAW 2
96
102
  #define IN_PROGRESS 3
97
103
 
98
- typedef unsigned short bool;
99
-
100
104
  char square_to_file (int square);
101
105
  char square_to_rank (int square);
102
106
  int coord_to_square (const char *coord);
@@ -1,4 +1,4 @@
1
- ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
1
+ ENV['RC_ARCHS'] = '' if RUBY_PLATFORM.include?('darwin')
2
2
 
3
3
  require 'mkmf'
4
4
 
@@ -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,36 @@ 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
184
- end
185
- raise BadNotationError.new(notation)
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
+ return expand
159
+ elsif SHORT_CASTLING_REGEXP.match?(notation)
160
+ return { name: 'K', dis: nil, from: 'e8', to: 'g8', promotion: nil } if self.board.active_color # black king short castling
161
+
162
+ return { name: 'K', dis: nil, from: 'e1', to: 'g1', promotion: nil } # white king short castling
163
+ elsif LONG_CASTLING_REGEXP.match?(notation)
164
+ return { name: 'K', dis: nil, from: 'e8', to: 'c8', promotion: nil } if self.board.active_color # black king long castling
165
+
166
+ return { name: 'K', dis: nil, from: 'e1', to: 'c1', promotion: nil } # white king long castling
186
167
  end
168
+ raise BadNotationError.new(notation)
169
+ end
187
170
  end
188
171
 
189
172
  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
173
+ SHORT_CASTLING_REGEXP = /^([0O])-([0O])([+#])?$/.freeze
174
+ LONG_CASTLING_REGEXP = /^([0O])-([0O])-([0O])([+#])?$/.freeze
175
+ private_constant :MOVE_REGEXP
176
+ private_constant :SHORT_CASTLING_REGEXP
177
+ private_constant :LONG_CASTLING_REGEXP
192
178
  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
@@ -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.
@@ -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,7 +82,7 @@ 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"
@@ -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