check for initial food placement
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Build / build (push) Successful in 1m59s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Build / build (push) Successful in 1m59s
				
			This commit is contained in:
		| @@ -240,7 +240,8 @@ struct PreparedGame { | ||||
|     corners: [Option<u8>; 4], | ||||
|     cardinals: [Option<u8>; 4], | ||||
|     corner_first: bool, | ||||
|     food_spawns: Vec<FoodSpawn>, | ||||
|     initial_food: Box<[(u8, u8)]>, | ||||
|     food_spawns: Box<[FoodSpawn]>, | ||||
|     food_chance: u8, | ||||
| } | ||||
|  | ||||
| @@ -258,42 +259,116 @@ enum FoodSpawn { | ||||
|  | ||||
| impl PreparedGame { | ||||
|     fn prepare(requests: &[Request]) -> Self { | ||||
|         debug!("Preparing initial board state"); | ||||
|         let request = &requests[0]; | ||||
|         assert_eq!(request.turn, 0); | ||||
|         let mn = 1; | ||||
|         let md = (request.board.width - 1) / 2; | ||||
|         let mx = request.board.width - 2; | ||||
|         let corners = [ | ||||
|             Coord { x: mn, y: mn }, | ||||
|             Coord { x: mn, y: mx }, | ||||
|             Coord { x: mx, y: mn }, | ||||
|             Coord { x: mx, y: mx }, | ||||
|         ]; | ||||
|         let cardinals = [ | ||||
|             Coord { x: mn, y: md }, | ||||
|             Coord { x: md, y: mn }, | ||||
|             Coord { x: md, y: mx }, | ||||
|             Coord { x: mx, y: md }, | ||||
|         ]; | ||||
|         let corner_first = corners.contains(&request.board.snakes[0].head); | ||||
|         let corners = corners.map(|start| { | ||||
|             request | ||||
|                 .board | ||||
|                 .snakes | ||||
|                 .iter() | ||||
|                 .position(|snake| snake.head == start) | ||||
|                 .map(|pos| (pos % 4).az()) | ||||
|         }); | ||||
|         let cardinals = cardinals.map(|start| { | ||||
|             request | ||||
|                 .board | ||||
|                 .snakes | ||||
|                 .iter() | ||||
|                 .position(|snake| snake.head == start) | ||||
|                 .map(|pos| (pos % 4).az()) | ||||
|         }); | ||||
|         let (corner_first, corners, cardinals) = prepare_starts(request); | ||||
|  | ||||
|         let initial_food = prepare_initial_food(request); | ||||
|  | ||||
|         let (food_chance, food_spawns) = prepare_food_placement(requests, request); | ||||
|  | ||||
|         Self { | ||||
|             corners, | ||||
|             cardinals, | ||||
|             corner_first, | ||||
|             initial_food, | ||||
|             food_spawns, | ||||
|             food_chance, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn validate(&self, input_rand: &mut SeedRand, seed: i64, log: bool) -> bool { | ||||
|         if log { | ||||
|             debug!("Checking Start positions"); | ||||
|         } | ||||
|         let mut corners = [None; 4]; | ||||
|         for (a, b) in self.corners.iter().zip(corners.iter_mut()) { | ||||
|             *b = a.map(usize::from); | ||||
|         } | ||||
|         let mut cardinals = [None; 4]; | ||||
|         for (a, b) in self.cardinals.iter().zip(cardinals.iter_mut()) { | ||||
|             *b = a.map(usize::from); | ||||
|         } | ||||
|         let rand = get_rand(input_rand, seed, 0); | ||||
|         if !rand.shuffle_check(&mut corners) { | ||||
|             if log { | ||||
|                 error!("Corners are shuffled wrong"); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         if !rand.shuffle_check(&mut cardinals) { | ||||
|             if log { | ||||
|                 error!("Cardinals are shuffled wrong"); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         if rand.int_n(2) == usize::from(self.corner_first) { | ||||
|             if log { | ||||
|                 error!("Corners and cardinals are flipped: {}", self.corner_first); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if log { | ||||
|             debug!("Checking Starting food"); | ||||
|         } | ||||
|         for &(selected, possibilities) in &self.initial_food { | ||||
|             if rand.int_n(usize::from(possibilities)).az::<u8>() != selected { | ||||
|                 if log { | ||||
|                     error!("Initial food spawn is wrong: expected {selected}"); | ||||
|                 } | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         self.food_spawns.iter().enumerate().all(|(turn, spawn)| { | ||||
|             if log { | ||||
|                 debug!("Checking turn {turn}"); | ||||
|             } | ||||
|             let rand = get_rand(input_rand, seed, (turn).az()); | ||||
|  | ||||
|             match spawn { | ||||
|                 spawn @ (FoodSpawn::Forced { | ||||
|                     selected, | ||||
|                     free_spots, | ||||
|                 } | ||||
|                 | FoodSpawn::Random { | ||||
|                     selected, | ||||
|                     free_spots, | ||||
|                 }) => { | ||||
|                     if matches!(spawn, FoodSpawn::Random { .. }) | ||||
|                         && 100 - rand.int_n(100) >= usize::from(self.food_chance) | ||||
|                     { | ||||
|                         if log { | ||||
|                             error!("Food not spawned, when some should have been spawned"); | ||||
|                         } | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     let mut foods = vec![None; usize::from(*free_spots)]; | ||||
|                     foods[usize::from(*selected)] = Some(0); | ||||
|                     let correct = rand.shuffle_check(&mut foods); | ||||
|                     if log && !correct { | ||||
|                         error!("Wrong food selected: {foods:?}"); | ||||
|                     } | ||||
|                     correct | ||||
|                 } | ||||
|                 FoodSpawn::None => { | ||||
|                     let correct = 100 - rand.int_n(100) >= usize::from(self.food_chance); | ||||
|                     if log && !correct { | ||||
|                         error!("Food spawned, when none should have been spawned"); | ||||
|                     } | ||||
|                     correct | ||||
|                 } | ||||
|                 FoodSpawn::Blocked => { | ||||
|                     // We can't get any info if there isn't even a chance for food | ||||
|                     true | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn prepare_food_placement(requests: &[Request], request: &Request) -> (u8, Box<[FoodSpawn]>) { | ||||
|     debug!("Preparing food placement"); | ||||
|     let food_chance = request.game.ruleset.settings.food_spawn_chance; | ||||
|     let food_min = request.game.ruleset.settings.minimum_food; | ||||
| @@ -354,94 +429,116 @@ impl PreparedGame { | ||||
|             } | ||||
|         }) | ||||
|         .collect(); | ||||
|  | ||||
|         Self { | ||||
|             corners, | ||||
|             cardinals, | ||||
|             corner_first, | ||||
|             food_spawns, | ||||
|             food_chance, | ||||
|         } | ||||
|     (food_chance, food_spawns) | ||||
| } | ||||
|  | ||||
|     fn validate(&self, input_rand: &mut SeedRand, seed: i64, log: bool) -> bool { | ||||
|         if log { | ||||
|             debug!("Checking Start positions"); | ||||
|         } | ||||
|         let mut corners = [None; 4]; | ||||
|         for (a, b) in self.corners.iter().zip(corners.iter_mut()) { | ||||
|             *b = a.map(usize::from); | ||||
|         } | ||||
|         let mut cardinals = [None; 4]; | ||||
|         for (a, b) in self.cardinals.iter().zip(cardinals.iter_mut()) { | ||||
|             *b = a.map(usize::from); | ||||
|         } | ||||
|         let rand = get_rand(input_rand, seed, 0); | ||||
|         if !rand.shuffle_check(&mut corners) { | ||||
|             if log { | ||||
|                 error!("Corners are shuffled wrong"); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         if !rand.shuffle_check(&mut cardinals) { | ||||
|             if log { | ||||
|                 error!("Cardinals are shuffled wrong"); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         if rand.int_n(2) == usize::from(self.corner_first) { | ||||
|             if log { | ||||
|                 error!("Corners and cardinals are flipped: {}", self.corner_first); | ||||
|             } | ||||
| fn prepare_initial_food(request: &Request) -> Box<[(u8, u8)]> { | ||||
|     debug!("Preparing initial food spawn"); | ||||
|     let center = Coord { | ||||
|         x: (request.board.width - 1) / 2, | ||||
|         y: (request.board.height) / 2, | ||||
|     }; | ||||
|     let initial_food = request | ||||
|         .board | ||||
|         .snakes | ||||
|         .iter() | ||||
|         .enumerate() | ||||
|         .map(|(i, snake)| { | ||||
|             let head = snake.head; | ||||
|             let possible_food_locations = [ | ||||
|                 Coord { | ||||
|                     x: head.x - 1, | ||||
|                     y: head.y - 1, | ||||
|                 }, | ||||
|                 Coord { | ||||
|                     x: head.x - 1, | ||||
|                     y: head.y + 1, | ||||
|                 }, | ||||
|                 Coord { | ||||
|                     x: head.x + 1, | ||||
|                     y: head.y - 1, | ||||
|                 }, | ||||
|                 Coord { | ||||
|                     x: head.x + 1, | ||||
|                     y: head.y + 1, | ||||
|                 }, | ||||
|             ]; | ||||
|             let available_food_locations: Vec<_> = possible_food_locations | ||||
|                 .into_iter() | ||||
|                 .filter(|food| { | ||||
|                     // Don't place in the center | ||||
|                     if *food == center { | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|         self.food_spawns.iter().enumerate().all(|(turn, spawn)| { | ||||
|             if log { | ||||
|                 debug!("Checking turn {turn}"); | ||||
|             } | ||||
|             let rand = get_rand(input_rand, seed, (turn).az()); | ||||
|  | ||||
|             match spawn { | ||||
|                 spawn @ (FoodSpawn::Forced { | ||||
|                     selected, | ||||
|                     free_spots, | ||||
|                 } | ||||
|                 | FoodSpawn::Random { | ||||
|                     selected, | ||||
|                     free_spots, | ||||
|                 }) => { | ||||
|                     if matches!(spawn, FoodSpawn::Random { .. }) | ||||
|                         && 100 - rand.int_n(100) >= usize::from(self.food_chance) | ||||
|                     // Food must be further than snake from center on at least one axis | ||||
|                     if !((food.x < head.x && head.x < center.x) | ||||
|                         || (food.x > head.x && head.x > center.x) | ||||
|                         || (food.y < head.y && head.y < center.y) | ||||
|                         || (food.y > head.y && head.y > center.y)) | ||||
|                     { | ||||
|                         if log { | ||||
|                             error!("Food not spawned, when some should have been spawned"); | ||||
|                         } | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     let mut foods = vec![None; usize::from(*free_spots)]; | ||||
|                     foods[usize::from(*selected)] = Some(0); | ||||
|                     let correct = rand.shuffle_check(&mut foods); | ||||
|                     if log && !correct { | ||||
|                         error!("Wrong food selected: {foods:?}"); | ||||
|                     // Don't spawn food in corners | ||||
|                     if (food.x == 0 || food.x == (request.board.width - 1)) | ||||
|                         && (food.y == 0 || food.y == (request.board.height - 1)) | ||||
|                     { | ||||
|                         return false; | ||||
|                     } | ||||
|                     correct | ||||
|                 } | ||||
|                 FoodSpawn::None => { | ||||
|                     let correct = 100 - rand.int_n(100) >= usize::from(self.food_chance); | ||||
|                     if log && !correct { | ||||
|                         error!("Food spawned, when none should have been spawned"); | ||||
|                     } | ||||
|                     correct | ||||
|                 } | ||||
|                 FoodSpawn::Blocked => { | ||||
|                     // We can't get any info if there isn't even a chance for food | ||||
|  | ||||
|                     true | ||||
|                 } | ||||
|             } | ||||
|                 }) | ||||
|                 .collect(); | ||||
|  | ||||
|             let possibilities = available_food_locations.len().az(); | ||||
|             let selected = available_food_locations | ||||
|                 .iter() | ||||
|                 .position(|food| *food == request.board.food[i]) | ||||
|                 .unwrap() | ||||
|                 .az(); | ||||
|             (selected, possibilities) | ||||
|         }) | ||||
|         .collect(); | ||||
|     initial_food | ||||
| } | ||||
|  | ||||
| fn prepare_starts(request: &Request) -> (bool, [Option<u8>; 4], [Option<u8>; 4]) { | ||||
|     debug!("Preparing initial board state"); | ||||
|     assert_eq!(request.turn, 0); | ||||
|     let mn = 1; | ||||
|     let md = (request.board.width - 1) / 2; | ||||
|     let mx = request.board.width - 2; | ||||
|     let corners = [ | ||||
|         Coord { x: mn, y: mn }, | ||||
|         Coord { x: mn, y: mx }, | ||||
|         Coord { x: mx, y: mn }, | ||||
|         Coord { x: mx, y: mx }, | ||||
|     ]; | ||||
|     let cardinals = [ | ||||
|         Coord { x: mn, y: md }, | ||||
|         Coord { x: md, y: mn }, | ||||
|         Coord { x: md, y: mx }, | ||||
|         Coord { x: mx, y: md }, | ||||
|     ]; | ||||
|     let corner_first = corners.contains(&request.board.snakes[0].head); | ||||
|     let corners = corners.map(|start| { | ||||
|         request | ||||
|             .board | ||||
|             .snakes | ||||
|             .iter() | ||||
|             .position(|snake| snake.head == start) | ||||
|             .map(|pos| (pos % 4).az()) | ||||
|     }); | ||||
|     let cardinals = cardinals.map(|start| { | ||||
|         request | ||||
|             .board | ||||
|             .snakes | ||||
|             .iter() | ||||
|             .position(|snake| snake.head == start) | ||||
|             .map(|pos| (pos % 4).az()) | ||||
|     }); | ||||
|     (corner_first, corners, cardinals) | ||||
| } | ||||
|  | ||||
| #[must_use] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user