chess 0.3.1 → 0.3.2

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 +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