diff options
Diffstat (limited to 'apps/plugins/puzzles/src/magnets.c')
-rw-r--r-- | apps/plugins/puzzles/src/magnets.c | 125 |
1 files changed, 78 insertions, 47 deletions
diff --git a/apps/plugins/puzzles/src/magnets.c b/apps/plugins/puzzles/src/magnets.c index edbb8490ad..7cb5fbc919 100644 --- a/apps/plugins/puzzles/src/magnets.c +++ b/apps/plugins/puzzles/src/magnets.c | |||
@@ -36,12 +36,17 @@ | |||
36 | #include <string.h> | 36 | #include <string.h> |
37 | #include <assert.h> | 37 | #include <assert.h> |
38 | #include <ctype.h> | 38 | #include <ctype.h> |
39 | #include <math.h> | 39 | #include <limits.h> |
40 | #ifdef NO_TGMATH_H | ||
41 | # include <math.h> | ||
42 | #else | ||
43 | # include <tgmath.h> | ||
44 | #endif | ||
40 | 45 | ||
41 | #include "puzzles.h" | 46 | #include "puzzles.h" |
42 | 47 | ||
43 | #ifdef STANDALONE_SOLVER | 48 | #ifdef STANDALONE_SOLVER |
44 | bool verbose = 0; | 49 | static bool verbose = false; |
45 | #endif | 50 | #endif |
46 | 51 | ||
47 | enum { | 52 | enum { |
@@ -230,8 +235,17 @@ static game_params *custom_params(const config_item *cfg) | |||
230 | 235 | ||
231 | static const char *validate_params(const game_params *params, bool full) | 236 | static const char *validate_params(const game_params *params, bool full) |
232 | { | 237 | { |
233 | if (params->w < 2) return "Width must be at least one"; | 238 | if (params->w < 2) return "Width must be at least two"; |
234 | if (params->h < 2) return "Height must be at least one"; | 239 | if (params->h < 2) return "Height must be at least two"; |
240 | if (params->w > INT_MAX / params->h) | ||
241 | return "Width times height must not be unreasonably large"; | ||
242 | if (params->diff >= DIFF_TRICKY) { | ||
243 | if (params->w < 5 && params->h < 5) | ||
244 | return "Either width or height must be at least five for Tricky"; | ||
245 | } else { | ||
246 | if (params->w < 3 && params->h < 3) | ||
247 | return "Either width or height must be at least three"; | ||
248 | } | ||
235 | if (params->diff < 0 || params->diff >= DIFFCOUNT) | 249 | if (params->diff < 0 || params->diff >= DIFFCOUNT) |
236 | return "Unknown difficulty level"; | 250 | return "Unknown difficulty level"; |
237 | 251 | ||
@@ -510,7 +524,9 @@ nextchar: | |||
510 | * (i.e. each end points to the other) */ | 524 | * (i.e. each end points to the other) */ |
511 | for (idx = 0; idx < state->wh; idx++) { | 525 | for (idx = 0; idx < state->wh; idx++) { |
512 | if (state->common->dominoes[idx] < 0 || | 526 | if (state->common->dominoes[idx] < 0 || |
513 | state->common->dominoes[idx] > state->wh || | 527 | state->common->dominoes[idx] >= state->wh || |
528 | (state->common->dominoes[idx] % state->w != idx % state->w && | ||
529 | state->common->dominoes[idx] / state->w != idx / state->w) || | ||
514 | state->common->dominoes[state->common->dominoes[idx]] != idx) { | 530 | state->common->dominoes[state->common->dominoes[idx]] != idx) { |
515 | *prob = "Domino descriptions inconsistent"; | 531 | *prob = "Domino descriptions inconsistent"; |
516 | goto done; | 532 | goto done; |
@@ -541,7 +557,7 @@ static const char *validate_desc(const game_params *params, const char *desc) | |||
541 | { | 557 | { |
542 | const char *prob; | 558 | const char *prob; |
543 | game_state *st = new_game_int(params, desc, &prob); | 559 | game_state *st = new_game_int(params, desc, &prob); |
544 | if (!st) return (char*)prob; | 560 | if (!st) return prob; |
545 | free_game(st); | 561 | free_game(st); |
546 | return NULL; | 562 | return NULL; |
547 | } | 563 | } |
@@ -1574,6 +1590,7 @@ static int lay_dominoes(game_state *state, random_state *rs, int *scratch) | |||
1574 | } | 1590 | } |
1575 | 1591 | ||
1576 | debug(("Laid %d dominoes, total %d dominoes.\n", nlaid, state->wh/2)); | 1592 | debug(("Laid %d dominoes, total %d dominoes.\n", nlaid, state->wh/2)); |
1593 | (void)nlaid; | ||
1577 | game_debug(state, "Final layout"); | 1594 | game_debug(state, "Final layout"); |
1578 | return ret; | 1595 | return ret; |
1579 | } | 1596 | } |
@@ -1720,7 +1737,7 @@ static game_ui *new_ui(const game_state *state) | |||
1720 | { | 1737 | { |
1721 | game_ui *ui = snew(game_ui); | 1738 | game_ui *ui = snew(game_ui); |
1722 | ui->cur_x = ui->cur_y = 0; | 1739 | ui->cur_x = ui->cur_y = 0; |
1723 | ui->cur_visible = false; | 1740 | ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
1724 | return ui; | 1741 | return ui; |
1725 | } | 1742 | } |
1726 | 1743 | ||
@@ -1729,15 +1746,6 @@ static void free_ui(game_ui *ui) | |||
1729 | sfree(ui); | 1746 | sfree(ui); |
1730 | } | 1747 | } |
1731 | 1748 | ||
1732 | static char *encode_ui(const game_ui *ui) | ||
1733 | { | ||
1734 | return NULL; | ||
1735 | } | ||
1736 | |||
1737 | static void decode_ui(game_ui *ui, const char *encoding) | ||
1738 | { | ||
1739 | } | ||
1740 | |||
1741 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 1749 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
1742 | const game_state *newstate) | 1750 | const game_state *newstate) |
1743 | { | 1751 | { |
@@ -1745,6 +1753,36 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, | |||
1745 | ui->cur_visible = false; | 1753 | ui->cur_visible = false; |
1746 | } | 1754 | } |
1747 | 1755 | ||
1756 | static const char *current_key_label(const game_ui *ui, | ||
1757 | const game_state *state, int button) | ||
1758 | { | ||
1759 | int idx; | ||
1760 | |||
1761 | if (IS_CURSOR_SELECT(button)) { | ||
1762 | if (!ui->cur_visible) return ""; | ||
1763 | idx = ui->cur_y * state->w + ui->cur_x; | ||
1764 | if (button == CURSOR_SELECT) { | ||
1765 | if (state->grid[idx] == NEUTRAL && state->flags[idx] & GS_SET) | ||
1766 | return ""; | ||
1767 | switch (state->grid[idx]) { | ||
1768 | case EMPTY: return "+"; | ||
1769 | case POSITIVE: return "-"; | ||
1770 | case NEGATIVE: return "Clear"; | ||
1771 | } | ||
1772 | } | ||
1773 | if (button == CURSOR_SELECT2) { | ||
1774 | if (state->grid[idx] != NEUTRAL) return ""; | ||
1775 | if (state->flags[idx] & GS_SET) /* neutral */ | ||
1776 | return "?"; | ||
1777 | if (state->flags[idx] & GS_NOTNEUTRAL) /* !neutral */ | ||
1778 | return "Clear"; | ||
1779 | else | ||
1780 | return "X"; | ||
1781 | } | ||
1782 | } | ||
1783 | return ""; | ||
1784 | } | ||
1785 | |||
1748 | struct game_drawstate { | 1786 | struct game_drawstate { |
1749 | int tilesize; | 1787 | int tilesize; |
1750 | bool started, solved; | 1788 | bool started, solved; |
@@ -1805,14 +1843,13 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1805 | char *nullret = NULL, buf[80], movech; | 1843 | char *nullret = NULL, buf[80], movech; |
1806 | enum { CYCLE_MAGNET, CYCLE_NEUTRAL } action; | 1844 | enum { CYCLE_MAGNET, CYCLE_NEUTRAL } action; |
1807 | 1845 | ||
1808 | if (IS_CURSOR_MOVE(button)) { | 1846 | if (IS_CURSOR_MOVE(button)) |
1809 | move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, false); | 1847 | return move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, |
1810 | ui->cur_visible = true; | 1848 | false, &ui->cur_visible); |
1811 | return UI_UPDATE; | 1849 | else if (IS_CURSOR_SELECT(button)) { |
1812 | } else if (IS_CURSOR_SELECT(button)) { | ||
1813 | if (!ui->cur_visible) { | 1850 | if (!ui->cur_visible) { |
1814 | ui->cur_visible = true; | 1851 | ui->cur_visible = true; |
1815 | return UI_UPDATE; | 1852 | return MOVE_UI_UPDATE; |
1816 | } | 1853 | } |
1817 | action = (button == CURSOR_SELECT) ? CYCLE_MAGNET : CYCLE_NEUTRAL; | 1854 | action = (button == CURSOR_SELECT) ? CYCLE_MAGNET : CYCLE_NEUTRAL; |
1818 | gx = ui->cur_x; | 1855 | gx = ui->cur_x; |
@@ -1821,7 +1858,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1821 | (button == LEFT_BUTTON || button == RIGHT_BUTTON)) { | 1858 | (button == LEFT_BUTTON || button == RIGHT_BUTTON)) { |
1822 | if (ui->cur_visible) { | 1859 | if (ui->cur_visible) { |
1823 | ui->cur_visible = false; | 1860 | ui->cur_visible = false; |
1824 | nullret = UI_UPDATE; | 1861 | nullret = MOVE_UI_UPDATE; |
1825 | } | 1862 | } |
1826 | action = (button == LEFT_BUTTON) ? CYCLE_MAGNET : CYCLE_NEUTRAL; | 1863 | action = (button == LEFT_BUTTON) ? CYCLE_MAGNET : CYCLE_NEUTRAL; |
1827 | } else if (button == LEFT_BUTTON && is_clue(state, gx, gy)) { | 1864 | } else if (button == LEFT_BUTTON && is_clue(state, gx, gy)) { |
@@ -1928,7 +1965,7 @@ badmove: | |||
1928 | */ | 1965 | */ |
1929 | 1966 | ||
1930 | static void game_compute_size(const game_params *params, int tilesize, | 1967 | static void game_compute_size(const game_params *params, int tilesize, |
1931 | int *x, int *y) | 1968 | const game_ui *ui, int *x, int *y) |
1932 | { | 1969 | { |
1933 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ | 1970 | /* Ick: fake up `ds->tilesize' for macro expansion purposes */ |
1934 | struct { int tilesize; } ads, *ds = &ads; | 1971 | struct { int tilesize; } ads, *ds = &ads; |
@@ -2205,12 +2242,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, | |||
2205 | flash = (int)(flashtime * 5 / FLASH_TIME) % 2; | 2242 | flash = (int)(flashtime * 5 / FLASH_TIME) % 2; |
2206 | 2243 | ||
2207 | if (!ds->started) { | 2244 | if (!ds->started) { |
2208 | /* draw background, corner +-. */ | 2245 | /* draw corner +-. */ |
2209 | draw_rect(dr, 0, 0, | ||
2210 | TILE_SIZE * (w+2) + 2 * BORDER, | ||
2211 | TILE_SIZE * (h+2) + 2 * BORDER, | ||
2212 | COL_BACKGROUND); | ||
2213 | |||
2214 | draw_sym(dr, ds, -1, -1, POSITIVE, COL_TEXT); | 2246 | draw_sym(dr, ds, -1, -1, POSITIVE, COL_TEXT); |
2215 | draw_sym(dr, ds, state->w, state->h, NEGATIVE, COL_TEXT); | 2247 | draw_sym(dr, ds, state->w, state->h, NEGATIVE, COL_TEXT); |
2216 | 2248 | ||
@@ -2309,24 +2341,21 @@ static int game_status(const game_state *state) | |||
2309 | return state->completed ? +1 : 0; | 2341 | return state->completed ? +1 : 0; |
2310 | } | 2342 | } |
2311 | 2343 | ||
2312 | static bool game_timing_state(const game_state *state, game_ui *ui) | 2344 | static void game_print_size(const game_params *params, const game_ui *ui, |
2313 | { | 2345 | float *x, float *y) |
2314 | return true; | ||
2315 | } | ||
2316 | |||
2317 | static void game_print_size(const game_params *params, float *x, float *y) | ||
2318 | { | 2346 | { |
2319 | int pw, ph; | 2347 | int pw, ph; |
2320 | 2348 | ||
2321 | /* | 2349 | /* |
2322 | * I'll use 6mm squares by default. | 2350 | * I'll use 6mm squares by default. |
2323 | */ | 2351 | */ |
2324 | game_compute_size(params, 600, &pw, &ph); | 2352 | game_compute_size(params, 600, ui, &pw, &ph); |
2325 | *x = pw / 100.0F; | 2353 | *x = pw / 100.0F; |
2326 | *y = ph / 100.0F; | 2354 | *y = ph / 100.0F; |
2327 | } | 2355 | } |
2328 | 2356 | ||
2329 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 2357 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
2358 | int tilesize) | ||
2330 | { | 2359 | { |
2331 | int w = state->w, h = state->h; | 2360 | int w = state->w, h = state->h; |
2332 | int ink = print_mono_colour(dr, 0); | 2361 | int ink = print_mono_colour(dr, 0); |
@@ -2430,12 +2459,14 @@ const struct game thegame = { | |||
2430 | free_game, | 2459 | free_game, |
2431 | true, solve_game, | 2460 | true, solve_game, |
2432 | true, game_can_format_as_text_now, game_text_format, | 2461 | true, game_can_format_as_text_now, game_text_format, |
2462 | NULL, NULL, /* get_prefs, set_prefs */ | ||
2433 | new_ui, | 2463 | new_ui, |
2434 | free_ui, | 2464 | free_ui, |
2435 | encode_ui, | 2465 | NULL, /* encode_ui */ |
2436 | decode_ui, | 2466 | NULL, /* decode_ui */ |
2437 | NULL, /* game_request_keys */ | 2467 | NULL, /* game_request_keys */ |
2438 | game_changed_state, | 2468 | game_changed_state, |
2469 | current_key_label, | ||
2439 | interpret_move, | 2470 | interpret_move, |
2440 | execute_move, | 2471 | execute_move, |
2441 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, | 2472 | PREFERRED_TILE_SIZE, game_compute_size, game_set_size, |
@@ -2449,7 +2480,7 @@ const struct game thegame = { | |||
2449 | game_status, | 2480 | game_status, |
2450 | true, false, game_print_size, game_print, | 2481 | true, false, game_print_size, game_print, |
2451 | false, /* wants_statusbar */ | 2482 | false, /* wants_statusbar */ |
2452 | false, game_timing_state, | 2483 | false, NULL, /* timing_state */ |
2453 | REQUIRE_RBUTTON, /* flags */ | 2484 | REQUIRE_RBUTTON, /* flags */ |
2454 | }; | 2485 | }; |
2455 | 2486 | ||
@@ -2458,14 +2489,14 @@ const struct game thegame = { | |||
2458 | #include <time.h> | 2489 | #include <time.h> |
2459 | #include <stdarg.h> | 2490 | #include <stdarg.h> |
2460 | 2491 | ||
2461 | const char *quis = NULL; | 2492 | static const char *quis = NULL; |
2462 | bool csv = false; | 2493 | static bool csv = false; |
2463 | 2494 | ||
2464 | void usage(FILE *out) { | 2495 | static void usage(FILE *out) { |
2465 | fprintf(out, "usage: %s [-v] [--print] <params>|<game id>\n", quis); | 2496 | fprintf(out, "usage: %s [-v] [--print] <params>|<game id>\n", quis); |
2466 | } | 2497 | } |
2467 | 2498 | ||
2468 | void doprint(game_state *state) | 2499 | static void doprint(game_state *state) |
2469 | { | 2500 | { |
2470 | char *fmt = game_text_format(state); | 2501 | char *fmt = game_text_format(state); |
2471 | printf("%s", fmt); | 2502 | printf("%s", fmt); |
@@ -2559,7 +2590,7 @@ static void start_soak(game_params *p, random_state *rs) | |||
2559 | sfree(aux); | 2590 | sfree(aux); |
2560 | } | 2591 | } |
2561 | 2592 | ||
2562 | int main(int argc, const char *argv[]) | 2593 | int main(int argc, char *argv[]) |
2563 | { | 2594 | { |
2564 | bool print = false, soak = false, solved = false; | 2595 | bool print = false, soak = false, solved = false; |
2565 | int ret; | 2596 | int ret; |
@@ -2608,7 +2639,7 @@ int main(int argc, const char *argv[]) | |||
2608 | decode_params(p, id); | 2639 | decode_params(p, id); |
2609 | err = validate_params(p, true); | 2640 | err = validate_params(p, true); |
2610 | if (err) { | 2641 | if (err) { |
2611 | fprintf(stderr, "%s: %s", argv[0], err); | 2642 | fprintf(stderr, "%s: %s\n", argv[0], err); |
2612 | goto done; | 2643 | goto done; |
2613 | } | 2644 | } |
2614 | 2645 | ||