use std::cell::UnsafeCell;
use std::marker::PhantomData;
use uci::{SetOption, OptionDescription};
use board::*;
use value::*;
use depth::*;
use moves::*;
use evaluator::Evaluator;
use qsearch::{Qsearch, QsearchParams, QsearchResult};
use move_generator::MoveGenerator;
use utils::MoveStack;
#[derive(Clone, Debug)]
pub struct StdQsearchResult {
value: Value,
searched_nodes: u64,
}
impl QsearchResult for StdQsearchResult {
#[inline]
fn new(value: Value, searched_nodes: u64) -> Self {
debug_assert!(VALUE_EVAL_MIN <= value && value <= VALUE_EVAL_MAX);
StdQsearchResult {
value: value,
searched_nodes: searched_nodes,
}
}
#[inline]
fn value(&self) -> Value {
self.value
}
#[inline]
fn searched_nodes(&self) -> u64 {
self.searched_nodes
}
}
pub struct StdQsearch<T: MoveGenerator> {
phantom: PhantomData<T>,
}
impl<T: MoveGenerator> Qsearch for StdQsearch<T> {
type MoveGenerator = T;
type QsearchResult = StdQsearchResult;
fn qsearch(params: QsearchParams<Self::MoveGenerator>) -> Self::QsearchResult {
debug_assert!(DEPTH_MIN <= params.depth && params.depth <= 0);
debug_assert!(params.lower_bound >= VALUE_MIN);
debug_assert!(params.upper_bound <= VALUE_MAX);
debug_assert!(params.lower_bound < params.upper_bound);
thread_local!(
static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
);
let mut searched_nodes = 0;
let value = MOVE_STACK.with(|s| unsafe {
qsearch(params.position,
params.lower_bound,
params.upper_bound,
params.static_eval,
0,
-params.depth,
&mut *s.get(),
&mut searched_nodes)
});
StdQsearchResult::new(value, searched_nodes)
}
}
impl<T: MoveGenerator> SetOption for StdQsearch<T> {
fn options() -> Vec<(&'static str, OptionDescription)> {
T::options()
}
fn set_option(name: &str, value: &str) {
T::set_option(name, value)
}
}
fn qsearch<T: MoveGenerator>(position: &mut T,
mut lower_bound: Value, upper_bound: Value, mut stand_pat: Value, mut recapture_squares: Bitboard,
ply: i8, move_stack: &mut MoveStack,
searched_nodes: &mut u64)
-> Value {
debug_assert!(lower_bound < upper_bound);
debug_assert!(stand_pat == VALUE_UNKNOWN ||
stand_pat == position.evaluator().evaluate(position.board()));
const PIECE_VALUES: [Value; 8] = [10000, 975, 500, 325, 325, 100, 0, 0];
let is_check = position.is_check();
if is_check {
stand_pat = lower_bound;
} else if stand_pat == VALUE_UNKNOWN {
stand_pat = position.evaluator().evaluate(position.board());
}
if stand_pat >= upper_bound {
return stand_pat;
}
if stand_pat > lower_bound {
lower_bound = stand_pat;
}
let obligatory_material_gain = (lower_bound as isize) - (stand_pat as isize) -
(PIECE_VALUES[KNIGHT] - 4 * PIECE_VALUES[PAWN] / 3) as isize;
move_stack.save();
position.generate_forcing(ply <= 0, move_stack);
'trymoves: while let Some(m) = move_stack.pull_best() {
let move_type = m.move_type();
let dest_square_bb = 1 << m.dest_square();
let captured_piece = m.captured_piece();
if !is_check && move_type != MOVE_ENPASSANT && recapture_squares & dest_square_bb == 0 {
match position.evaluate_move(m) {
x if x < 0 => continue 'trymoves,
0 if ply >= 2 && captured_piece < PIECE_NONE => continue 'trymoves,
_ => (),
}
}
if position.do_move(m).is_some() {
if !position.is_check() {
let material_gain = if move_type == MOVE_PROMOTION {
PIECE_VALUES[captured_piece] +
PIECE_VALUES[Move::piece_from_aux_data(m.aux_data())] -
PIECE_VALUES[PAWN]
} else {
unsafe { *PIECE_VALUES.get_unchecked(captured_piece) }
};
if (material_gain as isize) < obligatory_material_gain {
position.undo_move(m);
continue 'trymoves;
}
}
*searched_nodes += 1;
let value = -qsearch(position,
-upper_bound,
-lower_bound,
VALUE_UNKNOWN,
recapture_squares ^ dest_square_bb,
ply + 1,
move_stack,
searched_nodes);
position.undo_move(m);
if value >= upper_bound {
lower_bound = value;
break 'trymoves;
}
if value > lower_bound {
lower_bound = value;
}
recapture_squares &= !dest_square_bb;
}
}
move_stack.restore();
match lower_bound {
x if x < VALUE_EVAL_MIN => VALUE_EVAL_MIN,
x if x > VALUE_EVAL_MAX => VALUE_EVAL_MAX,
x => x,
}
}
#[cfg(test)]
mod tests {
use board::*;
use value::*;
use move_generator::*;
use stock::{SimpleEvaluator, StdMoveGenerator};
use utils::MoveStack;
type P = StdMoveGenerator<SimpleEvaluator>;
#[test]
fn qsearch() {
use super::qsearch;
let mut s = MoveStack::new();
let d = 32;
let fen = "8/8/8/8/6k1/6P1/8/6K1 b - - 0 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0).abs() <= d);
let fen = "8/8/8/8/6k1/6P1/8/5bK1 b - - 0 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0) > 225 - d);
let fen = "8/8/8/8/5pkp/6P1/5P1P/6K1 b - - 0 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0).abs() <= d);
let fen = "8/8/8/8/5pkp/6P1/5PKP/8 b - - 0 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0) <= -100 + d);
let fen = "r1bqkbnr/pppp2pp/2n2p2/4p3/2N1P2B/3P1N2/PPP2PPP/R2QKB1R w - - 5 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0).abs() <= d);
let fen = "r1bqkbnr/pppp2pp/2n2p2/4N3/4P2B/3P1N2/PPP2PPP/R2QKB1R b - - 5 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0) <= -100 + d);
let fen = "rn2kbnr/ppppqppp/8/4p3/2N1P1b1/3P1N2/PPP2PPP/R1BKQB1R w - - 5 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -1000, 1000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0).abs() <= d);
let fen = "8/8/8/8/8/7k/7q/7K w - - 0 1";
let board = Board::from_fen(fen).ok().unwrap();
let mut p = P::from_board(board).ok().unwrap();
assert!(qsearch(&mut p, -10000, 10000, VALUE_UNKNOWN, 0, 0, &mut s, &mut 0) <= -10000);
}
}