Skip to content

Commit 015a585

Browse files
update conway game of life example to auto-reset
1 parent 5089474 commit 015a585

File tree

3 files changed

+159
-44
lines changed

3 files changed

+159
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88
## [Unreleased]
99

1010
### Changed
11+
- Updated the Conway Game of Life example to auto-reset itself it the game evolves to a static, unchanging state.
1112

1213
### Added
1314
- Basic capability to specify the endian-ness of the column layout for a matrix. While column 0 is meant to indicate the left most column, the hardware layout of the matrix might not make the left most column the MSB in the shift register chain. This might happen if you make a mistake in your hardware (oops!). By setting the column endian value of the matrix,the high level software can still reference column 0 to mean the left most column, and the bit layout sent the shift registers will be adjusted according to where column 0 physically is in the hardware.

examples/conway-game-of-life-10x10/conway-game-of-life-10x10.ino

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public:
2828
const static LifeState BORN = 2;
2929
const static LifeState DYING = 3;
3030
const static LifeState DEAD = 0;
31+
const static LifeState GAME_OVER = 4;
3132

3233
private:
3334

@@ -37,7 +38,10 @@ private:
3738
LifeState* _cells;
3839
LifeState* _nextCells;
3940

41+
bool _isReseting;
42+
4043
RGBColorType getColorForLifeState( LifeState state ) const;
44+
4145

4246
protected:
4347
virtual void action();
@@ -49,11 +53,12 @@ public:
4953
unsigned long updateMicros
5054
);
5155

52-
void setCellStatus(int row, int column, LifeState cellStatus);
53-
LifeState getCellStatus(int row, int column) const;
54-
55-
bool isAlive(int row, int column) const;
56-
int countAliveNeighbors(int row, int column) const;
56+
void setCellStatus(unsigned int row, unsigned int column, LifeState cellStatus);
57+
LifeState getCellStatus(unsigned int row, unsigned int column) const;
58+
void createRandomState();
59+
60+
bool isAlive(unsigned int row, unsigned int column) const;
61+
int countAliveNeighbors(unsigned int row, unsigned int column) const;
5762

5863
void drawToScreen();
5964
};
@@ -64,46 +69,47 @@ CellUniverse::CellUniverse(
6469
) : TimerAction(updateMicros),
6570
_leds(matrix),
6671
_cells(new LifeState[matrix.rows()*matrix.columns()]),
67-
_nextCells(new LifeState[matrix.rows()*matrix.columns()])
72+
_nextCells(new LifeState[matrix.rows()*matrix.columns()]),
73+
_isReseting(false)
6874
{
6975
memset(_cells,DEAD,matrix.rows()*matrix.columns()*sizeof(LifeState));
7076
memset(_nextCells,DEAD,matrix.rows()*matrix.columns()*sizeof(LifeState));
7177
}
7278

