feat: basic basic search
This commit is contained in:
parent
5751215ffa
commit
7d0d81905e
@ -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, MoveGen, MoveGenType, ToUCIAlgebraic};
|
use chess_inator::movegen::{FromUCIAlgebraic, Move, MoveGen, MoveGenType, ToUCIAlgebraic};
|
||||||
|
use chess_inator::search::best_move;
|
||||||
use chess_inator::Board;
|
use chess_inator::Board;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
@ -87,8 +88,7 @@ fn cmd_position(mut tokens: std::str::SplitWhitespace<'_>) -> Board {
|
|||||||
|
|
||||||
/// Play the game.
|
/// Play the game.
|
||||||
fn cmd_go(mut _tokens: std::str::SplitWhitespace<'_>, board: &mut Board) {
|
fn cmd_go(mut _tokens: std::str::SplitWhitespace<'_>, board: &mut Board) {
|
||||||
let mvs: Vec<_> = board.gen_moves(MoveGenType::Legal).into_iter().collect();
|
let chosen = best_move(board);
|
||||||
let chosen = mvs.first();
|
|
||||||
match chosen {
|
match chosen {
|
||||||
Some(mv) => println!("bestmove {}", mv.to_uci_algebraic()),
|
Some(mv) => println!("bestmove {}", mv.to_uci_algebraic()),
|
||||||
None => println!("bestmove 0000"),
|
None => println!("bestmove 0000"),
|
||||||
|
@ -18,7 +18,7 @@ use crate::{Board, Color, N_PIECES};
|
|||||||
/// Signed centipawn type.
|
/// Signed centipawn type.
|
||||||
///
|
///
|
||||||
/// Positive is good for White, negative good for Black.
|
/// Positive is good for White, negative good for Black.
|
||||||
type EvalInt = i16;
|
pub type EvalInt = i16;
|
||||||
|
|
||||||
pub trait Eval {
|
pub trait Eval {
|
||||||
/// Evaluate a position and assign it a score.
|
/// Evaluate a position and assign it a score.
|
||||||
|
@ -19,6 +19,7 @@ use std::str::FromStr;
|
|||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod fen;
|
pub mod fen;
|
||||||
pub mod movegen;
|
pub mod movegen;
|
||||||
|
pub mod search;
|
||||||
|
|
||||||
use crate::fen::{FromFen, ToFen, START_POSITION};
|
use crate::fen::{FromFen, ToFen, START_POSITION};
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ pub struct AntiMove {
|
|||||||
|
|
||||||
impl AntiMove {
|
impl AntiMove {
|
||||||
/// Undo the move.
|
/// Undo the move.
|
||||||
fn unmake(self, pos: &mut Board) {
|
pub fn unmake(self, pos: &mut Board) {
|
||||||
pos.move_piece(self.dest, self.src);
|
pos.move_piece(self.dest, self.src);
|
||||||
pos.half_moves = self.half_moves;
|
pos.half_moves = self.half_moves;
|
||||||
pos.castle = self.castle;
|
pos.castle = self.castle;
|
||||||
|
69
src/search.rs
Normal file
69
src/search.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
This file is part of chess_inator.
|
||||||
|
|
||||||
|
chess_inator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
chess_inator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with chess_inator. If not, see https://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
Copyright © 2024 dogeystamp <dogeystamp@disroot.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! Game-tree search.
|
||||||
|
|
||||||
|
use crate::eval::{Eval, EvalInt};
|
||||||
|
use crate::movegen::{Move, MoveGen, MoveGenType};
|
||||||
|
use crate::Board;
|
||||||
|
use std::cmp::max;
|
||||||
|
|
||||||
|
/// Search the game tree to find the absolute (positive good) eval for the current player.
|
||||||
|
fn minmax(board: &mut Board, depth: usize) -> EvalInt {
|
||||||
|
if depth == 0 {
|
||||||
|
let eval = board.eval();
|
||||||
|
match board.turn {
|
||||||
|
crate::Color::White => return eval,
|
||||||
|
crate::Color::Black => return -eval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mvs: Vec<_> = board.gen_moves(MoveGenType::Legal).into_iter().collect();
|
||||||
|
|
||||||
|
let mut abs_best = EvalInt::MIN;
|
||||||
|
|
||||||
|
for mv in mvs {
|
||||||
|
let anti_mv = mv.make(board);
|
||||||
|
abs_best = max(abs_best, -minmax(board, depth - 1));
|
||||||
|
anti_mv.unmake(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_best
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the best move for a position (internal interface).
|
||||||
|
fn search(board: &mut Board) -> Option<Move> {
|
||||||
|
const DEPTH: usize = 4;
|
||||||
|
let mvs: Vec<_> = board.gen_moves(MoveGenType::Legal).into_iter().collect();
|
||||||
|
|
||||||
|
// absolute eval value
|
||||||
|
let mut best_eval = EvalInt::MIN;
|
||||||
|
let mut best_mv: Option<Move> = None;
|
||||||
|
|
||||||
|
for mv in mvs {
|
||||||
|
let anti_mv = mv.make(board);
|
||||||
|
let abs_eval = -minmax(board, DEPTH);
|
||||||
|
if abs_eval > best_eval {
|
||||||
|
best_eval = abs_eval;
|
||||||
|
best_mv = Some(mv);
|
||||||
|
}
|
||||||
|
anti_mv.unmake(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
best_mv
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the best move.
|
||||||
|
pub fn best_move(board: &mut Board) -> Option<Move> {
|
||||||
|
search(board)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user