summaryrefslogtreecommitdiff
path: root/apps/plugins/puzzles/src/galaxies.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/puzzles/src/galaxies.c')
-rw-r--r--apps/plugins/puzzles/src/galaxies.c734
1 files changed, 578 insertions, 156 deletions
diff --git a/apps/plugins/puzzles/src/galaxies.c b/apps/plugins/puzzles/src/galaxies.c
index 9172b90e12..8859917558 100644
--- a/apps/plugins/puzzles/src/galaxies.c
+++ b/apps/plugins/puzzles/src/galaxies.c
@@ -13,9 +13,46 @@
13 * Edges have on/off state; obviously the actual edges of the 13 * Edges have on/off state; obviously the actual edges of the
14 * board are fixed to on, and everything else starts as off. 14 * board are fixed to on, and everything else starts as off.
15 * 15 *
16 * TTD: 16 * Future solver directions:
17 * Cleverer solver 17 *
18 * Think about how to display remote groups of tiles? 18 * - Non-local version of the exclave extension? Suppose you have an
19 * exclave with multiple potential paths back home, but all of them
20 * go through the same tile somewhere in the middle of the path.
21 * Then _that_ critical square can be assigned to the home dot,
22 * even if we don't yet know the details of the path from it to
23 * either existing region.
24 *
25 * - Permit non-simply-connected puzzle instances in sub-Unreasonable
26 * mode? Even the simplest case 5x3:ubb is graded Unreasonable at
27 * present, because we have no solution technique short of
28 * recursion that can handle it.
29 *
30 * The reasoning a human uses for that puzzle is to observe that
31 * the centre left square has to connect to the centre dot, so it
32 * must have _some_ path back there. It could go round either side
33 * of the dot in the way. But _whichever_ way it goes, that rules
34 * out the left dot extending to the squares above and below it,
35 * because if it did that, that would block _both_ routes back to
36 * the centre.
37 *
38 * But the exclave-extending deduction we have at present is only
39 * capable of extending an exclave with _one_ liberty. This has
40 * two, so the only technique we have available is to try them one
41 * by one via recursion.
42 *
43 * My vague plan to fix this would be to re-run the exclave
44 * extension on a per-dot basis (probably after working out a
45 * non-local version as described above): instead of trying to find
46 * all exclaves at once, try it for one exclave at a time, or
47 * perhaps all exclaves relating to a particular home dot H. The
48 * point of this is that then you could spot pairs of squares with
49 * _two_ possible dots, one of which is H, and which are opposite
50 * to each other with respect to their other dot D (such as the
51 * squares above/below the left dot in this example). And then you
52 * merge those into one vertex of the connectivity graph, on the
53 * grounds that they're either both H or both D - and _then_ you
54 * have an exclave with only one path back home, and can make
55 * progress.
19 * 56 *
20 * Bugs: 57 * Bugs:
21 * 58 *
@@ -42,15 +79,22 @@
42#include <string.h> 79#include <string.h>
43#include <assert.h> 80#include <assert.h>
44#include <ctype.h> 81#include <ctype.h>
45#include <math.h> 82#include <limits.h>
83#ifdef NO_TGMATH_H
84# include <math.h>
85#else
86# include <tgmath.h>
87#endif
46 88
47#include "puzzles.h" 89#include "puzzles.h"
48 90
49#ifdef DEBUGGING 91#ifdef DEBUGGING
50#define solvep debug 92#define solvep debug
51#else 93#elif defined STANDALONE_SOLVER
52static bool solver_show_working; 94static bool solver_show_working;
53#define solvep(x) do { if (solver_show_working) { printf x; } } while(0) 95#define solvep(x) do { if (solver_show_working) { printf x; } } while(0)
96#else
97#define solvep(x) ((void)0)
54#endif 98#endif
55 99
56#ifdef STANDALONE_PICTURE_GENERATOR 100#ifdef STANDALONE_PICTURE_GENERATOR
@@ -148,13 +192,15 @@ struct game_state {
148 or -1 if stale. */ 192 or -1 if stale. */
149}; 193};
150 194
151static bool check_complete(const game_state *state, int *dsf, int *colours); 195static bool check_complete(const game_state *state, DSF *dsf, int *colours);
196static int solver_state_inner(game_state *state, int maxdiff, int depth);
152static int solver_state(game_state *state, int maxdiff); 197static int solver_state(game_state *state, int maxdiff);
153static int solver_obvious(game_state *state); 198static int solver_obvious(game_state *state);
154static int solver_obvious_dot(game_state *state, space *dot); 199static int solver_obvious_dot(game_state *state, space *dot);
155static space *space_opposite_dot(const game_state *state, const space *sp, 200static space *space_opposite_dot(const game_state *state, const space *sp,
156 const space *dot); 201 const space *dot);
157static space *tile_opposite(const game_state *state, const space *sp); 202static space *tile_opposite(const game_state *state, const space *sp);
203static game_state *execute_move(const game_state *state, const char *move);
158 204
159/* ---------------------------------------------------------- 205/* ----------------------------------------------------------
160 * Game parameters and presets 206 * Game parameters and presets
@@ -168,7 +214,9 @@ static const game_params galaxies_presets[] = {
168 { 7, 7, DIFF_NORMAL }, 214 { 7, 7, DIFF_NORMAL },
169 { 7, 7, DIFF_UNREASONABLE }, 215 { 7, 7, DIFF_UNREASONABLE },
170 { 10, 10, DIFF_NORMAL }, 216 { 10, 10, DIFF_NORMAL },
217 { 10, 10, DIFF_UNREASONABLE },
171 { 15, 15, DIFF_NORMAL }, 218 { 15, 15, DIFF_NORMAL },
219 { 15, 15, DIFF_UNREASONABLE },
172}; 220};
173 221
174static bool game_fetch_preset(int i, char **name, game_params **params) 222static bool game_fetch_preset(int i, char **name, game_params **params)
@@ -281,6 +329,10 @@ static const char *validate_params(const game_params *params, bool full)
281{ 329{
282 if (params->w < 3 || params->h < 3) 330 if (params->w < 3 || params->h < 3)
283 return "Width and height must both be at least 3"; 331 return "Width and height must both be at least 3";
332 if (params->w > INT_MAX / 2 || params->h > INT_MAX / 2 ||
333 params->w > (INT_MAX - params->w*2 - params->h*2 - 1) / 4 / params->h)
334 return "Width times height must not be unreasonably large";
335
284 /* 336 /*
285 * This shouldn't be able to happen at all, since decode_params 337 * This shouldn't be able to happen at all, since decode_params
286 * and custom_params will never generate anything that isn't 338 * and custom_params will never generate anything that isn't
@@ -352,6 +404,8 @@ static bool ok_to_add_assoc_with_opposite_internal(
352 int *colors; 404 int *colors;
353 bool toret; 405 bool toret;
354 406
407 if (tile->type != s_tile)
408 return false;
355 if (tile->flags & F_DOT) 409 if (tile->flags & F_DOT)
356 return false; 410 return false;
357 if (opposite == NULL) 411 if (opposite == NULL)
@@ -372,20 +426,21 @@ static bool ok_to_add_assoc_with_opposite_internal(
372 return toret; 426 return toret;
373} 427}
374 428
429#ifndef EDITOR
375static bool ok_to_add_assoc_with_opposite( 430static bool ok_to_add_assoc_with_opposite(
376 const game_state *state, space *tile, space *dot) 431 const game_state *state, space *tile, space *dot)
377{ 432{
378 space *opposite = space_opposite_dot(state, tile, dot); 433 space *opposite = space_opposite_dot(state, tile, dot);
379 return ok_to_add_assoc_with_opposite_internal(state, tile, opposite); 434 return ok_to_add_assoc_with_opposite_internal(state, tile, opposite);
380} 435}
436#endif
381 437
382static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) { 438static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
383 space *opposite = space_opposite_dot(state, tile, dot); 439 space *opposite = space_opposite_dot(state, tile, dot);
384 440
385 if(opposite) 441 if(opposite && ok_to_add_assoc_with_opposite_internal(
442 state, tile, opposite))
386 { 443 {
387 assert(ok_to_add_assoc_with_opposite_internal(state, tile, opposite));
388
389 remove_assoc_with_opposite(state, tile); 444 remove_assoc_with_opposite(state, tile);
390 add_assoc(state, tile, dot); 445 add_assoc(state, tile, dot);
391 remove_assoc_with_opposite(state, opposite); 446 remove_assoc_with_opposite(state, opposite);
@@ -393,12 +448,14 @@ static void add_assoc_with_opposite(game_state *state, space *tile, space *dot)
393 } 448 }
394} 449}
395 450
451#ifndef EDITOR
396static space *sp2dot(const game_state *state, int x, int y) 452static space *sp2dot(const game_state *state, int x, int y)
397{ 453{
398 space *sp = &SPACE(state, x, y); 454 space *sp = &SPACE(state, x, y);
399 if (!(sp->flags & F_TILE_ASSOC)) return NULL; 455 if (!(sp->flags & F_TILE_ASSOC)) return NULL;
400 return &SPACE(state, sp->dotx, sp->doty); 456 return &SPACE(state, sp->dotx, sp->doty);
401} 457}
458#endif
402 459
403#define IS_VERTICAL_EDGE(x) ((x % 2) == 0) 460#define IS_VERTICAL_EDGE(x) ((x % 2) == 0)
404 461
@@ -407,8 +464,24 @@ static bool game_can_format_as_text_now(const game_params *params)
407 return true; 464 return true;
408} 465}
409 466
467static char *encode_game(const game_state *state);
468
410static char *game_text_format(const game_state *state) 469static char *game_text_format(const game_state *state)
411{ 470{
471#ifdef EDITOR
472 game_params par;
473 char *params, *desc, *ret;
474 par.w = state->w;
475 par.h = state->h;
476 par.diff = DIFF_MAX; /* shouldn't be used */
477 params = encode_params(&par, false);
478 desc = encode_game(state);
479 ret = snewn(strlen(params) + strlen(desc) + 2, char);
480 sprintf(ret, "%s:%s", params, desc);
481 sfree(params);
482 sfree(desc);
483 return ret;
484#else
412 int maxlen = (state->sx+1)*state->sy, x, y; 485 int maxlen = (state->sx+1)*state->sy, x, y;
413 char *ret, *p; 486 char *ret, *p;
414 space *sp; 487 space *sp;
@@ -462,6 +535,7 @@ static char *game_text_format(const game_state *state)
462 *p = '\0'; 535 *p = '\0';
463 536
464 return ret; 537 return ret;
538#endif
465} 539}
466 540
467static void dbg_state(const game_state *state) 541static void dbg_state(const game_state *state)
@@ -684,7 +758,7 @@ static void tiles_from_edge(game_state *state, space *sp, space **ts)
684/* Returns a move string for use by 'solve', including the initial 758/* Returns a move string for use by 'solve', including the initial
685 * 'S' if issolve is true. */ 759 * 'S' if issolve is true. */
686static char *diff_game(const game_state *src, const game_state *dest, 760static char *diff_game(const game_state *src, const game_state *dest,
687 bool issolve) 761 bool issolve, int set_cdiff)
688{ 762{
689 int movelen = 0, movesize = 256, x, y, len; 763 int movelen = 0, movesize = 256, x, y, len;
690 char *move = snewn(movesize, char), buf[80]; 764 char *move = snewn(movesize, char), buf[80];
@@ -698,6 +772,26 @@ static char *diff_game(const game_state *src, const game_state *dest,
698 move[movelen++] = 'S'; 772 move[movelen++] = 'S';
699 sep = ";"; 773 sep = ";";
700 } 774 }
775#ifdef EDITOR
776 if (set_cdiff >= 0) {
777 switch (set_cdiff) {
778 case DIFF_IMPOSSIBLE:
779 movelen += sprintf(move+movelen, "%sII", sep);
780 break;
781 case DIFF_AMBIGUOUS:
782 movelen += sprintf(move+movelen, "%sIA", sep);
783 break;
784 case DIFF_UNFINISHED:
785 movelen += sprintf(move+movelen, "%sIU", sep);
786 break;
787 default:
788 movelen += sprintf(move+movelen, "%si%c",
789 sep, galaxies_diffchars[set_cdiff]);
790 break;
791 }
792 sep = ";";
793 }
794#endif
701 move[movelen] = '\0'; 795 move[movelen] = '\0';
702 for (x = 0; x < src->sx; x++) { 796 for (x = 0; x < src->sx; x++) {
703 for (y = 0; y < src->sy; y++) { 797 for (y = 0; y < src->sy; y++) {
@@ -747,7 +841,8 @@ static char *diff_game(const game_state *src, const game_state *dest,
747 841
748/* Returns true if a dot here would not be too close to any other dots 842/* Returns true if a dot here would not be too close to any other dots
749 * (and would avoid other game furniture). */ 843 * (and would avoid other game furniture). */
750static bool dot_is_possible(game_state *state, space *sp, bool allow_assoc) 844static bool dot_is_possible(const game_state *state, space *sp,
845 bool allow_assoc)
751{ 846{
752 int bx = 0, by = 0, dx, dy; 847 int bx = 0, by = 0, dx, dy;
753 space *adj; 848 space *adj;
@@ -921,7 +1016,7 @@ static void free_game(game_state *state)
921 * an edit mode. 1016 * an edit mode.
922 */ 1017 */
923 1018
924static char *encode_game(game_state *state) 1019static char *encode_game(const game_state *state)
925{ 1020{
926 char *desc, *p; 1021 char *desc, *p;
927 int run, x, y, area; 1022 int run, x, y, area;
@@ -1229,10 +1324,7 @@ static bool generate_try_block(game_state *state, random_state *rs,
1229} 1324}
1230 1325
1231#ifdef STANDALONE_SOLVER 1326#ifdef STANDALONE_SOLVER
1232int maxtries; 1327static bool one_try; /* override for soak testing */
1233#define MAXTRIES maxtries
1234#else
1235#define MAXTRIES 50
1236#endif 1328#endif
1237 1329
1238#define GP_DOTS 1 1330#define GP_DOTS 1
@@ -1242,6 +1334,8 @@ static void generate_pass(game_state *state, random_state *rs, int *scratch,
1242{ 1334{
1243 int sz = state->sx*state->sy, nspc, i, ret; 1335 int sz = state->sx*state->sy, nspc, i, ret;
1244 1336
1337 /* Random list of squares to try and process, one-by-one. */
1338 for (i = 0; i < sz; i++) scratch[i] = i;
1245 shuffle(scratch, sz, sizeof(int), rs); 1339 shuffle(scratch, sz, sizeof(int), rs);
1246 1340
1247 /* This bug took me a, er, little while to track down. On PalmOS, 1341 /* This bug took me a, er, little while to track down. On PalmOS,
@@ -1297,30 +1391,94 @@ static void generate_pass(game_state *state, random_state *rs, int *scratch,
1297 dbg_state(state); 1391 dbg_state(state);
1298} 1392}
1299 1393
1394/*
1395 * We try several times to generate a grid at all, before even feeding
1396 * it to the solver. Then we pick whichever of the resulting grids was
1397 * the most 'wiggly', as measured by the number of inward corners in
1398 * the shape of any region.
1399 *
1400 * Rationale: wiggly shapes are what make this puzzle fun, and it's
1401 * disappointing to be served a game whose entire solution is a
1402 * collection of rectangles. But we also don't want to introduce a
1403 * _hard requirement_ of wiggliness, because a player who knew that
1404 * was there would be able to use it as an extra clue. This way, we
1405 * just probabilistically skew in favour of wiggliness.
1406 */
1407#define GENERATE_TRIES 10
1408
1409static bool is_wiggle(const game_state *state, int x, int y, int dx, int dy)
1410{
1411 int x1 = x+2*dx, y1 = y+2*dy;
1412 int x2 = x-2*dy, y2 = y+2*dx;
1413 space *t, *t1, *t2;
1414
1415 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2))
1416 return false;
1417
1418 t = &SPACE(state, x, y);
1419 t1 = &SPACE(state, x1, y1);
1420 t2 = &SPACE(state, x2, y2);
1421 return ((t1->dotx == t2->dotx && t1->doty == t2->doty) &&
1422 !(t1->dotx == t->dotx && t1->doty == t->doty));
1423}
1424
1425static int measure_wiggliness(const game_state *state, int *scratch)
1426{
1427 int sz = state->sx*state->sy;
1428 int x, y, nwiggles = 0;
1429 memset(scratch, 0, sz);
1430
1431 for (y = 1; y < state->sy; y += 2) {
1432 for (x = 1; x < state->sx; x += 2) {
1433 if (y+2 < state->sy) {
1434 nwiggles += is_wiggle(state, x, y, 0, +1);
1435 nwiggles += is_wiggle(state, x, y, 0, -1);
1436 nwiggles += is_wiggle(state, x, y, +1, 0);
1437 nwiggles += is_wiggle(state, x, y, -1, 0);
1438 }
1439 }
1440 }
1441
1442 return nwiggles;
1443}
1444
1300static char *new_game_desc(const game_params *params, random_state *rs, 1445static char *new_game_desc(const game_params *params, random_state *rs,
1301 char **aux, bool interactive) 1446 char **aux, bool interactive)
1302{ 1447{
1303 game_state *state = blank_game(params->w, params->h), *copy; 1448 game_state *state = blank_game(params->w, params->h), *copy;
1304 char *desc; 1449 char *desc;
1305 int *scratch, sz = state->sx*state->sy, i; 1450 int *scratch, sz = state->sx*state->sy, i;
1306 int diff, ntries = 0; 1451 int diff, best_wiggliness;
1307 bool cc; 1452 bool cc;
1308 1453
1309 /* Random list of squares to try and process, one-by-one. */
1310 scratch = snewn(sz, int); 1454 scratch = snewn(sz, int);
1311 for (i = 0; i < sz; i++) scratch[i] = i;
1312 1455
1313generate: 1456generate:
1314 clear_game(state, true); 1457 best_wiggliness = -1;
1315 ntries++; 1458 copy = NULL;
1316 1459 for (i = 0; i < GENERATE_TRIES; i++) {
1317 /* generate_pass(state, rs, scratch, 10, GP_DOTS); */ 1460 int this_wiggliness;
1318 /* generate_pass(state, rs, scratch, 100, 0); */ 1461
1319 generate_pass(state, rs, scratch, 100, GP_DOTS); 1462 do {
1320 1463 clear_game(state, true);
1321 game_update_dots(state); 1464 generate_pass(state, rs, scratch, 100, GP_DOTS);
1322 1465 game_update_dots(state);
1323 if (state->ndots == 1) goto generate; 1466 } while (state->ndots == 1);
1467
1468 this_wiggliness = measure_wiggliness(state, scratch);
1469 debug(("Grid gen #%d: wiggliness=%d", i, this_wiggliness));
1470 if (this_wiggliness > best_wiggliness) {
1471 best_wiggliness = this_wiggliness;
1472 if (copy)
1473 free_game(copy);
1474 copy = dup_game(state);
1475 debug((" new best"));
1476 }
1477 debug(("\n"));
1478 }
1479 assert(copy);
1480 free_game(state);
1481 state = copy;
1324 1482
1325#ifdef DEBUGGING 1483#ifdef DEBUGGING
1326 { 1484 {
@@ -1345,12 +1503,17 @@ generate:
1345 assert(diff != DIFF_IMPOSSIBLE); 1503 assert(diff != DIFF_IMPOSSIBLE);
1346 if (diff != params->diff) { 1504 if (diff != params->diff) {
1347 /* 1505 /*
1348 * We'll grudgingly accept a too-easy puzzle, but we must 1506 * If the puzzle was insoluble at this difficulty level (i.e.
1349 * _not_ permit a too-hard one (one which the solver 1507 * too hard), _or_ soluble at a lower level (too easy), go
1350 * couldn't handle at all). 1508 * round again.
1509 *
1510 * An exception is in soak-testing mode, where we return the
1511 * first puzzle we got regardless.
1351 */ 1512 */
1352 if (diff > params->diff || 1513#ifdef STANDALONE_SOLVER
1353 ntries < MAXTRIES) goto generate; 1514 if (!one_try)
1515#endif
1516 goto generate;
1354 } 1517 }
1355 1518
1356#ifdef STANDALONE_PICTURE_GENERATOR 1519#ifdef STANDALONE_PICTURE_GENERATOR
@@ -1527,6 +1690,10 @@ generate:
1527 dbg_state(state); 1690 dbg_state(state);
1528#endif 1691#endif
1529 1692
1693 game_state *blank = blank_game(params->w, params->h);
1694 *aux = diff_game(blank, state, true, -1);
1695 free_game(blank);
1696
1530 free_game(state); 1697 free_game(state);
1531 sfree(scratch); 1698 sfree(scratch);
1532 1699
@@ -1623,13 +1790,17 @@ static game_state *new_game(midend *me, const game_params *params,
1623 * Solver and all its little wizards. 1790 * Solver and all its little wizards.
1624 */ 1791 */
1625 1792
1793#if defined DEBUGGING || defined STANDALONE_SOLVER
1626static int solver_recurse_depth; 1794static int solver_recurse_depth;
1795#define STATIC_RECURSION_DEPTH
1796#endif
1627 1797
1628typedef struct solver_ctx { 1798typedef struct solver_ctx {
1629 game_state *state; 1799 game_state *state;
1630 int sz; /* state->sx * state->sy */ 1800 int sz; /* state->sx * state->sy */
1631 space **scratch; /* size sz */ 1801 space **scratch; /* size sz */
1632 1802 DSF *dsf; /* size sz */
1803 int *iscratch; /* size sz */
1633} solver_ctx; 1804} solver_ctx;
1634 1805
1635static solver_ctx *new_solver(game_state *state) 1806static solver_ctx *new_solver(game_state *state)
@@ -1638,12 +1809,16 @@ static solver_ctx *new_solver(game_state *state)
1638 sctx->state = state; 1809 sctx->state = state;
1639 sctx->sz = state->sx*state->sy; 1810 sctx->sz = state->sx*state->sy;
1640 sctx->scratch = snewn(sctx->sz, space *); 1811 sctx->scratch = snewn(sctx->sz, space *);
1812 sctx->dsf = dsf_new(sctx->sz);
1813 sctx->iscratch = snewn(sctx->sz, int);
1641 return sctx; 1814 return sctx;
1642} 1815}
1643 1816
1644static void free_solver(solver_ctx *sctx) 1817static void free_solver(solver_ctx *sctx)
1645{ 1818{
1646 sfree(sctx->scratch); 1819 sfree(sctx->scratch);
1820 dsf_free(sctx->dsf);
1821 sfree(sctx->iscratch);
1647 sfree(sctx); 1822 sfree(sctx);
1648} 1823}
1649 1824
@@ -1804,7 +1979,7 @@ static int solver_lines_opposite_cb(game_state *state, space *edge, void *ctx)
1804 if (!(edge_opp->flags & F_EDGE_SET)) { 1979 if (!(edge_opp->flags & F_EDGE_SET)) {
1805 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n", 1980 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n",
1806 solver_recurse_depth*4, "", 1981 solver_recurse_depth*4, "",
1807 tile_opp->x-dx, tile_opp->y-dy, edge->x, edge->y)); 1982 tile_opp->x+dx, tile_opp->y+dy, edge->x, edge->y));
1808 edge_opp->flags |= F_EDGE_SET; 1983 edge_opp->flags |= F_EDGE_SET;
1809 didsth = 1; 1984 didsth = 1;
1810 } 1985 }
@@ -2097,6 +2272,177 @@ static int solver_expand_dots(game_state *state, solver_ctx *sctx)
2097 return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx); 2272 return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx);
2098} 2273}
2099 2274
2275static int solver_extend_exclaves(game_state *state, solver_ctx *sctx)
2276{
2277 int x, y, done_something = 0;
2278
2279 /*
2280 * Make a dsf by unifying any two adjacent tiles associated with
2281 * the same dot. This will identify separate connected components
2282 * of the tiles belonging to a given dot. Any such component that
2283 * doesn't contain its own dot is an 'exclave', and will need some
2284 * kind of path of tiles to connect it back to the dot.
2285 */
2286 dsf_reinit(sctx->dsf);
2287 for (x = 1; x < state->sx; x += 2) {
2288 for (y = 1; y < state->sy; y += 2) {
2289 int dotx, doty;
2290 space *tile, *othertile;
2291
2292 tile = &SPACE(state, x, y);
2293 if (!(tile->flags & F_TILE_ASSOC))
2294 continue; /* not associated with any dot */
2295 dotx = tile->dotx;
2296 doty = tile->doty;
2297
2298 if (INGRID(state, x+2, y)) {
2299 othertile = &SPACE(state, x+2, y);
2300 if ((othertile->flags & F_TILE_ASSOC) &&
2301 othertile->dotx == dotx && othertile->doty == doty)
2302 dsf_merge(sctx->dsf, y*state->sx+x, y*state->sx+(x+2));
2303 }
2304
2305 if (INGRID(state, x, y+2)) {
2306 othertile = &SPACE(state, x, y+2);
2307 if ((othertile->flags & F_TILE_ASSOC) &&
2308 othertile->dotx == dotx && othertile->doty == doty)
2309 dsf_merge(sctx->dsf, y*state->sx+x, (y+2)*state->sx+x);
2310 }
2311 }
2312 }
2313
2314 /*
2315 * Go through the grid again, and count the 'liberties' of each
2316 * connected component, in the Go sense, i.e. the number of
2317 * currently unassociated squares adjacent to the component. The
2318 * idea is that if an exclave has just one liberty, then that
2319 * square _must_ extend the exclave, or else it will be completely
2320 * cut off from connecting back to its home dot.
2321 *
2322 * We need to count each adjacent square just once, even if it
2323 * borders the component on multiple edges. So we'll go through
2324 * each unassociated square, check all four of its neighbours, and
2325 * de-duplicate them.
2326 *
2327 * We'll store the count of liberties in the entry of iscratch
2328 * corresponding to the square centre (i.e. with odd coordinates).
2329 * Every time we find a liberty, we store its index in the square
2330 * to the left of that, so that when a component has exactly one
2331 * liberty we can remember what it was.
2332 *
2333 * Square centres that are not the canonical dsf element of a
2334 * connected component will get their liberty count set to -1,
2335 * which will allow us to identify them in the later loop (after
2336 * we start making changes and need to spot that an associated
2337 * square _now_ was not associated when the dsf was built).
2338 */
2339
2340 /* Initialise iscratch */
2341 for (x = 1; x < state->sx; x += 2) {
2342 for (y = 1; y < state->sy; y += 2) {
2343 int index = y * state->sx + x;
2344 if (!(SPACE(state, x, y).flags & F_TILE_ASSOC) ||
2345 dsf_canonify(sctx->dsf, index) != index) {
2346 sctx->iscratch[index] = -1; /* mark as not a component */
2347 } else {
2348 sctx->iscratch[index] = 0; /* zero liberty count */
2349 sctx->iscratch[index-1] = 0; /* initialise neighbour id */
2350 }
2351 }
2352 }
2353
2354 /* Find each unassociated square and see what it's a liberty of */
2355 for (x = 1; x < state->sx; x += 2) {
2356 for (y = 1; y < state->sy; y += 2) {
2357 int dx, dy, ni[4], nn, i;
2358
2359 if ((SPACE(state, x, y).flags & F_TILE_ASSOC))
2360 continue; /* not an unassociated square */
2361
2362 /* Find distinct indices of adjacent components */
2363 nn = 0;
2364 for (dx = -1; dx <= 1; dx++) {
2365 for (dy = -1; dy <= 1; dy++) {
2366 if (dx != 0 && dy != 0) continue;
2367 if (dx == 0 && dy == 0) continue;
2368
2369 if (INGRID(state, x+2*dx, y+2*dy) &&
2370 (SPACE(state, x+2*dx, y+2*dy).flags & F_TILE_ASSOC)) {
2371 /* Find id of the component adjacent to x,y */
2372 int nindex = (y+2*dy) * state->sx + (x+2*dx);
2373 nindex = dsf_canonify(sctx->dsf, nindex);
2374
2375 /* See if we've seen it before in another direction */
2376 for (i = 0; i < nn; i++)
2377 if (ni[i] == nindex)
2378 break;
2379 if (i == nn) {
2380 /* No, it's new. Mark x,y as a liberty of it */
2381 sctx->iscratch[nindex]++;
2382 assert(nindex > 0);
2383 sctx->iscratch[nindex-1] = y * state->sx + x;
2384
2385 /* And record this component as having been seen */
2386 ni[nn++] = nindex;
2387 }
2388 }
2389 }
2390 }
2391 }
2392 }
2393
2394 /*
2395 * Now we have all the data we need to find exclaves with exactly
2396 * one liberty. In each case, associate the unique liberty square
2397 * with the same dot.
2398 */
2399 for (x = 1; x < state->sx; x += 2) {
2400 for (y = 1; y < state->sy; y += 2) {
2401 int index, dotx, doty, ex, ey, added;
2402 space *tile;
2403
2404 index = y*state->sx+x;
2405 if (sctx->iscratch[index] == -1)
2406 continue; /* wasn't canonical when dsf was built */
2407
2408 tile = &SPACE(state, x, y);
2409 if (!(tile->flags & F_TILE_ASSOC))
2410 continue; /* not associated with any dot */
2411 dotx = tile->dotx;
2412 doty = tile->doty;
2413
2414 if (index == dsf_canonify(
2415 sctx->dsf, (doty | 1) * state->sx + (dotx | 1)))
2416 continue; /* not an exclave - contains its own dot */
2417
2418 if (sctx->iscratch[index] == 0) {
2419 solvep(("%*sExclave at %d,%d has no liberties!\n",
2420 solver_recurse_depth*4, "", x, y));
2421 return -1;
2422 }
2423
2424 if (sctx->iscratch[index] != 1)
2425 continue; /* more than one liberty, can't be sure which */
2426
2427 assert(sctx->iscratch[index-1] != 0);
2428 ex = sctx->iscratch[index-1] % state->sx;
2429 ey = sctx->iscratch[index-1] / state->sx;
2430 tile = &SPACE(state, ex, ey);
2431 if (tile->flags & F_TILE_ASSOC)
2432 continue; /* already done by earlier iteration of this loop */
2433
2434 added = solver_add_assoc(state, tile, dotx, doty,
2435 "to connect exclave");
2436 if (added < 0)
2437 return -1;
2438 if (added > 0)
2439 done_something = 1;
2440 }
2441 }
2442
2443 return done_something;
2444}
2445
2100struct recurse_ctx { 2446struct recurse_ctx {
2101 space *best; 2447 space *best;
2102 int bestn; 2448 int bestn;
@@ -2124,14 +2470,14 @@ static int solver_recurse_cb(game_state *state, space *tile, void *ctx)
2124 2470
2125#define MAXRECURSE 5 2471#define MAXRECURSE 5
2126 2472
2127static int solver_recurse(game_state *state, int maxdiff) 2473static int solver_recurse(game_state *state, int maxdiff, int depth)
2128{ 2474{
2129 int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy; 2475 int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy;
2130 space *ingrid, *outgrid = NULL, *bestopp; 2476 space *ingrid, *outgrid = NULL, *bestopp;
2131 struct recurse_ctx rctx; 2477 struct recurse_ctx rctx;
2132 2478
2133 if (solver_recurse_depth >= MAXRECURSE) { 2479 if (depth >= MAXRECURSE) {
2134 solvep(("Limiting recursion to %d, returning.", MAXRECURSE)); 2480 solvep(("Limiting recursion to %d, returning.\n", MAXRECURSE));
2135 return DIFF_UNFINISHED; 2481 return DIFF_UNFINISHED;
2136 } 2482 }
2137 2483
@@ -2149,10 +2495,6 @@ static int solver_recurse(game_state *state, int maxdiff)
2149 solver_recurse_depth*4, "", 2495 solver_recurse_depth*4, "",
2150 rctx.best->x, rctx.best->y, rctx.bestn)); 2496 rctx.best->x, rctx.best->y, rctx.bestn));
2151 2497
2152#ifdef STANDALONE_SOLVER
2153 solver_recurse_depth++;
2154#endif
2155
2156 ingrid = snewn(gsz, space); 2498 ingrid = snewn(gsz, space);
2157 memcpy(ingrid, state->grid, gsz * sizeof(space)); 2499 memcpy(ingrid, state->grid, gsz * sizeof(space));
2158 2500
@@ -2166,7 +2508,11 @@ static int solver_recurse(game_state *state, int maxdiff)
2166 state->dots[n]->x, state->dots[n]->y, 2508 state->dots[n]->x, state->dots[n]->y,
2167 "Attempting for recursion"); 2509 "Attempting for recursion");
2168 2510
2169 ret = solver_state(state, maxdiff); 2511 ret = solver_state_inner(state, maxdiff, depth + 1);
2512
2513#ifdef STATIC_RECURSION_DEPTH
2514 solver_recurse_depth = depth; /* restore after recursion returns */
2515#endif
2170 2516
2171 if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) { 2517 if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) {
2172 /* we found our first solved grid; copy it away. */ 2518 /* we found our first solved grid; copy it away. */
@@ -2198,10 +2544,6 @@ static int solver_recurse(game_state *state, int maxdiff)
2198 break; 2544 break;
2199 } 2545 }
2200 2546
2201#ifdef STANDALONE_SOLVER
2202 solver_recurse_depth--;
2203#endif
2204
2205 if (outgrid) { 2547 if (outgrid) {
2206 /* we found (at least one) soln; copy it back to state */ 2548 /* we found (at least one) soln; copy it back to state */
2207 memcpy(state->grid, outgrid, gsz * sizeof(space)); 2549 memcpy(state->grid, outgrid, gsz * sizeof(space));
@@ -2211,7 +2553,7 @@ static int solver_recurse(game_state *state, int maxdiff)
2211 return diff; 2553 return diff;
2212} 2554}
2213 2555
2214static int solver_state(game_state *state, int maxdiff) 2556static int solver_state_inner(game_state *state, int maxdiff, int depth)
2215{ 2557{
2216 solver_ctx *sctx = new_solver(state); 2558 solver_ctx *sctx = new_solver(state);
2217 int ret, diff = DIFF_NORMAL; 2559 int ret, diff = DIFF_NORMAL;
@@ -2223,6 +2565,10 @@ static int solver_state(game_state *state, int maxdiff)
2223 picture = NULL; 2565 picture = NULL;
2224#endif 2566#endif
2225 2567
2568#ifdef STATIC_RECURSION_DEPTH
2569 solver_recurse_depth = depth;
2570#endif
2571
2226 ret = solver_obvious(state); 2572 ret = solver_obvious(state);
2227 if (ret < 0) { 2573 if (ret < 0) {
2228 diff = DIFF_IMPOSSIBLE; 2574 diff = DIFF_IMPOSSIBLE;
@@ -2247,6 +2593,9 @@ cont:
2247 ret = solver_expand_dots(state, sctx); 2593 ret = solver_expand_dots(state, sctx);
2248 CHECKRET(DIFF_NORMAL); 2594 CHECKRET(DIFF_NORMAL);
2249 2595
2596 ret = solver_extend_exclaves(state, sctx);
2597 CHECKRET(DIFF_NORMAL);
2598
2250 if (maxdiff <= DIFF_NORMAL) 2599 if (maxdiff <= DIFF_NORMAL)
2251 break; 2600 break;
2252 2601
@@ -2259,7 +2608,7 @@ cont:
2259 if (check_complete(state, NULL, NULL)) goto got_result; 2608 if (check_complete(state, NULL, NULL)) goto got_result;
2260 2609
2261 diff = (maxdiff >= DIFF_UNREASONABLE) ? 2610 diff = (maxdiff >= DIFF_UNREASONABLE) ?
2262 solver_recurse(state, maxdiff) : DIFF_UNFINISHED; 2611 solver_recurse(state, maxdiff, depth) : DIFF_UNFINISHED;
2263 2612
2264got_result: 2613got_result:
2265 free_solver(sctx); 2614 free_solver(sctx);
@@ -2275,6 +2624,11 @@ got_result:
2275 return diff; 2624 return diff;
2276} 2625}
2277 2626
2627static int solver_state(game_state *state, int maxdiff)
2628{
2629 return solver_state_inner(state, maxdiff, 0);
2630}
2631
2278#ifndef EDITOR 2632#ifndef EDITOR
2279static char *solve_game(const game_state *state, const game_state *currstate, 2633static char *solve_game(const game_state *state, const game_state *currstate,
2280 const char *aux, const char **error) 2634 const char *aux, const char **error)
@@ -2284,21 +2638,26 @@ static char *solve_game(const game_state *state, const game_state *currstate,
2284 int i; 2638 int i;
2285 int diff; 2639 int diff;
2286 2640
2287 tosolve = dup_game(currstate); 2641 if (aux) {
2288 diff = solver_state(tosolve, DIFF_UNREASONABLE); 2642 tosolve = execute_move(state, aux);
2289 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2290 debug(("solve_game solved with current state.\n"));
2291 goto solved; 2643 goto solved;
2292 } 2644 } else {
2293 free_game(tosolve); 2645 tosolve = dup_game(currstate);
2646 diff = solver_state(tosolve, DIFF_UNREASONABLE);
2647 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2648 debug(("solve_game solved with current state.\n"));
2649 goto solved;
2650 }
2651 free_game(tosolve);
2294 2652
2295 tosolve = dup_game(state); 2653 tosolve = dup_game(state);
2296 diff = solver_state(tosolve, DIFF_UNREASONABLE); 2654 diff = solver_state(tosolve, DIFF_UNREASONABLE);
2297 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) { 2655 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2298 debug(("solve_game solved with original state.\n")); 2656 debug(("solve_game solved with original state.\n"));
2299 goto solved; 2657 goto solved;
2658 }
2659 free_game(tosolve);
2300 } 2660 }
2301 free_game(tosolve);
2302 2661
2303 return NULL; 2662 return NULL;
2304 2663
@@ -2309,7 +2668,7 @@ solved:
2309 */ 2668 */
2310 for (i = 0; i < tosolve->sx*tosolve->sy; i++) 2669 for (i = 0; i < tosolve->sx*tosolve->sy; i++)
2311 tosolve->grid[i].flags &= ~F_TILE_ASSOC; 2670 tosolve->grid[i].flags &= ~F_TILE_ASSOC;
2312 ret = diff_game(currstate, tosolve, true); 2671 ret = diff_game(currstate, tosolve, true, -1);
2313 free_game(tosolve); 2672 free_game(tosolve);
2314 return ret; 2673 return ret;
2315} 2674}
@@ -2333,7 +2692,7 @@ static game_ui *new_ui(const game_state *state)
2333 game_ui *ui = snew(game_ui); 2692 game_ui *ui = snew(game_ui);
2334 ui->dragging = false; 2693 ui->dragging = false;
2335 ui->cur_x = ui->cur_y = 1; 2694 ui->cur_x = ui->cur_y = 1;
2336 ui->cur_visible = false; 2695 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2337 return ui; 2696 return ui;
2338} 2697}
2339 2698
@@ -2342,15 +2701,6 @@ static void free_ui(game_ui *ui)
2342 sfree(ui); 2701 sfree(ui);
2343} 2702}
2344 2703
2345static char *encode_ui(const game_ui *ui)
2346{
2347 return NULL;
2348}
2349
2350static void decode_ui(game_ui *ui, const char *encoding)
2351{
2352}
2353
2354static void game_changed_state(game_ui *ui, const game_state *oldstate, 2704static void game_changed_state(game_ui *ui, const game_state *oldstate,
2355 const game_state *newstate) 2705 const game_state *newstate)
2356{ 2706{
@@ -2383,7 +2733,7 @@ struct game_drawstate {
2383 blitter *blmirror; 2733 blitter *blmirror;
2384 2734
2385 bool dragging; 2735 bool dragging;
2386 int dragx, dragy; 2736 int dragx, dragy, oppx, oppy;
2387 2737
2388 int *colour_scratch; 2738 int *colour_scratch;
2389 2739
@@ -2445,34 +2795,74 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2445 int px, py; 2795 int px, py;
2446 space *sp; 2796 space *sp;
2447 2797
2448 px = 2*FROMCOORD((float)x) + 0.5; 2798 px = 2*FROMCOORD((float)x) + 0.5F;
2449 py = 2*FROMCOORD((float)y) + 0.5; 2799 py = 2*FROMCOORD((float)y) + 0.5F;
2450
2451 state->cdiff = -1;
2452 2800
2453 if (button == 'C' || button == 'c') return dupstr("C"); 2801 if (button == 'C' || button == 'c') return dupstr("C");
2454 2802
2455 if (button == 'S' || button == 's') { 2803 if (button == 'S' || button == 's') {
2456 char *ret; 2804 char *ret;
2457 game_state *tmp = dup_game(state); 2805 game_state *tmp = dup_game(state);
2458 state->cdiff = solver_state(tmp, DIFF_UNREASONABLE-1); 2806 int cdiff = solver_state(tmp, DIFF_UNREASONABLE-1);
2459 ret = diff_game(state, tmp, 0); 2807 ret = diff_game(state, tmp, 0, cdiff);
2460 free_game(tmp); 2808 free_game(tmp);
2461 return ret; 2809 return ret;
2462 } 2810 }
2463 2811
2464 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { 2812 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2465 if (!INUI(state, px, py)) return NULL; 2813 if (!INUI(state, px, py)) return MOVE_UNUSED;
2466 sp = &SPACE(state, px, py); 2814 sp = &SPACE(state, px, py);
2467 if (!dot_is_possible(state, sp, 1)) return NULL; 2815 if (!dot_is_possible(state, sp, 1)) return MOVE_NO_EFFECT;
2468 sprintf(buf, "%c%d,%d", 2816 sprintf(buf, "%c%d,%d",
2469 (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py); 2817 (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py);
2470 return dupstr(buf); 2818 return dupstr(buf);
2471 } 2819 }
2472 2820
2473 return NULL; 2821 return MOVE_UNUSED;
2474} 2822}
2475#else 2823#else
2824static bool edge_placement_legal(const game_state *state, int x, int y)
2825{
2826 space *sp = &SPACE(state, x, y);
2827 if (sp->type != s_edge)
2828 return false; /* this is a face-centre or a grid vertex */
2829
2830 /* Check this line doesn't actually intersect a dot */
2831 unsigned int flags = (GRID(state, grid, x, y).flags |
2832 GRID(state, grid, x & ~1U, y & ~1U).flags |
2833 GRID(state, grid, (x+1) & ~1U, (y+1) & ~1U).flags);
2834 if (flags & F_DOT)
2835 return false;
2836 return true;
2837}
2838
2839static const char *current_key_label(const game_ui *ui,
2840 const game_state *state, int button)
2841{
2842 space *sp;
2843
2844 if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
2845 sp = &SPACE(state, ui->cur_x, ui->cur_y);
2846 if (ui->dragging) {
2847 if (ui->cur_x == ui->srcx && ui->cur_y == ui->srcy)
2848 return "Cancel";
2849 if (ok_to_add_assoc_with_opposite(
2850 state, &SPACE(state, ui->cur_x, ui->cur_y),
2851 &SPACE(state, ui->dotx, ui->doty)))
2852 return "Place";
2853 return (ui->srcx == ui->dotx && ui->srcy == ui->doty) ?
2854 "Cancel" : "Remove";
2855 } else if (sp->flags & F_DOT)
2856 return "New arrow";
2857 else if (sp->flags & F_TILE_ASSOC)
2858 return "Move arrow";
2859 else if (sp->type == s_edge &&
2860 edge_placement_legal(state, ui->cur_x, ui->cur_y))
2861 return (sp->flags & F_EDGE_SET) ? "Clear" : "Edge";
2862 }
2863 return "";
2864}
2865
2476static char *interpret_move(const game_state *state, game_ui *ui, 2866static char *interpret_move(const game_state *state, game_ui *ui,
2477 const game_drawstate *ds, 2867 const game_drawstate *ds,
2478 int x, int y, int button) 2868 int x, int y, int button)
@@ -2499,7 +2889,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2499 char *ret; 2889 char *ret;
2500 game_state *tmp = dup_game(state); 2890 game_state *tmp = dup_game(state);
2501 solver_obvious(tmp); 2891 solver_obvious(tmp);
2502 ret = diff_game(state, tmp, false); 2892 ret = diff_game(state, tmp, false, -1);
2503 free_game(tmp); 2893 free_game(tmp);
2504 return ret; 2894 return ret;
2505 } 2895 }
@@ -2509,21 +2899,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2509 coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y), 2899 coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y),
2510 &px, &py); 2900 &px, &py);
2511 2901
2512 if (!INUI(state, px, py)) return NULL; 2902 if (!INUI(state, px, py)) return MOVE_UNUSED;
2903 if (!edge_placement_legal(state, px, py))
2904 return MOVE_NO_EFFECT;
2513 2905
2514 sp = &SPACE(state, px, py); 2906 sprintf(buf, "E%d,%d", px, py);
2515 assert(sp->type == s_edge); 2907 return dupstr(buf);
2516 {
2517 sprintf(buf, "E%d,%d", px, py);
2518 return dupstr(buf);
2519 }
2520 } else if (button == RIGHT_BUTTON) { 2908 } else if (button == RIGHT_BUTTON) {
2521 int px1, py1; 2909 int px1, py1;
2522 2910
2523 ui->cur_visible = false; 2911 ui->cur_visible = false;
2524 2912
2525 px = (int)(2*FROMCOORD((float)x) + 0.5); 2913 px = (int)(2*FROMCOORD((float)x) + 0.5F);
2526 py = (int)(2*FROMCOORD((float)y) + 0.5); 2914 py = (int)(2*FROMCOORD((float)y) + 0.5F);
2527 2915
2528 dot = NULL; 2916 dot = NULL;
2529 2917
@@ -2575,28 +2963,29 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2575 ui->dy = y; 2963 ui->dy = y;
2576 ui->dotx = dot->x; 2964 ui->dotx = dot->x;
2577 ui->doty = dot->y; 2965 ui->doty = dot->y;
2578 return UI_UPDATE; 2966 return MOVE_UI_UPDATE;
2579 } 2967 }
2968 return MOVE_NO_EFFECT;
2580 } else if (button == RIGHT_DRAG && ui->dragging) { 2969 } else if (button == RIGHT_DRAG && ui->dragging) {
2581 /* just move the drag coords. */ 2970 /* just move the drag coords. */
2582 ui->dx = x; 2971 ui->dx = x;
2583 ui->dy = y; 2972 ui->dy = y;
2584 return UI_UPDATE; 2973 return MOVE_UI_UPDATE;
2585 } else if (button == RIGHT_RELEASE && ui->dragging) { 2974 } else if (button == RIGHT_RELEASE && ui->dragging) {
2586 ui->dragging = false;
2587
2588 /* 2975 /*
2589 * Drags are always targeted at a single square. 2976 * Drags are always targeted at a single square.
2590 */ 2977 */
2591 px = 2*FROMCOORD(x+TILE_SIZE) - 1; 2978 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2592 py = 2*FROMCOORD(y+TILE_SIZE) - 1; 2979 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2593 2980
2981 dropped: /* We arrive here from the end of a keyboard drag. */
2982 ui->dragging = false;
2594 /* 2983 /*
2595 * Dragging an arrow on to the same square it started from 2984 * Dragging an arrow on to the same square it started from
2596 * is a null move; just update the ui and finish. 2985 * is a null move; just update the ui and finish.
2597 */ 2986 */
2598 if (px == ui->srcx && py == ui->srcy) 2987 if (px == ui->srcx && py == ui->srcy)
2599 return UI_UPDATE; 2988 return MOVE_UI_UPDATE;
2600 2989
2601 /* 2990 /*
2602 * Otherwise, we remove the arrow from its starting 2991 * Otherwise, we remove the arrow from its starting
@@ -2630,43 +3019,33 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2630 if (buf[0]) 3019 if (buf[0])
2631 return dupstr(buf); 3020 return dupstr(buf);
2632 else 3021 else
2633 return UI_UPDATE; 3022 return MOVE_UI_UPDATE;
2634 } else if (IS_CURSOR_MOVE(button)) { 3023 } else if (IS_CURSOR_MOVE(button)) {
2635 move_cursor(button, &ui->cur_x, &ui->cur_y, state->sx-1, state->sy-1, false); 3024 int cx = ui->cur_x - 1, cy = ui->cur_y - 1;
2636 if (ui->cur_x < 1) ui->cur_x = 1; 3025 char *ret = move_cursor(button, &cx, &cy, state->sx-2, state->sy-2,
2637 if (ui->cur_y < 1) ui->cur_y = 1; 3026 false, &ui->cur_visible);
2638 ui->cur_visible = true; 3027 ui->cur_x = cx + 1, ui->cur_y = cy + 1;
2639 if (ui->dragging) { 3028 if (ui->dragging) {
2640 ui->dx = SCOORD(ui->cur_x); 3029 ui->dx = SCOORD(ui->cur_x);
2641 ui->dy = SCOORD(ui->cur_y); 3030 ui->dy = SCOORD(ui->cur_y);
2642 } 3031 }
2643 return UI_UPDATE; 3032 return ret;
2644 } else if (IS_CURSOR_SELECT(button)) { 3033 } else if (IS_CURSOR_SELECT(button)) {
2645 if (!ui->cur_visible) { 3034 if (!ui->cur_visible) {
2646 ui->cur_visible = true; 3035 ui->cur_visible = true;
2647 return UI_UPDATE; 3036 return MOVE_UI_UPDATE;
2648 } 3037 }
2649 sp = &SPACE(state, ui->cur_x, ui->cur_y); 3038 sp = &SPACE(state, ui->cur_x, ui->cur_y);
2650 if (ui->dragging) { 3039 if (ui->dragging) {
2651 ui->dragging = false; 3040 px = ui->cur_x; py = ui->cur_y;
2652 3041 goto dropped;
2653 if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) &&
2654 SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) {
2655 sprintf(buf, "%sU%d,%d", sep, ui->srcx, ui->srcy);
2656 sep = ";";
2657 }
2658 if (sp->type == s_tile && !(sp->flags & F_DOT) && !(sp->flags & F_TILE_ASSOC)) {
2659 sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
2660 sep, ui->cur_x, ui->cur_y, ui->dotx, ui->doty);
2661 }
2662 return dupstr(buf);
2663 } else if (sp->flags & F_DOT) { 3042 } else if (sp->flags & F_DOT) {
2664 ui->dragging = true; 3043 ui->dragging = true;
2665 ui->dx = SCOORD(ui->cur_x); 3044 ui->dx = SCOORD(ui->cur_x);
2666 ui->dy = SCOORD(ui->cur_y); 3045 ui->dy = SCOORD(ui->cur_y);
2667 ui->dotx = ui->srcx = ui->cur_x; 3046 ui->dotx = ui->srcx = ui->cur_x;
2668 ui->doty = ui->srcy = ui->cur_y; 3047 ui->doty = ui->srcy = ui->cur_y;
2669 return UI_UPDATE; 3048 return MOVE_UI_UPDATE;
2670 } else if (sp->flags & F_TILE_ASSOC) { 3049 } else if (sp->flags & F_TILE_ASSOC) {
2671 assert(sp->type == s_tile); 3050 assert(sp->type == s_tile);
2672 ui->dragging = true; 3051 ui->dragging = true;
@@ -2676,18 +3055,19 @@ static char *interpret_move(const game_state *state, game_ui *ui,
2676 ui->doty = sp->doty; 3055 ui->doty = sp->doty;
2677 ui->srcx = ui->cur_x; 3056 ui->srcx = ui->cur_x;
2678 ui->srcy = ui->cur_y; 3057 ui->srcy = ui->cur_y;
2679 return UI_UPDATE; 3058 return MOVE_UI_UPDATE;
2680 } else if (sp->type == s_edge) { 3059 } else if (sp->type == s_edge &&
3060 edge_placement_legal(state, ui->cur_x, ui->cur_y)) {
2681 sprintf(buf, "E%d,%d", ui->cur_x, ui->cur_y); 3061 sprintf(buf, "E%d,%d", ui->cur_x, ui->cur_y);
2682 return dupstr(buf); 3062 return dupstr(buf);
2683 } 3063 }
2684 } 3064 }
2685 3065
2686 return NULL; 3066 return MOVE_UNUSED;
2687} 3067}
2688#endif 3068#endif
2689 3069
2690static bool check_complete(const game_state *state, int *dsf, int *colours) 3070static bool check_complete(const game_state *state, DSF *dsf, int *colours)
2691{ 3071{
2692 int w = state->w, h = state->h; 3072 int w = state->w, h = state->h;
2693 int x, y, i; 3073 int x, y, i;
@@ -2702,10 +3082,10 @@ static bool check_complete(const game_state *state, int *dsf, int *colours)
2702 } *sqdata; 3082 } *sqdata;
2703 3083
2704 if (!dsf) { 3084 if (!dsf) {
2705 dsf = snew_dsf(w*h); 3085 dsf = dsf_new(w*h);
2706 free_dsf = true; 3086 free_dsf = true;
2707 } else { 3087 } else {
2708 dsf_init(dsf, w*h); 3088 dsf_reinit(dsf);
2709 free_dsf = false; 3089 free_dsf = false;
2710 } 3090 }
2711 3091
@@ -2861,7 +3241,7 @@ static bool check_complete(const game_state *state, int *dsf, int *colours)
2861 3241
2862 sfree(sqdata); 3242 sfree(sqdata);
2863 if (free_dsf) 3243 if (free_dsf)
2864 sfree(dsf); 3244 dsf_free(dsf);
2865 3245
2866 return ret; 3246 return ret;
2867} 3247}
@@ -2959,6 +3339,35 @@ static game_state *execute_move(const game_state *state, const char *move)
2959 } else if (c == 'C') { 3339 } else if (c == 'C') {
2960 move++; 3340 move++;
2961 clear_game(ret, true); 3341 clear_game(ret, true);
3342 } else if (c == 'i') {
3343 int diff;
3344 move++;
3345 for (diff = 0; diff <= DIFF_UNREASONABLE; diff++)
3346 if (*move == galaxies_diffchars[diff])
3347 break;
3348 if (diff > DIFF_UNREASONABLE)
3349 goto badmove;
3350
3351 ret->cdiff = diff;
3352 move++;
3353 } else if (c == 'I') {
3354 int diff;
3355 move++;
3356 switch (*move) {
3357 case 'A':
3358 diff = DIFF_AMBIGUOUS;
3359 break;
3360 case 'I':
3361 diff = DIFF_IMPOSSIBLE;
3362 break;
3363 case 'U':
3364 diff = DIFF_UNFINISHED;
3365 break;
3366 default:
3367 goto badmove;
3368 }
3369 ret->cdiff = diff;
3370 move++;
2962#endif 3371#endif
2963 } else if (c == 'S') { 3372 } else if (c == 'S') {
2964 move++; 3373 move++;
@@ -2997,7 +3406,7 @@ badmove:
2997 */ 3406 */
2998 3407
2999static void game_compute_size(const game_params *params, int sz, 3408static void game_compute_size(const game_params *params, int sz,
3000 int *x, int *y) 3409 const game_ui *ui, int *x, int *y)
3001{ 3410{
3002 struct { int tilesize, w, h; } ads, *ds = &ads; 3411 struct { int tilesize, w, h; } ads, *ds = &ads;
3003 3412
@@ -3098,7 +3507,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
3098 ds->bl = NULL; 3507 ds->bl = NULL;
3099 ds->blmirror = NULL; 3508 ds->blmirror = NULL;
3100 ds->dragging = false; 3509 ds->dragging = false;
3101 ds->dragx = ds->dragy = 0; 3510 ds->dragx = ds->dragy = ds->oppx = ds->oppy = 0;
3102 3511
3103 ds->colour_scratch = snewn(ds->w * ds->h, int); 3512 ds->colour_scratch = snewn(ds->w * ds->h, int);
3104 3513
@@ -3145,7 +3554,10 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
3145static void draw_arrow(drawing *dr, game_drawstate *ds, 3554static void draw_arrow(drawing *dr, game_drawstate *ds,
3146 int cx, int cy, int ddx, int ddy, int col) 3555 int cx, int cy, int ddx, int ddy, int col)
3147{ 3556{
3148 float vlen = (float)sqrt(ddx*ddx+ddy*ddy); 3557 int sqdist = ddx*ddx+ddy*ddy;
3558 if (sqdist == 0)
3559 return; /* avoid division by zero */
3560 float vlen = (float)sqrt(sqdist);
3149 float xdx = ddx/vlen, xdy = ddy/vlen; 3561 float xdx = ddx/vlen, xdy = ddy/vlen;
3150 float ydx = -xdy, ydy = xdx; 3562 float ydx = -xdy, ydy = xdx;
3151 int e1x = cx + (int)(xdx*TILE_SIZE/3), e1y = cy + (int)(xdy*TILE_SIZE/3); 3563 int e1x = cx + (int)(xdx*TILE_SIZE/3), e1y = cy + (int)(xdy*TILE_SIZE/3);
@@ -3257,7 +3669,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3257 int w = ds->w, h = ds->h; 3669 int w = ds->w, h = ds->h;
3258 int x, y; 3670 int x, y;
3259 bool flashing = false; 3671 bool flashing = false;
3260 int oppx, oppy;
3261 3672
3262 if (flashtime > 0) { 3673 if (flashtime > 0) {
3263 int frame = (int)(flashtime / FLASH_TIME); 3674 int frame = (int)(flashtime / FLASH_TIME);
@@ -3267,14 +3678,10 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3267 if (ds->dragging) { 3678 if (ds->dragging) {
3268 assert(ds->bl); 3679 assert(ds->bl);
3269 assert(ds->blmirror); 3680 assert(ds->blmirror);
3270 calculate_opposite_point(ui, ds, ds->dragx + TILE_SIZE/2, 3681 blitter_load(dr, ds->blmirror, ds->oppx, ds->oppy);
3271 ds->dragy + TILE_SIZE/2, &oppx, &oppy); 3682 draw_update(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3272 oppx -= TILE_SIZE/2;
3273 oppy -= TILE_SIZE/2;
3274 blitter_load(dr, ds->bl, ds->dragx, ds->dragy); 3683 blitter_load(dr, ds->bl, ds->dragx, ds->dragy);
3275 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE); 3684 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3276 blitter_load(dr, ds->blmirror, oppx, oppy);
3277 draw_update(dr, oppx, oppy, TILE_SIZE, TILE_SIZE);
3278 ds->dragging = false; 3685 ds->dragging = false;
3279 } 3686 }
3280 if (ds->cur_visible) { 3687 if (ds->cur_visible) {
@@ -3285,7 +3692,6 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3285 } 3692 }
3286 3693
3287 if (!ds->started) { 3694 if (!ds->started) {
3288 draw_rect(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT, COL_BACKGROUND);
3289 draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1, 3695 draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1,
3290 w*TILE_SIZE + EDGE_THICKNESS*2 - 1, 3696 w*TILE_SIZE + EDGE_THICKNESS*2 - 1,
3291 h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE); 3697 h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE);
@@ -3433,16 +3839,28 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
3433 } 3839 }
3434 3840
3435 if (ui->dragging) { 3841 if (ui->dragging) {
3842 int oppx, oppy;
3843
3436 ds->dragging = true; 3844 ds->dragging = true;
3437 ds->dragx = ui->dx - TILE_SIZE/2; 3845 ds->dragx = ui->dx - TILE_SIZE/2;
3438 ds->dragy = ui->dy - TILE_SIZE/2; 3846 ds->dragy = ui->dy - TILE_SIZE/2;
3439 calculate_opposite_point(ui, ds, ui->dx, ui->dy, &oppx, &oppy); 3847 calculate_opposite_point(ui, ds, ui->dx, ui->dy, &oppx, &oppy);
3848 ds->oppx = oppx - TILE_SIZE/2;
3849 ds->oppy = oppy - TILE_SIZE/2;
3850
3440 blitter_save(dr, ds->bl, ds->dragx, ds->dragy); 3851 blitter_save(dr, ds->bl, ds->dragx, ds->dragy);
3441 blitter_save(dr, ds->blmirror, oppx - TILE_SIZE/2, oppy - TILE_SIZE/2); 3852 clip(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3442 draw_arrow(dr, ds, ui->dx, ui->dy, SCOORD(ui->dotx) - ui->dx, 3853 draw_arrow(dr, ds, ui->dx, ui->dy, SCOORD(ui->dotx) - ui->dx,
3443 SCOORD(ui->doty) - ui->dy, COL_ARROW); 3854 SCOORD(ui->doty) - ui->dy, COL_ARROW);
3855 unclip(dr);
3856 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3857
3858 blitter_save(dr, ds->blmirror, ds->oppx, ds->oppy);
3859 clip(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3444 draw_arrow(dr, ds, oppx, oppy, SCOORD(ui->dotx) - oppx, 3860 draw_arrow(dr, ds, oppx, oppy, SCOORD(ui->dotx) - oppx,
3445 SCOORD(ui->doty) - oppy, COL_ARROW); 3861 SCOORD(ui->doty) - oppy, COL_ARROW);
3862 unclip(dr);
3863 draw_update(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3446 } 3864 }
3447#ifdef EDITOR 3865#ifdef EDITOR
3448 { 3866 {
@@ -3508,13 +3926,9 @@ static int game_status(const game_state *state)
3508 return state->completed ? +1 : 0; 3926 return state->completed ? +1 : 0;
3509} 3927}
3510 3928
3511static bool game_timing_state(const game_state *state, game_ui *ui)
3512{
3513 return true;
3514}
3515
3516#ifndef EDITOR 3929#ifndef EDITOR
3517static void game_print_size(const game_params *params, float *x, float *y) 3930static void game_print_size(const game_params *params, const game_ui *ui,
3931 float *x, float *y)
3518{ 3932{
3519 int pw, ph; 3933 int pw, ph;
3520 3934
@@ -3522,17 +3936,19 @@ static void game_print_size(const game_params *params, float *x, float *y)
3522 * 8mm squares by default. (There isn't all that much detail 3936 * 8mm squares by default. (There isn't all that much detail
3523 * that needs to go in each square.) 3937 * that needs to go in each square.)
3524 */ 3938 */
3525 game_compute_size(params, 800, &pw, &ph); 3939 game_compute_size(params, 800, ui, &pw, &ph);
3526 *x = pw / 100.0F; 3940 *x = pw / 100.0F;
3527 *y = ph / 100.0F; 3941 *y = ph / 100.0F;
3528} 3942}
3529 3943
3530static void game_print(drawing *dr, const game_state *state, int sz) 3944static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3945 int sz)
3531{ 3946{
3532 int w = state->w, h = state->h; 3947 int w = state->w, h = state->h;
3533 int white, black, blackish; 3948 int white, black, blackish;
3534 int x, y, i, j; 3949 int x, y, i, j;
3535 int *colours, *dsf; 3950 int *colours;
3951 DSF *dsf;
3536 int *coords = NULL; 3952 int *coords = NULL;
3537 int ncoords = 0, coordsize = 0; 3953 int ncoords = 0, coordsize = 0;
3538 3954
@@ -3547,7 +3963,7 @@ static void game_print(drawing *dr, const game_state *state, int sz)
3547 /* 3963 /*
3548 * Get the completion information. 3964 * Get the completion information.
3549 */ 3965 */
3550 dsf = snewn(w * h, int); 3966 dsf = dsf_new(w * h);
3551 colours = snewn(w * h, int); 3967 colours = snewn(w * h, int);
3552 check_complete(state, dsf, colours); 3968 check_complete(state, dsf, colours);
3553 3969
@@ -3683,7 +4099,7 @@ static void game_print(drawing *dr, const game_state *state, int sz)
3683 black : white), black); 4099 black : white), black);
3684 } 4100 }
3685 4101
3686 sfree(dsf); 4102 dsf_free(dsf);
3687 sfree(colours); 4103 sfree(colours);
3688 sfree(coords); 4104 sfree(coords);
3689} 4105}
@@ -3714,12 +4130,18 @@ const struct game thegame = {
3714 true, solve_game, 4130 true, solve_game,
3715#endif 4131#endif
3716 true, game_can_format_as_text_now, game_text_format, 4132 true, game_can_format_as_text_now, game_text_format,
4133 NULL, NULL, /* get_prefs, set_prefs */
3717 new_ui, 4134 new_ui,
3718 free_ui, 4135 free_ui,
3719 encode_ui, 4136 NULL, /* encode_ui */
3720 decode_ui, 4137 NULL, /* decode_ui */
3721 NULL, /* game_request_keys */ 4138 NULL, /* game_request_keys */
3722 game_changed_state, 4139 game_changed_state,
4140#ifdef EDITOR
4141 NULL,
4142#else
4143 current_key_label,
4144#endif
3723 interpret_move, 4145 interpret_move,
3724 execute_move, 4146 execute_move,
3725 PREFERRED_TILE_SIZE, game_compute_size, game_set_size, 4147 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
@@ -3738,13 +4160,13 @@ const struct game thegame = {
3738 true, false, game_print_size, game_print, 4160 true, false, game_print_size, game_print,
3739 false, /* wants_statusbar */ 4161 false, /* wants_statusbar */
3740#endif 4162#endif
3741 false, game_timing_state, 4163 false, NULL, /* timing_state */
3742 REQUIRE_RBUTTON, /* flags */ 4164 REQUIRE_RBUTTON, /* flags */
3743}; 4165};
3744 4166
3745#ifdef STANDALONE_SOLVER 4167#ifdef STANDALONE_SOLVER
3746 4168
3747const char *quis; 4169static const char *quis;
3748 4170
3749#include <time.h> 4171#include <time.h>
3750 4172
@@ -3802,7 +4224,7 @@ static void soak(game_params *p, random_state *rs)
3802#endif 4224#endif
3803 tt_start = tt_now = time(NULL); 4225 tt_start = tt_now = time(NULL);
3804 for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0; 4226 for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0;
3805 maxtries = 1; 4227 one_try = true;
3806 4228
3807 printf("Soak-generating a %dx%d grid, max. diff %s.\n", 4229 printf("Soak-generating a %dx%d grid, max. diff %s.\n",
3808 p->w, p->h, galaxies_diffnames[p->diff]); 4230 p->w, p->h, galaxies_diffnames[p->diff]);
@@ -3812,7 +4234,9 @@ static void soak(game_params *p, random_state *rs)
3812 printf("]\n"); 4234 printf("]\n");
3813 4235
3814 while (1) { 4236 while (1) {
3815 desc = new_game_desc(p, rs, NULL, false); 4237 char *aux;
4238 desc = new_game_desc(p, rs, &aux, false);
4239 sfree(aux);
3816 st = new_game(NULL, p, desc); 4240 st = new_game(NULL, p, desc);
3817 diff = solver_state(st, p->diff); 4241 diff = solver_state(st, p->diff);
3818 nspaces += st->w*st->h; 4242 nspaces += st->w*st->h;
@@ -3866,8 +4290,6 @@ int main(int argc, char **argv)
3866 } 4290 }
3867 } 4291 }
3868 4292
3869 maxtries = 50;
3870
3871 p = default_params(); 4293 p = default_params();
3872 rs = random_new((void*)&seed, sizeof(time_t)); 4294 rs = random_new((void*)&seed, sizeof(time_t));
3873 4295