73-
void CellUniverse::setCellStatus(int row, int column, LifeState cellStatus) {
79+
void CellUniverse::setCellStatus(unsigned int row, unsigned int column, LifeState cellStatus) {
7480
if (row < 0 || row >= _leds.rows() || column < 0 || column >= _leds.columns()) {
7581
return;
7682
}
7783

78-
int idx = row*_leds.columns() + column;
84+
unsigned int idx = row*_leds.columns() + column;
7985

8086
_cells[idx] = cellStatus;
8187
}
8288

83-
CellUniverse::LifeState CellUniverse::getCellStatus(int row, int column) const {
89+
CellUniverse::LifeState CellUniverse::getCellStatus(unsigned int row, unsigned int column) const {
8490
// this causes the matrix to be a toroidal array
85-
int r = row < 0 ? row + _leds.rows() : ( row >= _leds.rows() ? row - _leds.rows() : row );
86-
int c = column < 0 ? column + _leds.columns() : ( column >= _leds.columns() ? column - _leds.columns() : column );
91+
unsigned int r = row < 0 ? row + _leds.rows() : ( row >= _leds.rows() ? row - _leds.rows() : row );
92+
unsigned int c = column < 0 ? column + _leds.columns() : ( column >= _leds.columns() ? column - _leds.columns() : column );
8793

8894
// double check just to be sure
8995
if (r < 0 || r >= _leds.rows() || c < 0 || c >= _leds.columns()) {
9096
return CellUniverse::DEAD;
9197
}
9298

93-
int idx = r*_leds.columns() + c;
99+
unsigned int idx = r*_leds.columns() + c;
94100

95101
return _cells[idx];
96102
}
97103

98-
bool CellUniverse::isAlive(int row, int column) const {
104+
bool CellUniverse::isAlive(unsigned int row, unsigned int column) const {
99105
return (this->getCellStatus(row, column) == CellUniverse::ALIVE || this->getCellStatus(row, column) == CellUniverse::BORN);
100106
}
101107

102-
int CellUniverse::countAliveNeighbors(int row, int column) const {
108+
int CellUniverse::countAliveNeighbors(unsigned int row, unsigned int column) const {
103109
int aliveCount = 0;
104110

105-
for (int x = column - 1; x <= column+1; x++) {
106-
for (int y = row - 1; y <= row + 1; y++ ) {
111+
for (unsigned int x = column - 1; x <= column+1; x++) {
112+
for (unsigned int y = row - 1; y <= row + 1; y++ ) {
107113
if (this->isAlive(y, x) && !(x == column && y == row)) {
108114
aliveCount++;
109115
}
@@ -114,9 +120,18 @@ int CellUniverse::countAliveNeighbors(int row, int column) const {
114120
}
115121

116122
void CellUniverse::action() {
123+
if (_isReseting) {
124+
delay(5000);
125+
this->createRandomState();
126+
_isReseting = false;
127+
return;
128+
}
129+
117130
_leds.startDrawing();
118-
for (int x = 0; x < _leds.columns(); x++) {
119-
for (int y = 0; y < _leds.rows(); y++ ) {
131+
bool isSame = true;
132+
133+
for (unsigned int x = 0; x < _leds.columns(); x++) {
134+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
120135
LifeState newState = DEAD;
121136
LifeState currentState = this->getCellStatus(y, x);
122137
int count = this->countAliveNeighbors(y, x);
@@ -139,8 +154,11 @@ void CellUniverse::action() {
139154
}
140155
break;
141156
}
142-
143-
int idx = y*_leds.columns() + x;
157+
if (currentState != newState) {
158+
isSame = false;
159+
}
160+
161+
unsigned int idx = y*_leds.columns() + x;
144162
_nextCells[idx] = newState;
145163

146164
RGBColorType cellColor = this->getColorForLifeState(newState);
@@ -149,13 +167,49 @@ void CellUniverse::action() {
149167
}
150168
_leds.stopDrawing();
151169

170+
// determine if life needs to start over
171+
if (isSame) {
172+
for (unsigned int x = 0; x < _leds.columns(); x++ ) {
173+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
174+
if (this->getCellStatus(y, x) == DEAD) {
175+
this->setCellStatus(y, x, GAME_OVER);
176+
}
177+
}
178+
}
179+
this->drawToScreen();
180+
_isReseting = true;
181+
}
182+
else {
152183
memcpy(_cells, _nextCells, _leds.rows()*_leds.columns()*sizeof(LifeState));
184+
}
185+
186+
}
187+
188+
void CellUniverse::createRandomState() {
189+
unsigned int numTotalCells = _leds.rows()*_leds.columns();
190+
191+
for (unsigned int x = 0; x < _leds.columns(); x++ ) {
192+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
193+
this->setCellStatus(y, x, CellUniverse::DEAD);
194+
}
195+
}
196+
197+
unsigned int countStartingCells = random(0.25*numTotalCells, 0.75*numTotalCells);
198+
199+
for (unsigned int i = 0; i < countStartingCells; i++ ) {
200+
int randomRow = random(0,_leds.rows());
201+
int randomColumn = random(0,_leds.columns());
202+
203+
this->setCellStatus(randomRow, randomColumn, CellUniverse::BORN);
204+
}
205+
206+
this->drawToScreen();
153207
}
154208

155209
void CellUniverse::drawToScreen() {
156210
_leds.startDrawing();
157-
for (int x = 0; x < _leds.columns(); x++) {
158-
for (int y = 0; y < _leds.rows(); y++ ) {
211+
for (unsigned int x = 0; x < _leds.columns(); x++) {
212+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
159213
LifeState currentState = this->getCellStatus(y, x);
160214
RGBColorType cellColor = this->getColorForLifeState(currentState);
161215
_leds.image().pixel(y, x) = cellColor;
@@ -176,6 +230,9 @@ RGBColorType CellUniverse::getColorForLifeState( LifeState state ) const {
176230
case DYING:
177231
cellColor = RED_COLOR;
178232
break;
233+
case GAME_OVER:
234+
cellColor = BLACK_COLOR;
235+
break;
179236
case DEAD:
180237
default:
181238
cellColor = BLACK_COLOR;

examples/conway-game-of-life-8x8/conway-game-of-life-8x8.ino

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public:
2828
const static LifeState BORN = 2;
2929
const static LifeState DYING = 3;
3030
const static LifeState DEAD = 0;
31+
const static LifeState GAME_OVER = 4;
3132

3233
private:
3334

@@ -37,7 +38,10 @@ private:
3738
LifeState* _cells;
3839
LifeState* _nextCells;
3940

41+
bool _isReseting;
42+
4043
RGBColorType getColorForLifeState( LifeState state ) const;
44+
4145

4246
protected:
4347
virtual void action();
@@ -49,11 +53,12 @@ public:
4953
unsigned long updateMicros
5054
);
5155

52-
void setCellStatus(int row, int column, LifeState cellStatus);
53-
LifeState getCellStatus(int row, int column) const;
54-
55-
bool isAlive(int row, int column) const;
56-
int countAliveNeighbors(int row, int column) const;
56+
void setCellStatus(unsigned int row, unsigned int column, LifeState cellStatus);
57+
LifeState getCellStatus(unsigned int row, unsigned int column) const;
58+
void createRandomState();
59+
60+
bool isAlive(unsigned int row, unsigned int column) const;
61+
int countAliveNeighbors(unsigned int row, unsigned int column) const;
5762

5863
void drawToScreen();
5964
};
@@ -64,46 +69,47 @@ CellUniverse::CellUniverse(
6469
) : TimerAction(updateMicros),
6570
_leds(matrix),
6671
_cells(new LifeState[matrix.rows()*matrix.columns()]),
67-
_nextCells(new LifeState[matrix.rows()*matrix.columns()])
72+
_nextCells(new LifeState[matrix.rows()*matrix.columns()]),
73+
_isReseting(false)
6874
{
6975
memset(_cells,DEAD,matrix.rows()*matrix.columns()*sizeof(LifeState));
7076
memset(_nextCells,DEAD,matrix.rows()*matrix.columns()*sizeof(LifeState));
7177
}
7278

73-
void CellUniverse::setCellStatus(int row, int column, LifeState cellStatus) {
79+
void CellUniverse::setCellStatus(unsigned int row, unsigned int column, LifeState cellStatus) {
7480
if (row < 0 || row >= _leds.rows() || column < 0 || column >= _leds.columns()) {
7581
return;
7682
}
7783

78-
int idx = row*_leds.columns() + column;
84+
unsigned int idx = row*_leds.columns() + column;
7985

8086
_cells[idx] = cellStatus;
8187
}
8288

83-
CellUniverse::LifeState CellUniverse::getCellStatus(int row, int column) const {
89+
CellUniverse::LifeState CellUniverse::getCellStatus(unsigned int row, unsigned int column) const {
8490
// this causes the matrix to be a toroidal array
85-
int r = row < 0 ? row + _leds.rows() : ( row >= _leds.rows() ? row - _leds.rows() : row );
86-
int c = column < 0 ? column + _leds.columns() : ( column >= _leds.columns() ? column - _leds.columns() : column );
91+
unsigned int r = row < 0 ? row + _leds.rows() : ( row >= _leds.rows() ? row - _leds.rows() : row );
92+
unsigned int c = column < 0 ? column + _leds.columns() : ( column >= _leds.columns() ? column - _leds.columns() : column );
8793

8894
// double check just to be sure
8995
if (r < 0 || r >= _leds.rows() || c < 0 || c >= _leds.columns()) {
9096
return CellUniverse::DEAD;
9197
}
9298

93-
int idx = r*_leds.columns() + c;
99+
unsigned int idx = r*_leds.columns() + c;
94100

95101
return _cells[idx];
96102
}
97103

98-
bool CellUniverse::isAlive(int row, int column) const {
104+
bool CellUniverse::isAlive(unsigned int row, unsigned int column) const {
99105
return (this->getCellStatus(row, column) == CellUniverse::ALIVE || this->getCellStatus(row, column) == CellUniverse::BORN);
100106
}
101107

102-
int CellUniverse::countAliveNeighbors(int row, int column) const {
108+
int CellUniverse::countAliveNeighbors(unsigned int row, unsigned int column) const {
103109
int aliveCount = 0;
104110

105-
for (int x = column - 1; x <= column+1; x++) {
106-
for (int y = row - 1; y <= row + 1; y++ ) {
111+
for (unsigned int x = column - 1; x <= column+1; x++) {
112+
for (unsigned int y = row - 1; y <= row + 1; y++ ) {
107113
if (this->isAlive(y, x) && !(x == column && y == row)) {
108114
aliveCount++;
109115
}
@@ -114,9 +120,18 @@ int CellUniverse::countAliveNeighbors(int row, int column) const {
114120
}
115121

116122
void CellUniverse::action() {
123+
if (_isReseting) {
124+
delay(5000);
125+
this->createRandomState();
126+
_isReseting = false;
127+
return;
128+
}
129+
117130
_leds.startDrawing();
118-
for (int x = 0; x < _leds.columns(); x++) {
119-
for (int y = 0; y < _leds.rows(); y++ ) {
131+
bool isSame = true;
132+
133+
for (unsigned int x = 0; x < _leds.columns(); x++) {
134+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
120135
LifeState newState = DEAD;
121136
LifeState currentState = this->getCellStatus(y, x);
122137
int count = this->countAliveNeighbors(y, x);
@@ -139,8 +154,11 @@ void CellUniverse::action() {
139154
}
140155
break;
141156
}
142-
143-
int idx = y*_leds.columns() + x;
157+
if (currentState != newState) {
158+
isSame = false;
159+
}
160+
161+
unsigned int idx = y*_leds.columns() + x;
144162
_nextCells[idx] = newState;
145163

146164
RGBColorType cellColor = this->getColorForLifeState(newState);
@@ -149,13 +167,49 @@ void CellUniverse::action() {
149167
}
150168
_leds.stopDrawing();
151169

170+
// determine if life needs to start over
171+
if (isSame) {
172+
for (unsigned int x = 0; x < _leds.columns(); x++ ) {
173+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
174+
if (this->getCellStatus(y, x) == DEAD) {
175+
this->setCellStatus(y, x, GAME_OVER);
176+
}
177+
}
178+
}
179+
this->drawToScreen();
180+
_isReseting = true;
181+
}
182+
else {
152183
memcpy(_cells, _nextCells, _leds.rows()*_leds.columns()*sizeof(LifeState));
184+
}
185+
186+
}
187+
188+
void CellUniverse::createRandomState() {
189+
unsigned int numTotalCells = _leds.rows()*_leds.columns();
190+
191+
for (unsigned int x = 0; x < _leds.columns(); x++ ) {
192+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
193+
this->setCellStatus(y, x, CellUniverse::DEAD);
194+
}
195+
}
196+
197+
unsigned int countStartingCells = random(0.25*numTotalCells, 0.75*numTotalCells);
198+
199+
for (unsigned int i = 0; i < countStartingCells; i++ ) {
200+
int randomRow = random(0,_leds.rows());
201+
int randomColumn = random(0,_leds.columns());
202+
203+
this->setCellStatus(randomRow, randomColumn, CellUniverse::BORN);
204+
}
205+
206+
this->drawToScreen();
153207
}
154208

155209
void CellUniverse::drawToScreen() {
156210
_leds.startDrawing();
157-
for (int x = 0; x < _leds.columns(); x++) {
158-
for (int y = 0; y < _leds.rows(); y++ ) {
211+
for (unsigned int x = 0; x < _leds.columns(); x++) {
212+
for (unsigned int y = 0; y < _leds.rows(); y++ ) {
159213
LifeState currentState = this->getCellStatus(y, x);
160214
RGBColorType cellColor = this->getColorForLifeState(currentState);
161215
_leds.image().pixel(y, x) = cellColor;
@@ -176,6 +230,9 @@ RGBColorType CellUniverse::getColorForLifeState( LifeState state ) const {
176230
case DYING:
177231
cellColor = RED_COLOR;
178232
break;
233+
case GAME_OVER:
234+
cellColor = BLACK_COLOR;
235+
break;
179236
case DEAD:
180237
default:
181238
cellColor = BLACK_COLOR;

0 commit comments

Comments
 (0)