replace lazy_children system

This commit is contained in:
Simon Gardling 2025-02-25 16:36:34 -05:00
parent 155a8d0548
commit 2d066a6a3c
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
2 changed files with 58 additions and 66 deletions

View File

@ -12,11 +12,10 @@ pub struct ComplexAgent {
#[allow(dead_code)] #[allow(dead_code)]
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 = 9;
const NON_LAZY_DEPTH: usize = 3;
Self { Self {
color, color,
future_moves: FutureMoves::new(color, MAX_DEPTH, NON_LAZY_DEPTH), future_moves: FutureMoves::new(color, MAX_DEPTH),
} }
} }
} }

View File

@ -18,22 +18,18 @@ pub struct FutureMoves {
/// Target depth of children to generate /// Target depth of children to generate
max_depth: usize, max_depth: usize,
/// How many deep should the lazy children status expire?
lazy_expire: usize,
/// Color w.r.t /// Color w.r.t
agent_color: Piece, agent_color: Piece,
} }
impl FutureMoves { impl FutureMoves {
pub const fn new(agent_color: Piece, max_depth: usize, lazy_expire: usize) -> Self { pub const fn new(agent_color: Piece, max_depth: usize) -> Self {
Self { Self {
arena: Vec::new(), arena: Vec::new(),
current_root: None, current_root: None,
current_depth: 0, current_depth: 0,
max_depth, max_depth,
agent_color, agent_color,
lazy_expire,
} }
} }
@ -45,17 +41,15 @@ impl FutureMoves {
/// Generate children for all children of `nodes` /// Generate children for all children of `nodes`
/// only `pub` for the sake of benchmarking /// only `pub` for the sake of benchmarking
pub fn extend_layers(&mut self) { pub fn extend_layers(&mut self) {
let mut next_nodes: Vec<usize> = (0..self.arena.len())
// we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated)
.filter(|&idx| {
let got = &self.arena[idx];
got.is_lazy || got.children.is_empty()
})
.filter(|&idx| self.is_connected_to_root(idx)) // put here so this will not extend needlessly before prunes
.collect();
for i in (self.current_depth + 1)..=self.max_depth { for i in (self.current_depth + 1)..=self.max_depth {
next_nodes = next_nodes (0..self.arena.len())
// we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated)
.filter(|&idx| {
let got = &self.arena[idx];
got.is_lazy || got.children.is_empty()
})
.filter(|&idx| self.is_connected_to_root(idx))
.collect::<Vec<usize>>()
.into_iter() .into_iter()
.progress_with_style( .progress_with_style(
ProgressStyle::with_template(&format!( ProgressStyle::with_template(&format!(
@ -64,19 +58,14 @@ impl FutureMoves {
)) ))
.unwrap(), .unwrap(),
) )
.flat_map(|node_idx| { .for_each(|node_idx| {
if (self.arena[node_idx].is_lazy && self.depth_of(node_idx) >= self.lazy_expire) self.generate_children(node_idx);
|| !self.arena[node_idx].is_lazy });
{
self.generate_children(node_idx, i >= self.lazy_expire) self.prune_bad_children();
} else { self.current_depth += 1;
None
}
})
.flatten()
.collect();
} }
self.current_depth = self.max_depth; assert_eq!(self.current_depth, self.max_depth);
} }
/// Determines if a [`Move`] at index `idx` is connected to `self.current_root` /// Determines if a [`Move`] at index `idx` is connected to `self.current_root`
@ -96,11 +85,7 @@ impl FutureMoves {
} }
/// 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
fn generate_children( fn generate_children(&mut self, parent_idx: usize) -> Option<impl Iterator<Item = usize>> {
&mut self,
parent_idx: usize,
lazy_children: bool,
) -> Option<impl Iterator<Item = usize>> {
let parent = &self.arena[parent_idx]; let parent = &self.arena[parent_idx];
// early-exit if a winner for the parent already exists // early-exit if a winner for the parent already exists
@ -131,9 +116,6 @@ impl FutureMoves {
}) })
.collect(); .collect();
// keep the TOP_K children of their magnitude
const TOP_K_CHILDREN: usize = 3;
let start_idx = self.arena.len(); let start_idx = self.arena.len();
self.arena.extend(new); self.arena.extend(new);
@ -141,16 +123,6 @@ impl FutureMoves {
self.arena[parent_idx].children.extend(new_indices.clone()); self.arena[parent_idx].children.extend(new_indices.clone());
if lazy_children && new_indices.clone().count() > TOP_K_CHILDREN {
let mut parent_copy = self.arena[parent_idx].clone();
parent_copy.sort_children(self.arena.as_mut_slice());
self.arena[parent_idx] = parent_copy;
for i in new_indices.clone().skip(TOP_K_CHILDREN) {
self.arena[i].is_lazy = true;
}
}
Some(new_indices) Some(new_indices)
} }
@ -298,7 +270,7 @@ impl FutureMoves {
fn set_root_idx_raw(&mut self, idx: usize) { fn set_root_idx_raw(&mut self, idx: usize) {
self.update_root_idx_raw(idx); self.update_root_idx_raw(idx);
// self.prune_bad_children(); self.prune_bad_children();
self.refocus_tree(); self.refocus_tree();
self.extend_layers(); self.extend_layers();
self.compute_values(0..self.arena.len()); self.compute_values(0..self.arena.len());
@ -343,23 +315,44 @@ impl FutureMoves {
} }
fn prune_bad_children(&mut self) { fn prune_bad_children(&mut self) {
const BOTTOM_PERC: f32 = 20.0; // values are needed in order to prune and see what's best
let Some(root) = self.current_root else { self.compute_values(0..self.arena_len());
return;
};
let mut children = self.arena[root].children.clone(); let by_depth =
self.by_depth((0..self.arena.len()).filter(|&i| self.is_connected_to_root(i)));
children.sort_by_key(|&i| -self.arena[i].value.expect("child has no value")); const TOP_K_CHIL: usize = 2;
let start_len = ((children.len()) as f32 * (1.0 - BOTTOM_PERC)) as usize;
let drained = children.drain(start_len..);
println!("{}", drained.len());
for i in drained { // the lower the value, the more conservative
self.arena[i].parent = None; const UP_TO: usize = 4;
for (depth, indexes) in by_depth {
if depth > UP_TO {
return;
}
// only prune moves of the agent
if indexes.first().map(|&i| self.arena[i].color) != Some(self.agent_color) {
continue;
}
for idx in indexes {
let mut m = self.arena[idx].clone();
if m.is_lazy {
continue;
}
m.is_lazy = true;
m.sort_children(&mut self.arena);
if m.children.len() > TOP_K_CHIL {
let drained = m.children.drain(TOP_K_CHIL..);
for idx in drained {
self.arena[idx].parent = None;
}
}
self.arena[idx] = m;
}
} }
self.refocus_tree();
self.arena[root].children = children;
} }
/// Rebuilds the Arena based on `self.current_root`, prunes unrelated nodes /// Rebuilds the Arena based on `self.current_root`, prunes unrelated nodes
@ -434,7 +427,7 @@ mod tests {
#[test] #[test]
fn prune_tree_test() { fn prune_tree_test() {
let mut futm = FutureMoves::new(Piece::Black, 0, 0); let mut futm = FutureMoves::new(Piece::Black, 0);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, i: 0,
j: 0, j: 0,
@ -503,7 +496,7 @@ mod tests {
#[test] #[test]
fn expand_layer_test() { fn expand_layer_test() {
let mut futm = FutureMoves::new(Piece::Black, 1, 1); let mut futm = FutureMoves::new(Piece::Black, 1);
futm.arena.push(Move::new( futm.arena.push(Move::new(
0, 0,
0, 0,
@ -535,7 +528,7 @@ mod tests {
#[test] #[test]
fn depth_of_test() { fn depth_of_test() {
let mut futm = FutureMoves::new(Piece::Black, 0, 0); let mut futm = FutureMoves::new(Piece::Black, 0);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, i: 0,
@ -599,7 +592,7 @@ mod tests {
#[test] #[test]
fn by_depth_test() { fn by_depth_test() {
let mut futm = FutureMoves::new(Piece::Black, 0, 0); let mut futm = FutureMoves::new(Piece::Black, 0);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, i: 0,