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,107 +259,18 @@ 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);
|
||||
|
||||
debug!("Preparing food placement");
|
||||
let food_chance = request.game.ruleset.settings.food_spawn_chance;
|
||||
let food_min = request.game.ruleset.settings.minimum_food;
|
||||
let initial_food = prepare_initial_food(request);
|
||||
|
||||
let food_spawns = requests
|
||||
.windows(2)
|
||||
.filter_map(|window| {
|
||||
if let [previous, next] = window {
|
||||
if previous.turn + 1 == next.turn {
|
||||
Some((previous, next))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
error!("Windows didn't return 2 requests: {requests:#?}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|(previous, next)| {
|
||||
let mut tmp = next.clone();
|
||||
tmp.board.food.clone_from(&previous.board.food);
|
||||
let heads: Vec<_> = tmp.board.snakes.iter().map(|snake| snake.head).collect();
|
||||
tmp.board.food.retain(|coord| !heads.contains(coord));
|
||||
let free_spots = get_unoccupied_points(&tmp);
|
||||
|
||||
if free_spots.is_empty() {
|
||||
return FoodSpawn::Blocked;
|
||||
}
|
||||
|
||||
let diff: Vec<_> = next
|
||||
.board
|
||||
.food
|
||||
.iter()
|
||||
.filter(|next| !previous.board.food.contains(next))
|
||||
.collect();
|
||||
if diff.is_empty() {
|
||||
// We didn't spawn any food. It was only eaten
|
||||
return FoodSpawn::None;
|
||||
}
|
||||
|
||||
let selected = free_spots
|
||||
.iter()
|
||||
.position(|spot| spot == diff[0])
|
||||
.unwrap()
|
||||
.az();
|
||||
let free_spots = free_spots.len().az();
|
||||
|
||||
if next.board.food.len() == usize::from(food_min) {
|
||||
FoodSpawn::Forced {
|
||||
selected,
|
||||
free_spots,
|
||||
}
|
||||
} else {
|
||||
FoodSpawn::Random {
|
||||
selected,
|
||||
free_spots,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let (food_chance, food_spawns) = prepare_food_placement(requests, request);
|
||||
|
||||
Self {
|
||||
corners,
|
||||
cardinals,
|
||||
corner_first,
|
||||
initial_food,
|
||||
food_spawns,
|
||||
food_chance,
|
||||
}
|
||||
@ -396,6 +308,18 @@ impl PreparedGame {
|
||||
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}");
|
||||
@ -444,6 +368,179 @@ impl PreparedGame {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
let food_spawns = requests
|
||||
.windows(2)
|
||||
.filter_map(|window| {
|
||||
if let [previous, next] = window {
|
||||
if previous.turn + 1 == next.turn {
|
||||
Some((previous, next))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
error!("Windows didn't return 2 requests: {requests:#?}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|(previous, next)| {
|
||||
let mut tmp = next.clone();
|
||||
tmp.board.food.clone_from(&previous.board.food);
|
||||
let heads: Vec<_> = tmp.board.snakes.iter().map(|snake| snake.head).collect();
|
||||
tmp.board.food.retain(|coord| !heads.contains(coord));
|
||||
let free_spots = get_unoccupied_points(&tmp);
|
||||
|
||||
if free_spots.is_empty() {
|
||||
return FoodSpawn::Blocked;
|
||||
}
|
||||
|
||||
let diff: Vec<_> = next
|
||||
.board
|
||||
.food
|
||||
.iter()
|
||||
.filter(|next| !previous.board.food.contains(next))
|
||||
.collect();
|
||||
if diff.is_empty() {
|
||||
// We didn't spawn any food. It was only eaten
|
||||
return FoodSpawn::None;
|
||||
}
|
||||
|
||||
let selected = free_spots
|
||||
.iter()
|
||||
.position(|spot| spot == diff[0])
|
||||
.unwrap()
|
||||
.az();
|
||||
let free_spots = free_spots.len().az();
|
||||
|
||||
if next.board.food.len() == usize::from(food_min) {
|
||||
FoodSpawn::Forced {
|
||||
selected,
|
||||
free_spots,
|
||||
}
|
||||
} else {
|
||||
FoodSpawn::Random {
|
||||
selected,
|
||||
free_spots,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(food_chance, food_spawns)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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]
|
||||
//#[flame]
|
||||
fn get_rand(rand: &mut SeedRand, seed: i64, turn: i64) -> &mut SeedRand {
|
||||
|
Reference in New Issue
Block a user