allow move arena to properly handle skipping a move

This commit is contained in:
Simon Gardling 2025-02-26 22:58:47 -05:00
parent 4a82c01d64
commit d3cac6ae91
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
2 changed files with 43 additions and 56 deletions

View File

@ -120,16 +120,22 @@ impl FutureMoves {
// use [`Board::all_positions`] here instead of [`Board::possible_moves`] // 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`] // because we use [`Board::what_if`] later and we want to reduce calls to [`Board::propegate_from_dry`]
let new: Vec<Move> = Board::all_positions() let mut new: Vec<Move> = Board::all_positions()
.flat_map(|(i, j)| { .flat_map(|(i, j)| {
parent parent
.board .board
.what_if(i, j, new_color) .what_if(i, j, new_color)
.map(move |x| (i, j, x)) .map(move |x| (i, j, x))
}) })
.map(|(i, j, new_board)| Move::new(i, j, new_board, new_color, self.agent_color)) .map(|(i, j, new_board)| {
Move::new(Some((i, j)), new_board, new_color, self.agent_color)
})
.collect(); .collect();
if new.is_empty() {
new.push(Move::new(None, parent.board, new_color, self.agent_color));
}
let start_idx = self.arena.len(); let start_idx = self.arena.len();
self.arena.extend(new); self.arena.extend(new);
@ -224,7 +230,7 @@ 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"
); );
}) })
.map(|&x| self.arena[x].coords()) .and_then(|&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
@ -248,13 +254,8 @@ impl FutureMoves {
/// Clear the arena and create and set a root which contains a Board /// Clear the arena and create and set a root which contains a Board
pub fn set_root_from_board(&mut self, board: Board) { pub fn set_root_from_board(&mut self, board: Board) {
self.arena.clear(); self.arena.clear();
self.arena.push(Move::new( self.arena
0, // dummy .push(Move::new(None, board, !self.agent_color, self.agent_color));
0, // dummy
board,
!self.agent_color,
self.agent_color,
));
// because we have to regenerate root from a [`Board`] // because we have to regenerate root from a [`Board`]
// we need to reset the current_depth (fixes `skip_move_recovery`) // we need to reset the current_depth (fixes `skip_move_recovery`)
self.current_depth = 0; self.current_depth = 0;
@ -275,7 +276,7 @@ impl FutureMoves {
self.arena self.arena
.iter() .iter()
.enumerate() .enumerate()
.find(|(_, node)| node.parent == self.current_root && node.coords() == (i, j)) .find(|(_, node)| node.parent == self.current_root && node.coord == Some((i, j)))
.map(|x| x.0) .map(|x| x.0)
// do raw set so we can prune it on the next move (in `update`) // do raw set so we can prune it on the next move (in `update`)
.inspect(|&root| self.update_root_idx_raw(root)) .inspect(|&root| self.update_root_idx_raw(root))
@ -470,8 +471,7 @@ mod tests {
let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG); let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, coord: None,
j: 0,
board: Board::new(), board: Board::new(),
winner: Winner::None, winner: Winner::None,
parent: None, parent: None,
@ -487,13 +487,12 @@ mod tests {
// child 1 // child 1
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(0, 1); futm.set_parent_child(0, 1);
// dummy (2) // dummy (2)
futm.arena.push(Move::new( futm.arena.push(Move::new(
1234, Some((1234, 1234)),
1234,
Board::new(), Board::new(),
Piece::White, Piece::White,
Piece::Black, Piece::Black,
@ -501,12 +500,12 @@ mod tests {
// 3 // 3
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(0, 3); futm.set_parent_child(0, 3);
// 4 // 4
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(0, 4); futm.set_parent_child(0, 4);
assert_eq!(futm.arena_len(), 5); assert_eq!(futm.arena_len(), 5);
@ -515,7 +514,11 @@ mod tests {
assert_eq!(futm.arena_len(), 4); assert_eq!(futm.arena_len(), 4);
assert_eq!(futm.arena[0].children.len(), 3); assert_eq!(futm.arena[0].children.len(), 3);
assert_ne!(futm.arena[2].i, 1234, "dummy value still exists"); assert_ne!(
futm.arena[2].coord,
Some((1234, 1234)),
"dummy value still exists"
);
} }
#[test] #[test]
@ -524,8 +527,7 @@ mod tests {
futm.config.max_depth = 1; futm.config.max_depth = 1;
futm.arena.push(Move::new( futm.arena.push(Move::new(
0, None,
0,
Board::new().starting_pos(), Board::new().starting_pos(),
Piece::Black, Piece::Black,
Piece::Black, Piece::Black,
@ -556,8 +558,7 @@ mod tests {
let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG); let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, coord: None,
j: 0,
board: Board::new(), board: Board::new(),
winner: Winner::None, winner: Winner::None,
parent: None, parent: None,
@ -573,25 +574,20 @@ mod tests {
// child 1 // child 1
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(0, 1); futm.set_parent_child(0, 1);
// dummy // dummy
futm.arena.push(Move::new( futm.arena
1234, .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
1234,
Board::new(),
Piece::White,
Piece::Black,
));
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(1, 3); futm.set_parent_child(1, 3);
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(0, 4); futm.set_parent_child(0, 4);
@ -603,8 +599,7 @@ mod tests {
let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG); let mut futm = FutureMoves::new(Piece::Black, FUTURE_MOVES_CONFIG);
futm.arena.push(Move { futm.arena.push(Move {
i: 0, coord: None,
j: 0,
board: Board::new(), board: Board::new(),
winner: Winner::None, winner: Winner::None,
parent: None, parent: None,
@ -620,20 +615,15 @@ mod tests {
// child 1 // child 1
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(0, 1); futm.set_parent_child(0, 1);
// dummy // dummy
futm.arena.push(Move::new( futm.arena
1234, .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
1234,
Board::new(),
Piece::White,
Piece::Black,
));
futm.arena futm.arena
.push(Move::new(0, 0, Board::new(), Piece::White, Piece::Black)); .push(Move::new(None, Board::new(), Piece::White, Piece::Black));
futm.set_parent_child(1, 3); futm.set_parent_child(1, 3);
assert_eq!( assert_eq!(

View File

@ -4,11 +4,8 @@ use lazy_static::lazy_static;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Move { pub struct Move {
/// `i` position of move /// Coordinates (i, j) of the move (if it exists)
pub i: usize, pub coord: Option<(usize, usize)>,
/// `j` position of move
pub j: usize,
/// [`Board`] state after move is made /// [`Board`] state after move is made
pub board: Board, pub board: Board,
@ -43,10 +40,14 @@ lazy_static! {
} }
impl Move { impl Move {
pub fn new(i: usize, j: usize, board: Board, color: Piece, agent_color: Piece) -> Self { pub fn new(
coord: Option<(usize, usize)>,
board: Board,
color: Piece,
agent_color: Piece,
) -> Self {
let mut m = Move { let mut m = Move {
i, coord,
j,
board, board,
winner: board.game_winner(), winner: board.game_winner(),
parent: None, parent: None,
@ -61,10 +62,6 @@ impl Move {
m m
} }
pub const fn coords(&self) -> (usize, usize) {
(self.i, self.j)
}
fn compute_self_value(&self, agent_color: Piece) -> i64 { fn compute_self_value(&self, agent_color: Piece) -> i64 {
if self.winner == Winner::Player(!agent_color) { if self.winner == Winner::Player(!agent_color) {
// if this board results in the opponent winning, MAJORLY negatively weigh this move // if this board results in the opponent winning, MAJORLY negatively weigh this move