Skip to content

Commit

Permalink
Work around hash collision
Browse files Browse the repository at this point in the history
  • Loading branch information
bdmendes committed Dec 25, 2024
1 parent b44b30a commit 3c1cb3a
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 83 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ rand = "0.8.5"
ctor = "0.2.8"
rayon = "1.10.0"
rstest = "0.23.0"
portable-atomic = "1.10.0"

[profile.dev]
opt-level = 1
Expand Down
4 changes: 2 additions & 2 deletions src/core/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use super::{
pub struct ZobristHash(pub u64);

// 2 colors, 6 pieces, 64 squares + 1 color + 4 castling rights + 64 ep squares
const ZOBRIST_NUMBERS_SIZE: usize = 2 * 6 * 64 + 2 + 4 + 64;
const ZOBRIST_NUMBERS_SIZE: usize = 2 * 6 * 64 + 1 + 4 + 64;

#[ctor]
static ZOBRIST_NUMBERS: [ZobristHash; ZOBRIST_NUMBERS_SIZE] = {
let mut rng = StdRng::seed_from_u64(0);
let mut rng = StdRng::seed_from_u64(42);
let mut numbers = [0; ZOBRIST_NUMBERS_SIZE];
numbers.iter_mut().take(ZOBRIST_NUMBERS_SIZE).for_each(|n| *n = rng.gen());
numbers.map(ZobristHash)
Expand Down
13 changes: 7 additions & 6 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@ impl Position {
generate_moves(self, stage)
}

pub fn is_legal(&mut self) -> bool {
self.flip_side_to_move();
let res = !self.is_check();
self.flip_side_to_move();
res
}

pub fn make_move(&self, mov: Move) -> Self {
make_move::<true>(self, mov)
}
Expand All @@ -276,12 +283,6 @@ impl Position {
for m in moves {
if mov == m.to_string().as_str() {
let new_position = self.make_move(m);
/*println!(
"hash after {}: {} {}",
mov,
new_position.hash().0,
new_position.hash_from_scratch().0
);*/
return Some(new_position);
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/core/moves/gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ use magics::{bishop_attacks, queen_attacks, rook_attacks};
use pawns::{pawn_attackers, pawn_moves, PAWN_ATTACKS_BLACK, PAWN_ATTACKS_WHITE};
use sliders::{bishop_moves, diagonal_attackers, file_attackers, queen_moves, rook_moves};

mod castle;
mod leapers;
mod magics;
mod pawns;
mod sliders;
pub mod castle;
pub mod leapers;
pub mod magics;
pub mod pawns;
pub mod sliders;

pub fn generate_moves(position: &Position, stage: MoveStage) -> Vec<Move> {
let mut moves = Vec::with_capacity(match stage {
Expand Down
21 changes: 17 additions & 4 deletions src/core/moves/make.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::core::{
Position,
};

use super::{Move, MoveFlag};
use super::{gen::pawns::pawn_attackers, Move, MoveFlag};

static COLOR_CASTLE_RANKS: [Bitboard; 2] = [Bitboard::rank_mask(0), Bitboard::rank_mask(7)];
static TO_SQUARE_KINGSIDE: [Square; 2] = [Square::G1, Square::G8];
Expand Down Expand Up @@ -39,7 +39,9 @@ fn make_castle<const UPDATE_META: bool>(

pub fn make_move<const UPDATE_META: bool>(position: &Position, mov: Move) -> Position {
let mut position = *position;

if position.piece_at(mov.from()).is_none() {
panic!("m: {}", mov);
}
let piece = position.piece_at(mov.from()).unwrap();
let side_to_move = position.side_to_move();

Expand Down Expand Up @@ -72,6 +74,7 @@ pub fn make_move<const UPDATE_META: bool>(position: &Position, mov: Move) -> Pos
));
}
MoveFlag::Quiet | MoveFlag::DoublePawnPush => {
// NOTICE: clear should not be needed here. This is a workaround for hash collisions.
position.set_square_low::<UPDATE_META, true>(mov.to(), piece, side_to_move);
}
MoveFlag::Capture => {
Expand Down Expand Up @@ -131,10 +134,15 @@ pub fn make_move<const UPDATE_META: bool>(position: &Position, mov: Move) -> Pos
});

if mov.flag() == MoveFlag::DoublePawnPush {
position.set_ep_square(match side_to_move {
let candidate_ep = match side_to_move {
Color::White => mov.to() >> 8,
Color::Black => mov.to() << 8,
});
};
if !pawn_attackers(&position, side_to_move.flipped(), candidate_ep).is_empty() {
position.set_ep_square(candidate_ep);
} else {
position.clear_ep_square();
}
} else {
position.clear_ep_square();
};
Expand Down Expand Up @@ -172,6 +180,11 @@ mod tests {
"e1d1",
"r3k2r/8/3Q4/8/8/8/8/R2K3R b kq - 0 2"
)]
#[case(
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"e2e4",
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
)]
fn make(#[case] position: &str, #[case] mov: &str, #[case] expected: &str) {
let position = Position::from_str(position).unwrap();
let moves = position.moves(MoveStage::All);
Expand Down
5 changes: 2 additions & 3 deletions src/core/moves/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,18 @@ impl Move {
}

pub fn is_pseudo_legal(&self, position: &Position) -> bool {
let from_piece_color = position.piece_color_at(self.from());
let to_color = position.color_at(self.to());
let to_piece = position.piece_at(self.to());

if let Some((piece, color)) = from_piece_color {
if let Some((piece, color)) = position.piece_color_at(self.from()) {
// Basic legality assumptions. This is a good and fast start,
// but not sufficient.
if color != position.side_to_move
|| (to_color == Some(position.side_to_move) && !self.is_castle())
|| to_piece == Some(Piece::King)
|| (self.is_capture()
&& self.flag() != MoveFlag::EnpassantCapture
&& to_piece.is_none())
&& to_color != Some(position.side_to_move.flipped()))
{
return false;
}
Expand Down
1 change: 0 additions & 1 deletion src/engine/commands/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ pub fn execute_do_move(mov_str: &str, position: &mut Position) {

pub fn execute_display(position: &Position) {
print!("{}", position);
println!("{}", position.fen());
println!("Static evaluation: {}", position.value());
println!("Chess960: {}", position.is_chess_960());
println!(
Expand Down
4 changes: 3 additions & 1 deletion src/evaluation/moves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use crate::{

pub fn evaluate_move(position: &Position, mov: Move) -> ValueScore {
let mut score = 0;

if position.piece_at(mov.from()).is_none() {
panic!("m: {}", mov);
}
let moving_piece = position.piece_at(mov.from()).unwrap();

if mov.is_capture() {
Expand Down
23 changes: 20 additions & 3 deletions src/search/movepick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
evaluation::{moves::evaluate_move, Evaluable, ValueScore},
};
use rand::{thread_rng, Rng};
use std::sync::Arc;
use std::{sync::Arc, thread::panicking};

type ScoredVec<Move> = Vec<(Move, ValueScore)>;

Expand All @@ -28,7 +28,12 @@ impl MovePicker<true> {
});
Self {
index: 0,
moves: decorate_moves_with_score(&moves, |mov| evaluate_move(position, mov)),
moves: decorate_moves_with_score(&moves, |mov| {
if mov.to_string().as_str() == "a1c1=N" || mov.to_string().as_str() == "a1g1=N" {
panic!("p2: {}", position.fen());
}
evaluate_move(position, mov)
}),
stage: MoveStage::CapturesAndPromotions,
position: *position,
table: None,
Expand Down Expand Up @@ -85,7 +90,14 @@ impl std::iter::Iterator for MovePicker<false> {
self.stage = MoveStage::CapturesAndPromotions;
self.moves = decorate_moves_with_score(
&self.position.moves(MoveStage::CapturesAndPromotions),
|mov| evaluate_move(&self.position, mov),
|mov| {
if mov.to_string().as_str() == "a1c1=N"
|| mov.to_string().as_str() == "a1g1=N"
{
panic!("p4: {}", self.position.fen());
}
evaluate_move(&self.position, mov)
},
);

self.index = 0;
Expand All @@ -100,6 +112,11 @@ impl std::iter::Iterator for MovePicker<false> {
if killers[1] == Some(mov) || killers[0] == Some(mov) {
Piece::Queen.value()
} else {
if mov.to_string().as_str() == "a1c1=N"
|| mov.to_string().as_str() == "a1g1=N"
{
panic!("p6: {}", self.position.fen());
}
evaluate_move(&self.position, mov)
}
});
Expand Down
4 changes: 2 additions & 2 deletions src/search/pvs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,13 @@ fn pvs<const ROOT: bool, const MAIN_THREAD: bool, const ALLOW_NMR: bool>(
}
}

let mut new_position = position.make_move(mov);

// Late move reduction: we assume our move ordering is good, and are less interested in
// expected non-PV nodes.
let late_move_reduction =
if depth > 2 && !is_check && mov.is_quiet() && i > 0 { 1 } else { 0 };

let mut new_position = position.make_move(mov);

history.visit_position(&new_position, mov.is_reversible());
let (score, nodes) = pvs_recurse::<MAIN_THREAD>(
&mut new_position,
Expand Down
Loading

0 comments on commit 3c1cb3a

Please sign in to comment.