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
+1
View File
@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
rand = "0.10.0"
+15 -3
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>,
}
+61 -2
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
}
}
+15 -4
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;
}
+22 -6
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 });
}