From 1d527a89cdfac393c38b44a6f388dcea7928d83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20K=C3=A4nner?= Date: Mon, 21 Apr 2025 21:48:11 +0200 Subject: [PATCH] make simulation faster --- battlesnake/src/types/simulation.rs | 84 ++++++++++++++++------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/battlesnake/src/types/simulation.rs b/battlesnake/src/types/simulation.rs index 2aa9d50..63dd247 100644 --- a/battlesnake/src/types/simulation.rs +++ b/battlesnake/src/types/simulation.rs @@ -224,7 +224,7 @@ impl Board { #[must_use] pub fn is_free(&self, tile: Coord) -> bool { - if !(tile.x < self.width && tile.y < self.height) { + if !self.is_in_bounds(tile) { return false; } let index = self.coord_to_linear(tile); @@ -286,10 +286,7 @@ impl Board { fn valid_actions_index(&self, index: usize) -> impl Iterator + use<'_> { let head = self.snakes[index].head(); enum_iterator::all::() - .map(move |direction| (direction, head.wrapping_apply(direction))) - .filter(|(_, tile)| self.is_in_bounds(*tile)) - .filter(|(_, tile)| self.is_free(*tile)) - .map(|(direction, _)| direction) + .filter(move |direction| self.is_free(head.wrapping_apply(*direction))) } fn move_standard(&mut self, actions: &[(u8, Direction)]) { @@ -432,44 +429,57 @@ impl Board { } fn spawn_food(&mut self) { - let num_food = self.food.count_ones(); - let needed_food = if num_food < usize::from(self.min_food) { - usize::from(self.min_food) - num_food - } else { - usize::from( - self.food_spawn_chance > 0 && rng().random_range(0..100) < self.food_spawn_chance, - ) - }; + let food_needed = self.check_food_needing_placement(); - if needed_food == 0 { - return; + if food_needed > 0 { + self.place_food_randomly(food_needed); + } + } + + fn check_food_needing_placement(&self) -> 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); + + 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 { + return 1; } - let food_spots = self + 0 + } + + fn place_food_randomly(&mut self, amount: u16) { + let tails: Vec<_> = self + .snakes + .iter() + .map(|snake| self.coord_to_linear(snake.tail())) + .collect(); + let possible_moves: Vec<_> = self + .snakes + .iter() + .flat_map(|snake| { + let head = snake.head(); + enum_iterator::all::() + .map(move |direction| head.wrapping_apply(direction)) + .filter(|tile| self.is_in_bounds(*tile)) + .map(|tile| self.coord_to_linear(tile)) + }) + .collect(); + let unoccupied_points = self .free .iter() + .by_vals() .enumerate() - .filter_map(|(i, free)| free.then_some(i)) - .filter(|i| { - self.snakes - .iter() - .all(|snake| self.coord_to_linear(snake.tail()) != *i) - }) - .filter(|i| { - self.snakes - .iter() - .flat_map(|snake| { - let head = snake.head(); - enum_iterator::all::() - .map(move |direction| head.wrapping_apply(direction)) - .filter(|tile| self.is_in_bounds(*tile)) - }) - .all(|action| *i != self.coord_to_linear(action)) - }) - .filter(|i| !self.food[*i]) - .choose_multiple(&mut rng(), needed_food); - for index in food_spots { - self.food.set(index, true); + .zip(self.hazard.iter().by_vals()) + .filter_map(|((i, free), hazard)| (!hazard && free).then_some(i)) + .filter(|i| !tails.contains(i)) + .filter(|i| !possible_moves.contains(i)); + + for food_spot in unoccupied_points.choose_multiple(&mut rng(), usize::from(amount)) { + self.food.set(food_spot, true); } }