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 { 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 { 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 { 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(); } } }