Files
checkers/src/logic/game_master.rs

226 lines
6.4 KiB
Rust

use log::{info, warn};
use serde::{Deserialize, Serialize};
use crate::logic::{
board::{
board::Board,
pawn::{CapturePath, MovePath},
},
math::position::Position,
player::Player,
};
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)]
pub enum GameResult {
Lost(uuid::Uuid),
Draw,
}
struct PlayerList {
pub players: [Player; 2],
current_turn: usize,
size: usize,
}
impl PlayerList {
pub fn new(player1_name: String, player2_name: String) -> Self {
PlayerList {
players: [Player::new(player1_name), Player::new(player2_name)],
current_turn: 0,
size: 2,
}
}
pub fn change_turn(&mut self) {
self.current_turn = (self.current_turn + 1) % self.size;
}
pub fn get_current_turn(&self) -> &Player {
&self.players[self.current_turn]
}
pub fn get_passive_player(&self) -> &Player {
&self.players[(self.current_turn + 1) % self.size]
}
}
pub struct GameMaster {
players: PlayerList,
board: Board,
}
impl GameMaster {
pub fn new(player1_name: String, player2_name: String) -> Self {
let mut players: PlayerList = PlayerList::new(player1_name, player2_name);
let [player1, player2] = &mut players.players;
let board = Board::new(player1, player2, None);
GameMaster { players, board }
}
pub fn get_current_player_captures(&self) -> Vec<CapturePath> {
self.board
.get_player_pawns_positions(self.players.get_current_turn())
.into_iter()
.filter_map(|pos| {
self.board
.get_available_captures(pos, self.players.get_current_turn())
})
.flatten()
.collect()
}
pub fn get_current_player_moves(&self) -> Vec<MovePath> {
self.board
.get_player_pawns_positions(self.players.get_current_turn())
.into_iter()
.filter_map(|pos| {
self.board
.get_available_moves(pos, self.players.get_current_turn())
})
.flatten()
.collect()
}
fn is_player_lost(&self, player: &Player) -> bool {
let player_pawns = self.board.get_player_pawns_positions(player);
player_pawns.len() == 0
|| player_pawns.iter().all(|pos| {
self.board.get_available_moves(*pos, player).is_none()
&& self.board.get_available_captures(*pos, player).is_none()
})
}
pub fn get_game_result(&self) -> Option<GameResult> {
let active: &Player = self.players.get_current_turn();
let passive: &Player = self.players.get_passive_player();
let active_lost: bool = self.is_player_lost(active);
let passive_lost: bool = self.is_player_lost(passive);
if active_lost && passive_lost {
info!("Game ended at a draw!");
Some(GameResult::Draw)
} else if active_lost {
info!("Player: {} won", passive.id);
Some(GameResult::Lost(active.id))
} else if passive_lost {
info!("Player: {} won", active.id);
Some(GameResult::Lost(passive.id))
} else {
None
}
}
pub fn move_pawn(&mut self, from: Position, to: Position) -> bool {
if self.get_current_player_moves().is_empty() {
warn!(
"Player: {} tried moving, but they don't have any moves available!",
self.players.get_current_turn().id
);
return false;
}
if !self.get_current_player_captures().is_empty() {
warn!(
"Player: {} tried moving, but he has captures available!",
self.players.get_current_turn().id
);
return false;
}
if self
.board
.move_pawn(self.players.get_current_turn(), from, to)
{
self.players.change_turn();
return true;
}
false
}
pub fn capture(&mut self, path: &CapturePath) -> bool {
if self.get_current_player_captures().is_empty() {
warn!(
"Player: {} tried capturing, but he doesn't have any captures available!",
self.players.get_current_turn().id
);
return false;
}
if self.board.capture(path, self.players.get_current_turn()) {
self.players.change_turn();
return true;
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use log::info;
use rand::seq::IndexedMutRandom;
use rand::seq::IndexedRandom;
fn run_game() {
let mut master: GameMaster = GameMaster::new("Morbius".to_string(), "Milo".to_string());
let mut turns = 0;
let max_turns = 2000;
while turns < max_turns {
if let Some(result) = master.get_game_result() {
match result {
GameResult::Lost(loser_id) => {
info!("Player: {} lost! Ending the loop", loser_id);
}
GameResult::Draw => {
info!("Game ended in a draw! Ending the loop");
}
}
break;
}
let mut rng = rand::rng();
let available_captures = master.get_current_player_captures();
if !available_captures.is_empty() {
let capture = available_captures.choose(&mut rng).unwrap();
master.capture(capture);
turns += 1;
continue;
}
let mut available_moves = master.get_current_player_moves();
if !available_moves.is_empty() {
let path = available_moves.choose_mut(&mut rng).unwrap();
let from = path.from;
let to = path.available_steps.choose_mut(&mut rng).unwrap();
master.move_pawn(from, *to);
turns += 1;
continue;
}
panic!("Test should never come to this place!");
}
assert!(turns < max_turns);
}
#[test]
fn test_game_flow() {
// random amount i pulled straight out of my ass
let game_amount = 100;
for game_number in 0..game_amount {
info!("Running game number: {}", game_number);
run_game();
}
}
}