Created
September 17, 2012 11:31
-
-
Save beniwohli/3736791 to your computer and use it in GitHub Desktop.
A Game Of Life implementation for Rockbox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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