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