Compare commits
2 Commits
f52b9e531e
...
701cb1f5a4
Author | SHA1 | Date | |
---|---|---|---|
701cb1f5a4 | |||
a198352aa9 |
@@ -1,4 +1,4 @@
|
||||
FROM rust:1.86-bullseye AS build
|
||||
FROM rust:1.88-bullseye AS build
|
||||
|
||||
COPY battlesnake/ /usr/app
|
||||
WORKDIR /usr/app
|
||||
|
@@ -3,7 +3,7 @@ authors = ["Max Känner"]
|
||||
name = "battlesnake"
|
||||
version = "2.0.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.86"
|
||||
rust-version = "1.88"
|
||||
|
||||
readme = "README.md"
|
||||
repository = "https://git.mkaenner.de/max/battlesnake"
|
||||
|
@@ -179,7 +179,28 @@ async fn get_move(
|
||||
{
|
||||
warn!("Unable to observe request: {e}");
|
||||
}
|
||||
let board = Game::from(&request);
|
||||
let mut board = Game::from(&request);
|
||||
board.agent = |snake, direction, board| {
|
||||
let food = board.food().collect::<Vec<_>>();
|
||||
if food.is_empty() {
|
||||
return 1.0;
|
||||
}
|
||||
let head = board.head(snake);
|
||||
let next_head = head.wrapping_apply(direction);
|
||||
let dist = food
|
||||
.iter()
|
||||
.map(|food| u16::from(food.x.abs_diff(head.x)) + u16::from(food.y.abs_diff(head.y)))
|
||||
.min()
|
||||
.unwrap_or(1);
|
||||
let next_dist = food
|
||||
.iter()
|
||||
.map(|food| {
|
||||
u16::from(food.x.abs_diff(next_head.x)) + u16::from(food.y.abs_diff(next_head.y))
|
||||
})
|
||||
.min()
|
||||
.unwrap_or(100);
|
||||
f32::from(next_dist) / f32::from(dist)
|
||||
};
|
||||
let timeout = Duration::from_millis(u64::from(request.game.timeout));
|
||||
let id = board.board.get_id(&request.you.id).unwrap_or_else(|| {
|
||||
error!("My id is not in the simulation board");
|
||||
|
@@ -69,6 +69,7 @@ pub struct Game {
|
||||
pub board: Board,
|
||||
map: Maps,
|
||||
ruleset: Rulesets,
|
||||
pub agent: fn(snake: u8, direction: Direction, board: &Board) -> f32,
|
||||
}
|
||||
|
||||
impl From<&Request> for Game {
|
||||
@@ -80,6 +81,7 @@ impl From<&Request> for Game {
|
||||
board,
|
||||
map,
|
||||
ruleset,
|
||||
agent: |_, _, _| 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,8 +93,12 @@ impl Game {
|
||||
.filter_map(|i| {
|
||||
self.board
|
||||
.valid_actions_index(i)
|
||||
.choose(rng)
|
||||
.map(|direction| (i.saturating_as(), direction))
|
||||
.collect::<Vec<_>>()
|
||||
.choose_weighted(rng, |&direction| {
|
||||
(self.agent)(i.saturating_as(), direction, &self.board)
|
||||
})
|
||||
.ok()
|
||||
.map(|&direction| (i.saturating_as(), direction))
|
||||
})
|
||||
.collect();
|
||||
if self.next_turn(&random_actions, rng) {
|
||||
@@ -111,8 +117,12 @@ impl Game {
|
||||
.or_else(|| {
|
||||
self.board
|
||||
.valid_actions_index(i)
|
||||
.choose(rng)
|
||||
.map(|direction| (i.saturating_as(), direction))
|
||||
.collect::<Vec<_>>()
|
||||
.choose_weighted(rng, |&direction| {
|
||||
(self.agent)(i.saturating_as(), direction, &self.board)
|
||||
})
|
||||
.ok()
|
||||
.map(|&direction| (i.saturating_as(), direction))
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
@@ -254,10 +264,15 @@ impl Board {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn num_snakes(&self) -> usize {
|
||||
pub const fn num_snakes(&self) -> usize {
|
||||
self.snakes.len()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn head(&self, id: u8) -> Coord {
|
||||
self.snakes[usize::from(id)].head()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn alive(&self, id: u8) -> bool {
|
||||
self.id_to_index(id).is_some()
|
||||
@@ -286,6 +301,10 @@ impl Board {
|
||||
self.food[index]
|
||||
}
|
||||
|
||||
pub fn food(&self) -> impl Iterator<Item = Coord> {
|
||||
self.food.iter_ones().map(|i| self.linear_to_coord(i))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_hazard(&self, tile: Coord) -> bool {
|
||||
let index = self.coord_to_linear(tile);
|
||||
@@ -308,30 +327,6 @@ impl Board {
|
||||
.flat_map(|index| self.valid_actions_index(index))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
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(rng)
|
||||
.unwrap_or(Direction::Up)
|
||||
}
|
||||
|
||||
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(rng)
|
||||
.unwrap_or(Direction::Up),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn id_to_index(&self, id: u8) -> Option<usize> {
|
||||
self.snakes.binary_search_by_key(&id, |snake| snake.id).ok()
|
||||
}
|
||||
|
@@ -31,6 +31,6 @@ impl Ruleset for Solo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn game_over(board: &Board) -> bool {
|
||||
pub const fn game_over(board: &Board) -> bool {
|
||||
board.num_snakes() == 0
|
||||
}
|
||||
|
@@ -175,6 +175,6 @@ pub fn feed_snakes(board: &mut Board) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn game_over(board: &Board) -> bool {
|
||||
pub const fn game_over(board: &Board) -> bool {
|
||||
board.num_snakes() <= 1
|
||||
}
|
||||
|
Reference in New Issue
Block a user