summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/bridges.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/bridges.c')
-rw-r--r--apps/plugins/puzzles/src/bridges.c210
1 files changed, 129 insertions, 81 deletions
diff --git a/apps/plugins/puzzles/src/bridges.c b/apps/plugins/puzzles/src/bridges.c
index 83086c9761..7e87764d6d 100644
--- a/apps/plugins/puzzles/src/bridges.c
+++ b/apps/plugins/puzzles/src/bridges.c
@@ -72,11 +72,15 @@
72#include <string.h> 72#include <string.h>
73#include <assert.h> 73#include <assert.h>
74#include <ctype.h> 74#include <ctype.h>
75#include <math.h> 75#include <limits.h>
76#ifdef NO_TGMATH_H
77# include <math.h>
78#else
79# include <tgmath.h>
80#endif
76 81
77#include "puzzles.h" 82#include "puzzles.h"
78 83
79/* Turn this on for hints about which lines are considered possibilities. */
80#undef DRAW_GRID 84#undef DRAW_GRID
81 85
82/* --- structures for params, state, etc. --- */ 86/* --- structures for params, state, etc. --- */
@@ -134,8 +138,8 @@ struct game_params {
134typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */ 138typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */
135 139
136struct solver_state { 140struct solver_state {
137 int *dsf, *comptspaces; 141 DSF *dsf, *tmpdsf;
138 int *tmpdsf, *tmpcompspaces; 142 int *comptspaces, *tmpcompspaces;
139 int refcount; 143 int refcount;
140}; 144};
141 145
@@ -183,7 +187,7 @@ struct game_state {
183 187
184#define GRIDCOUNT(s,x,y,f) ((GRID(s,x,y) & (f)) ? (INDEX(s,lines,x,y)) : 0) 188#define GRIDCOUNT(s,x,y,f) ((GRID(s,x,y) & (f)) ? (INDEX(s,lines,x,y)) : 0)
185 189
186#define WITHIN2(x,min,max) ((x) >= (min) && (x) < (max)) 190#define WITHIN2(x,min,max) ((x) >= (min) && (x) <= (max))
187#define WITHIN(x,min,max) ((min) > (max) ? \ 191#define WITHIN(x,min,max) ((min) > (max) ? \
188 WITHIN2(x,max,min) : WITHIN2(x,min,max)) 192 WITHIN2(x,max,min) : WITHIN2(x,min,max))
189 193
@@ -803,6 +807,8 @@ static const char *validate_params(const game_params *params, bool full)
803{ 807{
804 if (params->w < 3 || params->h < 3) 808 if (params->w < 3 || params->h < 3)
805 return "Width and height must be at least 3"; 809 return "Width and height must be at least 3";
810 if (params->w > INT_MAX / params->h)
811 return "Width times height must not be unreasonably large";
806 if (params->maxb < 1 || params->maxb > MAX_BRIDGES) 812 if (params->maxb < 1 || params->maxb > MAX_BRIDGES)
807 return "Too many bridges."; 813 return "Too many bridges.";
808 if (full) { 814 if (full) {
@@ -1133,13 +1139,13 @@ static bool map_hasloops(game_state *state, bool mark)
1133 1139
1134static void map_group(game_state *state) 1140static void map_group(game_state *state)
1135{ 1141{
1136 int i, wh = state->w*state->h, d1, d2; 1142 int i, d1, d2;
1137 int x, y, x2, y2; 1143 int x, y, x2, y2;
1138 int *dsf = state->solver->dsf; 1144 DSF *dsf = state->solver->dsf;
1139 struct island *is, *is_join; 1145 struct island *is, *is_join;
1140 1146
1141 /* Initialise dsf. */ 1147 /* Initialise dsf. */
1142 dsf_init(dsf, wh); 1148 dsf_reinit(dsf);
1143 1149
1144 /* For each island, find connected islands right or down 1150 /* For each island, find connected islands right or down
1145 * and merge the dsf for the island squares as well as the 1151 * and merge the dsf for the island squares as well as the
@@ -1160,7 +1166,7 @@ static void map_group(game_state *state)
1160 if (!is_join) continue; 1166 if (!is_join) continue;
1161 1167
1162 d2 = DINDEX(is_join->x, is_join->y); 1168 d2 = DINDEX(is_join->x, is_join->y);
1163 if (dsf_canonify(dsf,d1) == dsf_canonify(dsf,d2)) { 1169 if (dsf_equivalent(dsf, d1, d2)) {
1164 ; /* we have a loop. See comment in map_hasloops. */ 1170 ; /* we have a loop. See comment in map_hasloops. */
1165 /* However, we still want to merge all squares joining 1171 /* However, we still want to merge all squares joining
1166 * this side-that-makes-a-loop. */ 1172 * this side-that-makes-a-loop. */
@@ -1180,7 +1186,8 @@ static void map_group(game_state *state)
1180static bool map_group_check(game_state *state, int canon, bool warn, 1186static bool map_group_check(game_state *state, int canon, bool warn,
1181 int *nislands_r) 1187 int *nislands_r)
1182{ 1188{
1183 int *dsf = state->solver->dsf, nislands = 0; 1189 DSF *dsf = state->solver->dsf;
1190 int nislands = 0;
1184 int x, y, i; 1191 int x, y, i;
1185 bool allfull = true; 1192 bool allfull = true;
1186 struct island *is; 1193 struct island *is;
@@ -1212,7 +1219,8 @@ static bool map_group_check(game_state *state, int canon, bool warn,
1212 1219
1213static bool map_group_full(game_state *state, int *ngroups_r) 1220static bool map_group_full(game_state *state, int *ngroups_r)
1214{ 1221{
1215 int *dsf = state->solver->dsf, ngroups = 0; 1222 DSF *dsf = state->solver->dsf;
1223 int ngroups = 0;
1216 int i; 1224 int i;
1217 bool anyfull = false; 1225 bool anyfull = false;
1218 struct island *is; 1226 struct island *is;
@@ -1267,7 +1275,8 @@ static void map_clear(game_state *state)
1267static void solve_join(struct island *is, int direction, int n, bool is_max) 1275static void solve_join(struct island *is, int direction, int n, bool is_max)
1268{ 1276{
1269 struct island *is_orth; 1277 struct island *is_orth;
1270 int d1, d2, *dsf = is->state->solver->dsf; 1278 int d1, d2;
1279 DSF *dsf = is->state->solver->dsf;
1271 game_state *state = is->state; /* for DINDEX */ 1280 game_state *state = is->state; /* for DINDEX */
1272 1281
1273 is_orth = INDEX(is->state, gridi, 1282 is_orth = INDEX(is->state, gridi,
@@ -1281,7 +1290,7 @@ static void solve_join(struct island *is, int direction, int n, bool is_max)
1281 if (n > 0 && !is_max) { 1290 if (n > 0 && !is_max) {
1282 d1 = DINDEX(is->x, is->y); 1291 d1 = DINDEX(is->x, is->y);
1283 d2 = DINDEX(is_orth->x, is_orth->y); 1292 d2 = DINDEX(is_orth->x, is_orth->y);
1284 if (dsf_canonify(dsf, d1) != dsf_canonify(dsf, d2)) 1293 if (!dsf_equivalent(dsf, d1, d2))
1285 dsf_merge(dsf, d1, d2); 1294 dsf_merge(dsf, d1, d2);
1286 } 1295 }
1287} 1296}
@@ -1382,7 +1391,8 @@ static bool solve_island_stage1(struct island *is, bool *didsth_r)
1382static bool solve_island_checkloop(struct island *is, int direction) 1391static bool solve_island_checkloop(struct island *is, int direction)
1383{ 1392{
1384 struct island *is_orth; 1393 struct island *is_orth;
1385 int *dsf = is->state->solver->dsf, d1, d2; 1394 DSF *dsf = is->state->solver->dsf;
1395 int d1, d2;
1386 game_state *state = is->state; 1396 game_state *state = is->state;
1387 1397
1388 if (is->state->allowloops) 1398 if (is->state->allowloops)
@@ -1399,7 +1409,7 @@ static bool solve_island_checkloop(struct island *is, int direction)
1399 1409
1400 d1 = DINDEX(is->x, is->y); 1410 d1 = DINDEX(is->x, is->y);
1401 d2 = DINDEX(is_orth->x, is_orth->y); 1411 d2 = DINDEX(is_orth->x, is_orth->y);
1402 if (dsf_canonify(dsf, d1) == dsf_canonify(dsf, d2)) { 1412 if (dsf_equivalent(dsf, d1, d2)) {
1403 /* two islands are connected already; don't join them. */ 1413 /* two islands are connected already; don't join them. */
1404 return true; 1414 return true;
1405 } 1415 }
@@ -1457,7 +1467,8 @@ static bool solve_island_stage2(struct island *is, bool *didsth_r)
1457static bool solve_island_subgroup(struct island *is, int direction) 1467static bool solve_island_subgroup(struct island *is, int direction)
1458{ 1468{
1459 struct island *is_join; 1469 struct island *is_join;
1460 int nislands, *dsf = is->state->solver->dsf; 1470 int nislands;
1471 DSF *dsf = is->state->solver->dsf;
1461 game_state *state = is->state; 1472 game_state *state = is->state;
1462 1473
1463 debug(("..checking subgroups.\n")); 1474 debug(("..checking subgroups.\n"));
@@ -1520,7 +1531,6 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1520{ 1531{
1521 int i, n, x, y, missing, spc, curr, maxb; 1532 int i, n, x, y, missing, spc, curr, maxb;
1522 bool didsth = false; 1533 bool didsth = false;
1523 int wh = is->state->w * is->state->h;
1524 struct solver_state *ss = is->state->solver; 1534 struct solver_state *ss = is->state->solver;
1525 1535
1526 assert(didsth_r); 1536 assert(didsth_r);
@@ -1544,7 +1554,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1544 maxb = -1; 1554 maxb = -1;
1545 /* We have to squirrel the dsf away and restore it afterwards; 1555 /* We have to squirrel the dsf away and restore it afterwards;
1546 * it is additive only, and can't be removed from. */ 1556 * it is additive only, and can't be removed from. */
1547 memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int)); 1557 dsf_copy(ss->tmpdsf, ss->dsf);
1548 for (n = curr+1; n <= curr+spc; n++) { 1558 for (n = curr+1; n <= curr+spc; n++) {
1549 solve_join(is, i, n, false); 1559 solve_join(is, i, n, false);
1550 map_update_possibles(is->state); 1560 map_update_possibles(is->state);
@@ -1560,7 +1570,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1560 } 1570 }
1561 } 1571 }
1562 solve_join(is, i, curr, false); /* put back to before. */ 1572 solve_join(is, i, curr, false); /* put back to before. */
1563 memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int)); 1573 dsf_copy(ss->dsf, ss->tmpdsf);
1564 1574
1565 if (maxb != -1) { 1575 if (maxb != -1) {
1566 /*debug_state(is->state);*/ 1576 /*debug_state(is->state);*/
@@ -1629,7 +1639,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1629 is->adj.points[j].dx ? G_LINEH : G_LINEV); 1639 is->adj.points[j].dx ? G_LINEH : G_LINEV);
1630 if (before[i] != 0) continue; /* this idea is pointless otherwise */ 1640 if (before[i] != 0) continue; /* this idea is pointless otherwise */
1631 1641
1632 memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int)); 1642 dsf_copy(ss->tmpdsf, ss->dsf);
1633 1643
1634 for (j = 0; j < is->adj.npoints; j++) { 1644 for (j = 0; j < is->adj.npoints; j++) {
1635 spc = island_adjspace(is, true, missing, j); 1645 spc = island_adjspace(is, true, missing, j);
@@ -1644,7 +1654,7 @@ static bool solve_island_stage3(struct island *is, bool *didsth_r)
1644 1654
1645 for (j = 0; j < is->adj.npoints; j++) 1655 for (j = 0; j < is->adj.npoints; j++)
1646 solve_join(is, j, before[j], false); 1656 solve_join(is, j, before[j], false);
1647 memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int)); 1657 dsf_copy(ss->dsf, ss->tmpdsf);
1648 1658
1649 if (got) { 1659 if (got) {
1650 debug(("island at (%d,%d) must connect in direction (%d,%d) to" 1660 debug(("island at (%d,%d) must connect in direction (%d,%d) to"
@@ -1763,8 +1773,8 @@ static game_state *new_state(const game_params *params)
1763 ret->completed = false; 1773 ret->completed = false;
1764 1774
1765 ret->solver = snew(struct solver_state); 1775 ret->solver = snew(struct solver_state);
1766 ret->solver->dsf = snew_dsf(wh); 1776 ret->solver->dsf = dsf_new(wh);
1767 ret->solver->tmpdsf = snewn(wh, int); 1777 ret->solver->tmpdsf = dsf_new(wh);
1768 1778
1769 ret->solver->refcount = 1; 1779 ret->solver->refcount = 1;
1770 1780
@@ -1813,8 +1823,8 @@ static game_state *dup_game(const game_state *state)
1813static void free_game(game_state *state) 1823static void free_game(game_state *state)
1814{ 1824{
1815 if (--state->solver->refcount <= 0) { 1825 if (--state->solver->refcount <= 0) {
1816 sfree(state->solver->dsf); 1826 dsf_free(state->solver->dsf);
1817 sfree(state->solver->tmpdsf); 1827 dsf_free(state->solver->tmpdsf);
1818 sfree(state->solver); 1828 sfree(state->solver);
1819 } 1829 }
1820 1830
@@ -2004,28 +2014,38 @@ generated:
2004 2014
2005static const char *validate_desc(const game_params *params, const char *desc) 2015static const char *validate_desc(const game_params *params, const char *desc)
2006{ 2016{
2007 int i, wh = params->w * params->h; 2017 int i, j, wh = params->w * params->h, nislands = 0;
2018 bool *last_row = snewn(params->w, bool);
2008 2019
2020 memset(last_row, 0, params->w * sizeof(bool));
2009 for (i = 0; i < wh; i++) { 2021 for (i = 0; i < wh; i++) {
2010 if (*desc >= '1' && *desc <= '9') 2022 if ((*desc >= '1' && *desc <= '9') || (*desc >= 'A' && *desc <= 'G')) {
2011 /* OK */; 2023 nislands++;
2012 else if (*desc >= 'a' && *desc <= 'z') 2024 /* Look for other islands to the left and above. */
2025 if ((i % params->w > 0 && last_row[i % params->w - 1]) ||
2026 last_row[i % params->w]) {
2027 sfree(last_row);
2028 return "Game description contains joined islands";
2029 }
2030 last_row[i % params->w] = true;
2031 } else if (*desc >= 'a' && *desc <= 'z') {
2032 for (j = 0; j < *desc - 'a' + 1; j++)
2033 last_row[(i + j) % params->w] = false;
2013 i += *desc - 'a'; /* plus the i++ */ 2034 i += *desc - 'a'; /* plus the i++ */
2014 else if (*desc >= 'A' && *desc <= 'G') 2035 } else if (!*desc) {
2015 /* OK */; 2036 sfree(last_row);
2016 else if (*desc == 'V' || *desc == 'W' ||
2017 *desc == 'X' || *desc == 'Y' ||
2018 *desc == 'H' || *desc == 'I' ||
2019 *desc == 'J' || *desc == 'K')
2020 /* OK */;
2021 else if (!*desc)
2022 return "Game description shorter than expected"; 2037 return "Game description shorter than expected";
2023 else 2038 } else {
2039 sfree(last_row);
2024 return "Game description contains unexpected character"; 2040 return "Game description contains unexpected character";
2041 }
2025 desc++; 2042 desc++;
2026 } 2043 }
2044 sfree(last_row);
2027 if (*desc || i > wh) 2045 if (*desc || i > wh)
2028 return "Game description longer than expected"; 2046 return "Game description longer than expected";
2047 if (nislands < 2)
2048 return "Game description has too few islands";
2029 2049
2030 return NULL; 2050 return NULL;
2031} 2051}
@@ -2105,32 +2125,47 @@ static char *ui_cancel_drag(game_ui *ui)
2105 ui->dragx_src = ui->dragy_src = -1; 2125 ui->dragx_src = ui->dragy_src = -1;
2106 ui->dragx_dst = ui->dragy_dst = -1; 2126 ui->dragx_dst = ui->dragy_dst = -1;
2107 ui->dragging = false; 2127 ui->dragging = false;
2108 return UI_UPDATE; 2128 return MOVE_UI_UPDATE;
2109} 2129}
2110 2130
2111static game_ui *new_ui(const game_state *state) 2131static game_ui *new_ui(const game_state *state)
2112{ 2132{
2113 game_ui *ui = snew(game_ui); 2133 game_ui *ui = snew(game_ui);
2114 ui_cancel_drag(ui); 2134 ui_cancel_drag(ui);
2115 ui->cur_x = state->islands[0].x; 2135 if (state != NULL) {
2116 ui->cur_y = state->islands[0].y; 2136 ui->cur_x = state->islands[0].x;
2117 ui->cur_visible = false; 2137 ui->cur_y = state->islands[0].y;
2138 }
2139 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2118 ui->show_hints = false; 2140 ui->show_hints = false;
2119 return ui; 2141 return ui;
2120} 2142}
2121 2143
2122static void free_ui(game_ui *ui) 2144static config_item *get_prefs(game_ui *ui)
2123{ 2145{
2124 sfree(ui); 2146 config_item *ret;
2147
2148 ret = snewn(2, config_item);
2149
2150 ret[0].name = "Show possible bridge locations";
2151 ret[0].kw = "show-hints";
2152 ret[0].type = C_BOOLEAN;
2153 ret[0].u.boolean.bval = ui->show_hints;
2154
2155 ret[1].name = NULL;
2156 ret[1].type = C_END;
2157
2158 return ret;
2125} 2159}
2126 2160
2127static char *encode_ui(const game_ui *ui) 2161static void set_prefs(game_ui *ui, const config_item *cfg)
2128{ 2162{
2129 return NULL; 2163 ui->show_hints = cfg[0].u.boolean.bval;
2130} 2164}
2131 2165
2132static void decode_ui(game_ui *ui, const char *encoding) 2166static void free_ui(game_ui *ui)
2133{ 2167{
2168 sfree(ui);
2134} 2169}
2135 2170
2136static void game_changed_state(game_ui *ui, const game_state *oldstate, 2171static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -2138,6 +2173,20 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
2138{ 2173{
2139} 2174}
2140 2175
2176static const char *current_key_label(const game_ui *ui,
2177 const game_state *state, int button)
2178{
2179 if (IS_CURSOR_SELECT(button)) {
2180 if (!ui->cur_visible)
2181 return ""; /* Actually shows cursor. */
2182 if (ui->dragging || button == CURSOR_SELECT2)
2183 return "Finished";
2184 if (GRID(state, ui->cur_x, ui->cur_y) & G_ISLAND)
2185 return "Select";
2186 }
2187 return "";
2188}
2189
2141struct game_drawstate { 2190struct game_drawstate {
2142 int tilesize; 2191 int tilesize;
2143 int w, h; 2192 int w, h;
@@ -2307,7 +2356,7 @@ static char *update_drag_dst(const game_state *state, game_ui *ui,
2307 /*debug(("update_drag src (%d,%d) d(%d,%d) dst (%d,%d)\n", 2356 /*debug(("update_drag src (%d,%d) d(%d,%d) dst (%d,%d)\n",
2308 ui->dragx_src, ui->dragy_src, dx, dy, 2357 ui->dragx_src, ui->dragy_src, dx, dy,
2309 ui->dragx_dst, ui->dragy_dst));*/ 2358 ui->dragx_dst, ui->dragy_dst));*/
2310 return UI_UPDATE; 2359 return MOVE_UI_UPDATE;
2311} 2360}
2312 2361
2313static char *finish_drag(const game_state *state, game_ui *ui) 2362static char *finish_drag(const game_state *state, game_ui *ui)
@@ -2342,15 +2391,15 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2342 char buf[80], *ret; 2391 char buf[80], *ret;
2343 grid_type ggrid = INGRID(state,gx,gy) ? GRID(state,gx,gy) : 0; 2392 grid_type ggrid = INGRID(state,gx,gy) ? GRID(state,gx,gy) : 0;
2344 bool shift = button & MOD_SHFT, control = button & MOD_CTRL; 2393 bool shift = button & MOD_SHFT, control = button & MOD_CTRL;
2345 button &= ~MOD_MASK; 2394 button = STRIP_BUTTON_MODIFIERS(button);
2346 2395
2347 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 2396 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2348 if (!INGRID(state, gx, gy)) return NULL; 2397 if (!INGRID(state, gx, gy)) return MOVE_UNUSED;
2349 ui->cur_visible = false; 2398 ui->cur_visible = false;
2350 if (ggrid & G_ISLAND) { 2399 if (ggrid & G_ISLAND) {
2351 ui->dragx_src = gx; 2400 ui->dragx_src = gx;
2352 ui->dragy_src = gy; 2401 ui->dragy_src = gy;
2353 return UI_UPDATE; 2402 return MOVE_UI_UPDATE;
2354 } else 2403 } else
2355 return ui_cancel_drag(ui); 2404 return ui_cancel_drag(ui);
2356 } else if (button == LEFT_DRAG || button == RIGHT_DRAG) { 2405 } else if (button == LEFT_DRAG || button == RIGHT_DRAG) {
@@ -2364,7 +2413,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2364 /* cancel a drag when we go back to the starting point */ 2413 /* cancel a drag when we go back to the starting point */
2365 ui->dragx_dst = -1; 2414 ui->dragx_dst = -1;
2366 ui->dragy_dst = -1; 2415 ui->dragy_dst = -1;
2367 return UI_UPDATE; 2416 return MOVE_UI_UPDATE;
2368 } 2417 }
2369 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) { 2418 } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) {
2370 if (ui->dragging) { 2419 if (ui->dragging) {
@@ -2375,8 +2424,8 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2375 return ui_cancel_drag(ui); 2424 return ui_cancel_drag(ui);
2376 } 2425 }
2377 ui_cancel_drag(ui); 2426 ui_cancel_drag(ui);
2378 if (!INGRID(state, gx, gy)) return NULL; 2427 if (!INGRID(state, gx, gy)) return MOVE_UNUSED;
2379 if (!(GRID(state, gx, gy) & G_ISLAND)) return NULL; 2428 if (!(GRID(state, gx, gy) & G_ISLAND)) return MOVE_NO_EFFECT;
2380 sprintf(buf, "M%d,%d", gx, gy); 2429 sprintf(buf, "M%d,%d", gx, gy);
2381 return dupstr(buf); 2430 return dupstr(buf);
2382 } 2431 }
@@ -2397,9 +2446,9 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2397 if (ui->dragging) { 2446 if (ui->dragging) {
2398 int nx = ui->cur_x, ny = ui->cur_y; 2447 int nx = ui->cur_x, ny = ui->cur_y;
2399 2448
2400 move_cursor(button, &nx, &ny, state->w, state->h, false); 2449 move_cursor(button, &nx, &ny, state->w, state->h, false, NULL);
2401 if (nx == ui->cur_x && ny == ui->cur_y) 2450 if (nx == ui->cur_x && ny == ui->cur_y)
2402 return NULL; 2451 return MOVE_NO_EFFECT;
2403 update_drag_dst(state, ui, ds, 2452 update_drag_dst(state, ui, ds,
2404 COORD(nx)+TILE_SIZE/2, 2453 COORD(nx)+TILE_SIZE/2,
2405 COORD(ny)+TILE_SIZE/2); 2454 COORD(ny)+TILE_SIZE/2);
@@ -2451,19 +2500,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2451 2500
2452 if (!dingrid) break; 2501 if (!dingrid) break;
2453 } 2502 }
2454 if (!oingrid) return UI_UPDATE; 2503 if (!oingrid) return MOVE_UI_UPDATE;
2455 } 2504 }
2456 /* not reached */ 2505 /* not reached */
2457 2506
2458found: 2507found:
2459 ui->cur_x = nx; 2508 ui->cur_x = nx;
2460 ui->cur_y = ny; 2509 ui->cur_y = ny;
2461 return UI_UPDATE; 2510 return MOVE_UI_UPDATE;
2462 } 2511 }
2463 } else if (IS_CURSOR_SELECT(button)) { 2512 } else if (IS_CURSOR_SELECT(button)) {
2464 if (!ui->cur_visible) { 2513 if (!ui->cur_visible) {
2465 ui->cur_visible = true; 2514 ui->cur_visible = true;
2466 return UI_UPDATE; 2515 return MOVE_UI_UPDATE;
2467 } 2516 }
2468 if (ui->dragging || button == CURSOR_SELECT2) { 2517 if (ui->dragging || button == CURSOR_SELECT2) {
2469 ui_cancel_drag(ui); 2518 ui_cancel_drag(ui);
@@ -2471,7 +2520,7 @@ found:
2471 sprintf(buf, "M%d,%d", ui->cur_x, ui->cur_y); 2520 sprintf(buf, "M%d,%d", ui->cur_x, ui->cur_y);
2472 return dupstr(buf); 2521 return dupstr(buf);
2473 } else 2522 } else
2474 return UI_UPDATE; 2523 return MOVE_UI_UPDATE;
2475 } else { 2524 } else {
2476 grid_type v = GRID(state, ui->cur_x, ui->cur_y); 2525 grid_type v = GRID(state, ui->cur_x, ui->cur_y);
2477 if (v & G_ISLAND) { 2526 if (v & G_ISLAND) {
@@ -2480,7 +2529,7 @@ found:
2480 ui->dragy_src = ui->cur_y; 2529 ui->dragy_src = ui->cur_y;
2481 ui->dragx_dst = ui->dragy_dst = -1; 2530 ui->dragx_dst = ui->dragy_dst = -1;
2482 ui->drag_is_noline = (button == CURSOR_SELECT2); 2531 ui->drag_is_noline = (button == CURSOR_SELECT2);
2483 return UI_UPDATE; 2532 return MOVE_UI_UPDATE;
2484 } 2533 }
2485 } 2534 }
2486 } else if ((button >= '0' && button <= '9') || 2535 } else if ((button >= '0' && button <= '9') ||
@@ -2498,7 +2547,7 @@ found:
2498 2547
2499 if (!ui->cur_visible) { 2548 if (!ui->cur_visible) {
2500 ui->cur_visible = true; 2549 ui->cur_visible = true;
2501 return UI_UPDATE; 2550 return MOVE_UI_UPDATE;
2502 } 2551 }
2503 2552
2504 for (i = 0; i < state->n_islands; ++i) { 2553 for (i = 0; i < state->n_islands; ++i) {
@@ -2525,15 +2574,15 @@ found:
2525 if (best_x != -1 && best_y != -1) { 2574 if (best_x != -1 && best_y != -1) {
2526 ui->cur_x = best_x; 2575 ui->cur_x = best_x;
2527 ui->cur_y = best_y; 2576 ui->cur_y = best_y;
2528 return UI_UPDATE; 2577 return MOVE_UI_UPDATE;
2529 } else 2578 } else
2530 return NULL; 2579 return MOVE_NO_EFFECT;
2531 } else if (button == 'g' || button == 'G') { 2580 } else if (button == 'g' || button == 'G') {
2532 ui->show_hints = !ui->show_hints; 2581 ui->show_hints = !ui->show_hints;
2533 return UI_UPDATE; 2582 return MOVE_UI_UPDATE;
2534 } 2583 }
2535 2584
2536 return NULL; 2585 return MOVE_UNUSED;
2537} 2586}
2538 2587
2539static game_state *execute_move(const game_state *state, const char *move) 2588static game_state *execute_move(const game_state *state, const char *move)
@@ -2557,6 +2606,8 @@ static game_state *execute_move(const game_state *state, const char *move)
2557 goto badmove; 2606 goto badmove;
2558 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2)) 2607 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2559 goto badmove; 2608 goto badmove;
2609 /* Precisely one co-ordinate must differ between islands. */
2610 if ((x1 != x2) + (y1 != y2) != 1) goto badmove;
2560 is1 = INDEX(ret, gridi, x1, y1); 2611 is1 = INDEX(ret, gridi, x1, y1);
2561 is2 = INDEX(ret, gridi, x2, y2); 2612 is2 = INDEX(ret, gridi, x2, y2);
2562 if (!is1 || !is2) goto badmove; 2613 if (!is1 || !is2) goto badmove;
@@ -2568,6 +2619,7 @@ static game_state *execute_move(const game_state *state, const char *move)
2568 goto badmove; 2619 goto badmove;
2569 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2)) 2620 if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2570 goto badmove; 2621 goto badmove;
2622 if ((x1 != x2) + (y1 != y2) != 1) goto badmove;
2571 is1 = INDEX(ret, gridi, x1, y1); 2623 is1 = INDEX(ret, gridi, x1, y1);
2572 is2 = INDEX(ret, gridi, x2, y2); 2624 is2 = INDEX(ret, gridi, x2, y2);
2573 if (!is1 || !is2) goto badmove; 2625 if (!is1 || !is2) goto badmove;
@@ -2636,7 +2688,7 @@ static char *solve_game(const game_state *state, const game_state *currstate,
2636 */ 2688 */
2637 2689
2638static void game_compute_size(const game_params *params, int tilesize, 2690static void game_compute_size(const game_params *params, int tilesize,
2639 int *x, int *y) 2691 const game_ui *ui, int *x, int *y)
2640{ 2692{
2641 /* Ick: fake up `ds->tilesize' for macro expansion purposes */ 2693 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2642 struct { int tilesize; } ads, *ds = &ads; 2694 struct { int tilesize; } ads, *ds = &ads;
@@ -2987,9 +3039,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
2987 3039
2988 /* Clear screen, if required. */ 3040 /* Clear screen, if required. */
2989 if (!ds->started) { 3041 if (!ds->started) {
2990 draw_rect(dr, 0, 0,
2991 TILE_SIZE * ds->w + 2 * BORDER,
2992 TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND);
2993#ifdef DRAW_GRID 3042#ifdef DRAW_GRID
2994 draw_rect_outline(dr, 3043 draw_rect_outline(dr,
2995 COORD(0)-1, COORD(0)-1, 3044 COORD(0)-1, COORD(0)-1,
@@ -3177,22 +3226,19 @@ static int game_status(const game_state *state)
3177 return state->completed ? +1 : 0; 3226 return state->completed ? +1 : 0;
3178} 3227}
3179 3228
3180static bool game_timing_state(const game_state *state, game_ui *ui) 3229static void game_print_size(const game_params *params, const game_ui *ui,
3181{ 3230 float *x, float *y)
3182 return true;
3183}
3184
3185static void game_print_size(const game_params *params, float *x, float *y)
3186{ 3231{
3187 int pw, ph; 3232 int pw, ph;
3188 3233
3189 /* 10mm squares by default. */ 3234 /* 10mm squares by default. */
3190 game_compute_size(params, 1000, &pw, &ph); 3235 game_compute_size(params, 1000, ui, &pw, &ph);
3191 *x = pw / 100.0F; 3236 *x = pw / 100.0F;
3192 *y = ph / 100.0F; 3237 *y = ph / 100.0F;
3193} 3238}
3194 3239
3195static void game_print(drawing *dr, const game_state *state, int ts) 3240static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3241 int ts)
3196{ 3242{
3197 int ink = print_mono_colour(dr, 0); 3243 int ink = print_mono_colour(dr, 0);
3198 int paper = print_mono_colour(dr, 1); 3244 int paper = print_mono_colour(dr, 1);
@@ -3266,12 +3312,14 @@ const struct game thegame = {
3266 free_game, 3312 free_game,
3267 true, solve_game, 3313 true, solve_game,
3268 true, game_can_format_as_text_now, game_text_format, 3314 true, game_can_format_as_text_now, game_text_format,
3315 get_prefs, set_prefs,
3269 new_ui, 3316 new_ui,
3270 free_ui, 3317 free_ui,
3271 encode_ui, 3318 NULL, /* encode_ui */
3272 decode_ui, 3319 NULL, /* decode_ui */
3273 NULL, /* game_request_keys */ 3320 NULL, /* game_request_keys */
3274 game_changed_state, 3321 game_changed_state,
3322 current_key_label,
3275 interpret_move, 3323 interpret_move,
3276 execute_move, 3324 execute_move,
3277 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 3325 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3285,7 +3333,7 @@ const struct game thegame = {
3285 game_status, 3333 game_status,
3286 true, false, game_print_size, game_print, 3334 true, false, game_print_size, game_print,
3287 false, /* wants_statusbar */ 3335 false, /* wants_statusbar */
3288 false, game_timing_state, 3336 false, NULL, /* timing_state */
3289 REQUIRE_RBUTTON, /* flags */ 3337 REQUIRE_RBUTTON, /* flags */
3290}; 3338};
3291 3339