mod analysis;
mod dev;
mod saturation;
mod utils;
pub(crate) use analysis::Analysis;
use chess::{BitBoard, Board, Color, Piece};
pub(crate) fn is_statically_unwinnable(board: &Board) -> bool {
if board.en_passant().is_some() {
return false;
}
let mut analysis = Analysis::new(*board);
let white = board.color_combined(Color::White);
if white.popcnt() == 2 {
let last_white_piece = white & !board.pieces(Piece::King);
analysis.uncapturable_while_steady = Some(last_white_piece);
}
analysis.saturate();
if !analysis.black_could_get_mated() {
return true;
}
if let Some(squares) = analysis.first_steady_capturable {
for s in squares {
let mut analysis = Analysis::new(*board);
analysis.uncapturable_while_steady = Some(BitBoard::from_square(s));
analysis.saturate();
if analysis.black_could_get_mated() {
continue;
}
let mut analysis = Analysis::new(*board);
analysis.capturable_only_while_steady = Some(BitBoard::from_square(s));
analysis.saturate();
if !analysis.black_could_get_mated() {
return true;
}
}
}
if let Some(squares) = analysis.first_steady_capturable {
if squares.popcnt() == 2 {
let s1 = squares.to_square();
let s2 = (squares & !BitBoard::from_square(s1)).to_square();
let mut analysis = Analysis::new(*board);
analysis.uncapturable_while_steady = Some(squares);
analysis.saturate();
if analysis.black_could_get_mated() {
return false;
}
let mut analysis = Analysis::new(*board);
analysis.uncapturable_while_steady = Some(BitBoard::from_square(s1));
analysis.capturable_only_while_steady = Some(BitBoard::from_square(s2));
analysis.saturate();
if analysis.black_could_get_mated() {
return false;
}
let mut analysis = Analysis::new(*board);
analysis.capturable_only_while_steady = Some(BitBoard::from_square(s1));
analysis.uncapturable_while_steady = Some(BitBoard::from_square(s2));
analysis.saturate();
if analysis.black_could_get_mated() {
return false;
}
let mut analysis = Analysis::new(*board);
analysis.capturable_only_while_steady = Some(squares);
analysis.saturate();
return !analysis.black_could_get_mated();
}
}
false
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use chess::Board;
use super::*;
#[test]
fn test_statically_unwinnable() {
saturation::tests::all_test_cases().iter().for_each(|test_case| {
let board = Board::from_str(&format!("{} w - -", test_case.fen)).unwrap();
assert_eq!(is_statically_unwinnable(&board), !test_case.winnable);
});
let board = Board::from_str("B2b4/8/4k3/8/1p1p1p1p/1PpP1P1P/K1P4b/RB6 w - -").unwrap();
assert!(is_statically_unwinnable(&board));
let board = Board::from_str("1kb5/1p1p4/1P1P2p1/6p1/6Pb/6p1/6P1/7K w - -").unwrap();
assert!(is_statically_unwinnable(&board));
let board = Board::from_str("8/6R1/8/6p1/2N2pP1/PPPP4/QNP2P1N/1Bk1K2R b - g3").unwrap();
assert!(!is_statically_unwinnable(&board));
}
}