optimizations
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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,28 +87,28 @@ 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(
|
Some(node_idx),
|
||||||
Some(node_idx),
|
&self.arena[node_idx].board.clone(),
|
||||||
&self.arena[node_idx].board.clone(),
|
next_color,
|
||||||
next_color,
|
)
|
||||||
)
|
})
|
||||||
})
|
.progress_with(ProgressBar::new(arena_len as u64).with_style(
|
||||||
.progress_with(ProgressBar::new(arena_len as u64).with_style(
|
ProgressStyle::with_template("Children: ({pos}/{len}) {per_sec}").unwrap(),
|
||||||
ProgressStyle::with_template("({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,
|
||||||
|
|||||||
Reference in New Issue
Block a user