a bit more collision detection logic
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod field;
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod game_handler;
|
||||||
|
pub mod states;
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ pub mod inputs;
|
|||||||
pub mod snake;
|
pub mod snake;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
|
pub mod field;
|
||||||
|
pub mod game_handler;
|
||||||
|
|
||||||
pub fn add(left: u64, right: u64) -> u64 {
|
pub fn add(left: u64, right: u64) -> u64 {
|
||||||
left + right
|
left + right
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use crate::common::position::Position;
|
use crate::common::position::Position;
|
||||||
use crate::inputs::Inputs;
|
use crate::inputs::Inputs;
|
||||||
|
use crate::field::field::{Field, Tile};
|
||||||
use crate::snake::states::SnakeState;
|
use crate::snake::states::SnakeState;
|
||||||
|
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
@@ -43,10 +44,46 @@ impl Snake {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement this once we have wall data
|
pub fn is_colliding_against_itself(&self) -> bool {
|
||||||
// pub fn is_colliding_against_wall(other_snake: &Snake) {
|
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) {
|
pub fn do_move(&mut self) {
|
||||||
// probably a more optimal way of approaching this is just
|
// 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 {
|
pub struct SnakeHandler {
|
||||||
@@ -153,4 +200,49 @@ mod tests {
|
|||||||
let first_body = snake.body.next.as_ref().unwrap();
|
let first_body = snake.body.next.as_ref().unwrap();
|
||||||
assert_eq!(first_body.position, Position{x: 10, y: 10});
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user