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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +44 -0
- data/Rakefile +6 -7
- data/chess.gemspec +1 -1
- data/ext/bitboard.c +24 -0
- data/ext/board.c +78 -24
- data/ext/board.h +4 -2
- data/ext/chess.c +216 -196
- data/ext/chess.h +1 -0
- data/ext/common.c +10 -0
- data/ext/game.c +33 -16
- data/ext/special.c +14 -11
- data/ext/special.h +1 -2
- data/lib/chess/exceptions.rb +7 -10
- data/lib/chess/game.rb +66 -50
- data/lib/chess/gnuchess.rb +19 -25
- data/lib/chess/pgn.rb +49 -9
- data/lib/chess/utf8_notation.rb +11 -9
- data/lib/chess/version.rb +1 -1
- data/test/pgn_collection/illegal/0001.pgn +18 -0
- data/test/pgn_collection/illegal/0002.pgn +17 -0
- data/test/pgn_collection/illegal/0003.pgn +13 -0
- data/test/pgn_collection/illegal/0004.pgn +15 -0
- data/test/pgn_collection/illegal/0005.pgn +16 -0
- data/test/pgn_collection/invalid/0001.pgn +20 -0
- data/test/pgn_collection/invalid/0002.pgn +16 -0
- data/test/pgn_collection/invalid/0003.pgn +13 -0
- data/test/pgn_collection/invalid/0004.pgn +15 -0
- data/test/pgn_collection/valid/0001.pgn +6 -7
- data/test/pgn_collection/valid/0002.pgn +8 -9
- data/test/pgn_collection/valid/0005.pgn +7 -8
- data/test/pgn_collection/valid/0009.pgn +14 -15
- data/test/pgn_collection/valid/0010.pgn +6 -7
- data/test/pgn_collection/valid/0011.pgn +5 -6
- data/test/pgn_collection/valid/0012.pgn +0 -1
- data/test/pgn_collection/valid/0013.pgn +5 -6
- data/test/pgn_collection/valid/0014.pgn +6 -7
- data/test/pgn_collection/valid/0020.pgn +6 -7
- data/test/pgn_collection/valid/0021.pgn +15 -16
- data/test/pgn_collection/valid/0971.pgn +22 -14
- data/test/test_big_pgn_collection.rb +12 -5
- data/test/test_helper.rb +1 -1
- data/test/test_illegal_moves.rb +14 -0
- data/test/test_insufficient_material.rb +13 -1
- data/test/test_particular_situations.rb +14 -0
- data/test/test_pgn.rb +24 -0
- data/test/test_pgn_collection.rb +1 -4
- metadata +30 -63
- data/README.rdoc +0 -44
- data/doc/Chess.html +0 -112
- data/doc/Chess/BadNotationError.html +0 -107
- data/doc/Chess/Board.html +0 -700
- data/doc/Chess/CGame.html +0 -1004
- data/doc/Chess/Game.html +0 -684
- data/doc/Chess/Gnuchess.html +0 -215
- data/doc/Chess/IllegalMoveError.html +0 -105
- data/doc/Chess/InvalidFenFormatError.html +0 -105
- data/doc/Chess/InvalidPgnFormatError.html +0 -105
- data/doc/Chess/Pgn.html +0 -309
- data/doc/Chess/UTF8Notation.html +0 -174
- data/doc/README_rdoc.html +0 -143
- data/doc/created.rid +0 -15
- data/doc/css/fonts.css +0 -167
- data/doc/css/rdoc.css +0 -590
- data/doc/fonts/Lato-Light.ttf +0 -0
- data/doc/fonts/Lato-LightItalic.ttf +0 -0
- data/doc/fonts/Lato-Regular.ttf +0 -0
- data/doc/fonts/Lato-RegularItalic.ttf +0 -0
- data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
- data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
- data/doc/images/add.png +0 -0
- data/doc/images/arrow_up.png +0 -0
- data/doc/images/brick.png +0 -0
- data/doc/images/brick_link.png +0 -0
- data/doc/images/bug.png +0 -0
- data/doc/images/bullet_black.png +0 -0
- data/doc/images/bullet_toggle_minus.png +0 -0
- data/doc/images/bullet_toggle_plus.png +0 -0
- data/doc/images/date.png +0 -0
- data/doc/images/delete.png +0 -0
- data/doc/images/find.png +0 -0
- data/doc/images/loadingAnimation.gif +0 -0
- data/doc/images/macFFBgHack.png +0 -0
- data/doc/images/package.png +0 -0
- data/doc/images/page_green.png +0 -0
- data/doc/images/page_white_text.png +0 -0
- data/doc/images/page_white_width.png +0 -0
- data/doc/images/plugin.png +0 -0
- data/doc/images/ruby.png +0 -0
- data/doc/images/tag_blue.png +0 -0
- data/doc/images/tag_green.png +0 -0
- data/doc/images/transparent.png +0 -0
- data/doc/images/wrench.png +0 -0
- data/doc/images/wrench_orange.png +0 -0
- data/doc/images/zoom.png +0 -0
- data/doc/index.html +0 -160
- data/doc/js/darkfish.js +0 -161
- data/doc/js/jquery.js +0 -4
- data/doc/js/navigation.js +0 -142
- data/doc/js/navigation.js.gz +0 -0
- data/doc/js/search.js +0 -109
- data/doc/js/search_index.js +0 -1
- data/doc/js/search_index.js.gz +0 -0
- data/doc/js/searcher.js +0 -229
- data/doc/js/searcher.js.gz +0 -0
- 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
|
-
//
|
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',
|
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',
|
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',
|
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',
|
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',
|
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',
|
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',
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
20
|
+
void promote (Board *board, int square, char promote_in);
|
22
21
|
|
23
22
|
#endif
|
data/lib/chess/exceptions.rb
CHANGED
@@ -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
|
-
#
|
7
|
-
# Create a new exception.
|
6
|
+
# @param [String] notation The invalid notation.
|
8
7
|
def initialize(notation)
|
9
|
-
super("
|
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
|
-
#
|
16
|
-
# Create a new exception.
|
14
|
+
# @param [String] filename The PGN filename
|
17
15
|
def initialize(filename)
|
18
|
-
super("Invalid PGN file
|
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
|
-
#
|
25
|
-
# Create a new exception.
|
22
|
+
# @param [String] fen_string The FEN string.
|
26
23
|
def initialize(fen_string)
|
27
|
-
super("Invalid 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
|
-
#
|
9
|
-
#
|
10
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
41
|
-
#
|
42
|
-
#
|
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.
|
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
|
-
#
|
56
|
-
#
|
57
|
-
#
|
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
|
-
|
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
|
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
|
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
|
-
#
|
100
|
-
# *
|
101
|
-
# *
|
102
|
-
# *
|
103
|
-
# *
|
104
|
-
# *
|
105
|
-
# *
|
106
|
-
# *
|
107
|
-
# *
|
108
|
-
# *
|
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
|
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
|
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(
|
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]
|
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 =~
|
176
|
+
elsif m =~ SHORT_CASTLING_REGEXP
|
168
177
|
if self.board.active_color # black king short castling
|
169
|
-
return { name: 'K', dis:
|
178
|
+
return { name: 'K', dis: nil, from: 'e8', to: 'g8', promotion: nil }
|
170
179
|
else # white king short castling
|
171
|
-
return { name: 'K', dis:
|
180
|
+
return { name: 'K', dis: nil, from: 'e1', to: 'g1', promotion: nil }
|
172
181
|
end
|
173
|
-
elsif m =~
|
182
|
+
elsif m =~ LONG_CASTLING_REGEXP
|
174
183
|
if self.board.active_color # black king long castling
|
175
|
-
return { name: 'K', dis:
|
184
|
+
return { name: 'K', dis: nil, from: 'e8', to: 'c8', promotion: nil }
|
176
185
|
else # white king long castling
|
177
|
-
return { name: 'K', dis:
|
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
|