diff --git a/src/complexagent.rs b/src/complexagent.rs index ee32446..fdd2a26 100644 --- a/src/complexagent.rs +++ b/src/complexagent.rs @@ -29,6 +29,8 @@ struct Move { value: i64, color: Piece, + + lazy_children: bool, } impl Move { @@ -82,25 +84,39 @@ impl FutureMoves { } } + const LAZY_EXPIRE: usize = 6; + /// Generate children for all children of `nodes` fn extend_layers(&mut self) { let mut next_nodes: Vec = (0..self.arena.len()) - .filter(|&idx| self.arena[idx].children.is_empty()) + // we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated) + .filter(|&idx| self.arena[idx].children.is_empty() || self.arena[idx].lazy_children) .filter(|&idx| self.is_connected_to_root(idx)) // put here so this will not extend needlessly before prunes .collect(); - for _ in self.current_depth..=self.max_depth { - // TODO! use `i` in order to prune along-the-way - // i.e, every 4 moves of color `self.agent_color`, do a pruning step, - // only keeping the top [`Move`] - + for i in self.current_depth..=self.max_depth { next_nodes = next_nodes .into_iter() .progress_with_style( - ProgressStyle::with_template("Generating children: ({pos}/{len}) {per_sec}") - .unwrap(), + ProgressStyle::with_template(&format!( + "Generating children (depth: {}/{}): ({{pos}}/{{len}}) {{per_sec}}", + i, self.max_depth + )) + .unwrap(), ) - .flat_map(|node_idx| self.generate_children(node_idx)) + .flat_map(|node_idx| { + self.generate_children( + node_idx, + if self.arena[node_idx].lazy_children + && self.depth_of(node_idx) - 1 < Self::LAZY_EXPIRE + { + false + } else { + // this is a non-lazy_children child, should it be lazy? + i > Self::LAZY_EXPIRE + }, + ) + }) .flatten() .collect(); } @@ -124,17 +140,23 @@ impl FutureMoves { } /// Creates children for a parent (`parent`), returns an iterator it's children's indexes - fn generate_children(&mut self, parent_idx: usize) -> Option> { + fn generate_children( + &mut self, + parent_idx: usize, + lazy_children: bool, + ) -> Option> { // early-exit if a winner for the parent already exists if self.arena[parent_idx].winner != Winner::None { return None; } + let new_color = !self.arena[parent_idx].color; + let parent_lazy = self.arena[parent_idx].lazy_children; let mut new: Vec = // use [`Board::all_positions`] here instead of [`Board::possible_moves`] // because we use [`Board::what_if`] later and we want to reduce calls to [`Board::propegate_from_dry`] Board::all_positions() - .flat_map(|(i, j)| self.arena[parent_idx].board.what_if(i, j, !self.arena[parent_idx].color).map(|x| (i, j, x))) + .flat_map(|(i, j)| self.arena[parent_idx].board.what_if(i, j, new_color).map(|x| (i, j, x))) .map(|(i, j, new_board)| Move { i, j, @@ -143,22 +165,37 @@ impl FutureMoves { parent: Some(parent_idx), children: Vec::new(), value: 0, - color: !self.arena[parent_idx].color + color: new_color, + lazy_children, }).collect(); + // negative, because we want the max value to be at the first index + new.sort_by_key(|x| -x.compute_self_value(self.agent_color)); + // keep the TOP_K children `self.agent_color`-color moves const TOP_K_CHILDREN: usize = 1; // we want to keep only the best move of the agent - if !self.arena[parent_idx].color == self.agent_color && new.len() > TOP_K_CHILDREN { + if lazy_children && new_color == self.agent_color && new.len() > TOP_K_CHILDREN { // TODO! Move this to `extend_layers` so we can prune based on recursive [`Move`] value - // negative, because we want the max value to be at the first index - new.sort_by_key(|x| -x.compute_self_value(self.agent_color)); + new.drain(TOP_K_CHILDREN..); } let start_idx = self.arena.len(); - self.arena.extend(new); + if parent_lazy && !lazy_children { + // this move's children are being regenerated after lazy child expiration, don't append first node + if new.len() > 1 { + self.arena.extend(new.drain(1..)); + } else { + // nothing will be appended + // even though it was sorted the first time around + // there's still no more than one element (which is already in the arena) + return None; + } + } else { + self.arena.extend(new); + } let new_indices = start_idx..self.arena.len(); self.arena[parent_idx].children.extend(new_indices.clone()); @@ -250,6 +287,7 @@ impl FutureMoves { children: Vec::new(), value: 0, color: !self.agent_color, + lazy_children: false, }); self.update_root_idx(0); } @@ -348,7 +386,7 @@ pub struct ComplexAgent { impl ComplexAgent { pub const fn new(color: Piece) -> Self { - const MAX_DEPTH: usize = 15; + const MAX_DEPTH: usize = 10; Self { color, future_moves: FutureMoves::new(color, MAX_DEPTH),