Wed, 03 Sep 2008 17:15:47 +0100
added "naive" and "wraparound" algorithms to lpfklife
1 // lpfklife: Conway's Game of Life for the LPFK
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "liblpfk.h"
8 #define LIFE_ALGORITHM_NAIVE
10 /************************
11 * copied from http://linux-sxs.org/programming/kbhit.html
12 */
14 #include <termios.h>
15 #include <unistd.h> // for read()
17 static struct termios initial_settings, new_settings;
18 static int peek_character = -1;
20 void init_keyboard()
21 {
22 tcgetattr(0,&initial_settings);
23 new_settings = initial_settings;
24 new_settings.c_lflag &= ~ICANON;
25 new_settings.c_lflag &= ~ECHO;
26 new_settings.c_lflag &= ~ISIG;
27 new_settings.c_cc[VMIN] = 1;
28 new_settings.c_cc[VTIME] = 0;
29 tcsetattr(0, TCSANOW, &new_settings);
30 }
32 void close_keyboard()
33 {
34 tcsetattr(0, TCSANOW, &initial_settings);
35 }
37 int kbhit()
38 {
39 unsigned char ch;
40 int nread;
42 if (peek_character != -1) return 1;
43 new_settings.c_cc[VMIN]=0;
44 tcsetattr(0, TCSANOW, &new_settings);
45 nread = read(0,&ch,1);
46 new_settings.c_cc[VMIN]=1;
47 tcsetattr(0, TCSANOW, &new_settings);
48 if(nread == 1)
49 {
50 peek_character = ch;
51 return 1;
52 }
53 return 0;
54 }
56 int readch()
57 {
58 char ch;
60 if(peek_character != -1)
61 {
62 ch = peek_character;
63 peek_character = -1;
64 return ch;
65 }
66 read(0,&ch,1);
67 return ch;
68 }
70 /***********************/
72 int main(void)
73 {
74 int i, nei, x, y;
75 bool old_gamegrid[6][6];
76 bool gamegrid[6][6];
77 bool steadyState = false;
78 unsigned long iteration = 0;
79 LPFK_CTX ctx;
81 // initialisation
82 memset(&gamegrid, 0, sizeof(gamegrid));
84 init_keyboard();
85 atexit(close_keyboard);
87 // open lpfk port
88 if ((i = lpfk_open(&ctx, "/dev/ttyUSB0")) != LPFK_E_OK) {
89 // error opening lpfk
90 printf("Error opening LPFK: code %d\n", i);
91 return -1;
92 }
94 lpfk_enable(&ctx, true);
96 // allow user to set up their game grid
97 printf("Press the keys on the LPFK to set up the game grid, then press Enter to start the simulation.\n");
98 while (!kbhit()) {
99 i = lpfk_read(&ctx);
101 if (i >= 0) {
102 #ifdef DEBUG
103 printf("key %d\n", i);
104 #endif
105 // Key down, toggle the LED
106 lpfk_set_led(&ctx, i, !lpfk_get_led(&ctx, i));
108 // update game grid
109 if ((i >= 0) && (i < 4)) {
110 gamegrid[0][i+1] = !gamegrid[0][i+1]; // +1 because 1st row is 4-column
111 } else if ((i >= 4) && (i < 10)) {
112 gamegrid[1][i-4] = !gamegrid[1][i-4];
113 } else if ((i >= 10) && (i < 16)) {
114 gamegrid[2][i-10] = !gamegrid[2][i-10];
115 } else if ((i >= 16) && (i < 22)) {
116 gamegrid[3][i-16] = !gamegrid[3][i-16];
117 } else if ((i >= 22) && (i < 28)) {
118 gamegrid[4][i-22] = !gamegrid[4][i-22];
119 } else {
120 gamegrid[5][(i-28)+1] = !gamegrid[5][(i-28)+1]; // +1 because last row is 4-column
121 }
122 }
123 }
124 // flush keyboard buffer
125 while (kbhit()) readch();
127 // disable LPFK keys
128 lpfk_enable(&ctx, false);
130 #ifdef DEBUG
131 // print the game grid: debug only
132 printf("GAME GRID: [iter %lu]\n", iteration);
133 for (y=0; y<6; y++) {
134 for (x=0; x<6; x++) {
135 if (gamegrid[y][x]) printf("* "); else printf(". ");
136 }
137 printf("\n");
138 }
139 printf("\n");
140 #endif
142 printf("Press ENTER to stop the simulation.\n");
144 // run game
145 while (!kbhit() && !steadyState) {
146 // increase iteration counter
147 iteration++;
149 // save current game grid
150 memcpy(&old_gamegrid, &gamegrid, sizeof(gamegrid));
152 // loop over game grid
153 for (y=0; y<6; y++) {
154 for (x=0; x<6; x++) {
155 nei = 0;
157 // count neighbours
158 #ifdef LIFE_ALGORITHM_NAIVE
159 /// NAIVE ALGORITHM
160 // Assumes every cell outside the grid is dead.
161 if (y > 0) {
162 if (x > 0) { if (old_gamegrid[y-1][x-1]) nei++; }
163 if (old_gamegrid[y-1][x]) nei++;
164 if (x < 5) { if (old_gamegrid[y-1][x+1]) nei++; }
165 }
167 if (x > 0) { if (old_gamegrid[y][x-1]) nei++; }
168 // old_gamegrid[x][y] is us!
169 if (x < 5) { if (old_gamegrid[y][x+1]) nei++; }
171 if (y < 5) {
172 if (x > 0) { if (old_gamegrid[y+1][x-1]) nei++; }
173 if (old_gamegrid[y+1][x]) nei++;
174 if (x < 5) { if (old_gamegrid[y+1][x+1]) nei++; }
175 }
176 #else
177 /// WRAPPING ALGORITHM
178 // Accesses off of one side of the grid are wrapped to the
179 // opposite side.
180 //
181 // WARNING: this algorithm has NOT been extensively tested,
182 // and completely screws up the glider test.
183 //
184 // TODO: test and debug:
185 if (y > 0) {
186 if (x > 0) { if (old_gamegrid[y-1][x-1]) nei++; }
187 else { if (old_gamegrid[y-1][5]) nei++; }
189 if (old_gamegrid[y-1][x]) nei++;
191 if (x < 5) { if (old_gamegrid[y-1][x+1]) nei++; }
192 else { if (old_gamegrid[y-1][0]) nei++; }
193 } else {
194 if (x > 0) { if (old_gamegrid[5][x-1]) nei++; }
195 else { if (old_gamegrid[5][5]) nei++; }
197 if (old_gamegrid[y-1][x]) nei++;
199 if (x < 5) { if (old_gamegrid[5][x+1]) nei++; }
200 else { if (old_gamegrid[5][0]) nei++; }
201 }
204 if (x > 0) { if (old_gamegrid[y][x-1]) nei++; }
205 else { if (old_gamegrid[y][5]) nei++; }
207 // old_gamegrid[x][y] is us!
209 if (x < 5) { if (old_gamegrid[y][x+1]) nei++; }
210 else { if (old_gamegrid[y][0]) nei++; }
213 if (y < 5) {
214 if (x > 0) { if (old_gamegrid[y+1][x-1]) nei++; }
215 else { if (old_gamegrid[y+1][5]) nei++; } ///
217 if (old_gamegrid[y+1][x]) nei++;
219 if (x < 5) { if (old_gamegrid[y+1][x+1]) nei++; }
220 else { if (old_gamegrid[y+1][0]) nei++; } ///
221 } else {
222 if (x > 0) { if (old_gamegrid[y+1][x-1]) nei++; }
223 else { if (old_gamegrid[y+1][5]) nei++; } ///
225 if (old_gamegrid[y+1][x]) nei++;
227 if (x < 5) { if (old_gamegrid[y+1][x+1]) nei++; }
228 else { if (old_gamegrid[y-1][0]) nei++; } ///
229 }
230 #endif
232 // so what happens to our cell?
233 if (old_gamegrid[y][x]) {
234 // --- rules for live cells ---
235 if ((nei < 2) || (nei > 3)) {
236 // <2 neighbours, death due to loneliness.
237 // or >3 neighbours, death due to overcrowding.
238 gamegrid[y][x] = false;
239 }
240 } else {
241 // --- rules for dead cells ---
242 if (nei == 3) {
243 // any dead cell with three neighbours comes to life
244 gamegrid[y][x] = true;
245 }
246 }
247 }
248 }
250 if (memcmp(&gamegrid, &old_gamegrid, sizeof(gamegrid)) == 0) {
251 steadyState = true;
252 }
254 #ifdef DEBUG
255 printf("GAME GRID: [iter %lu]\n", iteration);
256 for (y=0; y<6; y++) {
257 for (x=0; x<6; x++) {
258 if (gamegrid[y][x]) printf("* "); else printf(". ");
259 }
260 printf("\n");
261 }
262 printf("\n");
263 #endif
265 // now update the LPFK from the game grid
266 for (i=0; i<32; i++) {
267 if (i < 4) lpfk_set_led_cached(&ctx, i, gamegrid[0][i+1]);
268 else if (i < 10) lpfk_set_led_cached(&ctx, i, gamegrid[1][i-4]);
269 else if (i < 16) lpfk_set_led_cached(&ctx, i, gamegrid[2][i-10]);
270 else if (i < 22) lpfk_set_led_cached(&ctx, i, gamegrid[3][i-16]);
271 else if (i < 28) lpfk_set_led_cached(&ctx, i, gamegrid[4][i-22]);
272 else lpfk_set_led_cached(&ctx, i, gamegrid[5][(i-28)+1]);
273 }
275 // flush updates to the LPFK
276 lpfk_update_leds(&ctx);
278 // make sure updates aren't too fast
279 sleep(1);
280 }
282 if (steadyState) {
283 printf("Steady state reached.\n");
284 }
285 printf("LPFK Life ran for %lu iterations.\n", iteration);
287 // close the LPFK
288 lpfk_close(&ctx);
289 }