improve parent selection

This commit is contained in:
Simon Gardling 2025-02-26 22:11:27 -05:00
parent bbeb1b8b37
commit 4ec9f3cf21
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
4 changed files with 38 additions and 24 deletions

View File

@ -8,9 +8,9 @@ use std::time::Duration;
fn extend_layers_test(depth: usize) { fn extend_layers_test(depth: usize) {
let config = FutureMoveConfig { let config = FutureMoveConfig {
max_depth: depth, max_depth: depth,
start_pruning_at_minus: 4, min_arena_depth_sub: 4,
top_k_children: 2, top_k_children: 2,
up_to_mod: 4, up_to_minus: 4,
max_arena_size: 10_000_000, max_arena_size: 10_000_000,
}; };
let mut fut = FutureMoves::new(Piece::Black, config); let mut fut = FutureMoves::new(Piece::Black, config);

View File

@ -14,10 +14,10 @@ impl ComplexAgent {
pub const fn new(color: Piece) -> Self { pub const fn new(color: Piece) -> Self {
const CONFIG: FutureMoveConfig = FutureMoveConfig { const CONFIG: FutureMoveConfig = FutureMoveConfig {
max_depth: 10, max_depth: 10,
start_pruning_at_minus: 4, min_arena_depth_sub: 3,
top_k_children: 2, top_k_children: 2,
up_to_mod: 4, up_to_minus: 4,
max_arena_size: 100_000_000, max_arena_size: 10_000_000,
}; };
Self { Self {
color, color,

View File

@ -26,16 +26,17 @@ pub struct FutureMoveConfig {
/// Max depth of that we should try and traverse /// Max depth of that we should try and traverse
pub max_depth: usize, pub max_depth: usize,
/// start pruning at X depth before self.max_depth is reached /// subtract this value from FutureMove.max_depth
/// used for pruning during tree generation before the tree is fully grown /// and that would be the min depth an arena should fill for
pub start_pruning_at_minus: usize, /// pruning to happen
pub min_arena_depth_sub: usize,
/// when pruning, keep the top_k # of children /// when pruning, keep the top_k # of children
pub top_k_children: usize, pub top_k_children: usize,
// the lower the value, the more conservative the pruning is, what level to stop pruning at? // the lower the value, the more conservative the pruning is, what level to stop pruning at?
// a lower value allows more possible paths // a lower value allows more possible paths
pub up_to_mod: usize, pub up_to_minus: usize,
/// Max size of the arena, will not generate more if /// Max size of the arena, will not generate more if
/// the arena is of that size or bigger /// the arena is of that size or bigger
@ -71,7 +72,7 @@ impl FutureMoves {
// we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated) // we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated)
.filter(|&idx| { .filter(|&idx| {
let got = &self.arena[idx]; let got = &self.arena[idx];
got.is_trimmed || got.children.is_empty() !got.is_trimmed && got.winner == Winner::None && !got.tried_children
}) })
.filter(|&idx| self.is_connected_to_root(idx)) .filter(|&idx| self.is_connected_to_root(idx))
.collect::<Vec<usize>>() .collect::<Vec<usize>>()
@ -84,7 +85,8 @@ impl FutureMoves {
.unwrap(), .unwrap(),
) )
.for_each(|node_idx| { .for_each(|node_idx| {
self.generate_children(node_idx); self.generate_children(node_idx).last();
self.arena[node_idx].tried_children = true;
}); });
self.prune_bad_children(); self.prune_bad_children();
@ -109,14 +111,11 @@ 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(&mut self, parent_idx: usize) -> Option<impl Iterator<Item = usize>> { /// Completely unchecked, the caller should be the one who tests to make sure child generation
/// hasn't already been tried on a parent
fn generate_children(&mut self, parent_idx: usize) -> 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
if parent.winner != Winner::None {
return None;
}
let new_color = !parent.color; let new_color = !parent.color;
// use [`Board::all_positions`] here instead of [`Board::possible_moves`] // use [`Board::all_positions`] here instead of [`Board::possible_moves`]
@ -140,7 +139,7 @@ impl FutureMoves {
self.set_parent_child(parent_idx, child_idx); self.set_parent_child(parent_idx, child_idx);
} }
Some(new_indices) new_indices
} }
/// Given an index from `self.arena`, what depth is it at? 0-indexed /// Given an index from `self.arena`, what depth is it at? 0-indexed
@ -346,7 +345,7 @@ impl FutureMoves {
if self if self
.config .config
.max_depth .max_depth
.saturating_sub(self.config.start_pruning_at_minus) .saturating_sub(self.config.min_arena_depth_sub)
> self.current_depth > self.current_depth
{ {
return; return;
@ -354,7 +353,7 @@ impl FutureMoves {
for (depth, indexes) in by_depth { for (depth, indexes) in by_depth {
// TODO! maybe update by_depth every iteration or something? // TODO! maybe update by_depth every iteration or something?
if depth > self.current_depth.saturating_sub(self.config.up_to_mod) { if depth > self.current_depth.saturating_sub(self.config.up_to_minus) {
return; return;
} }
@ -454,9 +453,17 @@ impl FutureMoves {
mod tests { mod tests {
use super::*; use super::*;
const FUTURE_MOVES_CONFIG: FutureMoveConfig = FutureMoveConfig {
max_depth: 1,
min_arena_depth_sub: 2,
top_k_children: 2,
up_to_minus: 0,
max_arena_size: 100,
};
#[test] #[test]
fn prune_tree_test() { fn prune_tree_test() {
let mut futm = FutureMoves::new(Piece::Black, Default::default()); let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, i: 0,
@ -469,6 +476,7 @@ mod tests {
self_value: 0, self_value: 0,
color: Piece::Black, color: Piece::Black,
is_trimmed: false, is_trimmed: false,
tried_children: false,
}); });
futm.update_root_idx_raw(0); futm.update_root_idx_raw(0);
@ -508,7 +516,7 @@ mod tests {
#[test] #[test]
fn expand_layer_test() { fn expand_layer_test() {
let mut futm = FutureMoves::new(Piece::Black, Default::default()); let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG);
futm.config.max_depth = 1; futm.config.max_depth = 1;
futm.arena.push(Move::new( futm.arena.push(Move::new(
@ -541,7 +549,7 @@ mod tests {
#[test] #[test]
fn depth_of_test() { fn depth_of_test() {
let mut futm = FutureMoves::new(Piece::Black, Default::default()); let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, i: 0,
@ -554,6 +562,7 @@ mod tests {
self_value: 0, self_value: 0,
color: Piece::Black, color: Piece::Black,
is_trimmed: false, is_trimmed: false,
tried_children: false,
}); });
futm.update_root_idx_raw(0); futm.update_root_idx_raw(0);
@ -587,7 +596,7 @@ mod tests {
#[test] #[test]
fn by_depth_test() { fn by_depth_test() {
let mut futm = FutureMoves::new(Piece::Black, Default::default()); let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, i: 0,
@ -600,6 +609,7 @@ mod tests {
self_value: 0, self_value: 0,
color: Piece::Black, color: Piece::Black,
is_trimmed: false, is_trimmed: false,
tried_children: false,
}); });
futm.update_root_idx_raw(0); futm.update_root_idx_raw(0);

View File

@ -22,6 +22,9 @@ pub struct Move {
/// Indices of this Move's Children /// Indices of this Move's Children
pub children: Vec<usize>, pub children: Vec<usize>,
/// Has this [`Move`] already attempted to create children?
pub tried_children: bool,
/// Value of this move (including children) /// Value of this move (including children)
pub value: Option<i128>, pub value: Option<i128>,
@ -52,6 +55,7 @@ impl Move {
color, color,
is_trimmed: false, is_trimmed: false,
self_value: 0, self_value: 0,
tried_children: false,
}; };
m.self_value = m.compute_self_value(agent_color); m.self_value = m.compute_self_value(agent_color);
m m