Refactor time info, update zobrist to tuple struct, naming and clippy

This commit is contained in:
stefiosif
2025-01-26 20:16:01 +02:00
parent 36aa30ea17
commit 53beda7fe3
11 changed files with 78 additions and 77 deletions

View File

@@ -31,6 +31,7 @@ pub const fn lsb(bitboard: Bitboard) -> usize {
bitboard.trailing_zeros() as usize
}
#[allow(dead_code)]
pub const fn bit_count(bitboard: Bitboard) -> usize {
bitboard.count_ones() as usize
}

View File

@@ -83,7 +83,8 @@ impl Game {
mailbox.set_piece_at(mv.dst, Some(piece_at_src));
}
MoveType::Capture => {
let piece_at_dst = piece_at_dst.expect("Expected piece at: {mv.dst}");
let piece_at_dst =
piece_at_dst.unwrap_or_else(|| panic!("Expected piece at: {}", mv.dst));
board.remove_opponent_piece(mv.dst, piece_at_dst.0);
hash.update_capture(mv.src, mv.dst, piece_at_src.0, piece_at_dst.0, color);
board.move_piece(mv.src, mv.dst, piece_at_src.0);
@@ -116,7 +117,8 @@ impl Game {
mailbox.set_piece_at(mv.dst, Some((promote.into_piece_type(), color)));
}
MoveType::PromotionCapture(promote) => {
let piece_at_dst = piece_at_dst.expect("Expected piece at dst: {mv.dst}");
let piece_at_dst =
piece_at_dst.unwrap_or_else(|| panic!("Expected piece at: {}", mv.dst));
board.remove_own_piece(mv.src, piece_at_src.0);
board.remove_opponent_piece(mv.dst, piece_at_dst.0);
board.promote_piece(mv.dst, promote);

View File

@@ -1,5 +1,6 @@
pub struct Square {}
#[allow(dead_code)]
impl Square {
pub const A1: usize = 0;
pub const B1: usize = 1;

View File

@@ -17,7 +17,7 @@ pub fn zobrist_keys() -> &'static ZobristKeys {
}
pub struct ZobristKeys {
piece_square_color: [[[u64; 2]; 6]; 64],
square_piece_color: [[[u64; 2]; 6]; 64],
en_passant: [u64; 8],
castling_ability: [[u64; 2]; 4],
side_to_move: u64,
@@ -30,8 +30,8 @@ impl ZobristKeys {
for square in Square::A1..=Square::H8 {
for piece_idx in 0..6 {
keys.piece_square_color[square][piece_idx][0] = state.next_u64();
keys.piece_square_color[square][piece_idx][1] = state.next_u64();
keys.square_piece_color[square][piece_idx][0] = state.next_u64();
keys.square_piece_color[square][piece_idx][1] = state.next_u64();
}
}
@@ -56,7 +56,7 @@ impl ZobristKeys {
while bitboard != 0 {
let square = lsb(bitboard);
hash ^= self.piece_square_color[square][piece_type][Color::White];
hash ^= self.square_piece_color[square][piece_type][Color::White];
bitboard &= bitboard - 1;
}
@@ -64,7 +64,7 @@ impl ZobristKeys {
while bitboard != 0 {
let square = lsb(bitboard);
hash ^= self.piece_square_color[square][piece_type][Color::Black];
hash ^= self.square_piece_color[square][piece_type][Color::Black];
bitboard &= bitboard - 1;
}
});
@@ -87,7 +87,7 @@ impl ZobristKeys {
impl Default for ZobristKeys {
fn default() -> Self {
Self {
piece_square_color: [[[0; 2]; 6]; 64],
square_piece_color: [[[0; 2]; 6]; 64],
en_passant: [0; 8],
castling_ability: [[0; 2]; 4],
side_to_move: 0,
@@ -96,18 +96,16 @@ impl Default for ZobristKeys {
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct ZobristHash {
pub hash: u64,
}
pub struct ZobristHash(pub u64);
impl ZobristHash {
pub const fn new(hash: u64) -> Self {
Self { hash }
Self(hash)
}
pub fn update_side_to_move_key(&mut self) {
let keys = zobrist_keys();
self.hash ^= keys.side_to_move
self.0 ^= keys.side_to_move
}
pub fn update_castling_ability_keys(
@@ -116,16 +114,16 @@ impl ZobristHash {
new_castling_ability: [Castle; 2],
) {
let keys = zobrist_keys();
self.hash ^= keys.castling_ability[old_castling_ability[0]][0];
self.hash ^= keys.castling_ability[old_castling_ability[1]][1];
self.hash ^= keys.castling_ability[new_castling_ability[0]][0];
self.hash ^= keys.castling_ability[new_castling_ability[1]][1]
self.0 ^= keys.castling_ability[old_castling_ability[0]][0];
self.0 ^= keys.castling_ability[old_castling_ability[1]][1];
self.0 ^= keys.castling_ability[new_castling_ability[0]][0];
self.0 ^= keys.castling_ability[new_castling_ability[1]][1]
}
pub fn update_quiet(&mut self, src: usize, dst: usize, piece_at_src: PieceType, color: Color) {
let keys = zobrist_keys();
self.hash ^= keys.piece_square_color[src][piece_at_src][color];
self.hash ^= keys.piece_square_color[dst][piece_at_src][color];
self.0 ^= keys.square_piece_color[src][piece_at_src][color];
self.0 ^= keys.square_piece_color[dst][piece_at_src][color];
}
pub fn update_capture(
@@ -137,16 +135,16 @@ impl ZobristHash {
color: Color,
) {
let keys = zobrist_keys();
self.hash ^= keys.piece_square_color[src][piece_at_src][color];
self.hash ^= keys.piece_square_color[dst][piece_at_src][color];
self.hash ^= keys.piece_square_color[dst][piece_at_dst][color.opponent()]
self.0 ^= keys.square_piece_color[src][piece_at_src][color];
self.0 ^= keys.square_piece_color[dst][piece_at_src][color];
self.0 ^= keys.square_piece_color[dst][piece_at_dst][color.opponent()]
}
pub fn update_en_passant(&mut self, src: usize, dst: usize, ep_capture: usize, color: Color) {
let keys = zobrist_keys();
self.hash ^= keys.piece_square_color[src][PieceType::Pawn][color];
self.hash ^= keys.piece_square_color[dst][PieceType::Pawn][color];
self.hash ^= keys.piece_square_color[ep_capture][PieceType::Pawn][color.opponent()]
self.0 ^= keys.square_piece_color[src][PieceType::Pawn][color];
self.0 ^= keys.square_piece_color[dst][PieceType::Pawn][color];
self.0 ^= keys.square_piece_color[ep_capture][PieceType::Pawn][color.opponent()]
}
pub fn update_double_push(
@@ -157,18 +155,18 @@ impl ZobristHash {
new_en_passant_target: Option<usize>,
) {
let keys = zobrist_keys();
self.hash ^= keys.piece_square_color[src][PieceType::Pawn][color];
self.hash ^= keys.piece_square_color[dst][PieceType::Pawn][color];
self.0 ^= keys.square_piece_color[src][PieceType::Pawn][color];
self.0 ^= keys.square_piece_color[dst][PieceType::Pawn][color];
if let Some(new_en_passant) = new_en_passant_target {
self.hash ^= keys.en_passant[square::to_file(new_en_passant)];
self.0 ^= keys.en_passant[square::to_file(new_en_passant)];
}
}
pub fn update_promotion(&mut self, src: usize, dst: usize, promote: &Promote, color: Color) {
let keys = zobrist_keys();
self.hash ^= keys.piece_square_color[src][PieceType::Pawn][color];
self.hash ^= keys.piece_square_color[dst][promote.into_piece_type()][color];
self.0 ^= keys.square_piece_color[src][PieceType::Pawn][color];
self.0 ^= keys.square_piece_color[dst][promote.into_piece_type()][color];
}
pub fn update_promotion_capture(
@@ -180,9 +178,9 @@ impl ZobristHash {
color: Color,
) {
let keys = zobrist_keys();
self.hash ^= keys.piece_square_color[src][PieceType::Pawn][color];
self.hash ^= keys.piece_square_color[dst][piece_at_dst][color.opponent()];
self.hash ^= keys.piece_square_color[dst][promote.into_piece_type()][color];
self.0 ^= keys.square_piece_color[src][PieceType::Pawn][color];
self.0 ^= keys.square_piece_color[dst][piece_at_dst][color.opponent()];
self.0 ^= keys.square_piece_color[dst][promote.into_piece_type()][color];
}
pub fn update_castle(
@@ -195,16 +193,16 @@ impl ZobristHash {
color: Color,
) {
let keys = zobrist_keys();
self.hash ^= keys.piece_square_color[src][piece_at_src][color];
self.hash ^= keys.piece_square_color[dst][piece_at_src][color];
self.hash ^= keys.piece_square_color[rook_src][PieceType::Rook][color];
self.hash ^= keys.piece_square_color[rook_dst][PieceType::Rook][color];
self.0 ^= keys.square_piece_color[src][piece_at_src][color];
self.0 ^= keys.square_piece_color[dst][piece_at_src][color];
self.0 ^= keys.square_piece_color[rook_src][PieceType::Rook][color];
self.0 ^= keys.square_piece_color[rook_dst][PieceType::Rook][color];
}
pub fn drop_en_passant_hash(&mut self, en_passant: Option<usize>) {
let keys = zobrist_keys();
if let Some(ep) = en_passant {
self.hash ^= keys.en_passant[square::to_file(ep)]
self.0 ^= keys.en_passant[square::to_file(ep)]
}
}
}

View File

@@ -194,13 +194,13 @@ pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboar
attacks
}
pub fn set_occupancy(index: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> Bitboard {
pub fn set_occupancy(idx: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> Bitboard {
let mut occupancy = EMPTY;
for bit in 0..relevant_bits {
let square = lsb(attack_mask);
attack_mask &= !square_to_bitboard(square);
if have_common_bit(index, square_to_bitboard(bit)) {
if have_common_bit(idx, square_to_bitboard(bit)) {
occupancy |= square_to_bitboard(square);
}
}
@@ -269,12 +269,12 @@ pub fn init_bishop_attacks() -> Box<[[Bitboard; 512]; 64]> {
let attack_mask = bishop_masks[square];
let occupancy_indices = square_to_bitboard(BISHOP_RELEVANT_BITS[square]);
for index in 0..occupancy_indices {
let occupancy = set_occupancy(index, BISHOP_RELEVANT_BITS[square], attack_mask);
let magic_index = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
for idx in 0..occupancy_indices {
let occupancy = set_occupancy(idx, BISHOP_RELEVANT_BITS[square], attack_mask);
let magic_idx = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
>> (64 - BISHOP_RELEVANT_BITS[square])) as usize;
bishop_atks[square][magic_index] =
bishop_atks[square][magic_idx] =
bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy);
}
}
@@ -297,11 +297,11 @@ pub fn init_rook_attacks() -> HashMap<usize, HashMap<usize, Bitboard>> {
for idx in 0..occupancy_indices {
let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask);
let magic_index = (occupancy.wrapping_mul(ROOK_MAGIC[square])
let magic_idx = (occupancy.wrapping_mul(ROOK_MAGIC[square])
>> (64 - ROOK_RELEVANT_BITS[square])) as usize;
square_map.insert(
magic_index,
magic_idx,
rook_attacks_on_the_fly(square_to_bitboard(square), occupancy),
);
}

View File

@@ -9,25 +9,26 @@ use crate::{
use super::{
negamax,
time::{hard_limit, TimeInfo},
time::TimeInfo,
};
pub fn iterative_deepening(
game: &mut Game,
max_depth: u8,
time_info: &TimeInfo,
time: &TimeInfo,
) -> anyhow::Result<Option<Move>> {
let mut best_move = None;
for depth in 1..=max_depth {
if hard_limit(time_info.instant, time_info.time, time_info.inc) {
if time.exceed_hard_limit() {
return Ok(best_move);
}
let mut nodes = 0;
let score = negamax::negamax(game, MIN_SCORE, MAX_SCORE, depth, 0, time_info, &mut nodes);
let score = negamax::negamax(game, MIN_SCORE, MAX_SCORE, depth, 0, time, &mut nodes);
if score.is_err() {
if let Err(e) = score {
write_response(&mut io::stdout(), &Response::Info(format!("{e}")))?;
break;
}
@@ -37,9 +38,9 @@ pub fn iterative_deepening(
&mut io::stdout(),
&log_depth_results(
depth,
time_info.instant.elapsed().as_millis() as u64,
time.instant.elapsed().as_millis() as u64,
nodes,
time_info.nps(nodes),
time.nps(nodes),
score?,
best_move,
),

View File

@@ -6,10 +6,9 @@ pub fn score_move(mailbox: &Mailbox, mv: Move, tt_move: Option<Move>) -> i32 {
}
let aggressor = mailbox.piece_at(mv.src).expect("No aggressor found.");
match mailbox.piece_at(mv.dst) {
Some(victim) => aggressor.0.idx() as i32 - (victim.0.idx() * 8) as i32,
None => 100,
}
mailbox.piece_at(mv.dst).map_or(100, |victim| {
aggressor.0.idx() as i32 - (victim.0.idx() * 8) as i32
})
}
#[cfg(test)]
@@ -35,7 +34,6 @@ mod tests {
assert_eq!(moves, vec![pawn_takes_queen, queen_takes_pawn, castle]);
let castle = Move::with_type(Square::E1, Square::C1, MoveType::Castle);
moves.sort_unstable_by_key(|mv| score_move(&game.mailbox, *mv, Some(castle)));
assert_eq!(moves, vec![castle, pawn_takes_queen, queen_takes_pawn]);

View File

@@ -8,7 +8,7 @@ use crate::{
use super::{
move_ordering::score_move,
quiescence::quiescence,
time::{hard_limit, TimeInfo},
time::TimeInfo,
transposition_table::TTEntry,
};
@@ -18,11 +18,11 @@ pub fn negamax(
beta: i32,
depth: u8,
plies: u8,
time_info: &TimeInfo,
time: &TimeInfo,
nodes: &mut u64,
) -> Result<i32> {
if hard_limit(time_info.instant, time_info.time, time_info.inc) {
bail!("Time is up! In Negamax");
if time.exceed_hard_limit() {
bail!("Hard limit exceeded in negamax");
}
if plies != 0 && game.in_repetition() {
@@ -31,7 +31,7 @@ pub fn negamax(
if depth == 0 {
let q_score =
quiescence(game, alpha, beta, time_info, nodes).map_err(|e| anyhow!("{e}"))?;
quiescence(game, alpha, beta, time, nodes).map_err(|e| anyhow!("{e}"))?;
return Ok(q_score);
}
@@ -55,7 +55,7 @@ pub fn negamax(
*nodes += 1;
let score = if legal_moves == 1 {
-negamax(game, -beta, -alpha, depth - 1, plies + 1, time_info, nodes)?
-negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)?
} else {
let mut score = -negamax(
game,
@@ -63,11 +63,11 @@ pub fn negamax(
-alpha,
depth - 1,
plies + 1,
time_info,
time,
nodes,
)?;
if score > alpha && score < beta {
score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, time_info, nodes)?;
score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)?;
}
score
};

View File

@@ -4,18 +4,18 @@ use crate::{board::game::Game, evaluation::pesto::pesto};
use super::{
move_ordering::score_move,
time::{hard_limit, TimeInfo},
time::TimeInfo,
};
pub fn quiescence(
game: &mut Game,
mut alpha: i32,
beta: i32,
time_info: &TimeInfo,
time: &TimeInfo,
total_nodes_searched: &mut u64,
) -> Result<i32> {
if hard_limit(time_info.instant, time_info.time, time_info.inc) {
bail!("Time is up! In Quiescence");
if time.exceed_hard_limit() {
bail!("Hard limit exceeded in quiescence");
}
let stand_pat = pesto().eval(game);
@@ -44,7 +44,7 @@ pub fn quiescence(
}
*total_nodes_searched += 1;
let score = -quiescence(game, -beta, -alpha, time_info, &mut 0)?;
let score = -quiescence(game, -beta, -alpha, time, &mut 0)?;
game.unmake_move();
if score > best_score {

View File

@@ -16,8 +16,8 @@ impl TimeInfo {
pub fn nps(&self, nodes: u64) -> u64 {
((nodes * 1_000_000) as u128).div_ceil(self.instant.elapsed().as_micros()) as u64
}
}
pub fn hard_limit(time_now: Instant, time: u128, inc: u128) -> bool {
time_now.elapsed().as_millis() >= time / HARD_LIMIT_DIVISION + inc / 2
pub fn exceed_hard_limit(&self) -> bool {
self.instant.elapsed().as_millis() >= self.time / HARD_LIMIT_DIVISION + self.inc / 2
}
}

View File

@@ -17,15 +17,15 @@ impl TranspositionTable {
pub fn lookup(&self, zobrist_hash: ZobristHash) -> Option<&TTEntry> {
let entry = self
.positions
.get((zobrist_hash.hash % self.size) as usize)
.get((zobrist_hash.0 % self.size) as usize)
.and_then(|entry| entry.as_ref());
entry
}
pub fn insert(&mut self, tt_entry: TTEntry) {
let index = (tt_entry.hash.hash % self.size) as usize;
self.positions[index] = Some(tt_entry);
let idx = (tt_entry.hash.0 % self.size) as usize;
self.positions[idx] = Some(tt_entry);
}
}