calculate valid moves
Some checks failed
Build / build (push) Failing after 1m52s

This commit is contained in:
Max Känner 2025-01-16 21:06:49 +01:00
parent d91d0e0a82
commit 9c76df1f69
5 changed files with 75 additions and 20 deletions

22
Cargo.lock generated
View File

@ -173,8 +173,10 @@ version = "2.0.0"
dependencies = [ dependencies = [
"axum", "axum",
"bitvec", "bitvec",
"enum-iterator",
"env_logger", "env_logger",
"log", "log",
"rand",
"serde", "serde",
"tokio", "tokio",
] ]
@ -299,6 +301,26 @@ dependencies = [
"cfg-if", "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]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.3" version = "0.1.3"

View File

@ -27,3 +27,5 @@ env_logger = "0.11"
# other # other
bitvec = "1.0" bitvec = "1.0"
enum-iterator = "2.1"
rand = "0.8"

View File

@ -6,7 +6,8 @@ use axum::{
routing::{get, post}, routing::{get, post},
Router, Router,
}; };
use log::{debug, info}; use log::{debug, info, warn};
use rand::prelude::*;
use serde::Serialize; use serde::Serialize;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use types::{ use types::{
@ -62,8 +63,15 @@ async fn start(request: Json<Request>) {
async fn get_move(request: Json<Request>) -> response::Json<Response> { async fn get_move(request: Json<Request>) -> response::Json<Response> {
let board = Board::from(&*request); let board = Board::from(&*request);
info!("got move request: {board}"); info!("got move request: {board}");
let actions = board.valid_actions(0).collect::<Vec<_>>();
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 { response::Json(Response {
direction: Direction::Up, direction: action.unwrap_or(Direction::Up),
shout: None, shout: None,
}) })
} }

View File

@ -1,3 +1,4 @@
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod simulation; pub mod simulation;
@ -9,7 +10,19 @@ pub struct Coord {
y: u8, y: u8,
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize)] impl Coord {
pub fn apply(self, direction: Direction) -> Option<Self> {
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 { pub enum Direction {
/// Move in positive y direction /// Move in positive y direction
Up, Up,

View File

@ -1,9 +1,9 @@
use std::{collections::VecDeque, fmt::Display}; use std::{collections::VecDeque, fmt::Display};
use bitvec::prelude::*; 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)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Board { pub struct Board {
@ -123,9 +123,31 @@ impl Board {
self.hazard[index] 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)); let index = usize::from(self.coord_to_linear(tile));
!self.free[index] self.free[index]
}
pub fn valid_actions(&self, id: u8) -> impl Iterator<Item = Direction> + 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::<Direction>()
.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 { fn coord_to_linear(&self, coord: Coord) -> u16 {
@ -140,18 +162,6 @@ struct Snake {
body: VecDeque<Coord>, body: VecDeque<Coord>,
} }
impl PartialOrd for Snake {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Snake {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl Snake { impl Snake {
pub fn head(&self) -> Coord { pub fn head(&self) -> Coord {
self.body.front().copied().unwrap_or_else(|| { self.body.front().copied().unwrap_or_else(|| {
@ -161,7 +171,7 @@ impl Snake {
} }
pub fn tail(&self) -> Coord { 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:?}"); error!("Snake without a tail: {self:?}");
Coord { x: 0, y: 0 } Coord { x: 0, y: 0 }
}) })