-
-
Save AndrewJakubowicz/9972b5d46be474c186a2dc3a71326de4 to your computer and use it in GitHub Desktop.
| [package] | |
| name = "snake_game" | |
| version = "0.1.0" | |
| authors = ["youcodethings <[email protected]>"] | |
| [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); | |
| } |
maybe just in windows ?
tried it on Windows but it just shows the screen for a few seconds then dies with "Congratulations" message
it just flashes and terminates by popping out the congratulations message on ubuntu.
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
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.
please tell me the dependencies. It doesn't work for me
Hi @SpyR1014 is there a specific reason this doesn't run on a mac. Here is the error I've been getting.
Screenshot
![]()
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.


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.