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