summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/tents.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/tents.c')
-rw-r--r--apps/plugins/puzzles/src/tents.c121
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
232bool verbose = false; 237static 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
1191static const char *validate_desc(const game_params *params, const char *desc) 1213static 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
1448static char *encode_ui(const game_ui *ui) 1471static void game_changed_state(game_ui *ui, const game_state *oldstate,
1449{ 1472 const game_state *newstate)
1450 return NULL;
1451}
1452
1453static void decode_ui(game_ui *ui, const char *encoding)
1454{ 1473{
1455} 1474}
1456 1475
1457static void game_changed_state(game_ui *ui, const game_state *oldstate, 1476static 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
1462struct game_drawstate { 1492struct 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
1877static void game_compute_size(const game_params *params, int tilesize, 1909static 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
2575static bool game_timing_state(const game_state *state, game_ui *ui) 2601static void game_print_size(const game_params *params, const game_ui *ui,
2576{ 2602 float *x, float *y)
2577 return true;
2578}
2579
2580static 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
2592static void game_print(drawing *dr, const game_state *state, int tilesize) 2614static 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