Created
December 13, 2019 09:33
-
-
Save Electronza/f6f83daff7343482d6d8b9fa8280bb24 to your computer and use it in GitHub Desktop.
Arduino Game of Life
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************* | |
____ __ ____ ___ ____ ____ __ __ _ ____ __ | |
( __)( ) ( __)/ __)(_ _)( _ \ / \ ( ( \(__ ) / _\ | |
) _) / (_/\ ) _)( (__ )( ) /( O )/ / / _/ / \ | |
(____)\____/(____)\___) (__) (__\_) \__/ \_)__)(____)\_/\_/ | |
Project name: Arduino: 16×8 game of life | |
Project page: https://electronza.com/arduino-16x8-game-of-life/ | |
Description: Arduino Uno, Mikroe Arduino Uno click shield with two 8x8 B Click boards | |
********************************************************************/ | |
#include <EEPROM.h> | |
#include "LedControl.h" // need the library | |
LedControl lc1=LedControl(11,13,9,1); // | |
LedControl lc2=LedControl(11,13,10,1); // | |
/** GAME OF LIFE | |
For an 16x8 LED matrix */ | |
const int NUMROWS = 16; | |
const int NUMCOLS = 8; | |
/** | |
* Conditional compilation directives | |
*/ | |
#define SHOW_STARTUP_SEQUENCE 1 // uncomment this line to enable the startup sequence | |
// The acorn | |
boolean gameBoard[NUMROWS][NUMCOLS] = { | |
{ 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, 1, 0, 0, 0, 0, 0 }, | |
{ 0, 0, 1, 0, 1, 0, 0, 0 }, | |
{ 0, 0, 0, 0, 0, 0, 0, 0 }, | |
{ 0, 0, 0, 1, 0, 0, 0, 0 }, | |
{ 0, 0, 1, 0, 0, 0, 0, 0 }, | |
{ 0, 0, 1, 0, 0, 0, 0, 0 }, | |
{ 0, 0, 1, 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 }, | |
}; | |
boolean newGameBoard[NUMROWS][NUMCOLS] = { | |
{ 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 }, | |
{ 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 }, | |
}; | |
///////////////////////////////// | |
void setup() { | |
Serial.begin(9600); | |
Serial.println("nBegin setup()"); | |
lc1.shutdown(0,false);// turn off power saving, enables display | |
lc1.setIntensity(0,4);// sets brightness (0~15 possible values) | |
lc1.clearDisplay(0);// clear screen | |
lc2.shutdown(0,false);// turn off power saving, enables display | |
lc2.setIntensity(0,4);// sets brightness (0~15 possible values) | |
lc2.clearDisplay(0);// clear screen | |
delay(10000); | |
startupSequence(); | |
lc1.clearDisplay(0);// clear screen | |
lc2.clearDisplay(0);// clear screen | |
delay(2000); | |
//setUpInitialBoard(); | |
//newGameBoard=gameBoard; | |
Serial.println("End setup()n"); | |
} | |
void loop() { | |
// Display the current game board for approx. 500ms | |
lc1.clearDisplay(0);// clear screen | |
lc2.clearDisplay(0);// clear screen | |
displayGameBoard(); | |
delay(250); | |
// Calculate the next iteration | |
calculateNewGameBoard(); | |
swapGameBoards(); | |
} | |
/** | |
* Does a nice little animation to aid visual checks that all LEDs are correctly connected and operating. | |
* Lights every LED in each row, going back and forth across the rows, from top to bottom. | |
*/ | |
void startupSequence() { | |
#ifdef SHOW_STARTUP_SEQUENCE | |
for (int row=0; row<8; row++) { | |
if (row%2 == 0) { | |
for (int col=0; col<NUMCOLS; col++) { | |
lc1.setLed(0,row,col,1); | |
delay(50); | |
lc1.setLed(0,row,col,0); | |
} | |
} else { | |
for (int col=NUMCOLS-1; col>=0; col--) { | |
lc1.setLed(0,row,col,1); | |
delay(50); | |
lc1.setLed(0,row,col,0); | |
} | |
} | |
} | |
for (int row=0; row<8; row++) { | |
if (row%2 == 0) { | |
for (int col=0; col<NUMCOLS; col++) { | |
lc2.setLed(0,row,col,1); | |
delay(50); | |
lc2.setLed(0,row,col,0); | |
} | |
} else { | |
for (int col=NUMCOLS-1; col>=0; col--) { | |
lc2.setLed(0,row,col,1); | |
delay(50); | |
lc2.setLed(0,row,col,0); | |
} | |
} | |
} | |
#endif // defined SHOW_STARTUP_SEQUENCE | |
} | |
/** | |
* Loops over all game board positions, and briefly turns on any LEDs for "on" positions. | |
*/ | |
void displayGameBoard() { | |
for (byte row=0; row<NUMROWS; row++) { | |
for (byte col=0; col<NUMCOLS; col++) { | |
if (gameBoard[row][col]) { | |
if (row < 8){ | |
lc1.setLed(0,row,col,1);} | |
else { | |
lc2.setLed(0,row-7,col,1); | |
} | |
} | |
} | |
} | |
} | |
/** | |
/** | |
* Counts the number of active cells surrounding the specified cell. | |
* Cells outside the board are considered "off" | |
* Returns a number in the range of 0 <= n < 9 | |
*/ | |
byte countNeighbors(byte row, byte col) { | |
byte count = 0; | |
for (char rowDelta=-1; rowDelta<=1; rowDelta++) { | |
for (char colDelta=-1; colDelta<=1; colDelta++) { | |
// skip the center cell | |
if (!(colDelta == 0 && rowDelta == 0)) { | |
if (isCellAlive(rowDelta+row, colDelta+col)) { | |
count++; | |
} | |
} | |
} | |
} | |
return count; | |
} | |
/** | |
* Returns whether or not the specified cell is on. | |
* If the cell specified is outside the game board, returns false. | |
*/ | |
boolean isCellAlive(char row, char col) { | |
if (row < 0 || col < 0 || row >= NUMROWS || col >= NUMCOLS) { | |
return false; | |
} | |
return (gameBoard[row][col]== 1); | |
} | |
/** | |
* Encodes the core rules of Conway's Game Of Life, and generates the next iteration of the board. | |
* Rules taken from wikipedia. | |
*/ | |
void calculateNewGameBoard() { | |
for (byte row=0; row<NUMROWS; row++) { | |
for (byte col=0; col<NUMCOLS; col++) { | |
byte numNeighbors = countNeighbors(row, col); | |
if (gameBoard[row][col]&& numNeighbors < 2) { | |
// Any live cell with fewer than two live neighbours dies, as if caused by under-population. | |
newGameBoard[row][col]= false; | |
} else if (gameBoard[row][col]&& (numNeighbors == 2 || numNeighbors == 3)) { | |
// Any live cell with two or three live neighbours lives on to the next generation. | |
newGameBoard[row][col]= true; | |
} else if (gameBoard[row][col]&& numNeighbors > 3) { | |
// Any live cell with more than three live neighbours dies, as if by overcrowding. | |
newGameBoard[row][col]= false; | |
} else if (!gameBoard[row][col]&& numNeighbors == 3) { | |
//} else if (gameBoard[row][col]&& numNeighbors == 3) { | |
// Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. | |
newGameBoard[row][col]= true; | |
} else { | |
// All other cells will remain off | |
newGameBoard[row][col]= false; | |
} | |
} | |
} | |
} | |
/** | |
* Copies the data from the new game board into the current game board array | |
*/ | |
void swapGameBoards() { | |
for (byte row=0; row<NUMROWS; row++) { | |
for (byte col=0; col<NUMCOLS; col++) { | |
gameBoard[row][col]= newGameBoard[row][col]; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment