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 "pluginlib_actions.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 /* not yet implemented */
82 static const struct plugin_api
* rb
;
83 const struct button_mapping
*plugin_contexts
[]
84 = {generic_directions
, generic_actions
};
87 unsigned char grid_a
[LCD_WIDTH
][LCD_HEIGHT
];
88 unsigned char grid_b
[LCD_WIDTH
][LCD_HEIGHT
];
94 static inline void set_cell(int x
, int y
, char *pgrid
){
95 pgrid
[x
+y
*LCD_WIDTH
]=1;
99 void init_grid(char *pgrid
){
102 for(y
=0; y
<LCD_HEIGHT
; y
++){
103 for(x
=0; x
<LCD_WIDTH
; x
++){
104 pgrid
[x
+y
*LCD_WIDTH
] = 0;
109 /* fill grid with initial pattern */
110 static void setup_grid(char *pgrid
, int pattern
){
114 max
= LCD_HEIGHT
*LCD_WIDTH
;
118 rb
->splash(HZ
, "Random");
119 #if 0 /* two oscilators, debug pattern */
120 set_cell( 0, 1 , pgrid
);
121 set_cell( 1, 1 , pgrid
);
122 set_cell( 2, 1 , pgrid
);
124 set_cell( 6, 7 , pgrid
);
125 set_cell( 7, 7 , pgrid
);
126 set_cell( 8, 7 , pgrid
);
129 /* fill screen randomly */
130 for(n
=0; n
<(max
>>2); n
++)
131 pgrid
[rb
->rand()%max
] = 1;
135 case PATTERN_GROWTH_1
:
136 rb
->splash(HZ
, "Growth");
137 xmid
= (LCD_WIDTH
>>1) - 2;
138 ymid
= (LCD_HEIGHT
>>1) - 2;
139 set_cell(xmid
+ 6, ymid
+ 0 , pgrid
);
140 set_cell(xmid
+ 4, ymid
+ 1 , pgrid
);
141 set_cell(xmid
+ 6, ymid
+ 1 , pgrid
);
142 set_cell(xmid
+ 7, ymid
+ 1 , pgrid
);
143 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
144 set_cell(xmid
+ 6, ymid
+ 2 , pgrid
);
145 set_cell(xmid
+ 4, ymid
+ 3 , pgrid
);
146 set_cell(xmid
+ 2, ymid
+ 4 , pgrid
);
147 set_cell(xmid
+ 0, ymid
+ 5 , pgrid
);
148 set_cell(xmid
+ 2, ymid
+ 5 , pgrid
);
151 rb
->splash(HZ
, "Acorn");
152 xmid
= (LCD_WIDTH
>>1) - 3;
153 ymid
= (LCD_HEIGHT
>>1) - 1;
154 set_cell(xmid
+ 1, ymid
+ 0 , pgrid
);
155 set_cell(xmid
+ 3, ymid
+ 1 , pgrid
);
156 set_cell(xmid
+ 0, ymid
+ 2 , pgrid
);
157 set_cell(xmid
+ 1, ymid
+ 2 , pgrid
);
158 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
159 set_cell(xmid
+ 5, ymid
+ 2 , pgrid
);
160 set_cell(xmid
+ 6, ymid
+ 2 , pgrid
);
162 case PATTERN_GROWTH_2
:
163 rb
->splash(HZ
, "Growth 2");
164 xmid
= (LCD_WIDTH
>>1) - 4;
165 ymid
= (LCD_HEIGHT
>>1) - 1;
166 set_cell(xmid
+ 0, ymid
+ 0 , pgrid
);
167 set_cell(xmid
+ 1, ymid
+ 0 , pgrid
);
168 set_cell(xmid
+ 2, ymid
+ 0 , pgrid
);
169 set_cell(xmid
+ 4, ymid
+ 0 , pgrid
);
170 set_cell(xmid
+ 0, ymid
+ 1 , pgrid
);
171 set_cell(xmid
+ 3, ymid
+ 2 , pgrid
);
172 set_cell(xmid
+ 4, ymid
+ 2 , pgrid
);
173 set_cell(xmid
+ 1, ymid
+ 3 , pgrid
);
174 set_cell(xmid
+ 2, ymid
+ 3 , pgrid
);
175 set_cell(xmid
+ 4, ymid
+ 3 , pgrid
);
176 set_cell(xmid
+ 0, ymid
+ 4 , pgrid
);
177 set_cell(xmid
+ 2, ymid
+ 4 , pgrid
);
178 set_cell(xmid
+ 4, ymid
+ 4 , pgrid
);
180 case PATTERN_GLIDER_GUN
:
181 rb
->splash(HZ
, "Glider Gun");
182 set_cell( 24, 0, pgrid
);
183 set_cell( 22, 1, pgrid
);
184 set_cell( 24, 1, pgrid
);
185 set_cell( 12, 2, pgrid
);
186 set_cell( 13, 2, pgrid
);
187 set_cell( 20, 2, pgrid
);
188 set_cell( 21, 2, pgrid
);
189 set_cell( 34, 2, pgrid
);
190 set_cell( 35, 2, pgrid
);
191 set_cell( 11, 3, pgrid
);
192 set_cell( 15, 3, pgrid
);
193 set_cell( 20, 3, pgrid
);
194 set_cell( 21, 3, pgrid
);
195 set_cell( 34, 3, pgrid
);
196 set_cell( 35, 3, pgrid
);
197 set_cell( 0, 4, pgrid
);
198 set_cell( 1, 4, pgrid
);
199 set_cell( 10, 4, pgrid
);
200 set_cell( 16, 4, pgrid
);
201 set_cell( 20, 4, pgrid
);
202 set_cell( 21, 4, pgrid
);
203 set_cell( 0, 5, pgrid
);
204 set_cell( 1, 5, pgrid
);
205 set_cell( 10, 5, pgrid
);
206 set_cell( 14, 5, pgrid
);
207 set_cell( 16, 5, pgrid
);
208 set_cell( 17, 5, pgrid
);
209 set_cell( 22, 5, pgrid
);
210 set_cell( 24, 5, pgrid
);
211 set_cell( 10, 6, pgrid
);
212 set_cell( 16, 6, pgrid
);
213 set_cell( 24, 6, pgrid
);
214 set_cell( 11, 7, pgrid
);
215 set_cell( 15, 7, pgrid
);
216 set_cell( 12, 8, pgrid
);
217 set_cell( 13, 8, pgrid
);
223 static void show_grid(char *pgrid
){
228 rb
->lcd_clear_display();
229 for(y
=0; y
<LCD_HEIGHT
; y
++){
230 for(x
=0; x
<LCD_WIDTH
; x
++){
235 rb
->lcd_set_foreground( LCD_RGBPACK( age
, age
, age
));
237 rb
->lcd_set_foreground(age
>>7);
239 rb
->lcd_drawpixel(x
, y
);
244 rb
->snprintf(buf
, sizeof(buf
), "g:%d p:%d", generation
, population
);
246 rb
->lcd_set_foreground( LCD_BLACK
);
248 rb
->lcd_puts(0, 0, buf
);
254 /* check state of cell depending on the number of neighbours */
255 static inline int check_cell(unsigned char *n
){
258 unsigned char live
= 0;
260 /* count empty neighbour cells */
261 if(n
[0]==0) empty_cells
++;
262 if(n
[1]==0) empty_cells
++;
263 if(n
[2]==0) empty_cells
++;
264 if(n
[3]==0) empty_cells
++;
265 if(n
[5]==0) empty_cells
++;
266 if(n
[6]==0) empty_cells
++;
267 if(n
[7]==0) empty_cells
++;
268 if(n
[8]==0) empty_cells
++;
270 /* now we build the number of non-zero neighbours :-P */
271 sum
= 8 - empty_cells
;
273 /* 1st and 2nd rule*/
274 if (n
[4] && (sum
<2 || sum
>3))
278 if (n
[4] && (sum
==2 || sum
==3))
288 /* Calculate the next generation of cells
290 * The borders of the grid are connected to their opposite sides.
293 * To avoid multiplications while accessing data in the 2-d grid
294 * (pgrid) we try to re-use previously accessed neighbourhood
295 * information which is stored in an 3x3 array.
298 static void next_generation(char *pgrid
, char *pnext_grid
){
305 rb
->memset(n
, 0, sizeof(n
));
308 * cell is (4) with 8 neighbours
319 /* go through the grid */
320 for(y
=0; y
<LCD_HEIGHT
; y
++){
321 for(x
=0; x
<LCD_WIDTH
; x
++){
323 /* first cell in first row, we have to load all neighbours */
324 n
[0] = pgrid
[((x
+LCD_WIDTH
-1)%LCD_WIDTH
)+((y
+LCD_HEIGHT
-1)%LCD_HEIGHT
)*LCD_WIDTH
];
325 n
[1] = pgrid
[((x
)%LCD_WIDTH
)+((y
+LCD_HEIGHT
-1)%LCD_HEIGHT
)*LCD_WIDTH
];
326 n
[2] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
+LCD_HEIGHT
-1)%LCD_HEIGHT
)*LCD_WIDTH
];
327 n
[3] = pgrid
[((x
+LCD_WIDTH
-1)%LCD_WIDTH
)+((y
)%LCD_HEIGHT
)*LCD_WIDTH
];
328 n
[5] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
)%LCD_HEIGHT
)*LCD_WIDTH
];
329 n
[6] = pgrid
[((x
+LCD_WIDTH
-1)%LCD_WIDTH
)+((y
+1)%LCD_HEIGHT
)*LCD_WIDTH
];
330 n
[7] = pgrid
[((x
)%LCD_WIDTH
)+((y
+1)%LCD_HEIGHT
)*LCD_WIDTH
];
331 n
[8] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
+1)%LCD_HEIGHT
)*LCD_WIDTH
];
334 /* beginning of a row, copy what we know about our predecessor,
335 0, 1, 3 are known, 2, 5, 6, 7, 8 have to be loaded
339 n
[2] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
+LCD_HEIGHT
-1)%LCD_HEIGHT
)*LCD_WIDTH
];
341 n
[5] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
)%LCD_HEIGHT
)*LCD_WIDTH
];
342 n
[6] = pgrid
[((x
+LCD_WIDTH
-1)%LCD_WIDTH
)+((y
+1)%LCD_HEIGHT
)*LCD_WIDTH
];
343 n
[7] = pgrid
[((x
)%LCD_WIDTH
)+((y
+1)%LCD_HEIGHT
)*LCD_WIDTH
];
344 n
[8] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
+1)%LCD_HEIGHT
)*LCD_WIDTH
];
346 /* we are moving right in a row,
347 * copy what we know about the neighbours on our left side,
348 * 2, 5, 8 have to be loaded
352 n
[2] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
+LCD_HEIGHT
-1)%LCD_HEIGHT
)*LCD_WIDTH
];
354 n
[5] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
)%LCD_HEIGHT
)*LCD_WIDTH
];
357 n
[8] = pgrid
[((x
+1)%LCD_WIDTH
)+((y
+1)%LCD_HEIGHT
)*LCD_WIDTH
];
363 /* how old is our cell? */
367 /* calculate the cell based on given neighbour information */
368 cell
= check_cell(n
);
370 /* is the actual cell alive? */
373 /* prevent overflow */
377 pnext_grid
[m
] = age
+ 1;
383 DEBUGF("x=%d,y=%d\n", x
, y
);
384 DEBUGF("cell: %d\n", cell
);
385 DEBUGF("%d %d %d\n", n
[0],n
[1],n
[2]);
386 DEBUGF("%d %d %d\n", n
[3],n
[4],n
[5]);
387 DEBUGF("%d %d %d\n", n
[6],n
[7],n
[8]);
388 DEBUGF("----------------\n");
395 /**********************************/
396 /* this is the plugin entry point */
397 /**********************************/
398 enum plugin_status
plugin_start(const struct plugin_api
* api
, const void* parameter
)
411 backlight_force_on(rb
); /* backlight control in lib/helper.c */
413 rb
->lcd_set_backdrop(NULL
);
414 #ifdef HAVE_LCD_COLOR
415 rb
->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */
417 rb
->lcd_set_background(LCD_DEFAULT_BG
);
418 #endif /* HAVE_LCD_COLOR */
419 #endif /* LCD_DEPTH > 1 */
421 /* link pointers to grids */
422 pgrid
= (char *)grid_a
;
423 pnext_grid
= (char *)grid_b
;
426 setup_grid(pgrid
, pattern
++);
430 button
= pluginlib_getaction(rb
, TIMEOUT_BLOCK
, plugin_contexts
, 2);
433 case ROCKLIFE_NEXT_REP
:
434 /* calculate next generation */
435 next_generation(pgrid
, pnext_grid
);
436 /* swap buffers, grid is the new generation */
440 /* show new generation */
443 case ROCKLIFE_PLAY_PAUSE
:
446 /* calculate next generation */
447 next_generation(pgrid
, pnext_grid
);
448 /* swap buffers, grid is the new generation */
452 /* show new generation */
455 button
= pluginlib_getaction(rb
, 0, plugin_contexts
, 2);
457 case ROCKLIFE_PLAY_PAUSE
:
469 setup_grid(pgrid
, pattern
);
474 case ROCKLIFE_STATUS
:
475 status_line
= !status_line
;
484 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
485 return PLUGIN_USB_CONNECTED
;
492 backlight_use_settings(rb
); /* backlight control in lib/helper.c */