|
|
|
@ -1,6 +1,9 @@
|
|
|
|
|
use std::f64::{INFINITY, NEG_INFINITY};
|
|
|
|
|
|
|
|
|
|
use chess::{Board, BoardStatus, ChessMove, Color, File, MoveGen, Piece, Rank, Square};
|
|
|
|
|
use chess::{
|
|
|
|
|
Board as ChessBoard, BoardStatus, ChessMove, Color, File, MoveGen, Piece, Rank, Square,
|
|
|
|
|
};
|
|
|
|
|
use itertools::Itertools;
|
|
|
|
|
|
|
|
|
|
use crate::piece_tables;
|
|
|
|
|
use crate::piece_values::get_piece_value;
|
|
|
|
@ -53,7 +56,7 @@ fn square_to_index(square: Square, playing_as: Color) -> usize {
|
|
|
|
|
idx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn evaluate_piece_square(board: Board, square: Square, playing_as: Color) -> (f64, f64, f64) {
|
|
|
|
|
fn evaluate_piece_square(board: ChessBoard, square: Square, playing_as: Color) -> (f64, f64, f64) {
|
|
|
|
|
let mut mg_value: f64 = 0.0;
|
|
|
|
|
let mut eg_value: f64 = 0.0;
|
|
|
|
|
let mut gamephase: f64 = 0.0;
|
|
|
|
@ -106,21 +109,7 @@ fn evaluate_piece_square(board: Board, square: Square, playing_as: Color) -> (f6
|
|
|
|
|
(mg_value, eg_value, gamephase)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn static_board_eval(
|
|
|
|
|
mov: ChessMove,
|
|
|
|
|
old_board: Board,
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn static_board_eval(board: ChessBoard, playing_as: Color, move_history: Vec<ChessBoard>) -> f64 {
|
|
|
|
|
// basic material counting + midgame or endgame determination
|
|
|
|
|
let mut our_mg_value: f64 = 0.0;
|
|
|
|
|
let mut our_eg_value: f64 = 0.0;
|
|
|
|
@ -167,14 +156,14 @@ fn static_board_eval(
|
|
|
|
|
.filter(|sq| board.color_on(*sq).unwrap() == !playing_as)
|
|
|
|
|
.for_each(|_| {
|
|
|
|
|
if !is_opponent_in_check && move_history.len() > 10 {
|
|
|
|
|
current_eval += 17.5;
|
|
|
|
|
current_eval += 5.0;
|
|
|
|
|
is_opponent_in_check = true;
|
|
|
|
|
} else {
|
|
|
|
|
current_eval += 3.5;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// deprioritize king movement
|
|
|
|
|
/* // deprioritize king movement
|
|
|
|
|
if board.piece_on(mov.get_source()).unwrap_or(Piece::Pawn) == Piece::King && !are_we_in_check {
|
|
|
|
|
current_eval -= 12.5;
|
|
|
|
|
}
|
|
|
|
@ -187,87 +176,63 @@ fn static_board_eval(
|
|
|
|
|
) {
|
|
|
|
|
current_eval +=
|
|
|
|
|
(get_piece_value(Some(captured_piece)) - get_piece_value(Some(capturing_piece)))
|
|
|
|
|
}
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
current_eval
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn eval_board(
|
|
|
|
|
board: Board,
|
|
|
|
|
last_board: Board,
|
|
|
|
|
is_maximizing_player: bool,
|
|
|
|
|
depth: u8,
|
|
|
|
|
move_history: Vec<(ChessMove, Board)>,
|
|
|
|
|
original_playing_as: Color,
|
|
|
|
|
mut alpha: f64,
|
|
|
|
|
mut beta: f64,
|
|
|
|
|
) -> f64 {
|
|
|
|
|
let last_move = if move_history.len() > 0 {
|
|
|
|
|
Some(move_history.last().unwrap().0)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if depth == 0 || board.status() == BoardStatus::Checkmate {
|
|
|
|
|
return static_board_eval(
|
|
|
|
|
last_move.unwrap(),
|
|
|
|
|
last_board,
|
|
|
|
|
board,
|
|
|
|
|
!board.side_to_move(),
|
|
|
|
|
move_history,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
let moves_iter = MoveGen::new_legal(&board).into_iter();
|
|
|
|
|
if is_maximizing_player {
|
|
|
|
|
let mut max_eval = NEG_INFINITY;
|
|
|
|
|
for mov in moves_iter {
|
|
|
|
|
let mut new_move_history = move_history.clone();
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Board {
|
|
|
|
|
pub current_board: ChessBoard,
|
|
|
|
|
pub board_history: Vec<ChessBoard>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let new_board = board.make_move_new(mov);
|
|
|
|
|
new_move_history.push((mov, new_board));
|
|
|
|
|
pub struct Game;
|
|
|
|
|
|
|
|
|
|
let eval = eval_board(
|
|
|
|
|
new_board,
|
|
|
|
|
board,
|
|
|
|
|
false,
|
|
|
|
|
depth - 1,
|
|
|
|
|
new_move_history,
|
|
|
|
|
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();
|
|
|
|
|
impl minimax::Game for Game {
|
|
|
|
|
type S = Board;
|
|
|
|
|
type M = ChessMove;
|
|
|
|
|
|
|
|
|
|
let new_board = board.make_move_new(mov);
|
|
|
|
|
new_move_history.push((mov, new_board));
|
|
|
|
|
fn generate_moves(state: &Self::S, moves: &mut Vec<Self::M>) {
|
|
|
|
|
let iter = MoveGen::new_legal(&state.current_board);
|
|
|
|
|
for mov in iter {
|
|
|
|
|
moves.push(mov)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let eval = eval_board(
|
|
|
|
|
new_board,
|
|
|
|
|
board,
|
|
|
|
|
true,
|
|
|
|
|
depth - 1,
|
|
|
|
|
new_move_history,
|
|
|
|
|
original_playing_as,
|
|
|
|
|
alpha,
|
|
|
|
|
beta,
|
|
|
|
|
);
|
|
|
|
|
min_eval = min_eval.min(eval);
|
|
|
|
|
beta = beta.min(eval);
|
|
|
|
|
if beta <= alpha {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return min_eval;
|
|
|
|
|
fn get_winner(b: &Board) -> Option<minimax::Winner> {
|
|
|
|
|
match b.current_board.status() {
|
|
|
|
|
BoardStatus::Ongoing => None,
|
|
|
|
|
BoardStatus::Stalemate => Some(minimax::Winner::Draw),
|
|
|
|
|
BoardStatus::Checkmate => Some(minimax::Winner::PlayerJustMoved),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply(b: &mut Board, m: ChessMove) -> Option<Board> {
|
|
|
|
|
let mut new_board_history = b.board_history.clone();
|
|
|
|
|
new_board_history.push(b.current_board);
|
|
|
|
|
Some(Board {
|
|
|
|
|
current_board: b.current_board.make_move_new(m),
|
|
|
|
|
board_history: new_board_history,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn zobrist_hash(b: &Board) -> u64 {
|
|
|
|
|
b.current_board.get_hash()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct Evaluator;
|
|
|
|
|
|
|
|
|
|
impl minimax::Evaluator for Evaluator {
|
|
|
|
|
type G = Game;
|
|
|
|
|
|
|
|
|
|
fn evaluate(&self, b: &Board) -> minimax::Evaluation {
|
|
|
|
|
static_board_eval(
|
|
|
|
|
b.current_board,
|
|
|
|
|
b.current_board.side_to_move(),
|
|
|
|
|
b.board_history.clone(),
|
|
|
|
|
) as i16
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|