use crate::{ board::game::Game, evaluation::{MATE_SCORE, MIN_SCORE}, movegen::r#move::Move, }; use super::{move_ordering::score_by_mvv_lva, quiescence::quiescence}; pub fn negamax( game: &mut Game, mut alpha: i32, beta: i32, depth: u8, plies: u8, ) -> (Option, i32) { let color = game.current_player(); if depth == 0 { return (None, quiescence(game, alpha, beta).1); } let (mut best_move, mut best_score, mate_score) = (None, MIN_SCORE, -MATE_SCORE + plies as i32); let mut legal_moves = 0; let mut pseudo_legal_moves = game.board.pseudo_moves_all(); pseudo_legal_moves.sort_unstable_by_key(|mv| score_by_mvv_lva(&game, *mv)); for mv in pseudo_legal_moves { game.make_move(&mv); if game.board.king_under_check(color) { game.unmake_move(); continue; } legal_moves += 1; let move_score = -negamax(game, -beta, -alpha, depth - 1, plies + 1).1; game.unmake_move(); if move_score > best_score { best_score = move_score; best_move = Some(mv); } if move_score >= beta { return (best_move, beta); } alpha = alpha.max(move_score); } if legal_moves == 0 { if game.board.king_under_check(color) { return (None, mate_score); } return (None, 0); } (best_move, best_score) } #[cfg(test)] mod tests { use crate::board::{fen::from_fen, square::Square}; use crate::evaluation::{MAX_SCORE, MIN_SCORE}; use crate::movegen::attack_generator::init_attacks; use crate::movegen::r#move::Move; use crate::search::negamax::negamax; const FEN_MATE_IN_1: [&str; 2] = [ "8/8/8/8/8/4q1k1/8/5K2 b - - 0 1", "8/8/8/8/8/4Q1K1/8/5k2 w - - 0 1", ]; #[test] fn test_negamax() -> Result<(), String> { init_attacks(); let mut game = from_fen(FEN_MATE_IN_1[0])?; let e3f2 = Move::new(Square::E3, Square::F2); let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0).0.unwrap(); assert_eq!(e3f2, anointed_move); // let mut game = from_fen(FEN_MATE_IN_1[1])?; // let e3f2 = Move::new(Square::E3, Square::F2); // let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0).0.unwrap(); // assert_eq!(e3f2, anointed_move); Ok(()) } }