reuse old simulation states

This commit is contained in:
Max Känner 2024-10-07 10:17:02 +02:00
parent 4e44ac548c
commit f3d7c3160d
2 changed files with 59 additions and 9 deletions

View File

@ -48,6 +48,7 @@ struct GameInfo {
calculation_time: Arc<Mutex<Duration>>, calculation_time: Arc<Mutex<Duration>>,
token_mapping: Arc<BTreeMap<String, SnakeToken>>, token_mapping: Arc<BTreeMap<String, SnakeToken>>,
my_token: SnakeToken, my_token: SnakeToken,
tree: Arc<Mutex<Option<Node>>>,
} }
static GAME_INFOS: LazyLock<Mutex<HashMap<(String, String), GameInfo>>> = static GAME_INFOS: LazyLock<Mutex<HashMap<(String, String), GameInfo>>> =
@ -70,6 +71,7 @@ pub fn start(game: &Game, _turn: i32, board: &Board, you: &Battlesnake) {
))), ))),
token_mapping, token_mapping,
my_token, my_token,
tree: Arc::new(Mutex::new(None)),
}, },
); );
} }
@ -114,6 +116,7 @@ pub fn get_move(
))), ))),
token_mapping, token_mapping,
my_token, my_token,
tree: Arc::new(Mutex::new(None)),
} }
}); });
@ -126,12 +129,9 @@ pub fn get_move(
game.ruleset.name == "constrictor", game.ruleset.name == "constrictor",
); );
let possible_actions = board.possible_actions().get(&game_info.my_token).cloned()?; let possible_actions = board.possible_actions().get(&game_info.my_token).cloned()?;
if possible_actions.len() <= 1 { if possible_actions.is_empty() {
info!("Only one movement option exists in turn {turn}. Skipping Tree evaluation"); info!("No movement options in turn {turn}");
return possible_actions.first().map(|direction| Action { return None;
r#move: *direction,
shout: None,
});
} }
// do some latency compensation // do some latency compensation
@ -153,7 +153,45 @@ pub fn get_move(
}, },
); );
let mut tree = Node::default(); let mut tree_guard = game_info.tree.lock();
let tree = match tree_guard {
Err(ref e) => {
error!("unable to lock tree: {e}");
None
}
Ok(ref mut guard) => guard.as_mut(),
};
let mut tree = tree
.and_then(|node| {
let snake_length_direction: BTreeMap<_, _> = board
.snakes()
.map(|snake| {
let length = board.snake_length(snake).unwrap_or_default();
let action = board.last_action(snake).unwrap_or(Direction::Up);
(snake, (action, length))
})
.collect();
let node_key = node
.childs
.keys()
.find(|child| {
child.iter().all(|(snake, (direction, length))| {
snake_length_direction
.get(snake)
.copied()
.unwrap_or((*direction, 0))
== (*direction, *length)
})
})?
.clone();
let node = node.childs.remove(&node_key)?;
info!(
"using previous node with {} simulations",
node.statistic.played
);
Some(node)
})
.unwrap_or_default();
while Instant::now() < deadline { while Instant::now() < deadline {
let mut board = board.clone(); let mut board = board.clone();
@ -175,6 +213,11 @@ pub fn get_move(
.map(|(direction, _)| *direction) .map(|(direction, _)| *direction)
.or_else(|| possible_actions.iter().choose(&mut thread_rng()).copied())?; .or_else(|| possible_actions.iter().choose(&mut thread_rng()).copied())?;
if let Ok(ref mut guard) = tree_guard {
**guard = Some(tree);
}
std::mem::drop(tree_guard);
info!( info!(
"DIRECTION {turn}: {chosen:?} after {}ms ({})", "DIRECTION {turn}: {chosen:?} after {}ms ({})",
start.elapsed().as_millis(), start.elapsed().as_millis(),

View File

@ -118,6 +118,14 @@ impl Board {
self.snakes.get(&token).map(|snake| snake.body.len()) self.snakes.get(&token).map(|snake| snake.body.len())
} }
#[must_use]
pub fn last_action(&self, token: SnakeToken) -> Option<Direction> {
self.snakes.get(&token).and_then(|snake| {
enum_iterator::all::<Direction>()
.find(|direction| snake.body[1].move_to(*direction) == *snake.head())
})
}
pub fn simulate_actions( pub fn simulate_actions(
&mut self, &mut self,
actions: &BTreeMap<SnakeToken, Direction>, actions: &BTreeMap<SnakeToken, Direction>,
@ -313,7 +321,6 @@ impl Battlesnake {
#[must_use] #[must_use]
pub fn head(&self) -> &Coord { pub fn head(&self) -> &Coord {
debug_assert!(!self.body.is_empty()); &self.body[0]
self.body.front().unwrap_or_else(|| unreachable!())
} }
} }