|
@@ -1,4 +1,4 @@
|
|
|
-use std::{clone, sync::{Arc, RwLock, mpsc}, cell::RefCell, time::{Instant, Duration}, ops::Add};
|
|
|
+use std::{clone, sync::{Arc, RwLock, mpsc, Mutex}, cell::{RefCell, UnsafeCell}, time::{Instant, Duration}, ops::Add, borrow::BorrowMut};
|
|
|
|
|
|
use movegen::*;
|
|
|
use board::Board;
|
|
@@ -30,7 +30,7 @@ pub struct SearchControl {
|
|
|
pub killer_moves: Vec<[Move; 2]>,
|
|
|
|
|
|
pub last_move: Option<Move>,
|
|
|
- pub countermoves: [[Move; 64]; 64],
|
|
|
+ pub countermoves: Arc<Mutex<CountermoveTable>>,
|
|
|
|
|
|
/// current halfmove clock for discarding old hash entries
|
|
|
pub halfmove_age: u16,
|
|
@@ -46,7 +46,7 @@ pub struct SearchControl {
|
|
|
pub movetime: Option<Duration>,
|
|
|
pub remaining_time: Option<Duration>,
|
|
|
|
|
|
- nullmoves_enabled: bool,
|
|
|
+ reductions_allowed: bool,
|
|
|
}
|
|
|
|
|
|
|
|
@@ -68,7 +68,7 @@ impl SearchControl {
|
|
|
hash,
|
|
|
killer_moves: Vec::new(),
|
|
|
last_move: None,
|
|
|
- countermoves: [[Move::nullmove(); 64]; 64],
|
|
|
+ countermoves: Arc::new(Mutex::new(CountermoveTable::new())),
|
|
|
halfmove_age: 0,
|
|
|
stop_channel,
|
|
|
stopping: false,
|
|
@@ -76,7 +76,7 @@ impl SearchControl {
|
|
|
movetime: None,
|
|
|
remaining_time: None,
|
|
|
initial_depth: 0,
|
|
|
- nullmoves_enabled: true
|
|
|
+ reductions_allowed: true
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -100,12 +100,6 @@ impl SearchControl {
|
|
|
self.killer_moves[ply_depth].contains(killer)
|
|
|
}
|
|
|
|
|
|
- pub fn countermove_to(&self, last_move: Move) -> Move {
|
|
|
- let from = last_move.to_simple().from;
|
|
|
- let to = last_move.to_simple().to;
|
|
|
- self.countermoves[from as usize][to as usize]
|
|
|
- }
|
|
|
-
|
|
|
fn reconstruct_pv(&self) -> Vec<Move> {
|
|
|
let mut pv: Vec<Move> = Vec::new();
|
|
|
let mut g = self.board.clone();
|
|
@@ -210,7 +204,7 @@ impl SearchControl {
|
|
|
|
|
|
let result =
|
|
|
if let Some(bv) = best_val {
|
|
|
- const WINDOW_RADIUS: PosValue = 10;
|
|
|
+ const WINDOW_RADIUS: PosValue = 20;
|
|
|
self.windowed_search(depth, bv, WINDOW_RADIUS)
|
|
|
} else {
|
|
|
search(self.board.clone(), self, self.hash.clone(), MIN_VALUE, MAX_VALUE, depth)
|
|
@@ -241,6 +235,10 @@ impl SearchControl {
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
+ if val.abs() > 10000 {
|
|
|
+ let neg = is_mate_in_p1(-val);
|
|
|
+ println!("neg of: {:?}", neg);
|
|
|
+ }
|
|
|
let infostring = format!("info depth {} score cp {} time {} nodes {} nps {} hashfull {} pv{}", depth, val, elapsed.as_millis(), nodes, nps, hashfull, pv);
|
|
|
println!("{}", infostring);
|
|
|
info!("{}", infostring);
|
|
@@ -410,7 +408,8 @@ pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut a
|
|
|
game,
|
|
|
cache_entry,
|
|
|
&sc.killer_moves[ply_depth],
|
|
|
- last_move.map(|lm| sc.countermove_to(lm)),
|
|
|
+ if depth >= 3 { last_move } else { None },
|
|
|
+ sc.countermoves.clone(),
|
|
|
game.turn
|
|
|
);
|
|
|
|
|
@@ -429,16 +428,16 @@ pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut a
|
|
|
}
|
|
|
|
|
|
// Nullmove
|
|
|
- if sc.nullmoves_enabled && !check && depth >= 4 && game.get_all_side(game.turn).count_ones() > 5 {
|
|
|
+ if sc.reductions_allowed && !check && depth >= 4 && game.get_all_side(game.turn).count_ones() > 5 {
|
|
|
|
|
|
- let reduce = if depth > 5 { if depth > 7 { 5 } else { 4 }} else { 3 };
|
|
|
+ let reduce = if depth > 5 { if depth > 7 { 4 } else { 3 }} else { 2 };
|
|
|
|
|
|
let nmov = Move::nullmove();
|
|
|
let undo = game.apply(nmov);
|
|
|
|
|
|
- sc.nullmoves_enabled = false;
|
|
|
+ sc.reductions_allowed = false;
|
|
|
let val = -negamax(game, sc, hash, -beta, -alpha, depth - reduce);
|
|
|
- sc.nullmoves_enabled = true;
|
|
|
+ sc.reductions_allowed = true;
|
|
|
game.undo_move(undo);
|
|
|
|
|
|
if is_mate_in_p1(val).is_none() {
|
|
@@ -461,14 +460,37 @@ pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut a
|
|
|
let mut best_move: Option<Move> = None;
|
|
|
|
|
|
while let Some(mov) = moves.next() {
|
|
|
+
|
|
|
+ let mut reduce = 0;
|
|
|
+ let reductions_allowed_before = sc.reductions_allowed;
|
|
|
+ if depth > 4 && moves.is_late() && !mov.is_promotion() && reductions_allowed_before {
|
|
|
+ reduce = 1;
|
|
|
+ if depth >= 8 {
|
|
|
+ reduce = 2;
|
|
|
+ }
|
|
|
+ sc.reductions_allowed = false;
|
|
|
+ }
|
|
|
+
|
|
|
//println!("mov: {}", mov.to_string());
|
|
|
let undo = game.apply(mov);
|
|
|
sc.last_move = Some(mov);
|
|
|
|
|
|
- let val = increase_mate_in(
|
|
|
- -negamax(game, sc, hash, -decrease_mate_in(beta), -decrease_mate_in(alpha), depth - 1)
|
|
|
+ let mut val = increase_mate_in(
|
|
|
+ -negamax(game, sc, hash, -decrease_mate_in(beta), -decrease_mate_in(alpha), depth - 1 - reduce)
|
|
|
);
|
|
|
game.undo_move(undo);
|
|
|
+ sc.reductions_allowed = reductions_allowed_before;
|
|
|
+
|
|
|
+ if reduce != 0 && (val >= beta || val > alpha) {
|
|
|
+ reduce = 0;
|
|
|
+ let undo = game.apply(mov);
|
|
|
+ sc.last_move = Some(mov);
|
|
|
+
|
|
|
+ val = increase_mate_in(
|
|
|
+ -negamax(game, sc, hash, -decrease_mate_in(beta), -decrease_mate_in(alpha), depth - 1 - reduce)
|
|
|
+ );
|
|
|
+ game.undo_move(undo);
|
|
|
+ }
|
|
|
|
|
|
// return if the search has been cancelled
|
|
|
if sc.stopping {
|
|
@@ -480,7 +502,9 @@ pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut a
|
|
|
if !mov.is_capture() {
|
|
|
sc.insert_killer(ply_depth, mov);
|
|
|
if depth >= 2 {
|
|
|
- sc.countermoves[mov.to_simple().from as usize][mov.to_simple().to as usize] = mov;
|
|
|
+ if let Some(lm) = last_move {
|
|
|
+ sc.countermoves.as_ref().lock().unwrap().update_score(game.turn, lm, mov, (depth * depth) as i16);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
return val;
|
|
@@ -488,8 +512,10 @@ pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut a
|
|
|
if val > alpha {
|
|
|
alpha = val;
|
|
|
best_move = Some(mov);
|
|
|
- if depth >= 3 {
|
|
|
- sc.countermoves[mov.to_simple().from as usize][mov.to_simple().to as usize] = mov;
|
|
|
+ if depth >= 2 {
|
|
|
+ if let Some(lm) = last_move {
|
|
|
+ //sc.countermoves.as_ref().lock().unwrap().update_score(lm, mov, (depth * depth) as i16);
|
|
|
+ }
|
|
|
}
|
|
|
if alpha >= mate_in_p1(1) {
|
|
|
break;
|
|
@@ -497,7 +523,7 @@ pub fn negamax(game: &mut Board, sc: &mut SearchControl, hash: &mut Cache, mut a
|
|
|
}
|
|
|
|
|
|
if let Some(lm) = last_move {
|
|
|
- moves.update_counter(sc.countermove_to(lm));
|
|
|
+ //moves.update_counter(sc.countermove_to(lm));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -593,14 +619,14 @@ impl SearchControl {
|
|
|
initial_depth: depth,
|
|
|
killer_moves: Vec::new(),
|
|
|
last_move: None,
|
|
|
- countermoves: [[Move::nullmove(); 64]; 64],
|
|
|
+ countermoves: Arc::new(Mutex::new(CountermoveTable::new())),
|
|
|
halfmove_age: board.ply_number() as _,
|
|
|
stop_channel,
|
|
|
stopping: false,
|
|
|
search_started: Instant::now(),
|
|
|
movetime: None,
|
|
|
remaining_time: None,
|
|
|
- nullmoves_enabled: false
|
|
|
+ reductions_allowed: false
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -640,8 +666,38 @@ impl SearchControl {
|
|
|
}
|
|
|
|
|
|
|
|
|
+pub struct CountermoveTable {
|
|
|
+ table: [[[[i16; 12]; 64]; 12]; 64],
|
|
|
+}
|
|
|
+
|
|
|
+unsafe impl Send for CountermoveTable { }
|
|
|
+unsafe impl Sync for CountermoveTable { }
|
|
|
+
|
|
|
+impl CountermoveTable {
|
|
|
|
|
|
+ pub fn new() -> Self {
|
|
|
+ CountermoveTable { table: [[[[0; 12]; 64]; 12]; 64] }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn get_score(&self, side: Side, last_move: Move, this_move: Move) -> i16 {
|
|
|
+ let side_offs = if side == BLACK { 6 } else { 0 };
|
|
|
+ self.table
|
|
|
+ [last_move.to() as usize]
|
|
|
+ [last_move.piece_type() as usize + side_offs]
|
|
|
+ [this_move.to() as usize]
|
|
|
+ [this_move.piece_type() as usize + side_offs]
|
|
|
+ }
|
|
|
|
|
|
+ pub fn update_score(&mut self, side: Side, last_move: Move, this_move: Move, increase: i16) {
|
|
|
+ let side_offs = if side == BLACK { 6 } else { 0 };
|
|
|
+ let entry = &mut self.table
|
|
|
+ [last_move.to() as usize]
|
|
|
+ [last_move.piece_type() as usize + side_offs]
|
|
|
+ [this_move.to() as usize]
|
|
|
+ [this_move.piece_type() as usize + side_offs];
|
|
|
+ *entry = entry.saturating_add(increase);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|