2048: Cleanup
- more whitespace to enhance readability - better/fixed/more comments ;) - some minor optimizations - general code cleanup Change-Id: I2b5f69aba0f83f989abb2c636920646e4315583f
This commit is contained in:
parent
24533635c0
commit
d8ee5fcfc4
1 changed files with 393 additions and 295 deletions
|
@ -46,25 +46,25 @@
|
|||
#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
|
||||
|
||||
/* screen-specific configuration */
|
||||
|
||||
#if LCD_WIDTH<LCD_HEIGHT
|
||||
/* tall screens */
|
||||
#if (LCD_WIDTH < LCD_HEIGHT) /* tall screens */
|
||||
# define TITLE_X 0
|
||||
# define TITLE_Y 0
|
||||
# define BASE_Y (BMPHEIGHT__2048_tiles*1.5)
|
||||
|
@ -73,8 +73,7 @@
|
|||
# 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*/
|
||||
#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)))
|
||||
|
@ -85,11 +84,11 @@
|
|||
# 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)
|
||||
/* 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_data;
|
||||
/* use a pointer to make save/load easier */
|
||||
static struct game_ctx_t *ctx=&ctx_data;
|
||||
static struct game_ctx_t *ctx = &game_ctx;
|
||||
|
||||
/*** 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;
|
||||
|
||||
#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 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;
|
||||
}
|
||||
|
||||
/* prepares to exit */
|
||||
/* prepares for exit */
|
||||
static void cleanup(void)
|
||||
{
|
||||
backlight_use_settings();
|
||||
|
@ -154,44 +163,55 @@ static inline int rand_2_or_4(void)
|
|||
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",
|
||||
|
||||
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},
|
||||
{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 ***/
|
||||
|
||||
/* 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;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
@ -206,7 +226,7 @@ static inline void slide_internal(int startx, int starty,
|
|||
}
|
||||
}
|
||||
}
|
||||
if(ctx->score>best_score_before && update_best)
|
||||
if(ctx->score > best_score_old && update_best)
|
||||
best_score = ctx->score;
|
||||
}
|
||||
|
||||
|
@ -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,8 +296,13 @@ 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)
|
||||
|
@ -287,17 +315,25 @@ static inline int ilog2(int n)
|
|||
}
|
||||
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);
|
||||
|
@ -316,14 +352,17 @@ static void draw(void)
|
|||
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);
|
||||
|
@ -340,17 +379,24 @@ static void draw(void)
|
|||
h = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(draw_title)
|
||||
rb->lcd_putsxy(TITLE_X, TITLE_Y, buf);
|
||||
|
||||
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);
|
||||
|
||||
/* try making the score fit */
|
||||
if(w + SCORE_X >= BACKGROUND_X && h + SCORE_Y >= BACKGROUND_Y)
|
||||
{
|
||||
/* score overflows */
|
||||
|
@ -362,28 +408,28 @@ static void draw(void)
|
|||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
@ -391,16 +437,20 @@ static void draw(void)
|
|||
else
|
||||
goto skip_draw_score;
|
||||
}
|
||||
|
||||
draw_lbl:
|
||||
rb->lcd_putsxy(SCORE_X, score_y, buf);
|
||||
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)
|
||||
|
@ -414,28 +464,28 @@ skip_draw_score:
|
|||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
@ -445,29 +495,38 @@ skip_draw_score:
|
|||
}
|
||||
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;
|
||||
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];
|
||||
}
|
||||
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;
|
||||
|
||||
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)
|
||||
|
@ -476,30 +535,34 @@ static void draw(void)
|
|||
{
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
{
|
||||
|
@ -512,61 +575,60 @@ static void place_random(void)
|
|||
if(!ctx->grid[x][y])
|
||||
{
|
||||
xpos[back] = x;
|
||||
ypos[back]=y;
|
||||
++back;
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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));
|
||||
up(false);
|
||||
if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES))
|
||||
if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid)))
|
||||
{
|
||||
restore_old_grid();
|
||||
RESTORE_GRID();
|
||||
ctx->score = oldscore;
|
||||
have_legal_move = true;
|
||||
}
|
||||
restore_old_grid();
|
||||
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();
|
||||
RESTORE_GRID();
|
||||
ctx->score = oldscore;
|
||||
have_legal_move = true;
|
||||
}
|
||||
restore_old_grid();
|
||||
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();
|
||||
RESTORE_GRID();
|
||||
ctx->score = oldscore;
|
||||
have_legal_move = true;
|
||||
}
|
||||
restore_old_grid();
|
||||
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();
|
||||
RESTORE_GRID();
|
||||
ctx->score = oldscore;
|
||||
have_legal_move = true;
|
||||
}
|
||||
|
@ -574,10 +636,11 @@ static bool check_gameover(void)
|
|||
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 x = 0; x < GRID_SIZE; ++x)
|
||||
|
@ -588,7 +651,6 @@ static bool check_gameover(void)
|
|||
draw();
|
||||
ctx->already_won = true;
|
||||
rb->splash(HZ*2,"You win!");
|
||||
/* don't let the user quit here :) */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -611,10 +673,11 @@ static void init_game(bool newgame)
|
|||
best_score = highscores[0].score;
|
||||
if(loaded && ctx->score > best_score)
|
||||
best_score = ctx->score;
|
||||
|
||||
if(newgame)
|
||||
{
|
||||
/* initialize the game context */
|
||||
memset(ctx->grid, 0, sizeof(int)*SPACES);
|
||||
memset(ctx->grid, 0, sizeof(ctx->grid));
|
||||
for(int i = 0; i < NUM_STARTING_TILES; ++i)
|
||||
{
|
||||
place_random();
|
||||
|
@ -622,18 +685,22 @@ static void init_game(bool newgame)
|
|||
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;
|
||||
|
||||
#if LCD_DEPTH <= 1
|
||||
max_numeral_width = rb->font_get_width(rb->font_get(WHAT_FONT), '0');
|
||||
#endif
|
||||
|
||||
backlight_ignore_timeout();
|
||||
rb->lcd_clear_display();
|
||||
draw();
|
||||
}
|
||||
|
||||
|
@ -646,11 +713,16 @@ static void save_game(void)
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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();
|
||||
|
@ -666,24 +738,31 @@ static bool load_game(void)
|
|||
rb->remove(RESUME_FILE);
|
||||
return false;
|
||||
}
|
||||
|
||||
int numread = rb->read(fd, ctx, sizeof(struct game_ctx_t));
|
||||
int calc=0;
|
||||
|
||||
/* 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;
|
||||
unsigned int biggest = 0;
|
||||
for(int x = 0; x < GRID_SIZE; ++x)
|
||||
{
|
||||
for(int y = 0; y < GRID_SIZE; ++y)
|
||||
|
@ -692,6 +771,7 @@ static void hs_check_update(bool noshow)
|
|||
biggest = ctx->grid[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
int hs_idx = highscore_update(ctx->score,biggest, "", highscores,NUM_SCORES);
|
||||
if(!noshow)
|
||||
{
|
||||
|
@ -782,12 +862,14 @@ 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);
|
||||
rb_atexit(exit_handler);
|
||||
int made_move = 0;
|
||||
while(1)
|
||||
{
|
||||
|
@ -797,9 +879,12 @@ static enum plugin_status do_game(bool newgame)
|
|||
/* 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));
|
||||
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
|
||||
|
@ -808,9 +893,9 @@ static enum plugin_status do_game(bool newgame)
|
|||
case KEY_UP:
|
||||
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();
|
||||
|
@ -821,9 +906,9 @@ static enum plugin_status do_game(bool newgame)
|
|||
case KEY_DOWN:
|
||||
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();
|
||||
|
@ -834,9 +919,9 @@ static enum plugin_status do_game(bool newgame)
|
|||
case KEY_LEFT:
|
||||
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();
|
||||
|
@ -847,9 +932,9 @@ static enum plugin_status do_game(bool newgame)
|
|||
case KEY_RIGHT:
|
||||
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();
|
||||
|
@ -866,7 +951,6 @@ static enum plugin_status do_game(bool newgame)
|
|||
init_game(true);
|
||||
made_move = 1;
|
||||
continue;
|
||||
break;
|
||||
case 2: /* quit without saving */
|
||||
check_hs = true;
|
||||
rb->remove(RESUME_FILE);
|
||||
|
@ -879,18 +963,19 @@ static enum plugin_status do_game(bool newgame)
|
|||
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();
|
||||
|
@ -917,22 +1002,30 @@ 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");
|
||||
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:
|
||||
|
@ -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;
|
||||
|
@ -986,8 +1081,11 @@ enum plugin_status plugin_start(const void* param)
|
|||
|
||||
/* now start the game menu */
|
||||
enum plugin_status ret = do_2048_menu();
|
||||
|
||||
highscore_save(HISCORES_FILE, highscores, NUM_SCORES);
|
||||
cleanup();
|
||||
|
||||
abnormal_exit = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue