test/lpfklife.c

Wed, 03 Sep 2008 17:15:47 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 03 Sep 2008 17:15:47 +0100
changeset 10
f76c929f65e6
parent 9
1519a9a8d924
permissions
-rw-r--r--

added "naive" and "wraparound" algorithms to lpfklife

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