diff --git a/src/board.rs b/src/board.rs index 77f282e10..f8f795f7d 100644 --- a/src/board.rs +++ b/src/board.rs @@ -27,7 +27,7 @@ struct InternalState { keys: Keys, en_passant: Square, castling: Castling, - halfmove_clock: u8, + fiftymove_clock: u8, material: i32, plies_from_null: usize, repetition: i32, @@ -42,13 +42,12 @@ struct InternalState { #[derive(Clone)] pub struct Board { - side_to_move: Color, pieces: [Bitboard; PieceType::NUM], colors: [Bitboard; Color::NUM], mailbox: [Piece; Square::NUM], state: InternalState, state_stack: Vec, - fullmove_number: usize, + halfmove_number: usize, castling_rights: [u8; Square::NUM], castling_path: [Bitboard; 16], castling_threat: [Bitboard; 16], @@ -65,23 +64,23 @@ impl Board { self.frc } - pub const fn side_to_move(&self) -> Color { - self.side_to_move + pub const fn fullmove_number(&self) -> usize { + self.halfmove_number / 2 } - pub const fn fullmove_number(&self) -> usize { - self.fullmove_number + pub fn side_to_move(&self) -> Color { + Color::new((self.halfmove_number & 1) as u8) } - pub fn halfmove_clock_bucket(&self) -> usize { - (self.halfmove_clock().saturating_sub(8) as usize / 8).min(15) + pub fn fiftymove_clock_bucket(&self) -> usize { + (self.fiftymove_clock().saturating_sub(8) as usize / 8).min(15) } pub fn hash(&self) -> u64 { // To mitigate Graph History Interaction (GHI) problems, the hash key is changed // every 8 plies to distinguish between positions that would otherwise appear // identical to the transposition table. - self.state.keys.full() ^ ZOBRIST.halfmove_clock[self.halfmove_clock_bucket()] + self.state.keys.full() ^ ZOBRIST.fiftymove_clock[self.fiftymove_clock_bucket()] } pub const fn pawn_key(&self) -> u64 { @@ -133,8 +132,8 @@ impl Board { self.state.castling } - pub const fn halfmove_clock(&self) -> u8 { - self.state.halfmove_clock + pub const fn fiftymove_clock(&self) -> u8 { + self.state.fiftymove_clock } pub const fn material(&self) -> i32 { @@ -193,14 +192,6 @@ impl Board { self.mailbox[mv.from()] } - pub const fn advance_fullmove_counter(&mut self) { - self.fullmove_number += self.side_to_move() as usize; - } - - pub const fn retreat_fullmove_counter(&mut self) { - self.fullmove_number -= self.side_to_move() as usize; - } - pub const fn set_frc(&mut self, frc: bool) { self.frc = frc; } @@ -258,12 +249,12 @@ impl Board { } pub fn has_repeated(&self) -> bool { - let end = self.state.plies_from_null.min(self.state.halfmove_clock as usize); + let end = self.state.plies_from_null.min(self.state.fiftymove_clock as usize); self.state_stack.iter().rev().take(end.saturating_sub(3)).any(|s| s.repetition != 0) } pub fn draw_by_fifty_move_rule(&self) -> bool { - self.halfmove_clock() >= 100 && (!self.in_check() || self.has_legal_moves()) + self.fiftymove_clock() >= 100 && (!self.in_check() || self.has_legal_moves()) } /// Checks if the position is a known draw by material, fifty-move or repetition. @@ -278,7 +269,7 @@ impl Board { /// /// pub fn upcoming_repetition(&self, ply: usize) -> bool { - let half_moves = self.state.plies_from_null.min(self.state.halfmove_clock as usize); + let half_moves = self.state.plies_from_null.min(self.state.fiftymove_clock as usize); if half_moves < 3 { return false; } @@ -558,13 +549,12 @@ impl Board { impl Default for Board { fn default() -> Self { Self { - side_to_move: Color::White, state: InternalState::default(), pieces: [Bitboard::default(); PieceType::NUM], colors: [Bitboard::default(); Color::NUM], mailbox: [Piece::None; Square::NUM], state_stack: Vec::with_capacity(2048), - fullmove_number: 0, + halfmove_number: 0, castling_rights: [0b1111; Square::NUM], castling_path: [Bitboard::default(); 16], castling_threat: [Bitboard::default(); 16], diff --git a/src/board/makemove.rs b/src/board/makemove.rs index 6079409b2..8401e2392 100644 --- a/src/board/makemove.rs +++ b/src/board/makemove.rs @@ -3,7 +3,7 @@ use crate::types::{Move, MoveKind, Piece, PieceType, Square}; impl Board { pub fn make_null_move(&mut self) { - self.side_to_move = !self.side_to_move; + self.halfmove_number += 1; self.state_stack.push(self.state); self.state.keys.toggle_side(); @@ -21,7 +21,7 @@ impl Board { } pub fn undo_null_move(&mut self) { - self.side_to_move = !self.side_to_move(); + self.halfmove_number -= 1; self.state = self.state_stack.pop().unwrap(); } @@ -32,7 +32,7 @@ impl Board { let from = mv.from(); let to = mv.to(); let piece = self.piece_on(from); - let stm = self.side_to_move; + let stm = self.side_to_move(); self.state_stack.push(self.state); @@ -49,9 +49,9 @@ impl Board { self.state.plies_from_null += 1; if mv.kind() == MoveKind::Capture || piece.piece_type() == PieceType::Pawn { - self.state.halfmove_clock = 0; + self.state.fiftymove_clock = 0; } else { - self.state.halfmove_clock += 1; + self.state.fiftymove_clock += 1; } if mv.is_castling() { @@ -113,8 +113,7 @@ impl Board { self.state.material += promotion.value() - PieceType::Pawn.value(); } - self.advance_fullmove_counter(); - self.side_to_move = !self.side_to_move; + self.halfmove_number += 1; self.state.castling.raw &= self.castling_rights[from] & self.castling_rights[to]; self.state.keys.toggle_castling(self.state.castling); @@ -124,7 +123,7 @@ impl Board { self.state.repetition = 0; - let end = self.state.plies_from_null.min(self.halfmove_clock() as usize); + let end = self.state.plies_from_null.min(self.fiftymove_clock() as usize); if end >= 4 { let mut idx = self.state_stack.len() as isize - 4; @@ -146,13 +145,12 @@ impl Board { } pub fn undo_move(&mut self, mv: Move) { - self.side_to_move = !self.side_to_move; - self.retreat_fullmove_counter(); + self.halfmove_number -= 1; let from = mv.from(); let to = mv.to(); let mover = self.remove_piece(to); - let stm = self.side_to_move; + let stm = self.side_to_move(); if mv.is_castling() { let (rook_from, rook_to) = self.get_castling_rook(to); diff --git a/src/board/parser.rs b/src/board/parser.rs index 816645fa8..ff6b4ac30 100644 --- a/src/board/parser.rs +++ b/src/board/parser.rs @@ -45,7 +45,7 @@ impl Board { } } - board.side_to_move = match parts.next() { + let side_to_move = match parts.next() { Some("w") => Color::White, Some("b") => Color::Black, _ => return Err(ParseFenError::InvalidActiveColor), @@ -54,8 +54,9 @@ impl Board { board.set_castling(parts.next().unwrap()); board.state.en_passant = parts.next().unwrap_or_default().try_into().unwrap_or_default(); - board.state.halfmove_clock = parts.next().unwrap_or_default().parse().unwrap_or_default(); - board.fullmove_number = parts.next().unwrap_or_default().parse().unwrap_or_default(); + board.state.fiftymove_clock = parts.next().unwrap_or_default().parse().unwrap_or_default(); + let fullmove_number: usize = parts.next().unwrap_or_default().parse().unwrap_or_default(); + board.halfmove_number = (2 * fullmove_number) + side_to_move as usize; board.update_threats(); board.update_hash_keys(); @@ -144,15 +145,15 @@ impl Board { } fen.push(' '); - fen.push_str(&self.side_to_move.to_string()); + fen.push_str(&self.side_to_move().to_string()); fen.push(' '); fen.push_str(&self.state.castling.to_string(self)); fen.push(' '); fen.push_str(&self.state.en_passant.to_string()); fen.push(' '); - fen.push_str(&self.state.halfmove_clock.to_string()); + fen.push_str(&self.state.fiftymove_clock.to_string()); fen.push(' '); - fen.push_str(&self.fullmove_number.to_string()); + fen.push_str(&self.fullmove_number().to_string()); fen } diff --git a/src/evaluation.rs b/src/evaluation.rs index 149744cce..75e14683e 100644 --- a/src/evaluation.rs +++ b/src/evaluation.rs @@ -5,7 +5,7 @@ pub fn correct_eval(td: &ThreadData, raw_eval: i32, correction_value: i32) -> i3 + td.optimism[td.board.side_to_move()] * (1543 + td.board.material())) / 26663; - eval = eval * (200 - td.board.halfmove_clock() as i32) / 200; + eval = eval * (200 - td.board.fiftymove_clock() as i32) / 200; eval += correction_value; diff --git a/src/search.rs b/src/search.rs index a5afa8d9f..9ce8e559f 100644 --- a/src/search.rs +++ b/src/search.rs @@ -329,7 +329,7 @@ fn search( let mut depth = depth.min(MAX_PLY as i32 - 1); let hash = td.board.hash(); - let entry = td.shared.tt.read(hash, td.board.halfmove_clock(), ply); + let entry = td.shared.tt.read(hash, td.board.fiftymove_clock(), ply); let mut tt_depth = 0; let mut tt_move = Move::NULL; @@ -365,7 +365,7 @@ fn search( update_continuation_histories(td, ply, td.board.moved_piece(tt_move), tt_move.to(), cont_bonus); } - if td.board.halfmove_clock() < 90 { + if td.board.fiftymove_clock() < 90 { return tt_score; } } @@ -376,7 +376,7 @@ fn search( if !NODE::ROOT && !excluded && !td.shared.stop_probing_tb.load(Ordering::Relaxed) - && td.board.halfmove_clock() == 0 + && td.board.fiftymove_clock() == 0 && td.board.castling().raw() == 0 && td.board.occupancies().popcount() <= tb::size() && let Some(outcome) = tb::probe(&td.board) @@ -1169,7 +1169,7 @@ fn qsearch(td: &mut ThreadData, mut alpha: i32, beta: i32, ply: } let hash = td.board.hash(); - let entry = td.shared.tt.read(hash, td.board.halfmove_clock(), ply); + let entry = td.shared.tt.read(hash, td.board.fiftymove_clock(), ply); let mut tt_score = Score::NONE; let mut tt_bound = Bound::None; @@ -1321,7 +1321,7 @@ fn qsearch(td: &mut ThreadData, mut alpha: i32, beta: i32, ply: fn eval_correction(td: &ThreadData, ply: isize) -> i32 { let stm = td.board.side_to_move(); - let bucket = td.board.halfmove_clock_bucket(); + let bucket = td.board.fiftymove_clock_bucket(); let corrhist = td.corrhist(); (corrhist.pawn.get(stm, td.board.pawn_key(), bucket) @@ -1342,7 +1342,7 @@ fn eval_correction(td: &ThreadData, ply: isize) -> i32 { fn update_correction_histories(td: &mut ThreadData, depth: i32, diff: i32, ply: isize) { let stm = td.board.side_to_move(); - let bucket = td.board.halfmove_clock_bucket(); + let bucket = td.board.fiftymove_clock_bucket(); let corrhist = td.corrhist(); let bonus = (146 * depth * diff / 128).clamp(-4449, 2659); diff --git a/src/tb.rs b/src/tb.rs index 91d08b4f3..f27b04465 100644 --- a/src/tb.rs +++ b/src/tb.rs @@ -113,7 +113,7 @@ pub fn rank_rootmoves(td: &mut ThreadData) { td.board.pieces(PieceType::Bishop).0, td.board.pieces(PieceType::Knight).0, td.board.pieces(PieceType::Pawn).0, - td.board.halfmove_clock() as u32, + td.board.fiftymove_clock() as u32, 0, ep_square, td.board.side_to_move() == Color::White, @@ -140,7 +140,7 @@ pub fn rank_rootmoves(td: &mut ThreadData) { td.board.pieces(PieceType::Bishop).0, td.board.pieces(PieceType::Knight).0, td.board.pieces(PieceType::Pawn).0, - td.board.halfmove_clock() as u32, + td.board.fiftymove_clock() as u32, 0, ep_square, td.board.side_to_move() == Color::White, diff --git a/src/types/zobrist.rs b/src/types/zobrist.rs index 0cf3f58bf..a29948caf 100644 --- a/src/types/zobrist.rs +++ b/src/types/zobrist.rs @@ -6,7 +6,7 @@ pub struct Zobrist { pub en_passant: [u64; 64], pub castling: [u64; 16], pub side: u64, - pub halfmove_clock: [u64; 16], + pub fiftymove_clock: [u64; 16], } pub const ZOBRIST: Zobrist = {