1
0

feat: implement core of the game

This commit is contained in:
Rokas Puzonas 2022-06-02 23:50:44 +03:00
parent 54c41c3680
commit 5b66ea248e
2 changed files with 124 additions and 7 deletions

View File

@ -3,6 +3,5 @@ name = "rust-wasm-snake"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rand = "0.8.5"

View File

@ -1,8 +1,126 @@
use std::collections::VecDeque;
use rand::prelude::*;
pub type Position = (usize, usize);
#[derive(Debug, PartialEq)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
#[derive(Debug)]
pub struct SnakeGame {
width: usize,
height: usize,
snake: VecDeque<Position>,
snake_direction: Direction,
food: Position,
game_over: bool
}
fn rand_position(width: usize, height: usize) -> Position {
let mut rng = thread_rng();
(rng.gen_range(0..width), rng.gen_range(0..height))
}
fn get_opposite_direction(direction: &Direction) -> Direction {
match direction {
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
Direction::Right => Direction::Left,
Direction::Left => Direction::Right,
}
}
impl SnakeGame {
pub fn new(width: usize, height: usize) -> SnakeGame {
Self {
width,
height,
snake: VecDeque::from([(width / 2, height / 2)]),
snake_direction: Direction::Left,
food: rand_position(width, height),
game_over: false
}
}
pub fn change_direction(&mut self, direction: Direction) {
// Because you can't start going in the opposite direction you are
// currently going
if get_opposite_direction(&self.snake_direction) == direction {
return;
}
self.snake_direction = direction
}
fn is_valid_move(&self, (x, y): Position, dx: i32, dy: i32) -> bool {
let new_x = x as i32 + dx;
let new_y = y as i32 + dy;
0 <= new_x && new_x < self.width as i32 && 0 <= new_y && new_y < self.height as i32
}
fn get_unoccupied_position(&self) -> Option<Position> {
let mut rng = thread_rng();
(0..self.height)
.flat_map(|y| (0..self.width).map(move |x| (x, y)))
.filter(|pos| !self.snake.contains(pos))
.choose(&mut rng)
}
pub fn tick(&mut self) {
if self.game_over {
return;
}
let head = self.snake.front();
if head.is_none() {
return;
}
let head = *head.unwrap();
let (dx, dy): (i32, i32) = match &self.snake_direction {
Direction::Up => (-1, 0),
Direction::Down => (1, 0),
Direction::Left => (-1, 0),
Direction::Right => (1, 0),
};
if !self.is_valid_move(head, dx, dy) {
self.game_over = true;
return;
}
let new_head = (
(head.0 as i32 + dx) as usize,
(head.1 as i32 + dy) as usize
);
self.snake.push_front(new_head);
if new_head == self.food {
let new_food = self.get_unoccupied_position();
if let Some(new_food) = new_food {
self.food = new_food;
} else {
self.game_over = true;
}
} else {
self.snake.pop_back();
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] use super::*;
fn it_works() { #[test]
let result = 2 + 2; fn it_works() {
assert_eq!(result, 4); let game = SnakeGame::new(10, 10);
}
println!("{:?}", game);
}
} }