Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 15 additions & 25 deletions src/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<InternalState>,
fullmove_number: usize,
halfmove_number: usize,
castling_rights: [u8; Square::NUM],
castling_path: [Bitboard; 16],
castling_threat: [Bitboard; 16],
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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.
Expand All @@ -278,7 +269,7 @@ impl Board {
///
/// <http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf>
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;
}
Expand Down Expand Up @@ -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],
Expand Down
20 changes: 9 additions & 11 deletions src/board/makemove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
}

Expand All @@ -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);

Expand All @@ -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() {
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand Down
13 changes: 7 additions & 6 deletions src/board/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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();
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion src/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
12 changes: 6 additions & 6 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ fn search<NODE: NodeType>(
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;
Expand Down Expand Up @@ -365,7 +365,7 @@ fn search<NODE: NodeType>(
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;
}
}
Expand All @@ -376,7 +376,7 @@ fn search<NODE: NodeType>(
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)
Expand Down Expand Up @@ -1169,7 +1169,7 @@ fn qsearch<NODE: NodeType>(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;
Expand Down Expand Up @@ -1321,7 +1321,7 @@ fn qsearch<NODE: NodeType>(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)
Expand All @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions src/tb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/types/zobrist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Loading