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}