implement some of the main game logic
This commit is contained in:
@@ -4,3 +4,4 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.10.0"
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user