properly handle skipping a move

This commit is contained in:
Simon Gardling 2025-02-27 09:41:56 -05:00
parent 8719d31c38
commit 2c31589e51
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
2 changed files with 37 additions and 37 deletions

View File

@ -32,11 +32,9 @@ impl Agent for ComplexAgent {
println!("# of moves stored: {}", self.future_moves.arena_len()); println!("# of moves stored: {}", self.future_moves.arena_len());
self.future_moves.best_move().inspect(|&(i, j)| { self.future_moves
if !self.future_moves.update_root_coord(i, j) { .best_move()
panic!("update_root_coord failed"); .expect("FutureMoves has no move?")
}
})
} }
fn name(&self) -> &'static str { fn name(&self) -> &'static str {

View File

@ -216,7 +216,7 @@ impl FutureMoves {
} }
/// Return the best move which is a child of `self.current_root` /// Return the best move which is a child of `self.current_root`
pub fn best_move(&self) -> Option<(Coord, Coord)> { pub fn best_move(&self) -> Option<Option<(Coord, Coord)>> {
self.current_root self.current_root
.and_then(|x| { .and_then(|x| {
self.arena[x] self.arena[x]
@ -230,25 +230,32 @@ impl FutureMoves {
"selected move color should be the same as the color of the agent" "selected move color should be the same as the color of the agent"
); );
}) })
.and_then(|&x| self.arena[x].coord) .map(|&x| self.arena[x].coord)
} }
/// Updates `FutureMoves` based on the current state of the board /// Updates `FutureMoves` based on the current state of the board
/// The board is supposed to be after the opposing move /// The board is supposed to be after the opposing move
pub fn update_from_board(&mut self, board: &Board) { /// Returns whether or not the arena was regenerated (bool)
pub fn update_from_board(&mut self, board: &Board) -> bool {
let curr_board = self let curr_board = self
.arena .arena
.iter() .iter()
.enumerate() .enumerate()
.find(|(_, m)| &m.board == board && (m.parent == self.current_root)) .filter(|(idx, _)| {
.map(|(idx, _)| idx) self.current_root
.filter(|_| self.current_root.is_some()); .map(|x| self.arena[x].children.contains(idx))
.unwrap_or(false)
})
.find(|(_, m)| &m.board == board)
.map(|(idx, _)| idx);
if let Some(curr_board_idx) = curr_board { if let Some(curr_board_idx) = curr_board {
self.set_root_idx_raw(curr_board_idx); self.set_root_idx_raw(curr_board_idx);
return false;
} else { } else {
dbg!("regenerating arena from board"); dbg!("regenerating arena from board");
self.set_root_from_board(*board); self.set_root_from_board(*board);
return true;
} }
} }
@ -264,26 +271,6 @@ impl FutureMoves {
self.set_root_idx_raw(0); self.set_root_idx_raw(0);
} }
/// Update the root based on the coordinate of the move
/// Returns a boolean, `true` if the operation was successful, false if not
#[must_use = "You must check if the root was properly set"]
pub fn update_root_coord(&mut self, i: Coord, j: Coord) -> bool {
// check to make sure current_root is some so we dont
// have to do that in the iterator
if self.current_root.is_none() {
return false;
}
self.arena
.iter()
.enumerate()
.find(|(_, node)| node.parent == self.current_root && node.coord == Some((i, j)))
.map(|x| x.0)
// do raw set so we can prune it on the next move (in `update`)
.inspect(|&root| self.update_root_idx_raw(root))
.is_some()
}
/// Update current root without modifying or pruning the Arena /// Update current root without modifying or pruning the Arena
fn update_root_idx_raw(&mut self, idx: usize) { fn update_root_idx_raw(&mut self, idx: usize) {
self.current_root = Some(idx); self.current_root = Some(idx);
@ -461,8 +448,8 @@ mod tests {
const FUTURE_MOVES_CONFIG: FutureMoveConfig = FutureMoveConfig { const FUTURE_MOVES_CONFIG: FutureMoveConfig = FutureMoveConfig {
max_depth: 1, max_depth: 1,
min_arena_depth_sub: 2, min_arena_depth_sub: 0,
top_k_children: 2, top_k_children: 1,
up_to_minus: 0, up_to_minus: 0,
max_arena_size: 100, max_arena_size: 100,
}; };
@ -662,17 +649,32 @@ mod tests {
(Some((4, 2)), Piece::White), (Some((4, 2)), Piece::White),
]; ];
for (coords, color) in moves { for (i, (coords, color)) in moves.into_iter().enumerate() {
if color == futm.agent_color { if color == futm.agent_color {
// my turn // my turn
futm.update_from_board(&board);
// seperate variable because we want it to always be executed
let update_result = futm.update_from_board(&board);
// make sure that the arena should only be
// regenerated on the first move
assert!(i <= 0 || update_result, "board regenerated on move #{}", i);
let best_move = futm.best_move(); let best_move = futm.best_move();
assert!(
best_move.is_some(),
"best_move (#{}) resulted in ABSOLUTE None: {:?}",
i,
best_move
);
if coords.is_none() { if coords.is_none() {
assert_eq!(best_move, None); assert_eq!(best_move, Some(None));
} else { } else {
assert_ne!(best_move, None); assert_ne!(best_move, Some(None));
} }
} }
if let Some((i, j)) = coords { if let Some((i, j)) = coords {
board.place(i, j, color).unwrap(); board.place(i, j, color).unwrap();
} }