Refactor time info, update zobrist to tuple struct, naming and clippy
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub struct Square {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Square {
|
||||
pub const A1: usize = 0;
|
||||
pub const B1: usize = 1;
|
||||
|
||||
@@ -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)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user