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 {
|
impl Board {
|
||||||
/// Create a new empty board
|
/// Create a new empty board
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
|
|||||||
@ -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);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user