diff options
Diffstat (limited to 'apps/plugins/puzzles/src/tents.c')
-rw-r--r-- | apps/plugins/puzzles/src/tents.c | 121 |
1 files changed, 73 insertions, 48 deletions
diff --git a/apps/plugins/puzzles/src/tents.c b/apps/plugins/puzzles/src/tents.c index ee06172baf..0d40959d26 100644 --- a/apps/plugins/puzzles/src/tents.c +++ b/apps/plugins/puzzles/src/tents.c | |||
@@ -32,7 +32,12 @@ | |||
32 | #include <string.h> | 32 | #include <string.h> |
33 | #include <assert.h> | 33 | #include <assert.h> |
34 | #include <ctype.h> | 34 | #include <ctype.h> |
35 | #include <math.h> | 35 | #include <limits.h> |
36 | #ifdef NO_TGMATH_H | ||
37 | # include <math.h> | ||
38 | #else | ||
39 | # include <tgmath.h> | ||
40 | #endif | ||
36 | 41 | ||
37 | #include "puzzles.h" | 42 | #include "puzzles.h" |
38 | #include "matching.h" | 43 | #include "matching.h" |
@@ -229,7 +234,7 @@ | |||
229 | */ | 234 | */ |
230 | #if defined STANDALONE_SOLVER | 235 | #if defined STANDALONE_SOLVER |
231 | #define SOLVER_DIAGNOSTICS | 236 | #define SOLVER_DIAGNOSTICS |
232 | bool verbose = false; | 237 | static bool verbose = false; |
233 | #elif defined SOLVER_DIAGNOSTICS | 238 | #elif defined SOLVER_DIAGNOSTICS |
234 | #define verbose true | 239 | #define verbose true |
235 | #endif | 240 | #endif |
@@ -408,6 +413,8 @@ static const char *validate_params(const game_params *params, bool full) | |||
408 | */ | 413 | */ |
409 | if (params->w < 4 || params->h < 4) | 414 | if (params->w < 4 || params->h < 4) |
410 | return "Width and height must both be at least four"; | 415 | return "Width and height must both be at least four"; |
416 | if (params->w > (INT_MAX - 1) / params->h) | ||
417 | return "Width times height must not be unreasonably large"; | ||
411 | return NULL; | 418 | return NULL; |
412 | } | 419 | } |
413 | 420 | ||
@@ -998,7 +1005,7 @@ static char *new_game_desc(const game_params *params_in, random_state *rs, | |||
998 | int dy, dx; | 1005 | int dy, dx; |
999 | bool ok = true; | 1006 | bool ok = true; |
1000 | 1007 | ||
1001 | which = i + random_upto(rs, j); | 1008 | which = i + random_upto(rs, w*h - i); |
1002 | tmp = order[which]; | 1009 | tmp = order[which]; |
1003 | order[which] = order[i]; | 1010 | order[which] = order[i]; |
1004 | order[i] = tmp; | 1011 | order[i] = tmp; |
@@ -1188,6 +1195,21 @@ static char *new_game_desc(const game_params *params_in, random_state *rs, | |||
1188 | return ret; | 1195 | return ret; |
1189 | } | 1196 | } |
1190 | 1197 | ||
1198 | /* | ||
1199 | * Grid description format: | ||
1200 | * | ||
1201 | * _ = tree | ||
1202 | * a = 1 BLANK then TREE | ||
1203 | * ... | ||
1204 | * y = 25 BLANKs then TREE | ||
1205 | * z = 25 BLANKs | ||
1206 | * ! = set previous square to TENT | ||
1207 | * - = set previous square to NONTENT | ||
1208 | * | ||
1209 | * Last character must be one that would insert a tree as the first | ||
1210 | * square after the grid. | ||
1211 | */ | ||
1212 | |||
1191 | static const char *validate_desc(const game_params *params, const char *desc) | 1213 | static const char *validate_desc(const game_params *params, const char *desc) |
1192 | { | 1214 | { |
1193 | int w = params->w, h = params->h; | 1215 | int w = params->w, h = params->h; |
@@ -1201,9 +1223,10 @@ static const char *validate_desc(const game_params *params, const char *desc) | |||
1201 | area += *desc - 'a' + 2; | 1223 | area += *desc - 'a' + 2; |
1202 | else if (*desc == 'z') | 1224 | else if (*desc == 'z') |
1203 | area += 25; | 1225 | area += 25; |
1204 | else if (*desc == '!' || *desc == '-') | 1226 | else if (*desc == '!' || *desc == '-') { |
1205 | /* do nothing */; | 1227 | if (area == 0 || area > w * h) |
1206 | else | 1228 | return "Tent or non-tent placed off the grid"; |
1229 | } else | ||
1207 | return "Invalid character in grid specification"; | 1230 | return "Invalid character in grid specification"; |
1208 | 1231 | ||
1209 | desc++; | 1232 | desc++; |
@@ -1436,7 +1459,7 @@ static game_ui *new_ui(const game_state *state) | |||
1436 | ui->drag_button = -1; | 1459 | ui->drag_button = -1; |
1437 | ui->drag_ok = false; | 1460 | ui->drag_ok = false; |
1438 | ui->cx = ui->cy = 0; | 1461 | ui->cx = ui->cy = 0; |
1439 | ui->cdisp = false; | 1462 | ui->cdisp = getenv_bool("PUZZLES_SHOW_CURSOR", false); |
1440 | return ui; | 1463 | return ui; |
1441 | } | 1464 | } |
1442 | 1465 | ||
@@ -1445,18 +1468,25 @@ static void free_ui(game_ui *ui) | |||
1445 | sfree(ui); | 1468 | sfree(ui); |
1446 | } | 1469 | } |
1447 | 1470 | ||
1448 | static char *encode_ui(const game_ui *ui) | 1471 | static void game_changed_state(game_ui *ui, const game_state *oldstate, |
1449 | { | 1472 | const game_state *newstate) |
1450 | return NULL; | ||
1451 | } | ||
1452 | |||
1453 | static void decode_ui(game_ui *ui, const char *encoding) | ||
1454 | { | 1473 | { |
1455 | } | 1474 | } |
1456 | 1475 | ||
1457 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 1476 | static const char *current_key_label(const game_ui *ui, |
1458 | const game_state *newstate) | 1477 | const game_state *state, int button) |
1459 | { | 1478 | { |
1479 | int w = state->p.w; | ||
1480 | int v = state->grid[ui->cy*w+ui->cx]; | ||
1481 | |||
1482 | if (IS_CURSOR_SELECT(button) && ui->cdisp) { | ||
1483 | switch (v) { | ||
1484 | case BLANK: | ||
1485 | return button == CURSOR_SELECT ? "Tent" : "Green"; | ||
1486 | case TENT: case NONTENT: return "Clear"; | ||
1487 | } | ||
1488 | } | ||
1489 | return ""; | ||
1460 | } | 1490 | } |
1461 | 1491 | ||
1462 | struct game_drawstate { | 1492 | struct game_drawstate { |
@@ -1546,7 +1576,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1546 | char tmpbuf[80]; | 1576 | char tmpbuf[80]; |
1547 | bool shift = button & MOD_SHFT, control = button & MOD_CTRL; | 1577 | bool shift = button & MOD_SHFT, control = button & MOD_CTRL; |
1548 | 1578 | ||
1549 | button &= ~MOD_MASK; | 1579 | button = STRIP_BUTTON_MODIFIERS(button); |
1550 | 1580 | ||
1551 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { | 1581 | if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { |
1552 | x = FROMCOORD(x); | 1582 | x = FROMCOORD(x); |
@@ -1559,7 +1589,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1559 | ui->dsy = ui->dey = y; | 1589 | ui->dsy = ui->dey = y; |
1560 | ui->drag_ok = true; | 1590 | ui->drag_ok = true; |
1561 | ui->cdisp = false; | 1591 | ui->cdisp = false; |
1562 | return UI_UPDATE; | 1592 | return MOVE_UI_UPDATE; |
1563 | } | 1593 | } |
1564 | 1594 | ||
1565 | if ((IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) && | 1595 | if ((IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) && |
@@ -1591,14 +1621,14 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1591 | } | 1621 | } |
1592 | 1622 | ||
1593 | if (IS_MOUSE_DRAG(button)) | 1623 | if (IS_MOUSE_DRAG(button)) |
1594 | return UI_UPDATE; | 1624 | return MOVE_UI_UPDATE; |
1595 | 1625 | ||
1596 | /* | 1626 | /* |
1597 | * The drag has been released. Enact it. | 1627 | * The drag has been released. Enact it. |
1598 | */ | 1628 | */ |
1599 | if (!ui->drag_ok) { | 1629 | if (!ui->drag_ok) { |
1600 | ui->drag_button = -1; | 1630 | ui->drag_button = -1; |
1601 | return UI_UPDATE; /* drag was just cancelled */ | 1631 | return MOVE_UI_UPDATE; /* drag was just cancelled */ |
1602 | } | 1632 | } |
1603 | 1633 | ||
1604 | xmin = min(ui->dsx, ui->dex); | 1634 | xmin = min(ui->dsx, ui->dex); |
@@ -1636,7 +1666,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1636 | 1666 | ||
1637 | if (buflen == 0) { | 1667 | if (buflen == 0) { |
1638 | sfree(buf); | 1668 | sfree(buf); |
1639 | return UI_UPDATE; /* drag was terminated */ | 1669 | return MOVE_UI_UPDATE; /* drag was terminated */ |
1640 | } else { | 1670 | } else { |
1641 | buf[buflen] = '\0'; | 1671 | buf[buflen] = '\0'; |
1642 | return buf; | 1672 | return buf; |
@@ -1644,11 +1674,12 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1644 | } | 1674 | } |
1645 | 1675 | ||
1646 | if (IS_CURSOR_MOVE(button)) { | 1676 | if (IS_CURSOR_MOVE(button)) { |
1647 | ui->cdisp = true; | 1677 | char *ret; |
1648 | if (shift || control) { | 1678 | if (shift || control) { |
1649 | int len = 0, i, indices[2]; | 1679 | int len = 0, i, indices[2]; |
1650 | indices[0] = ui->cx + w * ui->cy; | 1680 | indices[0] = ui->cx + w * ui->cy; |
1651 | move_cursor(button, &ui->cx, &ui->cy, w, h, false); | 1681 | ret = move_cursor(button, &ui->cx, &ui->cy, w, h, false, |
1682 | &ui->cdisp); | ||
1652 | indices[1] = ui->cx + w * ui->cy; | 1683 | indices[1] = ui->cx + w * ui->cy; |
1653 | 1684 | ||
1654 | /* NONTENTify all unique traversed eligible squares */ | 1685 | /* NONTENTify all unique traversed eligible squares */ |
@@ -1663,8 +1694,9 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1663 | tmpbuf[len] = '\0'; | 1694 | tmpbuf[len] = '\0'; |
1664 | if (len) return dupstr(tmpbuf); | 1695 | if (len) return dupstr(tmpbuf); |
1665 | } else | 1696 | } else |
1666 | move_cursor(button, &ui->cx, &ui->cy, w, h, false); | 1697 | ret = move_cursor(button, &ui->cx, &ui->cy, w, h, false, |
1667 | return UI_UPDATE; | 1698 | &ui->cdisp); |
1699 | return ret; | ||
1668 | } | 1700 | } |
1669 | if (ui->cdisp) { | 1701 | if (ui->cdisp) { |
1670 | char rep = 0; | 1702 | char rep = 0; |
@@ -1691,7 +1723,7 @@ static char *interpret_move(const game_state *state, game_ui *ui, | |||
1691 | } | 1723 | } |
1692 | } else if (IS_CURSOR_SELECT(button)) { | 1724 | } else if (IS_CURSOR_SELECT(button)) { |
1693 | ui->cdisp = true; | 1725 | ui->cdisp = true; |
1694 | return UI_UPDATE; | 1726 | return MOVE_UI_UPDATE; |
1695 | } | 1727 | } |
1696 | 1728 | ||
1697 | return NULL; | 1729 | return NULL; |
@@ -1875,7 +1907,7 @@ static game_state *execute_move(const game_state *state, const char *move) | |||
1875 | */ | 1907 | */ |
1876 | 1908 | ||
1877 | static void game_compute_size(const game_params *params, int tilesize, | 1909 | static void game_compute_size(const game_params *params, int tilesize, |
1878 | int *x, int *y) | 1910 | const game_ui *ui, int *x, int *y) |
1879 | { | 1911 | { |
1880 | /* fool the macros */ | 1912 | /* fool the macros */ |
1881 | struct dummy { int tilesize; } dummy, *ds = &dummy; | 1913 | struct dummy { int tilesize; } dummy, *ds = &dummy; |
@@ -1976,7 +2008,8 @@ static int *find_errors(const game_state *state, char *grid) | |||
1976 | { | 2008 | { |
1977 | int w = state->p.w, h = state->p.h; | 2009 | int w = state->p.w, h = state->p.h; |
1978 | int *ret = snewn(w*h + w + h, int); | 2010 | int *ret = snewn(w*h + w + h, int); |
1979 | int *tmp = snewn(w*h*2, int), *dsf = tmp + w*h; | 2011 | int *tmp = snewn(w*h, int); |
2012 | DSF *dsf; | ||
1980 | int x, y; | 2013 | int x, y; |
1981 | 2014 | ||
1982 | /* | 2015 | /* |
@@ -2193,7 +2226,7 @@ static int *find_errors(const game_state *state, char *grid) | |||
2193 | * all the tents in any component which has a smaller tree | 2226 | * all the tents in any component which has a smaller tree |
2194 | * count. | 2227 | * count. |
2195 | */ | 2228 | */ |
2196 | dsf_init(dsf, w*h); | 2229 | dsf = dsf_new(w*h); |
2197 | /* Construct the equivalence classes. */ | 2230 | /* Construct the equivalence classes. */ |
2198 | for (y = 0; y < h; y++) { | 2231 | for (y = 0; y < h; y++) { |
2199 | for (x = 0; x < w-1; x++) { | 2232 | for (x = 0; x < w-1; x++) { |
@@ -2236,7 +2269,7 @@ static int *find_errors(const game_state *state, char *grid) | |||
2236 | * start of the game, before the user had done anything wrong!) | 2269 | * start of the game, before the user had done anything wrong!) |
2237 | */ | 2270 | */ |
2238 | #define TENT(x) ((x)==TENT || (x)==BLANK) | 2271 | #define TENT(x) ((x)==TENT || (x)==BLANK) |
2239 | dsf_init(dsf, w*h); | 2272 | dsf_reinit(dsf); |
2240 | /* Construct the equivalence classes. */ | 2273 | /* Construct the equivalence classes. */ |
2241 | for (y = 0; y < h; y++) { | 2274 | for (y = 0; y < h; y++) { |
2242 | for (x = 0; x < w-1; x++) { | 2275 | for (x = 0; x < w-1; x++) { |
@@ -2272,6 +2305,7 @@ static int *find_errors(const game_state *state, char *grid) | |||
2272 | #undef TENT | 2305 | #undef TENT |
2273 | 2306 | ||
2274 | sfree(tmp); | 2307 | sfree(tmp); |
2308 | dsf_free(dsf); | ||
2275 | return ret; | 2309 | return ret; |
2276 | } | 2310 | } |
2277 | 2311 | ||
@@ -2411,14 +2445,6 @@ static void int_redraw(drawing *dr, game_drawstate *ds, | |||
2411 | } | 2445 | } |
2412 | 2446 | ||
2413 | if (printing || !ds->started) { | 2447 | if (printing || !ds->started) { |
2414 | if (!printing) { | ||
2415 | int ww, wh; | ||
2416 | game_compute_size(&state->p, TILESIZE, &ww, &wh); | ||
2417 | draw_rect(dr, 0, 0, ww, wh, COL_BACKGROUND); | ||
2418 | draw_update(dr, 0, 0, ww, wh); | ||
2419 | ds->started = true; | ||
2420 | } | ||
2421 | |||
2422 | if (printing) | 2448 | if (printing) |
2423 | print_line_width(dr, TILESIZE/64); | 2449 | print_line_width(dr, TILESIZE/64); |
2424 | 2450 | ||
@@ -2572,24 +2598,21 @@ static int game_status(const game_state *state) | |||
2572 | return state->completed ? +1 : 0; | 2598 | return state->completed ? +1 : 0; |
2573 | } | 2599 | } |
2574 | 2600 | ||
2575 | static bool game_timing_state(const game_state *state, game_ui *ui) | 2601 | static void game_print_size(const game_params *params, const game_ui *ui, |
2576 | { | 2602 | float *x, float *y) |
2577 | return true; | ||
2578 | } | ||
2579 | |||
2580 | static void game_print_size(const game_params *params, float *x, float *y) | ||
2581 | { | 2603 | { |
2582 | int pw, ph; | 2604 | int pw, ph; |
2583 | 2605 | ||
2584 | /* | 2606 | /* |
2585 | * I'll use 6mm squares by default. | 2607 | * I'll use 6mm squares by default. |
2586 | */ | 2608 | */ |
2587 | game_compute_size(params, 600, &pw, &ph); | 2609 | game_compute_size(params, 600, ui, &pw, &ph); |
2588 | *x = pw / 100.0F; | 2610 | *x = pw / 100.0F; |
2589 | *y = ph / 100.0F; | 2611 | *y = ph / 100.0F; |
2590 | } | 2612 | } |
2591 | 2613 | ||
2592 | static void game_print(drawing *dr, const game_state *state, int tilesize) | 2614 | static void game_print(drawing *dr, const game_state *state, const game_ui *ui, |
2615 | int tilesize) | ||
2593 | { | 2616 | { |
2594 | int c; | 2617 | int c; |
2595 | 2618 | ||
@@ -2628,12 +2651,14 @@ const struct game thegame = { | |||
2628 | free_game, | 2651 | free_game, |
2629 | true, solve_game, | 2652 | true, solve_game, |
2630 | true, game_can_format_as_text_now, game_text_format, | 2653 | true, game_can_format_as_text_now, game_text_format, |
2654 | NULL, NULL, /* get_prefs, set_prefs */ | ||
2631 | new_ui, | 2655 | new_ui, |
2632 | free_ui, | 2656 | free_ui, |
2633 | encode_ui, | 2657 | NULL, /* encode_ui */ |
2634 | decode_ui, | 2658 | NULL, /* decode_ui */ |
2635 | NULL, /* game_request_keys */ | 2659 | NULL, /* game_request_keys */ |
2636 | game_changed_state, | 2660 | game_changed_state, |
2661 | current_key_label, | ||
2637 | interpret_move, | 2662 | interpret_move, |
2638 | execute_move, | 2663 | execute_move, |
2639 | PREFERRED_TILESIZE, game_compute_size, game_set_size, | 2664 | PREFERRED_TILESIZE, game_compute_size, game_set_size, |
@@ -2647,7 +2672,7 @@ const struct game thegame = { | |||
2647 | game_status, | 2672 | game_status, |
2648 | true, false, game_print_size, game_print, | 2673 | true, false, game_print_size, game_print, |
2649 | false, /* wants_statusbar */ | 2674 | false, /* wants_statusbar */ |
2650 | false, game_timing_state, | 2675 | false, NULL, /* timing_state */ |
2651 | REQUIRE_RBUTTON, /* flags */ | 2676 | REQUIRE_RBUTTON, /* flags */ |
2652 | }; | 2677 | }; |
2653 | 2678 | ||