optimizations

This commit is contained in:
2025-02-14 11:13:49 -05:00
parent dafe585f0c
commit 92c7e0ca4d
2 changed files with 39 additions and 45 deletions

View File

@@ -7,12 +7,14 @@ edition = "2021"
# for profiling # for profiling
debug = true debug = true
lto = true
[dependencies] [dependencies]
arrayvec = "0.7" arrayvec = "0.7"
bitvec = "1.0.1" bitvec = "1.0.1"
either = "1.13" either = "1.13"
indicatif = "0.17.11" indicatif = "0.17"
lazy_static = "1.5.0" lazy_static = "1.5"
num = "0.4" num = "0.4"
rand = "0.9" rand = "0.9"
static_assertions = "1.1" static_assertions = "1.1"

View File

@@ -1,7 +1,8 @@
use either::Either; use std::num::NonZero;
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
use crate::{agent::Agent, board::Board, piece::Piece}; use crate::{agent::Agent, board::Board, piece::Piece};
use either::Either;
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
#[derive(Clone)] #[derive(Clone)]
struct Move { struct Move {
@@ -86,16 +87,16 @@ impl FutureMoves {
) { ) {
self.current_depth += remaining_depth; self.current_depth += remaining_depth;
let mut next_nodes: Vec<usize> = nodes.collect(); let mut next_nodes: Vec<usize> = nodes.collect();
let mut next_color = !color; let mut next_color = !color;
for _ in 0..remaining_depth { for _ in 0..remaining_depth {
// early exit if arena is larger than the max size
if self.arena.len() >= self.max_arena { if self.arena.len() >= self.max_arena {
break; break;
} }
let arena_len = self.arena.len(); let arena_len = self.arena.len();
next_nodes = next_nodes = next_nodes
next_nodes
.into_iter() .into_iter()
.flat_map(|node_idx| { .flat_map(|node_idx| {
self.generate_children( self.generate_children(
@@ -105,7 +106,7 @@ impl FutureMoves {
) )
}) })
.progress_with(ProgressBar::new(arena_len as u64).with_style( .progress_with(ProgressBar::new(arena_len as u64).with_style(
ProgressStyle::with_template("({pos}/{len}) {per_sec}").unwrap(), ProgressStyle::with_template("Children: ({pos}/{len}) {per_sec}").unwrap(),
)) ))
.collect(); .collect();
next_color = !next_color; next_color = !next_color;
@@ -159,7 +160,7 @@ impl FutureMoves {
true true
} }
}) })
.filter(|&idx| self.depth_of(idx) == depth) .filter(|&idx| self.depth_of(idx).get() == depth)
.collect(); .collect();
for idx in nodes_at_depth { for idx in nodes_at_depth {
@@ -179,26 +180,25 @@ impl FutureMoves {
} }
} }
/// Given an index from `self.arena`, what depth is it at? 0-indexed /// Given an index from `self.arena`, what depth is it at? 1-indexed (ROOT IS AT INDEX 1)
fn depth_of(&self, node_parent: usize) -> usize { fn depth_of(&self, node_idx: usize) -> NonZero<usize> {
let mut depth = 0; let mut depth = 0;
let mut current = Some(node_parent); let mut current = Some(node_idx);
while let Some(parent_idx) = current { while let Some(parent_idx) = current {
depth += 1; depth += 1;
current = self.arena[parent_idx].parent; current = self.arena[parent_idx].parent;
} }
depth
// 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 { (match self.current_root {
Some(root) => Either::Left(vec![root].into_iter()), Some(root) => Either::Left(vec![root].into_iter()),
None => Either::Right( None => {
self.arena Either::Right((0..self.arena.len()).filter(|&idx| self.depth_of(idx).get() == 1))
.iter() }
.enumerate()
.filter_map(|(idx, node)| node.parent.is_none().then_some(idx)),
),
}) })
.map(|idx| &self.arena[idx]) .map(|idx| &self.arena[idx])
.max_by_key(|x| x.value) .max_by_key(|x| x.value)
@@ -214,7 +214,7 @@ impl FutureMoves {
}) })
.is_some_and(|root| { .is_some_and(|root| {
self.current_root = Some(root); self.current_root = Some(root);
self.current_depth = self.max_depth - self.depth_of(root); self.current_depth = self.max_depth - self.depth_of(root).get();
self.prune_unrelated(); self.prune_unrelated();
true true
}) })
@@ -229,7 +229,7 @@ impl FutureMoves {
// stack is going to be AT MAXIMUM, the size of the array, // stack is going to be AT MAXIMUM, the size of the array,
// so lets just pre-allocate it // so lets just pre-allocate it
let mut stack = Vec::with_capacity(self.arena.len()); let mut stack: Vec<usize> = Vec::with_capacity(self.arena.len());
stack.push(root); stack.push(root);
// traverse children of the current root // traverse children of the current root
@@ -240,20 +240,12 @@ impl FutureMoves {
let mut index_map = vec![None; self.arena.len()]; let mut index_map = vec![None; self.arena.len()];
// PERF: reverse iterator to avoid unneeded `memcpy`s when deconstructing the Arena
self.arena = retain self.arena = retain
.into_iter() .into_iter()
.enumerate() .enumerate() // old_idx
.rev() .zip(self.arena.drain(..))
.enumerate() .flat_map(|((old_idx, keep), node)| keep.then_some((old_idx, node))) // filter out unrelated nodes
.flat_map(|(new_idx, (old_idx, keep))| { .enumerate() // new_idx
let node = self.arena.remove(old_idx);
if keep {
Some((new_idx, (old_idx, node)))
} else {
None
}
})
.map(|(new_idx, (old_idx, mut node))| { .map(|(new_idx, (old_idx, mut node))| {
index_map[old_idx] = Some(new_idx); index_map[old_idx] = Some(new_idx);
@@ -283,7 +275,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 = 10; const MAX_DEPTH: usize = 8;
const MAX_ARENA: usize = 100_000_000; const MAX_ARENA: usize = 100_000_000;
Self { Self {
color, color,