117 lines
3.5 KiB
Rust
117 lines
3.5 KiB
Rust
use crate::{
|
|
agent::Agent,
|
|
board::{Board, Winner},
|
|
piece::Piece,
|
|
};
|
|
|
|
pub struct Game {
|
|
players: [Box<dyn Agent>; 2],
|
|
board: Board,
|
|
}
|
|
|
|
impl std::fmt::Display for Game {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
writeln!(
|
|
f,
|
|
"Players: {} ({}) and {} ({})",
|
|
self.players[0].name(),
|
|
self.players[0].color().symbol(),
|
|
self.players[1].name(),
|
|
self.players[1].color().symbol()
|
|
)?;
|
|
write!(f, "{}", self.board)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl Game {
|
|
pub fn new(player1: Box<dyn Agent>, player2: Box<dyn Agent>) -> Self {
|
|
let player1_color = player1.color();
|
|
let player2_color = player2.color();
|
|
assert_ne!(
|
|
player1_color,
|
|
player2_color,
|
|
"Both players cannot have the same color {}",
|
|
player1_color.text()
|
|
);
|
|
|
|
assert_eq!(player1_color, Piece::Black, "player 1 must playing black");
|
|
assert_eq!(player2_color, Piece::White, "player 2 must play white");
|
|
|
|
Self {
|
|
players: [player1, player2],
|
|
board: Board::new().starting_pos(),
|
|
}
|
|
}
|
|
|
|
// Handle when a move is made
|
|
pub fn step(&mut self, player_i: usize) {
|
|
let player_color = self.players[player_i].color();
|
|
|
|
// TODO! move this to `ManualAgent` as it's only really useful
|
|
// when it comes to human input
|
|
loop {
|
|
let player_move = self.players[player_i].next_move(&self.board);
|
|
|
|
if let Some((i, j)) = player_move {
|
|
match self.board.place(i, j, player_color) {
|
|
// Check if its a valid move
|
|
Ok(_) => {
|
|
println!("Player {} placed at ({}, {})", player_i, i, j);
|
|
break;
|
|
}
|
|
|
|
// Lets the player try again if the move is invalid
|
|
// Now we dont need to restart the game if we mess up
|
|
Err(err) => {
|
|
panic!(
|
|
"Invalid move by Player {}: {}. Try again. Move: ({}, {})",
|
|
player_i, err, i, j
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
println!("Player {} did not make a move!", player_i);
|
|
// players are able to skip a move if they have no valid moves to make
|
|
if self.board.possible_moves(player_color).any(|_| true) {
|
|
println!(
|
|
"Player {} has possible moves, but skipped (invalid)",
|
|
player_i
|
|
);
|
|
continue;
|
|
}
|
|
return; // No valid move available
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO! make this loop good
|
|
pub fn game_loop(&mut self) {
|
|
let mut current_player: usize = 0;
|
|
|
|
loop {
|
|
// alternate which player plays
|
|
|
|
current_player %= self.players.len();
|
|
|
|
println!("{}", self);
|
|
|
|
match self.board.game_winner() {
|
|
Winner::Player(piece) => {
|
|
println!("{} Wins!", piece.text());
|
|
break;
|
|
}
|
|
Winner::Tie => {
|
|
println!("Game Tied!");
|
|
break;
|
|
}
|
|
Winner::None => {}
|
|
}
|
|
|
|
self.step(current_player);
|
|
current_player += 1;
|
|
}
|
|
}
|
|
}
|