allow terminating in progress simulations

This commit is contained in:
Max Känner 2024-10-03 05:11:33 +02:00
parent d28eec7ef2
commit 12276bc354

View File

@ -100,6 +100,22 @@ pub fn get_move(game: &Game, turn: i32, board: &Board, you: &Battlesnake) -> Opt
}
});
let board = simulation::Board::from_game_board(
board,
&game_info.token_mapping,
turn,
game.ruleset.settings.food_spawn_chance,
game.ruleset.settings.minimum_food,
);
let possible_actions = board.possible_actions().get(&game_info.my_token).cloned()?;
if possible_actions.len() == 1 {
info!("Only one movement option exists. Skipping Tree evaluation");
return possible_actions.first().map(|direction| Action {
r#move: *direction,
shout: None,
});
}
// do some latency compensation
game_info.calculation_time.set(
game_info.calculation_time.get()
@ -109,23 +125,16 @@ pub fn get_move(game: &Game, turn: i32, board: &Board, you: &Battlesnake) -> Opt
) / 3,
),
);
let board = simulation::Board::from_game_board(
board,
&game_info.token_mapping,
turn,
game.ruleset.settings.food_spawn_chance,
game.ruleset.settings.minimum_food,
);
let deadline = start + game_info.calculation_time.get();
let mut tree = Node::default();
while start.elapsed() < game_info.calculation_time.get() {
while Instant::now() < deadline {
let mut board = board.clone();
if game.ruleset.name == "solo" {
tree.monte_carlo_step_solo(&mut board);
let _ = tree.monte_carlo_step_solo(&mut board, &deadline);
} else {
tree.monte_carlo_step(&mut board);
let _ = tree.monte_carlo_step(&mut board, &deadline);
}
}
@ -164,6 +173,8 @@ struct ActionStatistic {
won: usize,
}
struct DeadlineError;
#[derive(Debug, PartialEq, Eq, Clone, Default)]
struct Node {
statistic: Statistics,
@ -175,10 +186,17 @@ impl Node {
/// Performs one monte carlo simulation step
///
/// Returns the snake that has won the simulation
fn monte_carlo_step(&mut self, board: &mut simulation::Board) -> Option<SnakeToken> {
fn monte_carlo_step(
&mut self,
board: &mut simulation::Board,
deadline: &Instant,
) -> Result<Option<SnakeToken>, DeadlineError> {
let winner = if self.statistic.played == 0 {
// We didn't simulate a game for this node yet. Do that
board.simulate_until(|board| board.alive_snakes() <= 1);
board.simulate_until(|board| board.alive_snakes() <= 1 || Instant::now() >= *deadline);
if Instant::now() >= *deadline {
return Err(DeadlineError);
}
board.snakes().next()
} else {
// select a node to simulate
@ -206,12 +224,15 @@ impl Node {
})
.collect();
if Instant::now() >= *deadline {
return Err(DeadlineError);
}
board.simulate_actions(&actions);
let winner = self
.childs
.entry(actions.clone())
.or_default()
.monte_carlo_step(board);
.monte_carlo_step(board, deadline)?;
// update child statistics
for (token, action) in &actions {
@ -237,7 +258,7 @@ impl Node {
.and_modify(|won| *won += 1)
.or_insert(1);
}
winner
Ok(winner)
}
/// Performs one monte carlo simulation step for a solo game
@ -246,7 +267,8 @@ impl Node {
fn monte_carlo_step_solo(
&mut self,
board: &mut simulation::Board,
) -> BTreeMap<SnakeToken, usize> {
deadline: &Instant,
) -> Result<BTreeMap<SnakeToken, usize>, DeadlineError> {
let lengths = if self.statistic.played == 0 {
// We didn't simulate a game for this node yet. Do that
let mut lengths: BTreeMap<_, _> = board
@ -254,6 +276,9 @@ impl Node {
.filter_map(|snake| Some((snake, board.snake_length(snake)?)))
.collect();
board.simulate_until(|board| {
if Instant::now() >= *deadline {
return true;
}
for snake in board.snakes() {
if let Some(length) = board.snake_length(snake) {
lengths.insert(snake, length);
@ -261,6 +286,9 @@ impl Node {
}
board.alive_snakes() == 0
});
if Instant::now() >= *deadline {
return Err(DeadlineError);
}
lengths
} else {
// select a node to simulate
@ -290,12 +318,15 @@ impl Node {
})
.collect();
if Instant::now() >= *deadline {
return Err(DeadlineError);
}
board.simulate_actions(&actions);
let lengths = self
.childs
.entry(actions.clone())
.or_default()
.monte_carlo_step_solo(board);
.monte_carlo_step_solo(board, deadline)?;
// update child statistics
for (token, action) in &actions {
@ -321,6 +352,6 @@ impl Node {
.and_modify(|won| *won += length)
.or_insert(*length);
}
lengths
Ok(lengths)
}
}