diff --git a/src/board/bitboard.rs b/src/board/bitboard.rs index 9ac832c..64a1f36 100644 --- a/src/board/bitboard.rs +++ b/src/board/bitboard.rs @@ -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 } diff --git a/src/board/game.rs b/src/board/game.rs index 1b48370..5b00dfe 100644 --- a/src/board/game.rs +++ b/src/board/game.rs @@ -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); diff --git a/src/board/square.rs b/src/board/square.rs index 28c6af2..74e0a82 100644 --- a/src/board/square.rs +++ b/src/board/square.rs @@ -1,5 +1,6 @@ pub struct Square {} +#[allow(dead_code)] impl Square { pub const A1: usize = 0; pub const B1: usize = 1; diff --git a/src/board/zobrist.rs b/src/board/zobrist.rs index 3800d88..b3c4aa7 100644 --- a/src/board/zobrist.rs +++ b/src/board/zobrist.rs @@ -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, ) { 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) { 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)] } } } diff --git a/src/movegen/attack_generator.rs b/src/movegen/attack_generator.rs index e5beae5..e70f22e 100644 --- a/src/movegen/attack_generator.rs +++ b/src/movegen/attack_generator.rs @@ -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> { 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), ); } diff --git a/src/search/iterative_deepening.rs b/src/search/iterative_deepening.rs index 58b38eb..23bc9e7 100644 --- a/src/search/iterative_deepening.rs +++ b/src/search/iterative_deepening.rs @@ -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> { 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, ), diff --git a/src/search/move_ordering.rs b/src/search/move_ordering.rs index 7402897..4534aff 100644 --- a/src/search/move_ordering.rs +++ b/src/search/move_ordering.rs @@ -6,10 +6,9 @@ pub fn score_move(mailbox: &Mailbox, mv: Move, tt_move: Option) -> 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]); diff --git a/src/search/negamax.rs b/src/search/negamax.rs index b4cc2f1..edd998a 100644 --- a/src/search/negamax.rs +++ b/src/search/negamax.rs @@ -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 { - 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 }; diff --git a/src/search/quiescence.rs b/src/search/quiescence.rs index 5ec3374..6f66a26 100644 --- a/src/search/quiescence.rs +++ b/src/search/quiescence.rs @@ -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 { - 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 { diff --git a/src/search/time.rs b/src/search/time.rs index cfa21b1..bb7b210 100644 --- a/src/search/time.rs +++ b/src/search/time.rs @@ -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 + } } diff --git a/src/search/transposition_table.rs b/src/search/transposition_table.rs index 72f6ae3..ec8aae2 100644 --- a/src/search/transposition_table.rs +++ b/src/search/transposition_table.rs @@ -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); } }