|
It is currently Wed Jul 06, 2011 6:29 pm
|
View unanswered posts | View active topics
 |
|
 |
|
| Author |
Message |
|
Stephanie
|
Post subject: Game of Life Watch - Stephanie's Version  Posted: Wed Mar 02, 2011 8:51 pm |
Joined: Tue Feb 15, 2011 6:42 pm Posts: 26 Location: Ontario, Canada
|
This is my version of Conway's Game of Life implemented on the inPulse. Mainly I wanted to be able to tell time along with enjoying the GoL patterns. Consequently, there are two 'modes' - a LowPower mode which just displays a small time & date text on a dimmed screen, and the 'Game of Life' mode which displays a GoL grid along with the current time. In LowPower mode, the display consists of HH:MM & YYYY MM DD which is updated every minute. Tapping the button will switch to Game of Life mode. In Game of Life mode, most of the display is taken up by the GoL grid, with the time displayed beneath in HH:MM:SS format. Time is updated every second. Tapping the button will randomly re-seed the GoL grid. Holding the button (for 2 seconds) will switch to LowPower mode. While in the Game of Life mode, the watch will automatically randomly re-seed the grid if if senses that the grid has either completely died, or has reached a static / unchanging state. Note: I've implemented the grid as a 24x24 cell 'world' with each cell 4x4 pixels (96 x 96 pixels in total). I treat the world as a finite square rather than a torus, so the cells do not wrap around at the edges. /** * * GOL Watch * * Watch based on Conway's Game of Life: http://en.wikipedia.org/wiki/Conway's_Game_of_Life * * Features: * 'low power mode' displays plain text time & date, updates on the minute (HH:MM YYYY:MM:DD) * Game of Life Mode displays GoL grid, and time (HH:MM:SS) * To switch from low power mode to GoL mode, press & release button * To switch from GoL mode to lowpower mode, press & hold button for 2 seconds * To re-seed GoL grid, press & release button * GoL grid will automatically re-seed itself if it detects the 'Life' has died or * if it detects that 'Life' has entered a static mode. * due to memory constraints, GoL grid is 24x24 cells, each cell is 4x4 pixels (96x96 pixels total) * * Copyright (C) 2011, Stephanie Maksylewich [email protected] * http://planetstephanie.net/ * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * **/
#include <pulse_os.h> #include <pulse_types.h> #include <app_resources.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h>
struct pulse_time_tm current_time; struct PWTextBox time_text_box; struct PWidgetTextDynamic time_text_widget; static const color24_t GREEN = {0x00, 0xff, 0x00, 0};
void short_pulse(void); void update_time(void); void grid_randomize(void); void grid_update(void); void grid_draw(void); void cell_draw(int, int, int); uint8_t neighbors(int, int);
char time_text_buffer[16]; uint8_t last_sec, save_mode, button_pressed, same, dead; uint32_t button_pressed_time;
uint8_t curr_grid[24][24]; uint8_t next_grid[24][24];
void cell_draw(int x, int y, int n) { pulse_set_draw_window(x, y, x+3, y+3); for(int i=0; i<16; i++) { if(n) { pulse_draw_point24(GREEN); } else { pulse_draw_point24(COLOR_BLACK24); } } }
uint8_t neighbors(int x, int y) { int folks = 0; if(x!=23) { if((y!=0) && (curr_grid[(x+1)][y-1])) folks++; if(curr_grid[(x+1)][y]) folks++; if((y!=(23)) && (curr_grid[(x + 1)][(y + 1)])) folks++; } if((y!=0) && (curr_grid[x][(y - 1)])) folks++; if((y!=(23)) && (curr_grid[x][(y + 1)])) folks++;; if(x!=0) { if((y!=0) && (curr_grid[(x - 1)][(y - 1)])) folks++; if(curr_grid[(x - 1)][y]) folks++; if((y!=(23)) && (curr_grid[(x - 1)][(y + 1)])) folks++; } return folks; }
void grid_randomize() { int i, j; for(i=0; i<24; i++) { for(j=0; j<24; j++) { curr_grid[i][j] = 0; next_grid[i][j] = 0; if((rand() % 10) < 4) { next_grid[i][j] = 1; } } } dead=0; same=0; }
void grid_update() { int i, j; for(i=0; i<24; i++) { for(j=0; j<24; j++) { uint8_t near = neighbors(i, j); next_grid[i][j] = curr_grid[i][j]; if(curr_grid[i][j]) { if((near<2) || (near>3)) next_grid[i][j] = 0; } else { if(near==3) next_grid[i][j] = 1; } } } int samesame = 1; int livelive = 0; for(i=0; i<24; i++) { for(j=0; j<24; j++) { if(next_grid[i][j] == 1) livelive=1; if(next_grid[i][j] != curr_grid[i][j]) samesame=0; } } if(samesame == 1) same++; if(livelive == 0) dead++; if(dead > 20) grid_randomize(); if(same > 25) grid_randomize(); }
void grid_draw() { int i, j; for(i=0; i<24; i++) { for(j=0; j<24; j++) { int x = i * 4; int y = j * 4; cell_draw(x, y, next_grid[i][j]); curr_grid[i][j] = next_grid[i][j]; } } }
void short_pulse() { pulse_vibe_on(); pulse_mdelay(100); pulse_vibe_off(); }
void update_time() { if(save_mode) { pulse_blank_canvas(); printf("\n"); printf("\n"); printf(" %02d:%02d\n", current_time.tm_hour, current_time.tm_min); printf("\n"); printf("\n"); printf("\n"); int year = current_time.tm_year; if(year<2000) year += 1900; printf(" %04d-%02d-%02d\n", year, current_time.tm_mon+1, current_time.tm_mday); last_sec = current_time.tm_min; } else { pulse_set_draw_window(0, 100, 95, 127); for(int i=0; i<2688; i++) { pulse_draw_point24(COLOR_BLACK24); } sprintf(time_text_buffer, "%02d:%02d:%02d", current_time.tm_hour, current_time.tm_min, current_time.tm_sec); pulse_render_text(&time_text_box, &time_text_widget); last_sec = current_time.tm_sec; } }
void main_app_handle_button_down() { button_pressed = 1; button_pressed_time = pulse_get_millis(); if(save_mode) { pulse_blank_canvas(); // Initialize the text widgets pulse_oled_set_brightness(100); pulse_init_dynamic_text_widget(&time_text_widget, time_text_buffer, FONT_CLOCKOPIA_22, COLOR_WHITE24, (PWTS_TRUNCATE | PWTS_CENTER)); save_mode = 0; update_time(); short_pulse(); } }
void main_app_handle_button_up() { button_pressed = 0; if(!save_mode) { grid_randomize(); grid_draw(); } }
void main_app_loop() { pulse_get_time_date(¤t_time);
if(save_mode) { if(last_sec != current_time.tm_min) update_time(); return; }
if(button_pressed) { if((pulse_get_millis() - button_pressed_time) > 2000) { button_pressed=0; pulse_oled_set_brightness(25); save_mode=1; short_pulse(); update_time(); return; } } else { grid_update(); grid_draw(); }
if(last_sec != current_time.tm_sec) update_time(); }
void main_app_init() { pulse_oled_set_brightness(25); save_mode=1; last_sec=99; button_pressed = 0;
time_text_box.top = 100; time_text_box.bottom = SCREEN_HEIGHT - 1; time_text_box.left = 0; time_text_box.right = SCREEN_WIDTH - 1;
sprintf(time_text_buffer, "");
update_time();
srand(current_time.tm_sec); }
void main_app_handle_doz() { }
void main_app_handle_hardware_update(enum PulseHardwareEvent event) { }In the resources folder, you'll need to add Clockopia.ttf, clockopia_22, 22, y, n to your fonts.txt file, and copy the clocktopia.ttf font from the PingStat example. Cheers!
| Attachments: |
File comment: Game of Life mode on the left, LowPower mode on the right.
GoL_Watch.jpg [ 45.91 KiB | Viewed 399 times ]
|
|
|
|
|
 |
|
Duane
|
Post subject: Re: Game of Life Watch - Stephanie's Version  Posted: Wed Mar 02, 2011 10:02 pm |
Joined: Mon Feb 21, 2011 10:03 am Posts: 26 Location: San Diego, CA
|
|
Very nice!
With apparently three independent implementations of CGOL floating around, I think we can declare the inPulse the "Official Watch Of Life".
|
|
|
|
 |
|
hudson
|
Post subject: Re: Game of Life Watch - Stephanie's Version  Posted: Thu Mar 03, 2011 7:12 pm |
Joined: Sun Feb 27, 2011 11:32 am Posts: 71
|
|
I love the game display! It's a very pleasing, eye catching watch face.
One problem I have is that the HH:MM:SS display works for a while, but sometimes after coming back from the low-power mode the time display is no longer present below the game. If I reflash the inpulse_resources.bin file it reappears.
Another two very minor bugs is that when it is displayed, the big time flickers slightly when drawing the seconds, and the font appears to not be monospace (1 is a few pixels narrower than the other numbers).
|
|
|
|
 |
|
mikeatroundhere
|
Post subject: Re: Game of Life Watch - Stephanie's Version  Posted: Thu Mar 03, 2011 8:18 pm |
Joined: Sat Feb 19, 2011 9:14 am Posts: 7
|
|
Great stuff Stephanie!
This is very slick!
|
|
|
|
 |
|
Eric
|
Post subject: Re: Game of Life Watch - Stephanie's Version  Posted: Thu Mar 03, 2011 8:26 pm |
Joined: Mon Feb 14, 2011 7:07 pm Posts: 172
|
|
Amazing app, Stephanie. Looks beautiful.
Again Stephanie, I want to apologize for the hassle in shipping et al. I'm extraordinarily happy that you got your watch and have been producing these amazing apps!
_________________ ---
Lead designer of inPulse
|
|
|
|
 |
|
Stephanie
|
Post subject: Re: Game of Life Watch - Stephanie's Version  Posted: Fri Mar 04, 2011 4:39 am |
Joined: Tue Feb 15, 2011 6:42 pm Posts: 26 Location: Ontario, Canada
|
Thanks for the feedback! Hudson, I've noticed some of those problems as well. The flickering is due to having to 'blank' the lower part of the screen before printing the updated time. It's a pesky thing but I haven't figured out a simpler & more elegant way to do it. Unfortunately if you don't black out the old time then the new time appears 'ontop' so the numbers all overwrite each other. I do plan to find a nice monospace font when I get a chance, the way the readout moves about irritates me as well.  At the moment I'm working on the bigger 'bug'... trying to get the low power mode to actually be really low power. I tried using the watch yesterday and 'low power mode' emptied the battery in about 3 1/2 hours. I will do an update once I've resolved these issues. Eric - thank you; sorry about my frustrations it's just the shipping thing I guess is one of my 'pet peeves'. Cheers!
|
|
|
|
 |
|
hudson
|
Post subject: Re: Game of Life Watch - Stephanie's Version  Posted: Fri Mar 04, 2011 7:03 pm |
Joined: Sun Feb 27, 2011 11:32 am Posts: 71
|
Stephanie wrote: Note: I've implemented the grid as a 24x24 cell 'world' with each cell 4x4 pixels (96 x 96 pixels in total). I treat the world as a finite square rather than a torus, so the cells do not wrap around at the edges.
This version increases the resolution to allow for a full 96x96 view of the world, with a cylinder instead of just the square. Doing a full torus would require stashing an extra row. I'm working on fixing the flicker by printing only the single digits that have changed each time, which also fixes the monospace issue. /** * * GOL Watch * * Watch based on Conway's Game of Life: http://en.wikipedia.org/wiki/Conway's_Game_of_Life * * Features: * 'low power mode' displays plain text time & date, updates on the minute (HH:MM YYYY:MM:DD) * Game of Life Mode displays GoL grid, and time (HH:MM:SS) * To switch from low power mode to GoL mode, press & release button * To switch from GoL mode to lowpower mode, press & hold button for 2 seconds * To re-seed GoL grid, press & release button * GoL grid will automatically re-seed itself if it detects the 'Life' has died or * if it detects that 'Life' has entered a static mode. * due to memory constraints, GoL grid is 24x24 cells, each cell is 4x4 pixels (96x96 pixels total) * * Copyright (C) 2011, Stephanie Maksylewich [email protected] * http://planetstephanie.net/ * and Trammell Hudson <[email protected]> * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * **/
#include <pulse_os.h> #include <pulse_types.h> #include <app_resources.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
struct pulse_time_tm current_time; struct PWTextBox time_text_box; struct PWidgetTextDynamic time_text_widget; static color24_t COLOR_GREEN24 = { 0, 0xFF, 0, 0 };
void short_pulse(void); void update_time(void); void grid_randomize(void); void grid_update(void); uint8_t neighbors(unsigned, unsigned);
char time_text_buffer[16]; uint8_t last_sec, save_mode, button_pressed, same; uint32_t button_pressed_time; uint16_t iter;
#define GAME_GRID (SCREEN_WIDTH)
uint8_t cur_grid[GAME_GRID][GAME_GRID / 8]; uint8_t temp_row[2][GAME_GRID / 8]; // 0 == current row, 1 == previous row uint8_t cur_row;
/** Check the current row, using the temp_row for the one that is currently * being processed. */ static unsigned alive( unsigned x, unsigned y ) { // Allow the sides to wrap around if (y == GAME_GRID + 1) y = 0; else if (y == (unsigned) - 1) y = GAME_GRID - 1;
// If we are processing row x right now, retrieve the current // value from temp_row[0]; uint8_t * row; if (x == cur_row) row = temp_row[0]; else if (x == cur_row - 1) row = temp_row[1]; else row = cur_grid[x];
uint8_t cell = row[y >> 3]; uint8_t bit = 1 << (y & 7); uint8_t status = cell & bit; return status; }
/** Update the row in place */ static void mark( unsigned x, unsigned y, unsigned status ) { if (x >= GAME_GRID || y >= GAME_GRID) return;
uint8_t * cell = &cur_grid[x][y >> 3]; uint8_t bit = 1 << (y & 7); if (status) *cell |= bit; else *cell &= ~bit; }
uint8_t neighbors(unsigned x, unsigned y) { int folks = 0; if (alive(x-1, y-1)) folks++; if (alive(x+0, y-1)) folks++; if (alive(x+1, y-1)) folks++; if (alive(x-1, y-0)) folks++; if (alive(x+1, y-0)) folks++; if (alive(x-1, y+1)) folks++; if (alive(x+0, y+1)) folks++; if (alive(x+1, y+1)) folks++;
return folks; }
void grid_randomize() { unsigned i, j; for(i=0; i<GAME_GRID; i++) for(j=0; j<GAME_GRID; j++) mark(i, j, (rand() % 10) < 4); iter = 0; }
void grid_update() { unsigned i, j; memset(temp_row[1], 0, sizeof(temp_row[1])); pulse_set_draw_window(0, 0, GAME_GRID-1, GAME_GRID-1);
for(i=0; i<GAME_GRID; i++) { cur_row = i; memcpy(temp_row[0], cur_grid[i], sizeof(temp_row[0]));
for(j=0; j<GAME_GRID; j++) { uint8_t near = neighbors(i, j); uint8_t still_alive = 0;
if(alive(i, j)) still_alive = (near == 2 || near == 3); else still_alive = (near == 3);
mark(i, j, still_alive);
if (still_alive) pulse_draw_point24(COLOR_GREEN24); else pulse_draw_point24(COLOR_BLACK24); }
memcpy(temp_row[1], temp_row[0], sizeof(temp_row[0])); }
if (iter++ > 1024) grid_randomize(); }
void short_pulse() { pulse_vibe_on(); pulse_mdelay(100); pulse_vibe_off(); }
void update_time() { if(save_mode) { pulse_blank_canvas(); printf("\n"); printf("\n"); printf(" %02d:%02d\n", current_time.tm_hour, current_time.tm_min); printf("\n"); printf("\n"); printf("\n"); int year = current_time.tm_year; if(year<2000) year += 1900; printf(" %04d-%02d-%02d\n", year, current_time.tm_mon+1, current_time.tm_mday); last_sec = current_time.tm_min; } else { pulse_set_draw_window(0, 100, 95, 127); for(int i=0; i<2688; i++) { pulse_draw_point24(COLOR_BLACK24); } sprintf(time_text_buffer, "%02d:%02d:%02d", current_time.tm_hour, current_time.tm_min, current_time.tm_sec); pulse_render_text(&time_text_box, &time_text_widget); last_sec = current_time.tm_sec; } }
void main_app_handle_button_down() { button_pressed = 1; button_pressed_time = pulse_get_millis(); if(save_mode) { pulse_blank_canvas(); // Initialize the text widgets pulse_oled_set_brightness(100); pulse_init_dynamic_text_widget(&time_text_widget, time_text_buffer, FONT_CLOCKOPIA_22, COLOR_WHITE24, (PWTS_TRUNCATE | PWTS_CENTER)); save_mode = 0; update_time(); short_pulse(); } }
void main_app_handle_button_up() { button_pressed = 0; if(!save_mode) { grid_randomize(); } }
void main_app_loop() { pulse_get_time_date(¤t_time);
if(save_mode) { if(last_sec != current_time.tm_min) update_time(); return; }
if(button_pressed) { if((pulse_get_millis() - button_pressed_time) > 2000) { button_pressed=0; pulse_oled_set_brightness(25); save_mode=1; short_pulse(); update_time(); return; } } else { grid_update(); }
if(last_sec != current_time.tm_sec) update_time(); }
void main_app_init() { pulse_oled_set_brightness(25); save_mode=1; last_sec=99; button_pressed = 0;
time_text_box.top = 100; time_text_box.bottom = SCREEN_HEIGHT - 1; time_text_box.left = 0; time_text_box.right = SCREEN_WIDTH - 1;
sprintf(time_text_buffer, "");
update_time();
srand(current_time.tm_sec); }
void main_app_handle_doz() { }
void main_app_handle_hardware_update(enum PulseHardwareEvent event) { }
|
|
|
|
 |
|
|
 |
|
 |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum
|
|