aboutsummaryrefslogtreecommitdiff
path: root/src/grossmeister
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2024-01-25 11:24:36 +0100
committereug-vs <eugene@eug-vs.xyz>2024-01-25 11:24:36 +0100
commit746e3bf17463a377b6c54b291ebef9a736d6ceb7 (patch)
treea4e965669871084b98d3ce89ac95fa9d50131699 /src/grossmeister
parent299c6d6dee96a50f9366955192f922d449d11f20 (diff)
downloadchessnost-canary.tar.gz
chore: autoformat codecanary
Use #[rustfmt:skip] to preserve aligned blocks
Diffstat (limited to 'src/grossmeister')
-rw-r--r--src/grossmeister/UCI.rs118
-rw-r--r--src/grossmeister/evaluation.rs276
-rw-r--r--src/grossmeister/mod.rs10
-rw-r--r--src/grossmeister/move_selector.rs66
-rw-r--r--src/grossmeister/search.rs309
-rw-r--r--src/grossmeister/ttable.rs51
6 files changed, 570 insertions, 260 deletions
diff --git a/src/grossmeister/UCI.rs b/src/grossmeister/UCI.rs
index 0c3c158..e345943 100644
--- a/src/grossmeister/UCI.rs
+++ b/src/grossmeister/UCI.rs
@@ -1,6 +1,18 @@
-use std::{io::stdin, thread::{JoinHandle, spawn, sleep}, str::Split, time::Duration, sync::{Arc, atomic::{AtomicBool, Ordering}}};
+use std::{
+ io::stdin,
+ str::Split,
+ sync::{
+ atomic::{AtomicBool, Ordering},
+ Arc,
+ },
+ thread::{sleep, spawn, JoinHandle},
+ time::Duration,
+};
-use crate::{board::{Board, io::IO, color::Color}, moves::{Move, MoveKind}};
+use crate::{
+ board::{color::Color, io::IO, Board},
+ moves::{Move, MoveKind},
+};
use super::Grossmeister;
@@ -15,12 +27,16 @@ impl Grossmeister {
let mut cmd = String::new();
stdin().read_line(&mut cmd).unwrap();
if !self.parse_command(cmd, &mut search_handle) {
- break
+ break;
}
}
}
- pub fn parse_command(&mut self, cmd: String, search_handle: &mut Option<JoinHandle<Self>>) -> bool {
+ pub fn parse_command(
+ &mut self,
+ cmd: String,
+ search_handle: &mut Option<JoinHandle<Self>>,
+ ) -> bool {
let mut tokens = cmd.trim().split(' ');
if let Some(token) = tokens.next() {
match token {
@@ -44,16 +60,17 @@ impl Grossmeister {
"setoption" => {
todo!()
}
- "ucinewgame" => {
- *self = Self::default()
- }
+ "ucinewgame" => *self = Self::default(),
"position" => {
- match tokens.next() {
+ match tokens.next() {
Some("startpos") => {
self.board = Board::new();
}
Some("fen") => {
- let fen = (0..6).filter_map(|_| tokens.next()).collect::<Vec<_>>().join(" ");
+ let fen = (0..6)
+ .filter_map(|_| tokens.next())
+ .collect::<Vec<_>>()
+ .join(" ");
self.board = Board::from_FEN(fen);
}
_ => panic!("Expected position"),
@@ -62,27 +79,34 @@ impl Grossmeister {
for token in tokens.by_ref() {
let input_move = Move::from_notation(token.chars());
let moves = self.board.generate_pseudolegal_moves();
- if let Some(mov) = moves
- .iter()
- .find(|m| {
- let promo_matches = match input_move.kind {
- MoveKind::Promotion(piece) => match m.kind {
- MoveKind::Promotion(another_piece) => piece.without_color() == another_piece.without_color(),
- _ => false
- },
- _ => true,
- };
- promo_matches && m.source == input_move.source && m.target == input_move.target
- }) {
- self.board.make_move(*mov);
- let repeated = self.board.positions.iter().filter(|&&p| p == self.board.hash).count();
- if self.board.hash == 13465997660506371065 {
- self.board.print();
- dbg!(mov, repeated, self.board.hash, self.board.ply);
+ if let Some(mov) = moves.iter().find(|m| {
+ let promo_matches = match input_move.kind {
+ MoveKind::Promotion(piece) => match m.kind {
+ MoveKind::Promotion(another_piece) => {
+ piece.without_color() == another_piece.without_color()
}
- } else {
- panic!("Illegal move: {}", input_move);
- }
+ _ => false,
+ },
+ _ => true,
+ };
+ promo_matches
+ && m.source == input_move.source
+ && m.target == input_move.target
+ }) {
+ self.board.make_move(*mov);
+ let repeated = self
+ .board
+ .positions
+ .iter()
+ .filter(|&&p| p == self.board.hash)
+ .count();
+ if self.board.hash == 13465997660506371065 {
+ self.board.print();
+ dbg!(mov, repeated, self.board.hash, self.board.ply);
+ }
+ } else {
+ panic!("Illegal move: {}", input_move);
+ }
}
}
}
@@ -90,7 +114,9 @@ impl Grossmeister {
// Before we go, let's join to the latest search
if let Some(hand) = search_handle.take() {
match hand.join() {
- Ok(better_self) => self.transposition_table = better_self.transposition_table,
+ Ok(better_self) => {
+ self.transposition_table = better_self.transposition_table
+ }
Err(err) => println!("info string error {:?}", err),
}
}
@@ -98,20 +124,25 @@ impl Grossmeister {
*search_handle = Some(self.parse_go(tokens, false));
}
"stop" => {
- self.should_halt.store(true, std::sync::atomic::Ordering::SeqCst);
+ self.should_halt
+ .store(true, std::sync::atomic::Ordering::SeqCst);
}
"ponderhit" => {
// Since we were pondering without any scheduled halting previously,
// in case of ponderhit we need to schedule a new halting based on clock
// TODO: isn't the color flipped here? Might lead to time management bugs
let color = self.board.color();
- let duration = (self.board.clock.time[color as usize] + self.board.clock.increment[color as usize]) / 20;
+ let duration = (self.board.clock.time[color as usize]
+ + self.board.clock.increment[color as usize])
+ / 20;
let halt_scheduled = self.schedule_halt(duration);
// Join to the current pondering search
if let Some(hand) = search_handle.take() {
match hand.join() {
- Ok(better_self) => self.transposition_table = better_self.transposition_table,
+ Ok(better_self) => {
+ self.transposition_table = better_self.transposition_table
+ }
Err(err) => println!("info string error {:?}", err),
}
halt_scheduled.store(false, Ordering::SeqCst); // Cancel scheduled halting
@@ -122,7 +153,7 @@ impl Grossmeister {
"quit" => return false,
// Non-UCI debug commands
"print" => self.board.print(),
- _ => {},
+ _ => {}
}
}
true
@@ -134,7 +165,7 @@ impl Grossmeister {
"searchmoves" => todo!(),
"ponder" => {
println!("info will start in pondering mode");
- return self.parse_go(tokens, true)
+ return self.parse_go(tokens, true);
}
"wtime" => {
if let Some(time) = tokens.next() {
@@ -151,13 +182,15 @@ impl Grossmeister {
"winc" => {
if let Some(time) = tokens.next() {
let time: u64 = time.parse().unwrap();
- self.board.clock.increment[Color::White as usize] = Duration::from_millis(time);
+ self.board.clock.increment[Color::White as usize] =
+ Duration::from_millis(time);
}
}
"binc" => {
if let Some(time) = tokens.next() {
let time: u64 = time.parse().unwrap();
- self.board.clock.increment[Color::Black as usize] = Duration::from_millis(time);
+ self.board.clock.increment[Color::Black as usize] =
+ Duration::from_millis(time);
}
}
"movestogo" => {}
@@ -166,7 +199,7 @@ impl Grossmeister {
let depth: u8 = depth.parse().unwrap();
return self.create_search_thread(depth, Duration::MAX);
}
- },
+ }
"nodes" => todo!(),
"mate" => todo!(),
"movetime" => {
@@ -174,12 +207,12 @@ impl Grossmeister {
let time: u64 = time.parse().unwrap();
let duration = Duration::from_millis(time);
- return self.create_search_thread(u8::MAX, duration)
+ return self.create_search_thread(u8::MAX, duration);
}
- },
+ }
"infinite" => {
return self.create_search_thread(u8::MAX, Duration::MAX);
- },
+ }
_ => {}
}
return self.parse_go(tokens, ponder);
@@ -190,7 +223,8 @@ impl Grossmeister {
let duration = if ponder {
Duration::MAX
} else {
- (self.board.clock.time[color as usize] + self.board.clock.increment[color as usize]) / 20
+ (self.board.clock.time[color as usize] + self.board.clock.increment[color as usize])
+ / 20
};
self.create_search_thread(u8::MAX, duration)
diff --git a/src/grossmeister/evaluation.rs b/src/grossmeister/evaluation.rs
index 1b57f5a..8deb929 100644
--- a/src/grossmeister/evaluation.rs
+++ b/src/grossmeister/evaluation.rs
@@ -1,12 +1,17 @@
use std::f32::INFINITY;
-use crate::{board::{piece::Piece, color::Color, CastlingSide}, bitboard::{Bitboard, BitboardFns}, square::Square};
+use crate::{
+ bitboard::{Bitboard, BitboardFns},
+ board::{color::Color, piece::Piece, CastlingSide},
+ square::Square,
+};
use super::Grossmeister;
/// Score in pawns (not centipawns)
pub type Score = f32;
+#[rustfmt::skip]
const PAWN_BONUS: [Score; 64] = [
// A B C D E F G H
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
@@ -19,6 +24,7 @@ const PAWN_BONUS: [Score; 64] = [
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00
];
+#[rustfmt::skip]
const PAWN_BONUS_PASSER_ENDGAME: [Score; 64] = [
// A B C D E F G H
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
@@ -31,6 +37,7 @@ const PAWN_BONUS_PASSER_ENDGAME: [Score; 64] = [
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
];
+#[rustfmt::skip]
const KNIGHT_BONUS: [Score; 64] = [
// A B C D E F G H
-0.50, -0.40, -0.30, -0.30, -0.30, -0.30, -0.40, -0.50,
@@ -43,6 +50,7 @@ const KNIGHT_BONUS: [Score; 64] = [
-0.50, -0.40, -0.30, -0.30, -0.30, -0.30, -0.40, -0.50,
];
+#[rustfmt::skip]
const BISHOP_BONUS: [Score; 64] = [
// A B C D E F G H
-0.20, -0.10, -0.10, -0.10, -0.10, -0.10, -0.10, -0.20,
@@ -55,6 +63,7 @@ const BISHOP_BONUS: [Score; 64] = [
-0.20, -0.10, -0.10, -0.10, -0.10, -0.10, -0.10, -0.20,
];
+#[rustfmt::skip]
const ROOK_BONUS: [Score; 64] = [
// A B C D E F G H
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
@@ -67,6 +76,7 @@ const ROOK_BONUS: [Score; 64] = [
0.00, 0.00, 0.00, 0.05, 0.05, 0.00, 0.00, 0.00
];
+#[rustfmt::skip]
const QUEEN_BONUS: [Score; 64] = [
// A B C D E F G H
-0.20, -0.10, -0.10, -0.05, -0.05, -0.10, -0.10, -0.20,
@@ -79,6 +89,7 @@ const QUEEN_BONUS: [Score; 64] = [
-0.20, -0.10, -0.10, -0.05, -0.05, -0.10, -0.10, -0.20
];
+#[rustfmt::skip]
const KING_BONUS: [Score; 64] = [
// A B C D E F G H
-0.30, -0.40, -0.40, -0.50, -0.50, -0.40, -0.40, -0.30,
@@ -91,6 +102,7 @@ const KING_BONUS: [Score; 64] = [
0.20, 0.10, 0.30, -0.20, 0.00, 0.10, 0.30, 0.20
];
+#[rustfmt::skip]
const KING_BONUS_ENGAME: [Score; 64] = [
// A B C D E F G H
-0.50, -0.40, -0.30, -0.20, -0.20, -0.30, -0.40, -0.50,
@@ -149,39 +161,50 @@ impl Grossmeister {
/// Compute total bonus for well-positioned pieces
/// according to Piece-Square Tables
pub fn pst_bonus(&self, color: Color, is_endgame: bool) -> f32 {
- self.board.pieces_by_color(color).iter().enumerate().fold(0., |total, (piece_index, bitboard)| {
- let pst = match Piece::from(piece_index).without_color() {
- Piece::Pawn => if is_endgame && (bitboard & self.passer_mask(color) > 0) {
- PAWN_BONUS_PASSER_ENDGAME
- } else {
- PAWN_BONUS
- }
- Piece::Knight => KNIGHT_BONUS,
- Piece::Bishop => BISHOP_BONUS,
- Piece::Rook => ROOK_BONUS,
- Piece::Queen => QUEEN_BONUS,
- Piece::King => if is_endgame {
- KING_BONUS_ENGAME
- } else {
- KING_BONUS
- }
- _ => panic!("Unreachable")
- };
- total + bitboard.serialize().fold(0., |acc, square| {
- let pst_index = match color {
- Color::White => square.mirror(),
- Color::Black => square,
- } as usize;
- acc + pst[pst_index]
- })
- })
+ self.board.pieces_by_color(color).iter().enumerate().fold(
+ 0.,
+ |total, (piece_index, bitboard)| {
+ let pst = match Piece::from(piece_index).without_color() {
+ Piece::Pawn => {
+ if is_endgame && (bitboard & self.passer_mask(color) > 0) {
+ PAWN_BONUS_PASSER_ENDGAME
+ } else {
+ PAWN_BONUS
+ }
+ }
+ Piece::Knight => KNIGHT_BONUS,
+ Piece::Bishop => BISHOP_BONUS,
+ Piece::Rook => ROOK_BONUS,
+ Piece::Queen => QUEEN_BONUS,
+ Piece::King => {
+ if is_endgame {
+ KING_BONUS_ENGAME
+ } else {
+ KING_BONUS
+ }
+ }
+ _ => panic!("Unreachable"),
+ };
+ total
+ + bitboard.serialize().fold(0., |acc, square| {
+ let pst_index = match color {
+ Color::White => square.mirror(),
+ Color::Black => square,
+ } as usize;
+ acc + pst[pst_index]
+ })
+ },
+ )
}
/// Count raw material of the given color
pub fn material(&self, color: Color) -> f32 {
- self.board.pieces_by_color(color).iter().enumerate().fold(0., |acc, (piece_index, bitboard)| {
- acc + Piece::from(piece_index).static_eval() * bitboard.pop_count() as f32
- })
+ self.board.pieces_by_color(color).iter().enumerate().fold(
+ 0.,
+ |acc, (piece_index, bitboard)| {
+ acc + Piece::from(piece_index).static_eval() * bitboard.pop_count() as f32
+ },
+ )
}
// Returns a value in [0, 240] representing how
@@ -193,24 +216,27 @@ impl Grossmeister {
let rook_phase = 20;
let queen_phase = 40;
- let total_phase = knight_phase*4 + bishop_phase*4 + rook_phase*4 + queen_phase*2;
- // If you change it, make sure to update denominator in interpolation
+ let total_phase = knight_phase * 4 + bishop_phase * 4 + rook_phase * 4 + queen_phase * 2;
+ // If you change it, make sure to update denominator in interpolation
debug_assert_eq!(total_phase, 240);
- self.board.piece_sets.iter().enumerate().fold(0, |acc, (piece_index, &bitboard)| {
- acc + match Piece::from(piece_index).without_color() {
- Piece::King => 0,
- Piece::Pawn => 0,
- Piece::Knight => knight_phase,
- Piece::Bishop => bishop_phase,
- Piece::Rook => rook_phase,
- Piece::Queen => queen_phase,
- _ => panic!("Unreachable")
- } * bitboard.pop_count()
- })
+ self.board
+ .piece_sets
+ .iter()
+ .enumerate()
+ .fold(0, |acc, (piece_index, &bitboard)| {
+ acc + match Piece::from(piece_index).without_color() {
+ Piece::King => 0,
+ Piece::Pawn => 0,
+ Piece::Knight => knight_phase,
+ Piece::Bishop => bishop_phase,
+ Piece::Rook => rook_phase,
+ Piece::Queen => queen_phase,
+ _ => panic!("Unreachable"),
+ } * bitboard.pop_count()
+ })
}
-
/// Evaluate a position relative to the current player
pub fn evaluate(&self) -> Score {
let color = self.board.color();
@@ -220,7 +246,7 @@ impl Grossmeister {
let loseable = self.board.is_theoretically_winnable(opponent_color);
if !winnable && !loseable {
- return 0.0
+ return 0.0;
}
let material_advantage = self.material(color) - self.material(opponent_color);
@@ -229,22 +255,21 @@ impl Grossmeister {
let pawn_islands_advantage = self.pawn_islands(opponent_color) - self.pawn_islands(color);
// Middlegame eval, assumming all pieces are on the Board
- let middlegame_eval =
- mobility_advantage * 0.05 +
- pawn_shield_advantage * 0.20 +
- pawn_islands_advantage * 0.10 +
- (self.pst_bonus(color, false) - self.pst_bonus(opponent_color, false));
+ let middlegame_eval = mobility_advantage * 0.05
+ + pawn_shield_advantage * 0.20
+ + pawn_islands_advantage * 0.10
+ + (self.pst_bonus(color, false) - self.pst_bonus(opponent_color, false));
// Engame eval, assuming no pieces are left on the Board (only kings and pawns)
- let endgame_eval =
- mobility_advantage * 0.03 +
- pawn_shield_advantage * 0.05 +
- pawn_islands_advantage * 0.15 +
- (self.pst_bonus(color, true) - self.pst_bonus(opponent_color, true));
+ let endgame_eval = mobility_advantage * 0.03
+ + pawn_shield_advantage * 0.05
+ + pawn_islands_advantage * 0.15
+ + (self.pst_bonus(color, true) - self.pst_bonus(opponent_color, true));
// Interpolate evalutaion based on amount of pieces to remove discontinuity
let phase = self.phase();
- let tapered_eval = (middlegame_eval * phase as f32 + endgame_eval * (240 - phase) as f32) / 240.;
+ let tapered_eval =
+ (middlegame_eval * phase as f32 + endgame_eval * (240 - phase) as f32) / 240.;
let eval = (material_advantage + tapered_eval)
.min(if winnable { INFINITY } else { 0.0 }) // Can not score > 0 if not winnable
@@ -279,7 +304,9 @@ impl Grossmeister {
let ep_bitboard = match self.board.ep_target {
Some(square) => {
let rank = square.rank();
- if (rank == 2 && color == Color::Black) || (rank == 5 && color == Color::White) {
+ if (rank == 2 && color == Color::Black)
+ || (rank == 5 && color == Color::White)
+ {
square.to_bitboard()
} else {
0
@@ -287,20 +314,30 @@ impl Grossmeister {
}
None => 0,
};
- mobility += (self.board.attacks.pawn[color as usize][source as usize] & (opponent_occupancy | ep_bitboard)).pop_count() as f32;
- mobility += (self.board.attacks.pawn_pushes[color as usize][source as usize] & empty).pop_count() as f32;
+ mobility += (self.board.attacks.pawn[color as usize][source as usize]
+ & (opponent_occupancy | ep_bitboard))
+ .pop_count() as f32;
+ mobility += (self.board.attacks.pawn_pushes[color as usize]
+ [source as usize]
+ & empty)
+ .pop_count() as f32;
}
let able_to_double_push_mask = match color {
Color::White => empty >> 8,
Color::Black => empty << 8,
};
for source in (*piece & able_to_double_push_mask).serialize() {
- mobility += (self.board.attacks.pawn_double_pushes[color as usize][source as usize] & empty).pop_count() as f32;
+ mobility += (self.board.attacks.pawn_double_pushes[color as usize]
+ [source as usize]
+ & empty)
+ .pop_count() as f32;
}
}
Piece::King => {
for source in piece.serialize() {
- mobility += (self.board.attacks.king[source as usize] & (empty | opponent_occupancy)).pop_count() as f32;
+ mobility += (self.board.attacks.king[source as usize]
+ & (empty | opponent_occupancy))
+ .pop_count() as f32;
// Castling
let king_home_position = match color {
@@ -308,56 +345,79 @@ impl Grossmeister {
Color::Black => Square::E8,
};
if *piece == king_home_position.to_bitboard() {
- for rook_square in player_pieces[Piece::Rook as usize]
- .serialize()
- .filter(|rook_square| rook_square.rank() == king_home_position.rank())
- {
- match rook_square.file() {
- 0 => {
- let castle_line = [
- king_home_position.west_one(),
- king_home_position.west_one().west_one(),
- ];
-
- let all_empty = castle_line.iter().all(|square| empty & square.to_bitboard() > 0);
- let any_checks = castle_line.iter().any(|square| self.board.is_square_attacked(*square, color.flip()));
-
- if all_empty && !any_checks && self.board.castling_rights[color as usize][CastlingSide::Queen as usize] {
- mobility += 1.;
- }
- },
- 7 => {
- let castle_line = [
- king_home_position.east_one(),
- king_home_position.east_one().east_one(),
- ];
-
- let all_empty = castle_line.iter().all(|square| empty & square.to_bitboard() > 0);
- let any_checks = castle_line.iter().any(|square| self.board.is_square_attacked(*square, color.flip()));
-
- if all_empty && !any_checks && self.board.castling_rights[color as usize][CastlingSide::King as usize] {
- mobility += 1.;
- }
- },
- _ => {},
+ for rook_square in
+ player_pieces[Piece::Rook as usize].serialize().filter(
+ |rook_square| rook_square.rank() == king_home_position.rank(),
+ )
+ {
+ match rook_square.file() {
+ 0 => {
+ let castle_line = [
+ king_home_position.west_one(),
+ king_home_position.west_one().west_one(),
+ ];
+
+ let all_empty = castle_line
+ .iter()
+ .all(|square| empty & square.to_bitboard() > 0);
+ let any_checks = castle_line.iter().any(|square| {
+ self.board.is_square_attacked(*square, color.flip())
+ });
+
+ if all_empty
+ && !any_checks
+ && self.board.castling_rights[color as usize]
+ [CastlingSide::Queen as usize]
+ {
+ mobility += 1.;
+ }
+ }
+ 7 => {
+ let castle_line = [
+ king_home_position.east_one(),
+ king_home_position.east_one().east_one(),
+ ];
+
+ let all_empty = castle_line
+ .iter()
+ .all(|square| empty & square.to_bitboard() > 0);
+ let any_checks = castle_line.iter().any(|square| {
+ self.board.is_square_attacked(*square, color.flip())
+ });
+
+ if all_empty
+ && !any_checks
+ && self.board.castling_rights[color as usize]
+ [CastlingSide::King as usize]
+ {
+ mobility += 1.;
+ }
}
+ _ => {}
}
+ }
}
}
}
Piece::Knight => {
for source in piece.serialize() {
- mobility += (self.board.attacks.knight[source as usize] & (empty | opponent_occupancy)).pop_count() as f32;
+ mobility += (self.board.attacks.knight[source as usize]
+ & (empty | opponent_occupancy))
+ .pop_count() as f32;
}
}
Piece::Bishop => {
for source in piece.serialize() {
- mobility += (self.board.attacks.bishop(self.board.occupancy, source) & (empty | opponent_occupancy)).pop_count() as f32;
+ mobility += (self.board.attacks.bishop(self.board.occupancy, source)
+ & (empty | opponent_occupancy))
+ .pop_count() as f32;
}
}
Piece::Rook => {
for source in piece.serialize() {
- mobility += (self.board.attacks.rook(self.board.occupancy, source) & (empty | opponent_occupancy)).pop_count() as f32;
+ mobility += (self.board.attacks.rook(self.board.occupancy, source)
+ & (empty | opponent_occupancy))
+ .pop_count() as f32;
}
}
Piece::Queen => {
@@ -372,7 +432,10 @@ impl Grossmeister {
#[cfg(test)]
mod tests {
- use crate::{board::{Board, io::IO}, moves::{Move, MoveKind}};
+ use crate::{
+ board::{io::IO, Board},
+ moves::{Move, MoveKind},
+ };
use super::*;
@@ -407,7 +470,6 @@ mod tests {
let board = Board::new();
let gm = Grossmeister::new(board);
assert_eq!(gm.material(Color::Black), gm.material(Color::White));
-
}
#[test]
@@ -421,7 +483,11 @@ mod tests {
fn king_tropism() {
let board = Board::new();
let mut gm = Grossmeister::new(board);
- gm.board.make_move(Move { source: Square::D1, target: Square::F5, kind: MoveKind::Quiet });
+ gm.board.make_move(Move {
+ source: Square::D1,
+ target: Square::F5,
+ kind: MoveKind::Quiet,
+ });
let score = gm.evaluate();
gm.board.print();
println!("Score {}", score);
@@ -482,7 +548,8 @@ mod tests {
#[test]
fn discourage_edge_knights() {
let score1 = {
- let fen = String::from("r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3");
+ let fen =
+ String::from("r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3");
let board = Board::from_FEN(fen);
let gm = Grossmeister::new(board);
let score = gm.evaluate();
@@ -492,7 +559,8 @@ mod tests {
};
let score2 = {
- let fen = String::from("r1bqkbnr/pppp1ppp/2n5/4p3/4P3/7N/PPPP1PPP/RNBQKB1R w KQkq - 2 3");
+ let fen =
+ String::from("r1bqkbnr/pppp1ppp/2n5/4p3/4P3/7N/PPPP1PPP/RNBQKB1R w KQkq - 2 3");
let board = Board::from_FEN(fen);
let gm = Grossmeister::new(board);
let score = gm.evaluate();
@@ -507,7 +575,8 @@ mod tests {
#[test]
fn mirrored_evaluation() {
let score1 = {
- let fen = String::from("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1");
+ let fen =
+ String::from("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1");
let board = Board::from_FEN(fen);
let gm = Grossmeister::new(board);
let score = gm.evaluate();
@@ -517,7 +586,8 @@ mod tests {
};
let score2 = {
- let fen = String::from("r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1 ");
+ let fen =
+ String::from("r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1 ");
let board = Board::from_FEN(fen);
let gm = Grossmeister::new(board);
let score = gm.evaluate();
diff --git a/src/grossmeister/mod.rs b/src/grossmeister/mod.rs
index 1f1a141..ea73bff 100644
--- a/src/grossmeister/mod.rs
+++ b/src/grossmeister/mod.rs
@@ -1,15 +1,15 @@
use std::sync::{atomic::AtomicBool, Arc};
-use smallvec::{SmallVec, smallvec};
+use smallvec::{smallvec, SmallVec};
+use self::{move_selector::MoveSelector, ttable::MasterTable};
use crate::board::Board;
-use self::{ttable::MasterTable, move_selector::MoveSelector};
-mod ttable;
-mod evaluation;
-mod search;
mod UCI;
+mod evaluation;
mod move_selector;
+mod search;
+mod ttable;
/// Grossmeister is a powerful entity that plays the game of Chess.
/// This structure represents a player, it stores his knowledge
diff --git a/src/grossmeister/move_selector.rs b/src/grossmeister/move_selector.rs
index 08e5cf1..debaba5 100644
--- a/src/grossmeister/move_selector.rs
+++ b/src/grossmeister/move_selector.rs
@@ -1,8 +1,8 @@
use smallvec::SmallVec;
-use crate::{moves::Move, board::{Board, move_generation::MoveList}};
+use crate::{board::move_generation::MoveList, moves::Move};
-use super::{Grossmeister, ttable::NodeType};
+use super::{ttable::NodeType, Grossmeister};
pub type ScoredMove = (Move, f32);
pub type ScoredMoveList = SmallVec<[ScoredMove; 128]>;
@@ -28,7 +28,7 @@ impl Iterator for ScoredMoveIter {
}
self.index += 1;
- return Some(self.moves[self.index - 1])
+ return Some(self.moves[self.index - 1]);
}
None
}
@@ -65,7 +65,7 @@ impl MoveGenStage {
MoveGenStage::WinningOrEqualTactical => MoveGenStage::Killer,
MoveGenStage::Killer => MoveGenStage::Quiet,
MoveGenStage::Quiet => MoveGenStage::LosingTactical,
- MoveGenStage::LosingTactical => todo!()
+ MoveGenStage::LosingTactical => todo!(),
}
}
}
@@ -96,17 +96,21 @@ impl Grossmeister {
/// Register killer for ply-before
pub fn register_killer(&mut self, killer: Move) {
if self.board.ply > 1 {
- let parent_killers = &mut self.move_selectors[(self.board.ply - 2) as usize].killer_moves;
+ let parent_killers =
+ &mut self.move_selectors[(self.board.ply - 2) as usize].killer_moves;
match parent_killers.iter().find(|m| **m == killer) {
None => {
parent_killers.push(killer);
- debug_assert!(!parent_killers.spilled(), "Killer move list should remain on the stack");
+ debug_assert!(
+ !parent_killers.spilled(),
+ "Killer move list should remain on the stack"
+ );
// We want to have max 3 killers, so if we exceed the limit remove the oldest killer
if parent_killers.len() > 3 {
parent_killers.remove(0);
}
}
- Some(..) => {},
+ Some(..) => {}
}
}
}
@@ -117,22 +121,17 @@ impl Grossmeister {
if m.is_tactical() {
let [source_eval, target_eval] = [m.source, m.target]
.map(|sq| self.board.piece_by_square(sq))
- .map(|p| {
- match p {
- Some(p) => p.static_eval(),
- None => 0.,
- }
+ .map(|p| match p {
+ Some(p) => p.static_eval(),
+ None => 0.,
});
- return 2. * target_eval - source_eval
+ return 2. * target_eval - source_eval;
}
0.0
}
pub fn score_moves(&self, movelist: MoveList) -> ScoredMoveList {
- movelist
- .iter()
- .map(|&m| (m, self.eval_move(m)))
- .collect()
+ movelist.iter().map(|&m| (m, self.eval_move(m))).collect()
}
pub fn next_tactical(&mut self) -> Option<Move> {
@@ -146,10 +145,7 @@ impl Grossmeister {
}
fn init_stage(&mut self, moves: ScoredMoveList) {
- self.move_selector().stage_moves = ScoredMoveIter {
- moves,
- index: 0,
- }
+ self.move_selector().stage_moves = ScoredMoveIter { moves, index: 0 }
}
/// TODO: next stage
@@ -168,7 +164,7 @@ impl Grossmeister {
if let Some(transposition) = self.transposition() {
if transposition.node_type != NodeType::All {
if let Some(mov) = transposition.mov {
- return Some(mov)
+ return Some(mov);
}
}
}
@@ -181,8 +177,10 @@ impl Grossmeister {
self.move_selector().tactical_moves = self.score_moves(moves);
// But we only care about current stage now
- let new_stage =
- self.move_selector().tactical_moves.iter()
+ let new_stage = self
+ .move_selector()
+ .tactical_moves
+ .iter()
.filter(|(_, score)| *score >= 0.0)
.copied()
.collect();
@@ -197,9 +195,17 @@ impl Grossmeister {
}
MoveGenStage::Killer => {
if self.move_selector().stage_moves.moves.is_empty() {
- let new_stage = self.move_selector().killer_moves.clone()
+ let new_stage = self
+ .move_selector()
+ .killer_moves
+ .clone()
.iter()
- .filter(|&m| self.move_selector().tactical_moves.iter().any(|(m2, _)| m2 == m)) // Test if killer is in the movelist
+ .filter(|&m| {
+ self.move_selector()
+ .tactical_moves
+ .iter()
+ .any(|(m2, _)| m2 == m)
+ }) // Test if killer is in the movelist
.map(|&m| (m, 0.0))
.collect();
self.init_stage(new_stage);
@@ -223,8 +229,10 @@ impl Grossmeister {
}
MoveGenStage::LosingTactical => {
if self.move_selector().stage_moves.moves.is_empty() {
- let new_stage =
- self.move_selector().tactical_moves.iter()
+ let new_stage = self
+ .move_selector()
+ .tactical_moves
+ .iter()
.filter(|(_, score)| *score < 0.0)
.copied()
.collect();
@@ -232,7 +240,7 @@ impl Grossmeister {
}
match self.move_selector().stage_moves.next() {
Some((mov, _)) => return Some(mov),
- None => return None
+ None => return None,
}
}
}
diff --git a/src/grossmeister/search.rs b/src/grossmeister/search.rs
index 372fecc..e8cb696 100644
--- a/src/grossmeister/search.rs
+++ b/src/grossmeister/search.rs
@@ -1,8 +1,15 @@
use std::f32::INFINITY;
-use crate::{moves::{Move, MoveKind}, board::io::IO};
+use crate::{
+ board::io::IO,
+ moves::{Move, MoveKind},
+};
-use super::{Grossmeister, ttable::{NodeType, TranspositionTableItem}, evaluation::Score};
+use super::{
+ evaluation::Score,
+ ttable::{NodeType, TranspositionTableItem},
+ Grossmeister,
+};
const SCORE_MATE: Score = 20_000.0;
@@ -19,38 +26,46 @@ impl Grossmeister {
// Mate distance pruning
// If a non-zero value (mating score) is returned, branch could be safely pruned
pub fn MDP(alpha: &mut Score, beta: &mut Score, root_distance: u8) -> Score {
- { // Update bound in case we are winning
+ {
+ // Update bound in case we are winning
let mating_score = SCORE_MATE - root_distance as Score;
if mating_score < *beta {
*beta = mating_score;
if *alpha >= mating_score {
- return mating_score
+ return mating_score;
}
}
}
- { // Update bound in case we are losing
+ {
+ // Update bound in case we are losing
let mating_score = -SCORE_MATE + root_distance as Score;
if mating_score > *alpha {
*alpha = mating_score;
if *beta <= mating_score {
- return mating_score
+ return mating_score;
}
}
}
0.0
}
- pub fn negamax_search(&mut self, mut alpha: Score, mut beta: Score, depth_left: u8, root_distance: u8) -> Score {
+ pub fn negamax_search(
+ &mut self,
+ mut alpha: Score,
+ mut beta: Score,
+ depth_left: u8,
+ root_distance: u8,
+ ) -> Score {
let color = self.board.color();
if self.board.threefold_repetition() {
- return 0.0
+ return 0.0;
}
// Mate distance pruning
let mating_score = Grossmeister::MDP(&mut alpha, &mut beta, root_distance);
if mating_score != 0.0 {
- return mating_score
+ return mating_score;
}
if let Some(transposition) = self.transposition() {
@@ -84,7 +99,7 @@ impl Grossmeister {
};
if depth_left == 0 {
- return self.quiscence(alpha, beta, root_distance)
+ return self.quiscence(alpha, beta, root_distance);
}
let mut full_window_search = true;
@@ -106,7 +121,12 @@ impl Grossmeister {
} else {
// After we have PV-node (that raised alpha) all other nodes will be searched
// with zero-window first to confirm this assumption
- let score = -self.negamax_search(-(alpha + 0.01), -alpha, depth_left - 1, root_distance + 1);
+ let score = -self.negamax_search(
+ -(alpha + 0.01),
+ -alpha,
+ depth_left - 1,
+ root_distance + 1,
+ );
// In case some of the other nodes raises alpha, then it's true PV node now,
// let's research with full window to find its exact value
if score > alpha {
@@ -115,7 +135,13 @@ impl Grossmeister {
score
}
};
- self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before);
+ self.board.unmake_move(
+ mov,
+ captured_piece,
+ ep_target_before,
+ castling_rights_before,
+ hash_before,
+ );
if score >= beta {
transposition.mov = Some(mov);
@@ -127,7 +153,7 @@ impl Grossmeister {
self.register_killer(mov);
}
- return beta
+ return beta;
}
if score > alpha {
alpha = score;
@@ -137,7 +163,13 @@ impl Grossmeister {
transposition.node_type = NodeType::PV;
}
} else {
- self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before);
+ self.board.unmake_move(
+ mov,
+ captured_piece,
+ ep_target_before,
+ castling_rights_before,
+ hash_before,
+ );
}
// Could not finish in time, return what we have so far
@@ -148,11 +180,11 @@ impl Grossmeister {
if !legal_move_found {
if self.board.is_king_in_check(color) {
- return -SCORE_MATE + root_distance as Score
+ return -SCORE_MATE + root_distance as Score;
} else {
transposition.score = 0.0;
self.store_transposition(transposition);
- return 0.0
+ return 0.0;
}
}
@@ -164,13 +196,13 @@ impl Grossmeister {
let color = self.board.color();
if self.board.threefold_repetition() {
- return 0.0
+ return 0.0;
}
// Mate distance pruning
let mating_score = Grossmeister::MDP(&mut alpha, &mut beta, root_distance);
if mating_score != 0.0 {
- return mating_score
+ return mating_score;
}
if let Some(transposition) = self.transposition() {
@@ -219,7 +251,11 @@ impl Grossmeister {
let mut legal_move_found = false;
self.cleanup_selector();
- while let Some(mov) = if tactical_only { self.next_tactical() } else { self.next_move() } {
+ while let Some(mov) = if tactical_only {
+ self.next_tactical()
+ } else {
+ self.next_move()
+ } {
let ep_target_before = self.board.ep_target;
let castling_rights_before = self.board.castling_rights;
let hash_before = self.board.hash;
@@ -228,7 +264,13 @@ impl Grossmeister {
if !self.board.is_king_in_check(color) {
legal_move_found = true;
let evaluation = -self.quiscence(-beta, -alpha, root_distance + 1);
- self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before);
+ self.board.unmake_move(
+ mov,
+ captured_piece,
+ ep_target_before,
+ castling_rights_before,
+ hash_before,
+ );
if evaluation >= beta {
transposition.mov = Some(mov);
@@ -249,12 +291,18 @@ impl Grossmeister {
transposition.node_type = NodeType::All;
}
} else {
- self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before);
+ self.board.unmake_move(
+ mov,
+ captured_piece,
+ ep_target_before,
+ castling_rights_before,
+ hash_before,
+ );
}
}
if !legal_move_found && self.board.is_king_in_check(color) {
- return -SCORE_MATE + root_distance as Score
+ return -SCORE_MATE + root_distance as Score;
}
self.store_transposition(transposition);
@@ -282,8 +330,13 @@ impl Grossmeister {
let mut subtree_pv = self.collect_pv(depth - 1);
pv.append(&mut subtree_pv);
- self.board.unmake_move(mov, captured, ep_target_before, castling_rights_before, hash_before);
-
+ self.board.unmake_move(
+ mov,
+ captured,
+ ep_target_before,
+ castling_rights_before,
+ hash_before,
+ );
}
} else {
println!("No PV node found for hash {}", self.board.hash);
@@ -304,11 +357,12 @@ impl Grossmeister {
let mut upperbound = INFINITY;
while lowerbound < upperbound {
- let beta = score + if score == lowerbound {
- window_size
- } else {
- 0.0
- };
+ let beta = score
+ + if score == lowerbound {
+ window_size
+ } else {
+ 0.0
+ };
score = self.negamax_search(beta - window_size, beta, depth, 0);
@@ -340,12 +394,14 @@ impl Grossmeister {
// Print UCI info
print!("info depth {} score ", depth);
- if score.abs() >= SCORE_MATE - 128f32 { // TODO: VALUE_WIN - MAX_PLY
- let mate_distance = (SCORE_MATE - score.abs()) * if score > 0.0 {
- 1.
- } else {
- -1. // -N means we are mated in N plies
- };
+ if score.abs() >= SCORE_MATE - 128f32 {
+ // TODO: VALUE_WIN - MAX_PLY
+ let mate_distance = (SCORE_MATE - score.abs())
+ * if score > 0.0 {
+ 1.
+ } else {
+ -1. // -N means we are mated in N plies
+ };
print!("mate {:.0} ", (mate_distance / 2.0).ceil());
} else {
print!("cp {:.0} ", score * 100.0)
@@ -430,35 +486,92 @@ impl Grossmeister {
result.checks += subtree_result.checks;
result.castles += subtree_result.castles;
result.en_passants += subtree_result.en_passants;
-
}
- self.board.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before);
+ self.board.unmake_move(
+ mov,
+ captured_piece,
+ ep_target_before,
+ castling_rights_before,
+ hash_before,
+ );
}
if print {
- println!("Found {} leaf nodes in this subtree (depth {})", result.leaf_nodes, depth);
+ println!(
+ "Found {} leaf nodes in this subtree (depth {})",
+ result.leaf_nodes, depth
+ );
}
result
}
}
-
#[cfg(test)]
mod tests {
- use crate::{board::{Board, io::IO}, square::Square, moves::{Move, MoveKind}, grossmeister::{Grossmeister, search::PerftResult}};
use super::SCORE_MATE;
+ use crate::{
+ board::{io::IO, Board},
+ grossmeister::{search::PerftResult, Grossmeister},
+ moves::{Move, MoveKind},
+ square::Square,
+ };
#[test]
fn perft() {
let board = Board::new();
let mut gm = Grossmeister::new(board);
- assert_eq!(gm.perft(0, false), PerftResult { leaf_nodes: 1, captures: 0, en_passants: 0, castles: 0 , checks: 0 });
- assert_eq!(gm.perft(1, false), PerftResult { leaf_nodes: 20, captures: 0, en_passants: 0, castles: 0 , checks: 0 });
- assert_eq!(gm.perft(2, false), PerftResult { leaf_nodes: 400, captures: 0, en_passants: 0, castles: 0 , checks: 0 });
- assert_eq!(gm.perft(3, false), PerftResult { leaf_nodes: 8902, captures: 34, en_passants: 0, castles: 0 , checks: 12 });
- assert_eq!(gm.perft(4, false), PerftResult { leaf_nodes: 197281, captures: 1576, en_passants: 0, castles: 0 , checks: 469 });
+ assert_eq!(
+ gm.perft(0, false),
+ PerftResult {
+ leaf_nodes: 1,
+ captures: 0,
+ en_passants: 0,
+ castles: 0,
+ checks: 0
+ }
+ );
+ assert_eq!(
+ gm.perft(1, false),
+ PerftResult {
+ leaf_nodes: 20,
+ captures: 0,
+ en_passants: 0,
+ castles: 0,
+ checks: 0
+ }
+ );
+ assert_eq!(
+ gm.perft(2, false),
+ PerftResult {
+ leaf_nodes: 400,
+ captures: 0,
+ en_passants: 0,
+ castles: 0,
+ checks: 0
+ }
+ );
+ assert_eq!(
+ gm.perft(3, false),
+ PerftResult {
+ leaf_nodes: 8902,
+ captures: 34,
+ en_passants: 0,
+ castles: 0,
+ checks: 12
+ }
+ );
+ assert_eq!(
+ gm.perft(4, false),
+ PerftResult {
+ leaf_nodes: 197281,
+ captures: 1576,
+ en_passants: 0,
+ castles: 0,
+ checks: 469
+ }
+ );
// assert_eq!(board.perft(5, false), PerftResult { leaf_nodes: 4865609, captures: 82719, en_passants: 258, castles: 0, checks: 27351 });
}
@@ -468,10 +581,46 @@ mod tests {
let board = Board::from_FEN(fen);
let mut gm = Grossmeister::new(board);
- assert_eq!(gm.perft(0, false), PerftResult { leaf_nodes: 1, captures: 0, en_passants: 0, castles: 0 , checks: 0 });
- assert_eq!(gm.perft(1, false), PerftResult { leaf_nodes: 48, captures: 8, en_passants: 0, castles: 2 , checks: 0 });
- assert_eq!(gm.perft(2, false), PerftResult { leaf_nodes: 2039, captures: 351, en_passants: 1, castles: 91 , checks: 3 });
- assert_eq!(gm.perft(3, false), PerftResult { leaf_nodes: 97862, captures: 17102, en_passants: 45, castles: 3162, checks: 993 });
+ assert_eq!(
+ gm.perft(0, false),
+ PerftResult {
+ leaf_nodes: 1,
+ captures: 0,
+ en_passants: 0,
+ castles: 0,
+ checks: 0
+ }
+ );
+ assert_eq!(
+ gm.perft(1, false),
+ PerftResult {
+ leaf_nodes: 48,
+ captures: 8,
+ en_passants: 0,
+ castles: 2,
+ checks: 0
+ }
+ );
+ assert_eq!(
+ gm.perft(2, false),
+ PerftResult {
+ leaf_nodes: 2039,
+ captures: 351,
+ en_passants: 1,
+ castles: 91,
+ checks: 3
+ }
+ );
+ assert_eq!(
+ gm.perft(3, false),
+ PerftResult {
+ leaf_nodes: 97862,
+ captures: 17102,
+ en_passants: 45,
+ castles: 3162,
+ checks: 993
+ }
+ );
// assert_eq!(board.perft(4, false), PerftResult { leaf_nodes: 4085603, captures: 757163, en_passants: 1929, castles: 128013, checks: 25523 });
}
@@ -481,8 +630,26 @@ mod tests {
let board = Board::from_FEN(fen);
let mut gm = Grossmeister::new(board);
- assert_eq!(gm.perft(1, false), PerftResult { leaf_nodes: 14, captures: 1, en_passants: 0, castles: 0 , checks: 2 });
- assert_eq!(gm.perft(2, false), PerftResult { leaf_nodes: 191, captures: 14, en_passants: 0, castles: 0 , checks: 10 });
+ assert_eq!(
+ gm.perft(1, false),
+ PerftResult {
+ leaf_nodes: 14,
+ captures: 1,
+ en_passants: 0,
+ castles: 0,
+ checks: 2
+ }
+ );
+ assert_eq!(
+ gm.perft(2, false),
+ PerftResult {
+ leaf_nodes: 191,
+ captures: 14,
+ en_passants: 0,
+ castles: 0,
+ checks: 10
+ }
+ );
// assert_eq!(board.perft(3, false), PerftResult { leaf_nodes: 2812, captures: 209, en_passants: 2, castles: 0 , checks: 267 });
}
@@ -494,11 +661,26 @@ mod tests {
let (score, pv) = gm.iterative_deepening(8);
assert_eq!(score, SCORE_MATE - 3.0);
- assert_eq!(pv, vec![
- Move { source: Square::F3, target: Square::C6, kind: MoveKind::Capture },
- Move { source: Square::B7, target: Square::C6, kind: MoveKind::Capture },
- Move { source: Square::D3, target: Square::A6, kind: MoveKind::Quiet },
- ]);
+ assert_eq!(
+ pv,
+ vec![
+ Move {
+ source: Square::F3,
+ target: Square::C6,
+ kind: MoveKind::Capture
+ },
+ Move {
+ source: Square::B7,
+ target: Square::C6,
+ kind: MoveKind::Capture
+ },
+ Move {
+ source: Square::D3,
+ target: Square::A6,
+ kind: MoveKind::Quiet
+ },
+ ]
+ );
}
#[test]
@@ -510,21 +692,30 @@ mod tests {
let (_, pv) = gm.iterative_deepening(6);
assert_eq!(
pv[0],
- Move { source: Square::F5, target: Square::H4, kind: MoveKind::Quiet },
+ Move {
+ source: Square::F5,
+ target: Square::H4,
+ kind: MoveKind::Quiet
+ },
"You should save this poor knight from danger!"
);
}
#[test]
fn weird_bishop_sac() {
- let fen = String::from("r1b1k1nr/p4pp1/1pp1p3/4n2p/1b1qP3/1B1P3N/PPPBQPPP/RN2K2R w KQkq - 7 10");
+ let fen =
+ String::from("r1b1k1nr/p4pp1/1pp1p3/4n2p/1b1qP3/1B1P3N/PPPBQPPP/RN2K2R w KQkq - 7 10");
let board = Board::from_FEN(fen);
let mut gm = Grossmeister::new(board);
let (_, pv) = gm.iterative_deepening(5);
assert_eq!(
pv[0],
- Move { source: Square::C2, target: Square::C3, kind: MoveKind::Quiet },
+ Move {
+ source: Square::C2,
+ target: Square::C3,
+ kind: MoveKind::Quiet
+ },
"You should fork this bastard!"
);
}
diff --git a/src/grossmeister/ttable.rs b/src/grossmeister/ttable.rs
index b40bac1..162e4f5 100644
--- a/src/grossmeister/ttable.rs
+++ b/src/grossmeister/ttable.rs
@@ -33,7 +33,7 @@ struct TranspositionTable {
impl Default for TranspositionTable {
fn default() -> Self {
Self {
- table: vec![None; TTABLE_SIZE as usize]
+ table: vec![None; TTABLE_SIZE as usize],
}
}
}
@@ -46,13 +46,9 @@ impl TranspositionTable {
/// This operation is safe from collisions since it compares the *full* hash
/// TODO: only compare the other half of the hash
fn get(&self, hash: u64) -> Option<&TranspositionTableItem> {
- self.table[(hash % TTABLE_SIZE) as usize].as_ref().and_then(|item| {
- if item.hash == hash {
- Some(item)
- } else {
- None
- }
- })
+ self.table[(hash % TTABLE_SIZE) as usize]
+ .as_ref()
+ .and_then(|item| if item.hash == hash { Some(item) } else { None })
}
fn len(&self) -> usize {
@@ -72,33 +68,43 @@ pub struct MasterTable {
impl Grossmeister {
pub fn transposition(&self) -> Option<&TranspositionTableItem> {
- self.transposition_table.depth_preferred.get(self.board.hash)
+ self.transposition_table
+ .depth_preferred
+ .get(self.board.hash)
.or(self.transposition_table.always_replace.get(self.board.hash))
}
pub fn store_transposition(&mut self, transposition: TranspositionTableItem) {
- self.transposition_table.always_replace.set(self.board.hash, transposition);
-
- if match self.transposition_table.depth_preferred.get(self.board.hash) {
+ self.transposition_table
+ .always_replace
+ .set(self.board.hash, transposition);
+
+ if match self
+ .transposition_table
+ .depth_preferred
+ .get(self.board.hash)
+ {
Some(existing_transposition) => transposition.depth >= existing_transposition.depth,
- None => true
+ None => true,
} {
- self.transposition_table.depth_preferred.set(self.board.hash, transposition)
+ self.transposition_table
+ .depth_preferred
+ .set(self.board.hash, transposition)
}
// Store PV/Cut nodes in PV table
// Note: Cut nodes are probably only relevant in close-to-mate situations
if match transposition.node_type {
NodeType::PV => true, // Always replace PV nodes
- NodeType::Cut => {
- match self.transposition_table.pv.get(self.board.hash) {
- Some(existing_transposition) => existing_transposition.node_type != NodeType::PV,
- None => true,
- }
- }
+ NodeType::Cut => match self.transposition_table.pv.get(self.board.hash) {
+ Some(existing_transposition) => existing_transposition.node_type != NodeType::PV,
+ None => true,
+ },
_ => false,
} {
- self.transposition_table.pv.set(self.board.hash, transposition)
+ self.transposition_table
+ .pv
+ .set(self.board.hash, transposition)
}
}
@@ -108,7 +114,8 @@ impl Grossmeister {
}
pub fn table_full(&self) -> u64 {
- let total_entries = self.transposition_table.always_replace.len() + self.transposition_table.depth_preferred.len();
+ let total_entries = self.transposition_table.always_replace.len()
+ + self.transposition_table.depth_preferred.len();
let total_size = TTABLE_SIZE * 2;
(1000.0 * (total_entries as f64 / total_size as f64)) as u64
}