aboutsummaryrefslogtreecommitdiff
path: root/src/board
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2023-01-25 05:18:38 +0300
committereug-vs <eugene@eug-vs.xyz>2023-01-25 05:18:38 +0300
commit3522398f7a456b400dc48de4c202a013490cd4fc (patch)
treefab5655517165e403a0c0965377f38306cc2b155 /src/board
parent46b862a25203cb492dd45ba1696a28338a42065b (diff)
downloadchessnost-3522398f7a456b400dc48de4c202a013490cd4fc.tar.gz
feat: use transposition table in negamax
Diffstat (limited to 'src/board')
-rw-r--r--src/board/engine.rs52
-rw-r--r--src/board/mod.rs12
-rw-r--r--src/board/ttable.rs25
3 files changed, 80 insertions, 9 deletions
diff --git a/src/board/engine.rs b/src/board/engine.rs
index 73c57af..fb06fa4 100644
--- a/src/board/engine.rs
+++ b/src/board/engine.rs
@@ -1,5 +1,7 @@
use crate::{bitboard::pop_count, board::*};
+use super::ttable::{NodeType, TranspositionTableItem};
+
#[derive(Debug, Default, PartialEq)]
pub struct PerftResult {
leaf_nodes: u64,
@@ -115,10 +117,23 @@ impl Board {
}
pub fn negamax_search(&mut self, mut alpha: f32, beta: f32, depth_left: u8) -> (f32, Vec<Move>) {
+ let transposition = self.transposition_table[(self.hash % TTABLE_SIZE) as usize];
+
let mut principal_variation = Vec::new();
let color = self.color();
- let moves = self.generate_pseudolegal_moves(color);
+ let moves = match transposition {
+ Some(item) => {
+ // println!("Cache hit! {:?}", item);
+
+ if item.node_type == NodeType::PV && item.depth >= depth_left {
+ vec![item.best_move]
+ } else {
+ self.generate_pseudolegal_moves(color)
+ }
+ }
+ None => self.generate_pseudolegal_moves(color),
+ };
if depth_left == 0 {
return (self.evaluate(Some(moves.len() as f32)), principal_variation);
@@ -131,18 +146,41 @@ impl Board {
let captured_piece = self.make_move(mov);
if !self.is_king_in_check(color) {
- let (mut evaluation, mut subtree_pv) = self.negamax_search(-beta, -alpha, depth_left - 1);
- evaluation *= -1.;
+ let (mut score, mut subtree_pv) = self.negamax_search(-beta, -alpha, depth_left - 1);
+ score *= -1.;
self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before);
- if evaluation >= beta {
- return (beta, principal_variation); // Fail-hard beta-cutoff
+ if score >= beta {
+ self.transposition_table[(self.hash % TTABLE_SIZE) as usize] = Some(TranspositionTableItem {
+ hash: self.hash,
+ best_move: mov,
+ depth: depth_left, // TODO: should be actual depth searched
+ node_type: NodeType::Cut,
+ score,
+ });
+ return (beta, principal_variation);
}
- if evaluation > alpha {
- alpha = evaluation;
+ if score > alpha {
+ alpha = score;
principal_variation = Vec::with_capacity(depth_left as usize);
principal_variation.push(mov);
principal_variation.append(&mut subtree_pv);
+
+ self.transposition_table[(self.hash % TTABLE_SIZE) as usize] = Some(TranspositionTableItem {
+ hash: self.hash,
+ best_move: mov,
+ depth: depth_left, // TODO: should be actual depth searched
+ node_type: NodeType::PV,
+ score,
+ });
+ } else {
+ self.transposition_table[(self.hash % TTABLE_SIZE) as usize] = Some(TranspositionTableItem {
+ hash: self.hash,
+ best_move: mov,
+ depth: depth_left, // TODO: should be actual depth searched
+ node_type: NodeType::All,
+ score,
+ });
}
} else {
self.unmake_move(mov, captured_piece, ep_target_before, castling_rights_before, hash_before);
diff --git a/src/board/mod.rs b/src/board/mod.rs
index a8166a1..2ff7f9c 100644
--- a/src/board/mod.rs
+++ b/src/board/mod.rs
@@ -1,13 +1,16 @@
use rand::Rng;
use crate::{bitboard::{Bitboard, serialize_bitboard, bitscan, pop_count}, moves::{Move, MoveKind}, attacks::Attacks, square::Square};
+
+use self::ttable::{TranspositionTable, TTABLE_SIZE};
mod engine;
+mod ttable;
pub enum CastlingSide {
King,
Queen,
}
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq)]
pub struct Board {
pub pieces: [Bitboard; 12],
@@ -26,6 +29,7 @@ pub struct Board {
/// Zobrist hash of the current position
pub hash: u64,
+ transposition_table: TranspositionTable,
zobrist_seed: [u64; 781],
attacks: Attacks,
}
@@ -121,6 +125,7 @@ impl Board {
castling_rights: [[true; 2]; 2], // TODO: actualy parse from FEN
ep_target: None, // TODO: parse from FEN
hash: 0,
+ transposition_table: vec![None; TTABLE_SIZE as usize],
zobrist_seed,
};
board.update_occupancy();
@@ -524,7 +529,10 @@ impl Board {
self.hash ^= self.zobrist_seed[source_piece * 12 + mov.target as usize];
PieceType::from(source_piece)
},
- None => panic!("Move is malformed: source piece not found"),
+ None => {
+ self.print();
+ panic!("{:?} is malformed: source piece not found", mov);
+ }
};
// When castling, also move a rook
diff --git a/src/board/ttable.rs b/src/board/ttable.rs
new file mode 100644
index 0000000..3a103da
--- /dev/null
+++ b/src/board/ttable.rs
@@ -0,0 +1,25 @@
+use crate::moves::Move;
+
+/// https://www.chessprogramming.org/Node_Types
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub enum NodeType {
+ /// Principal variation node - exact score
+ PV,
+ /// Fail-high
+ Cut,
+ /// Fail-low
+ All,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub struct TranspositionTableItem {
+ /// Zobrist hash of this position
+ pub hash: u64,
+ pub best_move: Move,
+ pub depth: u8,
+ pub score: f32,
+ pub node_type: NodeType,
+}
+
+pub const TTABLE_SIZE: u64 = 2u64.pow(24);
+pub type TranspositionTable = Vec<Option<TranspositionTableItem>>;