Skip to content

Commit

Permalink
Use lower and upper bounds in transposition table
Browse files Browse the repository at this point in the history
  • Loading branch information
vinc committed Oct 12, 2017
1 parent b14f90b commit 441470e
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Add getopt to parse program options
- Allow NMP, IID, and LMR at shallower depth
- Set NMP R to 3
- Save upper and lower bounds in transposition table


## [0.3.0] - 2017-10-11
Expand Down
35 changes: 30 additions & 5 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use fen::FEN;
use game::Game;
use moves::Move;
use moves_generator::MovesGenerator;
use transpositions::Bound;

pub trait Search {
fn perft(&mut self, depth: usize) -> u64;
Expand Down Expand Up @@ -80,7 +81,7 @@ impl Search for Game {
alpha
}

fn search(&mut self, mut alpha: Score, beta: Score, depth: usize, ply: usize) -> Score {
fn search(&mut self, mut alpha: Score, mut beta: Score, depth: usize, ply: usize) -> Score {
if self.clock.poll(self.nodes_count) {
return 0;
}
Expand All @@ -99,13 +100,31 @@ impl Search for Game {
let side = self.positions.top().side;
let is_null_move = !self.positions.top().null_move_right;
let is_pv = alpha != beta - 1;
let old_alpha = alpha;

let mut best_move = Move::new_null();

// Try to get the best move from transpositions table
if let Some(t) = self.tt.get(&hash) {
if t.depth() >= depth { // This node has already been searched
return t.score()
match t.bound() {
Bound::Exact => {
return t.score()
},
Bound::Lower => {
if t.score() > alpha {
alpha = t.score();
}
},
Bound::Upper => {
if t.score() < beta {
beta = t.score();
}
}
}
if alpha >= beta {
return t.score()
}
}

best_move = t.best_move();
Expand Down Expand Up @@ -227,6 +246,7 @@ impl Search for Game {
self.moves.add_killer_move(m);
}

self.tt.set(hash, depth, score, m, Bound::Lower);
return beta;
}

Expand All @@ -246,7 +266,12 @@ impl Search for Game {
}

if !best_move.is_null() {
self.tt.set(hash, depth, alpha, best_move);
let bound = if alpha <= old_alpha {
Bound::Upper
} else {
Bound::Lower
};
self.tt.set(hash, depth, alpha, best_move, bound);
}

alpha
Expand Down Expand Up @@ -331,7 +356,7 @@ impl Search for Game {
if self.is_verbose && !self.clock.poll(self.nodes_count) {
// TODO: skip the first thousand nodes to gain time?

self.tt.set(hash, depth, score, m);
self.tt.set(hash, depth, score, m, Bound::Exact);

// Get the PV line from the TT.
self.print_thinking(depth, score, m);
Expand All @@ -351,7 +376,7 @@ impl Search for Game {
best_score = best_scores[depth];

// TODO: use best_score instead of alpha?
self.tt.set(hash, depth, alpha, best_move);
self.tt.set(hash, depth, alpha, best_move, Bound::Exact);
}
if !has_legal_moves {
break;
Expand Down
31 changes: 23 additions & 8 deletions src/transpositions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,23 @@ use std::mem;
use common::*;
use moves::Move;

#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Bound {
Exact,
Lower,
Upper
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Transposition {
hash: u64, // 64 bits => 8 bytes
best_move: Move, // 16 bits => 2 bytes
score: Score, // 16 bits => 2 bytes
depth: u8 // 8 bits => 1 bytes
depth: u8, // 8 bits => 1 bytes
bound: Bound, // 8 bits => 1 bytes

// Total: 13 bytes, which will use 16 bytes including alignment padding.
// Total: 14 bytes, which will use 16 bytes including alignment padding.

// NOTE: `depth` will never go above MAX_PLY, which is 128 so we can store
// it as `u8`.
Expand All @@ -24,17 +33,18 @@ pub struct Transposition {
}

impl Transposition {
pub fn new(hash: u64, depth: usize, score: Score, best_move: Move) -> Transposition {
pub fn new(hash: u64, depth: usize, score: Score, best_move: Move, bound: Bound) -> Transposition {
Transposition {
hash: hash,
depth: depth as u8,
score: score,
best_move: best_move,
bound: bound
}
}

pub fn new_null() -> Transposition {
Transposition::new(0, 0, 0, Move::new_null())
Transposition::new(0, 0, 0, Move::new_null(), Bound::Exact)
}

pub fn depth(&self) -> usize {
Expand All @@ -48,6 +58,10 @@ impl Transposition {
pub fn best_move(&self) -> Move {
self.best_move
}

pub fn bound(&self) -> Bound {
self.bound
}
}

pub struct Transpositions {
Expand Down Expand Up @@ -103,10 +117,10 @@ impl Transpositions {
}
}

pub fn set(&mut self, hash: u64, depth: usize, score: Score, best_move: Move) {
pub fn set(&mut self, hash: u64, depth: usize, score: Score, best_move: Move, bound: Bound) {
self.stats_inserts += 1;

let t = Transposition::new(hash, depth, score, best_move);
let t = Transposition::new(hash, depth, score, best_move, bound);
let n = self.size as u64;
let k = (hash % n) as usize;

Expand Down Expand Up @@ -174,13 +188,14 @@ mod tests {
let m = Move::new(E2, E4, DOUBLE_PAWN_PUSH);
let s = 100;
let d = 8;
let t = Transposition::new(h, d, s, m);
let b = Bound::Exact;
let t = Transposition::new(h, d, s, m, b);

assert_eq!(t.best_move(), m);
assert_eq!(t.score(), s);
assert_eq!(t.depth(), d);

tt.set(h, d, s, m);
tt.set(h, d, s, m, b);

assert_eq!(tt.get(&h).unwrap().best_move(), m);

Expand Down

0 comments on commit 441470e

Please sign in to comment.