From 2d066a6a3ce0e4b236331422269964e7ddb8649c Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Tue, 25 Feb 2025 16:36:34 -0500 Subject: [PATCH] replace lazy_children system --- src/complexagent.rs | 5 +- src/logic/future_moves.rs | 119 ++++++++++++++++++-------------------- 2 files changed, 58 insertions(+), 66 deletions(-) diff --git a/src/complexagent.rs b/src/complexagent.rs index 8e83e2e..e3687a8 100644 --- a/src/complexagent.rs +++ b/src/complexagent.rs @@ -12,11 +12,10 @@ pub struct ComplexAgent { #[allow(dead_code)] impl ComplexAgent { pub const fn new(color: Piece) -> Self { - const MAX_DEPTH: usize = 10; - const NON_LAZY_DEPTH: usize = 3; + const MAX_DEPTH: usize = 9; Self { color, - future_moves: FutureMoves::new(color, MAX_DEPTH, NON_LAZY_DEPTH), + future_moves: FutureMoves::new(color, MAX_DEPTH), } } } diff --git a/src/logic/future_moves.rs b/src/logic/future_moves.rs index 7fcf7cf..e1e2469 100644 --- a/src/logic/future_moves.rs +++ b/src/logic/future_moves.rs @@ -18,22 +18,18 @@ pub struct FutureMoves { /// Target depth of children to generate max_depth: usize, - /// How many deep should the lazy children status expire? - lazy_expire: usize, - /// Color w.r.t agent_color: Piece, } 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 { arena: Vec::new(), current_root: None, current_depth: 0, max_depth, agent_color, - lazy_expire, } } @@ -45,17 +41,15 @@ impl FutureMoves { /// Generate children for all children of `nodes` /// only `pub` for the sake of benchmarking pub fn extend_layers(&mut self) { - let mut next_nodes: Vec = (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 { - 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::>() .into_iter() .progress_with_style( ProgressStyle::with_template(&format!( @@ -64,19 +58,14 @@ impl FutureMoves { )) .unwrap(), ) - .flat_map(|node_idx| { - if (self.arena[node_idx].is_lazy && self.depth_of(node_idx) >= self.lazy_expire) - || !self.arena[node_idx].is_lazy - { - self.generate_children(node_idx, i >= self.lazy_expire) - } else { - None - } - }) - .flatten() - .collect(); + .for_each(|node_idx| { + self.generate_children(node_idx); + }); + + self.prune_bad_children(); + self.current_depth += 1; } - 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` @@ -96,11 +85,7 @@ impl FutureMoves { } /// Creates children for a parent (`parent`), returns an iterator it's children's indexes - fn generate_children( - &mut self, - parent_idx: usize, - lazy_children: bool, - ) -> Option> { + fn generate_children(&mut self, parent_idx: usize) -> Option> { let parent = &self.arena[parent_idx]; // early-exit if a winner for the parent already exists @@ -131,9 +116,6 @@ impl FutureMoves { }) .collect(); - // keep the TOP_K children of their magnitude - const TOP_K_CHILDREN: usize = 3; - let start_idx = self.arena.len(); self.arena.extend(new); @@ -141,16 +123,6 @@ impl FutureMoves { 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) } @@ -298,7 +270,7 @@ impl FutureMoves { fn set_root_idx_raw(&mut self, idx: usize) { self.update_root_idx_raw(idx); - // self.prune_bad_children(); + self.prune_bad_children(); self.refocus_tree(); self.extend_layers(); self.compute_values(0..self.arena.len()); @@ -343,23 +315,44 @@ impl FutureMoves { } fn prune_bad_children(&mut self) { - const BOTTOM_PERC: f32 = 20.0; - let Some(root) = self.current_root else { - return; - }; + // values are needed in order to prune and see what's best + self.compute_values(0..self.arena_len()); - 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")); - let start_len = ((children.len()) as f32 * (1.0 - BOTTOM_PERC)) as usize; - let drained = children.drain(start_len..); - println!("{}", drained.len()); + const TOP_K_CHIL: usize = 2; - for i in drained { - self.arena[i].parent = None; + // the lower the value, the more conservative + 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.arena[root].children = children; + self.refocus_tree(); } /// Rebuilds the Arena based on `self.current_root`, prunes unrelated nodes @@ -434,7 +427,7 @@ mod tests { #[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 { i: 0, j: 0, @@ -503,7 +496,7 @@ mod tests { #[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( 0, 0, @@ -535,7 +528,7 @@ mod tests { #[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 { i: 0, @@ -599,7 +592,7 @@ mod tests { #[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 { i: 0,