diff options
Diffstat (limited to 'apps/plugins/puzzles/src/mines.c')
-rw-r--r-- | apps/plugins/puzzles/src/mines.c | 218 |
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, | |||
1880 | static bool *new_mine_layout(int w, int h, int n, int x, int y, bool unique, | 1903 | static 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 | ||
2390 | static void decode_ui(game_ui *ui, const char *encoding) | 2352 | static 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 | ||
2368 | static 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 | |||
2405 | struct game_drawstate { | 2397 | struct 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 | ||
2538 | uncover: | 2529 | uncover: |
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 | ||
2699 | static void game_compute_size(const game_params *params, int tilesize, | 2696 | static 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 | ||
3185 | static void game_print_size(const game_params *params, float *x, float *y) | ||
3186 | { | ||
3187 | } | ||
3188 | |||
3189 | static 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, |