diff options
Diffstat (limited to 'apps/plugins/puzzles/src/bridges.c')
-rw-r--r-- | apps/plugins/puzzles/src/bridges.c | 210 |
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 { | |||
134 | typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */ | 138 | typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */ |
135 | 139 | ||
136 | struct solver_state { | 140 | struct 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 | ||
1134 | static void map_group(game_state *state) | 1140 | static 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) | |||
1180 | static bool map_group_check(game_state *state, int canon, bool warn, | 1186 | static 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 | ||
1213 | static bool map_group_full(game_state *state, int *ngroups_r) | 1220 | static 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) | |||
1267 | static void solve_join(struct island *is, int direction, int n, bool is_max) | 1275 | static 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) | |||
1382 | static bool solve_island_checkloop(struct island *is, int direction) | 1391 | static 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) | |||
1457 | static bool solve_island_subgroup(struct island *is, int direction) | 1467 | static 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) | |||
1813 | static void free_game(game_state *state) | 1823 | static 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 | ||
2005 | static const char *validate_desc(const game_params *params, const char *desc) | 2015 | static 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 | ||
2111 | static game_ui *new_ui(const game_state *state) | 2131 | static 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 | ||
2122 | static void free_ui(game_ui *ui) | 2144 | static 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 | ||
2127 | static char *encode_ui(const game_ui *ui) | 2161 | static 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 | ||
2132 | static void decode_ui(game_ui *ui, const char *encoding) | 2166 | static void free_ui(game_ui *ui) |
2133 | { | 2167 | { |
2168 | sfree(ui); | ||
2134 | } | 2169 | } |
2135 | 2170 | ||
2136 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 2171 | static 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 | ||
2176 | static 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 | |||
2141 | struct game_drawstate { | 2190 | struct 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 | ||
2313 | static char *finish_drag(const game_state *state, game_ui *ui) | 2362 | static 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 | ||
2458 | found: | 2507 | found: |
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 | ||
2539 | static game_state *execute_move(const game_state *state, const char *move) | 2588 | static 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 | ||
2638 | static void game_compute_size(const game_params *params, int tilesize, | 2690 | static 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 | ||
3180 | static bool game_timing_state(const game_state *state, game_ui *ui) | 3229 | static void game_print_size(const game_params *params, const game_ui *ui, |
3181 | { | 3230 | float *x, float *y) |
3182 | return true; | ||
3183 | } | ||
3184 | |||
3185 | static 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 | ||
3195 | static void game_print(drawing *dr, const game_state *state, int ts) | 3240 | static 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 | ||