I study Rust: How I made the game "Snake"

I study Rust: How I made the game "Snake" 3r3618.  3r33625. I recently started learning the Rust programming language, and since when I learn a new language I make on it. The Snake I decided to make her own. 3r3618.  3r33625. For 3D graphics, the 3r3r11 library was used. Three.rs
which is the port of the Three.js
library.  3r33625. → Code 3r3618.  3r33625. → Download and play 3r3618.  3r33625. 3r3618.  3r33625.
3r3618.  3r33625. 3r3618.  3r33625. 3r3331.
Screenshot game [/b] 3r3334. 3r3335. 3r3618.  3r33625.
3r3618.  3r33625. 3r3618.  3r33625.
/* We connect external libraries. 3r33625. Also in Cargo.toml
3r33625.[dependencies]3r33625. rand = "*"
three = "*"
serde = "*"
bincode = "*"
serde_derive = "*"
3r33625. prescribe 3r33625. * /3r33625. extern crate rand; 3r33625. extern crate three; 3r33625. extern crate bincode; 3r33625. extern crate serde; 3r33625. #[macro_use]3r33625. extern crate serde_derive; 3r33625. 3r33625. //Add the things we need to our scope. 3r33625. use rand :: Rng; 3r33625. use three :: *; 3r33625. use std :: error :: Error; 3r33625. 3r33625. //Entities ------------------------------------------------ ------------------- 3r3363625. 3r33625. /* 3r32525. These are macros. They generate whatever code automatically. 3r33625. In our particular case:
Debug - Generates code that allows you to output our structure to the terminal 3r33625. Clone - This will create a code that will copy our structure, that is, our structure will have a clone () 3r33625 method. Eq and PartialEq allow us to compare our Point with the operator == 3r3-3625. * /3r33625. #[derive(Debug, Clone, Eq, PartialEq, Default)]3r33625. //Announcement of a structure with two fields. It will play the role of the point
struct point {3r33625. x: u?
y: u?
} 3r33625. 3r33625. //Methods of our structure 3r33625. impl point {3r33625. //You could just use the operator == In general, this is a method that checks whether our points intersect 3r3-3625. pub fn intersects (& self, point: & Point) -> bool {
self.x == point.x && self.y == point.y
} 3r33625.} 3r33625. 3r33625. #[derive(Debug, Clone, Eq, PartialEq, Default)]3r33625. //This structure will store the object representation of the frame boundaries within which our snake will move
struct Frame {3r3625. min_x: u?
min_y: u?
max_x: u?
max_y: u?
} 3r33625. 3r33625. impl Frame {3r3625. pub fn intersects (& self, point: & Point) -> bool {
point.x == self.min_x
|| point.y == self.min_y
|| point.x == self.max_x
|| point.y == self.max_y
} 3r33625.} 3r33625. 3r33625. #[derive(Debug, Clone, Eq, PartialEq)]3r33625. //Announcement of listing with 4 options 3r3363625. //It will be responsible for where the snake head is currently turned 3r-33625. enum Direction {
Left, 3r33625. Right, 3r33625. Top, 3r32525. Bottom, 3r33625.} 3r33625. 3r33625. //Implementing a trait (in other languages, this is also called an interface) 3r336255 //for our listing. 3r33625. //The object that implements this trait is able to have a default value. 3r33625. impl Default for Direction {
fn default () -> Direction {3r3363625. return Direction :: Right; 3r33625.} 3r33625.} 3r33625. 3r33625. #[derive(Debug, Clone, Eq, PartialEq, Default)]3r33625. //Actually our snake
struct Snake {3r33625. direction: Direction,
points: std :: collections :: VecDeque
, 3r32525. start_x: u?
start_y: u?
} 3r33625. 3r33625. impl Snake {
//Static method constructor for initializing a new instance of our snake 3r3-3625. pub fn new (x: u? y: u8) -> Snake {
let mut points = std :: collections :: VecDeque :: new (); 3r33625. for i in 03 {
points.push_front (Point {x: x + i, y: i + y}); 3r33625.} 3r33625. Snake {direction: Direction :: default (), points, start_x: x, start_y: y}
} 3r33625. //Increases the length of our snake by one point 3r33625. pub fn grow (mut self) -> Snake {3r3363625. if let some (tail) = self.points.pop_back () {3r-33625. self.points.push_back (Point {x: tail.x, y: tail.y}); 3r33625. self.points.push_back (tail); 3r33625.} 3r33625. self
} 3r33625. 3r33625. //Reset our snake to its initial state 3r33625. pub fn reset (self) -> Snake {3r3363625. Snake :: new (self.start_x, self.start_y)
} 3r33625. 3r33625. //Turns the head of the snake in the direction we need 3r33625. pub fn turn (mut self, direction: Direction) -> Snake {
self.direction = direction; 3r33625. self
} 3r33625. 3r33625. //If the snake's head reaches the food, it increases the length of the snake by one and returns information about whether the food was eaten
pub fn try_eat (mut self, point: & Point) -> (Snake, bool) {
let head = self.head (); 3r33625. if head.intersects (point) {
return (self.grow (), true); 3r33625.} 3r33625. (self, false)
} 3r33625. 3r33625. //If the snake head collides with the frame, it returns the snake to the initial state 3r33625. pub fn try_intersect_frame (mut self, frame: & Frame) -> Snake {
let head = self.head (); 3r33625. if frame.intersects (& head) {
return self.reset (); 3r33625.} 3r33625. self
} 3r33625. 3r33625. //If the snake head collides with the rest, it returns the snake to its initial state. 3r33625. pub fn try_intersect_tail (mut self) -> Snake {
let head = self.head (); 3r33625. let p = self.points.clone (); 3r33625. let points = p.into_iter (). filter (| p | head.intersects (p)); 3r33625. if points.count ()> 1 {3r3362525. return self.reset (); 3r33625.} 3r33625. self
} 3r33625. 3r33625. //Gives a snake head
pub fn head (& self) -> Point {3r3363625. self.points.front (). unwrap (). clone ()
} 3r33625. 3r33625. //Moves the snake to one point in the direction where the snake's head is currently looking at 3r3363625. pub fn move_snake (mut self) -> Snake {
if let some (mut tail) = self.points.pop_back () {3r-33625. let head = self.head (); 3r33625. match self.direction {
Direction :: Right => {
tail.x = head.x + 1; 3r33625. tail.y = head.y; 3r33625.} 3r33625. Direction :: Left => {
tail.x = head.x - 1; 3r33625. tail.y = head.y; 3r33625.} 3r33625. Direction :: Top => {
tail.x = head.x; 3r33625. tail.y = head.y - 1; 3r33625.} 3r33625. Direction :: Bottom => {
tail.x = head.x; 3r33625. tail.y = head.y + 1; 3r33625.} 3r33625.} 3r33625. self.points.push_front (tail); 3r33625.} 3r33625. self
} 3r33625.} 3r33625. 3r33625. //Data Access Layer --------------------------------------------- ------------------- 3r3363625. 3r33625. #[derive(Debug, Clone, Eq, PartialEq, Default)]3r33625. //Structure for creating a new food for the snake 3r363625. struct FoodGenerator {
frame: Frame
} 3r33625. 3r33625. impl FoodGenerator {
//Creates a new point in a random place within the frame 3r3-3625. pub fn generate (& self) -> Point {3r3363625. let x = rand :: thread_rng (). gen_range (self.frame.min_x + ? self.frame.max_x); 3r33625. let y = rand :: thread_rng (). gen_range (self.frame.min_y + ? self.frame.max_y); 3r33625. Point {x, y}
} 3r33625.} 3r33625. 3r33625. #[derive(Serialize, Deserialize)]3r33625. //Stores the current and maximum score of the game
struct ScoreRepository {
score: usize
} 3r33625. 3r33625. impl ScoreRepository {
//Static method for saving current account in file 3r3625. //Result is an enumeration that can store in itself either an error or the result of calculations 3r-33625. fn save (value: usize) -> Result <(), Box = bincode :: serialize (& score) ?; 3r33625. //Create a new file or if it already exists then overwrite it. 3r33625. let mut file = File :: create (". score.data") ?; 3r33625. match file.write_all (& bytes) {
Ok (t) => Ok (t), 3r33625. //Error is a trait and a trait does not have an exact size at compile time, so 3r33625. //we need to wrap the value in the Box and as a result we are working with a pointer to 3r-33625. //heap in memory where our object lies and not with the object itself, but the pointer has a certain size 3r33625. //known during compilation
Err (e) => Err (Box :: new (e)) 3r33625.} 3r33625.} 3r33625. 3r33625. //Load the saved result from file 3r33625. fn load () -> Result
Game {3r-3625. let frame = Frame {min_x: ? min_y: ? max_x: width, max_y: height}; 3r33625. let generator = FoodGenerator {frame: frame.clone ()}; 3r33625. let food = generator.generate (); 3r33625. let snake = Snake :: new (width /? height /2); 3r33625. Game {3r32525. snake,
frame, 3r32525. food, 3r33625. food_generator: generator,
score: ? 3r3363625. max_score: match ScoreRepository :: load () {
Ok (v) => v, 3r3363625. Err (_) => 0
}, 3r32525. total_time: 0f3?
} 3r33625.} 3r33625. //Check if enough time has passed since we last visited
//moved our snake and, if so, move it
//and check if the snake head collides with the other objects of the game
//otherwise we do nothing
fn update (mut self, time_delta_in_seconds: f32) -> Game {
let (game, is_moving) = self.is_time_to_move (time_delta_in_seconds); 3r33625. self = game; 3r33625. if is_moving {
self.snake = self.snake.clone ()
       .move_snake ()
.try_intersect_tail ()
.try_intersect_frame (& self.frame); 3r33625. self.try_eat ()
} else {
self
} 3r33625.} 3r33625. 3r33625. //Check if the time has come to move the snake. 3r33625. fn is_time_to_move (mut self, time_delta_in_seconds: f32) -> (Game, bool) {
let time_to_move: f32 = ???; 3r33625. self.total_time + = time_delta_in_seconds; 3r33625. if self.total_time> time_to_move {
self.total_time - = time_to_move; 3r33625. (self, true)
} else {
(self, false)
} 3r33625.} 3r33625. 3r33625. //Check if our snake ate food and if so 3r363625. //then create a new food, charge the player points 3r3363625. //otherwise, reset the current account to the player
fn try_eat (mut self) -> Game {3r33625. let initial_snake_len = 3; 3r33625. if self.snake.points.len () == initial_snake_len {
self.score = 0
} 3r33625. let (snake, eaten) = self.snake.clone (). try_eat (& self.food); 3r33625. self.snake = snake; 3r33625. if eaten {3r3625. self.food = self.food_generator.generate (); 3r33625. self.score + = 1; 3r33625. if self.max_score < self.score {
self.max_score = self.score; 3r33625. ScoreRepository :: save (self.max_score); 3r33625.} 3r33625.}; 3r33625. self
} 3r33625. 3r33625. //Turn the snake in the right direction 3r33625. fn handle_input (mut self, input: Direction) -> Game {
let snake = self.snake.turn (input); 3r33625. self.snake = snake; 3r33625. self
} 3r33625.} 3r33625. 3r33625. //Application Layer ---------------------------------------------- ----------------
//--- Model ---- 3r3253625. #[derive(Debug, Clone, Eq, PartialEq)]3r33625. enum PointDtoType {
Head, 3r33625. Tail, 3r33625. Food, 3r32525. Frame, 3r32525.} 3r33625. 3r33625. impl Default for PointDtoType {3r33625. fn default () -> PointDtoType {3r336255. PointDtoType :: Frame
} 3r33625.} 3r33625. 3r33625. #[derive(Debug, Clone, Eq, PartialEq, Default)]3r33625. //Model that will see the view to display to the user. 3r33625. struct PointDto {3r3363625. x: u?
y: u?
state_type: PointDtoType, 3r3363625.} 3r33625. 3r33625. //------------------------------ Controller ----------------- ------------ 3r3363625. #[derive(Debug, Clone, Default)]3r33625. //The controller that will be the intermediary between the presentation and the logic of our game 3r-33625. struct GameController {
game: Game,
} 3r33625. 3r33625. impl GameController {
fn new () -> GameController {3r3363625. GameController {game: Game :: new (3? 30)}
} 3r33625. 3r33625. //Get a collection of points to draw from at the moment 3r3363625. fn get_state (& self) -> Vec
{3r33625. let mut vec: Vec
= Vec :: new (); 3r33625. vec.push (PointDto {x: self.game.food.x, y: self.game.food.y, state_type: PointDtoType :: Food}); 3r33625. let head = self.game.snake.head (); 3r33625. vec.push (PointDto {x: head.x, y: head.y, state_type: PointDtoType :: Head}); 3r33625. //All points except the head of the snake
for p in self.game.snake.points.iter (). filter (| p | ** p! = head) {
vec.push (PointDto {x: p.x, y: p.y, state_type: PointDtoType :: Tail}); 3r33625.} 3r33625. //horizontal lines of the frame
for x in self.game.frame.min_x = self.game.frame.max_x {
vec.push (PointDto {x: x, y: self.game.frame.max_y, state_type: PointDtoType :: Frame}); 3r33625. vec.push (PointDto {x: x, y: self.game.frame.min_y, state_type: PointDtoType :: Frame}); 3r33625.} 3r33625. //Vertical lines of the frame
for y in self.game.frame.min_y = self.game.frame.max_y {
vec.push (PointDto {x: self.game.frame.max_x, y: y, state_type: PointDtoType :: Frame}); 3r33625. vec.push (PointDto {x: self.game.frame.min_x, y: y, state_type: PointDtoType :: Frame}); 3r33625.} 3r33625. vec 3r33625.} 3r33625. 3r33625. //Update the game state
fn update (mut self, time_delta: f3? direction: Option
) -> GameController {
let game = self.game.clone (); 3r33625. self.game = match direction {
None => game,
Some (d) => game.handle_input (d)
} 3r33625. .update (time_delta); 3r33625. self
} 3r33625. 3r33625. pub fn get_max_score (& self) -> usize {
self.game.max_score.clone () 3r3363625.} 3r33625. 3r33625. pub fn get_score (& self) -> usize {
self.game.score.clone ()
} 3r33625.} 3r33625. 3r33625. //------------------------ View --------------- 3r363625. //View to display the game for the user and receive commands from 3r32525. struct GameView {3r33625. controller: GameController,
window: three :: Window,
camera: three :: camera :: Camera,
ambient: three :: light :: Ambient,
directional: three :: light :: Directional,
font: Font,
current_score: Text,
max_score: Text,
} 3r33625. 3r33625. impl GameView {
3r33625. fn new () -> GameView {3r3363525. let controller = GameController :: new (); 3r33625. 3r33625. //Create a window in which our game will be displayed 3r3363625. let mut window = three :: Window :: new ("3D Snake Game By Victorem"); 3r33625. 3r33625. //Create a camera through which the player will see our game 3r33625. let camera = window.factory.perspective_camera (60.? ); 3r33625. //Move the camera to[x, y, z]3r33625. camera.set_position ([15.0, 15.0, 30.0]); 3r33625. //Create a permanent ambient light 3r33625. let ambient_light = window.factory.ambient_light (0xFFFFFF, 0.5); 3r33625. window.scene.add (& ambient_light); 3r33625. //Create a directional light
let mut dir_light = window.factory.directional_light (0xffffff, 0.5); 3r33625. dir_light.look_at ([350.0, 350.0, 550.0],[0.0, 0.0, 0.0], None); 3r33625. window.scene.add (& dir_light); 3r33625. //Load from the file the font that will write the text 3r3625. let font = window.factory.load_font (format! ("{} /DejaVuSans.ttf", env! ("CARGO_MANIFEST_DIR"))); 3r33625. //Create a text on the screen where it will record the current and maximum score 3r-33625. let current_score = window.factory.ui_text (& font, "0"); 3r33625. let mut max_score = window.factory.ui_text (& font, "0"); 3r33625. max_score.set_pos ([0.0, 40.0]); 3r33625. window.scene.add (& current_score); 3r33625. window.scene.add (& max_score); 3r33625. GameView {controller, window, camera, ambient: ambient_light, directional: dir_light, font, current_score, max_score}
} 3r33625. 3r33625. //Read the key that the user last pressed and, based on it, select the new direction 3r-33625. fn get_input (& self) -> Option
{3r33625. match self.window.input.keys_hit (). last () {
None => None,
Some (k) => 3r32525. match * k {
three :: Key :: Left => Some (Direction :: Left),
three :: Key :: Right => Some (Direction :: Right),
three :: Key :: Down => Some (Direction :: Top),
three :: Key :: Up => Some (Direction :: Bottom),
_ => None,
} 3r33625.} 3r33625.} 3r33625. 3r33625. //Transform the model received from the controller into a set of grid objects of our scene 3r3-3625. fn get_meshes (mut self) -> (Vec
, GameView) {
//Create a sphere
let sphere = & three :: Geometry :: uv_sphere (0.? 2? 24); 3r33625. //Create a green cover for our sphere with the Phong lighting model
let green = & three :: material :: Phong {
color: three :: color :: GREEN,
glossiness: 30.?
}; 3r33625. let blue = & three :: material :: Phong {
color: three :: color :: BLUE,
glossiness: 30.?
}; 3r33625. let red = & three :: material :: Phong {
color: three :: color :: RED,
glossiness: 30.?
}; 3r33625. let yellow = & three :: material :: Phong {
color: three :: color :: RED | three :: color :: GREEN,
glossiness: 30.?
}; 3r33625. 3r33625. //Convert our model to grid objects 3r33625. let meshes = self.controller.clone (). get_state (). iter (). map (| s | {3r33625. let state = s.clone ();
match state.state_type {
PointDtoType :: Frame = > {
Let m = self.window.factory.mesh (sphere.clone (), blue.clone ()); 3r-33625. M.set_position ([state.x as f32, state.y as f32, 0.0]); 3r362525. M 3r3363625.}
PointDtoType :: Tail => {
Let m = self.window.factory.mesh (sphere.clone (), yellow.clone ());
M.set_position ([state.x as f32, state.y as f32, 0.0]); 3r33625. M 3r3363625.}
PointDtoType :: Head => {3r33625. Let m = self.window.factory.mesh (sphere.clone (), red.clone ()); 3r3?625. M.set_position ([state.x as f32, state.y as f32, 0.0]); 3r33625. M 3r33625.}
PointDtoType: : Food => {3 r3r3625. let m = self.window.factory.mesh (sphere.clone (), green.clone ()); 3r33625. m.set_position ([state.x as f32, state.y as f32, 0.0]); 3r33625. m 3r33625.} 3r33625.} 3r33625.}). collect (); 3r33625. (meshes, self)
} 3r33625. 3r33625. //Update our
view. fn update (mut self) -> GameView {3r33625. //Amount of time since the last update of the game 3r3363625. let elapsed_time = self.window.input.delta_time (); 3r33625. let input = self.get_input (); 3r33625. let controller = self.controller.update (elapsed_time, input); 3r33625. self.controller = controller; 3r33625. self
} 3r33625. 3r33625. //Display our presentation to player
fn draw (mut self) -> GameView {3r3363625. let (meshes, view) = self.get_meshes (); 3r33625. self = view; 3r33625. //Add meshes to the scene. 3r33625. for m in & meshes {
self.window.scene.add (m); 3r33625.} 3r33625. //Display the scene on the camera 3r3363625. self.window.render (& self.camera); 3r33625. //Clear the scene
for m in meshes {
self.window.scene.remove (m); 3r33625.} 3r33625. //Display the current account to the user
self.max_score.set_text (format! ("MAX SCORE: {}", self.controller.get_max_score ())); 3r33625. self.current_score.set_text (format! ("CURRENT SCORE: {}", self.controller.get_score ())); 3r33625. self
} 3r33625. 3r33625. //Run the infinite update cycle and from the drawing of the game 3r336255. pub fn run (mut self) {
while self.window.update () &&! self.window.input.hit (three :: KEY_ESCAPE) {
self = self.update (). draw (); 3r33625.} 3r33625.} 3r33625.} 3r33625. 3r33625. fn main () {3r33625. let mut view = GameView :: new (); 3r33625. view.run (); 3r33625.} 3r33625. 3r31414. 3r3615. 3r3618.  3r33625. 3r3618.  3r33625. Unfortunately, within the framework of this game, it was not possible to touch the flows and work with the network. I will try it on the next project. So far I like the language and it’s a pleasure to work with it. I would be grateful for practical advice and constructive criticism.
3r33625. 3r33625.
! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r32424. 3r33625.
+ 0 -

Add comment