Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pleco/src/board/board_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,13 @@ impl BoardState {

let ep = self.ep_square;
if ep != NO_SQ {
self.zobrist ^= z_ep(ep);
// Only include EP in zobrist hash if an opponent pawn can capture en passant
let ep_owner = !board.turn;
if (pawn_attacks_from(ep, ep_owner) & board.piece_bb(board.turn, PieceType::P))
.is_not_empty()
{
self.zobrist ^= z_ep(ep);
}
}

match board.turn {
Expand Down
26 changes: 22 additions & 4 deletions pleco/src/board/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,15 @@ impl Board {
zob ^= z_square(to, piece) ^ z_square(from, piece);

if self.state.ep_square != NO_SQ {
zob ^= z_ep(self.state.ep_square);
// Only XOR out the EP hash if it was included in the zobrist
// (it was included only when an opponent pawn could capture en passant)
let ep_owner = !self.turn;
if (pawn_attacks_from(self.state.ep_square, ep_owner)
& self.piece_bb(self.turn, PieceType::P))
.is_not_empty()
{
zob ^= z_ep(self.state.ep_square);
}
new_state.ep_square = NO_SQ;
}

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

// Set en-passant square if the moved pawn can be captured
// Always set en-passant square after a double pawn push,
// per the FEN standard
new_state.ep_square = SQ(poss_ep);

// Only include in zobrist hash if the moved pawn can be captured
if (pawn_attacks_from(SQ(poss_ep), us) & self.piece_bb(them, PieceType::P))
.is_not_empty()
{
new_state.ep_square = SQ(poss_ep);
zob ^= z_ep(new_state.ep_square);
}
} else if bit_move.is_promo() {
Expand Down Expand Up @@ -1017,7 +1028,14 @@ impl Board {
new_state.prev = Some(Arc::clone(&self.state));

if self.state.ep_square != NO_SQ {
zob ^= z_ep(self.state.ep_square);
// Only XOR out the EP hash if it was included in the zobrist
let ep_owner = !self.turn;
if (pawn_attacks_from(self.state.ep_square, ep_owner)
& self.piece_bb(self.turn, PieceType::P))
.is_not_empty()
{
zob ^= z_ep(self.state.ep_square);
}
new_state.ep_square = NO_SQ;
}

Expand Down
13 changes: 13 additions & 0 deletions pleco/tests/board_move_apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ extern crate rand;

use pleco::board::Board;
use pleco::core::piece_move::BitMove;
use pleco::core::sq::SQ;
use std::*;

#[test]
Expand Down Expand Up @@ -42,3 +43,15 @@ fn apply_null_moves() {
trials += 1;
}
}

#[test]
fn double_pawn_push_sets_ep_square() {
let fen1 = "r1bqkbnr/pppppppp/2n5/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 1 2";
let mut board = Board::from_fen(fen1).unwrap();

let move_to_play = BitMove::make(BitMove::FLAG_DOUBLE_PAWN, SQ::D2, SQ::D4);
board.apply_move(move_to_play);

let expected_fen = "r1bqkbnr/pppppppp/2n5/8/3PP3/8/PPP2PPP/RNBQKBNR b KQkq d3 0 2";
assert_eq!(expected_fen, board.fen());
}