From 9d527634ebda485f5d617383aa2b72c12c39e5cb Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 14 Nov 2025 23:32:52 +0100 Subject: [PATCH] most basic best move search --- src/eval/basic.rs | 18 ++++++++++++++ src/eval/mod.rs | 1 + src/lib.rs | 6 +++-- src/main.rs | 11 +++++++-- src/move.rs | 17 +++++++++++++ src/search/minimax.rs | 55 +++++++++++++++++++++++++++++++++++++++++++ src/search/mod.rs | 1 + 7 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 src/eval/basic.rs create mode 100644 src/eval/mod.rs create mode 100644 src/search/minimax.rs create mode 100644 src/search/mod.rs diff --git a/src/eval/basic.rs b/src/eval/basic.rs new file mode 100644 index 0000000..44b25ea --- /dev/null +++ b/src/eval/basic.rs @@ -0,0 +1,18 @@ +use crate::board::{Board, Color, PieceType}; + +pub fn evaluate_board(board: &Board) -> i32 { + let mut score = 0_i32; + score += board.pieces[PieceType::Pawn as usize][Color::White as usize].count_ones() as i32 * 100; + score += board.pieces[PieceType::Knight as usize][Color::White as usize].count_ones() as i32 * 300; + score += board.pieces[PieceType::Bishop as usize][Color::White as usize].count_ones() as i32 * 300; + score += board.pieces[PieceType::Rook as usize][Color::White as usize].count_ones() as i32 * 500; + score += board.pieces[PieceType::Queen as usize][Color::White as usize].count_ones() as i32 * 900; + score += board.pieces[PieceType::King as usize][Color::White as usize].count_ones() as i32 * 10000; + score -= board.pieces[PieceType::Pawn as usize][Color::Black as usize].count_ones() as i32 * 100; + score -= board.pieces[PieceType::Knight as usize][Color::Black as usize].count_ones() as i32 * 300; + score -= board.pieces[PieceType::Bishop as usize][Color::Black as usize].count_ones() as i32 * 300; + score -= board.pieces[PieceType::Rook as usize][Color::Black as usize].count_ones() as i32 * 500; + score -= board.pieces[PieceType::Queen as usize][Color::Black as usize].count_ones() as i32 * 900; + score -= board.pieces[PieceType::King as usize][Color::Black as usize].count_ones() as i32 * 10000; + score +} \ No newline at end of file diff --git a/src/eval/mod.rs b/src/eval/mod.rs new file mode 100644 index 0000000..6ef0f7c --- /dev/null +++ b/src/eval/mod.rs @@ -0,0 +1 @@ +pub mod basic; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ef7ca28..4dfce6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,5 +2,7 @@ pub mod board; pub mod r#move; pub mod square; pub mod movegen; -mod display; -mod parsing; \ No newline at end of file +pub mod eval; +pub mod display; +pub mod parsing; +pub mod search; diff --git a/src/main.rs b/src/main.rs index e4ecfb3..6a74aa2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,15 @@ use chess_engine::board::Board; use chess_engine::movegen::generate_pseudo_legal_moves; use chess_engine::movegen::legal_check::is_king_attacked; use chess_engine::r#move::*; - +use chess_engine::search::minimax; +use chess_engine::search::minimax::minimax; fn main() { - + let mut board = Board::from_fen("rnb1kbnr/pppppppp/8/8/8/4q3/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); + let (opt_move, _) = minimax(&mut board, 5); + if let Some(mv) = opt_move { + println!("Found best move: {}", mv) + } else { + println!("No moves found") + } } diff --git a/src/move.rs b/src/move.rs index e1801d8..ed72ac5 100644 --- a/src/move.rs +++ b/src/move.rs @@ -1,4 +1,5 @@ use std::slice; +use std::ops::{Index, IndexMut}; use crate::square::Square; use crate::board::PieceType; use crate::square::SQUARES; @@ -103,6 +104,22 @@ impl MoveList { } } +impl Index for MoveList { + type Output = Move; + + #[inline(always)] + fn index(&self, index: usize) -> &Self::Output { + &self.moves[..self.count][index] + } +} + +impl IndexMut for MoveList { + #[inline(always)] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.moves[..self.count][index] + } +} + pub struct UndoMove { pub mv: Move, pub captured_piece: Option, diff --git a/src/search/minimax.rs b/src/search/minimax.rs new file mode 100644 index 0000000..c8854e2 --- /dev/null +++ b/src/search/minimax.rs @@ -0,0 +1,55 @@ +use crate::board::{Board, Color}; // <-- Assuming you have a Color enum (e.g., Color::White, Color::Black) +use crate::eval::basic::evaluate_board; +use crate::movegen::generate_pseudo_legal_moves; +use crate::movegen::legal_check::is_king_attacked; +use crate::r#move::{Move, MoveList}; + + +fn evaluate_board_relative(board: &Board) -> i32 { + let static_eval = evaluate_board(board); + match board.side_to_move { + Color::White => static_eval, + Color::Black => -static_eval, + } +} + +pub fn minimax(board: &mut Board, depth: u8) -> (Option, i32) { + if depth == 0 { + return (None, evaluate_board_relative(board)); + } + + let mut list = MoveList::new(); + generate_pseudo_legal_moves(board, &mut list); + let mut best_move: Option = None; + let mut best_score: i32 = -i32::MAX; + let mut legal_moves_found = false; + + for mv in list.iter() { + let undo_mv = board.make_move(*mv); + let is_illegal = is_king_attacked(board); + if is_illegal { + board.undo_move(undo_mv); + continue; + } + legal_moves_found = true; + let (_, score) = minimax(board, depth - 1); + let current_score = -score; + + if current_score > best_score { + best_score = current_score; + best_move = Some(*mv); + } + + board.undo_move(undo_mv); + } + + if !legal_moves_found { + if is_king_attacked(board) { + return (None, -i32::MAX); + } else { + return (None, 0); + } + } + + (best_move, best_score) +} \ No newline at end of file diff --git a/src/search/mod.rs b/src/search/mod.rs new file mode 100644 index 0000000..4ccb1d9 --- /dev/null +++ b/src/search/mod.rs @@ -0,0 +1 @@ +pub mod minimax; \ No newline at end of file