use crate::bitboard::Bitboard;
use crate::constants::{NUM_SHAPES, WIN_MASKS};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WinStatus {
NoWin,
Player0Wins,
Player1Wins,
}
pub fn has_winning_line(bb: &Bitboard) -> bool {
let shape_unions: [u16; NUM_SHAPES] = std::array::from_fn(|s| bb.planes[s] | bb.planes[s + 4]);
WIN_MASKS
.iter()
.any(|&mask| shape_unions.iter().all(|&su| su & mask != 0))
}
pub fn check_winner(bb: &Bitboard) -> WinStatus {
if !has_winning_line(bb) {
return WinStatus::NoWin;
}
let p0 = bb.player_piece_count(0);
let p1 = bb.player_piece_count(1);
if p0 > p1 {
WinStatus::Player0Wins
} else {
WinStatus::Player1Wins
}
}
pub fn current_player(bb: &Bitboard) -> Option<u8> {
let p0 = bb.player_piece_count(0);
let p1 = bb.player_piece_count(1);
if p0 == p1 {
Some(0)
} else if p0 == p1 + 1 {
Some(1)
} else {
None
}
}
pub fn is_game_over(bb: &Bitboard) -> bool {
check_winner(bb) != WinStatus::NoWin
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_board_no_win() {
assert_eq!(check_winner(&Bitboard::EMPTY), WinStatus::NoWin);
assert_eq!(current_player(&Bitboard::EMPTY), Some(0));
}
#[test]
fn row_0_win() {
let bb = Bitboard::EMPTY
.with_move(0, 0, 0) .with_move(1, 1, 1) .with_move(0, 2, 2) .with_move(1, 3, 3); assert!(has_winning_line(&bb));
assert_eq!(check_winner(&bb), WinStatus::Player1Wins);
}
#[test]
fn current_player_alternates() {
let bb = Bitboard::EMPTY;
assert_eq!(current_player(&bb), Some(0));
let bb = bb.with_move(0, 0, 0);
assert_eq!(current_player(&bb), Some(1));
let bb = bb.with_move(1, 1, 5);
assert_eq!(current_player(&bb), Some(0));
}
}