allow the game to end in cases of a non-full board
This commit is contained in:
parent
5c27572f9f
commit
ec0eb7f849
28
src/board.rs
28
src/board.rs
@ -48,14 +48,6 @@ impl fmt::Display for Board {
|
|||||||
white_score, black_score
|
white_score, black_score
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Print game over screen
|
|
||||||
if self.game_over() {
|
|
||||||
match self.get_winner() {
|
|
||||||
Some(piece) => writeln!(f, "{} Wins", piece.text()),
|
|
||||||
None => writeln!(f, "Tie"),
|
|
||||||
}?
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,24 +199,24 @@ impl Board {
|
|||||||
.fold((0_usize, 0usize), |(a, b), (c, d)| (a + c, b + d))
|
.fold((0_usize, 0usize), |(a, b), (c, d)| (a + c, b + d))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the winning piece (for game over screen mainly)
|
pub fn game_winner(&self, turn: Piece) -> Option<Piece> {
|
||||||
pub fn get_winner(&self) -> Option<Piece> {
|
|
||||||
let (white_score, black_score) = self.get_score();
|
let (white_score, black_score) = self.get_score();
|
||||||
|
let max_score = BOARD_SIZE * BOARD_SIZE;
|
||||||
|
let combined_score = black_score + white_score;
|
||||||
|
if max_score != combined_score {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if current player cannot make a move
|
||||||
|
if self.possible_moves(turn).count() > 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
match white_score.cmp(&black_score) {
|
match white_score.cmp(&black_score) {
|
||||||
Ordering::Greater => Some(Piece::White), // White win
|
Ordering::Greater => Some(Piece::White), // White win
|
||||||
Ordering::Less => Some(Piece::Black), // Black win
|
Ordering::Less => Some(Piece::Black), // Black win
|
||||||
Ordering::Equal => None, // Tie
|
Ordering::Equal => None, // Tie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn game_over(&self) -> bool {
|
|
||||||
let (white_score, black_score) = self.get_score();
|
|
||||||
let max_score = BOARD_SIZE * BOARD_SIZE;
|
|
||||||
let combined_score = black_score + white_score;
|
|
||||||
|
|
||||||
max_score == combined_score
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -30,39 +30,43 @@ impl Move {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn value(&self, agent_color: Piece, depth: usize) -> i64 {
|
fn value(&self, agent_color: Piece, depth: usize) -> i64 {
|
||||||
let mut captured_value = self.captured as i64;
|
let mut self_value = self.captured as i64;
|
||||||
|
|
||||||
if self.board.game_over() && self.board.get_winner() != Some(!agent_color) {
|
if self.board.game_winner(agent_color) != Some(!agent_color) {
|
||||||
// if this board results in the opponent winning, MAJORLY negatively weigh this move
|
// if this board results in the opponent winning, MAJORLY negatively weigh this move
|
||||||
captured_value = i64::MIN;
|
// NOTE! this branch isn't completely deleted because if so, the bot wouldn't make a move.
|
||||||
|
// We shouldn't prune branches because we still need to always react to the opponent's moves
|
||||||
|
self_value = i64::MIN;
|
||||||
} else if agent_color != self.color {
|
} else if agent_color != self.color {
|
||||||
captured_value = -captured_value;
|
self_value = -self_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduce value of capture based on depth of prediction
|
// Reduce value of capture based on depth of prediction
|
||||||
captured_value /= depth as i64;
|
self_value /= depth as i64;
|
||||||
|
|
||||||
let avg_next_move_value = (self
|
let avg_next_move_value = self
|
||||||
.next_move
|
.next_move
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.value(agent_color, depth + 1))
|
.map(|x| x.value(agent_color, depth + 1))
|
||||||
.sum::<i64>()
|
.sum::<i64>()
|
||||||
.checked_div(self.next_move.len() as i64))
|
.checked_div(self.next_move.len() as i64)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
captured_value + avg_next_move_value
|
self_value + avg_next_move_value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the # of moves in the entire tree
|
||||||
pub fn len(&self) -> u32 {
|
pub fn len(&self) -> u32 {
|
||||||
self.next_move.len() as u32 + self.next_move.iter().map(Move::len).sum::<u32>()
|
self.next_move.len() as u32 + self.next_move.iter().map(Move::len).sum::<u32>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a tuple containing the `i` and `j` fields
|
||||||
pub const fn coords(&self) -> (usize, usize) {
|
pub const fn coords(&self) -> (usize, usize) {
|
||||||
(self.i, self.j)
|
(self.i, self.j)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cursed function to create a dummy move type from a color and board
|
/// Cursed function to create a dummy move type from a color and board
|
||||||
/// Used to bootstrap [`ComplexAgent`]
|
/// Used to bootstrap [`ComplexAgent`]'s future moves
|
||||||
pub fn bootstrap(color: Piece, board: &Board) -> Self {
|
pub fn bootstrap(color: Piece, board: &Board) -> Self {
|
||||||
Move {
|
Move {
|
||||||
i: 0,
|
i: 0,
|
||||||
@ -109,6 +113,7 @@ impl Agent for ComplexAgent {
|
|||||||
.take()
|
.take()
|
||||||
.unwrap_or_else(|| Move::bootstrap(self.color, board));
|
.unwrap_or_else(|| Move::bootstrap(self.color, board));
|
||||||
|
|
||||||
|
// determine the move the other player made via the board state
|
||||||
let mut other_player_move = curr_move
|
let mut other_player_move = curr_move
|
||||||
.next_move
|
.next_move
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
34
src/game.rs
34
src/game.rs
@ -51,6 +51,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Player {} did not make a move!", player_i);
|
println!("Player {} did not make a move!", player_i);
|
||||||
|
// TODO! break, player should not be able to skip a move
|
||||||
return; // No valid move available
|
return; // No valid move available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,36 +68,9 @@ impl Game {
|
|||||||
|
|
||||||
println!("{}", self);
|
println!("{}", self);
|
||||||
|
|
||||||
// TODO! what if the board isn't full, but one player cannot win as
|
if let Some(game_winner) = self.board.game_winner(self.players[current_player].color())
|
||||||
// they don't have any moves to play?
|
{
|
||||||
// Example::
|
println!("{} Wins!", game_winner.text());
|
||||||
// Player 1 placed at (1, 0)
|
|
||||||
// Players: Complex Agent (□) and Manual Agent (■)
|
|
||||||
// 0 1 2 3 4 5 6 7
|
|
||||||
// -----------------
|
|
||||||
// 0|□|□|□|□|□|□|□|□|
|
|
||||||
// -----------------
|
|
||||||
// 1|■|■|■|□|□|□| |□|
|
|
||||||
// -----------------
|
|
||||||
// 2|■|■|□|■|□|□|□|□|
|
|
||||||
// -----------------
|
|
||||||
// 3|■|■|□|□|■|□|□|□|
|
|
||||||
// -----------------
|
|
||||||
// 4|■|■|□|□|■|□|□|□|
|
|
||||||
// -----------------
|
|
||||||
// 5|□|□|■|□|■|□|□|□|
|
|
||||||
// -----------------
|
|
||||||
// 6| |□|□|□|□|□|□|□|
|
|
||||||
// -----------------
|
|
||||||
// 7| | | |■|■| |■| |
|
|
||||||
// -----------------
|
|
||||||
// White Score: 17
|
|
||||||
// Black Score: 40
|
|
||||||
|
|
||||||
// (depth: 5) possible board states: 0
|
|
||||||
// thread 'main' panicked at src/complexagent.rs:139:9:
|
|
||||||
// ComplexAgent didn't make a move
|
|
||||||
if self.board.game_over() {
|
|
||||||
// end the game
|
// end the game
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,8 +10,8 @@ mod piece;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let player1 = complexagent::ComplexAgent::new(Piece::Black);
|
let player1 = complexagent::ComplexAgent::new(Piece::Black);
|
||||||
// let player2 = complexagent::ComplexAgent::new(Piece::White);
|
let player2 = complexagent::ComplexAgent::new(Piece::White);
|
||||||
let player2 = agent::ManualAgent::new(Piece::White);
|
// let player2 = agent::ManualAgent::new(Piece::White);
|
||||||
// let player2 = agent::RandomAgent::new(Piece::White);
|
// let player2 = agent::RandomAgent::new(Piece::White);
|
||||||
let mut game = Game::new(Box::new(player1), Box::new(player2));
|
let mut game = Game::new(Box::new(player1), Box::new(player2));
|
||||||
game.game_loop();
|
game.game_loop();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user