use super::helpers::{apply_usi_move, move_from_usi};
use super::*;
use crate::types::{Color, HandPiece, PieceType, RepetitionState};
#[test]
fn test_repetition_state_initializes_to_zero() {
let pos = crate::board::hirate_position();
assert_eq!(pos.state_stack().current().repetition_counter, 0);
assert_eq!(pos.state_stack().current().plies_from_null, 0);
}
#[test]
fn test_plies_from_null_increments_without_events() {
let mut pos = crate::board::hirate_position();
apply_usi_move(&mut pos, "2g2f");
assert_eq!(pos.state_stack().current().plies_from_null, 1);
apply_usi_move(&mut pos, "8c8d");
assert_eq!(pos.state_stack().current().plies_from_null, 2);
}
#[test]
fn test_plies_from_null_does_not_reset_on_capture() {
let mut pos = crate::board::hirate_position();
for usi in ["7g7f", "8c8d", "8g8f", "8d8e"] {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.state_stack().current().plies_from_null, 4);
apply_usi_move(&mut pos, "8f8e");
assert_eq!(pos.state_stack().current().plies_from_null, 5);
assert_ne!(pos.state_stack().current().cold.captured_piece(), Piece::NONE);
}
#[test]
fn test_plies_from_null_does_not_reset_on_drop() {
let mut pos = crate::board::hirate_position();
for usi in ["7g7f", "3c3d", "8h2b+", "3a2b"] {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.state_stack().current().plies_from_null, 4);
assert_eq!(pos.turn(), Color::BLACK);
let hand_piece = HandPiece::from_piece_type(PieceType::BISHOP).unwrap();
let count_of = pos.hand(pos.turn()).count(hand_piece);
assert_eq!(count_of, 1);
let drop_mv = move_from_usi(&pos, "B*4e");
pos.apply_move32(drop_mv);
assert_eq!(pos.state_stack().current().plies_from_null, 5);
}
#[test]
fn test_repetition_counter_increments_when_position_repeats() {
let mut pos = crate::board::hirate_position();
let sequences = ["2h3h", "8b7b", "3h2h", "7b8b"];
let initial_zobrist = pos.key();
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.key(), initial_zobrist);
assert_eq!(pos.state_stack().current().repetition_counter, 1);
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.key(), initial_zobrist);
assert_eq!(pos.state_stack().current().repetition_counter, 2);
}
#[test]
fn test_is_repetition_detects_threefold() {
let mut pos = crate::board::hirate_position();
assert!(!pos.is_repetition(1));
assert!(!pos.is_repetition(3));
let sequences = ["2h3h", "8b7b", "3h2h", "7b8b"];
for repeat in 0..3 {
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
match repeat {
0 => {
assert_eq!(pos.state_stack().current().repetition_counter, 1);
assert!(!pos.is_repetition(3));
}
1 => {
assert_eq!(pos.state_stack().current().repetition_counter, 2);
assert!(!pos.is_repetition(3));
}
_ => {}
}
}
assert!(pos.is_repetition(3));
}
#[test]
fn test_clone_for_search_truncates_history_and_preserves_current_state() {
let mut pos = crate::board::hirate_position();
let moves = [
"7g7f", "3c3d", "2g2f", "4c4d", "3i4h", "3a4b", "5g5f", "5c5d", "4h5g", "4b5c", "5g4f",
"5c4b", "4f5g", "4b5c", "5g4f", "5c4b", "4f5g", "4b5c", "5g4f", "5c4b",
];
for usi in moves {
apply_usi_move(&mut pos, usi);
}
let cloned = pos.clone_for_search();
assert_eq!(cloned.to_sfen(None), pos.to_sfen(None));
assert_eq!(cloned.key(), pos.key());
assert_eq!(cloned.board_key(), pos.board_key());
assert_eq!(cloned.hand_key(), pos.hand_key());
assert_eq!(cloned.checkers(), pos.checkers());
assert_eq!(cloned.last_move(), pos.last_move());
assert_eq!(cloned.last_moved_piece_type(), pos.last_moved_piece_type());
assert_eq!(cloned.repetition_counter(), pos.repetition_counter());
assert_eq!(cloned.repetition_distance(), pos.repetition_distance());
assert_eq!(cloned.state_stack_depth(), 16);
assert!(cloned.state_stack_depth() < pos.state_stack_depth());
}
#[test]
fn test_clone_for_search_keeps_enough_history_for_repetition() {
let mut pos = crate::board::hirate_position();
let loop_moves = ["2h3h", "8b7b", "3h2h", "7b8b", "2h3h", "8b7b", "3h2h", "7b8b"];
for usi in loop_moves {
apply_usi_move(&mut pos, usi);
}
let mut cloned = pos.clone_for_search();
for usi in ["2h3h", "8b7b", "3h2h", "7b8b"] {
apply_usi_move(&mut cloned, usi);
}
assert_eq!(cloned.repetition_state(), RepetitionState::Draw);
}
#[test]
fn test_continuous_check_counter_increments_on_check() {
let sfen = "lr2+R2G1/6g1k/p3ppppp/2p1b4/9/2P+b4P/P+p3PPP1/4G1SK1/LN3+p1NL b 2SNLgsn3p 1";
let mut pos = crate::board::position_from_sfen(sfen).expect("valid sfen");
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 0u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
apply_usi_move(&mut pos, "2a1a");
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 2u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
apply_usi_move(&mut pos, "1b2b");
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 2u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
apply_usi_move(&mut pos, "1a2a");
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 4u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
apply_usi_move(&mut pos, "2b1b");
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 4u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
}
#[test]
fn test_continuous_check_counter_stays_zero_on_non_check() {
let mut pos = crate::board::hirate_position();
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 0u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
apply_usi_move(&mut pos, "7g7f");
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 0u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
apply_usi_move(&mut pos, "8c8d");
assert_eq!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()], 0u16);
assert_eq!(pos.state_stack().current().continuous_check[Color::WHITE.to_index()], 0u16);
}
#[test]
fn test_get_repetition_state_detects_normal_repetition() {
let mut pos = crate::board::hirate_position();
assert_eq!(pos.repetition_state(), RepetitionState::None);
let sequences = ["2h3h", "8b7b", "3h2h", "7b8b"];
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.repetition_state(), RepetitionState::None);
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.repetition_state(), RepetitionState::None);
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.repetition_state(), RepetitionState::Draw);
}
#[test]
fn test_cached_repetition_state_with_ply_uses_signed_cached_distance() {
let mut pos = crate::board::hirate_position();
for usi in ["2h3h", "8b7b", "3h2h", "7b8b"] {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.repetition_counter(), 1);
assert_eq!(pos.repetition_distance(), 4);
assert_eq!(pos.current_state().repetition_type, RepetitionState::Draw);
assert_eq!(pos.cached_repetition_state_with_ply(4), RepetitionState::None);
assert_eq!(pos.cached_repetition_state_with_ply(5), RepetitionState::Draw);
assert_eq!(pos.repetition_state_with_ply(4), RepetitionState::Draw);
}
#[test]
fn test_cached_repetition_state_with_ply_returns_fourfold_even_at_ply_zero() {
let mut pos = crate::board::hirate_position();
for _ in 0..3 {
for usi in ["2h3h", "8b7b", "3h2h", "7b8b"] {
apply_usi_move(&mut pos, usi);
}
}
assert_eq!(pos.repetition_counter(), 3);
assert_eq!(pos.repetition_distance(), -4);
assert_eq!(pos.cached_repetition_state_with_ply(0), RepetitionState::Draw);
}
#[test]
fn test_get_repetition_state_detects_perpetual_check_by_black() {
let sfen = "lr2+R2G1/6g1k/p3ppppp/2p1b4/9/2P+b4P/P+p3PPP1/4G1SK1/LN3+p1NL b 2SNLgsn3p 1";
let mut pos = crate::board::position_from_sfen(sfen).expect("valid sfen");
assert_eq!(pos.repetition_state(), RepetitionState::None);
let sequences = ["2a1a", "1b2b", "1a2a", "2b1b"];
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.repetition_state(), RepetitionState::None);
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.repetition_state(), RepetitionState::None);
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.repetition_state(), RepetitionState::Lose);
}
#[test]
fn test_repetition_counter_thresholds() {
let mut pos = crate::board::hirate_position();
let sequences = ["2h3h", "8b7b", "3h2h", "7b8b"];
let initial_zobrist = pos.key();
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.key(), initial_zobrist);
assert_eq!(pos.state_stack().current().repetition_counter, 1);
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.key(), initial_zobrist);
assert_eq!(pos.state_stack().current().repetition_counter, 2);
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.key(), initial_zobrist);
assert_eq!(pos.state_stack().current().repetition_counter, 3);
assert_eq!(pos.repetition_state(), RepetitionState::Draw);
assert!(!pos.is_repetition(4)); assert!(pos.is_repetition(3)); assert!(pos.is_repetition(2)); assert!(pos.is_repetition(1)); }
#[test]
fn test_continuous_check_repetition() {
let sfen = "lr2+R2G1/6g1k/p3ppppp/2p1b4/9/2P+b4P/P+p3PPP1/4G1SK1/LN3+p1NL b 2SNLgsn3p 1";
let mut pos = crate::board::position_from_sfen(sfen).expect("valid sfen");
let sequences = ["2a1a", "1b2b", "1a2a", "2b1b"];
for _ in 0..3 {
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
}
assert_eq!(pos.repetition_state(), RepetitionState::Lose);
assert!(pos.state_stack().current().continuous_check[Color::BLACK.to_index()] > 0);
}
#[test]
fn test_superior_inferior_state_variants() {
let pos = crate::board::hirate_position();
let _ = (RepetitionState::Superior, RepetitionState::Inferior);
assert_eq!(pos.repetition_state(), RepetitionState::None);
}
#[test]
fn test_plies_from_null_progression() {
let mut pos = crate::board::hirate_position();
apply_usi_move(&mut pos, "7g7f");
assert_eq!(pos.state_stack().current().plies_from_null, 1);
apply_usi_move(&mut pos, "8c8d");
assert_eq!(pos.state_stack().current().plies_from_null, 2);
apply_usi_move(&mut pos, "8g8f");
apply_usi_move(&mut pos, "8d8e");
apply_usi_move(&mut pos, "8f8e"); assert_eq!(pos.state_stack().current().plies_from_null, 5);
}
#[test]
fn test_repetition_state_detects_superior_inferior() {
let sfen = "lr6l/4g1pkp/2ns1s3/4pp1SP/PPBP2Pp1/2gpPP3/2B2S3/2K6/L6RL b 4P2g3np 1";
let mut pos = crate::board::position_from_sfen(sfen).expect("valid sfen");
for usi in ["P*2c", "2b2a", "2c2b+", "2a2b"] {
apply_usi_move(&mut pos, usi);
}
assert_eq!(pos.turn(), Color::BLACK);
assert_eq!(pos.repetition_state(), RepetitionState::Inferior);
}
#[test]
fn test_repetition_state_handles_broken_perpetual_check() {
let sfen = "kns5l/lsp2+R3/1p2B3+P/p1+S+B4p/9/3pP3P/PPG1+pP3/L7s/KGg+r5 b G3NL4P2p 1";
let mut pos = crate::board::position_from_sfen(sfen).expect("valid sfen");
let sequences = ["G*8h", "7i8i", "8h8i", "G*7i"];
for _ in 0..3 {
for usi in sequences {
apply_usi_move(&mut pos, usi);
}
}
assert_eq!(pos.repetition_state(), RepetitionState::Draw);
}