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_FIRE
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_QUIT
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
[]
83 = {generic_directions
, generic_actions
};
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
];
96 static inline bool is_valid_cell(int x
, int y
) {
97 return (x
>= 0 && x
< GRID_W
98 && y
>= 0 && y
< GRID_H
);
101 static inline void set_cell_age(int x
, int y
, unsigned char age
, char *pgrid
) {
102 pgrid
[x
+y
*GRID_W
] = age
;
105 static inline void set_cell(int x
, int y
, char *pgrid
) {
106 set_cell_age(x
, y
, 1, pgrid
);
109 static inline unsigned char get_cell(int x
, int y
, char *pgrid
) {
112 else if (x
>= GRID_W
)
117 else if (y
>= GRID_H
)
120 return pgrid
[x
+y
*GRID_W
];
124 void init_grid(char *pgrid
){
125 memset(pgrid
, 0, GRID_W
* GRID_H
);
128 /*fill grid with pattern from file (viewer mode)*/
129 static bool load_cellfile(const char *file
, char *pgrid
){
131 fd
= rb
->open(file
, O_RDONLY
);
138 int nc
, x
, y
, xmid
, ymid
;
142 xmid
= (GRID_W
>>1) - 2;
143 ymid
= (GRID_H
>>1) - 2;
147 nc
= rb
->read(fd
, &c
, 1);
160 if (is_valid_cell(xmid
+ x
, ymid
+ y
))
161 set_cell(xmid
+ x
, ymid
+ y
, pgrid
);
178 /* fill grid with initial pattern */
179 static void setup_grid(char *pgrid
, int pattern
){
183 max
= GRID_W
* GRID_H
;
187 rb
->splash(HZ
, "Random");
188 #if 0 /* two oscilators, debug pattern */
189 set_cell( 0, 1 , pgrid
);
190 set_cell( 1, 1 , pgrid
);
191 set_cell( 2, 1 , pgrid
);
193 set_cell( 6, 7 , pgrid
);
194 set_cell( 7, 7 , pgrid
);
195 set_cell( 8, 7 , pgrid
);
198 /* fill screen randomly */
199 for(n
=0; n
<(max
>>2); n
++)
200 pgrid
[rb
->rand()%max
] = 1;
204 case PATTERN_GROWTH_1
:
205 rb
->splash(HZ
, "Growth");
206 xmid
= (GRID_W
>>1) - 2;
207 ymid
= (GRID_H
>>1) - 2;
208 set_cell(xmid
+ 6, ymid
+ 0 , pgrid
);
209 set_cell(xmid
+ 4, ymid
+ 1 , pgrid
);
210 set_cell(xmid
+ 6, ymid
+ 1 , pgrid
);
211 set_cell(xmid
+ 7, ymid
+ 1 , pgrid
);
212 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
213 set_cell(xmid
+ 6, ymid
+ 2 , pgrid
);
214 set_cell(xmid
+ 4, ymid
+ 3 , pgrid
);
215 set_cell(xmid
+ 2, ymid
+ 4 , pgrid
);
216 set_cell(xmid
+ 0, ymid
+ 5 , pgrid
);
217 set_cell(xmid
+ 2, ymid
+ 5 , pgrid
);
220 rb
->splash(HZ
, "Acorn");
221 xmid
= (GRID_W
>>1) - 3;
222 ymid
= (GRID_H
>>1) - 1;
223 set_cell(xmid
+ 1, ymid
+ 0 , pgrid
);
224 set_cell(xmid
+ 3, ymid
+ 1 , pgrid
);
225 set_cell(xmid
+ 0, ymid
+ 2 , pgrid
);
226 set_cell(xmid
+ 1, ymid
+ 2 , pgrid
);
227 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
228 set_cell(xmid
+ 5, ymid
+ 2 , pgrid
);
229 set_cell(xmid
+ 6, ymid
+ 2 , pgrid
);
231 case PATTERN_GROWTH_2
:
232 rb
->splash(HZ
, "Growth 2");
233 xmid
= (GRID_W
>>1) - 4;
234 ymid
= (GRID_H
>>1) - 1;
235 set_cell(xmid
+ 0, ymid
+ 0 , pgrid
);
236 set_cell(xmid
+ 1, ymid
+ 0 , pgrid
);
237 set_cell(xmid
+ 2, ymid
+ 0 , pgrid
);
238 set_cell(xmid
+ 4, ymid
+ 0 , pgrid
);
239 set_cell(xmid
+ 0, ymid
+ 1 , pgrid
);
240 set_cell(xmid
+ 3, ymid
+ 2 , pgrid
);
241 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
242 set_cell(xmid
+ 1, ymid
+ 3 , pgrid
);
243 set_cell(xmid
+ 2, ymid
+ 3 , pgrid
);
244 set_cell(xmid
+ 4, ymid
+ 3 , pgrid
);
245 set_cell(xmid
+ 0, ymid
+ 4 , pgrid
);
246 set_cell(xmid
+ 2, ymid
+ 4 , pgrid
);
247 set_cell(xmid
+ 4, ymid
+ 4 , pgrid
);
249 case PATTERN_GLIDER_GUN
:
250 rb
->splash(HZ
, "Glider Gun");
251 set_cell( 24, 0, pgrid
);
252 set_cell( 22, 1, pgrid
);
253 set_cell( 24, 1, pgrid
);
254 set_cell( 12, 2, pgrid
);
255 set_cell( 13, 2, pgrid
);
256 set_cell( 20, 2, pgrid
);
257 set_cell( 21, 2, pgrid
);
258 set_cell( 34, 2, pgrid
);
259 set_cell( 35, 2, pgrid
);
260 set_cell( 11, 3, pgrid
);
261 set_cell( 15, 3, pgrid
);
262 set_cell( 20, 3, pgrid
);
263 set_cell( 21, 3, pgrid
);
264 set_cell( 34, 3, pgrid
);
265 set_cell( 35, 3, pgrid
);
266 set_cell( 0, 4, pgrid
);
267 set_cell( 1, 4, pgrid
);
268 set_cell( 10, 4, pgrid
);
269 set_cell( 16, 4, pgrid
);
270 set_cell( 20, 4, pgrid
);
271 set_cell( 21, 4, pgrid
);
272 set_cell( 0, 5, pgrid
);
273 set_cell( 1, 5, pgrid
);
274 set_cell( 10, 5, pgrid
);
275 set_cell( 14, 5, pgrid
);
276 set_cell( 16, 5, pgrid
);
277 set_cell( 17, 5, pgrid
);
278 set_cell( 22, 5, pgrid
);
279 set_cell( 24, 5, pgrid
);
280 set_cell( 10, 6, pgrid
);
281 set_cell( 16, 6, pgrid
);
282 set_cell( 24, 6, pgrid
);
283 set_cell( 11, 7, pgrid
);
284 set_cell( 15, 7, pgrid
);
285 set_cell( 12, 8, pgrid
);
286 set_cell( 13, 8, pgrid
);
292 static void show_grid(char *pgrid
){
296 rb
->lcd_clear_display();
297 for(y
=0; y
<GRID_H
; y
++){
298 for(x
=0; x
<GRID_W
; x
++){
299 age
= get_cell(x
, y
, pgrid
);
302 rb
->lcd_set_foreground( LCD_RGBPACK( age
, age
, age
));
304 rb
->lcd_set_foreground(age
>>7);
306 rb
->lcd_drawpixel(x
, y
);
311 rb
->snprintf(buf
, sizeof(buf
), "g:%d p:%d", generation
, population
);
313 rb
->lcd_set_foreground( LCD_BLACK
);
315 rb
->lcd_puts(0, 0, buf
);
321 /* Calculates whether the cell will be alive in the next generation.
322 n is the array with 9 elements that represent the cell itself and its
323 neighborhood like this (the cell itself is n[4]):
328 static inline bool check_cell(unsigned char *n
)
334 /* count empty neighbour cells */
335 if(n
[0]==0) empty_cells
++;
336 if(n
[1]==0) empty_cells
++;
337 if(n
[2]==0) empty_cells
++;
338 if(n
[3]==0) empty_cells
++;
339 if(n
[5]==0) empty_cells
++;
340 if(n
[6]==0) empty_cells
++;
341 if(n
[7]==0) empty_cells
++;
342 if(n
[8]==0) empty_cells
++;
344 /* now we build the number of non-zero neighbours :-P */
345 alive_cells
= 8 - empty_cells
;
348 /* If the cell is alive, it stays alive iff it has 2 or 3 alive neighbours */
349 result
= (alive_cells
==2 || alive_cells
==3);
352 /* If the cell is dead, it gets alive iff it has 3 alive neighbours */
353 result
= (alive_cells
==3);
359 /* Calculate the next generation of cells
361 * The borders of the grid are connected to their opposite sides.
363 * To avoid multiplications while accessing data in the 2-d grid
364 * (pgrid) we try to re-use previously accessed neighbourhood
365 * information which is stored in an 3x3 array.
367 static void next_generation(char *pgrid
, char *pnext_grid
){
373 rb
->memset(n
, 0, sizeof(n
));
376 * cell is (4) with 8 neighbours
387 /* go through the grid */
388 for(y
=0; y
<GRID_H
; y
++){
389 for(x
=0; x
<GRID_W
; x
++){
391 /* first cell in first row, we have to load all neighbours */
392 n
[0] = get_cell(x
-1, y
-1, pgrid
);
393 n
[1] = get_cell(x
, y
-1, pgrid
);
394 n
[2] = get_cell(x
+1, y
-1, pgrid
);
395 n
[3] = get_cell(x
-1, y
, pgrid
);
396 n
[4] = get_cell(x
, y
, pgrid
);
397 n
[5] = get_cell(x
+1, y
, pgrid
);
398 n
[6] = get_cell(x
-1, y
+1, pgrid
);
399 n
[7] = get_cell(x
, y
+1, pgrid
);
400 n
[8] = get_cell(x
+1, y
+1, pgrid
);
403 /* beginning of a row, copy what we know about our predecessor,
404 0, 1, 3, 4 are known, 2, 5, 6, 7, 8 have to be loaded
408 n
[2] = get_cell(x
+1, y
-1, pgrid
);
411 n
[5] = get_cell(x
+1, y
, pgrid
);
412 n
[6] = get_cell(x
-1, y
+1, pgrid
);
413 n
[7] = get_cell(x
, y
+1, pgrid
);
414 n
[8] = get_cell(x
+1, y
+1, pgrid
);
416 /* we are moving right in a row,
417 * copy what we know about the neighbours on our left side,
418 * 2, 5, 8 have to be loaded
422 n
[2] = get_cell(x
+1, y
-1, pgrid
);
425 n
[5] = get_cell(x
+1, y
, pgrid
);
428 n
[8] = get_cell(x
+1, y
+1, pgrid
);
432 /* how old is our cell? */
435 /* calculate the cell based on given neighbour information */
436 cell
= check_cell(n
);
438 /* is the actual cell alive? */
441 /* prevent overflow */
444 set_cell_age(x
, y
, age
, pnext_grid
);
447 set_cell_age(x
, y
, 0, pnext_grid
);
449 DEBUGF("x=%d,y=%d\n", x
, y
);
450 DEBUGF("cell: %d\n", cell
);
451 DEBUGF("%d %d %d\n", n
[0],n
[1],n
[2]);
452 DEBUGF("%d %d %d\n", n
[3],n
[4],n
[5]);
453 DEBUGF("%d %d %d\n", n
[6],n
[7],n
[8]);
454 DEBUGF("----------------\n");
463 /**********************************/
464 /* this is the plugin entry point */
465 /**********************************/
466 enum plugin_status
plugin_start(const void* parameter
)
478 backlight_force_on(); /* backlight control in lib/helper.c */
480 rb
->lcd_set_backdrop(NULL
);
481 #ifdef HAVE_LCD_COLOR
482 rb
->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */
484 rb
->lcd_set_background(LCD_DEFAULT_BG
);
485 #endif /* HAVE_LCD_COLOR */
486 #endif /* LCD_DEPTH > 1 */
488 /* link pointers to grids */
489 pgrid
= (char *)grid_a
;
490 pnext_grid
= (char *)grid_b
;
494 if( parameter
== NULL
)
496 setup_grid(pgrid
, pattern
++);
500 if( load_cellfile(parameter
, pgrid
) )
502 rb
->splashf( 1*HZ
, "Cells loaded (%s)", (char *)parameter
);
506 rb
->splash( 1*HZ
, "File Open Error");
507 setup_grid(pgrid
, pattern
++); /* fall back to stored patterns */
515 button
= pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
, 2);
518 case ROCKLIFE_NEXT_REP
:
519 /* calculate next generation */
520 next_generation(pgrid
, pnext_grid
);
521 /* swap buffers, grid is the new generation */
525 /* show new generation */
528 case ROCKLIFE_PLAY_PAUSE
:
531 /* calculate next generation */
532 next_generation(pgrid
, pnext_grid
);
533 /* swap buffers, grid is the new generation */
537 /* show new generation */
540 button
= pluginlib_getaction(0, plugin_contexts
, 2);
542 case ROCKLIFE_PLAY_PAUSE
:
547 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
559 setup_grid(pgrid
, pattern
);
564 case ROCKLIFE_STATUS
:
565 status_line
= !status_line
;
573 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
582 backlight_use_settings(); /* backlight control in lib/helper.c */
583 return usb
? PLUGIN_USB_CONNECTED
: PLUGIN_OK
;