create a snake game in rust

To create a snake game in Rust, we will use the Piston game engine which provides a 2D graphics library and game development framework. First, we need to set up our project by adding the required dependencies to our Cargo.toml file:

main.rs
[dependencies]
piston_window = "0.110.0"
rand = "0.8.4"
56 chars
4 lines

Next, we will create a main.rs file and initialize our game window using the Piston library as follows:

main.rs
extern crate piston_window;
extern crate rand;

use piston_window::*;

fn main() {
    let (width, height) = (640, 480);
    let mut window: PistonWindow = WindowSettings::new("Snake", [width, height])
        .exit_on_esc(true)
        .build()
        .unwrap();

    while let Some(event) = window.next() {
        // Game logic goes here
        // Handle events here
        window.draw_2d(&event, |c, g, _| {
            // Draw game objects here
        });
    }
}
473 chars
21 lines

Now, we can implement the game logic for our snake game. We will define the Snake struct which will contain the body of the snake as a vector of Rectangle objects. We will also define an apple object as a Rectangle which will represent the food for the snake.

main.rs
use rand::Rng;

struct Snake {
    body: Vec<Rectangle>,
    direction: Direction,
    last_tail: Option<Rectangle>,
}

struct Apple {
    x: f64,
    y: f64,
    width: f64,
    height: f64,
}

enum Direction {
    Up,
    Down,
    Left,
    Right,
}

impl Snake {
    fn update(&mut self) {
        let mut head = self.body[0];
        self.last_tail = self.body.pop();

        match self.direction {
            Direction::Up => head.y -= head.height,
            Direction::Down => head.y += head.height,
            Direction::Left => head.x -= head.width,
            Direction::Right => head.x += head.width,
        }
        self.body.insert(0, head);

        if self.check_collision() {
            self.body = vec![Rectangle::new([0., 1., 0., 1.], head)];
        }

        match self.last_tail {
            Some(rect) => self.body.push(rect),
            None => { /* do nothing */ }
        }
    }

    fn check_collision(&self) -> bool {
        let head = self.body[0];
        if head.x < 0. || head.x > 640. || head.y < 0. || head.y > 480. {
            return true;
        }
        for i in 1..self.body.len() {
            if head.overlaps(&self.body[i]) {
                return true;
            }
        }
        false
    }
}

impl Apple {
    fn new() -> Apple {
        let mut rng = rand::thread_rng();
        Apple {
            x: rng.gen_range(0.0, 640.0),
            y: rng.gen_range(0.0, 480.0),
            width: 10.0,
            height: 10.0,
        }
    }

    fn check_collision(&self, other: &Rectangle) -> bool {
        self.x == other.x && self.y == other.y
    }
}
1621 chars
75 lines

We can now add the game objects to the window by implementing the draw_2d closure inside the while loop:

main.rs
let mut snake = Snake {
    body: vec![Rectangle::new([0., 1., 0., 1.], [100., 100., 10., 10.]),
               Rectangle::new([0., 1., 0., 1.], [110., 100., 10., 10.])],
    direction: Direction::Right,
    last_tail: None,
};

let mut apple = Apple::new();

while let Some(event) = window.next() {
    if let Some(Button::Keyboard(key)) = event.press_args() {
        match key {
            Key::Up => snake.direction = Direction::Up,
            Key::Down => snake.direction = Direction::Down,
            Key::Left => snake.direction = Direction::Left,
            Key::Right => snake.direction = Direction::Right,
            _ => {}
        }
    }

    snake.update();
    if apple.check_collision(&snake.body[0]) {
        snake.body.push(apple_rect);
        apple = Apple::new();
    }
    window.draw_2d(&event, |c, g, _| {
        clear([1.; 4], g);
        for rect in &snake.body {
            rectangle(rect.color, rect.clone(), c.transform, g);
        }
        rectangle([1., 0., 0., 1.], apple_rect.clone(), c.transform, g);
    });
}
1055 chars
34 lines

In the while loop, we listen for keyboard events to move the snake in the desired direction. We then update the snake's position and check if it collides with the apple object. If it does, we add a new Rectangle object to the snake's body and generate a new apple object. Finally, we draw the game objects on the window using the rectangle function provided by the Piston library.

This should be enough to create a simple snake game in Rust using the Piston game engine. Of course, you can add more features and improve the game as you see fit.

gistlibby LogSnag