chess 0.1.3 → 0.1.4

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/README.md +44 -0
  4. data/Rakefile +6 -7
  5. data/chess.gemspec +1 -1
  6. data/ext/bitboard.c +24 -0
  7. data/ext/board.c +78 -24
  8. data/ext/board.h +4 -2
  9. data/ext/chess.c +216 -196
  10. data/ext/chess.h +1 -0
  11. data/ext/common.c +10 -0
  12. data/ext/game.c +33 -16
  13. data/ext/special.c +14 -11
  14. data/ext/special.h +1 -2
  15. data/lib/chess/exceptions.rb +7 -10
  16. data/lib/chess/game.rb +66 -50
  17. data/lib/chess/gnuchess.rb +19 -25
  18. data/lib/chess/pgn.rb +49 -9
  19. data/lib/chess/utf8_notation.rb +11 -9
  20. data/lib/chess/version.rb +1 -1
  21. data/test/pgn_collection/illegal/0001.pgn +18 -0
  22. data/test/pgn_collection/illegal/0002.pgn +17 -0
  23. data/test/pgn_collection/illegal/0003.pgn +13 -0
  24. data/test/pgn_collection/illegal/0004.pgn +15 -0
  25. data/test/pgn_collection/illegal/0005.pgn +16 -0
  26. data/test/pgn_collection/invalid/0001.pgn +20 -0
  27. data/test/pgn_collection/invalid/0002.pgn +16 -0
  28. data/test/pgn_collection/invalid/0003.pgn +13 -0
  29. data/test/pgn_collection/invalid/0004.pgn +15 -0
  30. data/test/pgn_collection/valid/0001.pgn +6 -7
  31. data/test/pgn_collection/valid/0002.pgn +8 -9
  32. data/test/pgn_collection/valid/0005.pgn +7 -8
  33. data/test/pgn_collection/valid/0009.pgn +14 -15
  34. data/test/pgn_collection/valid/0010.pgn +6 -7
  35. data/test/pgn_collection/valid/0011.pgn +5 -6
  36. data/test/pgn_collection/valid/0012.pgn +0 -1
  37. data/test/pgn_collection/valid/0013.pgn +5 -6
  38. data/test/pgn_collection/valid/0014.pgn +6 -7
  39. data/test/pgn_collection/valid/0020.pgn +6 -7
  40. data/test/pgn_collection/valid/0021.pgn +15 -16
  41. data/test/pgn_collection/valid/0971.pgn +22 -14
  42. data/test/test_big_pgn_collection.rb +12 -5
  43. data/test/test_helper.rb +1 -1
  44. data/test/test_illegal_moves.rb +14 -0
  45. data/test/test_insufficient_material.rb +13 -1
  46. data/test/test_particular_situations.rb +14 -0
  47. data/test/test_pgn.rb +24 -0
  48. data/test/test_pgn_collection.rb +1 -4
  49. metadata +30 -63
  50. data/README.rdoc +0 -44
  51. data/doc/Chess.html +0 -112
  52. data/doc/Chess/BadNotationError.html +0 -107
  53. data/doc/Chess/Board.html +0 -700
  54. data/doc/Chess/CGame.html +0 -1004
  55. data/doc/Chess/Game.html +0 -684
  56. data/doc/Chess/Gnuchess.html +0 -215
  57. data/doc/Chess/IllegalMoveError.html +0 -105
  58. data/doc/Chess/InvalidFenFormatError.html +0 -105
  59. data/doc/Chess/InvalidPgnFormatError.html +0 -105
  60. data/doc/Chess/Pgn.html +0 -309
  61. data/doc/Chess/UTF8Notation.html +0 -174
  62. data/doc/README_rdoc.html +0 -143
  63. data/doc/created.rid +0 -15
  64. data/doc/css/fonts.css +0 -167
  65. data/doc/css/rdoc.css +0 -590
  66. data/doc/fonts/Lato-Light.ttf +0 -0
  67. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  68. data/doc/fonts/Lato-Regular.ttf +0 -0
  69. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  70. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  71. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  72. data/doc/images/add.png +0 -0
  73. data/doc/images/arrow_up.png +0 -0
  74. data/doc/images/brick.png +0 -0
  75. data/doc/images/brick_link.png +0 -0
  76. data/doc/images/bug.png +0 -0
  77. data/doc/images/bullet_black.png +0 -0
  78. data/doc/images/bullet_toggle_minus.png +0 -0
  79. data/doc/images/bullet_toggle_plus.png +0 -0
  80. data/doc/images/date.png +0 -0
  81. data/doc/images/delete.png +0 -0
  82. data/doc/images/find.png +0 -0
  83. data/doc/images/loadingAnimation.gif +0 -0
  84. data/doc/images/macFFBgHack.png +0 -0
  85. data/doc/images/package.png +0 -0
  86. data/doc/images/page_green.png +0 -0
  87. data/doc/images/page_white_text.png +0 -0
  88. data/doc/images/page_white_width.png +0 -0
  89. data/doc/images/plugin.png +0 -0
  90. data/doc/images/ruby.png +0 -0
  91. data/doc/images/tag_blue.png +0 -0
  92. data/doc/images/tag_green.png +0 -0
  93. data/doc/images/transparent.png +0 -0
  94. data/doc/images/wrench.png +0 -0
  95. data/doc/images/wrench_orange.png +0 -0
  96. data/doc/images/zoom.png +0 -0
  97. data/doc/index.html +0 -160
  98. data/doc/js/darkfish.js +0 -161
  99. data/doc/js/jquery.js +0 -4
  100. data/doc/js/navigation.js +0 -142
  101. data/doc/js/navigation.js.gz +0 -0
  102. data/doc/js/search.js +0 -109
  103. data/doc/js/search_index.js +0 -1
  104. data/doc/js/search_index.js.gz +0 -0
  105. data/doc/js/searcher.js +0 -229
  106. data/doc/js/searcher.js.gz +0 -0
  107. data/doc/table_of_contents.html +0 -331
