use crate::domain::piece::Piece;
pub const HALF_MOVE_LIMIT: u32 = 100;
#[inline]
#[must_use]
pub fn clock_resets(board_mover: Option<Piece>, captured: bool) -> bool {
captured || matches!(board_mover, Some(piece) if piece.is_foot_soldier())
}
#[inline]
#[must_use]
pub fn advanced_clock(current: u32, board_mover: Option<Piece>, captured: bool) -> u32 {
if clock_resets(board_mover, captured) {
0
} else {
current.saturating_add(1)
}
}
#[inline]
#[must_use]
pub const fn limit_reached(half_moves: u32) -> bool {
half_moves >= HALF_MOVE_LIMIT
}
#[cfg(test)]
mod tests {
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing
)]
use super::{advanced_clock, clock_resets, limit_reached, HALF_MOVE_LIMIT};
use crate::domain::piece::Piece;
use sashite_epin::Identifier as Epin;
fn piece(token: &str) -> Piece {
Piece::new(Epin::parse(token).expect("valid EPIN"))
}
#[test]
fn capture_resets() {
assert!(clock_resets(Some(piece("R")), true));
assert_eq!(advanced_clock(42, Some(piece("R")), true), 0);
}
#[test]
fn foot_soldier_move_resets() {
for token in ["P", "f", "S"] {
assert!(clock_resets(Some(piece(token)), false), "{token}");
}
assert_eq!(advanced_clock(42, Some(piece("P")), false), 0);
}
#[test]
fn non_foot_soldier_move_without_capture_increments() {
assert!(!clock_resets(Some(piece("R")), false));
assert_eq!(advanced_clock(42, Some(piece("R")), false), 43);
assert!(!clock_resets(Some(piece("T")), false));
assert_eq!(advanced_clock(7, Some(piece("t")), false), 8);
}
#[test]
fn drop_increments_without_reset() {
assert!(!clock_resets(None, false));
assert_eq!(advanced_clock(42, None, false), 43);
}
#[test]
fn threshold() {
assert!(!limit_reached(99));
assert!(limit_reached(HALF_MOVE_LIMIT));
assert!(limit_reached(101));
}
#[test]
fn saturating_advance() {
assert_eq!(advanced_clock(u32::MAX, Some(piece("R")), false), u32::MAX);
}
}