use crate::engine::evaluate_single_board;
use crate::pgn::{PgnMove, PgnPieceMove};
use crate::types::{Board, Move, PieceMove, Side, Square};
use crate::util::HighestScoreArray;
#[derive(Debug, Clone)]
pub struct ComputedBoard {
inner: Board,
duck_square: Option<Square>,
}
impl ComputedBoard {
pub fn from_board(board: Board) -> Self {
Self {
duck_square: Self::find_duck(&board),
inner: board,
}
}
fn find_duck(board: &Board) -> Option<Square> {
iter_board!(board.board)
.find(|(_square, p)| matches!(p, Some(p) if p.kind.is_duck()))
.map(|(sq, _p)| sq)
}
pub fn into_board(self) -> Board {
self.inner
}
pub fn new() -> Self {
let mut board = Board::new();
board.set_start_position();
Self::from_board(board)
}
pub fn next_move_side(&self) -> Side {
self.inner.next_move
}
pub fn moved_piece(&self) -> bool {
self.inner.moved_piece
}
#[cfg_attr(feature = "flamegraph", inline(never))]
pub fn available_piece_moves(&self, list: &mut Vec<PieceMove>) {
assert!(list.is_empty());
let my_side = self.inner.next_move;
for (i, piece) in self.inner.board.iter().enumerate() {
let Some(piece) = piece else { continue };
if piece.side == my_side {
let square = unsafe { Square::from_u8_unchecked(i as u8) };
self.inner.available_piece_moves(piece.kind, square, list);
}
}
}
pub fn available_duck_squares(&self, list: &mut Vec<Square>) {
assert!(list.is_empty());
self.inner.available_duck_squares(list);
}
pub fn convert_pgn_move(&self, mv: PgnMove) -> Move {
let (piece, from, to, capture) = match mv.piece {
PgnPieceMove::Piece {
piece,
from,
to,
capture,
} => (piece, from, to, capture),
PgnPieceMove::Castle { long } => {
let (fk, tk, fr, tr, y) = match self.inner.next_move {
Side::White if long => (4, 2, 0, 3, 7),
Side::White => (4, 6, 7, 5, 7),
Side::Black if long => (4, 2, 0, 3, 0),
Side::Black => (4, 6, 7, 5, 0),
};
return Move {
piece: PieceMove::Castle {
from_king: Square::from_xy(fk, y),
to_king: Square::from_xy(tk, y),
from_rook: Square::from_xy(fr, y),
to_rook: Square::from_xy(tr, y),
},
duck: mv.duck,
side: self.inner.next_move,
};
}
};
let mut list = vec![];
let my_side = self.inner.next_move;
for (i, p) in self.inner.board.iter().enumerate() {
let Some(p) = p else { continue };
if p.side != my_side || p.kind != piece {
continue;
}
let square = unsafe { Square::from_u8_unchecked(i as u8) };
self.inner.available_piece_moves(piece, square, &mut list);
}
for cand_mv in list {
let (mv_from, mv_to, mv_capture) = match cand_mv {
PieceMove::Piece {
from, to, capture, ..
} => (from, to, capture.is_some()),
PieceMove::EnPassant { from, to } => (from, to, true),
PieceMove::Castle { .. } => continue,
};
if capture != mv_capture {
continue;
}
if let Some(from) = from {
if mv_from != from {
continue;
}
}
if mv_to == to {
return Move {
piece: cand_mv,
duck: mv.duck,
side: self.inner.next_move,
};
}
}
panic!("no move found")
}
pub fn apply_piece_move(&mut self, piece_move: PieceMove) {
self.inner.apply_piece_move(piece_move);
}
pub fn apply_duck_move(&mut self, square: Square) {
self.inner.apply_duck_move(square, self.duck_square);
self.duck_square = Some(square);
}
pub fn evaluate(&self, depth: usize) -> HighestScoreArray<Move, 3> {
self.evaluate_inner(depth, f32::MIN, f32::MAX)
}
fn evaluate_inner(
&self,
depth: usize,
mut alpha: f32,
beta: f32,
) -> HighestScoreArray<Move, 3> {
let mut moves = HighestScoreArray::new();
let next_side = self.inner.next_move;
let mut piece_moves = Vec::with_capacity(128);
self.available_piece_moves(&mut piece_moves);
let mut duck_moves = Vec::with_capacity(128);
for piece_move in piece_moves {
duck_moves.clear();
let mut board = self.clone();
let side = board.inner.next_move;
board.apply_piece_move(piece_move);
board.inner.reasonable_duck_squares(&mut duck_moves);
for square in duck_moves.iter() {
let mut board = board.clone();
board.apply_duck_move(*square);
let score = if depth > 0 {
let next_moves = board.evaluate_inner(
depth - 1,
-1f32 * beta,
-1f32 * alpha,
);
let best_score = next_moves.highest_score().unwrap_or(0f32);
-1f32 * best_score
} else {
evaluate_single_board(&board.inner) * next_side.multi()
};
alpha = alpha.max(score);
moves.insert(
score,
Move {
piece: piece_move,
duck: *square,
side: side,
},
);
if beta <= alpha {
return moves;
}
}
}
moves
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn available_moves() {
let board = ComputedBoard::new();
let mut list = vec![];
board.available_piece_moves(&mut list);
assert_eq!(list.len(), 20);
}
#[test]
fn evaluate() {
let board = ComputedBoard::new();
let moves = board.evaluate(1);
assert_eq!(moves.len(), 396);
}
}