Skip to content

Instantly share code, notes, and snippets.

@beniwohli
Created September 17, 2012 11:31
Show Gist options
  • Save beniwohli/3736791 to your computer and use it in GitHub Desktop.
Save beniwohli/3736791 to your computer and use it in GitHub Desktop.
A Game Of Life implementation for Rockbox
#include "plugin.h"
PLUGIN_HEADER
/* variable button definitions */
#if CONFIG_KEYPAD == RECORDER_PAD
#define GOL_QUIT BUTTON_OFF
#define GOL_UP BUTTON_UP
#define GOL_DOWN BUTTON_DOWN
#elif CONFIG_KEYPAD == ONDIO_PAD
#define GOL_QUIT BUTTON_OFF
#define GOL_UP BUTTON_UP
#define GOL_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
#define GOL_QUIT BUTTON_OFF
#define GOL_UP BUTTON_UP
#define GOL_DOWN BUTTON_DOWN
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD)
#define GOL_UP BUTTON_MENU
#define GOL_DOWN BUTTON_PLAY
#define GOL_RIGHT BUTTON_RIGHT
#define GOL_LEFT BUTTON_LEFT
#define GOL_QUIT (BUTTON_SELECT | BUTTON_MENU)
#define GOL_PLAY (BUTTON_SELECT | BUTTON_PLAY)
#define GOL_SELECT (BUTTON_SELECT | BUTTON_REL)
#define GOL_EDIT (BUTTON_SELECT | BUTTON_RIGHT)
#elif (CONFIG_KEYPAD == IAUDIO_X5_PAD)
#define GOL_UP BUTTON_UP
#define GOL_DOWN BUTTON_DOWN
#define GOL_RIGHT BUTTON_RIGHT
#define GOL_LEFT BUTTON_LEFT
#define GOL_QUIT BUTTON_POWER
#define GOL_PLAY BUTTON_PLAY
#define GOL_SELECT BUTTON_SELECT
#define GOL_EDIT BUTTON_REC
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
#define GOL_UP BUTTON_UP
#define GOL_DOWN BUTTON_DOWN
#define GOL_QUIT BUTTON_A
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
#define GOL_UP BUTTON_UP
#define GOL_DOWN BUTTON_DOWN
#define GOL_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
#define GOL_UP BUTTON_SCROLL_UP
#define GOL_DOWN BUTTON_SCROLL_DOWN
#define GOL_QUIT BUTTON_POWER
#define GOL_LEFT BUTTON_LEFT
#define GOL_RIGHT BUTTON_RIGHT
#define GOL_PLAY BUTTON_PLAY
#define GOL_SELECT BUTTON_FF
#define GOL_EDIT BUTTON_REW
#endif
#define WORLDS_DIR PLUGIN_DIR "/world.gol"
#define XDIM LCD_HEIGHT
#define YDIM LCD_WIDTH
static struct plugin_api* rb;
static unsigned int length_in_bytes = XDIM*YDIM;
static unsigned char cells[XDIM*YDIM];
static unsigned char temp_cells[XDIM*YDIM];
/* zoom factor */
static short zoom = 1;
/* (x_top, y_top) is the cell in the upper left corner of the viewport */
static int x_top = 0;
static int y_top = 0;
static bool zoom_direction = false; /* false: zoom in; true: zoom out */
static bool game_started = false;
static bool play = false;
static bool in_edit = false;
static int x_edit = 0;
static int y_edit = 0;
static short blink = 10;
static int sim_speed = 10;
void set_cell(unsigned int x, unsigned int y){
unsigned w = YDIM;
unsigned h = XDIM;
int xoleft, xoright, yoabove, yobelow;
unsigned char *cell_ptr = cells + (y*w) + x;
if (x == 0)
xoleft = w - 1;
else
xoleft = -1;
if (y == 0)
yoabove = length_in_bytes - w;
else
yoabove = -w;
if (x == (w - 1))
xoright = -(w - 1);
else
xoright = 1;
if (y == (h - 1))
yobelow = -(length_in_bytes - w);
else
yobelow = w;
*(cell_ptr) |= 0x01;
*(cell_ptr + yoabove + xoleft) += 2;
*(cell_ptr + yoabove) += 2;
*(cell_ptr + yoabove + xoright) += 2;
*(cell_ptr + xoleft) += 2;
*(cell_ptr + xoright) += 2;
*(cell_ptr + yobelow + xoleft) += 2;
*(cell_ptr + yobelow) += 2;
*(cell_ptr + yobelow + xoright) += 2;
}
void clear_cell(unsigned int x, unsigned int y){
unsigned w = YDIM;
unsigned h = XDIM;
int xoleft, xoright, yoabove, yobelow;
unsigned char *cell_ptr = cells + (y*w) + x;
if (x == 0)
xoleft = w - 1;
else
xoleft = -1;
if (y == 0)
yoabove = length_in_bytes - w;
else
yoabove = -w;
if (x == (w - 1))
xoright = -(w - 1);
else
xoright = 1;
if (y == (h - 1))
yobelow = -(length_in_bytes - w);
else
yobelow = w;
*(cell_ptr) &= ~0x01;
*(cell_ptr + yoabove + xoleft) -= 2;
*(cell_ptr + yoabove) -= 2;
*(cell_ptr + yoabove + xoright) -= 2;
*(cell_ptr + xoleft) -= 2;
*(cell_ptr + xoright) -= 2;
*(cell_ptr + yobelow + xoleft) -= 2;
*(cell_ptr + yobelow) -= 2;
*(cell_ptr + yobelow + xoright) -= 2;
}
int cell_state(int x, int y){
unsigned char *cell_ptr;
cell_ptr = cells + (y*YDIM) + x;
return *cell_ptr & 0x01;
}
void game_step(void){
unsigned int x, y, count;
unsigned int w = YDIM;
unsigned int h = XDIM;
unsigned char *cell_ptr;
rb->memcpy(temp_cells, cells, length_in_bytes);
cell_ptr = temp_cells;
for (y=0; y < h; y++){
x = 0;
do {
while (*cell_ptr == 0){
cell_ptr++;
if (++x >= w) goto RowDone;
}
count = *cell_ptr >> 1;
if (*cell_ptr & 0x01){
if ((count != 2) && (count != 3)){
clear_cell(x, y);
}
} else {
if (count == 3){
set_cell(x, y);
}
}
cell_ptr++;
} while (++x < w);
RowDone:
;
}
}
void init_random_world(void){
rb->memset(cells, 0, length_in_bytes);
int i, j;
for(i = 0; i < XDIM; i++){
for(j = 0; j < YDIM; j++){
int r = rb->rand()%100;
if (r < 25){
set_cell(i, j);
}
}
}
}
void init_empty_world(void){
rb->memset(cells, 0, length_in_bytes);
}
int load_world(void){
unsigned const int h = XDIM;
unsigned const int w = YDIM;
rb->memset(cells, 0, length_in_bytes);
rb->memset(temp_cells, 0, length_in_bytes);
int fd = 0;
unsigned char buffer[w];
rb->memset(buffer, 0, w);
if ((fd = rb->open(WORLDS_DIR, O_RDONLY)) < 0) {
rb->splash(HZ*3, true, "Unable to open %s", WORLDS_DIR);
return -1;
}
unsigned int len = rb->read_line(fd, buffer, sizeof(buffer));
rb->memset(cells, 0, length_in_bytes);
unsigned int i = 0;
for (i = 0; i < h; i++){
if (buffer[0] == '\0') break;
unsigned int j;
for (j = 0; j < w; j++){
if ((buffer[j] == '\0')) break;
if (buffer[j] == '1') set_cell(i, j);
}
rb->memset(buffer, 0x0A, w);
len = rb->read_line(fd, buffer, sizeof(buffer));
}
return 1;
}
void set_edit_viewport(void){
if (in_edit == true){
x_top = x_edit - XDIM/(zoom*2);
y_top = y_edit - YDIM/(zoom*2);
if (x_top < 0) x_top = 0;
if (y_top < 0) y_top = 0;
if (x_top > (XDIM*(zoom - 1)/(zoom))) x_top = XDIM*(zoom - 1)/zoom;
if (y_top > (YDIM*(zoom - 1)/(zoom))) y_top = YDIM*(zoom - 1)/zoom;
return;
}
}
void set_viewport(int direction, int delta){
if (zoom == 1){
x_top = 0;
y_top = 0;
} else {
switch (direction){
case 1: /* north */
x_top -= delta;
if (x_top < 0) x_top = 0;
break;
case 2: /* east */
y_top += delta;
if (y_top > (YDIM*(zoom - 1)/(zoom))) y_top = YDIM*(zoom - 1)/zoom;
break;
case 3: /* south */
x_top += delta;
if (x_top > (XDIM*(zoom - 1)/(zoom))) x_top = XDIM*(zoom - 1)/zoom;
break;
case 4: /* west */
y_top -= delta;
if (y_top < 0) y_top = 0;
break;
case 0: /*reposition after zoom */
if (zoom == 3){
x_top += XDIM/12;
y_top += YDIM/12;
} else { /* zoom == 2 */
if (zoom_direction == true){
x_top -= XDIM/12;
y_top -= YDIM/12;
} else {
x_top = XDIM/4;
y_top = YDIM/4;
}
}
if ((y_top + YDIM/zoom) > YDIM) y_top = YDIM*(zoom - 1)/zoom;
if ((x_top + XDIM/zoom) > XDIM) x_top = XDIM*(zoom - 1)/zoom;
default:
break;
}
}
}
void display(void){
rb->lcd_clear_display();
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
if (zoom == 1){
rb->cpu_boost(true);
}
#endif
int i, j;
for(i = x_top; i < (XDIM/zoom + 4) + x_top; i++){
for(j = y_top; j < (YDIM/zoom + 4) + y_top; j++){
if(cell_state(i, j) == true){
rb->lcd_fillrect((j - y_top)*zoom, (i - x_top)*zoom, zoom, zoom);
}
}
}
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
if (zoom == 1){
rb->cpu_boost(false);
}
#endif
if (in_edit == true){
if (blink > 3){
if(cell_state(x_edit, y_edit) == true){
rb->lcd_fillrect((y_edit - y_top)*zoom - 1, (x_edit - x_top)*zoom - 1, zoom+2, zoom+2);
}else{
rb->lcd_fillrect((y_edit - y_top)*zoom, (x_edit - x_top)*zoom, zoom, zoom);
}
} else {
rb->lcd_fillrect((y_edit - y_top)*zoom + 1, (x_edit - x_top)*zoom + 1, zoom/2, zoom/2);
}
blink = (blink + 1)%6;
}
rb->lcd_update();
}
void edit_mode(void){
x_edit = x_top + XDIM/(zoom*2);
y_edit = y_top + YDIM/(zoom*2);
set_edit_viewport();
short old_zoom = zoom;
zoom = 4;
rb->splash(HZ, true, "Edit mode");
display();
int btn;
short speed = 5;
for(;;){
btn = rb->button_get(false);
switch(btn){
case GOL_UP:
x_edit--;
break;
case GOL_DOWN:
x_edit++;
break;
case GOL_RIGHT:
y_edit++;
break;
case GOL_LEFT:
y_edit--;
break;
case GOL_SELECT:
if(cell_state(x_edit, y_edit) == true){
clear_cell(x_edit, y_edit);
} else {
set_cell(x_edit, y_edit);
}
break;
case GOL_PLAY:
zoom = old_zoom;
in_edit = false;
return;
break;
default:
display();
rb->sleep(HZ/speed);
break;
}
if (x_edit < 0) x_edit = 0;
if (x_edit >= XDIM) x_edit = XDIM - 1;
if (y_edit < 0) y_edit = 0;
if (y_edit >= YDIM) y_edit = YDIM - 1;
set_edit_viewport();
}
}
int game_start(void){
game_started = true;
int btn;
for (;;){
btn = rb->button_get(false);
if (in_edit == true){
btn = GOL_EDIT;
}
switch(btn){
case GOL_EDIT:
in_edit = true;
play = false;
edit_mode();
set_viewport(0,0);
display();
break;
case GOL_SELECT:
if (zoom_direction == true){
zoom--;
if (zoom == 1) zoom_direction = false;
} else {
zoom++;
if (zoom == 3) zoom_direction = true;
}
set_viewport(0,0);
display();
break;
case GOL_PLAY:
if(play == true){
play = false;
rb->splash(HZ/2, true, "Pause");
}else{
play = true;
rb->splash(HZ/2, true, "Go!");
}
break;
case GOL_QUIT:
return 1;
break;
case GOL_UP:
set_viewport(1, 10);
display();
break;
case GOL_DOWN:
set_viewport(3, 10);
display();
break;
case GOL_RIGHT:
set_viewport(2, 10);
display();
break;
case GOL_LEFT:
set_viewport(4, 10);
display();
break;
default:
break;
}
if(play == true){
game_step();
display();
rb->sleep(HZ/sim_speed);
}else{
rb->sleep(HZ/2);
}
}
return 0;
}
int menu_mode(void){
/* "stolen" from the wonderful chopper game by Ben Basha et. al. */
int m;
int result;
int choice = 2;
bool menu_quit = false;
int sim_speed_arj[5] = {1, 5, 10, 20, 1000};
sim_speed = sim_speed_arj[choice];
static const struct menu_item items[] = {
{ "Random world", NULL },
{ "Empty world", NULL },
{ "Load world", NULL},
{ "Resume", NULL},
{ "Set speed", NULL},
{ "Help", NULL },
{ "Quit", NULL },
};
static const struct opt_items speed[5] = {
{ "very slow", -1 },
{ "slow", -1 },
{ "medium", -1},
{ "fast", -1},
{ "blazing", -1},
};
m = rb->menu_init(items, sizeof(items) / sizeof(*items),
NULL, NULL, NULL, NULL);
while (!menu_quit) {
result=rb->menu_show(m);
switch (result){
case 0: /* Start random world */
menu_quit = false;
init_random_world();
display();
game_start();
break;
case 1: /* Start empty world, go to edit mode */
menu_quit = false;
in_edit = true;
init_empty_world();
game_start();
break;
case 2:
load_world();
display();
play = false;
game_start();
break;
case 3:
if (game_started == true){
display();
game_start();
} else {
rb->splash(HZ*3, true, "Start a game first");
}
break;
case 4:
rb->set_option("Set speed", &choice, INT, speed, 5, NULL);
sim_speed = sim_speed_arj[choice];
break;
case 5:
rb->splash(HZ*3, true, "No help aviable yet, sorry");
break;
case 6:
return PLUGIN_OK;
break;
default:
break;
}
}
return PLUGIN_OK;
}
enum plugin_status plugin_start(struct plugin_api* api, void* parameter){
(void)parameter;
rb = api;
rb->srand(*rb->current_tick);
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
menu_mode();
return PLUGIN_OK;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment