1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Matthias Wientapper
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 * This is an implementatino of Conway's Game of Life
25 * from http://en.wikipedia.org/wiki/Conway's_Game_of_Life:
29 * The universe of the Game of Life is an infinite two-dimensional
30 * orthogonal grid of square cells, each of which is in one of two
31 * possible states, live or dead. Every cell interacts with its eight
32 * neighbours, which are the cells that are directly horizontally,
33 * vertically, or diagonally adjacent. At each step in time, the
34 * following transitions occur:
36 * 1. Any live cell with fewer than two live neighbours dies, as if by
39 * 2. Any live cell with more than three live neighbours dies, as if
42 * 3. Any live cell with two or three live neighbours lives,
43 * unchanged, to the next generation.
45 * 4. Any dead cell with exactly three live neighbours comes to life.
47 * The initial pattern constitutes the first generation of the
48 * system. The second generation is created by applying the above
49 * rules simultaneously to every cell in the first generation --
50 * births and deaths happen simultaneously, and the discrete moment at
51 * which this happens is sometimes called a tick. (In other words,
52 * each generation is based entirely on the one before.) The rules
53 * continue to be applied repeatedly to create further generations.
58 * - nicer colours for pixels with respect to age
59 * - editor for start patterns
60 * - probably tons of speed-up opportunities
64 #include "lib/pluginlib_actions.h"
65 #include "lib/helper.h"
69 #define ROCKLIFE_PLAY_PAUSE PLA_SELECT
70 #define ROCKLIFE_INIT PLA_DOWN
71 #define ROCKLIFE_NEXT PLA_RIGHT
72 #define ROCKLIFE_NEXT_REP PLA_RIGHT_REPEAT
73 #define ROCKLIFE_QUIT PLA_CANCEL
74 #define ROCKLIFE_STATUS PLA_LEFT
76 #define PATTERN_RANDOM 0
77 #define PATTERN_GROWTH_1 1
78 #define PATTERN_GROWTH_2 2
79 #define PATTERN_ACORN 3
80 #define PATTERN_GLIDER_GUN 4
82 const struct button_mapping
*plugin_contexts
[]
85 #define GRID_W LCD_WIDTH
86 #define GRID_H LCD_HEIGHT
88 unsigned char grid_a
[GRID_W
][GRID_H
];
89 unsigned char grid_b
[GRID_W
][GRID_H
];
95 static inline bool is_valid_cell(int x
, int y
) {
96 return (x
>= 0 && x
< GRID_W
97 && y
>= 0 && y
< GRID_H
);
100 static inline void set_cell_age(int x
, int y
, unsigned char age
, char *pgrid
) {
101 pgrid
[x
+y
*GRID_W
] = age
;
104 static inline void set_cell(int x
, int y
, char *pgrid
) {
105 set_cell_age(x
, y
, 1, pgrid
);
108 static inline unsigned char get_cell(int x
, int y
, char *pgrid
) {
111 else if (x
>= GRID_W
)
116 else if (y
>= GRID_H
)
119 return pgrid
[x
+y
*GRID_W
];
123 void init_grid(char *pgrid
){
124 memset(pgrid
, 0, GRID_W
* GRID_H
);
127 /*fill grid with pattern from file (viewer mode)*/
128 static bool load_cellfile(const char *file
, char *pgrid
){
130 fd
= rb
->open(file
, O_RDONLY
);
137 int nc
, x
, y
, xmid
, ymid
;
141 xmid
= (GRID_W
>>1) - 2;
142 ymid
= (GRID_H
>>1) - 2;
146 nc
= rb
->read(fd
, &c
, 1);
159 if (is_valid_cell(xmid
+ x
, ymid
+ y
))
160 set_cell(xmid
+ x
, ymid
+ y
, pgrid
);
177 /* fill grid with initial pattern */
178 static void setup_grid(char *pgrid
, int pattern
){
182 max
= GRID_W
* GRID_H
;
186 rb
->splash(HZ
, "Random");
187 #if 0 /* two oscilators, debug pattern */
188 set_cell( 0, 1 , pgrid
);
189 set_cell( 1, 1 , pgrid
);
190 set_cell( 2, 1 , pgrid
);
192 set_cell( 6, 7 , pgrid
);
193 set_cell( 7, 7 , pgrid
);
194 set_cell( 8, 7 , pgrid
);
197 /* fill screen randomly */
198 for(n
=0; n
<(max
>>2); n
++)
199 pgrid
[rb
->rand()%max
] = 1;
203 case PATTERN_GROWTH_1
:
204 rb
->splash(HZ
, "Growth");
205 xmid
= (GRID_W
>>1) - 2;
206 ymid
= (GRID_H
>>1) - 2;
207 set_cell(xmid
+ 6, ymid
+ 0 , pgrid
);
208 set_cell(xmid
+ 4, ymid
+ 1 , pgrid
);
209 set_cell(xmid
+ 6, ymid
+ 1 , pgrid
);
210 set_cell(xmid
+ 7, ymid
+ 1 , pgrid
);
211 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
212 set_cell(xmid
+ 6, ymid
+ 2 , pgrid
);
213 set_cell(xmid
+ 4, ymid
+ 3 , pgrid
);
214 set_cell(xmid
+ 2, ymid
+ 4 , pgrid
);
215 set_cell(xmid
+ 0, ymid
+ 5 , pgrid
);
216 set_cell(xmid
+ 2, ymid
+ 5 , pgrid
);
219 rb
->splash(HZ
, "Acorn");
220 xmid
= (GRID_W
>>1) - 3;
221 ymid
= (GRID_H
>>1) - 1;
222 set_cell(xmid
+ 1, ymid
+ 0 , pgrid
);
223 set_cell(xmid
+ 3, ymid
+ 1 , pgrid
);
224 set_cell(xmid
+ 0, ymid
+ 2 , pgrid
);
225 set_cell(xmid
+ 1, ymid
+ 2 , pgrid
);
226 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
227 set_cell(xmid
+ 5, ymid
+ 2 , pgrid
);
228 set_cell(xmid
+ 6, ymid
+ 2 , pgrid
);
230 case PATTERN_GROWTH_2
:
231 rb
->splash(HZ
, "Growth 2");
232 xmid
= (GRID_W
>>1) - 4;
233 ymid
= (GRID_H
>>1) - 1;
234 set_cell(xmid
+ 0, ymid
+ 0 , pgrid
);
235 set_cell(xmid
+ 1, ymid
+ 0 , pgrid
);
236 set_cell(xmid
+ 2, ymid
+ 0 , pgrid
);
237 set_cell(xmid
+ 4, ymid
+ 0 , pgrid
);
238 set_cell(xmid
+ 0, ymid
+ 1 , pgrid
);
239 set_cell(xmid
+ 3, ymid
+ 2 , pgrid
);
240 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
241 set_cell(xmid
+ 1, ymid
+ 3 , pgrid
);
242 set_cell(xmid
+ 2, ymid
+ 3 , pgrid
);
243 set_cell(xmid
+ 4, ymid
+ 3 , pgrid
);
244 set_cell(xmid
+ 0, ymid
+ 4 , pgrid
);
245 set_cell(xmid
+ 2, ymid
+ 4 , pgrid
);
246 set_cell(xmid
+ 4, ymid
+ 4 , pgrid
);
248 case PATTERN_GLIDER_GUN
:
249 rb
->splash(HZ
, "Glider Gun");
250 set_cell( 24, 0, pgrid
);
251 set_cell( 22, 1, pgrid
);
252 set_cell( 24, 1, pgrid
);
253 set_cell( 12, 2, pgrid
);
254 set_cell( 13, 2, pgrid
);
255 set_cell( 20, 2, pgrid
);
256 set_cell( 21, 2, pgrid
);
257 set_cell( 34, 2, pgrid
);
258 set_cell( 35, 2, pgrid
);
259 set_cell( 11, 3, pgrid
);
260 set_cell( 15, 3, pgrid
);
261 set_cell( 20, 3, pgrid
);
262 set_cell( 21, 3, pgrid
);
263 set_cell( 34, 3, pgrid
);
264 set_cell( 35, 3, pgrid
);
265 set_cell( 0, 4, pgrid
);
266 set_cell( 1, 4, pgrid
);
267 set_cell( 10, 4, pgrid
);
268 set_cell( 16, 4, pgrid
);
269 set_cell( 20, 4, pgrid
);
270 set_cell( 21, 4, pgrid
);
271 set_cell( 0, 5, pgrid
);
272 set_cell( 1, 5, pgrid
);
273 set_cell( 10, 5, pgrid
);
274 set_cell( 14, 5, pgrid
);
275 set_cell( 16, 5, pgrid
);
276 set_cell( 17, 5, pgrid
);
277 set_cell( 22, 5, pgrid
);
278 set_cell( 24, 5, pgrid
);
279 set_cell( 10, 6, pgrid
);
280 set_cell( 16, 6, pgrid
);
281 set_cell( 24, 6, pgrid
);
282 set_cell( 11, 7, pgrid
);
283 set_cell( 15, 7, pgrid
);
284 set_cell( 12, 8, pgrid
);
285 set_cell( 13, 8, pgrid
);
291 static void show_grid(char *pgrid
){
295 rb
->lcd_clear_display();
296 for(y
=0; y
<GRID_H
; y
++){
297 for(x
=0; x
<GRID_W
; x
++){
298 age
= get_cell(x
, y
, pgrid
);
301 rb
->lcd_set_foreground( LCD_RGBPACK( age
, age
, age
));
303 rb
->lcd_set_foreground(age
>>7);
305 rb
->lcd_drawpixel(x
, y
);
311 rb
->lcd_set_foreground( LCD_BLACK
);
313 rb
->lcd_putsf(0, 0, "g:%d p:%d", generation
, population
);
319 /* Calculates whether the cell will be alive in the next generation.
320 n is the array with 9 elements that represent the cell itself and its
321 neighborhood like this (the cell itself is n[4]):
326 static inline bool check_cell(unsigned char *n
)
332 /* count empty neighbour cells */
333 if(n
[0]==0) empty_cells
++;
334 if(n
[1]==0) empty_cells
++;
335 if(n
[2]==0) empty_cells
++;
336 if(n
[3]==0) empty_cells
++;
337 if(n
[5]==0) empty_cells
++;
338 if(n
[6]==0) empty_cells
++;
339 if(n
[7]==0) empty_cells
++;
340 if(n
[8]==0) empty_cells
++;
342 /* now we build the number of non-zero neighbours :-P */
343 alive_cells
= 8 - empty_cells
;
346 /* If the cell is alive, it stays alive iff it has 2 or 3 alive neighbours */
347 result
= (alive_cells
==2 || alive_cells
==3);
350 /* If the cell is dead, it gets alive iff it has 3 alive neighbours */
351 result
= (alive_cells
==3);
357 /* Calculate the next generation of cells
359 * The borders of the grid are connected to their opposite sides.
361 * To avoid multiplications while accessing data in the 2-d grid
362 * (pgrid) we try to re-use previously accessed neighbourhood
363 * information which is stored in an 3x3 array.
365 static void next_generation(char *pgrid
, char *pnext_grid
){
371 rb
->memset(n
, 0, sizeof(n
));
374 * cell is (4) with 8 neighbours
385 /* go through the grid */
386 for(y
=0; y
<GRID_H
; y
++){
387 for(x
=0; x
<GRID_W
; x
++){
389 /* first cell in first row, we have to load all neighbours */
390 n
[0] = get_cell(x
-1, y
-1, pgrid
);
391 n
[1] = get_cell(x
, y
-1, pgrid
);
392 n
[2] = get_cell(x
+1, y
-1, pgrid
);
393 n
[3] = get_cell(x
-1, y
, pgrid
);
394 n
[4] = get_cell(x
, y
, pgrid
);
395 n
[5] = get_cell(x
+1, y
, pgrid
);
396 n
[6] = get_cell(x
-1, y
+1, pgrid
);
397 n
[7] = get_cell(x
, y
+1, pgrid
);
398 n
[8] = get_cell(x
+1, y
+1, pgrid
);
401 /* beginning of a row, copy what we know about our predecessor,
402 0, 1, 3, 4 are known, 2, 5, 6, 7, 8 have to be loaded
406 n
[2] = get_cell(x
+1, y
-1, pgrid
);
409 n
[5] = get_cell(x
+1, y
, pgrid
);
410 n
[6] = get_cell(x
-1, y
+1, pgrid
);
411 n
[7] = get_cell(x
, y
+1, pgrid
);
412 n
[8] = get_cell(x
+1, y
+1, pgrid
);
414 /* we are moving right in a row,
415 * copy what we know about the neighbours on our left side,
416 * 2, 5, 8 have to be loaded
420 n
[2] = get_cell(x
+1, y
-1, pgrid
);
423 n
[5] = get_cell(x
+1, y
, pgrid
);
426 n
[8] = get_cell(x
+1, y
+1, pgrid
);
430 /* how old is our cell? */
433 /* calculate the cell based on given neighbour information */
434 cell
= check_cell(n
);
436 /* is the actual cell alive? */
439 /* prevent overflow */
442 set_cell_age(x
, y
, age
, pnext_grid
);
445 set_cell_age(x
, y
, 0, pnext_grid
);
447 DEBUGF("x=%d,y=%d\n", x
, y
);
448 DEBUGF("cell: %d\n", cell
);
449 DEBUGF("%d %d %d\n", n
[0],n
[1],n
[2]);
450 DEBUGF("%d %d %d\n", n
[3],n
[4],n
[5]);
451 DEBUGF("%d %d %d\n", n
[6],n
[7],n
[8]);
452 DEBUGF("----------------\n");
461 /**********************************/
462 /* this is the plugin entry point */
463 /**********************************/
464 enum plugin_status
plugin_start(const void* parameter
)
476 backlight_force_on(); /* backlight control in lib/helper.c */
478 rb
->lcd_set_backdrop(NULL
);
479 #ifdef HAVE_LCD_COLOR
480 rb
->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */
482 rb
->lcd_set_background(LCD_DEFAULT_BG
);
483 #endif /* HAVE_LCD_COLOR */
484 #endif /* LCD_DEPTH > 1 */
486 /* link pointers to grids */
487 pgrid
= (char *)grid_a
;
488 pnext_grid
= (char *)grid_b
;
492 if( parameter
== NULL
)
494 setup_grid(pgrid
, pattern
++);
498 if( load_cellfile(parameter
, pgrid
) )
500 rb
->splashf( 1*HZ
, "Cells loaded (%s)", (char *)parameter
);
504 rb
->splash( 1*HZ
, "File Open Error");
505 setup_grid(pgrid
, pattern
++); /* fall back to stored patterns */
513 button
= pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
, ARRAYLEN(plugin_contexts
));
516 case ROCKLIFE_NEXT_REP
:
517 /* calculate next generation */
518 next_generation(pgrid
, pnext_grid
);
519 /* swap buffers, grid is the new generation */
523 /* show new generation */
526 case ROCKLIFE_PLAY_PAUSE
:
529 /* calculate next generation */
530 next_generation(pgrid
, pnext_grid
);
531 /* swap buffers, grid is the new generation */
535 /* show new generation */
538 button
= pluginlib_getaction(0, plugin_contexts
, ARRAYLEN(plugin_contexts
));
540 case ROCKLIFE_PLAY_PAUSE
:
545 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
557 setup_grid(pgrid
, pattern
);
562 case ROCKLIFE_STATUS
:
563 status_line
= !status_line
;
571 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
580 backlight_use_settings(); /* backlight control in lib/helper.c */
581 return usb
? PLUGIN_USB_CONNECTED
: PLUGIN_OK
;