snake/
snake.rs

1use macroquad::prelude::*;
2
3use std::collections::LinkedList;
4
5const SQUARES: i16 = 16;
6
7type Point = (i16, i16);
8
9struct Snake {
10    head: Point,
11    body: LinkedList<Point>,
12    dir: Point,
13}
14
15#[macroquad::main("Snake")]
16async fn main() {
17    let mut snake = Snake {
18        head: (0, 0),
19        dir: (1, 0),
20        body: LinkedList::new(),
21    };
22    let mut fruit: Point = (rand::gen_range(0, SQUARES), rand::gen_range(0, SQUARES));
23    let mut score = 0;
24    let mut speed = 0.3;
25    let mut last_update = get_time();
26    let mut navigation_lock = false;
27    let mut game_over = false;
28
29    let up = (0, -1);
30    let down = (0, 1);
31    let right = (1, 0);
32    let left = (-1, 0);
33
34    loop {
35        if !game_over {
36            if is_key_down(KeyCode::Right) && snake.dir != left && !navigation_lock {
37                snake.dir = right;
38                navigation_lock = true;
39            } else if is_key_down(KeyCode::Left) && snake.dir != right && !navigation_lock {
40                snake.dir = left;
41                navigation_lock = true;
42            } else if is_key_down(KeyCode::Up) && snake.dir != down && !navigation_lock {
43                snake.dir = up;
44                navigation_lock = true;
45            } else if is_key_down(KeyCode::Down) && snake.dir != up && !navigation_lock {
46                snake.dir = down;
47                navigation_lock = true;
48            }
49
50            if get_time() - last_update > speed {
51                last_update = get_time();
52                snake.body.push_front(snake.head);
53                snake.head = (snake.head.0 + snake.dir.0, snake.head.1 + snake.dir.1);
54                if snake.head == fruit {
55                    fruit = (rand::gen_range(0, SQUARES), rand::gen_range(0, SQUARES));
56                    score += 100;
57                    speed *= 0.9;
58                } else {
59                    snake.body.pop_back();
60                }
61                if snake.head.0 < 0
62                    || snake.head.1 < 0
63                    || snake.head.0 >= SQUARES
64                    || snake.head.1 >= SQUARES
65                {
66                    game_over = true;
67                }
68                for (x, y) in &snake.body {
69                    if *x == snake.head.0 && *y == snake.head.1 {
70                        game_over = true;
71                    }
72                }
73                navigation_lock = false;
74            }
75        }
76        if !game_over {
77            clear_background(LIGHTGRAY);
78
79            let game_size = screen_width().min(screen_height());
80            let offset_x = (screen_width() - game_size) / 2. + 10.;
81            let offset_y = (screen_height() - game_size) / 2. + 10.;
82            let sq_size = (screen_height() - offset_y * 2.) / SQUARES as f32;
83
84            draw_rectangle(offset_x, offset_y, game_size - 20., game_size - 20., WHITE);
85
86            for i in 1..SQUARES {
87                draw_line(
88                    offset_x,
89                    offset_y + sq_size * i as f32,
90                    screen_width() - offset_x,
91                    offset_y + sq_size * i as f32,
92                    2.,
93                    LIGHTGRAY,
94                );
95            }
96
97            for i in 1..SQUARES {
98                draw_line(
99                    offset_x + sq_size * i as f32,
100                    offset_y,
101                    offset_x + sq_size * i as f32,
102                    screen_height() - offset_y,
103                    2.,
104                    LIGHTGRAY,
105                );
106            }
107
108            draw_rectangle(
109                offset_x + snake.head.0 as f32 * sq_size,
110                offset_y + snake.head.1 as f32 * sq_size,
111                sq_size,
112                sq_size,
113                DARKGREEN,
114            );
115
116            for (x, y) in &snake.body {
117                draw_rectangle(
118                    offset_x + *x as f32 * sq_size,
119                    offset_y + *y as f32 * sq_size,
120                    sq_size,
121                    sq_size,
122                    LIME,
123                );
124            }
125
126            draw_rectangle(
127                offset_x + fruit.0 as f32 * sq_size,
128                offset_y + fruit.1 as f32 * sq_size,
129                sq_size,
130                sq_size,
131                GOLD,
132            );
133
134            draw_text(format!("SCORE: {score}").as_str(), 10., 20., 20., DARKGRAY);
135        } else {
136            clear_background(WHITE);
137            let text = "Game Over. Press [enter] to play again.";
138            let font_size = 30.;
139            let text_size = measure_text(text, None, font_size as _, 1.0);
140
141            draw_text(
142                text,
143                screen_width() / 2. - text_size.width / 2.,
144                screen_height() / 2. + text_size.height / 2.,
145                font_size,
146                DARKGRAY,
147            );
148
149            if is_key_down(KeyCode::Enter) {
150                snake = Snake {
151                    head: (0, 0),
152                    dir: (1, 0),
153                    body: LinkedList::new(),
154                };
155                fruit = (rand::gen_range(0, SQUARES), rand::gen_range(0, SQUARES));
156                score = 0;
157                speed = 0.3;
158                last_update = get_time();
159                game_over = false;
160            }
161        }
162        next_frame().await;
163    }
164}