data/ext/chess.h CHANGED
@@ -36,6 +36,7 @@ VALUE board_king_in_check (VALUE self);
36
36
  VALUE board_king_in_checkmate (VALUE self);
37
37
  VALUE board_stalemate (VALUE self);
38
38
  VALUE board_insufficient_material (VALUE self);
39
+ VALUE board_only_kings (VALUE self);
39
40
  VALUE board_fifty_move_rule (VALUE self);
40
41
  VALUE board_active_color (VALUE self);
41
42
  VALUE board_halfmove_clock (VALUE self);
data/ext/common.c CHANGED
@@ -7,24 +7,28 @@
7
7
 
8
8
  #include "common.h"
9
9
 
10
+ // Given a square returns the file.
10
11
  char
11
12
  square_to_file (int square)
12
13
  {
13
14
  return (square % 8) + 97;
14
15
  }
15
16
 
17
+ // Given a square returns the rank.
16
18
  char
17
19
  square_to_rank (int square)
18
20
  {
19
21
  return (square / 8) + 49;
20
22
  }
21
23
 
24
+ // Given a coordinate (a1) returns the square (0..63).
22
25
  int
23
26
  coord_to_square (const char *coord)
24
27
  {
25
28
  return 8 * ((coord[1] | ' ') - 49) + ((coord[0] | ' ') - 97);
26
29
  }
27
30
 
31
+ // Given a square (0..63) returns the coordinate (a1).
28
32
  char*
29
33
  square_to_coord (int square)
30
34
  {
@@ -35,6 +39,8 @@ square_to_coord (int square)
35
39
  return s;
36
40
  }
37
41
 
42
+ // Given a move from-to returns the coordinated (a2a1) with promotion syntax if
43
+ // occured (a2a1=Q).
38
44
  char*
39
45
  ft_to_coord_move (int from, int to, char promote_in)
40
46
  {
@@ -53,6 +59,7 @@ ft_to_coord_move (int from, int to, char promote_in)
53
59
  return s;
54
60
  }
55
61
 
62
+ // Given the result int returns the corresponding string.
56
63
  char*
57
64
  result_to_s (unsigned short int r)
58
65
  {
@@ -75,6 +82,7 @@ result_to_s (unsigned short int r)
75
82
  return s;
76
83
  }
77
84
 
85
+ // Given the castling int returns the corresponding FEN string.
78
86
  char*
79
87
  castling_to_s (short int castling)
80
88
  {
@@ -89,6 +97,7 @@ castling_to_s (short int castling)
89
97
  return s;
90
98
  }
91
99
 
100
+ // Given the en passant int returns the corresponding FEN string.
92
101
  char*
93
102
  en_passant_to_s (short int en_passant)
94
103
  {
@@ -100,6 +109,7 @@ en_passant_to_s (short int en_passant)
100
109
  return s;
101
110
  }
102
111
 
112
+ // Compare two strings.
103
113
  int
104
114
  compare (const void *a, const void *b)
105
115
  {
data/ext/game.c CHANGED
@@ -9,6 +9,7 @@
9
9
 
10
10
  static Board STARTING_BOARD;
11
11
 
12
+ // Initialize the library.
12
13
  void
13
14
  init_chess_library ()
14
15
  {
@@ -16,6 +17,7 @@ init_chess_library ()
16
17
  precalculate_all_xray ();
17
18
  }
18
19
 
20
+ // Initialize the Game struct.
19
21
  Game*
20
22
  init_game ()
21
23
  {
@@ -25,7 +27,7 @@ init_game ()
25
27
  return g;
26
28
  }
27
29
 
28
- // Free memory of a game
30
+ // Free memory of a game.
29
31
  void
30
32
  free_game (Game *g)
31
33
  {
@@ -38,6 +40,7 @@ free_game (Game *g)
38
40
  free (g);
39
41
  }
40
42
 
43
+ // Returns the last board of the game.
41
44
  Board*
42
45
  current_board (Game *g)
43
46
  {
@@ -46,6 +49,8 @@ current_board (Game *g)
46
49
  return &STARTING_BOARD;
47
50
  }
48
51
 
52
+ // Returns the board at i-th position. If i is negative returns the starting
53
+ // board. NULL if i is out of bounds.
49
54
  Board*
50
55
  get_board (Game *g, int index)
51
56
  {
@@ -56,6 +61,7 @@ get_board (Game *g, int index)
56
61
  return NULL;
57
62
  }
58
63
 
64
+ // Returns the last move done.
59
65
  char*
60
66
  current_move (Game *g)
61
67
  {
@@ -64,6 +70,7 @@ current_move (Game *g)
64
70
  return 0;
65
71
  }
66
72
 
73
+ // Returns the last move done in coordinate format.
67
74
  char*
68
75
  current_coord_move (Game *g)
69
76
  {
@@ -72,11 +79,13 @@ current_coord_move (Game *g)
72
79
  return 0;
73
80
  }
74
81
 
82
+ // Returns true if the move from-to is legal. Add the new board on the game.
75
83
  bool
76
84
  apply_move (Game *g, int from, int to, char promote_in)
77
85
  {
78
- if (g->result != IN_PROGRESS) return FALSE;
86
+ if (g->result != IN_PROGRESS && g->result != DRAW) return FALSE;
79
87
  Board *board = current_board (g);
88
+ if (promote_in && invalid_promotion (board, from, to)) return FALSE;
80
89
  Board *new_board = NEW_BOARD;
81
90
  char capture = 0;
82
91
  char *move_done = castling (board, castling_type (board, from, to), new_board);
@@ -113,7 +122,7 @@ apply_move (Game *g, int from, int to, char promote_in)
113
122
  else
114
123
  strcat (move_done, "+");
115
124
  }
116
- // Test insufficient material
125
+ // Set game result to DRAW if insufficient material
117
126
  else if (insufficient_material (new_board))
118
127
  g->result = DRAW;
119
128
  // Test stalemate
@@ -122,6 +131,7 @@ apply_move (Game *g, int from, int to, char promote_in)
122
131
  return TRUE;
123
132
  }
