This commit is contained in:
parent
15d90357ec
commit
e5600fe038
@ -1,15 +1,16 @@
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion};
|
||||
use criterion::{Bencher, BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
||||
|
||||
use battlesnake::types::{
|
||||
Coord,
|
||||
simulation::Board,
|
||||
wire::{Battlesnake, Board as WireBoard, Game, Request, RoyaleSettings, Ruleset, Settings},
|
||||
Coord,
|
||||
};
|
||||
use rand::{SeedableRng, rngs::SmallRng};
|
||||
|
||||
fn create_start_snake(coord: Coord) -> Battlesnake {
|
||||
let id: Arc<str> = format!("{coord:?}").into();
|
||||
@ -74,7 +75,7 @@ fn standard(c: &mut Criterion) {
|
||||
let benchmark = |b: &mut Bencher, board: &Board| {
|
||||
b.iter(|| {
|
||||
let mut board = board.clone();
|
||||
let turn = board.simulate_random(|board| {
|
||||
let turn = board.simulate_random(&mut SmallRng::from_os_rng(), |board| {
|
||||
if board.num_snakes() <= 1 {
|
||||
Some(board.turn())
|
||||
} else {
|
||||
@ -151,7 +152,7 @@ fn constrictor(c: &mut Criterion) {
|
||||
let benchmark = |b: &mut Bencher, board: &Board| {
|
||||
b.iter(|| {
|
||||
let mut board = board.clone();
|
||||
let turn = board.simulate_random(|board| {
|
||||
let turn = board.simulate_random(&mut SmallRng::from_os_rng(), |board| {
|
||||
if board.num_snakes() <= 1 {
|
||||
Some(board.turn())
|
||||
} else {
|
||||
|
@ -13,7 +13,7 @@ use battlesnake::types::{
|
||||
};
|
||||
use float_ord::FloatOrd;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use rand::{prelude::*, rng};
|
||||
use rand::prelude::*;
|
||||
use serde::Serialize;
|
||||
use tokio::{
|
||||
net::TcpListener,
|
||||
@ -89,6 +89,7 @@ async fn get_move(request: Json<Request>) -> response::Json<Response> {
|
||||
info!("valid actions: {actions:?}");
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut rng = SmallRng::from_os_rng();
|
||||
if start.elapsed() > Duration::from_millis(10) {
|
||||
error!(
|
||||
"The calculation started late ({}ms)",
|
||||
@ -129,23 +130,22 @@ async fn get_move(request: Json<Request>) -> response::Json<Response> {
|
||||
.map(|id| MctsManager::new(u8::try_from(id).unwrap()))
|
||||
.collect();
|
||||
let c = f32::sqrt(2.0);
|
||||
let mut mcts_actions = Vec::new();
|
||||
while start.elapsed() < timeout * 4 / 5 {
|
||||
let mut board = board.clone();
|
||||
while end_condition(&board).is_none() {
|
||||
let actions: Vec<_> = mcts_managers
|
||||
.iter_mut()
|
||||
.filter_map(|mcts_manager| {
|
||||
mcts_manager
|
||||
.next_action(&board, c)
|
||||
.map(|action| (mcts_manager.snake, action))
|
||||
})
|
||||
.collect();
|
||||
board.next_turn(&actions);
|
||||
if actions.is_empty() {
|
||||
mcts_actions.clear();
|
||||
mcts_actions.extend(mcts_managers.iter_mut().filter_map(|mcts_manager| {
|
||||
mcts_manager
|
||||
.next_action(&board, c, &mut rng)
|
||||
.map(|action| (mcts_manager.snake, action))
|
||||
}));
|
||||
board.next_turn(&mcts_actions, &mut rng);
|
||||
if mcts_actions.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
board.simulate_random(end_condition);
|
||||
board.simulate_random(&mut rng, end_condition);
|
||||
for mcts_manager in &mut mcts_managers {
|
||||
let id = mcts_manager.snake;
|
||||
let score = score_fn(&board, id);
|
||||
@ -273,7 +273,7 @@ impl MctsManager {
|
||||
self.expanded = false;
|
||||
}
|
||||
|
||||
fn next_action(&mut self, board: &Board, c: f32) -> Option<Direction> {
|
||||
fn next_action(&mut self, board: &Board, c: f32, rng: &mut impl RngCore) -> Option<Direction> {
|
||||
if self.expanded {
|
||||
return None;
|
||||
}
|
||||
@ -294,11 +294,7 @@ impl MctsManager {
|
||||
.collect();
|
||||
trace!("got actions: {ucts:?}");
|
||||
if ucts.iter().any(|(_, uct)| uct.is_none()) {
|
||||
let action = ucts
|
||||
.iter()
|
||||
.filter(|(_, uct)| uct.is_none())
|
||||
.choose(&mut rng())?
|
||||
.0;
|
||||
let action = ucts.iter().filter(|(_, uct)| uct.is_none()).choose(rng)?.0;
|
||||
self.expanded = true;
|
||||
current.next[usize::from(action)].replace(ActionInfo::new());
|
||||
self.actions.push(action);
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
|
||||
use bitvec::prelude::*;
|
||||
use log::{error, warn};
|
||||
use rand::{prelude::*, rng};
|
||||
use rand::prelude::*;
|
||||
|
||||
use super::{Coord, Direction, wire::Request};
|
||||
|
||||
@ -256,43 +256,50 @@ impl Board {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn random_action(&self, id: u8) -> Direction {
|
||||
pub fn random_action(&self, id: u8, rng: &mut impl RngCore) -> Direction {
|
||||
let Some(index) = self.id_to_index(id) else {
|
||||
return Direction::Up;
|
||||
};
|
||||
self.valid_actions_index(index)
|
||||
.choose(&mut rng())
|
||||
.choose(rng)
|
||||
.unwrap_or(Direction::Up)
|
||||
}
|
||||
|
||||
pub fn random_actions(&self) -> impl Iterator<Item = (u8, Direction)> + use<'_> {
|
||||
pub fn random_actions<'a, R: RngCore>(
|
||||
&self,
|
||||
rng: &'a mut R,
|
||||
) -> impl Iterator<Item = (u8, Direction)> + use<'_, 'a, R> {
|
||||
(0..self.snakes.len()).map(|index| {
|
||||
(
|
||||
self.snakes[index].id,
|
||||
self.valid_actions_index(index)
|
||||
.choose(&mut rng())
|
||||
.choose(rng)
|
||||
.unwrap_or(Direction::Up),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn simulate_random<T>(&mut self, stop: impl Fn(&Self) -> Option<T>) -> T {
|
||||
pub fn simulate_random<T>(
|
||||
&mut self,
|
||||
rng: &mut impl RngCore,
|
||||
stop: impl Fn(&Self) -> Option<T>,
|
||||
) -> T {
|
||||
loop {
|
||||
if let Some(score) = stop(self) {
|
||||
break score;
|
||||
}
|
||||
self.next_turn(&[]);
|
||||
self.next_turn(&[], rng);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_turn(&mut self, actions: &[(u8, Direction)]) {
|
||||
self.move_standard(actions);
|
||||
pub fn next_turn(&mut self, actions: &[(u8, Direction)], rng: &mut impl RngCore) {
|
||||
self.move_standard(actions, rng);
|
||||
self.starvation_standard();
|
||||
self.hazard_damage_standard();
|
||||
self.feed_snakes_standard();
|
||||
self.eliminate_snake_standard();
|
||||
self.update_free_map();
|
||||
self.spawn_food();
|
||||
self.spawn_food(rng);
|
||||
self.turn += 1;
|
||||
}
|
||||
|
||||
@ -306,13 +313,13 @@ impl Board {
|
||||
.filter(move |direction| self.is_free(head.wrapping_apply(*direction)))
|
||||
}
|
||||
|
||||
fn move_standard(&mut self, actions: &[(u8, Direction)]) {
|
||||
fn move_standard(&mut self, actions: &[(u8, Direction)], rng: &mut impl RngCore) {
|
||||
for i in 0..self.snakes.len() {
|
||||
let snake = &self.snakes[i];
|
||||
let action = actions.iter().find(|(id, _)| *id == snake.id).map_or_else(
|
||||
|| {
|
||||
self.valid_actions_index(i)
|
||||
.choose(&mut rng())
|
||||
.choose(rng)
|
||||
.unwrap_or(Direction::Up)
|
||||
},
|
||||
|(_, action)| *action,
|
||||
@ -445,15 +452,15 @@ impl Board {
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_food(&mut self) {
|
||||
let food_needed = self.check_food_needing_placement();
|
||||
fn spawn_food(&mut self, rng: &mut impl RngCore) {
|
||||
let food_needed = self.check_food_needing_placement(rng);
|
||||
|
||||
if food_needed > 0 {
|
||||
self.place_food_randomly(food_needed);
|
||||
self.place_food_randomly(food_needed, rng);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_food_needing_placement(&self) -> u16 {
|
||||
fn check_food_needing_placement(&self, rng: &mut impl RngCore) -> u16 {
|
||||
let min_food = self.min_food;
|
||||
let food_spawn_chance = self.food_spawn_chance;
|
||||
let num_current_food = u16::try_from(self.food.count_ones()).unwrap_or(u16::MAX);
|
||||
@ -461,14 +468,14 @@ impl Board {
|
||||
if num_current_food < min_food {
|
||||
return min_food - num_current_food;
|
||||
}
|
||||
if food_spawn_chance > 0 && (100 - rng().random_range(0..100)) < food_spawn_chance {
|
||||
if food_spawn_chance > 0 && (100 - rng.random_range(0..100)) < food_spawn_chance {
|
||||
return 1;
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn place_food_randomly(&mut self, amount: u16) {
|
||||
fn place_food_randomly(&mut self, amount: u16, rng: &mut impl RngCore) {
|
||||
let tails: Vec<_> = self
|
||||
.snakes
|
||||
.iter()
|
||||
@ -495,7 +502,7 @@ impl Board {
|
||||
.filter(|i| !tails.contains(i))
|
||||
.filter(|i| !possible_moves.contains(i));
|
||||
|
||||
for food_spot in unoccupied_points.choose_multiple(&mut rng(), usize::from(amount)) {
|
||||
for food_spot in unoccupied_points.choose_multiple(rng, usize::from(amount)) {
|
||||
self.food.set(food_spot, true);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user