diff options
author | Franklin Wei <frankhwei536@gmail.com> | 2015-04-01 18:15:29 -0400 |
---|---|---|
committer | Gerrit Rockbox <gerrit@rockbox.org> | 2015-04-19 17:48:12 +0200 |
commit | d8ee5fcfc4282b4ed2cc002e3c11ce9bf5c44572 (patch) | |
tree | f711fee8773e9fea0a9ea409aeff5702990c4cef | |
parent | 24533635c0c47964db1e17d70655fa4161be5fba (diff) | |
download | rockbox-d8ee5fc.tar.gz rockbox-d8ee5fc.zip |
2048: Cleanup
- more whitespace to enhance readability
- better/fixed/more comments ;)
- some minor optimizations
- general code cleanup
Change-Id: I2b5f69aba0f83f989abb2c636920646e4315583f
-rw-r--r-- | apps/plugins/2048.c | 684 |
1 files changed, 391 insertions, 293 deletions
diff --git a/apps/plugins/2048.c b/apps/plugins/2048.c index 7c2fd85668..15d3f41191 100644 --- a/apps/plugins/2048.c +++ b/apps/plugins/2048.c @@ -46,50 +46,49 @@ #include "pluginbitmaps/_2048_tiles.h" #endif -/* defines */ +/* some constants */ -#define ANIM_SLEEPTIME (HZ/20) +static const int ANIM_SLEEPTIME = (HZ/20); +static const int NUM_STARTING_TILES = 2; +static const int VERT_SPACING = 4; +static const int WHAT_FONT = FONT_UI; +static const unsigned int WINNING_TILE = 2048; + +/* must use macros for these */ #define GRID_SIZE 4 #define HISCORES_FILE PLUGIN_GAMES_DATA_DIR "/2048.score" +#define MIN_SPACE (BMPHEIGHT__2048_tiles * 0.134) #define NUM_SCORES 5 -#define NUM_STARTING_TILES 2 #define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/2048.save" -#define WHAT_FONT FONT_UI -#define SPACES (GRID_SIZE*GRID_SIZE) -#define MIN_SPACE (BMPHEIGHT__2048_tiles*0.134) /* space between tiles */ -#define MAX_UNDOS 64 -#define VERT_SPACING 4 -#define WINNING_TILE 2048 +#define SPACES (GRID_SIZE * GRID_SIZE) /* screen-specific configuration */ -#if LCD_WIDTH<LCD_HEIGHT -/* tall screens */ -#define TITLE_X 0 -#define TITLE_Y 0 -#define BASE_Y (BMPHEIGHT__2048_tiles*1.5) -#define BASE_X (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) -#define SCORE_X 0 -#define SCORE_Y (max_numeral_height) -#define BEST_SCORE_X 0 -#define BEST_SCORE_Y (2*max_numeral_height) -#else -/* wide screens or square screens*/ -#define TITLE_X 0 -#define TITLE_Y 0 -#define BASE_X (LCD_WIDTH-(GRID_SIZE*BMPHEIGHT__2048_tiles)-(((GRID_SIZE+1)*MIN_SPACE))) -#define BASE_Y (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) -#define SCORE_X 0 -#define SCORE_Y (max_numeral_height) -#define BEST_SCORE_X 0 -#define BEST_SCORE_Y (2*max_numeral_height) -#endif /* LCD_WIDTH<LCD_HEIGHT */ - -#define BACKGROUND_X (int)(BASE_X-MIN_SPACE) -#define BACKGROUND_Y (int)(BASE_Y-MIN_SPACE) +#if (LCD_WIDTH < LCD_HEIGHT) /* tall screens */ +# define TITLE_X 0 +# define TITLE_Y 0 +# define BASE_Y (BMPHEIGHT__2048_tiles*1.5) +# define BASE_X (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) +# define SCORE_X 0 +# define SCORE_Y (max_numeral_height) +# define BEST_SCORE_X 0 +# define BEST_SCORE_Y (2*max_numeral_height) +#else /* wide or square screens */ +# define TITLE_X 0 +# define TITLE_Y 0 +# define BASE_X (LCD_WIDTH-(GRID_SIZE*BMPHEIGHT__2048_tiles)-(((GRID_SIZE+1)*MIN_SPACE))) +# define BASE_Y (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) +# define SCORE_X 0 +# define SCORE_Y (max_numeral_height) +# define BEST_SCORE_X 0 +# define BEST_SCORE_Y (2*max_numeral_height) +#endif /* LCD_WIDTH < LCD_HEIGHT */ -/* key mappings */ +/* where to draw the background bitmap */ +static const int BACKGROUND_X = (BASE_X-MIN_SPACE); +static const int BACKGROUND_Y = (BASE_Y-MIN_SPACE); +/* key mappings */ #define KEY_UP PLA_UP #define KEY_DOWN PLA_DOWN #define KEY_LEFT PLA_LEFT @@ -97,48 +96,58 @@ #define KEY_EXIT PLA_CANCEL #define KEY_UNDO PLA_SELECT +/* notice how "color" is spelled :P */ +#ifdef HAVE_LCD_COLOR + /* colors */ -#define BACKGROUND (LCD_RGBPACK(0xfa, 0xf8, 0xef)) -#define BOARD_BACKGROUND (LCD_RGBPACK(0xbb, 0xad, 0xa0)) -#define TEXT_COLOR (LCD_RGBPACK(0x77, 0x6e, 0x65)) +static const unsigned BACKGROUND = LCD_RGBPACK(0xfa, 0xf8, 0xef); +static const unsigned BOARD_BACKGROUND = LCD_RGBPACK(0xbb, 0xad, 0xa0); +static const unsigned TEXT_COLOR = LCD_RGBPACK(0x77, 0x6e, 0x65); + +#endif +/* PLA data */ static const struct button_mapping *plugin_contexts[] = { pla_main_ctx }; -/* game data */ +/*** game data structures ***/ + struct game_ctx_t { - int grid[GRID_SIZE][GRID_SIZE]; - int score; - int cksum; /* sum of grid, XORed by score */ - bool already_won; -}; + unsigned int grid[GRID_SIZE][GRID_SIZE]; /* 0 = empty */ + unsigned int score; + unsigned int cksum; /* sum of grid, XORed by score */ + bool already_won; /* has the player gotten 2048 yet? */ +} game_ctx; + +static struct game_ctx_t *ctx = &game_ctx; -static struct game_ctx_t ctx_data; -/* use a pointer to make save/load easier */ -static struct game_ctx_t *ctx=&ctx_data; +/*** temporary data ***/ -/* temporary data */ static bool merged_grid[GRID_SIZE][GRID_SIZE]; static int old_grid[GRID_SIZE][GRID_SIZE]; -static int max_numeral_height=-1; + +static int max_numeral_height = -1; + #if LCD_DEPTH <= 1 static int max_numeral_width; #endif -static bool loaded=false; -/* first init_game will set this, when it is exceeded, it will be updated in the slide functions */ -static int best_score; +static bool loaded = false; /* has a save been loaded? */ + +/* the high score */ +static unsigned int best_score; -static bool abnormal_exit=true; +static bool abnormal_exit = true; static struct highscore highscores[NUM_SCORES]; -/* returns a random int between min and max */ +/***************************** UTILITY FUNCTIONS *****************************/ + static inline int rand_range(int min, int max) { - return rb->rand()%(max-min+1)+min; + return rb->rand() % (max-min + 1) + min; } -/* prepares to exit */ +/* prepares for exit */ static void cleanup(void) { backlight_use_settings(); @@ -148,66 +157,77 @@ static void cleanup(void) static inline int rand_2_or_4(void) { /* 1 in 10 chance of a four */ - if(rb->rand()%10==0) + if(rb->rand() % 10 == 0) return 4; else return 2; } -/* display the help text */ +/* displays the help text */ static bool do_help(void) { + #ifdef HAVE_LCD_COLOR rb->lcd_set_foreground(LCD_WHITE); rb->lcd_set_background(LCD_BLACK); #endif + rb->lcd_setfont(FONT_UI); - char* help_text[]= {"2048", "", "Aim", - "", "Join", "the", "numbers", "to", "get", "to", "the", "2048", "tile!", "", "", - "How", "to", "Play", "", - "", "Use", "the", "directional", "keys", "to", "move", "the", "tiles.", "When", - "two", "tiles", "with", "the", "same", "number", "touch,", "they", "merge", "into", "one!"}; - struct style_text style[]={ - {0, TEXT_CENTER|TEXT_UNDERLINE}, - {2, C_RED}, - {15, C_RED}, {16, C_RED}, {17,C_RED}, + + static char* help_text[]= {"2048", "", "Aim", + "", "Join", "the", "numbers", "to", "get", "to", "the", "2048", "tile!", "", "", + "How", "to", "Play", "", + "", "Use", "the", "directional", "keys", "to", "move", "the", "tiles.", "When", + "two", "tiles", "with", "the", "same", "number", "touch,", "they", "merge", "into", "one!"}; + + struct style_text style[] = { + {0, TEXT_CENTER | TEXT_UNDERLINE}, + {2, C_RED}, + {15, C_RED}, + {16, C_RED}, + {17, C_RED}, LAST_STYLE_ITEM }; - return display_text(ARRAYLEN(help_text), help_text, style,NULL,true); -} -/*** the logic for sliding ***/ + return display_text(ARRAYLEN(help_text), help_text, style, NULL, true); +} -/* this is the helper function that does the actual tile moving */ +/*** tile movement logic ***/ +/* this function performs the tile movement */ static inline void slide_internal(int startx, int starty, int stopx, int stopy, int dx, int dy, int lookx, int looky, bool update_best) { - int best_score_before=best_score; - for(int y=starty;y!=stopy;y+=dy) + unsigned int best_score_old = best_score; + + /* loop over the rows or columns, moving the tiles in the specified direction */ + for(int y = starty; y != stopy; y += dy) { - for(int x=startx;x!=stopx;x+=dx) + for(int x = startx; x != stopx; x += dx) { - if(ctx->grid[x+lookx][y+looky]==ctx->grid[x][y] && ctx->grid[x][y] && !merged_grid[x+lookx][y+looky] && !merged_grid[x][y]) /* Slide into */ + if(ctx->grid[x + lookx][y + looky] == ctx->grid[x][y] && + ctx->grid[x][y] && + !merged_grid[x + lookx][y + looky] && + !merged_grid[x][y]) /* merge these two tiles */ { /* Each merged tile cannot be merged again */ - merged_grid[x+lookx][y+looky]=true; - ctx->grid[x+lookx][y+looky]=2*ctx->grid[x][y]; - ctx->score+=ctx->grid[x+lookx][y+looky]; - ctx->grid[x][y]=0; + merged_grid[x + lookx][y + looky] = true; + ctx->grid[x + lookx][y + looky] = 2 * ctx->grid[x][y]; + ctx->score += ctx->grid[x + lookx][y + looky]; + ctx->grid[x][y] = 0; } - else if(ctx->grid[x+lookx][y+looky]==0) /* Empty! */ + else if(ctx->grid[x + lookx][y + looky] == 0) /* Empty! */ { - ctx->grid[x+lookx][y+looky]=ctx->grid[x][y]; - ctx->grid[x][y]=0; + ctx->grid[x + lookx][y + looky] = ctx->grid[x][y]; + ctx->grid[x][y] = 0; } } } - if(ctx->score>best_score_before && update_best) - best_score=ctx->score; + if(ctx->score > best_score_old && update_best) + best_score = ctx->score; } /* these functions move each tile 1 space in the direction specified via calls to slide_internal */ @@ -227,6 +247,7 @@ static void up(bool update_best) 0, -1, /* lookahead values */ update_best); } + /* Down 0 v v v v 1 v v v v @@ -242,6 +263,7 @@ static void down(bool update_best) 0, 1, update_best); } + /* Left 0 < < < 1 < < < @@ -257,6 +279,7 @@ static void left(bool update_best) -1, 0, update_best); } + /* Right 0 > > > 1 > > > @@ -273,31 +296,44 @@ static void right(bool update_best) update_best); } -/* slightly modified version of base 2 log, returns 1 when given zero, and log2(n)+1 for anything else */ +/* copies old_grid to ctx->grid */ +static inline void RESTORE_GRID(void) +{ + memcpy(&ctx->grid, &old_grid, sizeof(ctx->grid)); +} +/* slightly modified base 2 logarithm, returns 1 when given zero, and log2(n) + 1 for anything else */ static inline int ilog2(int n) { - if(n==0) + if(n == 0) return 1; - int log=0; - while(n>1) + int log = 0; + while(n > 1) { - n>>=1; + n >>= 1; ++log; } - return log+1; + return log + 1; } + +/* low-depth displays resort to text drawing, see the #else case below */ + #if LCD_DEPTH > 1 + +/* draws game screen + updates LCD */ static void draw(void) { #ifdef HAVE_LCD_COLOR rb->lcd_set_background(BACKGROUND); #endif + rb->lcd_clear_display(); /* draw the background */ - rb->lcd_bitmap(_2048_background, BACKGROUND_X, BACKGROUND_Y, BMPWIDTH__2048_background, BMPWIDTH__2048_background); + rb->lcd_bitmap(_2048_background, + BACKGROUND_X, BACKGROUND_Y, + BMPWIDTH__2048_background, BMPWIDTH__2048_background); /* grey_gray_bitmap(_2048_background, BACKGROUND_X, BACKGROUND_Y, BMPWIDTH__2048_background, BMPHEIGHT__2048_background); @@ -305,290 +341,316 @@ static void draw(void) /* draw the grid */ - for(int y=0;y<GRID_SIZE;++y) + for(int y = 0; y < GRID_SIZE; ++y) { - for(int x=0;x<GRID_SIZE;++x) + for(int x = 0; x < GRID_SIZE; ++x) { - rb->lcd_bitmap_part(_2048_tiles, /* source */ - BMPWIDTH__2048_tiles-BMPHEIGHT__2048_tiles*ilog2(ctx->grid[x][y]), 0, /* source upper left corner */ - STRIDE(SCREEN_MAIN, BMPWIDTH__2048_tiles, BMPHEIGHT__2048_tiles), /* stride */ - (BMPHEIGHT__2048_tiles+MIN_SPACE)*x+BASE_X, (BMPHEIGHT__2048_tiles+MIN_SPACE)*y+BASE_Y, /* dest upper-left corner */ - BMPHEIGHT__2048_tiles, BMPHEIGHT__2048_tiles); /* size of the cut section */ + rb->lcd_bitmap_part(_2048_tiles, /* source */ + BMPWIDTH__2048_tiles - BMPHEIGHT__2048_tiles * ilog2(ctx->grid[x][y]), 0, /* source upper left corner */ + STRIDE(SCREEN_MAIN, BMPWIDTH__2048_tiles, BMPHEIGHT__2048_tiles), /* stride */ + (BMPHEIGHT__2048_tiles + MIN_SPACE) * x + BASE_X, (BMPHEIGHT__2048_tiles + MIN_SPACE) * y + BASE_Y, /* dest upper-left corner */ + BMPHEIGHT__2048_tiles, BMPHEIGHT__2048_tiles); /* size of the cut section */ } } + /* draw the title */ char buf[32]; + #ifdef HAVE_LCD_COLOR rb->lcd_set_foreground(TEXT_COLOR); #endif - rb->snprintf(buf, 31, "%d", WINNING_TILE); - /* check if the title will go into the grid */ + rb->snprintf(buf, sizeof(buf), "%d", WINNING_TILE); + + /* check if the title will overlap the grid */ int w, h; rb->lcd_setfont(FONT_UI); rb->font_getstringsize(buf, &w, &h, FONT_UI); - bool draw_title=true; - if(w+TITLE_X>=BACKGROUND_X && h+TITLE_Y>=BACKGROUND_Y) + bool draw_title = true; + if(w + TITLE_X >= BACKGROUND_X && h + TITLE_Y >= BACKGROUND_Y) { /* if it goes into the grid, use the system font, which should be smaller */ rb->lcd_setfont(FONT_SYSFIXED); rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); - if(w+TITLE_X>=BACKGROUND_X && h+TITLE_Y>=BACKGROUND_Y) + if(w + TITLE_X >= BACKGROUND_X && h + TITLE_Y >= BACKGROUND_Y) { /* title can't fit, don't draw it */ - draw_title=false; - h=0; + draw_title = false; + h = 0; } } + if(draw_title) rb->lcd_putsxy(TITLE_X, TITLE_Y, buf); - int score_y=TITLE_Y+h+VERT_SPACING; + + int score_y = TITLE_Y + h + VERT_SPACING; + /* draw the score */ - rb->snprintf(buf, 31, "Score: %d", ctx->score); + rb->snprintf(buf, sizeof(buf), "Score: %d", ctx->score); + #ifdef HAVE_LCD_COLOR rb->lcd_set_foreground(LCD_WHITE); rb->lcd_set_background(BOARD_BACKGROUND); #endif + rb->lcd_setfont(FONT_UI); rb->font_getstringsize(buf, &w, &h, FONT_UI); - if(w+SCORE_X>=BACKGROUND_X && h+SCORE_Y>=BACKGROUND_Y) + + /* try making the score fit */ + if(w + SCORE_X >= BACKGROUND_X && h + SCORE_Y >= BACKGROUND_Y) { /* score overflows */ /* first see if it fits with Score: and FONT_SYSFIXED */ rb->lcd_setfont(FONT_SYSFIXED); rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); - if(w+SCORE_X < BACKGROUND_X) + if(w + SCORE_X < BACKGROUND_X) /* it fits, go and draw it */ goto draw_lbl; /* now try with S: and FONT_UI */ - rb->snprintf(buf, 31, "S: %d", ctx->score); + rb->snprintf(buf, sizeof(buf), "S: %d", ctx->score); rb->font_getstringsize(buf, &w, &h, FONT_UI); rb->lcd_setfont(FONT_UI); - if(w+SCORE_X<BACKGROUND_X) + if(w + SCORE_X < BACKGROUND_X) goto draw_lbl; /* now try with S: and FONT_SYSFIXED */ - rb->snprintf(buf, 31, "S: %d", ctx->score); + rb->snprintf(buf, sizeof(buf), "S: %d", ctx->score); rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); rb->lcd_setfont(FONT_SYSFIXED); - if(w+SCORE_X<BACKGROUND_X) + if(w + SCORE_X < BACKGROUND_X) goto draw_lbl; /* then try without Score: and FONT_UI */ - rb->snprintf(buf, 31, "%d", ctx->score); + rb->snprintf(buf, sizeof(buf), "%d", ctx->score); rb->font_getstringsize(buf, &w, &h, FONT_UI); rb->lcd_setfont(FONT_UI); - if(w+SCORE_X<BACKGROUND_X) + if(w + SCORE_X < BACKGROUND_X) goto draw_lbl; /* as a last resort, don't use Score: and use the system font */ - rb->snprintf(buf, 31, "%d", ctx->score); + rb->snprintf(buf, sizeof(buf), "%d", ctx->score); rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); rb->lcd_setfont(FONT_SYSFIXED); - if(w+SCORE_X<BACKGROUND_X) + if(w + SCORE_X < BACKGROUND_X) goto draw_lbl; else goto skip_draw_score; } + draw_lbl: rb->lcd_putsxy(SCORE_X, score_y, buf); - score_y+=h+VERT_SPACING; + score_y += h + VERT_SPACING; + /* draw the best score */ skip_draw_score: - rb->snprintf(buf, 31, "Best: %d", best_score); + rb->snprintf(buf, sizeof(buf), "Best: %d", best_score); + #ifdef HAVE_LCD_COLOR rb->lcd_set_foreground(LCD_WHITE); rb->lcd_set_background(BOARD_BACKGROUND); #endif + rb->lcd_setfont(FONT_UI); rb->font_getstringsize(buf, &w, &h, FONT_UI); - if(w+BEST_SCORE_X>=BACKGROUND_X && h+BEST_SCORE_Y>=BACKGROUND_Y) + if(w + BEST_SCORE_X >= BACKGROUND_X && h + BEST_SCORE_Y >= BACKGROUND_Y) { /* score overflows */ /* first see if it fits with Score: and FONT_SYSFIXED */ rb->lcd_setfont(FONT_SYSFIXED); rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); - if(w+BEST_SCORE_X < BACKGROUND_X) + if(w + BEST_SCORE_X < BACKGROUND_X) /* it fits, go and draw it */ goto draw_best; /* now try with S: and FONT_UI */ - rb->snprintf(buf, 31, "B: %d", best_score); + rb->snprintf(buf, sizeof(buf), "B: %d", best_score); rb->font_getstringsize(buf, &w, &h, FONT_UI); rb->lcd_setfont(FONT_UI); - if(w+BEST_SCORE_X<BACKGROUND_X) + if(w + BEST_SCORE_X < BACKGROUND_X) goto draw_best; /* now try with S: and FONT_SYSFIXED */ - rb->snprintf(buf, 31, "B: %d", best_score); + rb->snprintf(buf, sizeof(buf), "B: %d", best_score); rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); rb->lcd_setfont(FONT_SYSFIXED); - if(w+BEST_SCORE_X<BACKGROUND_X) + if(w + BEST_SCORE_X < BACKGROUND_X) goto draw_best; /* then try without Score: and FONT_UI */ - rb->snprintf(buf, 31, "%d", best_score); + rb->snprintf(buf, sizeof(buf), "%d", best_score); rb->font_getstringsize(buf, &w, &h, FONT_UI); rb->lcd_setfont(FONT_UI); - if(w+BEST_SCORE_X<BACKGROUND_X) + if(w + BEST_SCORE_X < BACKGROUND_X) goto draw_best; /* as a last resort, don't use Score: and use the system font */ - rb->snprintf(buf, 31, "%d", best_score); + rb->snprintf(buf, sizeof(buf), "%d", best_score); rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); rb->lcd_setfont(FONT_SYSFIXED); - if(w+BEST_SCORE_X<BACKGROUND_X) + if(w + BEST_SCORE_X < BACKGROUND_X) goto draw_best; else goto skip_draw_best; } draw_best: rb->lcd_putsxy(BEST_SCORE_X, score_y, buf); + skip_draw_best: rb->lcd_update(); - /* revert the font back */ + + /* revert the font */ rb->lcd_setfont(WHAT_FONT); } + #else /* LCD_DEPTH > 1 */ + /* 1-bit display :( */ -/* bitmaps are unreadable with these screens, so just resort to text */ +/* bitmaps are unreadable on these screens, so just resort to text-based drawing */ static void draw(void) { rb->lcd_clear_display(); + /* Draw the grid */ /* find the biggest tile */ - int biggest_tile=-1; - for(int x=0;x<GRID_SIZE;++x) + unsigned int biggest_tile = 0; + for(int x = 0; x < GRID_SIZE; ++x) { - for(int y=0;y<GRID_SIZE;++y) - if(ctx->grid[x][y]>biggest_tile) - biggest_tile=ctx->grid[x][y]; + for(int y = 0; y < GRID_SIZE; ++y) + if(ctx->grid[x][y] > biggest_tile) + biggest_tile = ctx->grid[x][y]; } - char str[32]; - rb->snprintf(str, 31,"%d", biggest_tile); - int biggest_tile_width=rb->strlen(str)*rb->font_get_width(rb->font_get(WHAT_FONT), '0')+MIN_SPACE; - for(int y=0;y<GRID_SIZE;++y) + + char buf[32]; + + rb->snprintf(buf, 32, "%d", biggest_tile); + + int biggest_tile_width = rb->strlen(buf) * rb->font_get_width(rb->font_get(WHAT_FONT), '0') + MIN_SPACE; + + for(int y = 0; y < GRID_SIZE; ++y) { - for(int x=0;x<GRID_SIZE;++x) + for(int x = 0; x < GRID_SIZE; ++x) { if(ctx->grid[x][y]) { - if(ctx->grid[x][y]>biggest_tile) - biggest_tile=ctx->grid[x][y]; - rb->snprintf(str,31,"%d", ctx->grid[x][y]); - rb->lcd_putsxy(biggest_tile_width*x,y*max_numeral_height+max_numeral_height,str); + if(ctx->grid[x][y] > biggest_tile) + biggest_tile = ctx->grid[x][y]; + rb->snprintf(buf, 32, "%d", ctx->grid[x][y]); + rb->lcd_putsxy(biggest_tile_width * x, y * max_numeral_height + max_numeral_height, buf); } } } + /* Now draw the score, and the game title */ - rb->snprintf(str, 31, "Score: %d", ctx->score); - int str_width, str_height; - rb->font_getstringsize(str, &str_width, &str_height, WHAT_FONT); - int score_leftmost=LCD_WIDTH-str_width-1; + rb->snprintf(buf, 32, "Score: %d", ctx->score); + int buf_width, buf_height; + rb->font_getstringsize(buf, &buf_width, &buf_height, WHAT_FONT); + + int score_leftmost = LCD_WIDTH - buf_width - 1; /* Check if there is enough space to display "Score: ", otherwise, only display the score */ - if(score_leftmost>=0) - rb->lcd_putsxy(score_leftmost,0,str); + if(score_leftmost >= 0) + rb->lcd_putsxy(score_leftmost, 0, buf); else - rb->lcd_putsxy(score_leftmost,0,str+rb->strlen("Score: ")); - /* Reuse the same string for the title */ + rb->lcd_putsxy(score_leftmost, 0, buf + rb->strlen("Score: ")); + + rb->snprintf(buf, 32, "%d", WINNING_TILE); + rb->font_getstringsize(buf, &buf_width, &buf_height, WHAT_FONT); + if(buf_width < score_leftmost) + rb->lcd_putsxy(0, 0, buf); - rb->snprintf(str, 31, "%d", WINNING_TILE); - rb->font_getstringsize(str, &str_width, &str_height, WHAT_FONT); - if(str_width<score_leftmost) - rb->lcd_putsxy(0,0,str); rb->lcd_update(); } + #endif /* LCD_DEPTH > 1 */ + /* place a 2 or 4 in a random empty space */ static void place_random(void) { - int xpos[SPACES],ypos[SPACES]; - int back=0; + int xpos[SPACES], ypos[SPACES]; + int back = 0; /* get the indexes of empty spaces */ - for(int y=0;y<GRID_SIZE;++y) - for(int x=0;x<GRID_SIZE;++x) + for(int y = 0; y < GRID_SIZE; ++y) + for(int x = 0; x < GRID_SIZE; ++x) { if(!ctx->grid[x][y]) { - xpos[back]=x; - ypos[back]=y; - ++back; + xpos[back] = x; + ypos[back++] = y; } } + if(!back) /* no empty spaces */ return; - int idx=rand_range(0,back-1); - ctx->grid[xpos[idx]][ypos[idx]]=rand_2_or_4(); -} -/* copies old_grid to ctx->grid */ -static void restore_old_grid(void) -{ - memcpy(&ctx->grid, &old_grid, sizeof(int)*SPACES); + int idx = rand_range(0, back - 1); + ctx->grid[ xpos[idx] ][ ypos[idx] ] = rand_2_or_4(); } /* checks for a win or loss */ static bool check_gameover(void) { /* first, check for a loss */ - int oldscore=ctx->score; - bool have_legal_move=false; - memset(&merged_grid,0,SPACES*sizeof(bool)); + int oldscore = ctx->score; + bool have_legal_move = false; + + memset(&merged_grid, 0, SPACES * sizeof(bool)); up(false); - if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) + if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid))) { - restore_old_grid(); - ctx->score=oldscore; - have_legal_move=true; + RESTORE_GRID(); + ctx->score = oldscore; + have_legal_move = true; } - restore_old_grid(); - memset(&merged_grid,0,SPACES*sizeof(bool)); + RESTORE_GRID(); + + memset(&merged_grid, 0, SPACES * sizeof(bool)); down(false); - if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) + if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid))) { - restore_old_grid(); - ctx->score=oldscore; - have_legal_move=true; + RESTORE_GRID(); + ctx->score = oldscore; + have_legal_move = true; } - restore_old_grid(); - memset(&merged_grid,0,SPACES*sizeof(bool)); + RESTORE_GRID(); + + memset(&merged_grid, 0, SPACES * sizeof(bool)); left(false); - if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) + if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid))) { - restore_old_grid(); - ctx->score=oldscore; - have_legal_move=true; + RESTORE_GRID(); + ctx->score = oldscore; + have_legal_move = true; } - restore_old_grid(); - memset(&merged_grid,0,SPACES*sizeof(bool)); + RESTORE_GRID(); + + memset(&merged_grid, 0, SPACES * sizeof(bool)); right(false); - if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) + if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid))) { - restore_old_grid(); - ctx->score=oldscore; - have_legal_move=true; + RESTORE_GRID(); + ctx->score = oldscore; + have_legal_move = true; } - ctx->score=oldscore; + ctx->score = oldscore; if(!have_legal_move) { /* no more legal moves */ - draw(); /* Shame the player :) */ + draw(); /* Shame the player */ rb->splash(HZ*2, "Game Over!"); return true; } - for(int y=0;y<GRID_SIZE;++y) + + for(int y = 0;y < GRID_SIZE; ++y) { - for(int x=0;x<GRID_SIZE;++x) + for(int x = 0; x < GRID_SIZE; ++x) { - if(ctx->grid[x][y]==WINNING_TILE && !ctx->already_won) + if(ctx->grid[x][y] == WINNING_TILE && !ctx->already_won) { /* Let the user see the tile in its full glory... */ draw(); - ctx->already_won=true; + ctx->already_won = true; rb->splash(HZ*2,"You win!"); - /* don't let the user quit here :) */ } } } @@ -602,38 +664,43 @@ static void load_hs(void) if(rb->file_exists(HISCORES_FILE)) highscore_load(HISCORES_FILE, highscores, NUM_SCORES); else - memset(highscores, 0, sizeof(struct highscore)*NUM_SCORES); + memset(highscores, 0, sizeof(struct highscore) * NUM_SCORES); } /* initialize the data structures */ static void init_game(bool newgame) { - best_score=highscores[0].score; + best_score = highscores[0].score; if(loaded && ctx->score > best_score) - best_score=ctx->score; + best_score = ctx->score; + if(newgame) { /* initialize the game context */ - memset(ctx->grid, 0, sizeof(int)*SPACES); - for(int i=0;i<NUM_STARTING_TILES;++i) + memset(ctx->grid, 0, sizeof(ctx->grid)); + for(int i = 0; i < NUM_STARTING_TILES; ++i) { place_random(); } - ctx->score=0; - ctx->already_won=false; + ctx->score = 0; + ctx->already_won = false; } + /* using the menu resets the font */ /* set it again here */ + rb->lcd_setfont(WHAT_FONT); + /* Now calculate font sizes */ /* Now get the height of the font */ - rb->font_getstringsize("0123456789", NULL, &max_numeral_height,WHAT_FONT); - max_numeral_height+=VERT_SPACING; + rb->font_getstringsize("0123456789", NULL, &max_numeral_height, WHAT_FONT); + max_numeral_height += VERT_SPACING; + #if LCD_DEPTH <= 1 - max_numeral_width=rb->font_get_width(rb->font_get(WHAT_FONT), '0'); + max_numeral_width = rb->font_get_width(rb->font_get(WHAT_FONT), '0'); #endif + backlight_ignore_timeout(); - rb->lcd_clear_display(); draw(); } @@ -641,17 +708,22 @@ static void init_game(bool newgame) static void save_game(void) { rb->splash(0, "Saving..."); - int fd=rb->open(RESUME_FILE,O_WRONLY|O_CREAT, 0666); - if(fd<0) + int fd = rb->open(RESUME_FILE, O_WRONLY|O_CREAT, 0666); + if(fd < 0) { return; } - ctx->cksum=0; - for(int x=0;x<GRID_SIZE;++x) - for(int y=0;y<GRID_SIZE;++y) - ctx->cksum+=ctx->grid[x][y]; - ctx->cksum^=ctx->score; - rb->write(fd, ctx,sizeof(struct game_ctx_t)); + + /* calculate checksum */ + ctx->cksum = 0; + + for(int x = 0; x < GRID_SIZE; ++x) + for(int y = 0; y < GRID_SIZE; ++y) + ctx->cksum += ctx->grid[x][y]; + + ctx->cksum ^= ctx->score; + + rb->write(fd, ctx, sizeof(struct game_ctx_t)); rb->close(fd); rb->lcd_update(); } @@ -659,59 +731,67 @@ static void save_game(void) /* loads a saved game, returns true on success */ static bool load_game(void) { - int success=0; - int fd=rb->open(RESUME_FILE, O_RDONLY); - if(fd<0) + int success = 0; + int fd = rb->open(RESUME_FILE, O_RDONLY); + if(fd < 0) { rb->remove(RESUME_FILE); return false; } - int numread=rb->read(fd, ctx, sizeof(struct game_ctx_t)); - int calc=0; - for(int x=0;x<GRID_SIZE;++x) - for(int y=0;y<GRID_SIZE;++y) - calc+=ctx->grid[x][y]; - calc^=ctx->score; - if(numread==sizeof(struct game_ctx_t) && calc==ctx->cksum) + + int numread = rb->read(fd, ctx, sizeof(struct game_ctx_t)); + + /* verify checksum */ + unsigned int calc = 0; + for(int x = 0; x < GRID_SIZE; ++x) + for(int y = 0; y < GRID_SIZE; ++y) + calc += ctx->grid[x][y]; + + calc ^= ctx->score; + + if(numread == sizeof(struct game_ctx_t) && calc == ctx->cksum) ++success; + rb->close(fd); rb->remove(RESUME_FILE); - return (success==1); + + return (success > 0); } /* update the highscores with ctx->score */ static void hs_check_update(bool noshow) { /* first, find the biggest tile to show as the level */ - int biggest=0; - for(int x=0;x<GRID_SIZE;++x) + unsigned int biggest = 0; + for(int x = 0; x < GRID_SIZE; ++x) { - for(int y=0;y<GRID_SIZE;++y) + for(int y = 0; y < GRID_SIZE; ++y) { - if(ctx->grid[x][y]>biggest) - biggest=ctx->grid[x][y]; + if(ctx->grid[x][y] > biggest) + biggest = ctx->grid[x][y]; } } - int hs_idx=highscore_update(ctx->score,biggest, "", highscores,NUM_SCORES); + + int hs_idx = highscore_update(ctx->score,biggest, "", highscores,NUM_SCORES); if(!noshow) { /* show the scores if there is a new high score */ - if(hs_idx>=0) + if(hs_idx >= 0) { - rb->splashf(HZ*2,"New High Score: %d", ctx->score); + rb->splashf(HZ*2, "New High Score: %d", ctx->score); rb->lcd_clear_display(); - highscore_show(hs_idx,highscores,NUM_SCORES,true); + highscore_show(hs_idx, highscores, NUM_SCORES, true); } } - highscore_save(HISCORES_FILE,highscores,NUM_SCORES); + highscore_save(HISCORES_FILE, highscores, NUM_SCORES); } /* asks the user if they wish to quit */ static bool confirm_quit(void) { - const struct text_message prompt={(const char*[]){"Are you sure?", "This will clear your current game."}, 2}; - enum yesno_res response=rb->gui_syncyesno_run(&prompt, NULL, NULL); - if(response==YESNO_NO) + const struct text_message prompt = { (const char*[]) {"Are you sure?", "This will clear your current game."}, 2}; + enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL); + if(response == YESNO_NO) return false; else return true; @@ -720,7 +800,7 @@ static bool confirm_quit(void) /* show the pause menu */ static int do_2048_pause_menu(void) { - int sel=0; + int sel = 0; MENUITEM_STRINGLIST(menu,"2048 Menu", NULL, "Resume Game", "Start New Game", @@ -729,7 +809,7 @@ static int do_2048_pause_menu(void) "Help", "Quit without Saving", "Quit"); - bool quit=false; + bool quit = false; while(!quit) { switch(rb->do_menu(&menu, &sel, NULL, false)) @@ -748,7 +828,7 @@ static int do_2048_pause_menu(void) } } case 2: - highscore_show(-1,highscores, NUM_SCORES, true); + highscore_show(-1, highscores, NUM_SCORES, true); break; case 3: playback_control(NULL); @@ -782,80 +862,85 @@ static void exit_handler(void) #endif return; } + static bool check_hs; + /* main game loop */ static enum plugin_status do_game(bool newgame) { init_game(newgame); - rb_atexit(&exit_handler); - int made_move=0; + rb_atexit(exit_handler); + int made_move = 0; while(1) { #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(false); /* Save battery when idling */ #endif /* Wait for a button press */ - int button=pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts)); - made_move=0; - memset(&merged_grid,0,SPACES*sizeof(bool)); + int button = pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts)); + made_move = 0; + + memset(&merged_grid, 0, SPACES*sizeof(bool)); memcpy(&old_grid, &ctx->grid, sizeof(int)*SPACES); - int grid_before_anim_step[GRID_SIZE][GRID_SIZE]; + + unsigned int grid_before_anim_step[GRID_SIZE][GRID_SIZE]; + #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); /* doing work now... */ #endif switch(button) { case KEY_UP: - for(int i=0;i<GRID_SIZE-1;++i) + for(int i = 0; i < GRID_SIZE - 1; ++i) { - memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); + memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)); up(true); - if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) + if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid))) { rb->sleep(ANIM_SLEEPTIME); draw(); } } - made_move=1; + made_move = 1; break; case KEY_DOWN: - for(int i=0;i<GRID_SIZE-1;++i) + for(int i = 0; i < GRID_SIZE - 1; ++i) { - memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); + memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)); down(true); - if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) + if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid))) { rb->sleep(ANIM_SLEEPTIME); draw(); } } - made_move=1; + made_move = 1; break; case KEY_LEFT: - for(int i=0;i<GRID_SIZE-1;++i) + for(int i = 0; i < GRID_SIZE - 1; ++i) { - memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); + memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)); left(true); - if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) + if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid))) { rb->sleep(ANIM_SLEEPTIME); draw(); } } - made_move=1; + made_move = 1; break; case KEY_RIGHT: - for(int i=0;i<GRID_SIZE-1;++i) + for(int i = 0; i < GRID_SIZE - 1; ++i) { - memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); + memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)); right(true); - if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) + if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid))) { rb->sleep(ANIM_SLEEPTIME); draw(); } } - made_move=1; + made_move = 1; break; case KEY_EXIT: switch(do_2048_pause_menu()) @@ -864,33 +949,33 @@ static enum plugin_status do_game(bool newgame) break; case 1: /* new game */ init_game(true); - made_move=1; + made_move = 1; continue; - break; case 2: /* quit without saving */ - check_hs=true; + check_hs = true; rb->remove(RESUME_FILE); return PLUGIN_ERROR; case 3: /* save and quit */ - check_hs=false; + check_hs = false; save_game(); return PLUGIN_ERROR; } break; default: { - exit_on_usb(button); /* handles poweroff and USB events */ + exit_on_usb(button); /* handle poweroff and USB events */ break; } } + if(made_move) { - /* Check if we actually moved, then add random */ - if(memcmp(&old_grid, ctx->grid, sizeof(int)*SPACES)) + /* Check if any tiles moved, then add random */ + if(memcmp(&old_grid, ctx->grid, sizeof(ctx->grid))) { place_random(); } - memcpy(&old_grid, ctx->grid, sizeof(int)*SPACES); + memcpy(&old_grid, ctx->grid, sizeof(ctx->grid)); if(check_gameover()) return PLUGIN_OK; draw(); @@ -906,8 +991,8 @@ static enum plugin_status do_game(bool newgame) /* used to hide resume option when there is no save */ static int mainmenu_cb(int action, const struct menu_item_ex *this_item) { - int idx=((intptr_t)this_item); - if(action==ACTION_REQUEST_MENUITEM && !loaded && (idx==0 || idx==5)) + int idx = ((intptr_t)this_item); + if(action == ACTION_REQUEST_MENUITEM && !loaded && (idx == 0 || idx == 5)) return ACTION_EXIT_MENUITEM; return action; } @@ -915,29 +1000,37 @@ static int mainmenu_cb(int action, const struct menu_item_ex *this_item) /* show the main menu */ static enum plugin_status do_2048_menu(void) { - int sel=0; - loaded=load_game(); - MENUITEM_STRINGLIST(menu,"2048 Menu", mainmenu_cb, "Resume Game", "Start New Game","High Scores","Playback Control", "Help", "Quit without Saving", "Quit"); - bool quit=false; + int sel = 0; + loaded = load_game(); + MENUITEM_STRINGLIST(menu, + "2048 Menu", + mainmenu_cb, + "Resume Game", + "Start New Game", + "High Scores", + "Playback Control", + "Help", + "Quit without Saving", + "Quit"); + bool quit = false; while(!quit) { - int item; - switch(item=rb->do_menu(&menu,&sel,NULL,false)) + switch(rb->do_menu(&menu, &sel, NULL, false)) { case 0: /* Start new game or resume a game */ case 1: { - if(item==1 && loaded) + if(sel == 1 && loaded) { if(!confirm_quit()) break; } - enum plugin_status ret=do_game(item==1); + enum plugin_status ret = do_game(sel == 1); switch(ret) { case PLUGIN_OK: { - loaded=false; + loaded = false; rb->remove(RESUME_FILE); hs_check_update(false); break; @@ -956,7 +1049,7 @@ static enum plugin_status do_2048_menu(void) break; } case 2: - highscore_show(-1,highscores, NUM_SCORES, true); + highscore_show(-1, highscores, NUM_SCORES, true); break; case 3: playback_control(NULL); @@ -977,6 +1070,8 @@ static enum plugin_status do_2048_menu(void) } return PLUGIN_OK; } + +/* plugin entry point */ enum plugin_status plugin_start(const void* param) { (void)param; @@ -985,9 +1080,12 @@ enum plugin_status plugin_start(const void* param) rb->lcd_setfont(WHAT_FONT); /* now start the game menu */ - enum plugin_status ret=do_2048_menu(); - highscore_save(HISCORES_FILE,highscores,NUM_SCORES); + enum plugin_status ret = do_2048_menu(); + + highscore_save(HISCORES_FILE, highscores, NUM_SCORES); cleanup(); - abnormal_exit=false; + + abnormal_exit = false; + return ret; } |