This commit is contained in:
		| @@ -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); | ||||
|         } | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user