a bit more collision detection logic

This commit is contained in:
2026-03-08 19:06:34 -05:00
parent 23b7e1ad23
commit 6cbf4c9328
7 changed files with 178 additions and 3 deletions

View File

@@ -0,0 +1,30 @@
#[derive(Clone, Copy, PartialEq)]
pub enum Tile {
Empty,
Wall,
Food,
}
pub struct Field {
pub width: usize,
pub height: usize,
pub tiles: Vec<Tile>,
}
impl Field {
pub fn new(width: usize, height: usize) -> Self {
Self {
width,
height,
tiles: vec![Tile::Empty; width * height],
}
}
pub fn get_tile(&self, x: usize, y: usize) -> Tile {
self.tiles[y * self.width + x]
}
pub fn set_tile(&mut self, x: usize, y: usize, tile: Tile) {
self.tiles[y * self.width + x] = tile;
}
}

View File

@@ -0,0 +1 @@
pub mod field;

View File

@@ -0,0 +1,15 @@
use crate::{field::field::Field, snake::snake::SnakeHandler};
pub struct GameData {
field: Field,
snakes: Vec<SnakeHandler>,
}
impl GameData {
pub fn new(field: Field, snakes: Vec<SnakeHandler>) -> Self {
Self {
field,
snakes,
}
}
}

View File

@@ -0,0 +1,2 @@
pub mod game_handler;
pub mod states;

View File

@@ -0,0 +1,33 @@
use std::time::Duration;
use crate::{game_handler::game_handler::GameData, inputs::Inputs};
pub trait GameManagerState {
fn update(&mut self, delta: Duration, inputs: &Inputs, game_data: &mut GameData) -> Option<Box<dyn GameManagerState>>;
}
pub struct PlayingState {
dt_accumulator: Duration,
}
impl PlayingState {
pub fn new() -> Self {
Self { dt_accumulator: Duration::ZERO }
}
}
impl GameManagerState for PlayingState {
fn update(&mut self, delta: Duration, inputs: &Inputs, game_data: &mut GameData) -> Option<Box<dyn GameManagerState>> {
None
}
}
pub struct PausedState;
impl GameManagerState for PausedState {
fn update(&mut self, _delta: Duration, _inputs: &Inputs, _game_data: &mut GameData) -> Option<Box<dyn GameManagerState>> {
None
}
}

View File

@@ -3,6 +3,8 @@ pub mod inputs;
pub mod snake;
pub mod common;
pub mod traits;
pub mod field;
pub mod game_handler;
pub fn add(left: u64, right: u64) -> u64 {
left + right

View File

@@ -1,6 +1,7 @@
use std::time::Duration;
use crate::common::position::Position;
use crate::inputs::Inputs;
use crate::field::field::{Field, Tile};
use crate::snake::states::SnakeState;
pub struct Node {
@@ -43,10 +44,46 @@ impl Snake {
false
}
// TODO: implement this once we have wall data
// pub fn is_colliding_against_wall(other_snake: &Snake) {
pub fn is_colliding_against_itself(&self) -> bool {
let head_pos = self.body.position;
let mut current_node = self.body.next.as_deref();
// }
while let Some(node) = current_node {
if node.position == head_pos {
return true;
}
current_node = node.next.as_deref();
}
false
}
pub fn is_reaching_field_boundaries(&self, field: &Field) -> bool {
// TODO: implement logic to teleport snake's head to the other side
let pos = self.body.position;
if pos.x < 0 || pos.x >= field.width as isize || pos.y < 0 || pos.y >= field.height as isize {
return true;
}
false
}
pub fn is_colliding_against_wall(&self, field: &Field) -> bool {
let pos = self.body.position;
if pos.x < 0 || pos.x >= field.width as isize || pos.y < 0 || pos.y >= field.height as isize {
return false;
}
field.get_tile(pos.x as usize, pos.y as usize) == Tile::Wall
}
pub fn is_eating(&self, field: &Field) -> bool {
let pos = self.body.position;
if pos.x < 0 || pos.x >= field.width as isize || pos.y < 0 || pos.y >= field.height as isize {
return false;
}
field.get_tile(pos.x as usize, pos.y as usize) == Tile::Food
}
pub fn do_move(&mut self) {
// probably a more optimal way of approaching this is just
@@ -65,6 +102,16 @@ impl Snake {
}
}
pub fn grow(&mut self) {
let mut tail = &mut self.body;
while let Some(ref mut next_node) = tail.next {
tail = next_node;
}
tail.next = Some(Box::new(Node {
position: tail.position,
next: None,
}));
}
}
pub struct SnakeHandler {
@@ -153,4 +200,49 @@ mod tests {
let first_body = snake.body.next.as_ref().unwrap();
assert_eq!(first_body.position, Position{x: 10, y: 10});
}
#[test]
fn test_grow() {
// Setup: Snake with just a head at (10, 10)
let mut snake = Snake::new(Position { x: 10, y: 10 }, Position { x: 1, y: 0 }, 1.0);
assert!(snake.body.next.is_none());
snake.grow();
let first_body = snake.body.next.as_ref().unwrap();
assert!(first_body.next.is_none());
assert_eq!(first_body.position, Position { x: 10, y: 10 });
snake.grow();
let first_body = snake.body.next.as_ref().unwrap();
let second_body = first_body.next.as_ref().unwrap();
assert_eq!(second_body.position, Position { x: 10, y: 10 });
}
#[test]
fn test_is_colliding_against_itself() {
// Snake head at (5, 5)
let mut snake = Snake::new(Position { x: 5, y: 5 }, Position { x: 1, y: 0 }, 1.0);
// Body at (5, 5) -> (5, 6) -> (5, 5)
snake.body.next = Some(Box::new(Node {
position: Position { x: 5, y: 6 },
next: Some(Box::new(Node {
position: Position { x: 5, y: 5 },
next: None,
})),
}));
assert!(snake.is_colliding_against_itself());
// Snake with no self-collision
let mut safe_snake = Snake::new(Position { x: 5, y: 5 }, Position { x: 1, y: 0 }, 1.0);
safe_snake.body.next = Some(Box::new(Node {
position: Position { x: 6, y: 5 },
next: None,
}));
assert!(!safe_snake.is_colliding_against_itself());
}
}