summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 @@
46#include "pluginbitmaps/_2048_tiles.h" 46#include "pluginbitmaps/_2048_tiles.h"
47#endif 47#endif
48 48
49/* defines */ 49/* some constants */
50 50
51#define ANIM_SLEEPTIME (HZ/20) 51static const int ANIM_SLEEPTIME = (HZ/20);
52static const int NUM_STARTING_TILES = 2;
53static const int VERT_SPACING = 4;
54static const int WHAT_FONT = FONT_UI;
55static const unsigned int WINNING_TILE = 2048;
56
57/* must use macros for these */
52#define GRID_SIZE 4 58#define GRID_SIZE 4
53#define HISCORES_FILE PLUGIN_GAMES_DATA_DIR "/2048.score" 59#define HISCORES_FILE PLUGIN_GAMES_DATA_DIR "/2048.score"
60#define MIN_SPACE (BMPHEIGHT__2048_tiles * 0.134)
54#define NUM_SCORES 5 61#define NUM_SCORES 5
55#define NUM_STARTING_TILES 2
56#define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/2048.save" 62#define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/2048.save"
57#define WHAT_FONT FONT_UI 63#define SPACES (GRID_SIZE * GRID_SIZE)
58#define SPACES (GRID_SIZE*GRID_SIZE)
59#define MIN_SPACE (BMPHEIGHT__2048_tiles*0.134) /* space between tiles */
60#define MAX_UNDOS 64
61#define VERT_SPACING 4
62#define WINNING_TILE 2048
63 64
64/* screen-specific configuration */ 65/* screen-specific configuration */
65 66
66#if LCD_WIDTH<LCD_HEIGHT 67#if (LCD_WIDTH < LCD_HEIGHT) /* tall screens */
67/* tall screens */ 68# define TITLE_X 0
68#define TITLE_X 0 69# define TITLE_Y 0
69#define TITLE_Y 0 70# define BASE_Y (BMPHEIGHT__2048_tiles*1.5)
70#define BASE_Y (BMPHEIGHT__2048_tiles*1.5) 71# define BASE_X (BMPHEIGHT__2048_tiles*.5-MIN_SPACE)
71#define BASE_X (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) 72# define SCORE_X 0
72#define SCORE_X 0 73# define SCORE_Y (max_numeral_height)
73#define SCORE_Y (max_numeral_height) 74# define BEST_SCORE_X 0
74#define BEST_SCORE_X 0 75# define BEST_SCORE_Y (2*max_numeral_height)
75#define BEST_SCORE_Y (2*max_numeral_height) 76#else /* wide or square screens */
76#else 77# define TITLE_X 0
77/* wide screens or square screens*/ 78# define TITLE_Y 0
78#define TITLE_X 0 79# define BASE_X (LCD_WIDTH-(GRID_SIZE*BMPHEIGHT__2048_tiles)-(((GRID_SIZE+1)*MIN_SPACE)))
79#define TITLE_Y 0 80# define BASE_Y (BMPHEIGHT__2048_tiles*.5-MIN_SPACE)
80#define BASE_X (LCD_WIDTH-(GRID_SIZE*BMPHEIGHT__2048_tiles)-(((GRID_SIZE+1)*MIN_SPACE))) 81# define SCORE_X 0
81#define BASE_Y (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) 82# define SCORE_Y (max_numeral_height)
82#define SCORE_X 0 83# define BEST_SCORE_X 0
83#define SCORE_Y (max_numeral_height) 84# define BEST_SCORE_Y (2*max_numeral_height)
84#define BEST_SCORE_X 0 85#endif /* LCD_WIDTH < LCD_HEIGHT */
85#define BEST_SCORE_Y (2*max_numeral_height)
86#endif /* LCD_WIDTH<LCD_HEIGHT */
87
88#define BACKGROUND_X (int)(BASE_X-MIN_SPACE)
89#define BACKGROUND_Y (int)(BASE_Y-MIN_SPACE)
90 86
91/* key mappings */ 87/* where to draw the background bitmap */
88static const int BACKGROUND_X = (BASE_X-MIN_SPACE);
89static const int BACKGROUND_Y = (BASE_Y-MIN_SPACE);
92 90
91/* key mappings */
93#define KEY_UP PLA_UP 92#define KEY_UP PLA_UP
94#define KEY_DOWN PLA_DOWN 93#define KEY_DOWN PLA_DOWN
95#define KEY_LEFT PLA_LEFT 94#define KEY_LEFT PLA_LEFT
@@ -97,48 +96,58 @@
97#define KEY_EXIT PLA_CANCEL 96#define KEY_EXIT PLA_CANCEL
98#define KEY_UNDO PLA_SELECT 97#define KEY_UNDO PLA_SELECT
99 98
99/* notice how "color" is spelled :P */
100#ifdef HAVE_LCD_COLOR
101
100/* colors */ 102/* colors */
101 103
102#define BACKGROUND (LCD_RGBPACK(0xfa, 0xf8, 0xef)) 104static const unsigned BACKGROUND = LCD_RGBPACK(0xfa, 0xf8, 0xef);
103#define BOARD_BACKGROUND (LCD_RGBPACK(0xbb, 0xad, 0xa0)) 105static const unsigned BOARD_BACKGROUND = LCD_RGBPACK(0xbb, 0xad, 0xa0);
104#define TEXT_COLOR (LCD_RGBPACK(0x77, 0x6e, 0x65)) 106static const unsigned TEXT_COLOR = LCD_RGBPACK(0x77, 0x6e, 0x65);
107
108#endif
105 109
110/* PLA data */
106static const struct button_mapping *plugin_contexts[] = { pla_main_ctx }; 111static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
107 112
108/* game data */ 113/*** game data structures ***/
114
109struct game_ctx_t { 115struct game_ctx_t {
110 int grid[GRID_SIZE][GRID_SIZE]; 116 unsigned int grid[GRID_SIZE][GRID_SIZE]; /* 0 = empty */
111 int score; 117 unsigned int score;
112 int cksum; /* sum of grid, XORed by score */ 118 unsigned int cksum; /* sum of grid, XORed by score */
113 bool already_won; 119 bool already_won; /* has the player gotten 2048 yet? */
114}; 120} game_ctx;
121
122static struct game_ctx_t *ctx = &game_ctx;
115 123
116static struct game_ctx_t ctx_data; 124/*** temporary data ***/
117/* use a pointer to make save/load easier */
118static struct game_ctx_t *ctx=&ctx_data;
119 125
120/* temporary data */
121static bool merged_grid[GRID_SIZE][GRID_SIZE]; 126static bool merged_grid[GRID_SIZE][GRID_SIZE];
122static int old_grid[GRID_SIZE][GRID_SIZE]; 127static int old_grid[GRID_SIZE][GRID_SIZE];
123static int max_numeral_height=-1; 128
129static int max_numeral_height = -1;
130
124#if LCD_DEPTH <= 1 131#if LCD_DEPTH <= 1
125static int max_numeral_width; 132static int max_numeral_width;
126#endif 133#endif
127static bool loaded=false;
128 134
129/* first init_game will set this, when it is exceeded, it will be updated in the slide functions */ 135static bool loaded = false; /* has a save been loaded? */
130static int best_score; 136
137/* the high score */
138static unsigned int best_score;
131 139
132static bool abnormal_exit=true; 140static bool abnormal_exit = true;
133static struct highscore highscores[NUM_SCORES]; 141static struct highscore highscores[NUM_SCORES];
134 142
135/* returns a random int between min and max */ 143/***************************** UTILITY FUNCTIONS *****************************/
144
136static inline int rand_range(int min, int max) 145static inline int rand_range(int min, int max)
137{ 146{
138 return rb->rand()%(max-min+1)+min; 147 return rb->rand() % (max-min + 1) + min;
139} 148}
140 149
141/* prepares to exit */ 150/* prepares for exit */
142static void cleanup(void) 151static void cleanup(void)
143{ 152{
144 backlight_use_settings(); 153 backlight_use_settings();
@@ -148,66 +157,77 @@ static void cleanup(void)
148static inline int rand_2_or_4(void) 157static inline int rand_2_or_4(void)
149{ 158{
150 /* 1 in 10 chance of a four */ 159 /* 1 in 10 chance of a four */
151 if(rb->rand()%10==0) 160 if(rb->rand() % 10 == 0)
152 return 4; 161 return 4;
153 else 162 else
154 return 2; 163 return 2;
155} 164}
156 165
157/* display the help text */ 166/* displays the help text */
158static bool do_help(void) 167static bool do_help(void)
159{ 168{
169
160#ifdef HAVE_LCD_COLOR 170#ifdef HAVE_LCD_COLOR
161 rb->lcd_set_foreground(LCD_WHITE); 171 rb->lcd_set_foreground(LCD_WHITE);
162 rb->lcd_set_background(LCD_BLACK); 172 rb->lcd_set_background(LCD_BLACK);
163#endif 173#endif
174
164 rb->lcd_setfont(FONT_UI); 175 rb->lcd_setfont(FONT_UI);
165 char* help_text[]= {"2048", "", "Aim", 176
166 "", "Join", "the", "numbers", "to", "get", "to", "the", "2048", "tile!", "", "", 177 static char* help_text[]= {"2048", "", "Aim",
167 "How", "to", "Play", "", 178 "", "Join", "the", "numbers", "to", "get", "to", "the", "2048", "tile!", "", "",
168 "", "Use", "the", "directional", "keys", "to", "move", "the", "tiles.", "When", 179 "How", "to", "Play", "",
169 "two", "tiles", "with", "the", "same", "number", "touch,", "they", "merge", "into", "one!"}; 180 "", "Use", "the", "directional", "keys", "to", "move", "the", "tiles.", "When",
170 struct style_text style[]={ 181 "two", "tiles", "with", "the", "same", "number", "touch,", "they", "merge", "into", "one!"};
171 {0, TEXT_CENTER|TEXT_UNDERLINE}, 182
172 {2, C_RED}, 183 struct style_text style[] = {
173 {15, C_RED}, {16, C_RED}, {17,C_RED}, 184 {0, TEXT_CENTER | TEXT_UNDERLINE},
185 {2, C_RED},
186 {15, C_RED},
187 {16, C_RED},
188 {17, C_RED},
174 LAST_STYLE_ITEM 189 LAST_STYLE_ITEM
175 }; 190 };
176 return display_text(ARRAYLEN(help_text), help_text, style,NULL,true);
177}
178 191
179/*** the logic for sliding ***/ 192 return display_text(ARRAYLEN(help_text), help_text, style, NULL, true);
193}
180 194
181/* this is the helper function that does the actual tile moving */ 195/*** tile movement logic ***/
182 196
197/* this function performs the tile movement */
183static inline void slide_internal(int startx, int starty, 198static inline void slide_internal(int startx, int starty,
184 int stopx, int stopy, 199 int stopx, int stopy,
185 int dx, int dy, 200 int dx, int dy,
186 int lookx, int looky, 201 int lookx, int looky,
187 bool update_best) 202 bool update_best)
188{ 203{
189 int best_score_before=best_score; 204 unsigned int best_score_old = best_score;
190 for(int y=starty;y!=stopy;y+=dy) 205
206 /* loop over the rows or columns, moving the tiles in the specified direction */
207 for(int y = starty; y != stopy; y += dy)
191 { 208 {
192 for(int x=startx;x!=stopx;x+=dx) 209 for(int x = startx; x != stopx; x += dx)
193 { 210 {
194 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 */ 211 if(ctx->grid[x + lookx][y + looky] == ctx->grid[x][y] &&
212 ctx->grid[x][y] &&
213 !merged_grid[x + lookx][y + looky] &&
214 !merged_grid[x][y]) /* merge these two tiles */
195 { 215 {
196 /* Each merged tile cannot be merged again */ 216 /* Each merged tile cannot be merged again */
197 merged_grid[x+lookx][y+looky]=true; 217 merged_grid[x + lookx][y + looky] = true;
198 ctx->grid[x+lookx][y+looky]=2*ctx->grid[x][y]; 218 ctx->grid[x + lookx][y + looky] = 2 * ctx->grid[x][y];
199 ctx->score+=ctx->grid[x+lookx][y+looky]; 219 ctx->score += ctx->grid[x + lookx][y + looky];
200 ctx->grid[x][y]=0; 220 ctx->grid[x][y] = 0;
201 } 221 }
202 else if(ctx->grid[x+lookx][y+looky]==0) /* Empty! */ 222 else if(ctx->grid[x + lookx][y + looky] == 0) /* Empty! */
203 { 223 {
204 ctx->grid[x+lookx][y+looky]=ctx->grid[x][y]; 224 ctx->grid[x + lookx][y + looky] = ctx->grid[x][y];
205 ctx->grid[x][y]=0; 225 ctx->grid[x][y] = 0;
206 } 226 }
207 } 227 }
208 } 228 }
209 if(ctx->score>best_score_before && update_best) 229 if(ctx->score > best_score_old && update_best)
210 best_score=ctx->score; 230 best_score = ctx->score;
211} 231}
212 232
213/* these functions move each tile 1 space in the direction specified via calls to slide_internal */ 233/* 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)
227 0, -1, /* lookahead values */ 247 0, -1, /* lookahead values */
228 update_best); 248 update_best);
229} 249}
250
230/* Down 251/* Down
231 0 v v v v 252 0 v v v v
232 1 v v v v 253 1 v v v v
@@ -242,6 +263,7 @@ static void down(bool update_best)
242 0, 1, 263 0, 1,
243 update_best); 264 update_best);
244} 265}
266
245/* Left 267/* Left
246 0 < < < 268 0 < < <
247 1 < < < 269 1 < < <
@@ -257,6 +279,7 @@ static void left(bool update_best)
257 -1, 0, 279 -1, 0,
258 update_best); 280 update_best);
259} 281}
282
260/* Right 283/* Right
261 0 > > > 284 0 > > >
262 1 > > > 285 1 > > >
@@ -273,31 +296,44 @@ static void right(bool update_best)
273 update_best); 296 update_best);
274} 297}
275 298
276/* slightly modified version of base 2 log, returns 1 when given zero, and log2(n)+1 for anything else */ 299/* copies old_grid to ctx->grid */
300static inline void RESTORE_GRID(void)
301{
302 memcpy(&ctx->grid, &old_grid, sizeof(ctx->grid));
303}
277 304
305/* slightly modified base 2 logarithm, returns 1 when given zero, and log2(n) + 1 for anything else */
278static inline int ilog2(int n) 306static inline int ilog2(int n)
279{ 307{
280 if(n==0) 308 if(n == 0)
281 return 1; 309 return 1;
282 int log=0; 310 int log = 0;
283 while(n>1) 311 while(n > 1)
284 { 312 {
285 n>>=1; 313 n >>= 1;
286 ++log; 314 ++log;
287 } 315 }
288 return log+1; 316 return log + 1;
289} 317}
318
319/* low-depth displays resort to text drawing, see the #else case below */
320
290#if LCD_DEPTH > 1 321#if LCD_DEPTH > 1
322
323/* draws game screen + updates LCD */
291static void draw(void) 324static void draw(void)
292{ 325{
293#ifdef HAVE_LCD_COLOR 326#ifdef HAVE_LCD_COLOR
294 rb->lcd_set_background(BACKGROUND); 327 rb->lcd_set_background(BACKGROUND);
295#endif 328#endif
329
296 rb->lcd_clear_display(); 330 rb->lcd_clear_display();
297 331
298 /* draw the background */ 332 /* draw the background */
299 333
300 rb->lcd_bitmap(_2048_background, BACKGROUND_X, BACKGROUND_Y, BMPWIDTH__2048_background, BMPWIDTH__2048_background); 334 rb->lcd_bitmap(_2048_background,
335 BACKGROUND_X, BACKGROUND_Y,
336 BMPWIDTH__2048_background, BMPWIDTH__2048_background);
301 337
302 /* 338 /*
303 grey_gray_bitmap(_2048_background, BACKGROUND_X, BACKGROUND_Y, BMPWIDTH__2048_background, BMPHEIGHT__2048_background); 339 grey_gray_bitmap(_2048_background, BACKGROUND_X, BACKGROUND_Y, BMPWIDTH__2048_background, BMPHEIGHT__2048_background);
@@ -305,290 +341,316 @@ static void draw(void)
305 341
306 /* draw the grid */ 342 /* draw the grid */
307 343
308 for(int y=0;y<GRID_SIZE;++y) 344 for(int y = 0; y < GRID_SIZE; ++y)
309 { 345 {
310 for(int x=0;x<GRID_SIZE;++x) 346 for(int x = 0; x < GRID_SIZE; ++x)
311 { 347 {
312 rb->lcd_bitmap_part(_2048_tiles, /* source */ 348 rb->lcd_bitmap_part(_2048_tiles, /* source */
313 BMPWIDTH__2048_tiles-BMPHEIGHT__2048_tiles*ilog2(ctx->grid[x][y]), 0, /* source upper left corner */ 349 BMPWIDTH__2048_tiles - BMPHEIGHT__2048_tiles * ilog2(ctx->grid[x][y]), 0, /* source upper left corner */
314 STRIDE(SCREEN_MAIN, BMPWIDTH__2048_tiles, BMPHEIGHT__2048_tiles), /* stride */ 350 STRIDE(SCREEN_MAIN, BMPWIDTH__2048_tiles, BMPHEIGHT__2048_tiles), /* stride */
315 (BMPHEIGHT__2048_tiles+MIN_SPACE)*x+BASE_X, (BMPHEIGHT__2048_tiles+MIN_SPACE)*y+BASE_Y, /* dest upper-left corner */ 351 (BMPHEIGHT__2048_tiles + MIN_SPACE) * x + BASE_X, (BMPHEIGHT__2048_tiles + MIN_SPACE) * y + BASE_Y, /* dest upper-left corner */
316 BMPHEIGHT__2048_tiles, BMPHEIGHT__2048_tiles); /* size of the cut section */ 352 BMPHEIGHT__2048_tiles, BMPHEIGHT__2048_tiles); /* size of the cut section */
317 } 353 }
318 } 354 }
355
319 /* draw the title */ 356 /* draw the title */
320 char buf[32]; 357 char buf[32];
358
321#ifdef HAVE_LCD_COLOR 359#ifdef HAVE_LCD_COLOR
322 rb->lcd_set_foreground(TEXT_COLOR); 360 rb->lcd_set_foreground(TEXT_COLOR);
323#endif 361#endif
324 rb->snprintf(buf, 31, "%d", WINNING_TILE);
325 362
326 /* check if the title will go into the grid */ 363 rb->snprintf(buf, sizeof(buf), "%d", WINNING_TILE);
364
365 /* check if the title will overlap the grid */
327 int w, h; 366 int w, h;
328 rb->lcd_setfont(FONT_UI); 367 rb->lcd_setfont(FONT_UI);
329 rb->font_getstringsize(buf, &w, &h, FONT_UI); 368 rb->font_getstringsize(buf, &w, &h, FONT_UI);
330 bool draw_title=true; 369 bool draw_title = true;
331 if(w+TITLE_X>=BACKGROUND_X && h+TITLE_Y>=BACKGROUND_Y) 370 if(w + TITLE_X >= BACKGROUND_X && h + TITLE_Y >= BACKGROUND_Y)
332 { 371 {
333 /* if it goes into the grid, use the system font, which should be smaller */ 372 /* if it goes into the grid, use the system font, which should be smaller */
334 rb->lcd_setfont(FONT_SYSFIXED); 373 rb->lcd_setfont(FONT_SYSFIXED);
335 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); 374 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED);
336 if(w+TITLE_X>=BACKGROUND_X && h+TITLE_Y>=BACKGROUND_Y) 375 if(w + TITLE_X >= BACKGROUND_X && h + TITLE_Y >= BACKGROUND_Y)
337 { 376 {
338 /* title can't fit, don't draw it */ 377 /* title can't fit, don't draw it */
339 draw_title=false; 378 draw_title = false;
340 h=0; 379 h = 0;
341 } 380 }
342 } 381 }
382
343 if(draw_title) 383 if(draw_title)
344 rb->lcd_putsxy(TITLE_X, TITLE_Y, buf); 384 rb->lcd_putsxy(TITLE_X, TITLE_Y, buf);
345 int score_y=TITLE_Y+h+VERT_SPACING; 385
386 int score_y = TITLE_Y + h + VERT_SPACING;
387
346 /* draw the score */ 388 /* draw the score */
347 rb->snprintf(buf, 31, "Score: %d", ctx->score); 389 rb->snprintf(buf, sizeof(buf), "Score: %d", ctx->score);
390
348#ifdef HAVE_LCD_COLOR 391#ifdef HAVE_LCD_COLOR
349 rb->lcd_set_foreground(LCD_WHITE); 392 rb->lcd_set_foreground(LCD_WHITE);
350 rb->lcd_set_background(BOARD_BACKGROUND); 393 rb->lcd_set_background(BOARD_BACKGROUND);
351#endif 394#endif
395
352 rb->lcd_setfont(FONT_UI); 396 rb->lcd_setfont(FONT_UI);
353 rb->font_getstringsize(buf, &w, &h, FONT_UI); 397 rb->font_getstringsize(buf, &w, &h, FONT_UI);
354 if(w+SCORE_X>=BACKGROUND_X && h+SCORE_Y>=BACKGROUND_Y) 398
399 /* try making the score fit */
400 if(w + SCORE_X >= BACKGROUND_X && h + SCORE_Y >= BACKGROUND_Y)
355 { 401 {
356 /* score overflows */ 402 /* score overflows */
357 /* first see if it fits with Score: and FONT_SYSFIXED */ 403 /* first see if it fits with Score: and FONT_SYSFIXED */
358 rb->lcd_setfont(FONT_SYSFIXED); 404 rb->lcd_setfont(FONT_SYSFIXED);
359 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); 405 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED);
360 if(w+SCORE_X < BACKGROUND_X) 406 if(w + SCORE_X < BACKGROUND_X)
361 /* it fits, go and draw it */ 407 /* it fits, go and draw it */
362 goto draw_lbl; 408 goto draw_lbl;
363 409
364 /* now try with S: and FONT_UI */ 410 /* now try with S: and FONT_UI */
365 rb->snprintf(buf, 31, "S: %d", ctx->score); 411 rb->snprintf(buf, sizeof(buf), "S: %d", ctx->score);
366 rb->font_getstringsize(buf, &w, &h, FONT_UI); 412 rb->font_getstringsize(buf, &w, &h, FONT_UI);
367 rb->lcd_setfont(FONT_UI); 413 rb->lcd_setfont(FONT_UI);
368 if(w+SCORE_X<BACKGROUND_X) 414 if(w + SCORE_X < BACKGROUND_X)
369 goto draw_lbl; 415 goto draw_lbl;
370 416
371 /* now try with S: and FONT_SYSFIXED */ 417 /* now try with S: and FONT_SYSFIXED */
372 rb->snprintf(buf, 31, "S: %d", ctx->score); 418 rb->snprintf(buf, sizeof(buf), "S: %d", ctx->score);
373 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); 419 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED);
374 rb->lcd_setfont(FONT_SYSFIXED); 420 rb->lcd_setfont(FONT_SYSFIXED);
375 if(w+SCORE_X<BACKGROUND_X) 421 if(w + SCORE_X < BACKGROUND_X)
376 goto draw_lbl; 422 goto draw_lbl;
377 423
378 /* then try without Score: and FONT_UI */ 424 /* then try without Score: and FONT_UI */
379 rb->snprintf(buf, 31, "%d", ctx->score); 425 rb->snprintf(buf, sizeof(buf), "%d", ctx->score);
380 rb->font_getstringsize(buf, &w, &h, FONT_UI); 426 rb->font_getstringsize(buf, &w, &h, FONT_UI);
381 rb->lcd_setfont(FONT_UI); 427 rb->lcd_setfont(FONT_UI);
382 if(w+SCORE_X<BACKGROUND_X) 428 if(w + SCORE_X < BACKGROUND_X)
383 goto draw_lbl; 429 goto draw_lbl;
384 430
385 /* as a last resort, don't use Score: and use the system font */ 431 /* as a last resort, don't use Score: and use the system font */
386 rb->snprintf(buf, 31, "%d", ctx->score); 432 rb->snprintf(buf, sizeof(buf), "%d", ctx->score);
387 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); 433 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED);
388 rb->lcd_setfont(FONT_SYSFIXED); 434 rb->lcd_setfont(FONT_SYSFIXED);
389 if(w+SCORE_X<BACKGROUND_X) 435 if(w + SCORE_X < BACKGROUND_X)
390 goto draw_lbl; 436 goto draw_lbl;
391 else 437 else
392 goto skip_draw_score; 438 goto skip_draw_score;
393 } 439 }
440
394draw_lbl: 441draw_lbl:
395 rb->lcd_putsxy(SCORE_X, score_y, buf); 442 rb->lcd_putsxy(SCORE_X, score_y, buf);
396 score_y+=h+VERT_SPACING; 443 score_y += h + VERT_SPACING;
444
397 /* draw the best score */ 445 /* draw the best score */
398skip_draw_score: 446skip_draw_score:
399 rb->snprintf(buf, 31, "Best: %d", best_score); 447 rb->snprintf(buf, sizeof(buf), "Best: %d", best_score);
448
400#ifdef HAVE_LCD_COLOR 449#ifdef HAVE_LCD_COLOR
401 rb->lcd_set_foreground(LCD_WHITE); 450 rb->lcd_set_foreground(LCD_WHITE);
402 rb->lcd_set_background(BOARD_BACKGROUND); 451 rb->lcd_set_background(BOARD_BACKGROUND);
403#endif 452#endif
453
404 rb->lcd_setfont(FONT_UI); 454 rb->lcd_setfont(FONT_UI);
405 rb->font_getstringsize(buf, &w, &h, FONT_UI); 455 rb->font_getstringsize(buf, &w, &h, FONT_UI);
406 if(w+BEST_SCORE_X>=BACKGROUND_X && h+BEST_SCORE_Y>=BACKGROUND_Y) 456 if(w + BEST_SCORE_X >= BACKGROUND_X && h + BEST_SCORE_Y >= BACKGROUND_Y)
407 { 457 {
408 /* score overflows */ 458 /* score overflows */
409 /* first see if it fits with Score: and FONT_SYSFIXED */ 459 /* first see if it fits with Score: and FONT_SYSFIXED */
410 rb->lcd_setfont(FONT_SYSFIXED); 460 rb->lcd_setfont(FONT_SYSFIXED);
411 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); 461 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED);
412 if(w+BEST_SCORE_X < BACKGROUND_X) 462 if(w + BEST_SCORE_X < BACKGROUND_X)
413 /* it fits, go and draw it */ 463 /* it fits, go and draw it */
414 goto draw_best; 464 goto draw_best;
415 465
416 /* now try with S: and FONT_UI */ 466 /* now try with S: and FONT_UI */
417 rb->snprintf(buf, 31, "B: %d", best_score); 467 rb->snprintf(buf, sizeof(buf), "B: %d", best_score);
418 rb->font_getstringsize(buf, &w, &h, FONT_UI); 468 rb->font_getstringsize(buf, &w, &h, FONT_UI);
419 rb->lcd_setfont(FONT_UI); 469 rb->lcd_setfont(FONT_UI);
420 if(w+BEST_SCORE_X<BACKGROUND_X) 470 if(w + BEST_SCORE_X < BACKGROUND_X)
421 goto draw_best; 471 goto draw_best;
422 472
423 /* now try with S: and FONT_SYSFIXED */ 473 /* now try with S: and FONT_SYSFIXED */
424 rb->snprintf(buf, 31, "B: %d", best_score); 474 rb->snprintf(buf, sizeof(buf), "B: %d", best_score);
425 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); 475 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED);
426 rb->lcd_setfont(FONT_SYSFIXED); 476 rb->lcd_setfont(FONT_SYSFIXED);
427 if(w+BEST_SCORE_X<BACKGROUND_X) 477 if(w + BEST_SCORE_X < BACKGROUND_X)
428 goto draw_best; 478 goto draw_best;
429 479
430 /* then try without Score: and FONT_UI */ 480 /* then try without Score: and FONT_UI */
431 rb->snprintf(buf, 31, "%d", best_score); 481 rb->snprintf(buf, sizeof(buf), "%d", best_score);
432 rb->font_getstringsize(buf, &w, &h, FONT_UI); 482 rb->font_getstringsize(buf, &w, &h, FONT_UI);
433 rb->lcd_setfont(FONT_UI); 483 rb->lcd_setfont(FONT_UI);
434 if(w+BEST_SCORE_X<BACKGROUND_X) 484 if(w + BEST_SCORE_X < BACKGROUND_X)
435 goto draw_best; 485 goto draw_best;
436 486
437 /* as a last resort, don't use Score: and use the system font */ 487 /* as a last resort, don't use Score: and use the system font */
438 rb->snprintf(buf, 31, "%d", best_score); 488 rb->snprintf(buf, sizeof(buf), "%d", best_score);
439 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); 489 rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED);
440 rb->lcd_setfont(FONT_SYSFIXED); 490 rb->lcd_setfont(FONT_SYSFIXED);
441 if(w+BEST_SCORE_X<BACKGROUND_X) 491 if(w + BEST_SCORE_X < BACKGROUND_X)
442 goto draw_best; 492 goto draw_best;
443 else 493 else
444 goto skip_draw_best; 494 goto skip_draw_best;
445 } 495 }
446draw_best: 496draw_best:
447 rb->lcd_putsxy(BEST_SCORE_X, score_y, buf); 497 rb->lcd_putsxy(BEST_SCORE_X, score_y, buf);
498
448skip_draw_best: 499skip_draw_best:
449 rb->lcd_update(); 500 rb->lcd_update();
450 /* revert the font back */ 501
502 /* revert the font */
451 rb->lcd_setfont(WHAT_FONT); 503 rb->lcd_setfont(WHAT_FONT);
452} 504}
505
453#else /* LCD_DEPTH > 1 */ 506#else /* LCD_DEPTH > 1 */
507
454/* 1-bit display :( */ 508/* 1-bit display :( */
455/* bitmaps are unreadable with these screens, so just resort to text */ 509/* bitmaps are unreadable on these screens, so just resort to text-based drawing */
456static void draw(void) 510static void draw(void)
457{ 511{
458 rb->lcd_clear_display(); 512 rb->lcd_clear_display();
513
459 /* Draw the grid */ 514 /* Draw the grid */
460 /* find the biggest tile */ 515 /* find the biggest tile */
461 int biggest_tile=-1; 516 unsigned int biggest_tile = 0;
462 for(int x=0;x<GRID_SIZE;++x) 517 for(int x = 0; x < GRID_SIZE; ++x)
463 { 518 {
464 for(int y=0;y<GRID_SIZE;++y) 519 for(int y = 0; y < GRID_SIZE; ++y)
465 if(ctx->grid[x][y]>biggest_tile) 520 if(ctx->grid[x][y] > biggest_tile)
466 biggest_tile=ctx->grid[x][y]; 521 biggest_tile = ctx->grid[x][y];
467 } 522 }
468 char str[32]; 523
469 rb->snprintf(str, 31,"%d", biggest_tile); 524 char buf[32];
470 int biggest_tile_width=rb->strlen(str)*rb->font_get_width(rb->font_get(WHAT_FONT), '0')+MIN_SPACE; 525
471 for(int y=0;y<GRID_SIZE;++y) 526 rb->snprintf(buf, 32, "%d", biggest_tile);
527
528 int biggest_tile_width = rb->strlen(buf) * rb->font_get_width(rb->font_get(WHAT_FONT), '0') + MIN_SPACE;
529
530 for(int y = 0; y < GRID_SIZE; ++y)
472 { 531 {
473 for(int x=0;x<GRID_SIZE;++x) 532 for(int x = 0; x < GRID_SIZE; ++x)
474 { 533 {
475 if(ctx->grid[x][y]) 534 if(ctx->grid[x][y])
476 { 535 {
477 if(ctx->grid[x][y]>biggest_tile) 536 if(ctx->grid[x][y] > biggest_tile)
478 biggest_tile=ctx->grid[x][y]; 537 biggest_tile = ctx->grid[x][y];
479 rb->snprintf(str,31,"%d", ctx->grid[x][y]); 538 rb->snprintf(buf, 32, "%d", ctx->grid[x][y]);
480 rb->lcd_putsxy(biggest_tile_width*x,y*max_numeral_height+max_numeral_height,str); 539 rb->lcd_putsxy(biggest_tile_width * x, y * max_numeral_height + max_numeral_height, buf);
481 } 540 }
482 } 541 }
483 } 542 }
543
484 /* Now draw the score, and the game title */ 544 /* Now draw the score, and the game title */
485 rb->snprintf(str, 31, "Score: %d", ctx->score); 545 rb->snprintf(buf, 32, "Score: %d", ctx->score);
486 int str_width, str_height; 546 int buf_width, buf_height;
487 rb->font_getstringsize(str, &str_width, &str_height, WHAT_FONT); 547 rb->font_getstringsize(buf, &buf_width, &buf_height, WHAT_FONT);
488 int score_leftmost=LCD_WIDTH-str_width-1; 548
549 int score_leftmost = LCD_WIDTH - buf_width - 1;
489 /* Check if there is enough space to display "Score: ", otherwise, only display the score */ 550 /* Check if there is enough space to display "Score: ", otherwise, only display the score */
490 if(score_leftmost>=0) 551 if(score_leftmost >= 0)
491 rb->lcd_putsxy(score_leftmost,0,str); 552 rb->lcd_putsxy(score_leftmost, 0, buf);
492 else 553 else
493 rb->lcd_putsxy(score_leftmost,0,str+rb->strlen("Score: ")); 554 rb->lcd_putsxy(score_leftmost, 0, buf + rb->strlen("Score: "));
494 /* Reuse the same string for the title */ 555
556 rb->snprintf(buf, 32, "%d", WINNING_TILE);
557 rb->font_getstringsize(buf, &buf_width, &buf_height, WHAT_FONT);
558 if(buf_width < score_leftmost)
559 rb->lcd_putsxy(0, 0, buf);
495 560
496 rb->snprintf(str, 31, "%d", WINNING_TILE);
497 rb->font_getstringsize(str, &str_width, &str_height, WHAT_FONT);
498 if(str_width<score_leftmost)
499 rb->lcd_putsxy(0,0,str);
500 rb->lcd_update(); 561 rb->lcd_update();
501} 562}
563
502#endif /* LCD_DEPTH > 1 */ 564#endif /* LCD_DEPTH > 1 */
565
503/* place a 2 or 4 in a random empty space */ 566/* place a 2 or 4 in a random empty space */
504static void place_random(void) 567static void place_random(void)
505{ 568{
506 int xpos[SPACES],ypos[SPACES]; 569 int xpos[SPACES], ypos[SPACES];
507 int back=0; 570 int back = 0;
508 /* get the indexes of empty spaces */ 571 /* get the indexes of empty spaces */
509 for(int y=0;y<GRID_SIZE;++y) 572 for(int y = 0; y < GRID_SIZE; ++y)
510 for(int x=0;x<GRID_SIZE;++x) 573 for(int x = 0; x < GRID_SIZE; ++x)
511 { 574 {
512 if(!ctx->grid[x][y]) 575 if(!ctx->grid[x][y])
513 { 576 {
514 xpos[back]=x; 577 xpos[back] = x;
515 ypos[back]=y; 578 ypos[back++] = y;
516 ++back;
517 } 579 }
518 } 580 }
581
519 if(!back) 582 if(!back)
520 /* no empty spaces */ 583 /* no empty spaces */
521 return; 584 return;
522 int idx=rand_range(0,back-1);
523 ctx->grid[xpos[idx]][ypos[idx]]=rand_2_or_4();
524}
525 585
526/* copies old_grid to ctx->grid */ 586 int idx = rand_range(0, back - 1);
527static void restore_old_grid(void) 587 ctx->grid[ xpos[idx] ][ ypos[idx] ] = rand_2_or_4();
528{
529 memcpy(&ctx->grid, &old_grid, sizeof(int)*SPACES);
530} 588}
531 589
532/* checks for a win or loss */ 590/* checks for a win or loss */
533static bool check_gameover(void) 591static bool check_gameover(void)
534{ 592{
535 /* first, check for a loss */ 593 /* first, check for a loss */
536 int oldscore=ctx->score; 594 int oldscore = ctx->score;
537 bool have_legal_move=false; 595 bool have_legal_move = false;
538 memset(&merged_grid,0,SPACES*sizeof(bool)); 596
597 memset(&merged_grid, 0, SPACES * sizeof(bool));
539 up(false); 598 up(false);
540 if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) 599 if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid)))
541 { 600 {
542 restore_old_grid(); 601 RESTORE_GRID();
543 ctx->score=oldscore; 602 ctx->score = oldscore;
544 have_legal_move=true; 603 have_legal_move = true;
545 } 604 }
546 restore_old_grid(); 605 RESTORE_GRID();
547 memset(&merged_grid,0,SPACES*sizeof(bool)); 606
607 memset(&merged_grid, 0, SPACES * sizeof(bool));
548 down(false); 608 down(false);
549 if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) 609 if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid)))
550 { 610 {
551 restore_old_grid(); 611 RESTORE_GRID();
552 ctx->score=oldscore; 612 ctx->score = oldscore;
553 have_legal_move=true; 613 have_legal_move = true;
554 } 614 }
555 restore_old_grid(); 615 RESTORE_GRID();
556 memset(&merged_grid,0,SPACES*sizeof(bool)); 616
617 memset(&merged_grid, 0, SPACES * sizeof(bool));
557 left(false); 618 left(false);
558 if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) 619 if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid)))
559 { 620 {
560 restore_old_grid(); 621 RESTORE_GRID();
561 ctx->score=oldscore; 622 ctx->score = oldscore;
562 have_legal_move=true; 623 have_legal_move = true;
563 } 624 }
564 restore_old_grid(); 625 RESTORE_GRID();
565 memset(&merged_grid,0,SPACES*sizeof(bool)); 626
627 memset(&merged_grid, 0, SPACES * sizeof(bool));
566 right(false); 628 right(false);
567 if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) 629 if(memcmp(&old_grid, &ctx->grid, sizeof(ctx->grid)))
568 { 630 {
569 restore_old_grid(); 631 RESTORE_GRID();
570 ctx->score=oldscore; 632 ctx->score = oldscore;
571 have_legal_move=true; 633 have_legal_move = true;
572 } 634 }
573 ctx->score=oldscore; 635 ctx->score = oldscore;
574 if(!have_legal_move) 636 if(!have_legal_move)
575 { 637 {
576 /* no more legal moves */ 638 /* no more legal moves */
577 draw(); /* Shame the player :) */ 639 draw(); /* Shame the player */
578 rb->splash(HZ*2, "Game Over!"); 640 rb->splash(HZ*2, "Game Over!");
579 return true; 641 return true;
580 } 642 }
581 for(int y=0;y<GRID_SIZE;++y) 643
644 for(int y = 0;y < GRID_SIZE; ++y)
582 { 645 {
583 for(int x=0;x<GRID_SIZE;++x) 646 for(int x = 0; x < GRID_SIZE; ++x)
584 { 647 {
585 if(ctx->grid[x][y]==WINNING_TILE && !ctx->already_won) 648 if(ctx->grid[x][y] == WINNING_TILE && !ctx->already_won)
586 { 649 {
587 /* Let the user see the tile in its full glory... */ 650 /* Let the user see the tile in its full glory... */
588 draw(); 651 draw();
589 ctx->already_won=true; 652 ctx->already_won = true;
590 rb->splash(HZ*2,"You win!"); 653 rb->splash(HZ*2,"You win!");
591 /* don't let the user quit here :) */
592 } 654 }
593 } 655 }
594 } 656 }
@@ -602,38 +664,43 @@ static void load_hs(void)
602 if(rb->file_exists(HISCORES_FILE)) 664 if(rb->file_exists(HISCORES_FILE))
603 highscore_load(HISCORES_FILE, highscores, NUM_SCORES); 665 highscore_load(HISCORES_FILE, highscores, NUM_SCORES);
604 else 666 else
605 memset(highscores, 0, sizeof(struct highscore)*NUM_SCORES); 667 memset(highscores, 0, sizeof(struct highscore) * NUM_SCORES);
606} 668}
607 669
608/* initialize the data structures */ 670/* initialize the data structures */
609static void init_game(bool newgame) 671static void init_game(bool newgame)
610{ 672{
611 best_score=highscores[0].score; 673 best_score = highscores[0].score;
612 if(loaded && ctx->score > best_score) 674 if(loaded && ctx->score > best_score)
613 best_score=ctx->score; 675 best_score = ctx->score;
676
614 if(newgame) 677 if(newgame)
615 { 678 {
616 /* initialize the game context */ 679 /* initialize the game context */
617 memset(ctx->grid, 0, sizeof(int)*SPACES); 680 memset(ctx->grid, 0, sizeof(ctx->grid));
618 for(int i=0;i<NUM_STARTING_TILES;++i) 681 for(int i = 0; i < NUM_STARTING_TILES; ++i)
619 { 682 {
620 place_random(); 683 place_random();
621 } 684 }
622 ctx->score=0; 685 ctx->score = 0;
623 ctx->already_won=false; 686 ctx->already_won = false;
624 } 687 }
688
625 /* using the menu resets the font */ 689 /* using the menu resets the font */
626 /* set it again here */ 690 /* set it again here */
691
627 rb->lcd_setfont(WHAT_FONT); 692 rb->lcd_setfont(WHAT_FONT);
693
628 /* Now calculate font sizes */ 694 /* Now calculate font sizes */
629 /* Now get the height of the font */ 695 /* Now get the height of the font */
630 rb->font_getstringsize("0123456789", NULL, &max_numeral_height,WHAT_FONT); 696 rb->font_getstringsize("0123456789", NULL, &max_numeral_height, WHAT_FONT);
631 max_numeral_height+=VERT_SPACING; 697 max_numeral_height += VERT_SPACING;
698
632#if LCD_DEPTH <= 1 699#if LCD_DEPTH <= 1
633 max_numeral_width=rb->font_get_width(rb->font_get(WHAT_FONT), '0'); 700 max_numeral_width = rb->font_get_width(rb->font_get(WHAT_FONT), '0');
634#endif 701#endif
702
635 backlight_ignore_timeout(); 703 backlight_ignore_timeout();
636 rb->lcd_clear_display();
637 draw(); 704 draw();
638} 705}
639 706
@@ -641,17 +708,22 @@ static void init_game(bool newgame)
641static void save_game(void) 708static void save_game(void)
642{ 709{
643 rb->splash(0, "Saving..."); 710 rb->splash(0, "Saving...");
644 int fd=rb->open(RESUME_FILE,O_WRONLY|O_CREAT, 0666); 711 int fd = rb->open(RESUME_FILE, O_WRONLY|O_CREAT, 0666);
645 if(fd<0) 712 if(fd < 0)
646 { 713 {
647 return; 714 return;
648 } 715 }
649 ctx->cksum=0; 716
650 for(int x=0;x<GRID_SIZE;++x) 717 /* calculate checksum */
651 for(int y=0;y<GRID_SIZE;++y) 718 ctx->cksum = 0;
652 ctx->cksum+=ctx->grid[x][y]; 719
653 ctx->cksum^=ctx->score; 720 for(int x = 0; x < GRID_SIZE; ++x)
654 rb->write(fd, ctx,sizeof(struct game_ctx_t)); 721 for(int y = 0; y < GRID_SIZE; ++y)
722 ctx->cksum += ctx->grid[x][y];
723
724 ctx->cksum ^= ctx->score;
725
726 rb->write(fd, ctx, sizeof(struct game_ctx_t));
655 rb->close(fd); 727 rb->close(fd);
656 rb->lcd_update(); 728 rb->lcd_update();
657} 729}
@@ -659,59 +731,67 @@ static void save_game(void)
659/* loads a saved game, returns true on success */ 731/* loads a saved game, returns true on success */
660static bool load_game(void) 732static bool load_game(void)
661{ 733{
662 int success=0; 734 int success = 0;
663 int fd=rb->open(RESUME_FILE, O_RDONLY); 735 int fd = rb->open(RESUME_FILE, O_RDONLY);
664 if(fd<0) 736 if(fd < 0)
665 { 737 {
666 rb->remove(RESUME_FILE); 738 rb->remove(RESUME_FILE);
667 return false; 739 return false;
668 } 740 }
669 int numread=rb->read(fd, ctx, sizeof(struct game_ctx_t)); 741
670 int calc=0; 742 int numread = rb->read(fd, ctx, sizeof(struct game_ctx_t));
671 for(int x=0;x<GRID_SIZE;++x) 743
672 for(int y=0;y<GRID_SIZE;++y) 744 /* verify checksum */
673 calc+=ctx->grid[x][y]; 745 unsigned int calc = 0;
674 calc^=ctx->score; 746 for(int x = 0; x < GRID_SIZE; ++x)
675 if(numread==sizeof(struct game_ctx_t) && calc==ctx->cksum) 747 for(int y = 0; y < GRID_SIZE; ++y)
748 calc += ctx->grid[x][y];
749
750 calc ^= ctx->score;
751
752 if(numread == sizeof(struct game_ctx_t) && calc == ctx->cksum)
676 ++success; 753 ++success;
754
677 rb->close(fd); 755 rb->close(fd);
678 rb->remove(RESUME_FILE); 756 rb->remove(RESUME_FILE);
679 return (success==1); 757
758 return (success > 0);
680} 759}
681 760
682/* update the highscores with ctx->score */ 761/* update the highscores with ctx->score */
683static void hs_check_update(bool noshow) 762static void hs_check_update(bool noshow)
684{ 763{
685 /* first, find the biggest tile to show as the level */ 764 /* first, find the biggest tile to show as the level */
686 int biggest=0; 765 unsigned int biggest = 0;
687 for(int x=0;x<GRID_SIZE;++x) 766 for(int x = 0; x < GRID_SIZE; ++x)
688 { 767 {
689 for(int y=0;y<GRID_SIZE;++y) 768 for(int y = 0; y < GRID_SIZE; ++y)
690 { 769 {
691 if(ctx->grid[x][y]>biggest) 770 if(ctx->grid[x][y] > biggest)
692 biggest=ctx->grid[x][y]; 771 biggest = ctx->grid[x][y];
693 } 772 }
694 } 773 }
695 int hs_idx=highscore_update(ctx->score,biggest, "", highscores,NUM_SCORES); 774
775 int hs_idx = highscore_update(ctx->score,biggest, "", highscores,NUM_SCORES);
696 if(!noshow) 776 if(!noshow)
697 { 777 {
698 /* show the scores if there is a new high score */ 778 /* show the scores if there is a new high score */
699 if(hs_idx>=0) 779 if(hs_idx >= 0)
700 { 780 {
701 rb->splashf(HZ*2,"New High Score: %d", ctx->score); 781 rb->splashf(HZ*2, "New High Score: %d", ctx->score);
702 rb->lcd_clear_display(); 782 rb->lcd_clear_display();
703 highscore_show(hs_idx,highscores,NUM_SCORES,true); 783 highscore_show(hs_idx, highscores, NUM_SCORES, true);
704 } 784 }
705 } 785 }
706 highscore_save(HISCORES_FILE,highscores,NUM_SCORES); 786 highscore_save(HISCORES_FILE, highscores, NUM_SCORES);
707} 787}
708 788
709/* asks the user if they wish to quit */ 789/* asks the user if they wish to quit */
710static bool confirm_quit(void) 790static bool confirm_quit(void)
711{ 791{
712 const struct text_message prompt={(const char*[]){"Are you sure?", "This will clear your current game."}, 2}; 792 const struct text_message prompt = { (const char*[]) {"Are you sure?", "This will clear your current game."}, 2};
713 enum yesno_res response=rb->gui_syncyesno_run(&prompt, NULL, NULL); 793 enum yesno_res response = rb->gui_syncyesno_run(&prompt, NULL, NULL);
714 if(response==YESNO_NO) 794 if(response == YESNO_NO)
715 return false; 795 return false;
716 else 796 else
717 return true; 797 return true;
@@ -720,7 +800,7 @@ static bool confirm_quit(void)
720/* show the pause menu */ 800/* show the pause menu */
721static int do_2048_pause_menu(void) 801static int do_2048_pause_menu(void)
722{ 802{
723 int sel=0; 803 int sel = 0;
724 MENUITEM_STRINGLIST(menu,"2048 Menu", NULL, 804 MENUITEM_STRINGLIST(menu,"2048 Menu", NULL,
725 "Resume Game", 805 "Resume Game",
726 "Start New Game", 806 "Start New Game",
@@ -729,7 +809,7 @@ static int do_2048_pause_menu(void)
729 "Help", 809 "Help",
730 "Quit without Saving", 810 "Quit without Saving",
731 "Quit"); 811 "Quit");
732 bool quit=false; 812 bool quit = false;
733 while(!quit) 813 while(!quit)
734 { 814 {
735 switch(rb->do_menu(&menu, &sel, NULL, false)) 815 switch(rb->do_menu(&menu, &sel, NULL, false))
@@ -748,7 +828,7 @@ static int do_2048_pause_menu(void)
748 } 828 }
749 } 829 }
750 case 2: 830 case 2:
751 highscore_show(-1,highscores, NUM_SCORES, true); 831 highscore_show(-1, highscores, NUM_SCORES, true);
752 break; 832 break;
753 case 3: 833 case 3:
754 playback_control(NULL); 834 playback_control(NULL);
@@ -782,80 +862,85 @@ static void exit_handler(void)
782#endif 862#endif
783 return; 863 return;
784} 864}
865
785static bool check_hs; 866static bool check_hs;
867
786/* main game loop */ 868/* main game loop */
787static enum plugin_status do_game(bool newgame) 869static enum plugin_status do_game(bool newgame)
788{ 870{
789 init_game(newgame); 871 init_game(newgame);
790 rb_atexit(&exit_handler); 872 rb_atexit(exit_handler);
791 int made_move=0; 873 int made_move = 0;
792 while(1) 874 while(1)
793 { 875 {
794#ifdef HAVE_ADJUSTABLE_CPU_FREQ 876#ifdef HAVE_ADJUSTABLE_CPU_FREQ
795 rb->cpu_boost(false); /* Save battery when idling */ 877 rb->cpu_boost(false); /* Save battery when idling */
796#endif 878#endif
797 /* Wait for a button press */ 879 /* Wait for a button press */
798 int button=pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts)); 880 int button = pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts));
799 made_move=0; 881 made_move = 0;
800 memset(&merged_grid,0,SPACES*sizeof(bool)); 882
883 memset(&merged_grid, 0, SPACES*sizeof(bool));
801 memcpy(&old_grid, &ctx->grid, sizeof(int)*SPACES); 884 memcpy(&old_grid, &ctx->grid, sizeof(int)*SPACES);
802 int grid_before_anim_step[GRID_SIZE][GRID_SIZE]; 885
886 unsigned int grid_before_anim_step[GRID_SIZE][GRID_SIZE];
887
803#ifdef HAVE_ADJUSTABLE_CPU_FREQ 888#ifdef HAVE_ADJUSTABLE_CPU_FREQ
804 rb->cpu_boost(true); /* doing work now... */ 889 rb->cpu_boost(true); /* doing work now... */
805#endif 890#endif
806 switch(button) 891 switch(button)
807 { 892 {
808 case KEY_UP: 893 case KEY_UP:
809 for(int i=0;i<GRID_SIZE-1;++i) 894 for(int i = 0; i < GRID_SIZE - 1; ++i)
810 { 895 {
811 memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); 896 memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid));
812 up(true); 897 up(true);
813 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) 898 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)))
814 { 899 {
815 rb->sleep(ANIM_SLEEPTIME); 900 rb->sleep(ANIM_SLEEPTIME);
816 draw(); 901 draw();
817 } 902 }
818 } 903 }
819 made_move=1; 904 made_move = 1;
820 break; 905 break;
821 case KEY_DOWN: 906 case KEY_DOWN:
822 for(int i=0;i<GRID_SIZE-1;++i) 907 for(int i = 0; i < GRID_SIZE - 1; ++i)
823 { 908 {
824 memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); 909 memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid));
825 down(true); 910 down(true);
826 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) 911 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)))
827 { 912 {
828 rb->sleep(ANIM_SLEEPTIME); 913 rb->sleep(ANIM_SLEEPTIME);
829 draw(); 914 draw();
830 } 915 }
831 } 916 }
832 made_move=1; 917 made_move = 1;
833 break; 918 break;
834 case KEY_LEFT: 919 case KEY_LEFT:
835 for(int i=0;i<GRID_SIZE-1;++i) 920 for(int i = 0; i < GRID_SIZE - 1; ++i)
836 { 921 {
837 memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); 922 memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid));
838 left(true); 923 left(true);
839 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) 924 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)))
840 { 925 {
841 rb->sleep(ANIM_SLEEPTIME); 926 rb->sleep(ANIM_SLEEPTIME);
842 draw(); 927 draw();
843 } 928 }
844 } 929 }
845 made_move=1; 930 made_move = 1;
846 break; 931 break;
847 case KEY_RIGHT: 932 case KEY_RIGHT:
848 for(int i=0;i<GRID_SIZE-1;++i) 933 for(int i = 0; i < GRID_SIZE - 1; ++i)
849 { 934 {
850 memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); 935 memcpy(grid_before_anim_step, ctx->grid, sizeof(ctx->grid));
851 right(true); 936 right(true);
852 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) 937 if(memcmp(grid_before_anim_step, ctx->grid, sizeof(ctx->grid)))
853 { 938 {
854 rb->sleep(ANIM_SLEEPTIME); 939 rb->sleep(ANIM_SLEEPTIME);
855 draw(); 940 draw();
856 } 941 }
857 } 942 }
858 made_move=1; 943 made_move = 1;
859 break; 944 break;
860 case KEY_EXIT: 945 case KEY_EXIT:
861 switch(do_2048_pause_menu()) 946 switch(do_2048_pause_menu())
@@ -864,33 +949,33 @@ static enum plugin_status do_game(bool newgame)
864 break; 949 break;
865 case 1: /* new game */ 950 case 1: /* new game */
866 init_game(true); 951 init_game(true);
867 made_move=1; 952 made_move = 1;
868 continue; 953 continue;
869 break;
870 case 2: /* quit without saving */ 954 case 2: /* quit without saving */
871 check_hs=true; 955 check_hs = true;
872 rb->remove(RESUME_FILE); 956 rb->remove(RESUME_FILE);
873 return PLUGIN_ERROR; 957 return PLUGIN_ERROR;
874 case 3: /* save and quit */ 958 case 3: /* save and quit */
875 check_hs=false; 959 check_hs = false;
876 save_game(); 960 save_game();
877 return PLUGIN_ERROR; 961 return PLUGIN_ERROR;
878 } 962 }
879 break; 963 break;
880 default: 964 default:
881 { 965 {
882 exit_on_usb(button); /* handles poweroff and USB events */ 966 exit_on_usb(button); /* handle poweroff and USB events */
883 break; 967 break;
884 } 968 }
885 } 969 }
970
886 if(made_move) 971 if(made_move)
887 { 972 {
888 /* Check if we actually moved, then add random */ 973 /* Check if any tiles moved, then add random */
889 if(memcmp(&old_grid, ctx->grid, sizeof(int)*SPACES)) 974 if(memcmp(&old_grid, ctx->grid, sizeof(ctx->grid)))
890 { 975 {
891 place_random(); 976 place_random();
892 } 977 }
893 memcpy(&old_grid, ctx->grid, sizeof(int)*SPACES); 978 memcpy(&old_grid, ctx->grid, sizeof(ctx->grid));
894 if(check_gameover()) 979 if(check_gameover())
895 return PLUGIN_OK; 980 return PLUGIN_OK;
896 draw(); 981 draw();
@@ -906,8 +991,8 @@ static enum plugin_status do_game(bool newgame)
906/* used to hide resume option when there is no save */ 991/* used to hide resume option when there is no save */
907static int mainmenu_cb(int action, const struct menu_item_ex *this_item) 992static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
908{ 993{
909 int idx=((intptr_t)this_item); 994 int idx = ((intptr_t)this_item);
910 if(action==ACTION_REQUEST_MENUITEM && !loaded && (idx==0 || idx==5)) 995 if(action == ACTION_REQUEST_MENUITEM && !loaded && (idx == 0 || idx == 5))
911 return ACTION_EXIT_MENUITEM; 996 return ACTION_EXIT_MENUITEM;
912 return action; 997 return action;
913} 998}
@@ -915,29 +1000,37 @@ static int mainmenu_cb(int action, const struct menu_item_ex *this_item)
915/* show the main menu */ 1000/* show the main menu */
916static enum plugin_status do_2048_menu(void) 1001static enum plugin_status do_2048_menu(void)
917{ 1002{
918 int sel=0; 1003 int sel = 0;
919 loaded=load_game(); 1004 loaded = load_game();
920 MENUITEM_STRINGLIST(menu,"2048 Menu", mainmenu_cb, "Resume Game", "Start New Game","High Scores","Playback Control", "Help", "Quit without Saving", "Quit"); 1005 MENUITEM_STRINGLIST(menu,
921 bool quit=false; 1006 "2048 Menu",
1007 mainmenu_cb,
1008 "Resume Game",
1009 "Start New Game",
1010 "High Scores",
1011 "Playback Control",
1012 "Help",
1013 "Quit without Saving",
1014 "Quit");
1015 bool quit = false;
922 while(!quit) 1016 while(!quit)
923 { 1017 {
924 int item; 1018 switch(rb->do_menu(&menu, &sel, NULL, false))
925 switch(item=rb->do_menu(&menu,&sel,NULL,false))
926 { 1019 {
927 case 0: /* Start new game or resume a game */ 1020 case 0: /* Start new game or resume a game */
928 case 1: 1021 case 1:
929 { 1022 {
930 if(item==1 && loaded) 1023 if(sel == 1 && loaded)
931 { 1024 {
932 if(!confirm_quit()) 1025 if(!confirm_quit())
933 break; 1026 break;
934 } 1027 }
935 enum plugin_status ret=do_game(item==1); 1028 enum plugin_status ret = do_game(sel == 1);
936 switch(ret) 1029 switch(ret)
937 { 1030 {
938 case PLUGIN_OK: 1031 case PLUGIN_OK:
939 { 1032 {
940 loaded=false; 1033 loaded = false;
941 rb->remove(RESUME_FILE); 1034 rb->remove(RESUME_FILE);
942 hs_check_update(false); 1035 hs_check_update(false);
943 break; 1036 break;
@@ -956,7 +1049,7 @@ static enum plugin_status do_2048_menu(void)
956 break; 1049 break;
957 } 1050 }
958 case 2: 1051 case 2:
959 highscore_show(-1,highscores, NUM_SCORES, true); 1052 highscore_show(-1, highscores, NUM_SCORES, true);
960 break; 1053 break;
961 case 3: 1054 case 3:
962 playback_control(NULL); 1055 playback_control(NULL);
@@ -977,6 +1070,8 @@ static enum plugin_status do_2048_menu(void)
977 } 1070 }
978 return PLUGIN_OK; 1071 return PLUGIN_OK;
979} 1072}
1073
1074/* plugin entry point */
980enum plugin_status plugin_start(const void* param) 1075enum plugin_status plugin_start(const void* param)
981{ 1076{
982 (void)param; 1077 (void)param;
@@ -985,9 +1080,12 @@ enum plugin_status plugin_start(const void* param)
985 rb->lcd_setfont(WHAT_FONT); 1080 rb->lcd_setfont(WHAT_FONT);
986 1081
987 /* now start the game menu */ 1082 /* now start the game menu */
988 enum plugin_status ret=do_2048_menu(); 1083 enum plugin_status ret = do_2048_menu();
989 highscore_save(HISCORES_FILE,highscores,NUM_SCORES); 1084
1085 highscore_save(HISCORES_FILE, highscores, NUM_SCORES);
990 cleanup(); 1086 cleanup();
991 abnormal_exit=false; 1087
1088 abnormal_exit = false;
1089
992 return ret; 1090 return ret;
993} 1091}