Components and supplies
Resistor 1k ohm
Orange 3mm LED, 100 PCS
Arduino Nano
wood box
Transistor NPN (BC547 or similar)
General Purpose Transistor PNP
BC337-25 Transistor, NPN
Resistor 4.7k ohm
shift register - SN74HC595N
Resistor 22 Ohm 5% 1/16W 50V 0402 (RC0402JR-22R-Hitano)
Resistor 470 ohm
Resistor 220 ohm
10kOhm potentiometer
Tools and machines
Soldering kit
Multimeter
Wire Stripper
Apps and platforms
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