From 12276bc3544e5b5d45959ab4a8c536d1f20329ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20K=C3=A4nner?= Date: Thu, 3 Oct 2024 05:11:33 +0200 Subject: [PATCH] allow terminating in progress simulations --- battlesnake/src/logic.rs | 67 +++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/battlesnake/src/logic.rs b/battlesnake/src/logic.rs index 8a670f9..58d047a 100644 --- a/battlesnake/src/logic.rs +++ b/battlesnake/src/logic.rs @@ -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 { + fn monte_carlo_step( + &mut self, + board: &mut simulation::Board, + deadline: &Instant, + ) -> Result, 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 { + deadline: &Instant, + ) -> Result, 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) } }