Encode Move into a u16

This commit is contained in:
stefiosif
2025-02-03 20:01:20 +02:00
parent 888b3866b9
commit ea6800183d
8 changed files with 363 additions and 268 deletions

View File

@@ -11,7 +11,7 @@ use crate::movegen::move_generator::{
bishop_pseudo_moves, king_pseudo_moves, knight_pseudo_moves, pawn_pseudo_moves,
queen_pseudo_moves, rook_pseudo_moves,
};
use crate::movegen::r#move::{Move, MoveType, Promote};
use crate::movegen::r#move::{Move, Promote};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Board {
@@ -86,12 +86,7 @@ impl Board {
pub fn pseudo_moves_all_captures(&self) -> Vec<Move> {
self.pseudo_moves_all()
.into_iter()
.filter(|m| {
matches!(
m.move_type,
MoveType::Capture | MoveType::PromotionCapture(_)
)
})
.filter(|m| m.is_capture())
.collect()
}
@@ -168,7 +163,7 @@ impl Board {
self.color[self.state.next_player()] &= !square_to_bitboard(square);
}
pub fn promote_piece(&mut self, square: usize, promote: &Promote) {
pub fn promote_piece(&mut self, square: usize, promote: Promote) {
match promote {
Promote::Knight => self.pieces[PieceType::Knight] |= square_to_bitboard(square),
Promote::Bishop => self.pieces[PieceType::Bishop] |= square_to_bitboard(square),

View File

@@ -64,88 +64,98 @@ impl Game {
let hash = &mut self.hash;
let mailbox = &mut self.mailbox;
let color = board.state.current_player();
let pawn_move = board.is_pawn_move(mv.src);
let pawn_move = board.is_pawn_move(mv.src());
let mut en_passant_square = None;
let ep_capture = match color {
Color::White => mv.dst.saturating_sub(8),
Color::Black => mv.dst + 8,
Color::White => (mv.dst()).saturating_sub(8),
Color::Black => mv.dst() + 8,
};
let old_castling_ability = board.state.castling_ability;
let piece_at_src = mailbox
.piece_at(mv.src)
.unwrap_or_else(|| panic!("Expected piece at: {}", mv.src));
let piece_at_dst = mailbox.piece_at(mv.dst);
match &mv.move_type {
let piece_at_src = mailbox.piece_at(mv.src()).expect("No piece at src.");
let piece_at_dst = mailbox.piece_at(mv.dst());
match MoveType::from_move(mv) {
MoveType::Quiet => {
board.move_piece(mv.src, mv.dst, piece_at_src.0);
hash.update_quiet(mv.src, mv.dst, piece_at_src.0, color);
board.move_piece(mv.src(), mv.dst(), piece_at_src.0);
hash.update_quiet(mv.src(), mv.dst(), piece_at_src.0, color);
hash.drop_en_passant_hash(board.state.en_passant_square());
mailbox.set_piece_at(mv.dst, Some(piece_at_src));
mailbox.set_piece_at(mv.dst(), Some(piece_at_src));
}
MoveType::Capture => {
let piece_at_dst =
piece_at_dst.unwrap_or_else(|| panic!("Expected piece at: {}", mv.dst));
board.remove_opponent_piece(mv.dst, piece_at_dst.0);
hash.update_capture(mv.src, mv.dst, piece_at_src.0, piece_at_dst.0, color);
board.move_piece(mv.src, mv.dst, piece_at_src.0);
let piece_at_dst = piece_at_dst.expect("No piece at dst.");
board.remove_opponent_piece(mv.dst(), piece_at_dst.0);
hash.update_capture(mv.src(), mv.dst(), piece_at_src.0, piece_at_dst.0, color);
board.move_piece(mv.src(), mv.dst(), piece_at_src.0);
hash.drop_en_passant_hash(board.state.en_passant_square());
mailbox.set_piece_at(mv.dst, Some(piece_at_src));
mailbox.set_piece_at(mv.dst(), Some(piece_at_src));
}
MoveType::EnPassant => {
board.move_piece(mv.src, mv.dst, PieceType::Pawn);
board.move_piece(mv.src(), mv.dst(), PieceType::Pawn);
board.remove_opponent_piece(ep_capture, PieceType::Pawn);
hash.update_en_passant(mv.src, mv.dst, ep_capture, color);
hash.update_en_passant(mv.src(), mv.dst(), ep_capture, color);
hash.drop_en_passant_hash(board.state.en_passant_square());
mailbox.set_piece_at(mv.dst, Some((PieceType::Pawn, color)));
mailbox.set_piece_at(mv.dst(), Some((PieceType::Pawn, color)));
mailbox.set_piece_at(ep_capture, None);
}
MoveType::DoublePush => {
board.move_piece(mv.src, mv.dst, piece_at_src.0);
board.move_piece(mv.src(), mv.dst(), piece_at_src.0);
en_passant_square = match color {
Color::White => Some(mv.src + 8),
Color::Black => Some(mv.src.saturating_sub(8)),
Color::White => Some(mv.src() + 8),
Color::Black => Some(mv.src().saturating_sub(8)),
};
hash.update_double_push(mv.src, mv.dst, color, en_passant_square);
hash.update_double_push(mv.src(), mv.dst(), color, en_passant_square);
hash.drop_en_passant_hash(board.state.en_passant_square());
mailbox.set_piece_at(mv.dst, Some(piece_at_src));
mailbox.set_piece_at(mv.dst(), Some(piece_at_src));
}
MoveType::Promotion(promote) => {
board.remove_own_piece(mv.src, piece_at_src.0);
board.promote_piece(mv.dst, promote);
hash.update_promotion(mv.src, mv.dst, promote, color);
MoveType::PromotionKnight
| MoveType::PromotionBishop
| MoveType::PromotionRook
| MoveType::PromotionQueen => {
let promote = mv.promotion_type();
board.remove_own_piece(mv.src(), piece_at_src.0);
board.promote_piece(mv.dst(), promote);
hash.update_promotion(mv.src(), mv.dst(), promote, color);
hash.drop_en_passant_hash(board.state.en_passant_square());
mailbox.set_piece_at(mv.dst, Some((promote.into_piece_type(), color)));
mailbox.set_piece_at(mv.dst(), Some((promote.into_piece_type(), color)));
}
MoveType::PromotionCapture(promote) => {
let piece_at_dst =
piece_at_dst.unwrap_or_else(|| panic!("Expected piece at: {}", mv.dst));
board.remove_own_piece(mv.src, piece_at_src.0);
board.remove_opponent_piece(mv.dst, piece_at_dst.0);
board.promote_piece(mv.dst, promote);
hash.update_promotion_capture(mv.src, mv.dst, piece_at_dst.0, promote, color);
MoveType::PromotionCaptureKnight
| MoveType::PromotionCaptureBishop
| MoveType::PromotionCaptureRook
| MoveType::PromotionCaptureQueen => {
let piece_at_dst = piece_at_dst.expect("No piece at dst.");
let promote = mv.promotion_type();
board.remove_own_piece(mv.src(), piece_at_src.0);
board.remove_opponent_piece(mv.dst(), piece_at_dst.0);
board.promote_piece(mv.dst(), promote);
hash.update_promotion_capture(mv.src(), mv.dst(), piece_at_dst.0, &promote, color);
hash.drop_en_passant_hash(board.state.en_passant_square());
mailbox.set_piece_at(mv.dst, Some((promote.into_piece_type(), color)));
mailbox.set_piece_at(mv.dst(), Some((promote.into_piece_type(), color)));
}
MoveType::Castle => {
board.move_piece(mv.src, mv.dst, piece_at_src.0);
let (rook_src, rook_dst) = match mv.dst {
Square::C1 | Square::C8 => (mv.dst - 2, mv.dst + 1),
Square::G1 | Square::G8 => (mv.dst + 1, mv.dst - 1),
MoveType::KingCastle | MoveType::QueenCastle => {
board.move_piece(mv.src(), mv.dst(), piece_at_src.0);
let (rook_src, rook_dst) = match mv.dst() {
Square::C1 | Square::C8 => (mv.dst() - 2, mv.dst() + 1),
Square::G1 | Square::G8 => (mv.dst() + 1, mv.dst() - 1),
_ => return,
};
board.move_piece(rook_src, rook_dst, PieceType::Rook);
board.state.set_castling_ability(color, Castle::None);
hash.update_castle(mv.src, mv.dst, piece_at_src.0, rook_src, rook_dst, color);
hash.update_castle(
mv.src(),
mv.dst(),
piece_at_src.0,
rook_src,
rook_dst,
color,
);
hash.drop_en_passant_hash(board.state.en_passant_square());
mailbox.set_piece_at(mv.dst, Some(piece_at_src));
mailbox.set_piece_at(mv.dst(), Some(piece_at_src));
mailbox.set_piece_at(rook_src, None);
mailbox.set_piece_at(rook_dst, Some((PieceType::Rook, color)));
}
}
mailbox.set_piece_at(mv.src, None);
mailbox.set_piece_at(mv.src(), None);
board
.state
.update_game_state(mv, color, pawn_move, en_passant_square);
@@ -180,69 +190,75 @@ impl Game {
let mv = move_parameters
.mv
.expect("Expected move parameters from history stack");
let piece_at_dst = mailbox.piece_at(mv.dst).expect("Expected set piece");
match &mv.move_type {
let piece_at_dst = mailbox.piece_at(mv.dst()).expect("No piece at dst.");
match MoveType::from_move(&mv) {
MoveType::Quiet | MoveType::DoublePush => {
board.move_piece(mv.dst, mv.src, piece_at_dst.0);
mailbox.set_piece_at(mv.src, mailbox.piece_at(mv.dst));
mailbox.set_piece_at(mv.dst, None);
board.move_piece(mv.dst(), mv.src(), piece_at_dst.0);
mailbox.set_piece_at(mv.src(), mailbox.piece_at(mv.dst()));
mailbox.set_piece_at(mv.dst(), None);
}
MoveType::Capture => {
let captured_piece = move_parameters
.captured_piece
.expect("Expected captured piece to unmake Capture");
board.move_piece(mv.dst, mv.src, piece_at_dst.0);
board.insert_opponent_piece(mv.dst, captured_piece.0);
mailbox.set_piece_at(mv.src, move_parameters.moving_piece);
mailbox.set_piece_at(mv.dst, Some(captured_piece));
board.move_piece(mv.dst(), mv.src(), piece_at_dst.0);
board.insert_opponent_piece(mv.dst(), captured_piece.0);
mailbox.set_piece_at(mv.src(), move_parameters.moving_piece);
mailbox.set_piece_at(mv.dst(), Some(captured_piece));
}
MoveType::Promotion(_) => {
MoveType::PromotionKnight
| MoveType::PromotionBishop
| MoveType::PromotionRook
| MoveType::PromotionQueen => {
let promoted_piece = move_parameters
.promoted_piece
.expect("Expected promoted piece to unmake Promotion");
board.remove_own_piece(mv.dst, promoted_piece.0);
board.insert_own_piece(mv.src, PieceType::Pawn);
board.remove_own_piece(mv.dst(), promoted_piece.0);
board.insert_own_piece(mv.src(), PieceType::Pawn);
mailbox.set_piece_at(mv.src, Some((PieceType::Pawn, color_before_move)));
mailbox.set_piece_at(mv.dst, None);
mailbox.set_piece_at(mv.src(), Some((PieceType::Pawn, color_before_move)));
mailbox.set_piece_at(mv.dst(), None);
}
MoveType::PromotionCapture(_) => {
MoveType::PromotionCaptureKnight
| MoveType::PromotionCaptureBishop
| MoveType::PromotionCaptureRook
| MoveType::PromotionCaptureQueen => {
let promoted_piece = move_parameters
.promoted_piece
.expect("Expected promoted piece to unmake PromotionCapture");
let captured_piece = move_parameters
.captured_piece
.expect("Expected captured piece to unmake PromotionCapture");
board.remove_own_piece(mv.dst, promoted_piece.0);
board.insert_opponent_piece(mv.dst, captured_piece.0);
board.insert_own_piece(mv.src, PieceType::Pawn);
mailbox.set_piece_at(mv.src, Some((PieceType::Pawn, color_before_move)));
mailbox.set_piece_at(mv.dst, Some(captured_piece));
board.remove_own_piece(mv.dst(), promoted_piece.0);
board.insert_opponent_piece(mv.dst(), captured_piece.0);
board.insert_own_piece(mv.src(), PieceType::Pawn);
mailbox.set_piece_at(mv.src(), Some((PieceType::Pawn, color_before_move)));
mailbox.set_piece_at(mv.dst(), Some(captured_piece));
}
MoveType::EnPassant => {
let enemy_pawn_square = match color_before_move {
Color::White => mv.dst - 8,
Color::Black => mv.dst + 8,
Color::White => mv.dst() - 8,
Color::Black => mv.dst() + 8,
};
board.move_piece(mv.dst, mv.src, piece_at_dst.0);
board.move_piece(mv.dst(), mv.src(), piece_at_dst.0);
board.insert_opponent_piece(enemy_pawn_square, PieceType::Pawn);
mailbox.set_piece_at(mv.src, Some((PieceType::Pawn, color_before_move)));
mailbox.set_piece_at(mv.src(), Some((PieceType::Pawn, color_before_move)));
mailbox.set_piece_at(
enemy_pawn_square,
Some((PieceType::Pawn, color_before_move.opponent())),
);
}
MoveType::Castle => {
let (rook_src, rook_dst) = match mv.dst {
Square::C1 | Square::C8 => (mv.dst - 2, mv.dst + 1),
Square::G1 | Square::G8 => (mv.dst + 1, mv.dst - 1),
MoveType::KingCastle | MoveType::QueenCastle => {
let (rook_src, rook_dst) = match mv.dst() {
Square::C1 | Square::C8 => (mv.dst() - 2, mv.dst() + 1),
Square::G1 | Square::G8 => (mv.dst() + 1, mv.dst() - 1),
_ => return,
};
board.move_piece(mv.dst, mv.src, piece_at_dst.0);
board.move_piece(mv.dst(), mv.src(), piece_at_dst.0);
board.remove_own_piece(rook_dst, PieceType::Rook);
board.insert_own_piece(rook_src, PieceType::Rook);
mailbox.set_piece_at(mv.src, mailbox.piece_at(mv.dst));
mailbox.set_piece_at(mv.dst, None);
mailbox.set_piece_at(mv.src(), mailbox.piece_at(mv.dst()));
mailbox.set_piece_at(mv.dst(), None);
mailbox.set_piece_at(rook_src, Some((PieceType::Rook, color_before_move)));
mailbox.set_piece_at(rook_dst, None);
}
@@ -303,7 +319,7 @@ impl Default for Game {
mod tests {
use crate::{
board::{fen::from_fen, square::Square},
movegen::r#move::{MoveType, Promote},
movegen::r#move::MoveType,
};
use super::*;
@@ -327,7 +343,7 @@ mod tests {
#[test]
fn test_make_move_capture() -> Result<(), String> {
let mut game = from_fen(FEN)?;
let f3f5 = Move::with_type(Square::F3, Square::F5, MoveType::Capture);
let f3f5 = Move::new_with_type(Square::F3, Square::F5, MoveType::Capture);
game.make_move(&f3f5);
assert_eq!(game, from_fen(FEN_CAPTURE)?);
@@ -341,7 +357,7 @@ mod tests {
#[test]
fn test_make_move_en_passant() -> Result<(), String> {
let mut game = from_fen(FEN)?;
let h5g6 = Move::with_type(Square::H5, Square::G6, MoveType::EnPassant);
let h5g6 = Move::new_with_type(Square::H5, Square::G6, MoveType::EnPassant);
game.make_move(&h5g6);
assert_eq!(game, from_fen(FEN_EN_PASSANT)?);
@@ -355,7 +371,7 @@ mod tests {
#[test]
fn test_make_move_double_push() -> Result<(), String> {
let mut game = from_fen(FEN)?;
let b2b4 = Move::with_type(Square::B2, Square::B4, MoveType::DoublePush);
let b2b4 = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush);
game.make_move(&b2b4);
assert_eq!(game, from_fen(FEN_DOUBLE_PUSH)?);
@@ -367,7 +383,7 @@ mod tests {
#[test]
fn test_make_move_promotion() -> Result<(), String> {
let mut game = from_fen(FEN)?;
let c7c8 = Move::with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen));
let c7c8 = Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionQueen);
game.make_move(&c7c8);
assert_eq!(game, from_fen(FEN_PROMOTION)?);
@@ -380,11 +396,7 @@ mod tests {
#[test]
fn test_make_move_promotion_capture() -> Result<(), String> {
let mut game = from_fen(FEN)?;
let c7b8 = Move::with_type(
Square::C7,
Square::B8,
MoveType::PromotionCapture(Promote::Queen),
);
let c7b8 = Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureQueen);
game.make_move(&c7b8);
assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE)?);
@@ -396,7 +408,7 @@ mod tests {
#[test]
fn test_make_move_castle() -> Result<(), String> {
let mut game = from_fen(FEN)?;
let e1g1 = Move::with_type(Square::E1, Square::G1, MoveType::Castle);
let e1g1 = Move::new_with_type(Square::E1, Square::G1, MoveType::KingCastle);
game.make_move(&e1g1);
assert_eq!(game, from_fen(FEN_CASTLE)?);
Ok(())
@@ -409,13 +421,13 @@ mod tests {
fn test_unmake_quiet_and_double_push() -> Result<(), String> {
let mut game = from_fen(FEN_1)?;
let game_before_make = game.clone();
let mv = Move::with_type(Square::B2, Square::B3, MoveType::Quiet);
let mv = Move::new_with_type(Square::B2, Square::B3, MoveType::Quiet);
game.make_move(&mv);
game.unmake_move();
assert_eq!(game_before_make, game);
let mv = Move::with_type(Square::B2, Square::B4, MoveType::DoublePush);
let mv = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush);
game.make_move(&mv);
game.unmake_move();
@@ -428,23 +440,19 @@ mod tests {
fn test_unmake_capture_and_promotion() -> Result<(), String> {
let mut game = from_fen(FEN_1)?;
let game_before_make = game.clone();
let mv = Move::with_type(Square::D3, Square::B5, MoveType::Capture);
let mv = Move::new_with_type(Square::D3, Square::B5, MoveType::Capture);
game.make_move(&mv);
game.unmake_move();
assert_eq!(game_before_make, game);
let mv = Move::with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen));
let mv = Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionQueen);
game.make_move(&mv);
game.unmake_move();
assert_eq!(game_before_make, game);
let mv = Move::with_type(
Square::C7,
Square::B8,
MoveType::PromotionCapture(Promote::Queen),
);
let mv = Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureQueen);
game.make_move(&mv);
game.unmake_move();
@@ -457,7 +465,7 @@ mod tests {
fn test_unmake_en_passant() -> Result<(), String> {
let mut game = from_fen(FEN_2)?;
let game_before_make = game.clone();
let mv = Move::with_type(Square::A4, Square::B3, MoveType::EnPassant);
let mv = Move::new_with_type(Square::A4, Square::B3, MoveType::EnPassant);
game.make_move(&mv);
game.unmake_move();
@@ -470,7 +478,7 @@ mod tests {
fn test_unmake_castle() -> Result<(), String> {
let mut game = from_fen(FEN_1)?;
let game_before_make = game.clone();
let mv = Move::with_type(Square::E1, Square::C1, MoveType::Castle);
let mv = Move::new_with_type(Square::E1, Square::C1, MoveType::KingCastle);
game.make_move(&mv);
game.unmake_move();

View File

@@ -95,18 +95,23 @@ impl MoveParameters {
}
fn add_moving_piece(&mut self, mailbox: &Mailbox, mv: &Move) {
self.moving_piece = mailbox.piece_at(mv.src);
self.moving_piece = mailbox.piece_at(mv.src());
}
fn add_captured_piece(&mut self, mailbox: &Mailbox, mv: &Move) {
if let MoveType::Capture | MoveType::PromotionCapture(_) = mv.move_type {
self.captured_piece = mailbox.piece_at(mv.dst)
if let MoveType::Capture
| MoveType::PromotionCaptureKnight
| MoveType::PromotionCaptureBishop
| MoveType::PromotionCaptureRook
| MoveType::PromotionCaptureQueen = MoveType::from_move(mv)
{
self.captured_piece = mailbox.piece_at(mv.dst())
}
}
fn add_promoted_piece(&mut self, mv: &Move, color: Color) {
if let MoveType::Promotion(promote) | MoveType::PromotionCapture(promote) = mv.move_type {
self.promoted_piece = Some((promote.into_piece_type(), color));
if mv.is_promotion() {
self.promoted_piece = Some((mv.promotion_type().into_piece_type(), color));
}
}

View File

@@ -1,7 +1,4 @@
use crate::{
board::board::Color,
movegen::r#move::{Move, MoveType},
};
use crate::{board::board::Color, movegen::r#move::Move};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct State {
@@ -47,16 +44,16 @@ impl State {
en_passant_square: Option<usize>,
) {
self.set_en_passant_square(en_passant_square);
self.update_castling_state_quiet(mv.src, color);
self.update_castling_state_capture(mv.dst, color.opponent());
self.update_half_move(mv.move_type, pawn_move);
self.update_castling_state_quiet(mv.src(), color);
self.update_castling_state_capture(mv.dst(), color.opponent());
self.update_half_move(mv.is_capture(), pawn_move);
self.update_full_move(color);
self.change_side();
}
pub fn update_null_game_state(&mut self, color: Color) {
self.set_en_passant_square(None);
self.update_half_move(MoveType::Quiet, false);
self.update_half_move(false, false);
self.update_full_move(color);
self.change_side();
}
@@ -117,8 +114,8 @@ impl State {
}
}
pub fn update_half_move(&mut self, move_type: MoveType, pawn_move: bool) {
if move_type == MoveType::Capture || pawn_move {
pub fn update_half_move(&mut self, is_capture: bool, is_pawn_move: bool) {
if is_capture || is_pawn_move {
self.halfmove_clock = 0;
} else {
self.halfmove_clock += 1;

View File

@@ -163,7 +163,7 @@ impl ZobristHash {
}
}
pub fn update_promotion(&mut self, src: usize, dst: usize, promote: &Promote, color: Color) {
pub fn update_promotion(&mut self, src: usize, dst: usize, promote: Promote, color: Color) {
let keys = zobrist_keys();
self.0 ^= keys.square_piece_color[src][PieceType::Pawn][color];
self.0 ^= keys.square_piece_color[dst][promote.into_piece_type()][color];
@@ -211,7 +211,7 @@ impl ZobristHash {
mod tests {
use crate::{
board::{fen::from_fen, square::Square},
movegen::r#move::{Move, MoveType, Promote},
movegen::r#move::{Move, MoveType},
};
const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
@@ -244,7 +244,11 @@ mod tests {
#[test]
fn test_update_capture() -> Result<(), String> {
let mut incremental = from_fen(FEN)?;
incremental.make_move(&Move::with_type(Square::F3, Square::F5, MoveType::Capture));
incremental.make_move(&Move::new_with_type(
Square::F3,
Square::F5,
MoveType::Capture,
));
let from_scratch = from_fen(FEN_CAPTURE)?;
assert_eq!(incremental.hash, from_scratch.hash);
@@ -258,7 +262,7 @@ mod tests {
#[test]
fn test_update_en_passant() -> Result<(), String> {
let mut incremental = from_fen(FEN)?;
incremental.make_move(&Move::with_type(
incremental.make_move(&Move::new_with_type(
Square::H5,
Square::G6,
MoveType::EnPassant,
@@ -276,7 +280,7 @@ mod tests {
#[test]
fn test_update_double_push() -> Result<(), String> {
let mut incremental = from_fen(FEN)?;
incremental.make_move(&Move::with_type(
incremental.make_move(&Move::new_with_type(
Square::B2,
Square::B4,
MoveType::DoublePush,
@@ -293,10 +297,10 @@ mod tests {
#[test]
fn test_update_promotion() -> Result<(), String> {
let mut incremental = from_fen(FEN)?;
incremental.make_move(&Move::with_type(
incremental.make_move(&Move::new_with_type(
Square::C7,
Square::C8,
MoveType::Promotion(Promote::Queen),
MoveType::PromotionQueen,
));
let from_scratch = from_fen(FEN_PROMOTION)?;
@@ -311,10 +315,10 @@ mod tests {
#[test]
fn test_update_promotion_capture() -> Result<(), String> {
let mut incremental = from_fen(FEN)?;
incremental.make_move(&Move::with_type(
incremental.make_move(&Move::new_with_type(
Square::C7,
Square::B8,
MoveType::PromotionCapture(Promote::Queen),
MoveType::PromotionCaptureQueen,
));
let from_scratch = from_fen(FEN_PROMOTION_CAPTURE)?;
@@ -328,7 +332,11 @@ mod tests {
#[test]
fn test_update_castle() -> Result<(), String> {
let mut incremental = from_fen(FEN)?;
incremental.make_move(&Move::with_type(Square::E1, Square::G1, MoveType::Castle));
incremental.make_move(&Move::new_with_type(
Square::E1,
Square::G1,
MoveType::KingCastle,
));
let from_scratch = from_fen(FEN_CASTLE)?;
assert_eq!(incremental.hash, from_scratch.hash);

View File

@@ -11,8 +11,8 @@ use crate::board::{
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum Promote {
Knight,
Rook,
Bishop,
Rook,
Queen,
}
@@ -27,77 +27,154 @@ impl Promote {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum MoveType {
Quiet,
Capture,
DoublePush,
Promotion(Promote),
PromotionCapture(Promote),
EnPassant,
Castle,
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct Move(u16);
impl Move {
pub const fn new(src: usize, dst: usize) -> Self {
Self((src << 10 | dst << 4) as u16)
}
pub const fn new_with_type(src: usize, dst: usize, move_type: MoveType) -> Self {
Self((src << 10 | dst << 4 | (move_type as u8) as usize) as u16)
}
pub const fn is_capture(&self) -> bool {
self.0 & 0b0100 != 0
}
pub const fn is_promotion(&self) -> bool {
self.0 & 0b1000 != 0
}
pub const fn src(&self) -> usize {
(self.0 as usize) >> 10
}
pub const fn dst(&self) -> usize {
(self.0 as usize) >> 4 & 0b111111
}
pub const fn promo_piece(&self) -> usize {
(self.0 as usize) & 0b11
}
pub const fn move_type(&self) -> usize {
(self.0 as usize) & 0b1111
}
pub fn promotion_type(&self) -> Promote {
match self.promo_piece() {
0b00 => Promote::Knight,
0b01 => Promote::Bishop,
0b10 => Promote::Rook,
0b11 => Promote::Queen,
_ => unreachable!(),
}
}
}
// https://www.chessprogramming.org/Encoding_Moves#From-To_Based
#[rustfmt::skip]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct Move {
pub src: usize,
pub dst: usize,
pub move_type: MoveType,
#[repr(u8)]
pub enum MoveType {
Quiet = 0b0000,
DoublePush = 0b0001,
KingCastle = 0b0010,
QueenCastle = 0b0011,
Capture = 0b0100,
EnPassant = 0b0101,
PromotionKnight = 0b1000,
PromotionBishop = 0b1001,
PromotionRook = 0b1010,
PromotionQueen = 0b1011,
PromotionCaptureKnight = 0b1100,
PromotionCaptureBishop = 0b1101,
PromotionCaptureRook = 0b1110,
PromotionCaptureQueen = 0b1111,
}
impl MoveType {
pub const fn new_promo(promo_piece: Promote) -> Self {
match promo_piece {
Promote::Knight => Self::PromotionKnight,
Promote::Bishop => Self::PromotionBishop,
Promote::Rook => Self::PromotionRook,
Promote::Queen => Self::PromotionQueen,
}
}
pub const fn new_promo_capture(promo_piece: Promote) -> Self {
match promo_piece {
Promote::Knight => Self::PromotionCaptureKnight,
Promote::Bishop => Self::PromotionCaptureBishop,
Promote::Rook => Self::PromotionCaptureRook,
Promote::Queen => Self::PromotionCaptureQueen,
}
}
pub fn from_move(mv: &Move) -> Self {
if mv.is_promotion() {
if mv.is_capture() {
match mv.promotion_type() {
Promote::Knight => Self::PromotionCaptureKnight,
Promote::Bishop => Self::PromotionCaptureBishop,
Promote::Rook => Self::PromotionCaptureRook,
Promote::Queen => Self::PromotionCaptureQueen,
}
} else {
match mv.promotion_type() {
Promote::Knight => Self::PromotionKnight,
Promote::Bishop => Self::PromotionBishop,
Promote::Rook => Self::PromotionRook,
Promote::Queen => Self::PromotionQueen,
}
}
} else {
match mv.move_type() {
0b0000 => Self::Quiet,
0b0001 => Self::DoublePush,
0b0010 => Self::KingCastle,
0b0011 => Self::QueenCastle,
0b0100 => Self::Capture,
0b0101 => Self::EnPassant,
_ => unreachable!(),
}
}
}
}
fn format_move(mv: &Move, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", to_algebraic(mv.src()), to_algebraic(mv.dst()))?;
if mv.is_promotion() {
let piece = mv.promotion_type();
let promote_char = match piece {
Promote::Knight => 'n',
Promote::Bishop => 'b',
Promote::Rook => 'r',
Promote::Queen => 'q',
};
write!(f, "{promote_char}")?;
}
Ok(())
}
impl fmt::Debug for Move {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", to_algebraic(self.src), to_algebraic(self.dst))?;
if let MoveType::Promotion(piece) | MoveType::PromotionCapture(piece) = &self.move_type {
let promote_char = match piece {
Promote::Knight => 'n',
Promote::Bishop => 'b',
Promote::Rook => 'r',
Promote::Queen => 'q',
};
write!(f, "{promote_char}")?;
}
Ok(())
format_move(self, f)
}
}
impl fmt::Display for Move {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", to_algebraic(self.src), to_algebraic(self.dst))?;
if let MoveType::Promotion(piece) | MoveType::PromotionCapture(piece) = &self.move_type {
let promote_char = match piece {
Promote::Knight => 'n',
Promote::Bishop => 'b',
Promote::Rook => 'r',
Promote::Queen => 'q',
};
write!(f, "{promote_char}")?;
}
Ok(())
format_move(self, f)
}
}
impl Move {
pub const fn new(src: usize, dst: usize) -> Self {
Self {
src,
dst,
move_type: MoveType::Quiet,
}
}
pub const fn with_type(src: usize, dst: usize, move_type: MoveType) -> Self {
Self {
src,
dst,
move_type,
}
}
#[allow(clippy::unwrap_used)]
pub fn parse_promotion(mut mv_chars: Chars<'_>) -> Result<Promote, String> {
match mv_chars.next().unwrap() {
@@ -114,20 +191,16 @@ impl Move {
if mv.len() != 4 && mv.len() != 5 {
return Err("Invalid move characters length".to_string());
}
let mut mv_chars = mv.chars();
let src_file = (mv_chars.next().unwrap() as usize) - ('a' as usize);
let src_rank = (mv_chars.next().unwrap() as usize) - ('1' as usize);
let dst_file = (mv_chars.next().unwrap() as usize) - ('a' as usize);
let dst_rank = (mv_chars.next().unwrap() as usize) - ('1' as usize);
let src = from_coords(src_rank, src_file);
let dst = from_coords(dst_rank, dst_file);
let promote_into = (mv.len() == 5)
.then(|| Self::parse_promotion(mv_chars))
.transpose()?;
Ok(Self::build_with_type(&game.mailbox, src, dst, promote_into))
}
@@ -139,54 +212,49 @@ impl Move {
) -> Self {
let moving = mailbox.piece_at(src);
let captured = mailbox.piece_at(dst);
match (captured, promote_into) {
(Some(_), None) => return Self::with_type(src, dst, MoveType::Capture),
(None, Some(promote_into)) => {
return Self::with_type(src, dst, MoveType::Promotion(promote_into))
(Some(_), None) => return Self::new_with_type(src, dst, MoveType::Capture),
(None, Some(promote)) => {
return Self::new_with_type(src, dst, MoveType::new_promo(promote))
}
(Some(_), Some(promote_into)) => {
return Self::with_type(src, dst, MoveType::PromotionCapture(promote_into))
(Some(_), Some(promote)) => {
return Self::new_with_type(src, dst, MoveType::new_promo_capture(promote))
}
_ => (),
}
if let Some((PieceType::Pawn, _)) = moving {
if src.abs_diff(dst) == 16 {
return Self::with_type(src, dst, MoveType::DoublePush);
return Self::new_with_type(src, dst, MoveType::DoublePush);
}
if captured.is_none() && src.abs_diff(dst) != 8 {
return Self::with_type(src, dst, MoveType::EnPassant);
return Self::new_with_type(src, dst, MoveType::EnPassant);
}
}
if let Some((PieceType::King, _)) = moving {
if src.abs_diff(dst) == 2 {
return Self::with_type(src, dst, MoveType::Castle);
return if dst > src {
Self::new_with_type(src, dst, MoveType::KingCastle)
} else {
Self::new_with_type(src, dst, MoveType::QueenCastle)
};
}
}
Self::new(src, dst)
}
pub fn parse_into_str(&self) -> String {
format!("{self:?}")
}
pub const fn is_capture(&self) -> bool {
matches!(
self.move_type,
MoveType::Capture | MoveType::PromotionCapture(_)
)
}
}
#[cfg(test)]
mod tests {
use super::Move;
use crate::board::fen::from_fen;
use crate::board::square::Square;
use crate::movegen::r#move::{Move, MoveType, Promote};
use crate::movegen::r#move::MoveType;
const FEN: &'static str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
@@ -207,7 +275,7 @@ mod tests {
let game = from_fen(FEN)?;
let mv_str = "d3b5";
let actual = Move::parse_from_str(&game, &mv_str)?;
let expected = Move::with_type(Square::D3, Square::B5, MoveType::Capture);
let expected = Move::new_with_type(Square::D3, Square::B5, MoveType::Capture);
assert_eq!(expected, actual);
@@ -219,7 +287,7 @@ mod tests {
let game = from_fen(FEN)?;
let mv_str = "h5g6";
let actual = Move::parse_from_str(&game, &mv_str)?;
let expected = Move::with_type(Square::H5, Square::G6, MoveType::EnPassant);
let expected = Move::new_with_type(Square::H5, Square::G6, MoveType::EnPassant);
assert_eq!(expected, actual);
@@ -231,7 +299,7 @@ mod tests {
let game = from_fen(FEN)?;
let mv_str = "b2b4";
let actual = Move::parse_from_str(&game, &mv_str)?;
let expected = Move::with_type(Square::B2, Square::B4, MoveType::DoublePush);
let expected = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush);
assert_eq!(expected, actual);
@@ -243,7 +311,7 @@ mod tests {
let game = from_fen(FEN)?;
let mv_str = "c7c8q";
let actual = Move::parse_from_str(&game, &mv_str)?;
let expected = Move::with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen));
let expected = Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionQueen);
assert_eq!(expected, actual);
@@ -255,11 +323,7 @@ mod tests {
let game = from_fen(FEN)?;
let mv_str = "c7b8q";
let actual = Move::parse_from_str(&game, &mv_str)?;
let expected = Move::with_type(
Square::C7,
Square::B8,
MoveType::PromotionCapture(Promote::Queen),
);
let expected = Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureQueen);
assert_eq!(expected, actual);

View File

@@ -8,7 +8,7 @@ use crate::movegen::attack_generator::{
fetch_bishop_attacks, fetch_king_attacks, fetch_knight_attacks, fetch_pawn_attacks,
fetch_queen_attacks, fetch_rook_attacks,
};
use crate::movegen::r#move::{Move, MoveType, Promote};
use crate::movegen::r#move::{Move, MoveType};
use u64 as Bitboard;
pub fn pawn_pseudo_moves(
@@ -41,18 +41,21 @@ pub fn pawn_pseudo_moves(
}
fn add_promotion_moves(moves: &mut Vec<Move>, src: usize, dst: usize, capture: bool) {
let promotion_type = if capture {
MoveType::PromotionCapture
if capture {
moves.extend([
Move::new_with_type(src, dst, MoveType::PromotionCaptureKnight),
Move::new_with_type(src, dst, MoveType::PromotionCaptureBishop),
Move::new_with_type(src, dst, MoveType::PromotionCaptureRook),
Move::new_with_type(src, dst, MoveType::PromotionCaptureQueen),
]);
} else {
MoveType::Promotion
moves.extend([
Move::new_with_type(src, dst, MoveType::PromotionKnight),
Move::new_with_type(src, dst, MoveType::PromotionBishop),
Move::new_with_type(src, dst, MoveType::PromotionRook),
Move::new_with_type(src, dst, MoveType::PromotionQueen),
]);
};
moves.extend([
Move::with_type(src, dst, promotion_type(Promote::Knight)),
Move::with_type(src, dst, promotion_type(Promote::Bishop)),
Move::with_type(src, dst, promotion_type(Promote::Rook)),
Move::with_type(src, dst, promotion_type(Promote::Queen)),
]);
}
fn white_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec<Move> {
@@ -77,7 +80,7 @@ fn white_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec<Move> {
while double_push != 0 {
let dst = lsb(double_push);
let src = dst - 16;
moves.push(Move::with_type(src, dst, MoveType::DoublePush));
moves.push(Move::new_with_type(src, dst, MoveType::DoublePush));
double_push &= double_push - 1;
}
moves
@@ -105,7 +108,7 @@ fn black_pawn_quiet_moves(pawns: Bitboard, occupancies: Bitboard) -> Vec<Move> {
while double_push != 0 {
let dst = lsb(double_push);
let src = dst + 16;
moves.push(Move::with_type(src, dst, MoveType::DoublePush));
moves.push(Move::new_with_type(src, dst, MoveType::DoublePush));
double_push &= double_push - 1;
}
moves
@@ -127,7 +130,7 @@ fn white_pawn_capture_moves(
if have_common_bit(square_to_bitboard(dst), RANK_8) {
add_promotion_moves(&mut moves, src, dst, true);
} else {
moves.push(Move::with_type(src, dst, MoveType::Capture));
moves.push(Move::new_with_type(src, dst, MoveType::Capture));
}
w_pawns_capture_east &= w_pawns_capture_east - 1;
}
@@ -138,7 +141,7 @@ fn white_pawn_capture_moves(
if have_common_bit(square_to_bitboard(dst), RANK_8) {
add_promotion_moves(&mut moves, src, dst, true);
} else {
moves.push(Move::with_type(src, dst, MoveType::Capture));
moves.push(Move::new_with_type(src, dst, MoveType::Capture));
}
w_pawns_capture_west &= w_pawns_capture_west - 1;
}
@@ -147,7 +150,7 @@ fn white_pawn_capture_moves(
let attacked_src = fetch_pawn_attacks(en_passant_sq, Color::Black);
let mut result = attacked_src & pawns;
while result != 0 {
moves.push(Move::with_type(
moves.push(Move::new_with_type(
lsb(result),
en_passant_sq,
MoveType::EnPassant,
@@ -174,7 +177,7 @@ fn black_pawn_capture_moves(
if have_common_bit(square_to_bitboard(dst), RANK_1) {
add_promotion_moves(&mut moves, src, dst, true);
} else {
moves.push(Move::with_type(src, dst, MoveType::Capture));
moves.push(Move::new_with_type(src, dst, MoveType::Capture));
}
b_pawns_capture_east &= b_pawns_capture_east - 1;
}
@@ -185,7 +188,7 @@ fn black_pawn_capture_moves(
if have_common_bit(square_to_bitboard(dst), RANK_1) {
add_promotion_moves(&mut moves, src, dst, true);
} else {
moves.push(Move::with_type(src, dst, MoveType::Capture));
moves.push(Move::new_with_type(src, dst, MoveType::Capture));
}
b_pawns_capture_west &= b_pawns_capture_west - 1;
}
@@ -194,7 +197,7 @@ fn black_pawn_capture_moves(
let attacked_src = fetch_pawn_attacks(en_passant_square, Color::White);
let mut result = attacked_src & pawns;
while result != 0 {
moves.push(Move::with_type(
moves.push(Move::new_with_type(
lsb(result),
en_passant_square,
MoveType::EnPassant,
@@ -221,7 +224,7 @@ pub fn knight_pseudo_moves(
while attacks != 0 {
let attack_sq = lsb(attacks);
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) {
moves.push(Move::with_type(src, attack_sq, MoveType::Capture));
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else {
moves.push(Move::new(src, attack_sq));
}
@@ -248,7 +251,7 @@ pub fn bishop_pseudo_moves(
while attacks != 0 {
let attack_sq = lsb(attacks);
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) {
moves.push(Move::with_type(src, attack_sq, MoveType::Capture));
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else {
moves.push(Move::new(src, attack_sq));
}
@@ -275,7 +278,7 @@ pub fn rook_pseudo_moves(
while attacks != 0 {
let attack_sq = lsb(attacks);
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) {
moves.push(Move::with_type(src, attack_sq, MoveType::Capture));
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else {
moves.push(Move::new(src, attack_sq));
}
@@ -302,7 +305,7 @@ pub fn queen_pseudo_moves(
while attacks != 0 {
let attack_sq = lsb(attacks);
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) {
moves.push(Move::with_type(src, attack_sq, MoveType::Capture));
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else {
moves.push(Move::new(src, attack_sq));
}
@@ -329,7 +332,7 @@ pub fn king_pseudo_moves(
while attacks != 0 {
let attack_sq = lsb(attacks);
if have_common_bit(square_to_bitboard(attack_sq), opponent_occupancies) {
moves.push(Move::with_type(src, attack_sq, MoveType::Capture));
moves.push(Move::new_with_type(src, attack_sq, MoveType::Capture));
} else {
moves.push(Move::new(src, attack_sq));
}
@@ -349,7 +352,19 @@ fn king_castling_moves(board: &Board, color: Color, all_occupancies: Bitboard) -
let mut add_move_if_empty_path = |path_mask, king_dst| {
if !have_common_bit(all_occupancies, path_mask) {
moves.push(Move::with_type(king_src, king_dst, MoveType::Castle));
if king_dst > king_src {
moves.push(Move::new_with_type(
king_src,
king_dst,
MoveType::KingCastle,
));
} else {
moves.push(Move::new_with_type(
king_src,
king_dst,
MoveType::QueenCastle,
));
}
}
};
@@ -408,19 +423,19 @@ mod tests {
let new_game = from_fen(FEN_PAWN_MOVES)?;
let expected = vec![
Move::new(9, 17),
Move::with_type(9, 25, MoveType::DoublePush),
Move::new_with_type(9, 25, MoveType::DoublePush),
Move::new(10, 18),
Move::with_type(10, 26, MoveType::DoublePush),
Move::new_with_type(10, 26, MoveType::DoublePush),
Move::new(14, 22),
Move::with_type(14, 30, MoveType::DoublePush),
Move::new_with_type(14, 30, MoveType::DoublePush),
Move::new(15, 23),
Move::with_type(15, 31, MoveType::DoublePush),
Move::new_with_type(15, 31, MoveType::DoublePush),
Move::new(29, 37),
Move::new(32, 40),
Move::with_type(32, 41, MoveType::Capture),
Move::with_type(36, 43, MoveType::Capture),
Move::new_with_type(32, 41, MoveType::Capture),
Move::new_with_type(36, 43, MoveType::Capture),
Move::new(36, 44),
Move::with_type(36, 45, MoveType::Capture),
Move::new_with_type(36, 45, MoveType::Capture),
];
let mut actual = new_game.board.pseudo_moves(Color::White, PieceType::Pawn);
@@ -428,12 +443,12 @@ mod tests {
assert_eq!(expected, actual);
let expected = vec![
Move::with_type(41, 32, MoveType::Capture),
Move::new_with_type(41, 32, MoveType::Capture),
Move::new(41, 33),
Move::new(43, 35),
Move::with_type(43, 36, MoveType::Capture),
Move::new_with_type(43, 36, MoveType::Capture),
Move::new(48, 40),
Move::with_type(55, 39, MoveType::DoublePush),
Move::new_with_type(55, 39, MoveType::DoublePush),
Move::new(55, 47),
];
let mut actual = new_game.board.pseudo_moves(Color::Black, PieceType::Pawn);
@@ -457,7 +472,7 @@ mod tests {
Move::new(21, 11),
Move::new(21, 31),
Move::new(21, 36),
Move::with_type(21, 38, MoveType::Capture),
Move::new_with_type(21, 38, MoveType::Capture),
];
let mut actual = new_game.board.pseudo_moves(Color::White, PieceType::Knight);
actual.sort();
@@ -483,7 +498,7 @@ mod tests {
Move::new(26, 35),
Move::new(26, 40),
Move::new(26, 44),
Move::with_type(26, 53, MoveType::Capture),
Move::new_with_type(26, 53, MoveType::Capture),
];
let mut actual = new_game.board.pseudo_moves(Color::White, PieceType::Bishop);
actual.sort();
@@ -504,14 +519,14 @@ mod tests {
Move::new(11, 35),
Move::new(11, 43),
Move::new(11, 51),
Move::with_type(11, 59, MoveType::Capture),
Move::new_with_type(11, 59, MoveType::Capture),
Move::new(12, 13),
Move::new(12, 14),
Move::new(12, 15),
Move::new(12, 20),
Move::new(12, 28),
Move::new(12, 36),
Move::with_type(12, 44, MoveType::Capture),
Move::new_with_type(12, 44, MoveType::Capture),
];
let mut actual = new_game.board.pseudo_moves(Color::White, PieceType::Rook);
actual.sort();
@@ -534,7 +549,7 @@ mod tests {
Move::new(17, 25),
Move::new(17, 33),
Move::new(17, 41),
Move::with_type(17, 49, MoveType::Capture),
Move::new_with_type(17, 49, MoveType::Capture),
];
let mut actual = new_game.board.pseudo_moves(Color::White, PieceType::Queen);
actual.sort();
@@ -567,7 +582,10 @@ mod tests {
assert_eq!(expected, actual);
let new_game_2 = from_fen(FEN_KING_MOVES[1])?;
let expected = vec![Move::with_type(4, 2, MoveType::Castle), Move::new(4, 3)];
let expected = vec![
Move::new_with_type(4, 2, MoveType::QueenCastle),
Move::new(4, 3),
];
let mut actual = new_game_2.board.pseudo_moves(Color::White, PieceType::King);
actual.sort();
assert_eq!(expected, actual);
@@ -578,7 +596,7 @@ mod tests {
Move::new(60, 53),
Move::new(60, 59),
Move::new(60, 61),
Move::with_type(60, 62, MoveType::Castle),
Move::new_with_type(60, 62, MoveType::KingCastle),
];
let mut actual = new_game_3.board.pseudo_moves(Color::Black, PieceType::King);
actual.sort();

View File

@@ -14,8 +14,8 @@ pub fn score_move(
return -90;
}
let aggressor = mailbox.piece_at(mv.src).expect("No aggressor found.");
mailbox.piece_at(mv.dst).map_or(100, |victim| {
let aggressor = mailbox.piece_at(mv.src()).expect("No aggressor found.");
mailbox.piece_at(mv.dst()).map_or(100, |victim| {
aggressor.0.idx() as i32 - (victim.0.idx() * 8) as i32
})
}
@@ -34,9 +34,9 @@ mod tests {
#[test]
fn test_score_move() -> Result<(), String> {
let game = from_fen(FEN)?;
let queen_takes_pawn = Move::with_type(Square::F3, Square::F5, MoveType::Capture);
let pawn_takes_queen = Move::with_type(Square::H5, Square::G6, MoveType::Capture);
let castle = Move::with_type(Square::E1, Square::C1, MoveType::Castle);
let queen_takes_pawn = Move::new_with_type(Square::F3, Square::F5, MoveType::Capture);
let pawn_takes_queen = Move::new_with_type(Square::H5, Square::G6, MoveType::Capture);
let castle = Move::new_with_type(Square::E1, Square::C1, MoveType::QueenCastle);
let mut moves = vec![castle, queen_takes_pawn, pawn_takes_queen];
moves.sort_unstable_by_key(|mv| score_move(&game.mailbox, *mv, None, None));