Compare commits
2 Commits
c0e8766fee
...
d1506e4d6c
Author | SHA1 | Date | |
---|---|---|---|
d1506e4d6c | |||
0591f29c66 |
@ -15,6 +15,7 @@ Copyright © 2024 dogeystamp <dogeystamp@disroot.org>
|
|||||||
use chess_inator::fen::FromFen;
|
use chess_inator::fen::FromFen;
|
||||||
use chess_inator::movegen::{FromUCIAlgebraic, Move, ToUCIAlgebraic};
|
use chess_inator::movegen::{FromUCIAlgebraic, Move, ToUCIAlgebraic};
|
||||||
use chess_inator::search::{best_line, SearchEval};
|
use chess_inator::search::{best_line, SearchEval};
|
||||||
|
use chess_inator::eval::{eval_metrics};
|
||||||
use chess_inator::Board;
|
use chess_inator::Board;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
@ -108,6 +109,12 @@ fn cmd_go(mut _tokens: std::str::SplitWhitespace<'_>, board: &mut Board) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print static evaluation of the position.
|
||||||
|
fn cmd_eval(mut _tokens: std::str::SplitWhitespace<'_>, board: &mut Board) {
|
||||||
|
let res = eval_metrics(board);
|
||||||
|
println!("STATIC EVAL (negative black, positive white):\n- pst: {}\n- king distance: {} ({} distance)\n- phase: {}\n- total: {}", res.pst_eval, res.king_distance_eval, res.king_distance, res.phase, res.total_eval);
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
|
|
||||||
@ -137,6 +144,10 @@ fn main() {
|
|||||||
"go" => {
|
"go" => {
|
||||||
cmd_go(tokens, &mut board);
|
cmd_go(tokens, &mut board);
|
||||||
}
|
}
|
||||||
|
// non-standard command.
|
||||||
|
"eval" => {
|
||||||
|
cmd_eval(tokens, &mut board);
|
||||||
|
}
|
||||||
_ => ignore!(),
|
_ => ignore!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
src/eval.rs
75
src/eval.rs
@ -14,7 +14,7 @@ Copyright © 2024 dogeystamp <dogeystamp@disroot.org>
|
|||||||
//! Position evaluation.
|
//! Position evaluation.
|
||||||
|
|
||||||
use crate::{Board, Color, Piece, Square, N_COLORS, N_PIECES, N_SQUARES};
|
use crate::{Board, Color, Piece, Square, N_COLORS, N_PIECES, N_SQUARES};
|
||||||
use core::cmp::max;
|
use core::cmp::{max, min};
|
||||||
use core::ops::Index;
|
use core::ops::Index;
|
||||||
|
|
||||||
/// Signed centipawn type.
|
/// Signed centipawn type.
|
||||||
@ -306,14 +306,14 @@ pub const PST_ENDGAME: Pst = Pst([
|
|||||||
|
|
||||||
// king
|
// king
|
||||||
make_pst([
|
make_pst([
|
||||||
-50, -50, -50, -50, -50, -50, -50, -50, // 8
|
-100,-100, -90, -70, -70, -90,-100,-100, // 8
|
||||||
-50, -10, -10, -10, -10, -10, -10, -50, // 7
|
-100, -20, -15, -13, -13, -15, -20,-100, // 7
|
||||||
-50, -10, 0, 0, 0, 0, -10, -50, // 6
|
-90, -15, -5, -5, -5, -5, -15, -90, // 6
|
||||||
-50, -10, 0, 4, 4, 0, -10, -50, // 5
|
-70, -13, -5, 4, 4, -5, -13, -70, // 5
|
||||||
-50, -10, 0, 4, 4, 0, -10, -50, // 4
|
-70, -13, -5, 4, 4, -5, -13, -70, // 4
|
||||||
-50, -10, 0, 0, 0, 0, -10, -50, // 3
|
-90, -15, -5, -5, -5, -5, -15, -90, // 3
|
||||||
-50, -10, -10, -10, -10, -10, -10, -50, // 2
|
-100, -20, -15, -13, -13, -15, -20,-100, // 2
|
||||||
-50, -50, -50, -50, -50, -50, -50, -50, // 1
|
-100,-100, -90, -70, -70, -90,-100,-100, // 1
|
||||||
// a b c d e f g h
|
// a b c d e f g h
|
||||||
], 20_000),
|
], 20_000),
|
||||||
|
|
||||||
@ -344,16 +344,57 @@ pub const PST_ENDGAME: Pst = Pst([
|
|||||||
], 100),
|
], 100),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
/// Centipawn, signed, eval metrics.
|
||||||
|
pub struct EvalMetrics {
|
||||||
|
pub pst_eval: i32,
|
||||||
|
/// Manhattan distance between kings.
|
||||||
|
pub king_distance: usize,
|
||||||
|
pub king_distance_eval: i32,
|
||||||
|
pub total_eval: i32,
|
||||||
|
/// game phase, from 14 (start) to 0 (end endgame).
|
||||||
|
pub phase: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_metrics(board: &Board) -> EvalMetrics {
|
||||||
|
// we'll define endgame as the moment when there are 7 non pawn/king pieces left on the
|
||||||
|
// board in total.
|
||||||
|
//
|
||||||
|
// `phase` will range from 14 (game start) to 7 (endgame) to 0 (end end game).
|
||||||
|
let phase = i32::from(board.eval.min_maj_pieces);
|
||||||
|
|
||||||
|
// piece square tables
|
||||||
|
let pst_eval = i32::from(board.eval.midgame.score) * max(7, phase - 7) / 7
|
||||||
|
+ i32::from(board.eval.endgame.score) * min(14 - phase, 7) / 7;
|
||||||
|
|
||||||
|
// Signed factor of the color with the material advantage.
|
||||||
|
let advantage = pst_eval / 10;
|
||||||
|
|
||||||
|
let kings1 = board[board.turn][Piece::King].into_iter();
|
||||||
|
let kings2 = board[board.turn.flip()][Piece::King].into_iter();
|
||||||
|
let king_distance = kings1
|
||||||
|
.zip(kings2)
|
||||||
|
.next()
|
||||||
|
.map_or(0, |(k1, k2)| k1.manhattan(k2));
|
||||||
|
|
||||||
|
// attempt to minimize king distance for checkmates
|
||||||
|
let king_distance_eval = -advantage * i32::try_from(king_distance).unwrap() * max(7 - phase, 0) / 100;
|
||||||
|
|
||||||
|
let eval = pst_eval + king_distance_eval;
|
||||||
|
|
||||||
|
EvalMetrics {
|
||||||
|
pst_eval,
|
||||||
|
king_distance,
|
||||||
|
king_distance_eval,
|
||||||
|
total_eval: eval,
|
||||||
|
phase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Eval for Board {
|
impl Eval for Board {
|
||||||
fn eval(&self) -> EvalInt {
|
fn eval(&self) -> EvalInt {
|
||||||
// we'll define endgame as the moment when there are 7 non pawn/king pieces left on the
|
let res = eval_metrics(self);
|
||||||
// board in total.
|
|
||||||
//
|
res.total_eval.try_into().unwrap()
|
||||||
// `phase` will range from 7 (game start) to 0 (endgame).
|
|
||||||
let phase = i32::from(self.eval.min_maj_pieces.saturating_sub(7));
|
|
||||||
let eval = i32::from(self.eval.midgame.score) * phase / 7
|
|
||||||
+ i32::from(self.eval.endgame.score) * max(7 - phase, 0) / 7;
|
|
||||||
eval.try_into().unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
src/lib.rs
39
src/lib.rs
@ -205,14 +205,14 @@ macro_rules! from_row_col_generic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Square {
|
impl Square {
|
||||||
fn from_row_col(r: usize, c: usize) -> Result<Self, SquareError> {
|
pub fn from_row_col(r: usize, c: usize) -> Result<Self, SquareError> {
|
||||||
//! Get index of square based on row and column.
|
//! Get index of square based on row and column.
|
||||||
from_row_col_generic!(usize, r, c)
|
from_row_col_generic!(usize, r, c)
|
||||||
}
|
}
|
||||||
fn from_row_col_signed(r: isize, c: isize) -> Result<Self, SquareError> {
|
pub fn from_row_col_signed(r: isize, c: isize) -> Result<Self, SquareError> {
|
||||||
from_row_col_generic!(isize, r, c)
|
from_row_col_generic!(isize, r, c)
|
||||||
}
|
}
|
||||||
fn to_row_col(self) -> (usize, usize) {
|
pub fn to_row_col(self) -> (usize, usize) {
|
||||||
//! Get row, column from index
|
//! Get row, column from index
|
||||||
let div = usize::from(self.0) / BOARD_WIDTH;
|
let div = usize::from(self.0) / BOARD_WIDTH;
|
||||||
let rem = usize::from(self.0) % BOARD_WIDTH;
|
let rem = usize::from(self.0) % BOARD_WIDTH;
|
||||||
@ -220,19 +220,26 @@ impl Square {
|
|||||||
debug_assert!(rem <= 7);
|
debug_assert!(rem <= 7);
|
||||||
(div, rem)
|
(div, rem)
|
||||||
}
|
}
|
||||||
fn to_row_col_signed(self) -> (isize, isize) {
|
pub fn to_row_col_signed(self) -> (isize, isize) {
|
||||||
//! Get row, column (signed) from index
|
//! Get row, column (signed) from index
|
||||||
let (r, c) = self.to_row_col();
|
let (r, c) = self.to_row_col();
|
||||||
(r.try_into().unwrap(), c.try_into().unwrap())
|
(r.try_into().unwrap(), c.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Vertically mirror a square.
|
/// Vertically mirror a square.
|
||||||
fn mirror_vert(&self) -> Self {
|
pub fn mirror_vert(&self) -> Self {
|
||||||
let (r, c) = self.to_row_col();
|
let (r, c) = self.to_row_col();
|
||||||
let (nr, nc) = (BOARD_HEIGHT - 1 - r, c);
|
let (nr, nc) = (BOARD_HEIGHT - 1 - r, c);
|
||||||
Square::from_row_col(nr, nc)
|
Square::from_row_col(nr, nc)
|
||||||
.unwrap_or_else(|e| panic!("mirrored square should be valid: nr {nr} nc {nc}: {e:?}"))
|
.unwrap_or_else(|e| panic!("mirrored square should be valid: nr {nr} nc {nc}: {e:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Manhattan (grid-based) distance with another Square.
|
||||||
|
pub fn manhattan(&self, other: Self) -> usize {
|
||||||
|
let (r1, c1) = self.to_row_col();
|
||||||
|
let (r2, c2) = other.to_row_col();
|
||||||
|
r1.abs_diff(r2) + c1.abs_diff(c2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Square {
|
impl Display for Square {
|
||||||
@ -761,4 +768,26 @@ mod tests {
|
|||||||
assert_eq!(tc.flip_colors(), expect);
|
assert_eq!(tc.flip_colors(), expect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn manhattan_distance() {
|
||||||
|
let test_cases = [
|
||||||
|
("a3", "a3", 0),
|
||||||
|
("a3", "a4", 1),
|
||||||
|
("a3", "b3", 1),
|
||||||
|
("a3", "b4", 2),
|
||||||
|
("a1", "b8", 8),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (sq_str1, sq_str2, expected) in test_cases {
|
||||||
|
let sq1 = Square::from_str(sq_str1).unwrap();
|
||||||
|
let sq2 = Square::from_str(sq_str2).unwrap();
|
||||||
|
let res = sq1.manhattan(sq2);
|
||||||
|
assert_eq!(
|
||||||
|
res, expected,
|
||||||
|
"failed {sq_str1} and {sq_str2}: got manhattan {}, expected {}",
|
||||||
|
res, expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ use crate::{
|
|||||||
|
|
||||||
/// Piece enum specifically for promotions.
|
/// Piece enum specifically for promotions.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
enum PromotePiece {
|
pub enum PromotePiece {
|
||||||
Rook,
|
Rook,
|
||||||
Bishop,
|
Bishop,
|
||||||
Knight,
|
Knight,
|
||||||
@ -45,7 +45,7 @@ impl From<PromotePiece> for char {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NonPromotePiece;
|
pub struct NonPromotePiece;
|
||||||
|
|
||||||
impl TryFrom<Piece> for PromotePiece {
|
impl TryFrom<Piece> for PromotePiece {
|
||||||
type Error = NonPromotePiece;
|
type Error = NonPromotePiece;
|
||||||
@ -84,7 +84,7 @@ pub struct AntiMove {
|
|||||||
dest: Square,
|
dest: Square,
|
||||||
src: Square,
|
src: Square,
|
||||||
/// Captured piece, always assumed to be of enemy color.
|
/// Captured piece, always assumed to be of enemy color.
|
||||||
cap: Option<Piece>,
|
pub (crate) cap: Option<Piece>,
|
||||||
move_type: AntiMoveType,
|
move_type: AntiMoveType,
|
||||||
/// Half-move counter prior to this move
|
/// Half-move counter prior to this move
|
||||||
half_moves: usize,
|
half_moves: usize,
|
||||||
@ -150,7 +150,7 @@ impl AntiMove {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
enum MoveType {
|
pub enum MoveType {
|
||||||
/// Pawn promotes to another piece.
|
/// Pawn promotes to another piece.
|
||||||
Promotion(PromotePiece),
|
Promotion(PromotePiece),
|
||||||
/// Capture, or push move. Includes castling and en-passant too.
|
/// Capture, or push move. Includes castling and en-passant too.
|
||||||
@ -161,9 +161,9 @@ enum MoveType {
|
|||||||
/// No checking is done when constructing this.
|
/// No checking is done when constructing this.
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
pub struct Move {
|
pub struct Move {
|
||||||
src: Square,
|
pub src: Square,
|
||||||
dest: Square,
|
pub dest: Square,
|
||||||
move_type: MoveType,
|
pub move_type: MoveType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Move {
|
impl Move {
|
||||||
|
107
src/search.rs
107
src/search.rs
@ -14,8 +14,8 @@ Copyright © 2024 dogeystamp <dogeystamp@disroot.org>
|
|||||||
//! Game-tree search.
|
//! Game-tree search.
|
||||||
|
|
||||||
use crate::eval::{Eval, EvalInt};
|
use crate::eval::{Eval, EvalInt};
|
||||||
use crate::movegen::{Move, MoveGen};
|
use crate::movegen::{Move, MoveGen, ToUCIAlgebraic};
|
||||||
use crate::Board;
|
use crate::{Board, Piece};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
// min can't be represented as positive
|
// min can't be represented as positive
|
||||||
@ -95,8 +95,10 @@ impl PartialOrd for SearchEval {
|
|||||||
pub struct SearchConfig {
|
pub struct SearchConfig {
|
||||||
/// Enable alpha-beta pruning.
|
/// Enable alpha-beta pruning.
|
||||||
alpha_beta_on: bool,
|
alpha_beta_on: bool,
|
||||||
/// Limit search depth (will probably change as quiescence search is implemented)
|
/// Limit regular search depth
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
/// Limit quiescence search depth (extra depth on top of regular depth)
|
||||||
|
quiesce_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SearchConfig {
|
impl Default for SearchConfig {
|
||||||
@ -104,10 +106,88 @@ impl Default for SearchConfig {
|
|||||||
SearchConfig {
|
SearchConfig {
|
||||||
alpha_beta_on: true,
|
alpha_beta_on: true,
|
||||||
depth: 5,
|
depth: 5,
|
||||||
|
quiesce_depth: 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If a move is a capture, return which piece is capturing what.
|
||||||
|
fn move_get_capture(board: &mut Board, mv: &Move) -> Option<(Piece, Piece)> {
|
||||||
|
// TODO: en passant
|
||||||
|
board
|
||||||
|
.get_piece(mv.dest)
|
||||||
|
.map(|cap_pc| (board.get_piece(mv.src).unwrap().into(), cap_pc.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Least valuable victim, most valuable attacker heuristic for captures.
|
||||||
|
fn lvv_mva_eval(src_pc: Piece, cap_pc: Piece) -> EvalInt {
|
||||||
|
let pc_values = [500, 300, 300, 20000, 900, 100];
|
||||||
|
pc_values[cap_pc as usize] - pc_values[src_pc as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assign a priority to a move based on how promising it is.
|
||||||
|
fn move_priority(board: &mut Board, mv: &Move) -> EvalInt {
|
||||||
|
// move eval
|
||||||
|
let mut eval: EvalInt = 0;
|
||||||
|
if let Some((src_pc, cap_pc)) = move_get_capture(board, mv) {
|
||||||
|
// least valuable victim, most valuable attacker
|
||||||
|
eval += lvv_mva_eval(src_pc, cap_pc)
|
||||||
|
}
|
||||||
|
|
||||||
|
eval
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search past the "horizon" caused by limiting the minmax depth.
|
||||||
|
///
|
||||||
|
/// We'll only search captures.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Absolute (good for current side is positive) evaluation of the position.
|
||||||
|
fn quiesce(
|
||||||
|
board: &mut Board,
|
||||||
|
config: &SearchConfig,
|
||||||
|
depth: usize,
|
||||||
|
mut alpha: EvalInt,
|
||||||
|
beta: EvalInt,
|
||||||
|
) -> EvalInt {
|
||||||
|
if depth == 0 {
|
||||||
|
let eval = board.eval();
|
||||||
|
return eval * EvalInt::from(board.turn.sign());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut abs_best = None;
|
||||||
|
|
||||||
|
// sort moves by decreasing priority
|
||||||
|
let mut mvs: Vec<_> = board
|
||||||
|
.gen_moves()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|mv| (move_priority(board, &mv), mv))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
mvs.sort_unstable_by_key(|mv| -mv.0);
|
||||||
|
for (_priority, mv) in mvs {
|
||||||
|
if move_get_capture(board, &mv).is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let anti_mv = mv.make(board);
|
||||||
|
let abs_score = -quiesce(board, config, depth - 1, -beta, -alpha);
|
||||||
|
anti_mv.unmake(board);
|
||||||
|
if let Some(abs_best_score) = abs_best {
|
||||||
|
abs_best = Some(max(abs_best_score, abs_score));
|
||||||
|
} else {
|
||||||
|
abs_best = Some(abs_score);
|
||||||
|
}
|
||||||
|
alpha = max(alpha, abs_best.unwrap());
|
||||||
|
if alpha >= beta && config.alpha_beta_on {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abs_best.unwrap_or(board.eval() * EvalInt::from(board.turn.sign()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Search the game tree to find the absolute (positive good) move and corresponding eval for the
|
/// Search the game tree to find the absolute (positive good) move and corresponding eval for the
|
||||||
/// current player.
|
/// current player.
|
||||||
///
|
///
|
||||||
@ -134,14 +214,19 @@ fn minmax(
|
|||||||
let beta = beta.unwrap_or(EVAL_BEST);
|
let beta = beta.unwrap_or(EVAL_BEST);
|
||||||
|
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
let eval = board.eval();
|
let eval = quiesce(board, config, config.quiesce_depth, alpha, beta);
|
||||||
return (
|
return (Vec::new(), SearchEval::Centipawns(eval));
|
||||||
Vec::new(),
|
|
||||||
SearchEval::Centipawns(eval * EvalInt::from(board.turn.sign())),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mvs: Vec<_> = board.gen_moves().into_iter().collect();
|
// sort moves by decreasing priority
|
||||||
|
let mut mvs: Vec<_> = board
|
||||||
|
.gen_moves()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|mv| (move_priority(board, &mv), mv))
|
||||||
|
.collect();
|
||||||
|
mvs.sort_unstable_by_key(|mv| -mv.0);
|
||||||
|
|
||||||
let mut abs_best = SearchEval::Centipawns(EVAL_WORST);
|
let mut abs_best = SearchEval::Centipawns(EVAL_WORST);
|
||||||
let mut best_move: Option<Move> = None;
|
let mut best_move: Option<Move> = None;
|
||||||
@ -156,7 +241,7 @@ fn minmax(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for mv in mvs {
|
for (_priority, mv) in mvs {
|
||||||
let anti_mv = mv.make(board);
|
let anti_mv = mv.make(board);
|
||||||
let (continuation, score) = minmax(board, config, depth - 1, Some(-beta), Some(-alpha));
|
let (continuation, score) = minmax(board, config, depth - 1, Some(-beta), Some(-alpha));
|
||||||
let abs_score = score.increment();
|
let abs_score = score.increment();
|
||||||
@ -219,6 +304,7 @@ mod tests {
|
|||||||
Some(SearchConfig {
|
Some(SearchConfig {
|
||||||
alpha_beta_on: false,
|
alpha_beta_on: false,
|
||||||
depth: 3,
|
depth: 3,
|
||||||
|
quiesce_depth: Default::default(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -230,6 +316,7 @@ mod tests {
|
|||||||
Some(SearchConfig {
|
Some(SearchConfig {
|
||||||
alpha_beta_on: true,
|
alpha_beta_on: true,
|
||||||
depth: 3,
|
depth: 3,
|
||||||
|
quiesce_depth: Default::default(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user