rewrite progress 2

This commit is contained in:
Simon Gardling 2025-02-17 11:54:06 -05:00
parent 127c9874da
commit 87f58e5e40
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
4 changed files with 88 additions and 64 deletions

View File

@ -118,6 +118,12 @@ impl fmt::Display for Board {
} }
} }
impl fmt::Debug for Board {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl Board { impl Board {
/// Create a new empty board /// Create a new empty board
pub const fn new() -> Self { pub const fn new() -> Self {

View File

@ -5,7 +5,7 @@ use either::Either;
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle}; use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
use num::Integer; use num::Integer;
#[derive(Clone)] #[derive(Clone, Debug)]
struct Move { struct Move {
/// `i` position of move /// `i` position of move
i: usize, i: usize,
@ -32,7 +32,7 @@ struct Move {
} }
impl Move { impl Move {
fn compute_self_value(&self, agent_color: Piece, depth: usize) -> i64 { fn compute_self_value(&self, agent_color: Piece, depth: i64) -> i64 {
let mut self_value = self.value; let mut self_value = self.value;
if self.winner == Some(!agent_color) { if self.winner == Some(!agent_color) {
@ -45,14 +45,14 @@ impl Move {
self_value = i64::MAX; self_value = i64::MAX;
} }
self_value / depth as i64 self_value / depth
} }
} }
struct FutureMoves { struct FutureMoves {
arena: Vec<Move>, arena: Vec<Move>,
current_root: Option<usize>, current_root: Option<usize>,
current_depth: usize, current_depth: isize,
max_depth: usize, max_depth: usize,
@ -77,7 +77,7 @@ impl FutureMoves {
.filter(|&idx| self.arena[idx].children.is_empty()) .filter(|&idx| self.arena[idx].children.is_empty())
.collect(); .collect();
for _ in (self.current_depth..self.max_depth).progress() { for _ in self.current_depth..=(self.max_depth as isize) {
let arena_len = self.arena.len(); let arena_len = self.arena.len();
next_nodes = next_nodes next_nodes = next_nodes
.into_iter() .into_iter()
@ -98,6 +98,7 @@ impl FutureMoves {
}) })
.collect(); .collect();
} }
self.current_depth = self.max_depth as isize;
} }
/// Creates children for a parent (`parent`), returns an iterator it's children's indexes /// Creates children for a parent (`parent`), returns an iterator it's children's indexes
@ -143,6 +144,19 @@ impl FutureMoves {
new_indices new_indices
} }
/// 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> {
let mut depth = 0;
let mut current = Some(node_idx);
while let Some(parent_idx) = current {
depth += 1;
current = self.arena[parent_idx].parent;
}
// SAFETY! because `node_idx` is of type `usize`, depth will never be 0
unsafe { NonZero::new_unchecked(depth) }
}
fn compute_values(&mut self) { fn compute_values(&mut self) {
// PERF! doing this filtering here previously visited moves reduces time // PERF! doing this filtering here previously visited moves reduces time
// spent in `depth_of` from 27.79% -> 2.9% // spent in `depth_of` from 27.79% -> 2.9%
@ -158,12 +172,12 @@ impl FutureMoves {
true true
} }
}) })
.filter(|&idx| self.depth_of(idx).get() == depth) .filter(|&idx| self.depth_of(idx).get() == depth as usize)
.collect(); .collect();
for idx in nodes_at_depth { for idx in nodes_at_depth {
let self_value = self.arena[idx] let self_value = self.arena[idx]
.compute_self_value(self.agent_color, self.current_depth - depth + 1); .compute_self_value(self.agent_color, (self.current_depth - depth + 1) as i64);
let children_value = self.arena[idx] let children_value = self.arena[idx]
.children .children
@ -177,70 +191,72 @@ 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> {
let mut depth = 0;
let mut current = Some(node_idx);
while let Some(parent_idx) = current {
depth += 1;
current = self.arena[parent_idx].parent;
}
// SAFETY! because `node_idx` is of type `usize`, depth will never be 0
unsafe { NonZero::new_unchecked(depth) }
}
pub fn best_move(&self) -> Option<(usize, usize)> { pub fn best_move(&self) -> Option<(usize, usize)> {
(match self.current_root { self.current_root
Some(root) => Either::Left(vec![root].into_iter()), .and_then(|x| {
None => { self.arena[x]
Either::Right((0..self.arena.len()).filter(|&idx| self.depth_of(idx).get() == 1)) .children
} .iter()
}) .max_by_key(|&&idx| self.arena[idx].value)
.map(|idx| &self.arena[idx]) })
.max_by_key(|x| x.value) .inspect(|&&x| {
.map(|x| (x.i, x.j)) assert_eq!(
self.arena[x].color, self.agent_color,
"selected move color should be the same as the color of the agent"
);
})
.map(|&x| (self.arena[x].i, self.arena[x].j))
} }
/// Updates `FutureMoves` based on the current state of the board
/// The board is supposed to be after the opposing move
pub fn update(&mut self, board: &Board) { pub fn update(&mut self, board: &Board) {
self.current_root = Some(self.current_root.unwrap_or_else(|| { let curr_board = self
self.arena .arena
.iter() .iter()
.enumerate() .enumerate()
.find_map(|(idx, m)| (&m.board == board).then_some(idx)) .find(|(_, m)| {
.unwrap_or_else(|| { &m.board == board && (m.parent == self.current_root) && self.current_root.is_some()
self.arena.push(Move { })
i: 0, .map(|(idx, _)| idx);
j: 0,
board: *board,
winner: None,
parent: None,
children: Vec::new(),
value: 0,
color: !self.agent_color,
});
0
})
}));
self.extend_layers(); if let Some(curr_board_idx) = curr_board {
self.current_root = Some(curr_board_idx);
self.update_root_idx(curr_board_idx);
} else {
println!("Generating root of FutureMoves");
self.arena.clear();
self.arena.push(Move {
i: 0,
j: 0,
board: *board,
winner: None,
parent: None,
children: Vec::new(),
value: 0,
color: !self.agent_color,
});
self.update_root_idx(0);
}
} }
pub fn update_root(&mut self, i: usize, j: usize) -> bool { pub fn update_root_coord(&mut self, i: usize, j: usize) -> bool {
self.arena self.arena
.iter() .iter()
.enumerate() .enumerate()
.find_map(|(idx, node)| { .find_map(|(idx, node)| {
(node.parent == self.current_root && node.i == i && node.j == j).then_some(idx) (node.parent == self.current_root && node.i == i && node.j == j).then_some(idx)
}) })
.is_some_and(|root| { .inspect(|&root| self.update_root_idx(root))
self.current_root = Some(root); .is_some()
self.current_depth = self.max_depth - self.depth_of(root).get(); }
self.prune_unrelated();
self.extend_layers(); fn update_root_idx(&mut self, idx: usize) {
true self.current_root = Some(idx);
}) self.current_depth -= self.depth_of(idx).get() as isize;
self.prune_unrelated();
self.extend_layers();
self.compute_values();
} }
fn prune_unrelated(&mut self) { fn prune_unrelated(&mut self) {
@ -298,7 +314,7 @@ pub struct ComplexAgent {
impl ComplexAgent { impl ComplexAgent {
pub const fn new(color: Piece) -> Self { pub const fn new(color: Piece) -> Self {
const MAX_DEPTH: usize = 17; const MAX_DEPTH: usize = 10;
Self { Self {
color, color,
future_moves: FutureMoves::new(color, MAX_DEPTH), future_moves: FutureMoves::new(color, MAX_DEPTH),
@ -309,12 +325,11 @@ impl ComplexAgent {
impl Agent for ComplexAgent { impl Agent for ComplexAgent {
fn next_move(&mut self, board: &Board) -> Option<(usize, usize)> { fn next_move(&mut self, board: &Board) -> Option<(usize, usize)> {
self.future_moves.update(board); self.future_moves.update(board);
self.future_moves.compute_values();
println!("# of moves stored: {}", self.future_moves.arena.len()); println!("# of moves stored: {}", self.future_moves.arena.len());
self.future_moves.best_move().inspect(|&(i, j)| { self.future_moves.best_move().inspect(|&(i, j)| {
self.future_moves.update_root(i, j); self.future_moves.update_root_coord(i, j);
}) })
} }

View File

@ -60,7 +60,10 @@ impl Game {
// Lets the player try again if the move is invalid // Lets the player try again if the move is invalid
// Now we dont need to restart the game if we mess up // Now we dont need to restart the game if we mess up
Err(err) => { Err(err) => {
panic!("Invalid move by Player {}: {}. Try again.", player_i, err); panic!(
"Invalid move by Player {}: {}. Try again. Move: ({}, {})",
player_i, err, i, j
);
} }
} }
} else { } else {

View File

@ -12,8 +12,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();
} }