summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/mines.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/mines.c')
-rw-r--r--apps/plugins/puzzles/src/mines.c218
1 files changed, 101 insertions, 117 deletions
diff --git a/apps/plugins/puzzles/src/mines.c b/apps/plugins/puzzles/src/mines.c
index 706777c4f1..e1441fae13 100644
--- a/apps/plugins/puzzles/src/mines.c
+++ b/apps/plugins/puzzles/src/mines.c
@@ -3,8 +3,7 @@
3 * 3 *
4 * Still TODO: 4 * Still TODO:
5 * 5 *
6 * - think about configurably supporting question marks. Once, 6 * - think about configurably supporting question marks.
7 * that is, we've thought about configurability in general!
8 */ 7 */
9 8
10#include <stdio.h> 9#include <stdio.h>
@@ -12,7 +11,12 @@
12#include <string.h> 11#include <string.h>
13#include <assert.h> 12#include <assert.h>
14#include <ctype.h> 13#include <ctype.h>
15#include <math.h> 14#include <limits.h>
15#ifdef NO_TGMATH_H
16# include <math.h>
17#else
18# include <tgmath.h>
19#endif
16 20
17#include "tree234.h" 21#include "tree234.h"
18#include "puzzles.h" 22#include "puzzles.h"
@@ -34,8 +38,8 @@ enum {
34#else 38#else
35#define BORDER (TILE_SIZE * 3 / 2) 39#define BORDER (TILE_SIZE * 3 / 2)
36#endif 40#endif
37#define HIGHLIGHT_WIDTH (TILE_SIZE / 10) 41#define HIGHLIGHT_WIDTH (TILE_SIZE / 10 ? TILE_SIZE / 10 : 1)
38#define OUTER_HIGHLIGHT_WIDTH (BORDER / 10) 42#define OUTER_HIGHLIGHT_WIDTH (BORDER / 10 ? BORDER / 10 : 1)
39#define COORD(x) ( (x) * TILE_SIZE + BORDER ) 43#define COORD(x) ( (x) * TILE_SIZE + BORDER )
40#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 ) 44#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
41 45
@@ -162,7 +166,9 @@ static void decode_params(game_params *params, char const *string)
162 params->n = atoi(p); 166 params->n = atoi(p);
163 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++; 167 while (*p && (*p == '.' || isdigit((unsigned char)*p))) p++;
164 } else { 168 } else {
165 params->n = params->w * params->h / 10; 169 if (params->h > 0 && params->w > 0 &&
170 params->w <= INT_MAX / params->h)
171 params->n = params->w * params->h / 10;
166 } 172 }
167 173
168 while (*p) { 174 while (*p) {
@@ -250,7 +256,7 @@ static const char *validate_params(const game_params *params, bool full)
250 * blocking the way and no idea what's behind them, or one mine 256 * blocking the way and no idea what's behind them, or one mine
251 * and no way to know which of the two rows it's in. If the 257 * and no way to know which of the two rows it's in. If the
252 * mine count is even you can create a soluble grid by packing 258 * mine count is even you can create a soluble grid by packing
253 * all the mines at one end (so what when you hit a two-mine 259 * all the mines at one end (so that when you hit a two-mine
254 * wall there are only as many covered squares left as there 260 * wall there are only as many covered squares left as there
255 * are mines); but if it's odd, you are doomed, because you 261 * are mines); but if it's odd, you are doomed, because you
256 * _have_ to have a gap somewhere which you can't determine the 262 * _have_ to have a gap somewhere which you can't determine the
@@ -258,12 +264,25 @@ static const char *validate_params(const game_params *params, bool full)
258 */ 264 */
259 if (full && params->unique && (params->w <= 2 || params->h <= 2)) 265 if (full && params->unique && (params->w <= 2 || params->h <= 2))
260 return "Width and height must both be greater than two"; 266 return "Width and height must both be greater than two";
267 if (params->w < 1 || params->h < 1)
268 return "Width and height must both be at least one";
269 if (params->w > SHRT_MAX || params->h > SHRT_MAX)
270 return "Neither width nor height may be unreasonably large";
271 /*
272 * We use random_upto() to place mines, and its maximum limit is 2^28-1.
273 */
274#if (1<<28)-1 < INT_MAX
275 if (params->w > ((1<<28)-1) / params->h)
276#else
277 if (params->w > INT_MAX / params->h)
278#endif
279 return "Width times height must not be unreasonably large";
261 if (params->n < 0) 280 if (params->n < 0)
262 return "Mine count may not be negative"; 281 return "Mine count may not be negative";
263 if (params->n > params->w * params->h - 9)
264 return "Too many mines for grid size";
265 if (params->n < 1) 282 if (params->n < 1)
266 return "Number of mines must be greater than zero"; 283 return "Number of mines must be greater than zero";
284 if (params->n > params->w * params->h - 9)
285 return "Too many mines for grid size";
267 286
268 /* 287 /*
269 * FIXME: Need more constraints here. Not sure what the 288 * FIXME: Need more constraints here. Not sure what the
@@ -428,7 +447,9 @@ static void ss_add(struct setstore *ss, int x, int y, int mask, int mines)
428 * Create a set structure and add it to the tree. 447 * Create a set structure and add it to the tree.
429 */ 448 */
430 s = snew(struct set); 449 s = snew(struct set);
450 assert(SHRT_MIN <= x && x <= SHRT_MAX);
431 s->x = x; 451 s->x = x;
452 assert(SHRT_MIN <= y && y <= SHRT_MAX);
432 s->y = y; 453 s->y = y;
433 s->mask = mask; 454 s->mask = mask;
434 s->mines = mines; 455 s->mines = mines;
@@ -499,7 +520,9 @@ static struct set **ss_overlap(struct setstore *ss, int x, int y, int mask)
499 /* 520 /*
500 * Find the first set with these top left coordinates. 521 * Find the first set with these top left coordinates.
501 */ 522 */
523 assert(SHRT_MIN <= xx && xx <= SHRT_MAX);
502 stmp.x = xx; 524 stmp.x = xx;
525 assert(SHRT_MIN <= yy && yy <= SHRT_MAX);
503 stmp.y = yy; 526 stmp.y = yy;
504 stmp.mask = 0; 527 stmp.mask = 0;
505 528
@@ -1880,72 +1903,7 @@ static char *describe_layout(bool *grid, int area, int x, int y,
1880static bool *new_mine_layout(int w, int h, int n, int x, int y, bool unique, 1903static bool *new_mine_layout(int w, int h, int n, int x, int y, bool unique,
1881 random_state *rs, char **game_desc) 1904 random_state *rs, char **game_desc)
1882{ 1905{
1883 bool *grid; 1906 bool *grid = minegen(w, h, n, x, y, unique, rs);
1884
1885#ifdef TEST_OBFUSCATION
1886 static int tested_obfuscation = false;
1887 if (!tested_obfuscation) {
1888 /*
1889 * A few simple test vectors for the obfuscator.
1890 *
1891 * First test: the 28-bit stream 1234567. This divides up
1892 * into 1234 and 567[0]. The SHA of 56 70 30 (appending
1893 * "0") is 15ce8ab946640340bbb99f3f48fd2c45d1a31d30. Thus,
1894 * we XOR the 16-bit string 15CE into the input 1234 to get
1895 * 07FA. Next, we SHA that with "0": the SHA of 07 FA 30 is
1896 * 3370135c5e3da4fed937adc004a79533962b6391. So we XOR the
1897 * 12-bit string 337 into the input 567 to get 650. Thus
1898 * our output is 07FA650.
1899 */
1900 {
1901 unsigned char bmp1[] = "\x12\x34\x56\x70";
1902 obfuscate_bitmap(bmp1, 28, false);
1903 printf("test 1 encode: %s\n",
1904 memcmp(bmp1, "\x07\xfa\x65\x00", 4) ? "failed" : "passed");
1905 obfuscate_bitmap(bmp1, 28, true);
1906 printf("test 1 decode: %s\n",
1907 memcmp(bmp1, "\x12\x34\x56\x70", 4) ? "failed" : "passed");
1908 }
1909 /*
1910 * Second test: a long string to make sure we switch from
1911 * one SHA to the next correctly. My input string this time
1912 * is simply fifty bytes of zeroes.
1913 */
1914 {
1915 unsigned char bmp2[50];
1916 unsigned char bmp2a[50];
1917 memset(bmp2, 0, 50);
1918 memset(bmp2a, 0, 50);
1919 obfuscate_bitmap(bmp2, 50 * 8, false);
1920 /*
1921 * SHA of twenty-five zero bytes plus "0" is
1922 * b202c07b990c01f6ff2d544707f60e506019b671. SHA of
1923 * twenty-five zero bytes plus "1" is
1924 * fcb1d8b5a2f6b592fe6780b36aa9d65dd7aa6db9. Thus our
1925 * first half becomes
1926 * b202c07b990c01f6ff2d544707f60e506019b671fcb1d8b5a2.
1927 *
1928 * SHA of that lot plus "0" is
1929 * 10b0af913db85d37ca27f52a9f78bba3a80030db. SHA of the
1930 * same string plus "1" is
1931 * 3d01d8df78e76d382b8106f480135a1bc751d725. So the
1932 * second half becomes
1933 * 10b0af913db85d37ca27f52a9f78bba3a80030db3d01d8df78.
1934 */
1935 printf("test 2 encode: %s\n",
1936 memcmp(bmp2, "\xb2\x02\xc0\x7b\x99\x0c\x01\xf6\xff\x2d\x54"
1937 "\x47\x07\xf6\x0e\x50\x60\x19\xb6\x71\xfc\xb1\xd8"
1938 "\xb5\xa2\x10\xb0\xaf\x91\x3d\xb8\x5d\x37\xca\x27"
1939 "\xf5\x2a\x9f\x78\xbb\xa3\xa8\x00\x30\xdb\x3d\x01"
1940 "\xd8\xdf\x78", 50) ? "failed" : "passed");
1941 obfuscate_bitmap(bmp2, 50 * 8, true);
1942 printf("test 2 decode: %s\n",
1943 memcmp(bmp2, bmp2a, 50) ? "failed" : "passed");
1944 }
1945 }
1946#endif
1947
1948 grid = minegen(w, h, n, x, y, unique, rs);
1949 1907
1950 if (game_desc) 1908 if (game_desc)
1951 *game_desc = describe_layout(grid, w * h, x, y, true); 1909 *game_desc = describe_layout(grid, w * h, x, y, true);
@@ -2001,6 +1959,8 @@ static const char *validate_desc(const game_params *params, const char *desc)
2001 desc++; 1959 desc++;
2002 if (!*desc || !isdigit((unsigned char)*desc)) 1960 if (!*desc || !isdigit((unsigned char)*desc))
2003 return "No initial mine count in game description"; 1961 return "No initial mine count in game description";
1962 if (atoi(desc) > wh - 9)
1963 return "Too many mines for grid size";
2004 while (*desc && isdigit((unsigned char)*desc)) 1964 while (*desc && isdigit((unsigned char)*desc))
2005 desc++; /* skip over mine count */ 1965 desc++; /* skip over mine count */
2006 if (*desc != ',') 1966 if (*desc != ',')
@@ -2139,6 +2099,8 @@ static int open_square(game_state *state, int x, int y)
2139 break; 2099 break;
2140 } 2100 }
2141 2101
2102 /* If the player has already lost, don't let them win as well. */
2103 if (state->dead) return 0;
2142 /* 2104 /*
2143 * Finally, scan the grid and see if exactly as many squares 2105 * Finally, scan the grid and see if exactly as many squares
2144 * are still covered as there are mines. If so, set the `won' 2106 * are still covered as there are mines. If so, set the `won'
@@ -2365,7 +2327,7 @@ static game_ui *new_ui(const game_state *state)
2365 ui->completed = false; 2327 ui->completed = false;
2366 ui->flash_is_death = false; /* *shrug* */ 2328 ui->flash_is_death = false; /* *shrug* */
2367 ui->cur_x = ui->cur_y = 0; 2329 ui->cur_x = ui->cur_y = 0;
2368 ui->cur_visible = false; 2330 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2369 return ui; 2331 return ui;
2370} 2332}
2371 2333
@@ -2387,7 +2349,8 @@ static char *encode_ui(const game_ui *ui)
2387 return dupstr(buf); 2349 return dupstr(buf);
2388} 2350}
2389 2351
2390static void decode_ui(game_ui *ui, const char *encoding) 2352static void decode_ui(game_ui *ui, const char *encoding,
2353 const game_state *state)
2391{ 2354{
2392 int p= 0; 2355 int p= 0;
2393 sscanf(encoding, "D%d%n", &ui->deaths, &p); 2356 sscanf(encoding, "D%d%n", &ui->deaths, &p);
@@ -2402,6 +2365,35 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2402 ui->completed = true; 2365 ui->completed = true;
2403} 2366}
2404 2367
2368static const char *current_key_label(const game_ui *ui,
2369 const game_state *state, int button)
2370{
2371 int cx = ui->cur_x, cy = ui->cur_y;
2372 int v = state->grid[cy * state->w + cx];
2373
2374 if (state->dead || state->won || !ui->cur_visible) return "";
2375 if (button == CURSOR_SELECT2) {
2376 if (v == -2) return "Mark";
2377 if (v == -1) return "Unmark";
2378 return "";
2379 }
2380 if (button == CURSOR_SELECT) {
2381 int dy, dx, n = 0;
2382 if (v == -2 || v == -3) return "Uncover";
2383 if (v == 0) return "";
2384 /* Count mine markers. */
2385 for (dy = -1; dy <= +1; dy++)
2386 for (dx = -1; dx <= +1; dx++)
2387 if (cx+dx >= 0 && cx+dx < state->w &&
2388 cy+dy >= 0 && cy+dy < state->h) {
2389 if (state->grid[(cy+dy)*state->w+(cx+dx)] == -1)
2390 n++;
2391 }
2392 if (n == v) return "Clear";
2393 }
2394 return "";
2395}
2396
2405struct game_drawstate { 2397struct game_drawstate {
2406 int w, h, tilesize, bg; 2398 int w, h, tilesize, bg;
2407 bool started; 2399 bool started;
@@ -2432,22 +2424,20 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2432 cx = FROMCOORD(x); 2424 cx = FROMCOORD(x);
2433 cy = FROMCOORD(y); 2425 cy = FROMCOORD(y);
2434 2426
2435 if (IS_CURSOR_MOVE(button)) { 2427 if (IS_CURSOR_MOVE(button))
2436 move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h, false); 2428 return move_cursor(button, &ui->cur_x, &ui->cur_y, from->w, from->h,
2437 ui->cur_visible = true; 2429 false, &ui->cur_visible);
2438 return UI_UPDATE;
2439 }
2440 if (IS_CURSOR_SELECT(button)) { 2430 if (IS_CURSOR_SELECT(button)) {
2441 int v = from->grid[ui->cur_y * from->w + ui->cur_x]; 2431 int v = from->grid[ui->cur_y * from->w + ui->cur_x];
2442 2432
2443 if (!ui->cur_visible) { 2433 if (!ui->cur_visible) {
2444 ui->cur_visible = true; 2434 ui->cur_visible = true;
2445 return UI_UPDATE; 2435 return MOVE_UI_UPDATE;
2446 } 2436 }
2447 if (button == CURSOR_SELECT2) { 2437 if (button == CURSOR_SELECT2) {
2448 /* As for RIGHT_BUTTON; only works on covered square. */ 2438 /* As for RIGHT_BUTTON; only works on covered square. */
2449 if (v != -2 && v != -1) 2439 if (v != -2 && v != -1)
2450 return NULL; 2440 return MOVE_NO_EFFECT;
2451 sprintf(buf, "F%d,%d", ui->cur_x, ui->cur_y); 2441 sprintf(buf, "F%d,%d", ui->cur_x, ui->cur_y);
2452 return dupstr(buf); 2442 return dupstr(buf);
2453 } 2443 }
@@ -2468,7 +2458,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2468 if (button == LEFT_BUTTON || button == LEFT_DRAG || 2458 if (button == LEFT_BUTTON || button == LEFT_DRAG ||
2469 button == MIDDLE_BUTTON || button == MIDDLE_DRAG) { 2459 button == MIDDLE_BUTTON || button == MIDDLE_DRAG) {
2470 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) 2460 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2471 return NULL; 2461 return MOVE_UNUSED;
2472 2462
2473 /* 2463 /*
2474 * Mouse-downs and mouse-drags just cause highlighting 2464 * Mouse-downs and mouse-drags just cause highlighting
@@ -2482,12 +2472,12 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2482 else if (button == MIDDLE_BUTTON) 2472 else if (button == MIDDLE_BUTTON)
2483 ui->validradius = 1; 2473 ui->validradius = 1;
2484 ui->cur_visible = false; 2474 ui->cur_visible = false;
2485 return UI_UPDATE; 2475 return MOVE_UI_UPDATE;
2486 } 2476 }
2487 2477
2488 if (button == RIGHT_BUTTON) { 2478 if (button == RIGHT_BUTTON) {
2489 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) 2479 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2490 return NULL; 2480 return MOVE_UNUSED;
2491 2481
2492 /* 2482 /*
2493 * Right-clicking only works on a covered square, and it 2483 * Right-clicking only works on a covered square, and it
@@ -2498,7 +2488,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2498 */ 2488 */
2499 if (from->grid[cy * from->w + cx] != -2 && 2489 if (from->grid[cy * from->w + cx] != -2 &&
2500 from->grid[cy * from->w + cx] != -1) 2490 from->grid[cy * from->w + cx] != -1)
2501 return NULL; 2491 return MOVE_NO_EFFECT;
2502 2492
2503 sprintf(buf, "F%d,%d", cx, cy); 2493 sprintf(buf, "F%d,%d", cx, cy);
2504 return dupstr(buf); 2494 return dupstr(buf);
@@ -2509,11 +2499,12 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2509 ui->hradius = 0; 2499 ui->hradius = 0;
2510 2500
2511 /* 2501 /*
2512 * At this stage we must never return NULL: we have adjusted 2502 * At this stage we must never return MOVE_UNUSED or
2513 * the ui, so at worst we return UI_UPDATE. 2503 * MOVE_NO_EFFECT: we have adjusted the ui, so at worst we
2504 * return MOVE_UI_UPDATE.
2514 */ 2505 */
2515 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h) 2506 if (cx < 0 || cx >= from->w || cy < 0 || cy >= from->h)
2516 return UI_UPDATE; 2507 return MOVE_UI_UPDATE;
2517 2508
2518 /* 2509 /*
2519 * Left-clicking on a covered square opens a tile. Not 2510 * Left-clicking on a covered square opens a tile. Not
@@ -2533,7 +2524,7 @@ static char *interpret_move(const game_state *from, game_ui *ui,
2533 } 2524 }
2534 goto uncover; 2525 goto uncover;
2535 } 2526 }
2536 return NULL; 2527 return MOVE_UNUSED;
2537 2528
2538uncover: 2529uncover:
2539 { 2530 {
@@ -2591,7 +2582,7 @@ uncover:
2591 } 2582 }
2592 } 2583 }
2593 2584
2594 return UI_UPDATE; 2585 return MOVE_UI_UPDATE;
2595 } 2586 }
2596} 2587}
2597 2588
@@ -2603,6 +2594,7 @@ static game_state *execute_move(const game_state *from, const char *move)
2603 if (!strcmp(move, "S")) { 2594 if (!strcmp(move, "S")) {
2604 int yy, xx; 2595 int yy, xx;
2605 2596
2597 if (!from->layout->mines) return NULL; /* Game not started. */
2606 ret = dup_game(from); 2598 ret = dup_game(from);
2607 if (!ret->dead) { 2599 if (!ret->dead) {
2608 /* 2600 /*
@@ -2656,12 +2648,17 @@ static game_state *execute_move(const game_state *from, const char *move)
2656 2648
2657 return ret; 2649 return ret;
2658 } else { 2650 } else {
2651 /* Dead players should stop trying to move. */
2652 if (from->dead)
2653 return NULL;
2659 ret = dup_game(from); 2654 ret = dup_game(from);
2660 2655
2661 while (*move) { 2656 while (*move) {
2662 if (move[0] == 'F' && 2657 if (move[0] == 'F' &&
2663 sscanf(move+1, "%d,%d", &cx, &cy) == 2 && 2658 sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
2664 cx >= 0 && cx < from->w && cy >= 0 && cy < from->h) { 2659 cx >= 0 && cx < from->w && cy >= 0 && cy < from->h &&
2660 (ret->grid[cy * from->w + cx] == -1 ||
2661 ret->grid[cy * from->w + cx] == -2)) {
2665 ret->grid[cy * from->w + cx] ^= (-2 ^ -1); 2662 ret->grid[cy * from->w + cx] ^= (-2 ^ -1);
2666 } else if (move[0] == 'O' && 2663 } else if (move[0] == 'O' &&
2667 sscanf(move+1, "%d,%d", &cx, &cy) == 2 && 2664 sscanf(move+1, "%d,%d", &cx, &cy) == 2 &&
@@ -2697,7 +2694,7 @@ static game_state *execute_move(const game_state *from, const char *move)
2697 */ 2694 */
2698 2695
2699static void game_compute_size(const game_params *params, int tilesize, 2696static void game_compute_size(const game_params *params, int tilesize,
2700 int *x, int *y) 2697 const game_ui *ui, int *x, int *y)
2701{ 2698{
2702 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2699 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2703 struct { int tilesize; } ads, *ds = &ads; 2700 struct { int tilesize; } ads, *ds = &ads;
@@ -2870,12 +2867,12 @@ static void draw_tile(drawing *dr, game_drawstate *ds,
2870 coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \ 2867 coords[(n)*2+0] = x + (int)(TILE_SIZE * (dx)); \
2871 coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \ 2868 coords[(n)*2+1] = y + (int)(TILE_SIZE * (dy)); \
2872} while (0) 2869} while (0)
2873 SETCOORD(0, 0.6F, 0.7F); 2870 SETCOORD(0, 0.6F, 0.35F);
2874 SETCOORD(1, 0.8F, 0.8F); 2871 SETCOORD(1, 0.6F, 0.7F);
2875 SETCOORD(2, 0.25F, 0.8F); 2872 SETCOORD(2, 0.8F, 0.8F);
2876 SETCOORD(3, 0.55F, 0.7F); 2873 SETCOORD(3, 0.25F, 0.8F);
2877 SETCOORD(4, 0.55F, 0.35F); 2874 SETCOORD(4, 0.55F, 0.7F);
2878 SETCOORD(5, 0.6F, 0.35F); 2875 SETCOORD(5, 0.55F, 0.35F);
2879 draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE); 2876 draw_polygon(dr, coords, 6, COL_FLAGBASE, COL_FLAGBASE);
2880 2877
2881 SETCOORD(0, 0.6F, 0.2F); 2878 SETCOORD(0, 0.6F, 0.2F);
@@ -2980,13 +2977,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2980 if (!ds->started) { 2977 if (!ds->started) {
2981 int coords[10]; 2978 int coords[10];
2982 2979
2983 draw_rect(dr, 0, 0,
2984 TILE_SIZE * state->w + 2 * BORDER,
2985 TILE_SIZE * state->h + 2 * BORDER, COL_BACKGROUND);
2986 draw_update(dr, 0, 0,
2987 TILE_SIZE * state->w + 2 * BORDER,
2988 TILE_SIZE * state->h + 2 * BORDER);
2989
2990 /* 2980 /*
2991 * Recessed area containing the whole puzzle. 2981 * Recessed area containing the whole puzzle.
2992 */ 2982 */
@@ -3182,14 +3172,6 @@ static bool game_timing_state(const game_state *state, game_ui *ui)
3182 return true; 3172 return true;
3183} 3173}
3184 3174
3185static void game_print_size(const game_params *params, float *x, float *y)
3186{
3187}
3188
3189static void game_print(drawing *dr, const game_state *state, int tilesize)
3190{
3191}
3192
3193#ifdef COMBINED 3175#ifdef COMBINED
3194#define thegame mines 3176#define thegame mines
3195#endif 3177#endif
@@ -3211,12 +3193,14 @@ const struct game thegame = {
3211 free_game, 3193 free_game,
3212 true, solve_game, 3194 true, solve_game,
3213 true, game_can_format_as_text_now, game_text_format, 3195 true, game_can_format_as_text_now, game_text_format,
3196 NULL, NULL, /* get_prefs, set_prefs */
3214 new_ui, 3197 new_ui,
3215 free_ui, 3198 free_ui,
3216 encode_ui, 3199 encode_ui,
3217 decode_ui, 3200 decode_ui,
3218 NULL, /* game_request_keys */ 3201 NULL, /* game_request_keys */
3219 game_changed_state, 3202 game_changed_state,
3203 current_key_label,
3220 interpret_move, 3204 interpret_move,
3221 execute_move, 3205 execute_move,
3222 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3206 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3228,7 +3212,7 @@ const struct game thegame = {
3228 game_flash_length, 3212 game_flash_length,
3229 game_get_cursor_location, 3213 game_get_cursor_location,
3230 game_status, 3214 game_status,
3231 false, false, game_print_size, game_print, 3215 false, false, NULL, NULL, /* print_size, print */
3232 true, /* wants_statusbar */ 3216 true, /* wants_statusbar */
3233 true, game_timing_state, 3217 true, game_timing_state,
3234 BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON, 3218 BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON,