chess 0.1.3 → 0.1.4

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