solo mode (not verry good)
This commit is contained in:
parent
23be6f1a18
commit
11ba9b7c94
@ -186,7 +186,11 @@ pub fn get_move(game: &Game, turn: i32, board: &Board, you: &Battlesnake) -> Opt
|
||||
|
||||
while start.elapsed() < game_info.calculation_time.get() {
|
||||
let mut board = board.clone();
|
||||
tree.monte_carlo_step(&mut board);
|
||||
if game.ruleset.name == "solo" {
|
||||
tree.monte_carlo_step_solo(&mut board);
|
||||
} else {
|
||||
tree.monte_carlo_step(&mut board);
|
||||
}
|
||||
}
|
||||
|
||||
let actions = tree.child_statistics.entry(game_info.my_token).or_default();
|
||||
@ -299,4 +303,88 @@ impl Node {
|
||||
}
|
||||
winner
|
||||
}
|
||||
|
||||
/// Performs one monte carlo simulation step for a solo game
|
||||
///
|
||||
/// Returns the lengths before death
|
||||
fn monte_carlo_step_solo(
|
||||
&mut self,
|
||||
board: &mut simulation::Board,
|
||||
) -> BTreeMap<SnakeToken, usize> {
|
||||
let lengths = if self.statistic.played == 0 {
|
||||
// We didn't simulate a game for this node yet. Do that
|
||||
let mut lengths: BTreeMap<_, _> = board
|
||||
.snakes()
|
||||
.filter_map(|snake| Some((snake, board.snake_length(snake)?)))
|
||||
.collect();
|
||||
board.simulate_until(|board| {
|
||||
for snake in board.snakes() {
|
||||
if let Some(length) = board.snake_length(snake) {
|
||||
lengths.insert(snake, length);
|
||||
}
|
||||
}
|
||||
board.alive_snakes() == 0
|
||||
});
|
||||
lengths
|
||||
} else {
|
||||
// select a node to simulate
|
||||
let possible_actions = board.possible_actions();
|
||||
|
||||
let actions = possible_actions
|
||||
.iter()
|
||||
.filter_map(|(token, actions)| {
|
||||
let statistics = self.child_statistics.entry(*token).or_default();
|
||||
let selected = actions.iter().copied().max_by_key(|direction| {
|
||||
let statistics = statistics.entry(*direction).or_default();
|
||||
if statistics.played == 0 {
|
||||
return OrderedFloat(f64::INFINITY);
|
||||
}
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let exploitation = statistics.won as f64
|
||||
/ board.spaces() as f64
|
||||
/ statistics.played as f64;
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let exploration = f64::consts::SQRT_2
|
||||
* f64::sqrt(
|
||||
f64::ln(self.statistic.played as f64) / statistics.played as f64,
|
||||
);
|
||||
OrderedFloat(exploitation + exploration)
|
||||
})?;
|
||||
Some((*token, selected))
|
||||
})
|
||||
.collect();
|
||||
|
||||
board.simulate_actions(&actions);
|
||||
let lengths = self
|
||||
.childs
|
||||
.entry(actions.clone())
|
||||
.or_default()
|
||||
.monte_carlo_step_solo(board);
|
||||
|
||||
// update child statistics
|
||||
for (token, action) in &actions {
|
||||
let entry = self
|
||||
.child_statistics
|
||||
.entry(*token)
|
||||
.or_default()
|
||||
.entry(*action)
|
||||
.or_default();
|
||||
entry.played += 1;
|
||||
if let Some(length) = lengths.get(&token) {
|
||||
entry.won += length;
|
||||
}
|
||||
}
|
||||
|
||||
lengths
|
||||
};
|
||||
self.statistic.played += 1;
|
||||
for (token, length) in &lengths {
|
||||
self.statistic
|
||||
.won
|
||||
.entry(*token)
|
||||
.and_modify(|won| *won += length)
|
||||
.or_insert(*length);
|
||||
}
|
||||
lengths
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +81,11 @@ impl Board {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
pub const fn spaces(&self) -> usize {
|
||||
self.height as usize * self.width as usize
|
||||
}
|
||||
|
||||
pub fn alive_snakes(&self) -> usize {
|
||||
self.snakes.len()
|
||||
}
|
||||
@ -89,6 +94,10 @@ impl Board {
|
||||
self.snakes.keys().copied()
|
||||
}
|
||||
|
||||
pub fn snake_length(&self, token: SnakeToken) -> Option<usize> {
|
||||
self.snakes.get(&token).map(|snake| snake.body.len())
|
||||
}
|
||||
|
||||
pub fn simulate_actions(&mut self, actions: &BTreeMap<SnakeToken, Direction>) {
|
||||
// move snakes
|
||||
for (token, snake) in &mut self.snakes {
|
||||
@ -158,7 +167,7 @@ impl Board {
|
||||
self.turn += 1;
|
||||
}
|
||||
|
||||
pub fn simulate_until(&mut self, exit: impl Fn(&Self) -> bool) {
|
||||
pub fn simulate_until(&mut self, mut exit: impl FnMut(&Self) -> bool) {
|
||||
while !exit(self) {
|
||||
let actions = self
|
||||
.possible_actions()
|
||||
|
Loading…
Reference in New Issue
Block a user