eschac
This commit is contained in:
commit
faccfbc1c5
16 changed files with 5154 additions and 0 deletions
302
tests/tests.rs
Normal file
302
tests/tests.rs
Normal file
|
@ -0,0 +1,302 @@
|
|||
use eschac::board::*;
|
||||
use eschac::position::*;
|
||||
use eschac::san::*;
|
||||
use eschac::setup::*;
|
||||
|
||||
static P1: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -";
|
||||
static P2: &'static str = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -";
|
||||
static P3: &'static str = "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -";
|
||||
static P4: &'static str = "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq -";
|
||||
static P5: &'static str = "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ -";
|
||||
static P6: &'static str = "r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - -";
|
||||
|
||||
fn recursive_check_aux(position: Position, depth: usize) {
|
||||
assert_eq!(
|
||||
position,
|
||||
position
|
||||
.as_setup()
|
||||
.to_string()
|
||||
.parse::<Setup>()
|
||||
.unwrap()
|
||||
.validate()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
if let Some(passed) = position.pass() {
|
||||
let mut position = position.clone();
|
||||
position.remove_en_passant_target_square();
|
||||
assert_eq!(position, passed.pass().unwrap());
|
||||
}
|
||||
|
||||
let computed_mirror = {
|
||||
let mut setup = Setup::new();
|
||||
for square in Square::all() {
|
||||
setup.set(
|
||||
square.mirror(),
|
||||
position.get(square).map(|piece| Piece {
|
||||
role: piece.role,
|
||||
color: !piece.color,
|
||||
}),
|
||||
);
|
||||
}
|
||||
setup.set_turn(!position.turn());
|
||||
for color in Color::all() {
|
||||
for side in CastlingSide::all() {
|
||||
setup.set_castling_rights(!color, side, position.castling_rights(color, side));
|
||||
}
|
||||
}
|
||||
setup.set_en_passant_target_square(
|
||||
position
|
||||
.en_passant_target_square()
|
||||
.map(|square| square.mirror()),
|
||||
);
|
||||
setup.validate().unwrap()
|
||||
};
|
||||
let expected_mirror = position.mirror();
|
||||
assert_eq!(computed_mirror, expected_mirror);
|
||||
assert_eq!(expected_mirror.mirror(), position);
|
||||
|
||||
match depth.checked_sub(1) {
|
||||
None => (),
|
||||
Some(depth) => {
|
||||
position.legal_moves().into_iter().for_each(|m| {
|
||||
let uci = m.to_uci();
|
||||
assert_eq!(uci, uci.to_move(&position).unwrap().to_uci());
|
||||
let san: San = m.to_san();
|
||||
match san.to_move(&position) {
|
||||
Ok(m) => assert_eq!(san, m.to_san()),
|
||||
Err(err) => {
|
||||
panic!("{san} is {err} on {position:?}")
|
||||
}
|
||||
};
|
||||
recursive_check_aux(m.make(), depth)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
fn recursive_check(record: &str) {
|
||||
recursive_check_aux(record.parse::<Setup>().unwrap().validate().unwrap(), 4);
|
||||
}
|
||||
#[test]
|
||||
fn recursive_check_1() {
|
||||
recursive_check(P1);
|
||||
}
|
||||
#[test]
|
||||
fn recursive_check_2() {
|
||||
recursive_check(P2);
|
||||
}
|
||||
#[test]
|
||||
fn recursive_check_3() {
|
||||
recursive_check(P3);
|
||||
}
|
||||
#[test]
|
||||
fn recursive_check_4() {
|
||||
recursive_check(P4);
|
||||
}
|
||||
#[test]
|
||||
fn recursive_check_5() {
|
||||
recursive_check(P5);
|
||||
}
|
||||
#[test]
|
||||
fn recursive_check_6() {
|
||||
recursive_check(P6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn setup() {
|
||||
assert_eq!(Position::new().as_setup().to_string(), P1);
|
||||
assert_eq!(Setup::new().to_string(), "8/8/8/8/8/8/8/8 w - -");
|
||||
assert_eq!(
|
||||
"8/8/8/8/1Pp5/8/R1k5/K7 w - b3"
|
||||
.parse::<Setup>()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
"8/8/8/8/1Pp5/8/R1k5/K7 w - b3",
|
||||
);
|
||||
|
||||
for (record, err) in [
|
||||
("", ParseSetupError::InvalidBoard),
|
||||
(" w - -", ParseSetupError::InvalidBoard),
|
||||
("8/8/8/8/8/8/8 w - -", ParseSetupError::InvalidBoard),
|
||||
("1/1/1/1/1/1/1/1 w - -", ParseSetupError::InvalidBoard),
|
||||
(
|
||||
"44/44/44/44/44/44/44/44 w - -",
|
||||
ParseSetupError::InvalidBoard,
|
||||
),
|
||||
("8/8/8/8/8/8/8/8/8 w - -", ParseSetupError::InvalidBoard),
|
||||
("p8/8/8/8/8/8/8/8 w - -", ParseSetupError::InvalidBoard),
|
||||
("8/8/8/8/8/8/8/8 - - - ", ParseSetupError::InvalidTurn),
|
||||
(
|
||||
"8/8/8/8/8/8/8/8 w QQQQ -",
|
||||
ParseSetupError::InvalidCastlingRights,
|
||||
),
|
||||
] {
|
||||
assert_eq!(record.parse::<Setup>(), Err(err), "{record}");
|
||||
}
|
||||
for (record, reason) in [
|
||||
(
|
||||
"8/8/8/8/8/8/8/8 w KQkq -",
|
||||
IllegalPositionReason::MissingKing,
|
||||
),
|
||||
(
|
||||
"3kk3/8/8/8/8/8/8/3KK3 w KQkq -",
|
||||
IllegalPositionReason::TooManyKings,
|
||||
),
|
||||
(
|
||||
"4k3/8/8/1Q6/8/8/8/4K3 w - -",
|
||||
IllegalPositionReason::HangingKing,
|
||||
),
|
||||
(
|
||||
"4k3/8/3N4/8/8/8/8/4K3 w - -",
|
||||
IllegalPositionReason::HangingKing,
|
||||
),
|
||||
(
|
||||
"4k3/8/8/8/8/8/8/4K2R w KQ -",
|
||||
IllegalPositionReason::InvalidCastlingRights,
|
||||
),
|
||||
(
|
||||
"4k3/8/8/8/8/8/8/P3K3 w KQ -",
|
||||
IllegalPositionReason::PawnOnBackRank,
|
||||
),
|
||||
(
|
||||
"p3k3/8/8/8/8/8/8/4K3 w KQ -",
|
||||
IllegalPositionReason::PawnOnBackRank,
|
||||
),
|
||||
(
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq e3",
|
||||
IllegalPositionReason::InvalidEnPassant,
|
||||
),
|
||||
(
|
||||
"rnbqkbnr/pppppppp/8/8/8/4P3/PPPP1PPP/RNBQKBNR b KQkq e3",
|
||||
IllegalPositionReason::InvalidEnPassant,
|
||||
),
|
||||
(
|
||||
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e3",
|
||||
IllegalPositionReason::InvalidEnPassant,
|
||||
),
|
||||
(
|
||||
"8/8/8/5B2/4P3/3k4/8/4K3 b - e3",
|
||||
IllegalPositionReason::ImpossibleEnPassantPin,
|
||||
),
|
||||
(
|
||||
"8/8/8/3k4/4P3/5B2/8/4K3 b - e3",
|
||||
IllegalPositionReason::ImpossibleEnPassantPin,
|
||||
),
|
||||
(
|
||||
"rnbqkbnr/pppppppp/8/8/4B3/8/PPPPPPPP/RNBQKBNR b KQkq -",
|
||||
IllegalPositionReason::TooMuchMaterial,
|
||||
),
|
||||
(
|
||||
"rnbqkbnr/pppppppp/8/8/8/QBNP4/PPPPPPP1/RNBQKBNR b KQkq -",
|
||||
IllegalPositionReason::TooMuchMaterial,
|
||||
),
|
||||
] {
|
||||
assert!(
|
||||
record.parse::<Setup>().map(|record| record.to_string()) == Ok(record.to_string()),
|
||||
"{record}",
|
||||
);
|
||||
assert!(
|
||||
record
|
||||
.parse::<Setup>()
|
||||
.unwrap()
|
||||
.validate()
|
||||
.is_err_and(|e| e.reasons().contains(reason)),
|
||||
"{record} should be invalid because of {reason:?}",
|
||||
);
|
||||
}
|
||||
|
||||
for record in [
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPB/RNBQKBNR b KQkq -",
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/NNNNNNNN/RNBQKBNR b KQkq -",
|
||||
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3",
|
||||
"3kr3/8/8/8/4P3/8/8/4K3 b - e3",
|
||||
"8/8/8/3k4/3P4/8/8/3RK3 b - d3",
|
||||
] {
|
||||
assert!(record.parse::<Setup>().is_ok(), "{record}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mirror() {
|
||||
assert_eq!(Position::new().pass(), Some(Position::new().mirror()));
|
||||
let position = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b Kq e3"
|
||||
.parse::<Setup>()
|
||||
.unwrap()
|
||||
.validate()
|
||||
.unwrap();
|
||||
let mirror = "rnbqkbnr/pppp1ppp/8/4p3/8/8/PPPPPPPP/RNBQKBNR w Qk e6"
|
||||
.parse::<Setup>()
|
||||
.unwrap()
|
||||
.validate()
|
||||
.unwrap();
|
||||
assert_eq!(mirror, position.mirror());
|
||||
}
|
||||
|
||||
fn perft_aux(record: &str, tests: &[u128]) {
|
||||
let position = record.parse::<Setup>().unwrap().validate().unwrap();
|
||||
for (depth, value) in tests.iter().copied().enumerate() {
|
||||
assert_eq!(
|
||||
position.perft(depth),
|
||||
value,
|
||||
"\"{record}\" at depth {depth}",
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn perft_1() {
|
||||
perft_aux(P1, &[1, 20, 400, 8_902, 197_281, 4_865_609, 119_060_324]);
|
||||
}
|
||||
#[test]
|
||||
fn perft_2() {
|
||||
perft_aux(P2, &[1, 48, 2039, 97_862, 4_085_603, 193_690_690]);
|
||||
}
|
||||
#[test]
|
||||
fn perft_3() {
|
||||
perft_aux(
|
||||
P3,
|
||||
&[1, 14, 191, 2_812, 43_238, 674_624, 11_030_083, 178_633_661],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn perft_4() {
|
||||
perft_aux(P4, &[1, 6, 264, 9_467, 422_333, 15_833_292]);
|
||||
}
|
||||
#[test]
|
||||
fn perft_5() {
|
||||
perft_aux(P5, &[1, 44, 1_486, 62_379, 2_103_487, 89_941_194]);
|
||||
}
|
||||
#[test]
|
||||
fn perft_6() {
|
||||
perft_aux(P6, &[1, 46, 2_079, 89_890, 3_894_594, 164_075_551]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn san() {
|
||||
let position = "8/2KN1p2/5p2/3N1B1k/5PNp/7P/7P/8 w - -"
|
||||
.parse::<Setup>()
|
||||
.unwrap()
|
||||
.validate()
|
||||
.unwrap();
|
||||
let san1 = "N7xf6#".parse::<San>().unwrap();
|
||||
let m1 = san1.to_move(&position).unwrap();
|
||||
let san2 = "N5xf6#".parse::<San>().unwrap();
|
||||
let m2 = san2.to_move(&position).unwrap();
|
||||
let san3 = "Ngxf6+".parse::<San>().unwrap();
|
||||
let m3 = san3.to_move(&position).unwrap();
|
||||
assert_eq!(m1.to_san(), san1);
|
||||
assert_eq!(m2.to_san(), san2);
|
||||
assert_eq!(m3.to_san(), san3);
|
||||
assert_eq!(
|
||||
"Nd7f6"
|
||||
.parse::<San>()
|
||||
.unwrap()
|
||||
.to_move(&position)
|
||||
.unwrap()
|
||||
.to_san(),
|
||||
san1,
|
||||
);
|
||||
assert_eq!(
|
||||
"Nf6".parse::<San>().unwrap().to_move(&position).map(|_| ()),
|
||||
Err(InvalidSan::Ambiguous),
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue