logical improvements and cleanup

This commit is contained in:
Simon Gardling 2025-02-20 20:52:59 -05:00
parent 2011ae001b
commit 3b07a67d2e
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
3 changed files with 48 additions and 31 deletions

View File

@ -12,10 +12,10 @@ pub struct ComplexAgent {
#[allow(dead_code)]
impl ComplexAgent {
pub const fn new(color: Piece) -> Self {
const MAX_DEPTH: usize = 12;
const MAX_DEPTH: usize = 10;
Self {
color,
future_moves: FutureMoves::new(color, MAX_DEPTH, 6),
future_moves: FutureMoves::new(color, MAX_DEPTH, 8),
}
}
}
@ -24,7 +24,7 @@ impl Agent for ComplexAgent {
fn next_move(&mut self, board: &Board) -> Option<(usize, usize)> {
self.future_moves.update(board);
println!("# of moves stored: {}", self.future_moves.len());
println!("# of moves stored: {}", self.future_moves.arena_len());
self.future_moves.best_move().inspect(|&(i, j)| {
if !self.future_moves.update_root_coord(i, j) {

View File

@ -37,14 +37,10 @@ impl FutureMoves {
}
}
pub fn len(&self) -> usize {
pub fn arena_len(&self) -> usize {
self.arena.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Generate children for all children of `nodes`
/// only `pub` for the sake of benchmarking
pub fn extend_layers(&mut self) {
@ -118,26 +114,17 @@ impl FutureMoves {
// 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)| parent.board.what_if(i, j, new_color).map(move |x| (i, j, x)))
.map(|(i, j, new_board)| Move {
i,
j,
board: new_board,
winner: new_board.game_winner(),
parent: Some(parent_idx),
children: Vec::new(),
value: 0,
color: new_color,
lazy_children,
}).collect();
.map(|(i, j, new_board)| Move::new(i, j, new_board, new_color, lazy_children, self.agent_color, Some(parent_idx))).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));
// abs because we want the most EXTREME possible outcome (win or lose for example)
new.sort_by_key(|x| -x.self_value.abs());
// keep the TOP_K children `self.agent_color`-color moves
const TOP_K_CHILDREN: usize = 1;
const TOP_K_CHILDREN: usize = 2;
// we want to keep only the best move of the agent
if lazy_children && new_color == self.agent_color && new.len() > TOP_K_CHILDREN {
if lazy_children && new.len() > TOP_K_CHILDREN {
new.drain(TOP_K_CHILDREN..);
}
@ -156,6 +143,7 @@ impl FutureMoves {
} else {
self.arena.extend(new);
}
let new_indices = start_idx..self.arena.len();
self.arena[parent_idx].children.extend(new_indices.clone());
@ -191,15 +179,14 @@ impl FutureMoves {
// TODO! impl dynamic sorting based on children's states, maybe it propegates
// upwards using the `parent` field
// SAFETY! the sort_by_key function should not modify anything
unsafe { (*(self as *mut Self)).arena.get_unchecked_mut(idx) }
unsafe { &mut (*(self as *mut Self)) }.arena[idx]
.children
// negative because we want the largest value in the first index
.sort_by_key(|&x| -self.arena[x].value);
// abs so we get the most extreme solutions
// but base on `.value` for recursive behavior
.sort_by_key(|&x| -self.arena[x].value.abs());
let node = &self.arena[idx];
let self_value = node.compute_self_value(self.agent_color) / (depth + 1) as i64;
let children_value = node
let children_value = self.arena[idx]
.children
.iter()
.rev() // rev then reverse so we get an index starting from the back
@ -207,10 +194,11 @@ impl FutureMoves {
// since children are sorted by value, we should weight the first one more
.map(|(i, &child)| self.arena[child].value * (i as i64 + 1))
.sum::<i64>()
.checked_div(node.children.len() as i64)
.checked_div((&self.arena[idx]).children.len() as i64)
.unwrap_or(0);
self.arena[idx].value = self_value + children_value;
self.arena[idx].value =
self.arena[idx].self_value / (depth as i64 + 1) + children_value;
}
}
}
@ -264,6 +252,7 @@ impl FutureMoves {
value: 0,
color: !self.agent_color,
lazy_children: false,
self_value: 0,
});
}

View File

@ -24,9 +24,11 @@ pub struct Move {
/// Indices of this Move's Children
pub children: Vec<usize>,
/// Value of this move
/// Value of this move (including children)
pub value: i64,
pub self_value: i64,
pub color: Piece,
pub lazy_children: bool,
@ -37,6 +39,32 @@ lazy_static! {
}
impl Move {
pub fn new(
i: usize,
j: usize,
board: Board,
color: Piece,
lazy_children: bool,
agent_color: Piece,
parent: Option<usize>,
) -> Self {
let mut m = Move {
i,
j,
board,
winner: board.game_winner(),
parent,
children: Vec::new(),
value: 0,
color,
lazy_children,
self_value: 0,
};
m.self_value = m.compute_self_value(agent_color);
m.value = m.self_value;
m
}
pub const fn coords(&self) -> (usize, usize) {
(self.i, self.j)
}