summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/rocklife.c489
2 files changed, 490 insertions, 0 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 51306ea011..14f4d552d1 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -105,6 +105,7 @@ spacerocks.c
105rockpaint.c 105rockpaint.c
106#endif 106#endif
107 107
108rocklife.c
108#endif /* HAVE_LCD_BITMAP */ 109#endif /* HAVE_LCD_BITMAP */
109 110
110#ifdef HAVE_LCD_CHARCELLS /* Player model only */ 111#ifdef HAVE_LCD_CHARCELLS /* Player model only */
diff --git a/apps/plugins/rocklife.c b/apps/plugins/rocklife.c
new file mode 100644
index 0000000000..7424c67b4b
--- /dev/null
+++ b/apps/plugins/rocklife.c
@@ -0,0 +1,489 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: helloworld.c 8349 2006-01-15 18:20:18Z amiconn $
9 *
10 * Copyright (C) 2007 Matthias Wientapper
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/*
21 * This is an implementatino of Conway's Game of Life
22 *
23 * from http://en.wikipedia.org/wiki/Conway's_Game_of_Life:
24 *
25 * Rules
26 *
27 * The universe of the Game of Life is an infinite two-dimensional
28 * orthogonal grid of square cells, each of which is in one of two
29 * possible states, live or dead. Every cell interacts with its eight
30 * neighbours, which are the cells that are directly horizontally,
31 * vertically, or diagonally adjacent. At each step in time, the
32 * following transitions occur:
33 *
34 * 1. Any live cell with fewer than two live neighbours dies, as if by
35 * loneliness.
36 *
37 * 2. Any live cell with more than three live neighbours dies, as if
38 * by overcrowding.
39 *
40 * 3. Any live cell with two or three live neighbours lives,
41 * unchanged, to the next generation.
42 *
43 * 4. Any dead cell with exactly three live neighbours comes to life.
44 *
45 * The initial pattern constitutes the first generation of the
46 * system. The second generation is created by applying the above
47 * rules simultaneously to every cell in the first generation --
48 * births and deaths happen simultaneously, and the discrete moment at
49 * which this happens is sometimes called a tick. (In other words,
50 * each generation is based entirely on the one before.) The rules
51 * continue to be applied repeatedly to create further generations.
52 *
53 *
54 *
55 * TODO:
56 * - nicer colours for pixels with respect to age
57 * - editor for start patterns
58 * - probably tons of speed-up opportunities
59 */
60
61#include "plugin.h"
62#include "pluginlib_actions.h"
63
64PLUGIN_HEADER
65
66#define ROCKLIFE_PLAY_PAUSE PLA_FIRE
67#define ROCKLIFE_INIT PLA_DOWN
68#define ROCKLIFE_NEXT PLA_RIGHT
69#define ROCKLIFE_NEXT_REP PLA_RIGHT_REPEAT
70#define ROCKLIFE_QUIT PLA_QUIT
71#define ROCKLIFE_STATUS PLA_LEFT
72
73#define PATTERN_RANDOM 0
74#define PATTERN_GROWTH_1 1
75#define PATTERN_GROWTH_2 2
76#define PATTERN_ACORN 3
77#define PATTERN_GLIDER_GUN 4 /* not yet implemented */
78
79static struct plugin_api* rb;
80const struct button_mapping *plugin_contexts[]
81= {generic_directions, generic_actions};
82
83
84unsigned char grid_a[LCD_WIDTH][LCD_HEIGHT];
85unsigned char grid_b[LCD_WIDTH][LCD_HEIGHT];
86int generation = 0;
87int population = 0;
88int status_line = 0;
89char buf[30];
90
91static inline void set_cell(int x, int y, char *pgrid){
92 pgrid[x+y*LCD_WIDTH]=1;
93}
94
95/* clear grid */
96void init_grid(char *pgrid){
97 int x, y;
98
99 for(y=0; y<LCD_HEIGHT; y++){
100 for(x=0; x<LCD_WIDTH; x++){
101 pgrid[x+y*LCD_WIDTH] = 0;
102 }
103 }
104}
105
106/* fill grid with initial pattern */
107static void setup_grid(char *pgrid, int pattern){
108 int n, max;
109 int xmid, ymid;
110
111 max = LCD_HEIGHT*LCD_WIDTH;
112
113 switch(pattern){
114 case PATTERN_RANDOM:
115 rb->splash(HZ, "Random");
116#if 0 /* two oscilators, debug pattern */
117 set_cell( 0, 1 , pgrid);
118 set_cell( 1, 1 , pgrid);
119 set_cell( 2, 1 , pgrid);
120
121 set_cell( 6, 7 , pgrid);
122 set_cell( 7, 7 , pgrid);
123 set_cell( 8, 7 , pgrid);
124#endif
125
126 /* fill screen randomly */
127 for(n=0; n<(max>>2); n++)
128 pgrid[rb->rand()%max] = 1;
129
130 break;
131
132 case PATTERN_GROWTH_1:
133 rb->splash(HZ, "Growth");
134 xmid = (LCD_WIDTH>>1) - 2;
135 ymid = (LCD_HEIGHT>>1) - 2;
136 set_cell(xmid + 6, ymid + 0 , pgrid);
137 set_cell(xmid + 4, ymid + 1 , pgrid);
138 set_cell(xmid + 6, ymid + 1 , pgrid);
139 set_cell(xmid + 7, ymid + 1 , pgrid);
140 set_cell(xmid + 4, ymid + 2 , pgrid);
141 set_cell(xmid + 6, ymid + 2 , pgrid);
142 set_cell(xmid + 4, ymid + 3 , pgrid);
143 set_cell(xmid + 2, ymid + 4 , pgrid);
144 set_cell(xmid + 0, ymid + 5 , pgrid);
145 set_cell(xmid + 2, ymid + 5 , pgrid);
146 break;
147 case PATTERN_ACORN:
148 rb->splash(HZ, "Acorn");
149 xmid = (LCD_WIDTH>>1) - 3;
150 ymid = (LCD_HEIGHT>>1) - 1;
151 set_cell(xmid + 1, ymid + 0 , pgrid);
152 set_cell(xmid + 3, ymid + 1 , pgrid);
153 set_cell(xmid + 0, ymid + 2 , pgrid);
154 set_cell(xmid + 1, ymid + 2 , pgrid);
155 set_cell(xmid + 4, ymid + 2 , pgrid);
156 set_cell(xmid + 5, ymid + 2 , pgrid);
157 set_cell(xmid + 6, ymid + 2 , pgrid);
158 break;
159 case PATTERN_GROWTH_2:
160 rb->splash(HZ, "Growth 2");
161 xmid = (LCD_WIDTH>>1) - 4;
162 ymid = (LCD_HEIGHT>>1) - 1;
163 set_cell(xmid + 0, ymid + 0 , pgrid);
164 set_cell(xmid + 1, ymid + 0 , pgrid);
165 set_cell(xmid + 2, ymid + 0 , pgrid);
166 set_cell(xmid + 4, ymid + 0 , pgrid);
167 set_cell(xmid + 0, ymid + 1 , pgrid);
168 set_cell(xmid + 3, ymid + 2 , pgrid);
169 set_cell(xmid + 4, ymid + 2 , pgrid);
170 set_cell(xmid + 1, ymid + 3 , pgrid);
171 set_cell(xmid + 2, ymid + 3 , pgrid);
172 set_cell(xmid + 4, ymid + 3 , pgrid);
173 set_cell(xmid + 0, ymid + 4 , pgrid);
174 set_cell(xmid + 2, ymid + 4 , pgrid);
175 set_cell(xmid + 4, ymid + 4 , pgrid);
176 break;
177 case PATTERN_GLIDER_GUN:
178 rb->splash(HZ, "Glider Gun");
179 set_cell( 24, 0, pgrid);
180 set_cell( 22, 1, pgrid);
181 set_cell( 24, 1, pgrid);
182 set_cell( 12, 2, pgrid);
183 set_cell( 13, 2, pgrid);
184 set_cell( 20, 2, pgrid);
185 set_cell( 21, 2, pgrid);
186 set_cell( 34, 2, pgrid);
187 set_cell( 35, 2, pgrid);
188 set_cell( 11, 3, pgrid);
189 set_cell( 15, 3, pgrid);
190 set_cell( 20, 3, pgrid);
191 set_cell( 21, 3, pgrid);
192 set_cell( 34, 3, pgrid);
193 set_cell( 35, 3, pgrid);
194 set_cell( 0, 4, pgrid);
195 set_cell( 1, 4, pgrid);
196 set_cell( 10, 4, pgrid);
197 set_cell( 16, 4, pgrid);
198 set_cell( 20, 4, pgrid);
199 set_cell( 21, 4, pgrid);
200 set_cell( 0, 5, pgrid);
201 set_cell( 1, 5, pgrid);
202 set_cell( 10, 5, pgrid);
203 set_cell( 14, 5, pgrid);
204 set_cell( 16, 5, pgrid);
205 set_cell( 17, 5, pgrid);
206 set_cell( 22, 5, pgrid);
207 set_cell( 24, 5, pgrid);
208 set_cell( 10, 6, pgrid);
209 set_cell( 16, 6, pgrid);
210 set_cell( 24, 6, pgrid);
211 set_cell( 11, 7, pgrid);
212 set_cell( 15, 7, pgrid);
213 set_cell( 12, 8, pgrid);
214 set_cell( 13, 8, pgrid);
215 break;
216 }
217}
218
219/* display grid */
220static void show_grid(char *pgrid){
221 int x, y;
222 int m;
223 unsigned char age;
224
225 rb->lcd_clear_display();
226 for(y=0; y<LCD_HEIGHT; y++){
227 for(x=0; x<LCD_WIDTH; x++){
228 m = y*LCD_WIDTH+x;
229 age = pgrid[m];
230 if(age){
231#if LCD_DEPTH >= 16
232 rb->lcd_set_foreground( LCD_RGBPACK( age, age, age ));
233#elif LCD_DEPTH == 2
234 rb->lcd_set_foreground(age>>7);
235#endif
236 rb->lcd_drawpixel(x, y);
237 }
238 }
239 }
240 if(status_line){
241 rb->snprintf(buf, sizeof(buf), "g:%d p:%d", generation, population);
242#if LCD_DEPTH > 1
243 rb->lcd_set_foreground( LCD_BLACK );
244#endif
245 rb->lcd_puts(0, 0, buf);
246 }
247 rb->lcd_update();
248}
249
250
251/* check state of cell depending on the number of neighbours */
252static inline int check_cell(unsigned char *n){
253 int sum;
254 int empty_cells = 0;
255 unsigned char live = 0;
256
257 /* count empty neighbour cells */
258 if(n[0]==0) empty_cells++;
259 if(n[1]==0) empty_cells++;
260 if(n[2]==0) empty_cells++;
261 if(n[3]==0) empty_cells++;
262 if(n[5]==0) empty_cells++;
263 if(n[6]==0) empty_cells++;
264 if(n[7]==0) empty_cells++;
265 if(n[8]==0) empty_cells++;
266
267 /* now we build the number of non-zero neighbours :-P */
268 sum = 8 - empty_cells;
269
270 /* 1st and 2nd rule*/
271 if (n[4] && (sum<2 || sum>3))
272 live = false;
273
274 /* 3rd rule */
275 if (n[4] && (sum==2 || sum==3))
276 live = true;
277
278 /* 4rd rule */
279 if (!n[4] && sum==3)
280 live = true;
281
282 return live;
283}
284
285/* Calculate the next generation of cells
286 *
287 * The borders of the grid are connected to their opposite sides.
288 *
289 *
290 * To avoid multiplications while accessing data in the 2-d grid
291 * (pgrid) we try to re-use previously accessed neighbourhood
292 * information which is stored in an 3x3 array.
293 *
294 */
295static void next_generation(char *pgrid, char *pnext_grid){
296 int x, y;
297 unsigned char cell;
298 int age;
299 int m;
300 unsigned char n[9];
301
302 rb->memset(n, 0, sizeof(n));
303
304 /*
305 * cell is (4) with 8 neighbours
306 *
307 * 0|1|2
308 * -----
309 * 3|4|5
310 * -----
311 * 6|7|8
312 */
313
314 population = 0;
315
316 /* go through the grid */
317 for(y=0; y<LCD_HEIGHT; y++){
318 for(x=0; x<LCD_WIDTH; x++){
319 if(y==0 && x==0){
320 /* first cell in first row, we have to load all neighbours */
321 n[0] = pgrid[((x+LCD_WIDTH-1)%LCD_WIDTH)+((y+LCD_HEIGHT-1)%LCD_HEIGHT)*LCD_WIDTH];
322 n[1] = pgrid[((x )%LCD_WIDTH)+((y+LCD_HEIGHT-1)%LCD_HEIGHT)*LCD_WIDTH];
323 n[2] = pgrid[((x +1)%LCD_WIDTH)+((y+LCD_HEIGHT-1)%LCD_HEIGHT)*LCD_WIDTH];
324 n[3] = pgrid[((x+LCD_WIDTH-1)%LCD_WIDTH)+((y )%LCD_HEIGHT)*LCD_WIDTH];
325 n[5] = pgrid[((x +1)%LCD_WIDTH)+((y )%LCD_HEIGHT)*LCD_WIDTH];
326 n[6] = pgrid[((x+LCD_WIDTH-1)%LCD_WIDTH)+((y +1)%LCD_HEIGHT)*LCD_WIDTH];
327 n[7] = pgrid[((x )%LCD_WIDTH)+((y +1)%LCD_HEIGHT)*LCD_WIDTH];
328 n[8] = pgrid[((x +1)%LCD_WIDTH)+((y +1)%LCD_HEIGHT)*LCD_WIDTH];
329 } else {
330 if(x==0){
331 /* beginning of a row, copy what we know about our predecessor,
332 0, 1, 3 are known, 2, 5, 6, 7, 8 have to be loaded
333 */
334 n[0] = n[4];
335 n[1] = n[5];
336 n[2] = pgrid[((x +1)%LCD_WIDTH)+((y+LCD_HEIGHT-1)%LCD_HEIGHT)*LCD_WIDTH];
337 n[3] = n[7];
338 n[5] = pgrid[((x +1)%LCD_WIDTH)+((y )%LCD_HEIGHT)*LCD_WIDTH];
339 n[6] = pgrid[((x+LCD_WIDTH-1)%LCD_WIDTH)+((y +1)%LCD_HEIGHT)*LCD_WIDTH];
340 n[7] = pgrid[((x )%LCD_WIDTH)+((y +1)%LCD_HEIGHT)*LCD_WIDTH];
341 n[8] = pgrid[((x +1)%LCD_WIDTH)+((y +1)%LCD_HEIGHT)*LCD_WIDTH];
342 } else {
343 /* we are moving right in a row,
344 * copy what we know about the neighbours on our left side,
345 * 2, 5, 8 have to be loaded
346 */
347 n[0] = n[1];
348 n[1] = n[2];
349 n[2] = pgrid[((x +1)%LCD_WIDTH)+((y+LCD_HEIGHT-1)%LCD_HEIGHT)*LCD_WIDTH];
350 n[3] = n[4];
351 n[5] = pgrid[((x +1)%LCD_WIDTH)+((y )%LCD_HEIGHT)*LCD_WIDTH];
352 n[6] = n[7];
353 n[7] = n[8];
354 n[8] = pgrid[((x +1)%LCD_WIDTH)+((y +1)%LCD_HEIGHT)*LCD_WIDTH];
355 }
356 }
357
358 m = x+y*LCD_WIDTH;
359
360 /* how old is our cell? */
361 n[4] = pgrid[m];
362 age = n[4];
363
364 /* calculate the cell based on given neighbour information */
365 cell = check_cell(n);
366
367 /* is the actual cell alive? */
368 if(cell){
369 population++;
370 /* prevent overflow */
371 if(age>252){
372 pnext_grid[m] = 252;
373 } else {
374 pnext_grid[m] = age + 1;
375 }
376 }
377 else
378 pnext_grid[m] = 0;
379#if 0
380 DEBUGF("x=%d,y=%d\n", x, y);
381 DEBUGF("cell: %d\n", cell);
382 DEBUGF("%d %d %d\n", n[0],n[1],n[2]);
383 DEBUGF("%d %d %d\n", n[3],n[4],n[5]);
384 DEBUGF("%d %d %d\n", n[6],n[7],n[8]);
385 DEBUGF("----------------\n");
386#endif
387 }
388 }
389 generation++;
390}
391
392/**********************************/
393/* this is the plugin entry point */
394/**********************************/
395enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
396{
397 int button = 0;
398 int quit = 0;
399 int stop = 0;
400 int pattern = 0;
401 char *pgrid;
402 char *pnext_grid;
403 char *ptemp;
404
405 (void)parameter;
406 rb = api;
407
408 rb->backlight_set_timeout(1);
409#if LCD_DEPTH > 1
410 rb->lcd_set_backdrop(NULL);
411 rb->lcd_set_background(LCD_DEFAULT_BG);
412#endif
413
414 /* link pointers to grids */
415 pgrid = (char *)grid_a;
416 pnext_grid = (char *)grid_b;
417
418 init_grid(pgrid);
419 setup_grid(pgrid, pattern++);
420 show_grid(pgrid);
421
422 while(!quit) {
423 button = pluginlib_getaction(rb, TIMEOUT_BLOCK, plugin_contexts, 2);
424 switch(button) {
425 case ROCKLIFE_NEXT:
426 case ROCKLIFE_NEXT_REP:
427 /* calculate next generation */
428 next_generation(pgrid, pnext_grid);
429 /* swap buffers, grid is the new generation */
430 ptemp = pgrid;
431 pgrid = pnext_grid;
432 pnext_grid = ptemp;
433 /* show new generation */
434 show_grid(pgrid);
435 break;
436 case ROCKLIFE_PLAY_PAUSE:
437 stop = 0;
438 while(!stop){
439 /* calculate next generation */
440 next_generation(pgrid, pnext_grid);
441 /* swap buffers, grid is the new generation */
442 ptemp = pgrid;
443 pgrid = pnext_grid;
444 pnext_grid = ptemp;
445 /* show new generation */
446 rb->yield();
447 show_grid(pgrid);
448 button = pluginlib_getaction(rb, 0, plugin_contexts, 2);
449 switch(button) {
450 case ROCKLIFE_PLAY_PAUSE:
451 case ROCKLIFE_QUIT:
452 stop = 1;
453 break;
454 default:
455 break;
456 }
457 rb->yield();
458 }
459 break;
460 case ROCKLIFE_INIT:
461 init_grid(pgrid);
462 setup_grid(pgrid, pattern);
463 show_grid(pgrid);
464 pattern++;
465 pattern%=5;
466 break;
467 case ROCKLIFE_STATUS:
468 status_line = !status_line;
469 show_grid(pgrid);
470 break;
471 case ROCKLIFE_QUIT:
472 /* quit plugin */
473 quit=true;
474 return PLUGIN_OK;
475 break;
476 default:
477 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
478 return PLUGIN_USB_CONNECTED;
479 }
480 break;
481 }
482 rb->yield();
483 }
484
485 rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
486 return PLUGIN_OK;
487}
488
489