Skip to content

Instantly share code, notes, and snippets.

@AndrewJakubowicz
Last active June 7, 2023 13:25
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save AndrewJakubowicz/9972b5d46be474c186a2dc3a71326de4 to your computer and use it in GitHub Desktop.
Save AndrewJakubowicz/9972b5d46be474c186a2dc3a71326de4 to your computer and use it in GitHub Desktop.
[package]
name = "snake_game"
version = "0.1.0"
authors = ["youcodethings <spyr1014@gmail.com>"]
[dependencies]
piston = "0.36.0"
piston2d-graphics = "0.26.0"
pistoncore-glutin_window = "0.45.0"
piston2d-opengl_graphics = "0.52.0"
rand = "0.4"
//! Snake game
//!
//! Example of 2d graphics in Rust.
//!
//! Completed code for https://youtu.be/HCwMb0KslX8
//! Author: @youcodethings
//!
//! All additional changes are in logic only.
//! No new rendering or library stuff.
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
extern crate rand;
use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow;
use opengl_graphics::{GlGraphics, OpenGL};
use std::collections::LinkedList;
use std::iter::FromIterator;
pub struct Game {
gl: GlGraphics,
rows: u32,
cols: u32,
snake: Snake,
just_eaten: bool,
square_width: u32,
food: Food,
score: u32,
}
impl Game {
fn render(&mut self, args: &RenderArgs) {
use graphics;
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
self.gl.draw(args.viewport(), |c, gl| {
graphics::clear(GREEN, gl);
});
self.snake.render(args);
self.food.render(&mut self.gl, args, self.square_width);
}
fn update(&mut self, args: &UpdateArgs) -> bool {
if !self.snake.update(self.just_eaten, self.cols, self.rows) {
return false;
}
if self.just_eaten {
self.score += 1;
self.just_eaten = false;
}
self.just_eaten = self.food.update(&self.snake);
if self.just_eaten {
use rand::Rng;
use rand::thread_rng;
// try my luck
let mut r = thread_rng();
loop {
let new_x = r.gen_range(0, self.cols);
let new_y = r.gen_range(0, self.rows);
if !self.snake.is_collide(new_x, new_y) {
self.food = Food { x: new_x, y: new_y };
break;
}
}
}
true
}
fn pressed(&mut self, btn: &Button) {
let last_direction = self.snake.d.clone();
self.snake.d = match btn {
&Button::Keyboard(Key::Up) if last_direction != Direction::DOWN => Direction::UP,
&Button::Keyboard(Key::Down) if last_direction != Direction::UP => Direction::DOWN,
&Button::Keyboard(Key::Left) if last_direction != Direction::RIGHT => Direction::LEFT,
&Button::Keyboard(Key::Right) if last_direction != Direction::LEFT => Direction::RIGHT,
_ => last_direction,
};
}
}
/// The direction the snake moves in.
#[derive(Clone, PartialEq)]
enum Direction {
UP,
DOWN,
LEFT,
RIGHT,
}
pub struct Snake {
gl: GlGraphics,
snake_parts: LinkedList<Snake_Piece>,
width: u32,
d: Direction,
}
#[derive(Clone)]
pub struct Snake_Piece(u32, u32);
impl Snake {
pub fn render(&mut self, args: &RenderArgs) {
use graphics;
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
let squares: Vec<graphics::types::Rectangle> = self.snake_parts
.iter()
.map(|p| Snake_Piece(p.0 * self.width, p.1 * self.width))
.map(|p| graphics::rectangle::square(p.0 as f64, p.1 as f64, self.width as f64))
.collect();
self.gl.draw(args.viewport(), |c, gl| {
let transform = c.transform;
squares
.into_iter()
.for_each(|square| graphics::rectangle(RED, square, transform, gl));
})
}
/// Move the snake if valid, otherwise returns false.
pub fn update(&mut self, just_eaten: bool, cols: u32, rows: u32) -> bool {
let mut new_front: Snake_Piece =
(*self.snake_parts.front().expect("No front of snake found.")).clone();
if (self.d == Direction::UP && new_front.1 == 0)
|| (self.d == Direction::LEFT && new_front.0 == 0)
|| (self.d == Direction::DOWN && new_front.1 == rows - 1)
|| (self.d == Direction::RIGHT && new_front.0 == cols - 1)
{
return false;
}
match self.d {
Direction::UP => new_front.1 -= 1,
Direction::DOWN => new_front.1 += 1,
Direction::LEFT => new_front.0 -= 1,
Direction::RIGHT => new_front.0 += 1,
}
if !just_eaten {
self.snake_parts.pop_back();
}
// Checks self collision.
if self.is_collide(new_front.0, new_front.1) {
return false;
}
self.snake_parts.push_front(new_front);
true
}
fn is_collide(&self, x: u32, y: u32) -> bool {
self.snake_parts.iter().any(|p| x == p.0 && y == p.1)
}
}
pub struct Food {
x: u32,
y: u32,
}
impl Food {
// Return true if snake ate food this update
fn update(&mut self, s: &Snake) -> bool {
let front = s.snake_parts.front().unwrap();
if front.0 == self.x && front.1 == self.y {
true
} else {
false
}
}
fn render(&mut self, gl: &mut GlGraphics, args: &RenderArgs, width: u32) {
use graphics;
const BLACK: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
let x = self.x * width;
let y = self.y * width;
let square = graphics::rectangle::square(x as f64, y as f64, width as f64);
gl.draw(args.viewport(), |c, gl| {
let transform = c.transform;
graphics::rectangle(BLACK, square, transform, gl)
});
}
}
fn main() {
// Change this to OpenGL::V2_1 if this fails.
let opengl = OpenGL::V3_2;
const COLS: u32 = 30;
const ROWS: u32 = 20;
const SQUARE_WIDTH: u32 = 20;
let WIDTH = COLS * SQUARE_WIDTH;
let HEIGHT = ROWS * SQUARE_WIDTH;
let mut window: GlutinWindow = WindowSettings::new("Snake Game", [WIDTH, HEIGHT])
.opengl(opengl)
.exit_on_esc(true)
.build()
.unwrap();
let mut game = Game {
gl: GlGraphics::new(opengl),
rows: ROWS,
cols: COLS,
square_width: SQUARE_WIDTH,
just_eaten: false,
food: Food { x: 1, y: 1 },
score: 0,
snake: Snake {
gl: GlGraphics::new(opengl),
snake_parts: LinkedList::from_iter((vec![Snake_Piece(COLS / 2, ROWS / 2)]).into_iter()),
width: SQUARE_WIDTH,
d: Direction::DOWN,
},
};
let mut events = Events::new(EventSettings::new()).ups(10);
while let Some(e) = events.next(&mut window) {
if let Some(r) = e.render_args() {
game.render(&r);
}
if let Some(u) = e.update_args() {
if !game.update(&u) {
break;
}
}
if let Some(k) = e.button_args() {
if k.state == ButtonState::Press {
game.pressed(&k.button);
}
}
}
println!("Congratulations, your score was: {}", game.score);
}
@j33n
Copy link

