-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsnake.ino
346 lines (293 loc) · 10.5 KB
/
snake.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
// #include <Arduino_LED_Matrix.h>
#include "JoyStick.h"
#include "number_array.h" // In this file is function to get the bitmap of a number
#include "clear_screen.h"
#include "simple_ai.h"
ArduinoLEDMatrix matrix;
SimpleAI simple_ai(3, 10);
// 507 and 509 are values for the joystick in normal position
// 5 and 5 are the deadzone precent (to ignore small movements of the joystick)
// You can change the SW pin to digital pin
JoyStick joy_stick(A0, A1, A2, 509, 507, 5, 5);
const int screen_width = 12;
const int screen_height = 8;
bool ai = false;
bool show_menu = true;
bool playing = false;
bool show_end_screen = false;
float difficulty = 5; // 1-9
float multiplier; // 0.02*difficulty + 1
float speed = 3;
float delay_time = 1000/speed; // ms
float current_millis = 0;
float last_millis_end_screen = 0;
float last_millis_food = 0;
float blink_delay = 400;
bool btn_pressed = false;
int btn_pressed_millis = 0;
int btn_released_millis = 0;
int long_press = 750;
struct coordinates{
int x;
int y;
};
int snake_length = 1;
coordinates snake[96]; // Max length of snake is 96 (8*12)
coordinates food;
String joy_stick_pos = ""; // in which direction the joystick is pointing ("UP", "RIGHT", "DOWN", "LEFT", "CENTER")
String direction = ""; // in which direction the snake is moving ("UP", "RIGHT", "DOWN", "LEFT")
String last_direction = ""; // in which direction the snake was moving last ("UP", "RIGHT", "DOWN", "LEFT")
void setup() {
Serial.begin(9600);
matrix.begin();
randomSeed(analogRead(A4) + analogRead(A5)); // Seed for random food position (I am using 2 analog pins to get a more random seed)
generate_food(); // Generate the first food
snake[0].x = screen_width/2; // Start position of the snake is in the middle of the screen
snake[0].y = screen_height/2; // Start position of the snake is in the middle of the screen
if (ai){
playing = false;
show_end_screen = false;
show_menu = false;
}
}
void loop() {
// Here you can call print function to print the values
// print();
// If the stage of the game is playing (main)
if (playing){
delay_time = 1000/speed; // Update the delay time
for (int _ = 0; _ < 10; _++){ // If this loop wasn't here, the joystick would be checked only once every delay time
get_direction();
delay(delay_time/10);
// If the game is still playing, render the snake and the food on the screen
if (!show_end_screen){
render_on_screen(); // Render the snake and the food on the screen
}
}
last_direction = direction; // To prevent the snake from going back on itself
move(); // Move the snake
check_for_collisions(); // Check for collisions
// If the stage of the game is menu
}else if (show_menu){
// If the joystick is in the left the difficulty will decrease
if (difficulty > 0 && joy_stick.get_x_direction() == "LEFT"){
difficulty--;
delay(150); // To prevent the difficulty from changing too fast
// If the joystick is in the right the difficulty will increase
}else if (difficulty < 10 && joy_stick.get_x_direction() == "RIGHT"){
difficulty++;
delay(150); // To prevent the difficulty from changing too fast
}
// // If the joystick is pressed, the game will start
// if (joy_stick.get_SW()){
// multiplier = float(0.02*difficulty) + 1; // The multiplier is used to increase the speed of the snake based on the difficulty
// speed *= multiplier; // Increase the speed of the snake
// playing = true; // The game is now playing
// show_menu = false; // The menu is now hidden
// }
if (joy_stick.get_SW() && !btn_pressed){
btn_pressed = true;
btn_pressed_millis = millis();
}
if (btn_pressed && !joy_stick.get_SW()){
btn_released_millis = millis();
btn_pressed = false;
}
if (btn_released_millis - btn_pressed_millis >= long_press){
ai = true;
playing = false;
show_end_screen = false;
show_menu = false;
btn_pressed = false;
btn_pressed_millis = 0;
btn_released_millis = 0;
}else if (btn_released_millis - btn_pressed_millis > 0){
multiplier = float(0.02*difficulty) + 1; // The multiplier is used to increase the speed of the snake based on the difficulty
speed *= multiplier; // Increase the speed of the snake
playing = true; // The game is now playing
show_menu = false; // The menu is now hidden
btn_pressed = false;
btn_pressed_millis = 0;
btn_released_millis = 0;
}
// Render the difficulty on the screen
number_on_matrix(difficulty);
// If the stage of the game is end screen
}else if (show_end_screen){
current_millis = millis();
int score = snake_length - 1; // calculate the score
if (current_millis - last_millis_end_screen >= blink_delay){
matrix.renderBitmap(clear_screen, 8, 12); // Render the clear screen
if (current_millis - last_millis_end_screen >= blink_delay*2){
number_on_matrix(score); // Render the score on the screen
last_millis_end_screen = current_millis;
}
}
// If the joystick is pressed, the game will reset
if (joy_stick.get_SW()){
reset_variables(); // Reset the variables to start the game again
delay(100);
}
}else if (ai){
if (simple_ai._playing){
delay(1000/simple_ai._speed);
simple_ai.move();
simple_ai.check_for_collisions();
if (simple_ai._playing){
simple_ai.render_on_screen();
}
}else{
delay(250);
number_on_matrix(simple_ai.get_score());
delay(100*simple_ai.get_score());
simple_ai.reset_variables();
}
}
}
void get_direction(){
joy_stick_pos = joy_stick.get_direction(); // Get the direction of the joystick
if (joy_stick_pos != "CENTER"){ // If the joystick is not in the center
if (joy_stick_pos == "UP" && last_direction != "DOWN"){ // If the joystick is pointing up and the snake wasn't moving down
direction = "UP";
}else if (joy_stick_pos == "RIGHT" && last_direction != "LEFT"){ // If the joystick is pointing right and the snake wasn't moving left
direction = "RIGHT";
}else if (joy_stick_pos == "DOWN" && last_direction != "UP"){ // If the joystick is pointing down and the snake wasn't moving up
direction = "DOWN";
}else if (joy_stick_pos == "LEFT" && last_direction != "RIGHT"){ // If the joystick is pointing left and the snake wasn't moving right
direction = "LEFT";
}
}
}
void move(){
// Move the body of the snake
for (int i = snake_length; i > 0; i--){
snake[i].x = snake[i-1].x;
snake[i].y = snake[i-1].y;
}
// Move the head of the snake
if (direction == "UP"){
snake[0].y -= 1;
}else if (direction == "RIGHT"){
snake[0].x += 1;
}else if (direction == "DOWN"){
snake[0].y += 1;
}else if (direction == "LEFT"){
snake[0].x -= 1;
}
}
void check_for_collisions(){
// If the snake collides with the food
if (snake[0].x == food.x && snake[0].y == food.y){
snake_length++; // Increase the length of the snake
speed *= float(multiplier); // Increase the speed of the snake
generate_food(); // Generate a new food
}
// If the snake collides with itself
for (int i = 1; i < snake_length; i++){
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y){ // If the head of the snake collides with the body of the snake
playing = false;
show_end_screen = true;
}
}
// If the snake is out of the screen
if (snake[0].x > screen_width-1){
playing = false;
show_end_screen = true;
}else if (snake[0].x < 0){
playing = false;
show_end_screen = true;
}else if (snake[0].y < 0){
playing = false;
show_end_screen = true;
}else if (snake[0].y > screen_height-1){
playing = false;
show_end_screen = true;
}
}
// Generate a new food
void generate_food(){
food.x = random(0, screen_width); // Generate a random x position for the food
food.y = random(0, screen_height); // Generate a random y position for the food
// To avoid the food to be generated on the snake
for (int i = 0; i < snake_length; i++){
// If the food is generated on the snake, generate a new food
if (food.x == snake[i].x && food.y == snake[i].y){
generate_food();
}
}
}
// Render the snake and the food on the screen
void render_on_screen(){
// Create clear screen
byte on_screen[8][12] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
// Render each part of the snake
for (int i = 0; i < snake_length; i++){
on_screen[snake[i].y][snake[i].x] = 1;
}
current_millis = millis();
if (current_millis - last_millis_food >= blink_delay/4){
on_screen[food.y][food.x] = 1;
if (current_millis - last_millis_food >= blink_delay){
on_screen[food.y][food.x] = 0;
last_millis_food = current_millis;
}
}
matrix.renderBitmap(on_screen, 8, 12); // Render the bitmap on the screen
}
// This function is here because the get_number function is returning a pointer to a 2D array
void number_on_matrix(int number){
byte (*p)[8][12] = get_number(number); // Get the pointer to the bitmap of the number
// Create a copy of the bitmap
byte copy[8][12];
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 12; ++j) {
copy[i][j] = (*p)[i][j];
}
}
matrix.renderBitmap(copy, 8, 12); // Render the bitmap on the screen
}
// Print the values to the serial monitor
void print(){
if (playing){
Serial.println("");
Serial.print("direction: ");
Serial.println(direction);
Serial.print("snake length: ");
Serial.println(snake_length);
Serial.print("speed: ");
Serial.println(speed);
Serial.print("delay: ");
Serial.print(1000/speed);
Serial.println("ms");
}
}
// Reset the variables to start the game again
void reset_variables(){
randomSeed(analogRead(A4) + analogRead(A5)); // Seed for random food position (I am using 2 analog pins to get a more random seed)
playing = false;
show_end_screen = false;
show_menu = true;
speed = 3;
// Reset the snake
for (int i = 0; i < snake_length; i++){
snake[i].x = 0;
snake[i].y = 0;
}
snake_length = 1;
snake[0].x = screen_width/2;
snake[0].y = screen_height/2;
direction = "";
joy_stick_pos = "";
generate_food();
matrix.renderBitmap(clear_screen, 8, 12);
delay(250);
}