rewrite progress 2
This commit is contained in:
parent
127c9874da
commit
87f58e5e40
@ -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 {
|
||||
/// Create a new empty board
|
||||
pub const fn new() -> Self {
|
||||
|
||||
@ -5,7 +5,7 @@ use either::Either;
|
||||
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
|
||||
use num::Integer;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct Move {
|
||||
/// `i` position of move
|
||||
i: usize,
|
||||
@ -32,7 +32,7 @@ struct 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;
|
||||
|
||||
if self.winner == Some(!agent_color) {
|
||||
@ -45,14 +45,14 @@ impl Move {
|
||||
self_value = i64::MAX;
|
||||
}
|
||||
|
||||
self_value / depth as i64
|
||||
self_value / depth
|
||||
}
|
||||
}
|
||||
|
||||
struct FutureMoves {
|
||||
arena: Vec<Move>,
|
||||
current_root: Option<usize>,
|
||||
current_depth: usize,
|
||||
current_depth: isize,
|
||||
|
||||
max_depth: usize,
|
||||
|
||||
@ -77,7 +77,7 @@ impl FutureMoves {
|
||||
.filter(|&idx| self.arena[idx].children.is_empty())
|
||||
.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();
|
||||
next_nodes = next_nodes
|
||||
.into_iter()
|
||||
@ -98,6 +98,7 @@ impl FutureMoves {
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
self.current_depth = self.max_depth as isize;
|
||||
}
|
||||
|
||||
/// Creates children for a parent (`parent`), returns an iterator it's children's indexes
|
||||
@ -143,6 +144,19 @@ impl FutureMoves {
|
||||
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) {
|
||||
// PERF! doing this filtering here previously visited moves reduces time
|
||||
// spent in `depth_of` from 27.79% -> 2.9%
|
||||
@ -158,12 +172,12 @@ impl FutureMoves {
|
||||
true
|
||||
}
|
||||
})
|
||||
.filter(|&idx| self.depth_of(idx).get() == depth)
|
||||
.filter(|&idx| self.depth_of(idx).get() == depth as usize)
|
||||
.collect();
|
||||
|
||||
for idx in nodes_at_depth {
|
||||
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]
|
||||
.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)> {
|
||||
(match self.current_root {
|
||||
Some(root) => Either::Left(vec![root].into_iter()),
|
||||
None => {
|
||||
Either::Right((0..self.arena.len()).filter(|&idx| self.depth_of(idx).get() == 1))
|
||||
}
|
||||
})
|
||||
.map(|idx| &self.arena[idx])
|
||||
.max_by_key(|x| x.value)
|
||||
.map(|x| (x.i, x.j))
|
||||
self.current_root
|
||||
.and_then(|x| {
|
||||
self.arena[x]
|
||||
.children
|
||||
.iter()
|
||||
.max_by_key(|&&idx| self.arena[idx].value)
|
||||
})
|
||||
.inspect(|&&x| {
|
||||
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) {
|
||||
self.current_root = Some(self.current_root.unwrap_or_else(|| {
|
||||
self.arena
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(idx, m)| (&m.board == board).then_some(idx))
|
||||
.unwrap_or_else(|| {
|
||||
self.arena.push(Move {
|
||||
i: 0,
|
||||
j: 0,
|
||||
board: *board,
|
||||
winner: None,
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
value: 0,
|
||||
color: !self.agent_color,
|
||||
});
|
||||
0
|
||||
})
|
||||
}));
|
||||
let curr_board = self
|
||||
.arena
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, m)| {
|
||||
&m.board == board && (m.parent == self.current_root) && self.current_root.is_some()
|
||||
})
|
||||
.map(|(idx, _)| idx);
|
||||
|
||||
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
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(idx, node)| {
|
||||
(node.parent == self.current_root && node.i == i && node.j == j).then_some(idx)
|
||||
})
|
||||
.is_some_and(|root| {
|
||||
self.current_root = Some(root);
|
||||
self.current_depth = self.max_depth - self.depth_of(root).get();
|
||||
self.prune_unrelated();
|
||||
self.extend_layers();
|
||||
true
|
||||
})
|
||||
.inspect(|&root| self.update_root_idx(root))
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn update_root_idx(&mut self, idx: usize) {
|
||||
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) {
|
||||
@ -298,7 +314,7 @@ pub struct ComplexAgent {
|
||||
|
||||
impl ComplexAgent {
|
||||
pub const fn new(color: Piece) -> Self {
|
||||
const MAX_DEPTH: usize = 17;
|
||||
const MAX_DEPTH: usize = 10;
|
||||
Self {
|
||||
color,
|
||||
future_moves: FutureMoves::new(color, MAX_DEPTH),
|
||||
@ -309,12 +325,11 @@ impl ComplexAgent {
|
||||
impl Agent for ComplexAgent {
|
||||
fn next_move(&mut self, board: &Board) -> Option<(usize, usize)> {
|
||||
self.future_moves.update(board);
|
||||
self.future_moves.compute_values();
|
||||
|
||||
println!("# of moves stored: {}", self.future_moves.arena.len());
|
||||
|
||||
self.future_moves.best_move().inspect(|&(i, j)| {
|
||||
self.future_moves.update_root(i, j);
|
||||
self.future_moves.update_root_coord(i, j);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,10 @@ impl Game {
|
||||
// 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.", player_i, err);
|
||||
panic!(
|
||||
"Invalid move by Player {}: {}. Try again. Move: ({}, {})",
|
||||
player_i, err, i, j
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -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