124
133
 
134
+ // Rollback last move.
125
135
  void
126
136
  rollback (Game *g)
127
137
  {
@@ -135,6 +145,7 @@ rollback (Game *g)
135
145
  }
136
146
  }
137
147
 
148
+ // Returns true if a player can claim draw by the threefold repetition rule.
138
149
  bool
139
150
  threefold_repetition (Game *g)
140
151
  {
@@ -179,6 +190,7 @@ threefold_repetition (Game *g)
179
190
  }
180
191
 
181
192
  /*
193
+ * Set the game position by FEN string.
182
194
  A FEN string is composed of 6 parts separated by " " (space).
183
195
  1. Piece placement (from white's perspective).
184
196
  2. Active color. "w" means white moves next, "b" means black.
@@ -335,6 +347,8 @@ set_fen (Game *g, const char *fen)
335
347
  free (s);
336
348
  }
337
349
 
350
+
351
+
338
352
  ///////////////////////////////////
339
353
  // MAIN (only for internal test) //
340
354
  ///////////////////////////////////
@@ -353,54 +367,57 @@ main ()
353
367
 
354
368
  // 1. e4 a6 2. Bc4 a5 3. Qh5 a4 4. Qxf7#
355
369
  board = current_board (g);
356
- get_coord (board, 'P', "e", "e4", 0, &from, &to);
370
+ get_coord (board, 'P', NULL, "e4", '\0', &from, &to);
357
371
  pseudo_legal_move (board, from, to);
358
372
  apply_move (g, from, to, 0);
359
373
  fen = to_fen (board);
360
374
  free (fen);
361
375
 
362
376
  board = current_board (g);
363
- get_coord (board, 'P', "a", "a6", 0, &from, &to);
377
+ get_coord (board, 'P', NULL, "a6", '\0', &from, &to);
364
378
  pseudo_legal_move (board, from, to);
365
- apply_move (g, from, to, 0);
379
+ apply_move (g, from, to, '\0');
366
380
  fen = to_fen (current_board (g));
367
381
  free (fen);
368
382
 
369
383
  board = current_board (g);
370
- get_coord (board, 'B', "", "c4", 0, &from, &to);
384
+ get_coord (board, 'B', NULL, "c4", '\0', &from, &to);
371
385
  pseudo_legal_move (board, from, to);
372
- apply_move (g, from, to, 0);
386
+ apply_move (g, from, to, '\0');
373
387
  fen = to_fen (current_board (g));
374
388
  free (fen);
375
389
 
376
390
  board = current_board (g);
377
- get_coord (board, 'P', "a", "a5", 0, &from, &to);
391
+ get_coord (board, 'P', NULL, "a5", '\0', &from, &to);
378
392
  pseudo_legal_move (board, from, to);
379
- apply_move (g, from, to, 0);
393
+ apply_move (g, from, to, '\0');
380
394
  fen = to_fen (current_board (g));
381
395
  free (fen);
382
396
 
383
397
  board = current_board (g);
384
- get_coord (board, 'Q', "", "h5", 0, &from, &to);
398
+ get_coord (board, 'Q', NULL, "h5", '\0', &from, &to);
385
399
  pseudo_legal_move (board, from, to);
386
- apply_move (g, from, to, 0);
400
+ apply_move (g, from, to, '\0');
387
401
  fen = to_fen (current_board (g));
388
402
  free (fen);
389
403
 
390
404
  board = current_board (g);
391
- get_coord (board, 'P', "a", "a4", 0, &from, &to);
405
+ get_coord (board, 'P', NULL, "a4", '\0', &from, &to);
392
406
  pseudo_legal_move (board, from, to);
393
- apply_move (g, from, to, 0);
407
+ apply_move (g, from, to, '\0');
394
408
  fen = to_fen (current_board (g));
395
409
  free (fen);
396
410
 
397
411
  board = current_board (g);
398
- get_coord (board, 'Q', "", "f7", 0, &from, &to);
412
+ get_coord (board, 'Q', NULL, "f7", '\0', &from, &to);
399
413
  pseudo_legal_move (board, from, to);
400
- apply_move (g, from, to, 0);
414
+ apply_move (g, from, to, '\0');
401
415
  fen = to_fen (current_board (g));
402
416
  free (fen);
403
417
 
418
+ // board = current_board (g);
419
+ // printf("%s\n", print_board (board));
420
+
404
421
  free_game (g);
405
422
  }
406
423
  return 0;
data/ext/special.c CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  #include "special.h"
9
9
 
10
+ // Update the board castling bits (FEN style).
10
11
  void
11
12
  update_castling (Board *board, int from)
12
13
  {
@@ -33,6 +34,8 @@ update_castling (Board *board, int from)
33
34
  }
34
35
  }
35
36
 
37
+ // Returns the castling type for the move from-to. If not a castling move
38
+ // returns 0.
36
39
  int
37
40
  castling_type (Board *board, int from, int to)
38
41
  {
@@ -79,6 +82,9 @@ castling_type (Board *board, int from, int to)
79
82
  return 0;
80
83
  }
81
84
 
85
+ // Returns the short algebraic chess notation and the new board of a castling
86
+ // type move. Assume that the castling move is legal. Equivalent of
87
+ // Game#apply_move but for a castling move.
82
88
  char*
83
89
  castling (Board *board, int castling_type, Board *new_board)
84
90
  {
@@ -140,6 +146,7 @@ castling (Board *board, int castling_type, Board *new_board)
140
146
  return move;
141
147
  }
142
148
 
149
+ // Update the board en passant bits (FEN style).
143
150
  void
144
151
  update_en_passant (Board *board, int from, int to)
145
152
  {
@@ -151,6 +158,8 @@ update_en_passant (Board *board, int from, int to)
151
158
  board->en_passant = -1;
152
159
  }
153
160
 
161
+ // Returns the position (0..63) "behind" the pawn that can perform a en passant
162
+ // capture. 0 if en passant is not available.
154
163
  int
155
164
  have_en_passant (Board *board, int from, int to)
156
165
  {
@@ -163,19 +172,16 @@ have_en_passant (Board *board, int from, int to)
163
172
  return 0;
164
173
  }
165
174
 
166
- int
167
- have_en_passant2 (Board *board, int to)
168
- {
169
- return have_en_passant (board, to + 1, board->en_passant) || have_en_passant (board, to - 1, board->en_passant);
170
- }
171
-
175
+ // Returns true if a pawn must be promoted (a pawn is on the edges).
172
176
  bool
173
177
  require_a_promotion (Board *board)
174
178
  {
175
179
  return ((board->pawns[WHITE] | board->pawns[BLACK]) & 0xff000000000000ff) ? TRUE : FALSE;
176
180
  }
177
181
 
178
- bool
182
+ // Perform the promotion of the piece on the square (transform the piece in the
183
+ // promoted piece). If promote_in is invalid promote in a Queen.
184
+ void
179
185
  promote (Board *board, int square, char promote_in)
180
186
  {
181
187
  if (board->active_color)
@@ -191,9 +197,6 @@ promote (Board *board, int square, char promote_in)
191
197
  promote_in = 'Q';
192
198
  }
193
199
  *(get_bitboard (board, square)) ^= 1ULL << square;
194
- bboard *bb = get_piece_bitboard (board, promote_in);
195
- if (!bb) return FALSE;
196
- *(bb) ^= 1ULL << square;
200
+ *(get_piece_bitboard (board, promote_in)) ^= 1ULL << square;
197
201
  board->placement[square] = promote_in;
198
- return TRUE;
199
202
  }
data/ext/special.h CHANGED
@@ -16,8 +16,7 @@ int castling_type (Board *board, int from, int to);
16
16
  char* castling (Board *board, int castling_type, Board *new_board);
17
17
  void update_en_passant (Board *board, int from, int to);
18
18
  int have_en_passant (Board *board, int from, int to);
19
- int have_en_passant2 (Board *board, int to);
20
19
  bool require_a_promotion (Board *board);
21
- bool promote (Board *board, int square, char promote_in);
20
+ void promote (Board *board, int square, char promote_in);
22
21
 
23
22
  #endif
@@ -1,30 +1,27 @@
1
1
  module Chess
2
2
 
3
3
  # This exception will be raised when an invalid short algebraic chess notation
4
- # string is passed to the Game#move function.
4
+ # string is passed to the {Game#move} function.
5
5
  class BadNotationError < StandardError
6
- # :nodoc:
7
- # Create a new exception.
6
+ # @param [String] notation The invalid notation.
8
7
  def initialize(notation)
9
- super("The notation '#{notation}' is invalid")
8
+ super("Invalid notation '#{notation}'")
10
9
  end
11
10
  end
12
11
 
13
12
  # This exception will be raised when a malformed PGN file is loaded.
14
13
  class InvalidPgnFormatError < StandardError
15
- # :nodoc:
16
- # Create a new exception.
14
+ # @param [String] filename The PGN filename
17
15
  def initialize(filename)
18
- super("Invalid PGN file: '#{filename}'")
16
+ super("Invalid PGN file '#{filename}'")
19
17
  end
20
18
  end
21
19
 
22
20
  # This exception will be raised when an invalid FEN string is used.
23
21
  class InvalidFenFormatError < StandardError
24
- # :nodoc:
25
- # Create a new exception.
22
+ # @param [String] fen_string The FEN string.
26
23
  def initialize(fen_string)
27
- super("Invalid FEN string: '#{fen_string}'")
24
+ super("Invalid FEN string '#{fen_string}'")
28
25
  end
29
26
  end
30
27
 
data/lib/chess/game.rb CHANGED
@@ -5,16 +5,19 @@ module Chess
5
5
  # This class rappresents a chess game.
6
6
  class Game < CGame
7
7
 
8
- # Create a new game. If an array of moves is provided, the moves will be performed.
9
- #
10
- # May be raise an IllegalMoveError or BadNotationError.
8
+ # @param [Array<String>] moves If an array of moves is provided, the moves will be performed.
9
+ # @raise [IllegalMoveError]
10
+ # @raise [BadNotationError]
11
11
  def initialize(moves = [])
12
12
  moves.each { |m| move(m) }
13
13
  end
14
14
 
15
15
  # Creates a new game from a file in PGN format.
16
- #
17
- # May be raise an InvalidPgnFormatError or IllegalMoveError or BadNotationError.
16
+ # @param [String] file The path of the PGN to laod.
17
+ # @return [Game]
18
+ # @raise [InvalidPgnFormatError]
19
+ # @raise [IllegalMoveError]
20
+ # @raise [BadNotationError]
18
21
  def self.load_pgn(file)
19
22
  pgn = Chess::Pgn.new(file)
20
23
  game = Chess::Game.new
@@ -26,20 +29,17 @@ module Chess
26
29
  when '0-1'
27
30
  game.resign(:white)
28
31
  when '1/2-1/2'
29
- if game.board.insufficient_material? || game.board.stalemate? ||
30
- game.threefold_repetition? || game.board.fifty_rule_move?
31
- game.draw
32
- end
32
+ game.draw
33
33
  end
34
34
  end
35
35
  return game
36
36
  end
37
37
 
38
38
  # Creates a new game from a FEN string.
39
- #
40
- # May be raise an InvalidFenFormatError.
41
- #
42
- # *Warning*: this game do not have history before the FEN placement.
39
+ # @param [String] fen The FEN string to laod.
40
+ # @return [Game]
41
+ # @raise [InvalidFenFormatError]
42
+ # @note This game do not have history before the FEN placement.
43
43
  def self.load_fen(fen)
44
44
  if fen =~ /^((?:[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+)$/
45
45
  game = Chess::Game.new
@@ -50,19 +50,17 @@ module Chess
50
50
  end
51
51
  end
52
52
 
53
- # Make a move. This add a new Board in the Storyboard.
53
+ # Make a move.
54
+ # @note This add a new {Board} in the {Game}.
55
+ # @param [String] m Represents the short algebraic chess notation string of
56
+ # the move. `m` can be also _from_square_ plus _to_square_ _('e2e4', ...,
57
+ # 'b1c3')_ (coordinate chess notation).
58
+ # @return [String] Returns a string that represents the short algebraic
59
+ # chess notation of the move.
54
60
  #
55
- # Parameters are:
56
- # +m+:: represents the short algebraic chess notation string of the move.
57
- # +m+ can be <em>from_square</em> plus <em>to_square</em>
58
- # <em>('e2e4', ..., 'b1c3')</em> (coordinate chess notation).
59
- #
60
- # This method returns a string that represents the short algebraic chess
61
- # notation of the move.
62
- #
63
- # Raise an IllegalMoveError if the move is illegal.
64
- # Raise an BadNotationError if the short algebraic chess notation is
65
- # malformed.
61
+ # @raise {IllegalMoveError} if the move is illegal.
62
+ # @raise {BadNotationError} if the short algebraic chess notation is
63
+ # malformed.
66
64
  def move(m)
67
65
  begin
68
66
  expand = expand_move(m)
@@ -72,40 +70,50 @@ module Chess
72
70
  super(expand[:name], expand[:dis], expand[:to], expand[:promotion])
73
71
  end
74
72
  rescue IllegalMoveError
75
- raise IllegalMoveError.new("Illegal move '#{m}'\n#{self.active_player} turn\n#{self.to_s}")
73
+ if ENV['DEBUG']
74
+ raise IllegalMoveError.new("Illegal move '#{m}'\nStatus: #{self.status}\nPlayer turn #{self.active_player}\n#{self.to_s}")
75
+ else
76
+ raise IllegalMoveError.new("Illegal move '#{m}'")
77
+ end
76
78
  end
77
79
  end
78
80
  alias :move= :move
79
81
  alias :<< :move
80
82
 
81
83
  # Make the array of moves.
84
+ # @param [Array<String>] moves The array of moves to performe.
82
85
  def moves=(moves)
83
86
  moves.each { |m| move(m) }
84
87
  end
85
88
 
86
- # Returns +:white+ if the active player is the white player, +:black+ otherwise.
89
+ # Returns `:white` if the active player is the white player, `:black`
90
+ # otherwise.
91
+ # @return [Symbol]
87
92
  def active_player
88
93
  self.board.active_color ? :black : :white
89
94
  end
90
95
 
91
- # Returns +:white+ if the inactive player is the white player, +:black+ otherwise.
96
+ # Returns `:white` if the inactive player is the white player, `:black`
97
+ # otherwise.
98
+ # @return [Symbol]
92
99
  def inactive_player
93
100
  self.board.active_color ? :white : :black
94
101
  end
95
102
 
96
103
  # Returns the status of the game.
97
- #
98
104
  # Possible states are:
99
- # * +in_progress+:: the game is in progress.
100
- # * +white_won+:: white player has won with a checkmate.
101
- # * +black_won+:: black player has won with a checkmate.
102
- # * +white_won_resign+:: white player has won for resign.
103
- # * +black_won_resign+:: black player has won for resign.
104
- # * +stalemate+:: draw for stalemate.
105
- # * +insufficient_material+:: draw for insufficient material to checkmate.
106
- # * +fifty_rule_move+:: draw for fifty rule move.
107
- # * +threefold_repetition+:: draw for threefold_repetition.
108
- # * +unknown+:: something went wrong.
105
+ #
106
+ # * `in_progress`: the game is in progress.
107
+ # * `white_won`: white player has won with a checkmate.
108
+ # * `black_won`: black player has won with a checkmate.
109
+ # * `white_won_resign`: white player has won for resign.
110
+ # * `black_won_resign`: black player has won for resign.
111
+ # * `stalemate`: draw for stalemate.
112
+ # * `insufficient_material`: draw for insufficient material to checkmate.
113
+ # * `fifty_rule_move`: draw for fifty rule move.
114
+ # * `threefold_repetition`: draw for threefold_repetition.
115
+ # * `unknown`: something went wrong.
116
+ # @return [String]
109
117
  def status
110
118
  case self.result
111
119
  when '*'
@@ -136,12 +144,13 @@ module Chess
136
144
  return :unknown
137
145
  end
138
146
 
139
- # Returns +true+ if the game is over
147
+ # Returns `true` if the game is over.
140
148
  def over?
141
149
  return self.result != '*'
142
150
  end
143
151
 
144
152
  # Returns the PGN rappresenting the game.
153
+ # @return [String]
145
154
  def pgn
146
155
  pgn = Chess::Pgn.new
147
156
  pgn.moves = self.moves
@@ -151,34 +160,41 @@ module Chess
151
160
 
152
161
  private
153
162
 
154
- # Expand the short algebraic chess notation string +m+ in a hash like this:
163
+ # Expand the short algebraic chess notation string `m` in a hash like this:
155
164
  #
156
- # Ngxe2 ==> { name: 'N', dis: 'g', from: nil, to: 'e2', promotion: '' }
165
+ # Ngxe2 ==> { name: 'N', dis: 'g', from: nil, to: 'e2', promotion: nil }
157
166
  def expand_move(m)
158
- if match = m.match(/^([RNBQK])?([a-h]?[1-8]?)(?:x)?([a-h][1-8])(?:=?([RrNnBbQq]))?(?:ep)?(?:\+|\#)?$/)
167
+ if match = m.match(Chess::MOVE_REGEXP)
159
168
  expand = {
160
169
  name: match[1] || 'P', # Piece name [RNBQK]
161
170
  dis: match[2], # Disambiguating move
162
171
  to: match[3], # Move to
163
- promotion: match[4].to_s, # Promote with
172
+ promotion: match[4], # Promote with
164
173
  }
165
174
  expand[:from] = match[2] if match[2] && match[2].size == 2
166
175
  return expand
167
- elsif m =~ /^([0O])-([0O])([\+#])?$/
176
+ elsif m =~ SHORT_CASTLING_REGEXP
168
177
  if self.board.active_color # black king short castling
169
- return { name: 'K', dis: '', from: 'e8', to: 'g8', promotion: '' }
178
+ return { name: 'K', dis: nil, from: 'e8', to: 'g8', promotion: nil }
170
179
  else # white king short castling
171
- return { name: 'K', dis: '', from: 'e1', to: 'g1', promotion: '' }
180
+ return { name: 'K', dis: nil, from: 'e1', to: 'g1', promotion: nil }
172
181
  end
173
- elsif m =~ /^([0O])-([0O])-([0O])([\+#])?$/
182
+ elsif m =~ LONG_CASTLING_REGEXP
174
183
  if self.board.active_color # black king long castling
175
- return { name: 'K', dis: '', from: 'e8', to: 'c8', promotion: '' }
184
+ return { name: 'K', dis: nil, from: 'e8', to: 'c8', promotion: nil }
176
185
  else # white king long castling
177
- return { name: 'K', dis: '', from: 'e1', to: 'c1', promotion: '' }
186
+ return { name: 'K', dis: nil, from: 'e1', to: 'c1', promotion: nil }
178
187
  end
179
188
  end
180
189
  raise BadNotationError.new(m)
181
190
  end
182
191
 
183
192
  end
193
+
194
+ private
195
+
196
+ MOVE_REGEXP = /^([RNBQK])?([a-h]|[1-8]|[a-h][1-8])?(?:x)?([a-h][1-8])(?:=?([RrNnBbQq]))?(?:ep)?(?:\+|\#)?$/
197
+ SHORT_CASTLING_REGEXP = /^([0O])-([0O])([\+#])?$/
198
+ LONG_CASTLING_REGEXP = /^([0O])-([0O])-([0O])([\+#])?$/
199
+
184
200
  end