I re-purposed the display I made earlier to act as a snake game. It’s not perfect but it’s a good start. Code is after the break. Photos here.
/* Snake Game for Arduino * * By Matt Mets, completed in 2008 * * This code is released into the public domain. Attribution is appreciated. */ /***** Board setup ************************************************************************/ /* Note that the data and row pins are accessed using the PORTB and PORTBD macros, so they are * not defined here. */ int rowEnable = 9; int redClock = 10; int greenClock = 11; int blueClock = 12; int buttonN = 7; // Input buttons int buttonS = 8; int buttonE = 14; int buttonW = 15; #define BOARD_SIZE 7 // board dimension per side (so 7x7 array) #define MAX_SNAKE_SIZE 20 // Maximum length our snake can grow to #define APPLES_BEFORE_GROW 4 // Number of apples the snake must eat before growing #define START_SPEED 60 // Starting speed, in screen refresh times per turn /***** Type Defines ***********************************************************************/ typedef struct row_data_t { byte red; byte green; byte blue; } row_data_t; typedef struct coord_t { byte x; byte y; } coord_t; enum colors_t { COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_CYAN, COLOR_PURPLE, COLOR_WHITE }; enum direction_t { DIR_NORTH, DIR_EAST, DIR_SOUTH, DIR_WEST } direction_t; /***** Library functions ******************************************************************/ /* Store the data byte in a color bank register */ int writeColor(byte data, int clockPort) { PORTD = data; digitalWrite(clockPort, LOW); digitalWrite(clockPort, HIGH); return 0; } /* Cause a row to be displayed by writing its address to the DEMUX, then strobing the enable * line high for a short time. */ int strobeRow(byte row) { PORTD = row << 4; digitalWrite(rowEnable, HIGH); delay(1); digitalWrite(rowEnable, LOW); return 0; } /* Draw an entire page, by drawing each row in succession. */ int writePage(struct row_data_t* page) { int i; for(i = 0; i < 7; i++) { // write each color, then hold the row high for a small time to show it writeColor(page[i].red, redClock); writeColor(page[i].green, greenClock); writeColor(page[i].blue, blueClock); strobeRow(i); } return 0; } /* Set a pixel in an image page to a given color */ int setPixel(row_data_t* page, struct coord_t coord, int color) { if(color == COLOR_RED || color == COLOR_YELLOW || color == COLOR_PURPLE || color == COLOR_WHITE) { page[coord.y].red |= (1 << coord.x); // Set the bit } else { page[coord.y].red &= ~(1 << coord.x); // Clear the bit } if(color == COLOR_GREEN || color == COLOR_CYAN || color == COLOR_YELLOW || color == COLOR_WHITE) { page[coord.y].green |= (1 << coord.x); // Set the bit } else { page[coord.y].green &= ~(1 << coord.x); // Clear the bit } if(color == COLOR_BLUE || color == COLOR_PURPLE || color == COLOR_CYAN|| color == COLOR_WHITE) { page[coord.y].blue |= (1 << coord.x); // Set the bit } else { page[coord.y].blue &= ~(1 << coord.x); // Clear the bit } return 0; } /* Return the color of a pixel at a given coordinate */ int getPixel(row_data_t* page, struct coord_t coord) { // Look for red if(page[coord.y].red & (1 << coord.x)) { if(page[coord.y].green & (1 << coord.x)) { if(page[coord.y].blue & (1 << coord.x)) return COLOR_WHITE; return COLOR_YELLOW; } else { if(page[coord.y].blue & (1 << coord.x)) return COLOR_PURPLE; return COLOR_RED; } } else { if(page[coord.y].green & (1 << coord.x)) { if(page[coord.y].blue & (1 << coord.x)) return COLOR_CYAN; return COLOR_GREEN; } else { if(page[coord.y].blue & (1 << coord.x)) return COLOR_BLUE; return COLOR_BLACK; } } } /* Snake Game */ void snakeGame() { int i; int snakeLength = 3; // Length of our snake; this grows for every x 'apples' eaten coord_t snakeBody[MAX_SNAKE_SIZE]; // Table of each piece of the snakes body int snakeAlive = 1; // The state of our snake int snakeDirection = DIR_EAST; // The direction the snake is heading coord_t applePosition; // Location of the apple int applesEaten = 0; // Number of apples that have been eaten int gameSpeed = START_SPEED; // Speed of the game (in ms per turn) row_data_t gamePage[BOARD_SIZE]; // The page to draw each frame of the game // Put the snake in an initial position snakeBody[0].x = 2; snakeBody[0].y = 4; snakeBody[1].x = 1; snakeBody[1].y = 4; snakeBody[2].x = 0; snakeBody[2].y = 4; // Put the apple in an initial position (this could collide with the snake!) applePosition.x = random(0, 6); applePosition.y = random(0, 6); /* Game loop */ while(snakeAlive) { /*** Draw the screen ***/ /* clear the frame */ memset(gamePage, 0, sizeof(row_data_t)*BOARD_SIZE); // Clear the screen /* paint the snake */ for(i = 0; i < snakeLength; i++) setPixel(gamePage, snakeBody[i], COLOR_GREEN); /* paint the apple */ setPixel(gamePage, applePosition, COLOR_RED); /* draw the screen x times, watching for a change in input */ for(i = 0; i < gameSpeed; i++) { writePage(gamePage); /* Look for change in input- if button pressed, update direction. * If more than one button pressed, the last one counts. */ if(digitalRead(buttonN) == HIGH) snakeDirection = DIR_NORTH; if(digitalRead(buttonS) == HIGH) snakeDirection = DIR_SOUTH; if(digitalRead(buttonE) == HIGH) snakeDirection = DIR_EAST; if(digitalRead(buttonW) == HIGH) snakeDirection = DIR_WEST; } /*** Update the game state ***/ /* update snake position */ for(i = snakeLength-1; i >= 0; i--) // Push the current position back one snakeBody[i+1] = snakeBody[i]; // (this causes the end of the tail to dissapear) switch(snakeDirection) // Update the head (this causes the snake to move forward) { case DIR_EAST: snakeBody[0].x = (snakeBody[0].x + 1) % BOARD_SIZE; break; case DIR_WEST: snakeBody[0].x = (snakeBody[0].x + BOARD_SIZE - 1) % BOARD_SIZE; break; case DIR_NORTH: snakeBody[0].y = (snakeBody[0].y + BOARD_SIZE - 1) % BOARD_SIZE; break; case DIR_SOUTH: snakeBody[0].y = (snakeBody[0].y + 1) % BOARD_SIZE; break; } /* see if the snake head collided with anything */ switch(getPixel(gamePage, snakeBody[0])) { case COLOR_RED: /* Snake ate an apple */ /* increment score */ applesEaten++; /* draw a new apple somewhere that doesnt overlap the snake */ applePosition.x = random(0, 6); applePosition.y = random(0, 6); /* if x apples eaten, grow snake and speed up */ if(applesEaten % APPLES_BEFORE_GROW == 0) { snakeLength++; gameSpeed -= 5; } break; case COLOR_GREEN: /* Snake hit self */ snakeAlive = 0; break; case COLOR_YELLOW: /* Snake hit a barrier (note: there are no barriers) */ snakeAlive = 0; break; } } } /* Initialize the IO ports */ void setup() { DDRD = B11111111; // data port DDRB = B00111111; // row addresses, data clock pinMode(rowEnable, OUTPUT); pinMode(buttonN, INPUT); pinMode(buttonS, INPUT); pinMode(buttonE, INPUT); pinMode(buttonW, INPUT); } /* Main loop */ void loop() { /* Just play the game, then restart if the player dies. */ snakeGame(); } |
Can i have the electrical schematic for this?
Hi Yaniv, I don’t think you really want to re-create my circuit, this is what it looks like:
http://www.flickr.com/photos/cibomahto/2250485968/
You might want to check out the Meggy Jr, which is an open source project that is much more polished. They have source code and schematics available:
http://www.evilmadscientist.com/article.php/meggyjr
You are right, i am using a different LED matrix, and its only one color, but its a good way to lear more about the aurduino….
cn i have the concept you are using in building this snake game
how to produce a food,and how to increase the length of the snake
i want to do with different programming
top view