implement some of the main game logic

This commit is contained in:
2026-03-09 18:05:08 -05:00
parent 6cbf4c9328
commit 085c3ab62f
6 changed files with 518 additions and 15 deletions

404
Cargo.lock generated
View File

@@ -2,6 +2,56 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures",
"rand_core",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "game_cli_frontend"
version = "0.1.0"
@@ -12,3 +62,357 @@ dependencies = [
[[package]]
name = "game_core"
version = "0.1.0"
dependencies = [
"rand",
]
[[package]]
name = "getrandom"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"rand_core",
"wasip2",
"wasip3",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "indexmap"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
[[package]]
name = "itoa"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.183"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
dependencies = [
"chacha20",
"getrandom",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

View File

@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
rand = "0.10.0"

View File

@@ -1,8 +1,10 @@
use crate::{field::field::Field, snake::snake::SnakeHandler};
use crate::{field::field::{Field, Tile}, game_handler::states::GameManagerState, snake::snake::SnakeHandler};
pub struct GameData {
field: Field,
snakes: Vec<SnakeHandler>,
pub field: Field,
pub snakes: Vec<SnakeHandler>,
}
impl GameData {
@@ -12,4 +14,14 @@ impl GameData {
snakes,
}
}
pub fn get_food_count(&self) -> usize {
self.field.tiles.iter().filter(|tile| **tile == Tile::Food).count()
}
}
pub struct GameManager {
game_data: GameData,
game_manager_state: Box<dyn GameManagerState>,
}

View File

@@ -1,6 +1,8 @@
use std::time::Duration;
use crate::{game_handler::game_handler::GameData, inputs::Inputs};
use rand::prelude::*;
use crate::{field::field::Tile, game_handler::game_handler::GameData, inputs::Inputs, snake::snake::Snake};
pub trait GameManagerState {
@@ -16,10 +18,59 @@ impl PlayingState {
pub fn new() -> Self {
Self { dt_accumulator: Duration::ZERO }
}
fn spawn_food(&mut self, game_data: &mut GameData) {
let mut empty_positions = Vec::new();
// Get all empty tiles from the field
for y in 0..game_data.field.height {
for x in 0..game_data.field.width {
if game_data.field.get_tile(x, y) == Tile::Empty {
empty_positions.push((x, y));
}
}
}
// Filter out positions occupied by snakes
for snake_handler in &game_data.snakes {
let mut current = Some(&snake_handler.snake.body);
while let Some(node) = current {
empty_positions.retain(|&(x, y)| (x as isize, y as isize) != (node.position.x, node.position.y));
current = node.next.as_deref();
}
}
// Spawn food
if let Some(&(x, y)) = empty_positions.choose(&mut rand::rng()) {
game_data.field.set_tile(x, y, Tile::Food);
}
}
}
impl GameManagerState for PlayingState {
fn update(&mut self, delta: Duration, inputs: &Inputs, game_data: &mut GameData) -> Option<Box<dyn GameManagerState>> {
let mut should_spawn_food = false;
let all_snakes: Vec<Snake> = game_data.snakes.iter().map(|sh| sh.snake.clone()).collect();
for (current_snake_index, snake_handler) in game_data.snakes.iter_mut().enumerate() {
if snake_handler.snake.is_eating(&game_data.field) {
should_spawn_food = true;
}
let other_snakes = all_snakes
.iter()
.enumerate()
.filter_map(|(i, s)| {
if i != current_snake_index {
return Some(s.clone())
}
None
})
.collect();
snake_handler.update(delta, inputs, &other_snakes, &game_data.field);
}
if should_spawn_food {
self.spawn_food(game_data);
}
None
}
}
@@ -30,4 +81,12 @@ impl GameManagerState for PausedState {
fn update(&mut self, _delta: Duration, _inputs: &Inputs, _game_data: &mut GameData) -> Option<Box<dyn GameManagerState>> {
None
}
}
}
pub struct FinishedState;
impl GameManagerState for FinishedState {
fn update(&mut self, _delta: Duration, _inputs: &Inputs, _game_data: &mut GameData) -> Option<Box<dyn GameManagerState>> {
None
}
}

View File

@@ -4,11 +4,13 @@ use crate::inputs::Inputs;
use crate::field::field::{Field, Tile};
use crate::snake::states::SnakeState;
#[derive(Clone)]
pub struct Node {
pub position: Position<isize>,
next: Option<Box<Node>>,
pub next: Option<Box<Node>>,
}
#[derive(Clone)]
pub struct Snake {
pub body: Node,
pub speed: f32,
@@ -44,6 +46,15 @@ impl Snake {
false
}
pub fn is_colliding_against_snakes(&self, other_snakes: &Vec<Snake>) -> bool {
for other_snake in other_snakes {
if self.is_colliding_against_snake(other_snake) {
return true;
}
}
false
}
pub fn is_colliding_against_itself(&self) -> bool {
let head_pos = self.body.position;
let mut current_node = self.body.next.as_deref();
@@ -115,14 +126,14 @@ impl Snake {
}
pub struct SnakeHandler {
snake: Snake,
pub snake: Snake,
state: Box<dyn SnakeState>,
}
impl SnakeHandler {
pub fn update(&mut self, delta: Duration, inputs: &Inputs) {
let new_state = self.state.update(delta, inputs, &mut self.snake);
pub fn update(&mut self, delta: Duration, inputs: &Inputs, other_snakes: &Vec<Snake>, field: &Field) {
let new_state = self.state.update(delta, inputs, &mut self.snake, other_snakes, field);
if let Some(new_state) = new_state {
self.state = new_state;
}

View File

@@ -1,15 +1,17 @@
use std::time::Duration;
use crate::common::position::Position;
use crate::field::field::Field;
use crate::{inputs::{InputKey, Inputs}, snake::snake::Snake};
pub trait SnakeState {
fn update(&mut self, delta: Duration, inputs: &Inputs, snake: &mut Snake) -> Option<Box<dyn SnakeState>>;
fn update(&mut self, delta: Duration, inputs: &Inputs, snake: &mut Snake, other_snakes: &Vec<Snake>, field: &Field) -> Option<Box<dyn SnakeState>>;
}
pub struct MovingState {
dt_accumulator: Duration,
}
pub struct StoppedState;
pub struct DeadState;
impl MovingState {
@@ -37,7 +39,14 @@ impl MovingState {
}
impl SnakeState for MovingState {
fn update(&mut self, delta: Duration, inputs: &Inputs, snake: &mut Snake) -> Option<Box<dyn SnakeState>> {
fn update(&mut self, delta: Duration, inputs: &Inputs, snake: &mut Snake, other_snakes: &Vec<Snake>, field: &Field) -> Option<Box<dyn SnakeState>> {
if
snake.is_colliding_against_itself()
|| snake.is_colliding_against_snakes(other_snakes)
|| snake.is_colliding_against_wall(field)
{
return Some(Box::new(DeadState{}));
}
self.handle_direction_change(inputs, snake);
let interval = Duration::from_secs_f32(1.0 / snake.speed);
self.dt_accumulator += delta;
@@ -51,8 +60,14 @@ impl SnakeState for MovingState {
}
impl SnakeState for StoppedState {
fn update(&mut self, _delta: Duration, _inputs: &Inputs, _snake: &mut Snake) -> Option<Box<dyn SnakeState>> {
Some(Box::new(Self))
fn update(&mut self, _delta: Duration, _inputs: &Inputs, _snake: &mut Snake, _other_snakes: &Vec<Snake>, _field: &Field) -> Option<Box<dyn SnakeState>> {
None
}
}
impl SnakeState for DeadState {
fn update(&mut self, _delta: Duration, _inputs: &Inputs, _snake: &mut Snake, _other_snakes: &Vec<Snake>, _field: &Field) -> Option<Box<dyn SnakeState>> {
None
}
}
@@ -66,13 +81,14 @@ mod tests {
let mut snake = Snake::new(Position { x: 0, y: 0 }, Position { x: 1, y: 0 }, 1.0);
let mut state = MovingState::new();
let inputs = Inputs::new();
let field = Field::new(40, 40);
// Should not move
state.update(Duration::from_millis(500), &inputs, &mut snake);
state.update(Duration::from_millis(500), &inputs, &mut snake, &vec![], &field);
assert_eq!(snake.body.position, Position { x: 0, y: 0 });
// Should move
state.update(Duration::from_millis(500), &inputs, &mut snake);
state.update(Duration::from_millis(500), &inputs, &mut snake, &vec![], &field);
assert_eq!(snake.body.position, Position { x: 1, y: 0 });
}