use crate::chess_game_move_analyzer;
use crate::piece::PieceType::King;
use crate::piece::{ChessPiece, PieceType};
use crate::ChessMoveType::Move;
use crate::{ChessGame, ChessMoveType, Color};
use game_board::Board;
#[derive(Debug, PartialEq)]
pub enum GameState {
InProgress {
legal_moves: Vec<ChessMoveType>,
turn: Color,
},
Check {
legal_moves: Vec<ChessMoveType>,
turn: Color,
},
Checkmate {
winner: Color,
},
Stalemate,
}
pub fn get_game_state(game: &mut ChessGame) -> GameState {
let legal_moves = chess_game_move_analyzer::get_legal_moves(game);
if is_in_check(game.get_current_players_turn(), game.get_board()) {
if legal_moves.is_empty() {
GameState::Checkmate {
winner: game.get_current_players_turn().opposite(),
}
} else {
GameState::Check {
legal_moves,
turn: game.get_current_players_turn(),
}
}
} else {
if legal_moves.is_empty() {
GameState::Stalemate
} else {
GameState::InProgress {
legal_moves,
turn: game.get_current_players_turn(),
}
}
}
}
pub fn is_in_check(color: Color, board: &Board<ChessPiece>) -> bool {
for row in 0..board.get_height() {
for col in 0..board.get_width() {
if let Some(piece) = board.get_piece_at_space(col, row) {
if piece.get_color() == color.opposite() {
let moves = piece.possible_moves((col, row), board, None);
for m in moves {
match m {
Move { taken_piece, .. } => {
if let Some(taken_piece) = taken_piece {
if taken_piece.get_piece_type() == King {
return true;
}
}
}
_ => return false,
}
}
}
}
}
}
false
}
pub fn is_insufficient_material(board: &Board<ChessPiece>) -> bool {
let mut white_pieces = vec![];
let mut black_pieces = vec![];
for col in 0..board.get_width() {
for row in 0..board.get_height() {
if let Some(piece) = board.get_piece_at_space(col, row) {
match piece.get_color() {
Color::White => white_pieces.push(piece),
Color::Black => black_pieces.push(piece),
}
}
}
}
let check = |pieces: &Vec<&ChessPiece>| -> bool {
if pieces.len() < 2 {
return true;
} else if pieces.len() == 2 {
let piece_type_a = pieces[0].get_piece_type();
let piece_type_b = pieces[1].get_piece_type();
let other = if piece_type_a == King {
piece_type_b
} else {
piece_type_a
};
return matches!(other, PieceType::Knight | PieceType::Bishop);
}
false
};
check(&white_pieces) && check(&black_pieces)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::chess_game_state_analyzer::GameState::{Check, Checkmate, InProgress, Stalemate};
use crate::codec::forsyth_edwards_notation::build_game_from_string;
use crate::Color::{Black, White};
#[test]
fn game_with_starting_position_is_in_progress() {
let mut game = ChessGame::new();
match get_game_state(&mut game) {
InProgress { legal_moves, turn } => {
assert_eq!(legal_moves.len(), 20);
assert_eq!(White, turn);
}
_ => panic!("Game state is not in progress."),
};
}
#[test]
fn game_in_check_has_legal_moves() {
let mut game = build_game_from_string("4k3/8/8/8/8/8/8/r3R3 b - - 0 1").unwrap();
match get_game_state(&mut game) {
Check { legal_moves, turn } => {
assert_eq!(turn, Black);
assert_eq!(5, legal_moves.len())
}
_ => panic!("Game state is not in progress."),
};
}
#[test]
fn game_is_in_stalemate() {
let mut game = build_game_from_string("k7/7R/8/8/8/8/8/1RK5 b - - 0 1").unwrap();
match get_game_state(&mut game) {
Stalemate {} => (),
_ => panic!("Game state is not in progress."),
}
}
#[test]
fn game_is_in_check_mate() {
let mut game = build_game_from_string("k6R/pp6/8/8/8/8/8/8 b - - 0 1").unwrap();
match get_game_state(&mut game) {
Checkmate { winner } => assert_eq!(White, winner),
_ => panic!("Game state is not in progress."),
}
}
#[test]
fn game_with_starting_position_has_sufficient_material() {
let game = ChessGame::new();
let insufficient_material = is_insufficient_material(game.get_board());
assert!(!insufficient_material);
}
#[test]
fn one_player_has_insufficient_material_the_other_does_not() {
let game = build_game_from_string("k7/8/8/8/8/8/8/KQ6 b - - 0 1").unwrap();
let insufficient_material = is_insufficient_material(game.get_board());
assert!(!insufficient_material);
}
#[test]
fn only_kings_is_insufficient_material() {
let game = build_game_from_string("k7/8/8/8/8/8/8/K7 b - - 0 1").unwrap();
assert!(is_insufficient_material(game.get_board()));
}
#[test]
fn king_and_bishop_or_knight_is_insufficient_material() {
let game = build_game_from_string("k7/8/bN6/8/8/8/8/K7 b - - 0 1").unwrap();
assert!(is_insufficient_material(game.get_board()));
}
}