j33n commented Apr 9, 2018

Hi @SpyR1014 is there a specific reason this doesn't run on a mac.
Here is the error I've been getting.

Screenshot

screen shot 2018-04-09 at 10 15 42

@AndrewJakubowicz
Copy link
Author

Thanks for pointing this out @JeanAbayo. I didn't test on mac so don't know why that error is occurring.
Are you using the most up to date Rust, and/or nightly Rust?

Are you able to get the basic code working? I fear there is an error in the winit library.

@craigtao
Copy link

craigtao commented May 7, 2019

maybe just in windows ?

@sebolio
Copy link

sebolio commented Jun 28, 2019

tried it on Windows but it just shows the screen for a few seconds then dies with "Congratulations" message

@PurvaG1700
Copy link

it just flashes and terminates by popping out the congratulations message on ubuntu.

@AndrewJakubowicz
Copy link
Author

Unsure what is going on. But I now recommend checking out GGEZ and their snake example: https://github.com/ggez/ggez/blob/master/examples/04_snake.rs

There is also amethyst: https://github.com/amethyst/amethyst

@lufog
Copy link

lufog commented Jan 5, 2022

I don't know how to push commit to GitHub Gist, so I just put link for tweaked version here: https://gist.github.com/640f17e2e92b5d7db5002b84b662deed.git

Although there is a LOT of room for improvement, I decided to fix the very minimum so that the code does not differ much from the one shown in the video. What I did:

  • updated dependencies;
  • removed redundant imports;
  • removed extern(s) (not needed after 2015 edition, I believe);
  • a couple of naming and formatting fixes;
  • fixes in a couple of places with changed API.

If @AndrewJakubowicz "merge" it here, I'll delete my gist.

@JJodn
Copy link

JJodn commented Apr 17, 2023

please tell me the dependencies. It doesn't work for me

@Swoiksdmop
Copy link

Hi @SpyR1014 is there a specific reason this doesn't run on a mac. Here is the error I've been getting.

Screenshot

screen shot 2018-04-09 at 10 15 42

Hi, (I'm only a beginner in Rust programming, but i got intermediate knowledge in Rust). You said that the code isn't running, and you've gotten that error, As i can see in your terminal the code panicked at window.rs at line number 13 in main.rs "extern crate opengl_graphics;" which i think is the cause, so maybe opengl. I don't know the exact cause of the problem but that's worth to find out.

@Swoiksdmop
Copy link

Swoiksdmop commented Jun 7, 2023

Suprisingly enough, it runs on mine, and i'm running this Rust compiler version(rustc version) rustc 1.70.0 (90c541806 2023-05-31). Not important, but I run Endeavour OS cassini nova, Based on Arch
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment