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