Skip to content

Commit 83f8953

Browse files
Merge pull request #165 from pleco-rs/copilot/fix-en-passant-flag
Fix en passant square not set by Board::apply_move after double pawn push
2 parents 4ba53e7 + 90d8028 commit 83f8953

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

pleco/src/board/board_state.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,13 @@ impl BoardState {
205205

206206
let ep = self.ep_square;
207207
if ep != NO_SQ {
208-
self.zobrist ^= z_ep(ep);
208+
// Only include EP in zobrist hash if an opponent pawn can capture en passant
209+
let ep_owner = !board.turn;
210+
if (pawn_attacks_from(ep, ep_owner) & board.piece_bb(board.turn, PieceType::P))
211+
.is_not_empty()
212+
{
213+
self.zobrist ^= z_ep(ep);
214+
}
209215
}
210216

211217
match board.turn {

pleco/src/board/mod.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,15 @@ impl Board {
787787
zob ^= z_square(to, piece) ^ z_square(from, piece);
788788

789789
if self.state.ep_square != NO_SQ {
790-
zob ^= z_ep(self.state.ep_square);
790+
// Only XOR out the EP hash if it was included in the zobrist
791+
// (it was included only when an opponent pawn could capture en passant)
792+
let ep_owner = !self.turn;
793+
if (pawn_attacks_from(self.state.ep_square, ep_owner)
794+
& self.piece_bb(self.turn, PieceType::P))
795+
.is_not_empty()
796+
{
797+
zob ^= z_ep(self.state.ep_square);
798+
}
791799
new_state.ep_square = NO_SQ;
792800
}
793801

@@ -810,11 +818,14 @@ impl Board {
810818
// Double Push
811819
let poss_ep: u8 = (to.0 as i8 - us.pawn_push()) as u8;
812820

813-
// Set en-passant square if the moved pawn can be captured
821+
// Always set en-passant square after a double pawn push,
822+
// per the FEN standard
823+
new_state.ep_square = SQ(poss_ep);
824+
825+
// Only include in zobrist hash if the moved pawn can be captured
814826
if (pawn_attacks_from(SQ(poss_ep), us) & self.piece_bb(them, PieceType::P))
815827
.is_not_empty()
816828
{
817-
new_state.ep_square = SQ(poss_ep);
818829
zob ^= z_ep(new_state.ep_square);
819830
}
820831
} else if bit_move.is_promo() {
@@ -1017,7 +1028,14 @@ impl Board {
10171028
new_state.prev = Some(Arc::clone(&self.state));
10181029

10191030
if self.state.ep_square != NO_SQ {
1020-
zob ^= z_ep(self.state.ep_square);
1031+
// Only XOR out the EP hash if it was included in the zobrist
1032+
let ep_owner = !self.turn;
1033+
if (pawn_attacks_from(self.state.ep_square, ep_owner)
1034+
& self.piece_bb(self.turn, PieceType::P))
1035+
.is_not_empty()
1036+
{
1037+
zob ^= z_ep(self.state.ep_square);
1038+
}
10211039
new_state.ep_square = NO_SQ;
10221040
}
10231041

pleco/tests/board_move_apply.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extern crate rand;
33

44
use pleco::board::Board;
55
use pleco::core::piece_move::BitMove;
6+
use pleco::core::sq::SQ;
67
use std::*;
78

89
#[test]
@@ -42,3 +43,15 @@ fn apply_null_moves() {
4243
trials += 1;
4344
}
4445
}
46+
47+
#[test]
48+
fn double_pawn_push_sets_ep_square() {
49+
let fen1 = "r1bqkbnr/pppppppp/2n5/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 1 2";
50+
let mut board = Board::from_fen(fen1).unwrap();
51+
52+
let move_to_play = BitMove::make(BitMove::FLAG_DOUBLE_PAWN, SQ::D2, SQ::D4);
53+
board.apply_move(move_to_play);
54+
55+
let expected_fen = "r1bqkbnr/pppppppp/2n5/8/3PP3/8/PPP2PPP/RNBQKBNR b KQkq d3 0 2";
56+
assert_eq!(expected_fen, board.fen());
57+
}

0 commit comments

Comments
 (0)