summaryrefslogtreecommitdiffstats
path: root/apps/plugins/2048.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/2048.c')
-rw-r--r--apps/plugins/2048.c684
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;
}