use rand::prelude::*; use std::f64::{INFINITY, NEG_INFINITY}; use chess::{Board, BoardStatus, ChessMove, Color, MoveGen, Piece}; fn static_board_eval( mov: ChessMove, board: Board, playing_as: Color, move_history: Vec<(ChessMove, Board)>, ) -> f64 { if move_history.len() > 6 { if move_history[move_history.len() - 1].0 == mov && move_history[move_history.len() - 2].0 == mov { return -666.0; } } let mut current_eval = 0.0; // basic material counting board.color_combined(playing_as).for_each(|square| { match board.piece_on(square) { Some(Piece::Pawn) => current_eval += 1.0, Some(Piece::Knight) => current_eval += 3.0, Some(Piece::Bishop) => current_eval += 5.0, Some(Piece::Rook) => current_eval += 5.0, Some(Piece::Queen) => current_eval += 8.0, _ => { /* do nothing */ } } }); board.color_combined(!playing_as).for_each(|square| { match board.piece_on(square) { Some(Piece::Pawn) => current_eval -= 1.0, Some(Piece::Knight) => current_eval -= 3.0, Some(Piece::Bishop) => current_eval -= 5.0, Some(Piece::Rook) => current_eval -= 5.0, Some(Piece::Queen) => current_eval -= 8.0, _ => { /* do nothing */ } } }); // checking heuristic let mut are_we_in_check = false; let mut is_opponent_in_check = false; board .checkers() .filter(|sq| board.color_on(*sq).unwrap() == playing_as) .for_each(|_| { if !are_we_in_check { current_eval -= 66.6; are_we_in_check = true; } else { current_eval -= 12.5; } }); board .checkers() .filter(|sq| board.color_on(*sq).unwrap() == !playing_as) .for_each(|_| { if !is_opponent_in_check && move_history.len() > *vec![10, 11, 12, 13, 14, 15] .choose(&mut rand::thread_rng()) .unwrap() as usize { current_eval += 17.5; is_opponent_in_check = true; } else { current_eval += 3.5; } }); // deprioritize king movement if board.piece_on(mov.get_source()).unwrap_or(Piece::Pawn) == Piece::King && !are_we_in_check { current_eval -= 12.5; } current_eval } pub fn eval_board( board: Board, is_maximizing_player: bool, depth: u8, move_history: Vec<(ChessMove, Board)>, last_move: Option, original_playing_as: Color, mut alpha: f64, mut beta: f64, ) -> f64 { let moves_iter = MoveGen::new_legal(&board).into_iter(); if depth == 0 || board.status() == BoardStatus::Checkmate { return static_board_eval( last_move.unwrap_or(move_history.last().unwrap().0), board, original_playing_as, move_history, ); } else { if is_maximizing_player { let mut max_eval = NEG_INFINITY; for mov in moves_iter { let mut new_move_history = move_history.clone(); let new_board = board.make_move_new(mov); new_move_history.push((mov, new_board)); let eval = eval_board( new_board, false, depth - 1, new_move_history, Some(mov), original_playing_as, alpha, beta, ); max_eval = max_eval.max(eval); alpha = alpha.max(eval); if beta <= alpha { break; } } return max_eval; } else { let mut min_eval = INFINITY; for mov in moves_iter { let mut new_move_history = move_history.clone(); let new_board = board.make_move_new(mov); new_move_history.push((mov, new_board)); let eval = eval_board( new_board, true, depth - 1, new_move_history, Some(mov), original_playing_as, alpha, beta, ); min_eval = min_eval.min(eval); beta = beta.min(eval); if beta <= alpha { break; } } return min_eval; } } }