From b046b79f7ed45775a3108e29c62dcf3216473dc1 Mon Sep 17 00:00:00 2001 From: Antoine Cellerier Date: Fri, 29 Jun 2007 19:40:03 +0000 Subject: FS #6616 - "rocklife: Conway's Game of Life" by Matthias Wientapper. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13736 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/rocklife.c | 489 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 apps/plugins/rocklife.c (limited to 'apps/plugins/rocklife.c') 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: helloworld.c 8349 2006-01-15 18:20:18Z amiconn $ + * + * Copyright (C) 2007 Matthias Wientapper + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* + * This is an implementatino of Conway's Game of Life + * + * from http://en.wikipedia.org/wiki/Conway's_Game_of_Life: + * + * Rules + * + * The universe of the Game of Life is an infinite two-dimensional + * orthogonal grid of square cells, each of which is in one of two + * possible states, live or dead. Every cell interacts with its eight + * neighbours, which are the cells that are directly horizontally, + * vertically, or diagonally adjacent. At each step in time, the + * following transitions occur: + * + * 1. Any live cell with fewer than two live neighbours dies, as if by + * loneliness. + * + * 2. Any live cell with more than three live neighbours dies, as if + * by overcrowding. + * + * 3. Any live cell with two or three live neighbours lives, + * unchanged, to the next generation. + * + * 4. Any dead cell with exactly three live neighbours comes to life. + * + * The initial pattern constitutes the first generation of the + * system. The second generation is created by applying the above + * rules simultaneously to every cell in the first generation -- + * births and deaths happen simultaneously, and the discrete moment at + * which this happens is sometimes called a tick. (In other words, + * each generation is based entirely on the one before.) The rules + * continue to be applied repeatedly to create further generations. + * + * + * + * TODO: + * - nicer colours for pixels with respect to age + * - editor for start patterns + * - probably tons of speed-up opportunities + */ + +#include "plugin.h" +#include "pluginlib_actions.h" + +PLUGIN_HEADER + +#define ROCKLIFE_PLAY_PAUSE PLA_FIRE +#define ROCKLIFE_INIT PLA_DOWN +#define ROCKLIFE_NEXT PLA_RIGHT +#define ROCKLIFE_NEXT_REP PLA_RIGHT_REPEAT +#define ROCKLIFE_QUIT PLA_QUIT +#define ROCKLIFE_STATUS PLA_LEFT + +#define PATTERN_RANDOM 0 +#define PATTERN_GROWTH_1 1 +#define PATTERN_GROWTH_2 2 +#define PATTERN_ACORN 3 +#define PATTERN_GLIDER_GUN 4 /* not yet implemented */ + +static struct plugin_api* rb; +const struct button_mapping *plugin_contexts[] += {generic_directions, generic_actions}; + + +unsigned char grid_a[LCD_WIDTH][LCD_HEIGHT]; +unsigned char grid_b[LCD_WIDTH][LCD_HEIGHT]; +int generation = 0; +int population = 0; +int status_line = 0; +char buf[30]; + +static inline void set_cell(int x, int y, char *pgrid){ + pgrid[x+y*LCD_WIDTH]=1; +} + +/* clear grid */ +void init_grid(char *pgrid){ + int x, y; + + for(y=0; ysplash(HZ, "Random"); +#if 0 /* two oscilators, debug pattern */ + set_cell( 0, 1 , pgrid); + set_cell( 1, 1 , pgrid); + set_cell( 2, 1 , pgrid); + + set_cell( 6, 7 , pgrid); + set_cell( 7, 7 , pgrid); + set_cell( 8, 7 , pgrid); +#endif + + /* fill screen randomly */ + for(n=0; n<(max>>2); n++) + pgrid[rb->rand()%max] = 1; + + break; + + case PATTERN_GROWTH_1: + rb->splash(HZ, "Growth"); + xmid = (LCD_WIDTH>>1) - 2; + ymid = (LCD_HEIGHT>>1) - 2; + set_cell(xmid + 6, ymid + 0 , pgrid); + set_cell(xmid + 4, ymid + 1 , pgrid); + set_cell(xmid + 6, ymid + 1 , pgrid); + set_cell(xmid + 7, ymid + 1 , pgrid); + set_cell(xmid + 4, ymid + 2 , pgrid); + set_cell(xmid + 6, ymid + 2 , pgrid); + set_cell(xmid + 4, ymid + 3 , pgrid); + set_cell(xmid + 2, ymid + 4 , pgrid); + set_cell(xmid + 0, ymid + 5 , pgrid); + set_cell(xmid + 2, ymid + 5 , pgrid); + break; + case PATTERN_ACORN: + rb->splash(HZ, "Acorn"); + xmid = (LCD_WIDTH>>1) - 3; + ymid = (LCD_HEIGHT>>1) - 1; + set_cell(xmid + 1, ymid + 0 , pgrid); + set_cell(xmid + 3, ymid + 1 , pgrid); + set_cell(xmid + 0, ymid + 2 , pgrid); + set_cell(xmid + 1, ymid + 2 , pgrid); + set_cell(xmid + 4, ymid + 2 , pgrid); + set_cell(xmid + 5, ymid + 2 , pgrid); + set_cell(xmid + 6, ymid + 2 , pgrid); + break; + case PATTERN_GROWTH_2: + rb->splash(HZ, "Growth 2"); + xmid = (LCD_WIDTH>>1) - 4; + ymid = (LCD_HEIGHT>>1) - 1; + set_cell(xmid + 0, ymid + 0 , pgrid); + set_cell(xmid + 1, ymid + 0 , pgrid); + set_cell(xmid + 2, ymid + 0 , pgrid); + set_cell(xmid + 4, ymid + 0 , pgrid); + set_cell(xmid + 0, ymid + 1 , pgrid); + set_cell(xmid + 3, ymid + 2 , pgrid); + set_cell(xmid + 4, ymid + 2 , pgrid); + set_cell(xmid + 1, ymid + 3 , pgrid); + set_cell(xmid + 2, ymid + 3 , pgrid); + set_cell(xmid + 4, ymid + 3 , pgrid); + set_cell(xmid + 0, ymid + 4 , pgrid); + set_cell(xmid + 2, ymid + 4 , pgrid); + set_cell(xmid + 4, ymid + 4 , pgrid); + break; + case PATTERN_GLIDER_GUN: + rb->splash(HZ, "Glider Gun"); + set_cell( 24, 0, pgrid); + set_cell( 22, 1, pgrid); + set_cell( 24, 1, pgrid); + set_cell( 12, 2, pgrid); + set_cell( 13, 2, pgrid); + set_cell( 20, 2, pgrid); + set_cell( 21, 2, pgrid); + set_cell( 34, 2, pgrid); + set_cell( 35, 2, pgrid); + set_cell( 11, 3, pgrid); + set_cell( 15, 3, pgrid); + set_cell( 20, 3, pgrid); + set_cell( 21, 3, pgrid); + set_cell( 34, 3, pgrid); + set_cell( 35, 3, pgrid); + set_cell( 0, 4, pgrid); + set_cell( 1, 4, pgrid); + set_cell( 10, 4, pgrid); + set_cell( 16, 4, pgrid); + set_cell( 20, 4, pgrid); + set_cell( 21, 4, pgrid); + set_cell( 0, 5, pgrid); + set_cell( 1, 5, pgrid); + set_cell( 10, 5, pgrid); + set_cell( 14, 5, pgrid); + set_cell( 16, 5, pgrid); + set_cell( 17, 5, pgrid); + set_cell( 22, 5, pgrid); + set_cell( 24, 5, pgrid); + set_cell( 10, 6, pgrid); + set_cell( 16, 6, pgrid); + set_cell( 24, 6, pgrid); + set_cell( 11, 7, pgrid); + set_cell( 15, 7, pgrid); + set_cell( 12, 8, pgrid); + set_cell( 13, 8, pgrid); + break; + } +} + +/* display grid */ +static void show_grid(char *pgrid){ + int x, y; + int m; + unsigned char age; + + rb->lcd_clear_display(); + for(y=0; y= 16 + rb->lcd_set_foreground( LCD_RGBPACK( age, age, age )); +#elif LCD_DEPTH == 2 + rb->lcd_set_foreground(age>>7); +#endif + rb->lcd_drawpixel(x, y); + } + } + } + if(status_line){ + rb->snprintf(buf, sizeof(buf), "g:%d p:%d", generation, population); +#if LCD_DEPTH > 1 + rb->lcd_set_foreground( LCD_BLACK ); +#endif + rb->lcd_puts(0, 0, buf); + } + rb->lcd_update(); +} + + +/* check state of cell depending on the number of neighbours */ +static inline int check_cell(unsigned char *n){ + int sum; + int empty_cells = 0; + unsigned char live = 0; + + /* count empty neighbour cells */ + if(n[0]==0) empty_cells++; + if(n[1]==0) empty_cells++; + if(n[2]==0) empty_cells++; + if(n[3]==0) empty_cells++; + if(n[5]==0) empty_cells++; + if(n[6]==0) empty_cells++; + if(n[7]==0) empty_cells++; + if(n[8]==0) empty_cells++; + + /* now we build the number of non-zero neighbours :-P */ + sum = 8 - empty_cells; + + /* 1st and 2nd rule*/ + if (n[4] && (sum<2 || sum>3)) + live = false; + + /* 3rd rule */ + if (n[4] && (sum==2 || sum==3)) + live = true; + + /* 4rd rule */ + if (!n[4] && sum==3) + live = true; + + return live; +} + +/* Calculate the next generation of cells + * + * The borders of the grid are connected to their opposite sides. + * + * + * To avoid multiplications while accessing data in the 2-d grid + * (pgrid) we try to re-use previously accessed neighbourhood + * information which is stored in an 3x3 array. + * + */ +static void next_generation(char *pgrid, char *pnext_grid){ + int x, y; + unsigned char cell; + int age; + int m; + unsigned char n[9]; + + rb->memset(n, 0, sizeof(n)); + + /* + * cell is (4) with 8 neighbours + * + * 0|1|2 + * ----- + * 3|4|5 + * ----- + * 6|7|8 + */ + + population = 0; + + /* go through the grid */ + for(y=0; y252){ + pnext_grid[m] = 252; + } else { + pnext_grid[m] = age + 1; + } + } + else + pnext_grid[m] = 0; +#if 0 + DEBUGF("x=%d,y=%d\n", x, y); + DEBUGF("cell: %d\n", cell); + DEBUGF("%d %d %d\n", n[0],n[1],n[2]); + DEBUGF("%d %d %d\n", n[3],n[4],n[5]); + DEBUGF("%d %d %d\n", n[6],n[7],n[8]); + DEBUGF("----------------\n"); +#endif + } + } + generation++; +} + +/**********************************/ +/* this is the plugin entry point */ +/**********************************/ +enum plugin_status plugin_start(struct plugin_api* api, void* parameter) +{ + int button = 0; + int quit = 0; + int stop = 0; + int pattern = 0; + char *pgrid; + char *pnext_grid; + char *ptemp; + + (void)parameter; + rb = api; + + rb->backlight_set_timeout(1); +#if LCD_DEPTH > 1 + rb->lcd_set_backdrop(NULL); + rb->lcd_set_background(LCD_DEFAULT_BG); +#endif + + /* link pointers to grids */ + pgrid = (char *)grid_a; + pnext_grid = (char *)grid_b; + + init_grid(pgrid); + setup_grid(pgrid, pattern++); + show_grid(pgrid); + + while(!quit) { + button = pluginlib_getaction(rb, TIMEOUT_BLOCK, plugin_contexts, 2); + switch(button) { + case ROCKLIFE_NEXT: + case ROCKLIFE_NEXT_REP: + /* calculate next generation */ + next_generation(pgrid, pnext_grid); + /* swap buffers, grid is the new generation */ + ptemp = pgrid; + pgrid = pnext_grid; + pnext_grid = ptemp; + /* show new generation */ + show_grid(pgrid); + break; + case ROCKLIFE_PLAY_PAUSE: + stop = 0; + while(!stop){ + /* calculate next generation */ + next_generation(pgrid, pnext_grid); + /* swap buffers, grid is the new generation */ + ptemp = pgrid; + pgrid = pnext_grid; + pnext_grid = ptemp; + /* show new generation */ + rb->yield(); + show_grid(pgrid); + button = pluginlib_getaction(rb, 0, plugin_contexts, 2); + switch(button) { + case ROCKLIFE_PLAY_PAUSE: + case ROCKLIFE_QUIT: + stop = 1; + break; + default: + break; + } + rb->yield(); + } + break; + case ROCKLIFE_INIT: + init_grid(pgrid); + setup_grid(pgrid, pattern); + show_grid(pgrid); + pattern++; + pattern%=5; + break; + case ROCKLIFE_STATUS: + status_line = !status_line; + show_grid(pgrid); + break; + case ROCKLIFE_QUIT: + /* quit plugin */ + quit=true; + return PLUGIN_OK; + break; + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { + return PLUGIN_USB_CONNECTED; + } + break; + } + rb->yield(); + } + + rb->backlight_set_timeout(rb->global_settings->backlight_timeout); + return PLUGIN_OK; +} + + -- cgit v1.2.3