/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 Matthias Wientapper * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * 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 "lib/pluginlib_actions.h" #include "lib/helper.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 */ 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; yopen(file, O_RDONLY); if (fd==-1) return false; file_size = rb->filesize(fd); if (file_size==-1) return false; char buf1[file_size]; int i, j, k, xmid, ymid; j=0; k=0; xmid = (LCD_WIDTH>>1) - 2; ymid = (LCD_HEIGHT>>1) - 2; rb->read(fd, buf1, file_size - 1); for(i=0; iclose(fd); return true; } /* fill grid with initial pattern */ static void setup_grid(char *pgrid, int pattern){ int n, max; int xmid, ymid; max = LCD_HEIGHT*LCD_WIDTH; switch(pattern){ case PATTERN_RANDOM: rb->splash(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(const void* parameter) { int button = 0; int quit = 0; int stop = 0; int pattern = 0; char *pgrid; char *pnext_grid; char *ptemp; (void)(parameter); backlight_force_on(); /* backlight control in lib/helper.c */ #if LCD_DEPTH > 1 rb->lcd_set_backdrop(NULL); #ifdef HAVE_LCD_COLOR rb->lcd_set_background(LCD_RGBPACK(182, 198, 229)); /* rockbox blue */ #else rb->lcd_set_background(LCD_DEFAULT_BG); #endif /* HAVE_LCD_COLOR */ #endif /* LCD_DEPTH > 1 */ /* link pointers to grids */ pgrid = (char *)grid_a; pnext_grid = (char *)grid_b; init_grid(pgrid); if( parameter == NULL ) { setup_grid(pgrid, pattern++); } else { if( load_cellfile(parameter, pgrid) ) { rb->splashf( 1*HZ, "Cells loaded (%s)", (char *)parameter ); } else { rb->splash( 1*HZ, "File Open Error"); setup_grid(pgrid, pattern++); /* fall back to stored patterns */ } } show_grid(pgrid); while(!quit) { button = pluginlib_getaction(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(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(); } backlight_use_settings(); /* backlight control in lib/helper.c */ return PLUGIN_OK; }