rewrite basically done
This commit is contained in:
parent
87f58e5e40
commit
7049039a77
19
src/board.rs
19
src/board.rs
@ -59,6 +59,13 @@ fn gen_adj_lookup() -> PosMap<ChainCollection> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Winner {
|
||||
Player(Piece),
|
||||
Tie,
|
||||
None,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Precompute all possible chains for each position on the board
|
||||
pub static ref ADJ_LOOKUP: PosMap<ChainCollection> = gen_adj_lookup();
|
||||
@ -263,22 +270,20 @@ impl Board {
|
||||
}
|
||||
|
||||
/// Returns the winner of the board (if any)
|
||||
pub fn game_winner(&self, turn: Piece) -> Option<Piece> {
|
||||
pub fn game_winner(&self, turn: Piece) -> Winner {
|
||||
// Wikipedia: `Players take alternate turns. If one player cannot make a valid move, play passes back to the other player. The game ends when the grid has filled up or if neither player can make a valid move.`
|
||||
|
||||
if self.possible_moves(turn).next().is_some() || self.possible_moves(!turn).next().is_some()
|
||||
{
|
||||
// player can still make a move, there is no winner
|
||||
return None;
|
||||
return Winner::None;
|
||||
}
|
||||
|
||||
let (white_score, black_score) = self.get_score();
|
||||
match white_score.cmp(&black_score) {
|
||||
Ordering::Greater => Some(Piece::White), // White win
|
||||
Ordering::Less => Some(Piece::Black), // Black win
|
||||
|
||||
// TODO! this will end up being parsed the same as a "no winner", it should be a seperate type
|
||||
Ordering::Equal => None, // Tie
|
||||
Ordering::Greater => Winner::Player(Piece::White), // White win
|
||||
Ordering::Less => Winner::Player(Piece::Black), // Black win
|
||||
Ordering::Equal => Winner::Tie,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use std::num::NonZero;
|
||||
|
||||
use crate::{agent::Agent, board::Board, piece::Piece};
|
||||
use either::Either;
|
||||
use crate::{
|
||||
agent::Agent,
|
||||
board::{Board, Winner},
|
||||
piece::Piece,
|
||||
};
|
||||
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
|
||||
use num::Integer;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Move {
|
||||
@ -17,7 +17,7 @@ struct Move {
|
||||
board: Board,
|
||||
|
||||
/// Current winner of the match
|
||||
winner: Option<Piece>,
|
||||
winner: Winner,
|
||||
|
||||
/// Index of this move's parent
|
||||
parent: Option<usize>,
|
||||
@ -35,15 +35,16 @@ impl Move {
|
||||
fn compute_self_value(&self, agent_color: Piece, depth: i64) -> i64 {
|
||||
let mut self_value = self.value;
|
||||
|
||||
if self.winner == Some(!agent_color) {
|
||||
if self.winner == Winner::Player(!agent_color) {
|
||||
// if this board results in the opponent winning, MAJORLY negatively weigh this move
|
||||
// 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 self.winner == Some(agent_color) {
|
||||
} else if self.winner == Winner::Player(agent_color) {
|
||||
// results in a win for the agent
|
||||
self_value = i64::MAX;
|
||||
}
|
||||
// TODO! handle ties... what should they be valued as? maybe `i64::MAX / 2`?
|
||||
|
||||
self_value / depth
|
||||
}
|
||||
@ -78,11 +79,11 @@ impl FutureMoves {
|
||||
.collect();
|
||||
|
||||
for _ in self.current_depth..=(self.max_depth as isize) {
|
||||
let arena_len = self.arena.len();
|
||||
let prog_len = next_nodes.len();
|
||||
next_nodes = next_nodes
|
||||
.into_iter()
|
||||
.progress_with(
|
||||
ProgressBar::new(arena_len as u64).with_style(
|
||||
ProgressBar::new(prog_len as u64).with_style(
|
||||
ProgressStyle::with_template(
|
||||
"Generating children: ({pos}/{len}) {per_sec}",
|
||||
)
|
||||
@ -128,7 +129,8 @@ impl FutureMoves {
|
||||
// we want to keep only the best move of the agent
|
||||
if color == self.agent_color {
|
||||
if new.len() > 1 {
|
||||
new.sort_by_key(|x| x.compute_self_value(self.agent_color, 1));
|
||||
// negative, because we want the max value to be at the first index
|
||||
new.sort_by_key(|x| -x.compute_self_value(self.agent_color, 1));
|
||||
new.drain(1..);
|
||||
}
|
||||
}
|
||||
@ -145,7 +147,7 @@ impl FutureMoves {
|
||||
}
|
||||
|
||||
/// Given an index from `self.arena`, what depth is it at? 1-indexed (ROOT IS AT INDEX 1)
|
||||
fn depth_of(&self, node_idx: usize) -> NonZero<usize> {
|
||||
fn depth_of(&self, node_idx: usize) -> usize {
|
||||
let mut depth = 0;
|
||||
let mut current = Some(node_idx);
|
||||
while let Some(parent_idx) = current {
|
||||
@ -153,8 +155,7 @@ impl FutureMoves {
|
||||
current = self.arena[parent_idx].parent;
|
||||
}
|
||||
|
||||
// SAFETY! because `node_idx` is of type `usize`, depth will never be 0
|
||||
unsafe { NonZero::new_unchecked(depth) }
|
||||
depth
|
||||
}
|
||||
|
||||
fn compute_values(&mut self) {
|
||||
@ -163,19 +164,16 @@ impl FutureMoves {
|
||||
let mut visited = vec![false; self.arena.len()];
|
||||
|
||||
for depth in (0..=self.current_depth).rev() {
|
||||
let nodes_at_depth: Vec<usize> = (0..self.arena.len())
|
||||
.filter(|&idx| {
|
||||
if visited[idx] {
|
||||
false
|
||||
} else {
|
||||
visited[idx] = true;
|
||||
true
|
||||
}
|
||||
})
|
||||
.filter(|&idx| self.depth_of(idx).get() == depth as usize)
|
||||
.collect();
|
||||
for idx in 0..self.arena.len() {
|
||||
if visited[idx] {
|
||||
continue;
|
||||
} else {
|
||||
visited[idx] = true;
|
||||
}
|
||||
if self.depth_of(idx) != depth as usize {
|
||||
continue;
|
||||
}
|
||||
|
||||
for idx in nodes_at_depth {
|
||||
let self_value = self.arena[idx]
|
||||
.compute_self_value(self.agent_color, (self.current_depth - depth + 1) as i64);
|
||||
|
||||
@ -230,7 +228,7 @@ impl FutureMoves {
|
||||
i: 0,
|
||||
j: 0,
|
||||
board: *board,
|
||||
winner: None,
|
||||
winner: Winner::None,
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
value: 0,
|
||||
@ -253,7 +251,7 @@ impl FutureMoves {
|
||||
|
||||
fn update_root_idx(&mut self, idx: usize) {
|
||||
self.current_root = Some(idx);
|
||||
self.current_depth -= self.depth_of(idx).get() as isize;
|
||||
self.current_depth -= self.depth_of(idx) as isize - 1;
|
||||
self.prune_unrelated();
|
||||
self.extend_layers();
|
||||
self.compute_values();
|
||||
@ -264,6 +262,9 @@ impl FutureMoves {
|
||||
return;
|
||||
};
|
||||
|
||||
// make sure `root` doesn't reference another node
|
||||
self.arena[root].parent = None;
|
||||
|
||||
let mut retain = vec![false; self.arena.len()];
|
||||
|
||||
// stack is going to be AT MAXIMUM, the size of the array,
|
||||
@ -314,7 +315,7 @@ pub struct ComplexAgent {
|
||||
|
||||
impl ComplexAgent {
|
||||
pub const fn new(color: Piece) -> Self {
|
||||
const MAX_DEPTH: usize = 10;
|
||||
const MAX_DEPTH: usize = 17;
|
||||
Self {
|
||||
color,
|
||||
future_moves: FutureMoves::new(color, MAX_DEPTH),
|
||||
|
||||
15
src/game.rs
15
src/game.rs
@ -92,11 +92,16 @@ impl Game {
|
||||
|
||||
println!("{}", self);
|
||||
|
||||
if let Some(game_winner) = self.board.game_winner(self.players[current_player].color())
|
||||
{
|
||||
println!("{} Wins!", game_winner.text());
|
||||
// end the game
|
||||
break;
|
||||
match self.board.game_winner(self.players[current_player].color()) {
|
||||
crate::board::Winner::Player(piece) => {
|
||||
println!("{} Wins!", piece.text());
|
||||
break;
|
||||
}
|
||||
crate::board::Winner::Tie => {
|
||||
println!("Game Tied!");
|
||||
break;
|
||||
}
|
||||
crate::board::Winner::None => {}
|
||||
}
|
||||
|
||||
self.step(current_player);
|
||||
|
||||
@ -12,8 +12,8 @@ mod piece;
|
||||
fn main() {
|
||||
let player1 = complexagent::ComplexAgent::new(Piece::Black);
|
||||
// let player2 = complexagent::ComplexAgent::new(Piece::White);
|
||||
let player2 = agent::ManualAgent::new(Piece::White);
|
||||
// let player2 = agent::RandomAgent::new(Piece::White);
|
||||
// let player2 = agent::ManualAgent::new(Piece::White);
|
||||
let player2 = agent::RandomAgent::new(Piece::White);
|
||||
let mut game = Game::new(Box::new(player1), Box::new(player2));
|
||||
game.game_loop();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user