From 9c76df1f69a75848ee7efd241bf878ff2985cb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20K=C3=A4nner?= Date: Thu, 16 Jan 2025 21:06:49 +0100 Subject: [PATCH] calculate valid moves --- Cargo.lock | 22 +++++++++++++++ battlesnake/Cargo.toml | 2 ++ battlesnake/src/main.rs | 12 ++++++-- battlesnake/src/types/mod.rs | 15 +++++++++- battlesnake/src/types/simulation.rs | 44 ++++++++++++++++++----------- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9dfcde..e346af3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,8 +173,10 @@ version = "2.0.0" dependencies = [ "axum", "bitvec", + "enum-iterator", "env_logger", "log", + "rand", "serde", "tokio", ] @@ -299,6 +301,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-iterator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_filter" version = "0.1.3" diff --git a/battlesnake/Cargo.toml b/battlesnake/Cargo.toml index f19b503..5a2d705 100644 --- a/battlesnake/Cargo.toml +++ b/battlesnake/Cargo.toml @@ -27,3 +27,5 @@ env_logger = "0.11" # other bitvec = "1.0" +enum-iterator = "2.1" +rand = "0.8" diff --git a/battlesnake/src/main.rs b/battlesnake/src/main.rs index 2433bba..6859f83 100644 --- a/battlesnake/src/main.rs +++ b/battlesnake/src/main.rs @@ -6,7 +6,8 @@ use axum::{ routing::{get, post}, Router, }; -use log::{debug, info}; +use log::{debug, info, warn}; +use rand::prelude::*; use serde::Serialize; use tokio::net::TcpListener; use types::{ @@ -62,8 +63,15 @@ async fn start(request: Json) { async fn get_move(request: Json) -> response::Json { let board = Board::from(&*request); info!("got move request: {board}"); + let actions = board.valid_actions(0).collect::>(); + info!("valid actions: {:?}", actions); + let action = actions.choose(&mut thread_rng()).copied(); + if action.is_none() { + warn!("unable to find a valid action"); + } + info!("chose {action:?}"); response::Json(Response { - direction: Direction::Up, + direction: action.unwrap_or(Direction::Up), shout: None, }) } diff --git a/battlesnake/src/types/mod.rs b/battlesnake/src/types/mod.rs index 74ebf56..2394dfc 100644 --- a/battlesnake/src/types/mod.rs +++ b/battlesnake/src/types/mod.rs @@ -1,3 +1,4 @@ +use enum_iterator::Sequence; use serde::{Deserialize, Serialize}; pub mod simulation; @@ -9,7 +10,19 @@ pub struct Coord { y: u8, } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize)] +impl Coord { + pub fn apply(self, direction: Direction) -> Option { + match direction { + Direction::Up => self.y.checked_add(1).map(|y| Self { y, x: self.x }), + Direction::Down => self.y.checked_sub(1).map(|y| Self { y, x: self.x }), + Direction::Left => self.x.checked_sub(1).map(|x| Self { x, y: self.y }), + Direction::Right => self.x.checked_add(1).map(|x| Self { x, y: self.y }), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Sequence)] +#[serde(rename_all = "lowercase")] pub enum Direction { /// Move in positive y direction Up, diff --git a/battlesnake/src/types/simulation.rs b/battlesnake/src/types/simulation.rs index bff271a..2285ced 100644 --- a/battlesnake/src/types/simulation.rs +++ b/battlesnake/src/types/simulation.rs @@ -1,9 +1,9 @@ use std::{collections::VecDeque, fmt::Display}; use bitvec::prelude::*; -use log::error; +use log::{error, warn}; -use super::{wire::Request, Coord}; +use super::{wire::Request, Coord, Direction}; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Board { @@ -123,9 +123,31 @@ impl Board { self.hazard[index] } - pub fn is_blocked(&self, tile: Coord) -> bool { + pub fn is_free(&self, tile: Coord) -> bool { let index = usize::from(self.coord_to_linear(tile)); - !self.free[index] + self.free[index] + } + + pub fn valid_actions(&self, id: u8) -> impl Iterator + use<'_> { + let head = self + .snakes + .binary_search_by_key(&id, |snake| snake.id) + .ok() + .map(|index| self.snakes[index].head()); + if head.is_none() { + warn!( + "Asked for an action for a snake that doesn't exist: {id} not in {:?}", + self.snakes + ); + } + enum_iterator::all::() + .filter_map(move |direction| { + head.and_then(|head| head.apply(direction)) + .map(|tile| (direction, tile)) + }) + .filter(|(_, tile)| tile.x < self.width && tile.y < self.height) + .filter(|(_, tile)| self.is_free(*tile)) + .map(|(direction, _)| direction) } fn coord_to_linear(&self, coord: Coord) -> u16 { @@ -140,18 +162,6 @@ struct Snake { body: VecDeque, } -impl PartialOrd for Snake { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Snake { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.id.cmp(&other.id) - } -} - impl Snake { pub fn head(&self) -> Coord { self.body.front().copied().unwrap_or_else(|| { @@ -161,7 +171,7 @@ impl Snake { } pub fn tail(&self) -> Coord { - self.body.front().copied().unwrap_or_else(|| { + self.body.back().copied().unwrap_or_else(|| { error!("Snake without a tail: {self:?}"); Coord { x: 0, y: 0 } })