1 /* Copyright (C) 2007-2011 Vincent Ollivier
3 * Purple Haze is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * Purple Haze is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 static bool king_castle_allowed(const Color c
,
23 const Square from
, const Square to
,
24 const Board
&board
, const Pieces
&pieces
)
26 assert(from
== Board::flip(E1
, c
));
27 assert(to
== Board::flip(G1
, c
));
28 const Square rook
= Board::flip(H1
, c
);
30 board
.is_empty(Board::flip(F1
, c
)) &&
32 board
[rook
].is(c
, ROOK
) &&
33 !board
.is_attacked_by(!c
, from
, pieces
) &&
34 !board
.is_attacked_by(!c
, to
, pieces
) &&
35 !board
.is_attacked_by(!c
, Board::flip(F1
, c
), pieces
);
38 static bool queen_castle_allowed(const Color c
,
39 const Square from
, const Square to
,
40 const Board
&board
, const Pieces
&pieces
)
42 assert(from
== Board::flip(E1
, c
));
43 assert(to
== Board::flip(C1
, c
));
44 const Square rook
= Board::flip(A1
, c
);
46 board
.is_empty(Board::flip(B1
, c
)) &&
47 board
.is_empty(Board::flip(D1
, c
)) &&
49 board
[rook
].is(c
, ROOK
) &&
50 !board
.is_attacked_by(!c
, from
, pieces
) &&
51 !board
.is_attacked_by(!c
, to
, pieces
) &&
52 !board
.is_attacked_by(!c
, Board::flip(D1
, c
), pieces
);
55 void Moves::generate_pieces(Color c
, PieceType t
, MoveType mt
)
57 for (int i
= 0, n
= pieces
.count(c
, t
); i
< n
; ++i
) {
58 const Square from
= pieces
.position(c
, t
, i
);
59 for (const Direction
&dir
: PIECES_DIRS
[t
]) {
60 Square to
= static_cast<Square
>(from
+ dir
);
61 while (!board
.is_out(to
)) {
62 if (!board
.is_empty(to
)) {
63 if (board
[to
].is(c
)) {
66 if (mt
!= QUIET_MOVE
) {
67 add(Move(from
, to
, CAPTURE
));
70 } else if (mt
!= CAPTURE
) {
71 add(Move(from
, to
, QUIET_MOVE
));
73 if (t
== KNIGHT
|| t
== KING
) {
76 to
= static_cast<Square
>(to
+ dir
); // Sliders
82 void Moves::generate(MoveType mt
)
84 const Color c
= current_position
.side();
87 for (int i
= 0, n
= pieces
.count(c
, PAWN
); i
< n
; ++i
) {
88 const Square from
= pieces
.position(c
, PAWN
, i
);
91 if (mt
!= QUIET_MOVE
) {
92 for (const Direction
&dir
: PAWN_CAPTURE_DIRS
[c
]) {
93 const Square to
= static_cast<Square
>(from
+ dir
);
94 if (board
.is_out(to
)) {
97 if (!board
.is_empty(to
) && board
[to
].color() != c
) {
98 if (board
.is_pawn_end(c
, to
)) {
100 add(Move(from
, to
, KNIGHT_PROMOTION_CAPTURE
));
101 add(Move(from
, to
, BISHOP_PROMOTION_CAPTURE
));
102 add(Move(from
, to
, ROOK_PROMOTION_CAPTURE
));
103 add(Move(from
, to
, QUEEN_PROMOTION_CAPTURE
));
106 add(Move(from
, to
, CAPTURE
));
108 } else if (to
== current_position
.en_passant()) {
110 add(Move(from
, to
, EN_PASSANT
));
118 Square to
= static_cast<Square
>(from
+ PAWN_PUSH_DIRS
[c
]);
119 assert(!board
.is_out(to
)); // Should never happend
120 if (!board
.is_empty(to
)) {
125 if (board
.is_pawn_end(c
, to
)) {
126 add(Move(from
, to
, KNIGHT_PROMOTION
));
127 add(Move(from
, to
, BISHOP_PROMOTION
));
128 add(Move(from
, to
, ROOK_PROMOTION
));
129 add(Move(from
, to
, QUEEN_PROMOTION
));
134 add(Move(from
, to
, QUIET_MOVE
));
137 if (board
.is_pawn_begin(c
, from
)) {
138 to
= static_cast<Square
>(to
+ PAWN_PUSH_DIRS
[c
]);
139 if (!board
.is_empty(to
)) {
142 add(Move(from
, to
, DOUBLE_PAWN_PUSH
));
147 for (const PieceType
& t
: NOT_PAWN_TYPES
) {
148 generate_pieces(c
, t
, mt
);
156 const Square from
= Board::flip(E1
, c
);
157 if (current_position
.can_castle(c
, KING
)) {
158 const Square to
= Board::flip(G1
, c
);
159 if (king_castle_allowed(c
, from
, to
, board
, pieces
)) {
160 add(Move(from
, to
, KING_CASTLE
));
163 if (current_position
.can_castle(c
, QUEEN
)) {
164 const Square to
= Board::flip(C1
, c
);
165 if (queen_castle_allowed(c
, from
, to
, board
, pieces
)) {
166 add(Move(from
, to
, QUEEN_CASTLE
));
171 void Game::make_move(Move m
)
173 const Square orig
= m
.orig();
174 const Square dest
= m
.dest();
175 const Square ep
= current_position().en_passant();
176 const Color c
= current_position().side();
177 const Piece p
= board
[orig
];
178 const PieceType t
= p
.type();
179 assert(!board
.is_out(orig
));
180 assert(!board
.is_out(dest
));
183 new_position(); // current_position() is now refering to a new position
184 Position
& pos
= current_position();
186 // Update halfmove counter
187 if (t
== PAWN
|| m
.is_capture()) {
188 pos
.reset_halfmove();
195 pos
.set_en_passant(OUT
);
199 // Update castling rights
200 if (pos
.can_castle(c
, KING
)) {
201 if (t
== KING
|| (t
== ROOK
&& orig
== Board::flip(H1
, c
))) {
202 pos
.set_castle_right(c
, KING
, false);
203 zobrist
.update_castle_right(pos
.hash(), c
, KING
);
206 if (pos
.can_castle(c
, QUEEN
)) {
207 if (t
== KING
|| (t
== ROOK
&& orig
== Board::flip(A1
, c
))) {
208 pos
.set_castle_right(c
, QUEEN
, false);
209 zobrist
.update_castle_right(pos
.hash(), c
, QUEEN
);
214 if (m
.is_capture()) {
216 if (m
.is_en_passant()) {
217 s
= static_cast<Square
>(ep
+ PAWN_PUSH_DIRS
[!c
]);
219 assert(!board
.is_empty(s
));
221 Piece capture
= board
[s
];
222 if (capture
.is(ROOK
)) { // Update opponent's castling rights
223 if (dest
== Board::flip(H1
, !c
)) {
224 pos
.set_castle_right(!c
, KING
, false);
225 zobrist
.update_castle_right(pos
.hash(), !c
, KING
);
226 } else if (dest
== Board::flip(A1
, !c
)) {
227 pos
.set_castle_right(!c
, QUEEN
, false);
228 zobrist
.update_castle_right(pos
.hash(), !c
, QUEEN
);
232 pos
.set_capture(capture
);
233 assert(board
.is_empty(s
));
240 switch (m
.castle_side()) {
242 rook_orig
= Board::flip(H1
, c
);
243 rook_dest
= Board::flip(F1
, c
);
246 rook_orig
= Board::flip(A1
, c
);
247 rook_dest
= Board::flip(D1
, c
);
255 Piece rook
= board
[rook_orig
];
256 board
[rook_orig
] = Piece();
257 board
[rook_dest
] = rook
;
258 pieces
.set_position(rook
, rook_dest
);
259 zobrist
.update_piece(pos
.hash(), c
, ROOK
, rook_orig
);
260 zobrist
.update_piece(pos
.hash(), c
, ROOK
, rook_dest
);
261 pos
.set_has_castled(c
); // For bonus/malus in eval
265 if (m
.is_promotion()) {
266 add_piece(p
.color(), m
.promotion_type(), dest
);
269 board
[orig
] = Piece();
271 pieces
.set_position(p
, dest
);
272 zobrist
.update_piece(pos
.hash(), c
, t
, orig
);
273 zobrist
.update_piece(pos
.hash(), c
, t
, dest
);
277 if (m
.is_double_pawn_push()) {
278 Square new_ep
= static_cast<Square
>((orig
+ dest
) / 2);
279 pos
.set_en_passant(new_ep
);
280 zobrist
.update_en_passant(pos
.hash(), new_ep
);
282 pos
.set_en_passant(OUT
);
286 void Game::undo_move(Move m
)
288 Square orig
= m
.orig();
289 Square dest
= m
.dest();
291 // Move back the piece to its origin
292 Piece p
= board
[dest
];
293 if (m
.is_promotion()) {
294 add_piece(p
.color(), PAWN
, orig
);
296 } else if (!m
.is_null()) {
298 pieces
.set_position(p
, orig
);
301 // Restore captured piece
302 if (m
.is_capture()) {
303 Piece capture
= current_position().capture();
305 if (m
.is_en_passant()) {
306 const Color c
= current_position().side();
307 s
= static_cast<Square
>(dest
+ PAWN_PUSH_DIRS
[c
]);
308 board
[dest
] = Piece();
310 add_piece(capture
.color(), capture
.type(), s
);
311 } else if (!m
.is_null()) {
312 board
[dest
] = Piece();
319 const Color c
= current_position().side();
322 switch (m
.castle_side()) {
324 rook_orig
= Board::flip(H1
, c
);
325 rook_dest
= Board::flip(F1
, c
);
328 rook_orig
= Board::flip(A1
, c
);
329 rook_dest
= Board::flip(D1
, c
);
337 Piece rook
= board
[rook_dest
];
338 board
[rook_dest
] = Piece();
339 board
[rook_orig
] = rook
;
340 pieces
.set_position(rook
, rook_orig
);
345 * Check the pseudo legality of a move m
347 bool Game::is_legal(Move m
)
349 // Null-move is obviously wrong
354 Square from
= m
.orig();
355 Square to
= m
.dest();
357 // There must be a piece to move on the board
358 if (board
.is_empty(from
)) {
362 const Piece p
= board
[from
];
363 const PieceType t
= p
.type();
364 const Color c
= p
.color();
366 // The piece cannot be one of the opponent
367 if (c
!= current_position().side()) {
371 // It must be able to do the move
372 if (!m
.is_en_passant() && !m
.is_castle()) {
373 if (!board
.can_go(p
, from
, to
)) {
379 if (t
== PAWN
&& board
.is_pawn_end(c
, to
) && !m
.is_promotion()) {
382 if (m
.is_promotion()) {
383 if (t
!= PAWN
|| !board
.is_pawn_end(c
, to
)) {
389 if (m
.is_capture()) {
392 // There are special conditions for en passant
393 if (m
.is_en_passant()) {
394 if (t
!= PAWN
) { // It must be a pawn
397 Square ep
= current_position().en_passant();
398 if (to
!= ep
) { // After a double push
401 // from another pawn, the later being captured by the former
402 s
= static_cast<Square
>(ep
+ PAWN_PUSH_DIRS
[!c
]);
403 if (board
[s
].type() != PAWN
) {
408 // An opponent's piece must be captured
409 if (board
.is_empty(s
)) {
412 if (c
== board
[s
].color()) {
416 } else if (m
.is_castle()) {
417 if (from
!= Board::flip(E1
, c
)) {
420 switch (m
.castle_side()) {
422 return king_castle_allowed(c
, from
, to
, board
, pieces
);
424 return queen_castle_allowed(c
, from
, to
, board
, pieces
);
428 } else if (m
.is_double_pawn_push()) {
432 if (!board
.is_pawn_begin(c
, from
)) {
433 return false; // Done by can_go()
435 } else if (!board
.is_empty(to
)) {