Components and supplies
2
Resistor 1k ohm
2
Orange 3mm LED, 100 PCS
1
Arduino Nano
1
wood box
1
Transistor NPN (BC547 or similar)
1
General Purpose Transistor PNP
9
BC337-25 Transistor, NPN
2
Resistor 4.7k ohm
4
shift register - SN74HC595N
24
Resistor 22 Ohm 5% 1/16W 50V 0402 (RC0402JR-22R-Hitano)
8
Resistor 470 ohm
1
Resistor 220 ohm
1
10kOhm potentiometer
Tools and machines
1
Soldering kit
1
Multimeter
1
Wire Stripper
Apps and platforms
1
Arduino IDE
Project description
Code
Snake
cpp
Code for snake game on LED display
1/* 2 * Snake game, for my LED matrix 3 * 4 * There are two versions: autopilot and controlled by joystick. These can be 5 * toggled between by commenting out the define statement below or leaving it in. 6 * 7 * Andrew Brink, Feb 24, 2023 8 * 9 * This code is in the public domain 10 */ 11 12#include <LedMatrix.h> 13 14#define AUTOPILOT //for switching autopilot off and on 15#define MAX_SIZE 100 //max length of snake 16 17#ifdef AUTOPILOT 18const int millisDelay = 80; //controls speed of snake 19#else 20const int millisDelay = 150; 21#endif 22 23LedMatrix myLeds; 24 25//joystick connections 26const int upDown = A6; 27const int leftRight = A5; 28 29//class declarations 30class Node; 31class Snake; 32 33typedef signed char dir; 34 35class Node { //class to facilitate storage and manipulation of points 36 public: 37 void set(byte xPos, byte yPos) {x = xPos; y=yPos; myLeds.write(x,y,HIGH);} 38 void clear() {myLeds.write(x,y,LOW);} 39 byte x; 40 byte y; 41}; 42 43class Snake { //contains information of where the snake is and what it is doing 44 public: 45 void reset(); 46 byte growHead(); 47 void cutTail(); 48// void printSnake(); 49 int nextX() {return snakeNodes[head].x + dx;} 50 int nextY() {return snakeNodes[head].y + dy;} 51 int curX() {return snakeNodes[head].x;} 52 int curY() {return snakeNodes[head].y;} 53 public: 54 dir dx; 55 dir dy; 56 byte head; 57 byte length = 0; 58 Node snakeNodes[MAX_SIZE]; 59}; 60 61//for autopilot 62class infoNode { 63 public: 64 dir dx; 65 dir dy; 66}; 67 68infoNode infoMatrix[26][10]; 69 70// function declarations 71void spawnApple(); 72void resetGame(); 73void getDirection(); 74bool unSafe(dir x, dir y); 75 76// variable declarations 77unsigned long lastUpdate = 0; 78bool alive; 79byte counter; 80dir nextdx; 81dir nextdy; 82int score; 83Node apple; 84Snake mySnake; 85 86 87void setup() { 88 Serial.begin(9600); //debugging 89 Serial.println("resetting.."); 90 myLeds.begin(); 91 randomSeed(analogRead(A1)); //initialize random with noise from A1 92 resetGame(); 93 94 //stuff for autopilot 95 for (int i = 1; i<=12; i++) { 96 infoMatrix[i][0].dx = -1; 97 infoMatrix[i][0].dy = 0; 98 infoMatrix[i][9].dx = -1; 99 infoMatrix[i][9].dy = 0; 100 } 101 for (int i = 13; i<=24; i++) { 102 infoMatrix[i][0].dx = 1; 103 infoMatrix[i][0].dy = 0; 104 infoMatrix[i][9].dx = 1; 105 infoMatrix[i][9].dy = 0; 106 } 107 for (int i = 1; i<=4; i++) { 108 infoMatrix[0][i].dx = 0; 109 infoMatrix[0][i].dy = -1; 110 infoMatrix[25][i].dx = 0; 111 infoMatrix[25][i].dy = -1; 112 } 113 for (int i = 5; i<=8; i++) { 114 infoMatrix[0][i].dx = 0; 115 infoMatrix[0][i].dy = 1; 116 infoMatrix[25][i].dx = 0; 117 infoMatrix[25][i].dy = 1; 118 } 119} 120 121void loop() { 122 myLeds.refresh(); 123 124 #ifndef AUTOPILOT 125 getDirection(); //putting this here makes sure no user input is missed 126 #endif 127 128 if (millis() - lastUpdate > millisDelay) { 129 lastUpdate = millis(); 130 #ifdef AUTOPILOT 131 getDirection(); //putting this here saves processing when in autopilot 132 #endif 133 if (alive) { 134 mySnake.dx = nextdx; 135 mySnake.dy = nextdy; 136 byte snakeStatus = mySnake.growHead(); 137 if(snakeStatus == 2) { //if snake hit wall or self 138 alive = false; 139 } 140 if (snakeStatus == 1) { //if snake got apple 141 score++; 142 spawnApple(); 143 } else if (snakeStatus == 0) { //normal case 144 mySnake.cutTail(); 145 } 146 myLeds.toggle(apple.x, apple.y); //flash apple 147 } else { 148 counter++; 149 if (counter >= 3) { 150 myLeds.clear(); 151 myLeds.writeNumber(score); 152 //print state in which snake died 153// Serial.println(score); 154// for (int j =0; j<8; j++) { 155// for (int i = 0; i<24; i++) { 156// if (i==apple.x && j==apple.y) { 157// Serial.print(" * "); 158// } else if (myLeds.read(i, j)==0 ) { 159// Serial.print(". "); 160// } else if (infoMatrix[i+1][j+1].dx == 1) { 161// Serial.print("-> "); 162// } else if (infoMatrix[i+1][j+1].dx == -1) { 163// Serial.print("<- "); 164// } else if (infoMatrix[i+1][j+1].dy == 1) { 165// Serial.print("\\/ "); 166// } else if (infoMatrix[i+1][j+1].dy == -1) { 167// Serial.print("/\\ "); 168// } 169// } 170// Serial.println(); 171// } 172 } 173 if (counter >= 20) { //display score for some amount of time before resetting 174 resetGame(); 175 } 176 } 177 } 178} 179 180void Snake::reset() { 181 while(length) cutTail(); 182 head = 0; 183 length = 1; 184 snakeNodes[head].set(0,0); 185 dx = 1; 186 dy = 0; 187} 188 189byte Snake::growHead() { 190 int newX = nextX(); 191 int newY = nextY(); 192 byte success = 0; 193 if (apple.x == newX && apple.y == newY) { 194 success = 1; 195 } 196 if (myLeds.read(newX,newY) && !success) { 197 return 2; 198 } 199 if (myLeds.inBounds(newX,newY) ==0) { 200 return 2; 201 } 202 head = (head + 1)%MAX_SIZE; 203 length++; 204 while (length > MAX_SIZE) cutTail(); 205 snakeNodes[head].set(newX,newY); 206 infoMatrix[newX+1][newY+1].dx = dx; 207 infoMatrix[newX+1][newY+1].dy = dy; 208 return success; 209} 210 211void Snake::cutTail() { 212 snakeNodes[(head-length+MAX_SIZE+1)%MAX_SIZE].clear(); 213 length--; 214} 215 216 217void spawnApple() { 218 byte x, y; 219 while (1) { 220 x = random(0, myLeds.getWidth()); 221 y = random(0, myLeds.getHeight()); 222 if (myLeds.read(x,y)==0) { 223 apple.set(x,y); 224 return; 225 } 226 } 227} 228 229void resetGame() { 230 myLeds.clear(); 231 score = 0; 232 alive = true; 233 mySnake.reset(); 234 spawnApple(); 235 counter = 0; 236 nextdx = 1; 237 nextdy = 0; 238} 239 240void getDirection() { 241 #ifdef AUTOPILOT 242 dir toAppleX, toAppleY; 243 if (apple.x == mySnake.curX()) toAppleX = 0; 244 if (apple.x > mySnake.curX()) toAppleX = 1; 245 if (apple.x < mySnake.curX()) toAppleX = -1; 246 if (apple.y == mySnake.curY()) toAppleY = 0; 247 if (apple.y > mySnake.curY()) toAppleY = 1; 248 if (apple.y < mySnake.curY()) toAppleY = -1; 249 250 nextdx = mySnake.dx; 251 nextdy = mySnake.dy; 252 dir defaultX = unSafe(mySnake.nextX(), mySnake.nextY()) ? -1*infoMatrix[mySnake.nextX()+1][mySnake.nextY()+1].dx : toAppleX; 253 dir defaultY = unSafe(mySnake.nextX(), mySnake.nextY()) ? -1*infoMatrix[mySnake.nextX()+1][mySnake.nextY()+1].dy : toAppleY; 254 if (defaultX == 0) defaultX = 1; 255 if (defaultY == 0) defaultY = 1; 256 257 if (!unSafe(mySnake.curX() + nextdx, mySnake.curY() + nextdy)) { //if the next square is unsafe, skip all this and go right to the life-saving manuevers. 258 259 if ((toAppleX == 0 && mySnake.dx) || (toAppleY == 0 && mySnake.dy)) { //turning towards apple 260 nextdx = toAppleX; 261 nextdy = toAppleY; 262 } 263 264 //looking ahead 265 for (byte i = 0; unSafe(mySnake.curX() + 2*nextdx, mySnake.curY() + 2*nextdy) && i<3 && !(mySnake.nextX() == apple.x || mySnake.nextY() == apple.y); i++) { 266 switch (i) { 267 case 0: 268 nextdx = mySnake.dx; //if possible continue in the current direction 269 nextdy = mySnake.dy; 270 break; 271 case 1: 272 nextdx = mySnake.dx ? 0 : (toAppleX ? toAppleX : defaultX) ; //if previously going forward, turn towards apple 273 nextdy = mySnake.dy ? 0 : (toAppleY ? toAppleY : defaultY); 274 break; 275 case 2: 276 nextdx = -1*nextdx; //otherwise, turn away from apple 277 nextdy = -1*nextdy; 278 break; 279 } 280 } 281 } 282 283 for (byte i = 0; unSafe(mySnake.curX() + nextdx, mySnake.curY() + nextdy) && i<3; i++) { 284 switch (i) { 285 case 0: 286 nextdx = mySnake.dx; //if possible continue in the current direction 287 nextdy = mySnake.dy; 288 break; 289 case 1: 290 nextdx = mySnake.dx ? 0 : defaultX; //turn in direction previously defined 291 nextdy = mySnake.dy ? 0 : defaultY; 292 break; 293 case 2: 294 nextdx = -1*nextdx; //otherwise, turn away from apple 295 nextdy = -1*nextdy; 296 break; 297 } 298 } 299 #else 300 if (analogRead(leftRight) > 823 && mySnake.dy) { 301 nextdx = 1; 302 nextdy = 0; 303 } 304 if (analogRead(leftRight) < 200 && mySnake.dy) { 305 nextdx = -1; 306 nextdy = 0; 307 } 308 if (analogRead(upDown) > 823 && mySnake.dx) { 309 nextdx = 0; 310 nextdy = -1; 311 } 312 if (analogRead(upDown) < 200 && mySnake.dx) { 313 nextdx = 0; 314 nextdy = 1; 315 } 316 #endif 317} 318 319bool unSafe(dir x, dir y) { 320 if (x==apple.x && y==apple.y) return false; 321 return !myLeds.inBounds(x, y) || myLeds.read(x, y); 322}
Downloadable files
LedMatrix
Library for control of custom LED matrix
LedMatrix.zip
Comments
Only logged in users can leave comments