Add more tests
This commit is contained in:
@@ -317,174 +317,61 @@ impl Default for Game {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use rstest::rstest;
|
||||||
board::{fen::from_fen, square::Square},
|
|
||||||
movegen::r#move::MoveType,
|
use crate::board::{fen::from_fen, square::Square};
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
|
const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
|
||||||
const FEN_QUIET: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2BQ3/1P1PNPP1/R3K2R b KQk - 1 1";
|
|
||||||
|
|
||||||
#[test]
|
#[rstest]
|
||||||
fn test_make_move_quiet() -> Result<(), String> {
|
#[rustfmt::skip]
|
||||||
let mut game = from_fen(FEN)?;
|
#[case::quiet("b2b3", "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/PP1B1Q2/3PNPP1/R3K2R b KQk - 0 1")]
|
||||||
let f3e3 = Move::new(Square::F3, Square::E3);
|
#[case::double_push("b2b4", "1r2k2r/2P1pq1p/2npb3/1p3ppP/pP2P3/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1")]
|
||||||
game.make_move(&f3e3);
|
#[case::king_castle("e1g1", "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R4RK1 b k - 1 1")]
|
||||||
|
#[case::queen_castle("e1c1", "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/2KR3R b k - 1 1")]
|
||||||
|
#[case::capture("d3b5", "1r2k2r/2P1pq1p/2npb3/1B3ppP/p3P3/P4Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::en_passant("h5g6", "1r2k2r/2P1pq1p/2npb1P1/1p3p2/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_knight("c7c8n", "1rN1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_bishop("c7c8b", "1rB1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_rook("c7c8r", "1rR1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_queen("c7c8q", "1rQ1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_capture_knight("c7b8n", "1N2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_capture_bishop("c7b8b", "1B2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_capture_rook("c7b8r", "1R2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_capture_queen("c7b8q", "1Q2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
fn test_make_move(#[case] mv: &str, #[case] expected_fen: &str) {
|
||||||
|
let mut game = from_fen(FEN).unwrap();
|
||||||
|
let mv = Move::parse_from_str(&game, mv).unwrap();
|
||||||
|
game.make_move(&mv);
|
||||||
|
|
||||||
assert_eq!(game, from_fen(FEN_QUIET)?);
|
assert_eq!(game, from_fen(expected_fen).unwrap());
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FEN_CAPTURE: &str = "1r2k2r/2P1pq1p/2npb3/1p3QpP/p3P3/P2B4/1P1PNPP1/R3K2R b KQk - 0 1";
|
#[rstest]
|
||||||
|
#[case::quiet("b2b3")]
|
||||||
#[test]
|
#[case::double_push("b2b4")]
|
||||||
fn test_make_move_capture() -> Result<(), String> {
|
#[case::king_castle("e1g1")]
|
||||||
let mut game = from_fen(FEN)?;
|
#[case::queen_castle("e1c1")]
|
||||||
let f3f5 = Move::new_with_type(Square::F3, Square::F5, MoveType::Capture);
|
#[case::capture("d3b5")]
|
||||||
game.make_move(&f3f5);
|
#[case::en_passant("h5g6")]
|
||||||
|
#[case::promotion_knight("c7c8n")]
|
||||||
assert_eq!(game, from_fen(FEN_CAPTURE)?);
|
#[case::promotion_bishop("c7c8b")]
|
||||||
|
#[case::promotion_rook("c7c8r")]
|
||||||
Ok(())
|
#[case::promotion_queen("c7c8q")]
|
||||||
}
|
#[case::promotion_capture_knight("c7b8n")]
|
||||||
|
#[case::promotion_capture_bishop("c7b8b")]
|
||||||
const FEN_EN_PASSANT: &str =
|
#[case::promotion_capture_rook("c7b8r")]
|
||||||
"1r2k2r/2P1pq1p/2npb1P1/1p3p2/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1";
|
#[case::promotion_capture_queen("c7b8q")]
|
||||||
|
fn test_unmake_move(#[case] mv: &str) {
|
||||||
#[test]
|
let mut game = from_fen(FEN).unwrap();
|
||||||
fn test_make_move_en_passant() -> Result<(), String> {
|
let game_copy = game.clone();
|
||||||
let mut game = from_fen(FEN)?;
|
let mv = Move::parse_from_str(&game, mv).unwrap();
|
||||||
let h5g6 = Move::new_with_type(Square::H5, Square::G6, MoveType::EnPassant);
|
|
||||||
game.make_move(&h5g6);
|
|
||||||
|
|
||||||
assert_eq!(game, from_fen(FEN_EN_PASSANT)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_DOUBLE_PUSH: &str =
|
|
||||||
"1r2k2r/2P1pq1p/2npb3/1p3ppP/pP2P3/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_make_move_double_push() -> Result<(), String> {
|
|
||||||
let mut game = from_fen(FEN)?;
|
|
||||||
let b2b4 = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush);
|
|
||||||
game.make_move(&b2b4);
|
|
||||||
assert_eq!(game, from_fen(FEN_DOUBLE_PUSH)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_PROMOTION: &str = "1rQ1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_make_move_promotion() -> Result<(), String> {
|
|
||||||
let mut game = from_fen(FEN)?;
|
|
||||||
let c7c8 = Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionQueen);
|
|
||||||
game.make_move(&c7c8);
|
|
||||||
assert_eq!(game, from_fen(FEN_PROMOTION)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_PROMOTION_CAPTURE: &str =
|
|
||||||
"1Q2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_make_move_promotion_capture() -> Result<(), String> {
|
|
||||||
let mut game = from_fen(FEN)?;
|
|
||||||
let c7b8 = Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureQueen);
|
|
||||||
game.make_move(&c7b8);
|
|
||||||
assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_CASTLE: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R4RK1 b k - 1 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_make_move_castle() -> Result<(), String> {
|
|
||||||
let mut game = from_fen(FEN)?;
|
|
||||||
let e1g1 = Move::new_with_type(Square::E1, Square::G1, MoveType::KingCastle);
|
|
||||||
game.make_move(&e1g1);
|
|
||||||
assert_eq!(game, from_fen(FEN_CASTLE)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_1: &str = "1r2k2r/2P1p1qp/2npb3/1p3p2/p3P1pP/P2B1Q2/1P1PNPP1/R3K2R w KQk - 0 1";
|
|
||||||
const FEN_2: &str = "1r2k2r/2P1p1qp/2npb3/1p3p2/pP2P1pP/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
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::new_with_type(Square::B2, Square::B3, MoveType::Quiet);
|
|
||||||
game.make_move(&mv);
|
game.make_move(&mv);
|
||||||
game.unmake_move();
|
game.unmake_move();
|
||||||
|
|
||||||
assert_eq!(game_before_make, game);
|
assert_eq!(game, game_copy);
|
||||||
|
|
||||||
let mv = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush);
|
|
||||||
game.make_move(&mv);
|
|
||||||
game.unmake_move();
|
|
||||||
|
|
||||||
assert_eq!(game_before_make, game);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unmake_capture_and_promotion() -> Result<(), String> {
|
|
||||||
let mut game = from_fen(FEN_1)?;
|
|
||||||
let game_before_make = game.clone();
|
|
||||||
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::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::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureQueen);
|
|
||||||
game.make_move(&mv);
|
|
||||||
game.unmake_move();
|
|
||||||
|
|
||||||
assert_eq!(game_before_make, game);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unmake_en_passant() -> Result<(), String> {
|
|
||||||
let mut game = from_fen(FEN_2)?;
|
|
||||||
let game_before_make = game.clone();
|
|
||||||
let mv = Move::new_with_type(Square::A4, Square::B3, MoveType::EnPassant);
|
|
||||||
game.make_move(&mv);
|
|
||||||
game.unmake_move();
|
|
||||||
|
|
||||||
assert_eq!(game_before_make, game);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unmake_castle() -> Result<(), String> {
|
|
||||||
let mut game = from_fen(FEN_1)?;
|
|
||||||
let game_before_make = game.clone();
|
|
||||||
let mv = Move::new_with_type(Square::E1, Square::C1, MoveType::KingCastle);
|
|
||||||
game.make_move(&mv);
|
|
||||||
game.unmake_move();
|
|
||||||
|
|
||||||
assert_eq!(game_before_make, game);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -512,13 +399,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_make_and_unmake_null_move() -> Result<(), String> {
|
fn test_make_and_unmake_null_move() -> Result<(), String> {
|
||||||
let mut game = from_fen(FEN_W)?;
|
let mut game = from_fen(FEN_W)?;
|
||||||
|
|
||||||
game.make_null_move();
|
game.make_null_move();
|
||||||
|
|
||||||
assert_eq!(game, from_fen(FEN_B)?);
|
assert_eq!(game, from_fen(FEN_B)?);
|
||||||
|
|
||||||
game.unmake_null_move();
|
game.unmake_null_move();
|
||||||
|
|
||||||
assert!(game == from_fen(FEN_W)?);
|
assert!(game == from_fen(FEN_W)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -209,138 +209,34 @@ impl ZobristHash {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::board::fen::from_fen;
|
||||||
board::{fen::from_fen, square::Square},
|
use crate::movegen::r#move::Move;
|
||||||
movegen::r#move::{Move, MoveType},
|
use rstest::rstest;
|
||||||
};
|
|
||||||
|
|
||||||
const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
|
const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
|
||||||
|
|
||||||
#[test]
|
#[rstest]
|
||||||
fn test_identical_boards() -> Result<(), String> {
|
#[rustfmt::skip]
|
||||||
let game1 = from_fen(FEN)?;
|
#[case::quiet("b2b3", "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/PP1B1Q2/3PNPP1/R3K2R b KQk - 0 1")]
|
||||||
let game2 = from_fen(FEN)?;
|
#[case::double_push("b2b4", "1r2k2r/2P1pq1p/2npb3/1p3ppP/pP2P3/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1")]
|
||||||
|
#[case::king_castle("e1g1", "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R4RK1 b k - 1 1")]
|
||||||
assert_eq!(game1, game2);
|
#[case::queen_castle("e1c1", "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/2KR3R b k - 1 1")]
|
||||||
|
#[case::capture("d3b5", "1r2k2r/2P1pq1p/2npb3/1B3ppP/p3P3/P4Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
Ok(())
|
#[case::en_passant("h5g6", "1r2k2r/2P1pq1p/2npb1P1/1p3p2/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
}
|
#[case::promotion_knight("c7c8n", "1rN1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_bishop("c7c8b", "1rB1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
const FEN_QUIET: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2BQ3/1P1PNPP1/R3K2R b KQk - 1 1";
|
#[case::promotion_rook("c7c8r", "1rR1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
|
#[case::promotion_queen("c7c8q", "1rQ1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
#[test]
|
#[case::promotion_capture_knight("c7b8n", "1N2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
fn test_update_quiet() -> Result<(), String> {
|
#[case::promotion_capture_bishop("c7b8b", "1B2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
let mut incremental = from_fen(FEN)?;
|
#[case::promotion_capture_rook("c7b8r", "1R2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
incremental.make_move(&Move::new(Square::F3, Square::E3));
|
#[case::promotion_capture_queen("c7b8q", "1Q2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1")]
|
||||||
let from_scratch = from_fen(FEN_QUIET)?;
|
fn test_update(#[case] mv_str: &str, #[case] fen: &str) {
|
||||||
|
let mut incremental = from_fen(FEN).unwrap();
|
||||||
|
let mv = Move::parse_from_str(&incremental, mv_str).unwrap();
|
||||||
|
incremental.make_move(&mv);
|
||||||
|
let from_scratch = from_fen(fen).unwrap();
|
||||||
|
|
||||||
assert_eq!(incremental.hash, from_scratch.hash);
|
assert_eq!(incremental.hash, from_scratch.hash);
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_CAPTURE: &str = "1r2k2r/2P1pq1p/2npb3/1p3QpP/p3P3/P2B4/1P1PNPP1/R3K2R b KQk - 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_update_capture() -> Result<(), String> {
|
|
||||||
let mut incremental = from_fen(FEN)?;
|
|
||||||
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);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_EN_PASSANT: &str =
|
|
||||||
"1r2k2r/2P1pq1p/2npb1P1/1p3p2/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_update_en_passant() -> Result<(), String> {
|
|
||||||
let mut incremental = from_fen(FEN)?;
|
|
||||||
incremental.make_move(&Move::new_with_type(
|
|
||||||
Square::H5,
|
|
||||||
Square::G6,
|
|
||||||
MoveType::EnPassant,
|
|
||||||
));
|
|
||||||
let from_scratch = from_fen(FEN_EN_PASSANT)?;
|
|
||||||
|
|
||||||
assert_eq!(incremental.hash, from_scratch.hash);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_DOUBLE_PUSH: &str =
|
|
||||||
"1r2k2r/2P1pq1p/2npb3/1p3ppP/pP2P3/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_update_double_push() -> Result<(), String> {
|
|
||||||
let mut incremental = from_fen(FEN)?;
|
|
||||||
incremental.make_move(&Move::new_with_type(
|
|
||||||
Square::B2,
|
|
||||||
Square::B4,
|
|
||||||
MoveType::DoublePush,
|
|
||||||
));
|
|
||||||
let from_scratch = from_fen(FEN_DOUBLE_PUSH)?;
|
|
||||||
|
|
||||||
assert_eq!(incremental.hash, from_scratch.hash);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_PROMOTION: &str = "1rQ1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_update_promotion() -> Result<(), String> {
|
|
||||||
let mut incremental = from_fen(FEN)?;
|
|
||||||
incremental.make_move(&Move::new_with_type(
|
|
||||||
Square::C7,
|
|
||||||
Square::C8,
|
|
||||||
MoveType::PromotionQueen,
|
|
||||||
));
|
|
||||||
let from_scratch = from_fen(FEN_PROMOTION)?;
|
|
||||||
|
|
||||||
assert_eq!(incremental.hash, from_scratch.hash);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_PROMOTION_CAPTURE: &str =
|
|
||||||
"1Q2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_update_promotion_capture() -> Result<(), String> {
|
|
||||||
let mut incremental = from_fen(FEN)?;
|
|
||||||
incremental.make_move(&Move::new_with_type(
|
|
||||||
Square::C7,
|
|
||||||
Square::B8,
|
|
||||||
MoveType::PromotionCaptureQueen,
|
|
||||||
));
|
|
||||||
let from_scratch = from_fen(FEN_PROMOTION_CAPTURE)?;
|
|
||||||
|
|
||||||
assert_eq!(incremental.hash, from_scratch.hash);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN_CASTLE: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R4RK1 b k - 1 1";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_update_castle() -> Result<(), String> {
|
|
||||||
let mut incremental = from_fen(FEN)?;
|
|
||||||
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);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,10 +251,10 @@ impl Move {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::Move;
|
||||||
use crate::board::fen::from_fen;
|
use crate::board::fen::from_fen;
|
||||||
use crate::board::square::Square;
|
use crate::board::square::Square;
|
||||||
use crate::movegen::r#move::MoveType;
|
use crate::movegen::r#move::MoveType;
|
||||||
use super::Move;
|
|
||||||
|
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
@@ -291,8 +291,12 @@ mod tests {
|
|||||||
"c7c8b" => Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionBishop),
|
"c7c8b" => Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionBishop),
|
||||||
"c7c8r" => Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionRook),
|
"c7c8r" => Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionRook),
|
||||||
"c7c8q" => Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionQueen),
|
"c7c8q" => Move::new_with_type(Square::C7, Square::C8, MoveType::PromotionQueen),
|
||||||
"c7b8n" => Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureKnight),
|
"c7b8n" => {
|
||||||
"c7b8b" => Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureBishop),
|
Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureKnight)
|
||||||
|
}
|
||||||
|
"c7b8b" => {
|
||||||
|
Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureBishop)
|
||||||
|
}
|
||||||
"c7b8r" => Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureRook),
|
"c7b8r" => Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureRook),
|
||||||
"c7b8q" => Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureQueen),
|
"c7b8q" => Move::new_with_type(Square::C7, Square::B8, MoveType::PromotionCaptureQueen),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -301,7 +305,6 @@ mod tests {
|
|||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case("c7c8qq")]
|
#[case("c7c8qq")]
|
||||||
#[should_panic(expected = "Invalid move characters length")]
|
#[should_panic(expected = "Invalid move characters length")]
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
use crate::board::game::Game;
|
use crate::board::game::Game;
|
||||||
|
|
||||||
pub fn driver(game: &mut Game, nodes: &mut u64, depth: u8) {
|
pub fn driver(game: &mut Game, nodes: &mut u64, depth_nodes: &mut u64, depth: u8) {
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
*nodes += 1;
|
*nodes += 1;
|
||||||
|
*depth_nodes += 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = game.current_player();
|
let color = game.current_player();
|
||||||
let pseudo_moves = game.board.pseudo_moves_all();
|
let pseudo_moves = game.board.pseudo_moves_all();
|
||||||
|
let current_depth_nodes = *depth_nodes;
|
||||||
|
|
||||||
for mv in pseudo_moves {
|
for mv in pseudo_moves {
|
||||||
|
if depth == MAX_DEPTH {
|
||||||
|
println!("{mv:?}");
|
||||||
|
}
|
||||||
game.make_move(&mv);
|
game.make_move(&mv);
|
||||||
|
|
||||||
if game.board.king_under_check(color) {
|
if game.board.king_under_check(color) {
|
||||||
@@ -17,18 +22,26 @@ pub fn driver(game: &mut Game, nodes: &mut u64, depth: u8) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
driver(game, nodes, depth - 1);
|
driver(game, nodes, depth_nodes, depth - 1);
|
||||||
game.unmake_move();
|
game.unmake_move();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let nodes_at_current_depth = *depth_nodes - current_depth_nodes;
|
||||||
|
println!("Nodes at depth {depth}: {nodes_at_current_depth}");
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_DEPTH: u8 = 3;
|
const MAX_DEPTH: u8 = 2;
|
||||||
|
|
||||||
pub fn perftree_script() {
|
pub fn perftree_script() {
|
||||||
let fen = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
|
let fen = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
|
||||||
let mut game = crate::board::fen::from_fen(fen).expect("Invalid FEN string");
|
let mut game = crate::board::fen::from_fen(fen).expect("Invalid FEN string");
|
||||||
let (mut nodes, depth): (u64, u8) = (0, MAX_DEPTH);
|
let mut nodes: u64 = 0;
|
||||||
driver(&mut game, &mut nodes, depth);
|
let mut depth_nodes: u64 = 0;
|
||||||
|
let depth: u8 = MAX_DEPTH;
|
||||||
|
|
||||||
|
driver(&mut game, &mut nodes, &mut depth_nodes, depth);
|
||||||
|
|
||||||
|
println!("Total nodes: {nodes}");
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -57,9 +70,9 @@ pub fn _square_to_notation(square: u8) -> &'static str {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{board::fen::from_fen, movegen::attack_generator::init_attacks};
|
|
||||||
|
|
||||||
use super::driver;
|
use super::driver;
|
||||||
|
use crate::{board::fen::from_fen, movegen::attack_generator::init_attacks};
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
// Examples from https://www.chessprogramming.org/Perft_Results
|
// Examples from https://www.chessprogramming.org/Perft_Results
|
||||||
const FEN_PERFT: [&str; 6] = [
|
const FEN_PERFT: [&str; 6] = [
|
||||||
@@ -71,76 +84,20 @@ mod tests {
|
|||||||
"r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
|
"r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10",
|
||||||
];
|
];
|
||||||
|
|
||||||
fn perft(fen: &str, depth: u8) -> Result<u64, String> {
|
#[rstest]
|
||||||
|
#[case(0, 4865609)]
|
||||||
|
#[case(1, 193690690)]
|
||||||
|
#[case(2, 674624)]
|
||||||
|
#[case(3, 15833292)]
|
||||||
|
#[case(4, 89941194)]
|
||||||
|
#[case(5, 164075551)]
|
||||||
|
fn test_perft(#[case] num: usize, #[case] expected_nodes: u64) {
|
||||||
init_attacks();
|
init_attacks();
|
||||||
|
|
||||||
let time_now = std::time::Instant::now();
|
let mut game = from_fen(FEN_PERFT[num]).unwrap();
|
||||||
|
|
||||||
let mut game = from_fen(fen)?;
|
|
||||||
let mut nodes = 0;
|
let mut nodes = 0;
|
||||||
driver(&mut game, &mut nodes, depth);
|
driver(&mut game, &mut nodes, &mut 0, 5);
|
||||||
|
|
||||||
dbg!(nodes, time_now.elapsed());
|
assert_eq!(nodes, expected_nodes);
|
||||||
|
|
||||||
Ok(nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1";
|
|
||||||
|
|
||||||
// #[ignore]
|
|
||||||
#[test]
|
|
||||||
fn test_nodes() -> Result<(), String> {
|
|
||||||
init_attacks();
|
|
||||||
perft(FEN, 4)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_perft_1() -> Result<(), String> {
|
|
||||||
init_attacks();
|
|
||||||
assert_eq!(perft(FEN_PERFT[0], 5)?, 4865609);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_perft_2() -> Result<(), String> {
|
|
||||||
init_attacks();
|
|
||||||
assert_eq!(perft(FEN_PERFT[1], 5)?, 193690690);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_perft_3() -> Result<(), String> {
|
|
||||||
init_attacks();
|
|
||||||
assert_eq!(perft(FEN_PERFT[2], 5)?, 674624);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_perft_4() -> Result<(), String> {
|
|
||||||
init_attacks();
|
|
||||||
assert_eq!(perft(FEN_PERFT[3], 5)?, 15833292);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_perft_5() -> Result<(), String> {
|
|
||||||
init_attacks();
|
|
||||||
assert_eq!(perft(FEN_PERFT[4], 5)?, 89941194);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_perft_6() -> Result<(), String> {
|
|
||||||
init_attacks();
|
|
||||||
assert_eq!(perft(FEN_PERFT[5], 5)?, 164075551);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user