diff options
Diffstat (limited to 'apps/plugins/puzzles/src/galaxies.c')
-rw-r--r-- | apps/plugins/puzzles/src/galaxies.c | 734 |
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 |
52 | static bool solver_show_working; | 94 | static 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 | ||
151 | static bool check_complete(const game_state *state, int *dsf, int *colours); | 195 | static bool check_complete(const game_state *state, DSF *dsf, int *colours); |
196 | static int solver_state_inner(game_state *state, int maxdiff, int depth); | ||
152 | static int solver_state(game_state *state, int maxdiff); | 197 | static int solver_state(game_state *state, int maxdiff); |
153 | static int solver_obvious(game_state *state); | 198 | static int solver_obvious(game_state *state); |
154 | static int solver_obvious_dot(game_state *state, space *dot); | 199 | static int solver_obvious_dot(game_state *state, space *dot); |
155 | static space *space_opposite_dot(const game_state *state, const space *sp, | 200 | static space *space_opposite_dot(const game_state *state, const space *sp, |
156 | const space *dot); | 201 | const space *dot); |
157 | static space *tile_opposite(const game_state *state, const space *sp); | 202 | static space *tile_opposite(const game_state *state, const space *sp); |
203 | static 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 | ||
174 | static bool game_fetch_preset(int i, char **name, game_params **params) | 222 | static 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 | ||
375 | static bool ok_to_add_assoc_with_opposite( | 430 | static 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 | ||
382 | static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) { | 438 | static 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 | ||
396 | static space *sp2dot(const game_state *state, int x, int y) | 452 | static 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 | ||
467 | static char *encode_game(const game_state *state); | ||
468 | |||
410 | static char *game_text_format(const game_state *state) | 469 | static 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 | ||
467 | static void dbg_state(const game_state *state) | 541 | static 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. */ |
686 | static char *diff_game(const game_state *src, const game_state *dest, | 760 | static 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). */ |
750 | static bool dot_is_possible(game_state *state, space *sp, bool allow_assoc) | 844 | static 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 | ||
924 | static char *encode_game(game_state *state) | 1019 | static 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 |
1232 | int maxtries; | 1327 | static 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 | |||
1409 | static 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 | |||
1425 | static 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 | |||
1300 | static char *new_game_desc(const game_params *params, random_state *rs, | 1445 | static 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 | ||
1313 | generate: | 1456 | generate: |
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 | ||
1626 | static int solver_recurse_depth; | 1794 | static int solver_recurse_depth; |
1795 | #define STATIC_RECURSION_DEPTH | ||
1796 | #endif | ||
1627 | 1797 | ||
1628 | typedef struct solver_ctx { | 1798 | typedef 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 | ||
1635 | static solver_ctx *new_solver(game_state *state) | 1806 | static 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 | ||
1644 | static void free_solver(solver_ctx *sctx) | 1817 | static 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 | ||
2275 | static 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 | |||
2100 | struct recurse_ctx { | 2446 | struct 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 | ||
2127 | static int solver_recurse(game_state *state, int maxdiff) | 2473 | static 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 | ||
2214 | static int solver_state(game_state *state, int maxdiff) | 2556 | static 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 | ||
2264 | got_result: | 2613 | got_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 | ||
2627 | static 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 |
2279 | static char *solve_game(const game_state *state, const game_state *currstate, | 2633 | static 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 | ||
2345 | static char *encode_ui(const game_ui *ui) | ||
2346 | { | ||
2347 | return NULL; | ||
2348 | } | ||
2349 | |||
2350 | static void decode_ui(game_ui *ui, const char *encoding) | ||
2351 | { | ||
2352 | } | ||
2353 | |||
2354 | static void game_changed_state(game_ui *ui, const game_state *oldstate, | 2704 | static 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 |
2824 | static 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 | |||
2839 | static 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 | |||
2476 | static char *interpret_move(const game_state *state, game_ui *ui, | 2866 | static 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 | ||
2690 | static bool check_complete(const game_state *state, int *dsf, int *colours) | 3070 | static 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 | ||
2999 | static void game_compute_size(const game_params *params, int sz, | 3408 | static 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) | |||
3145 | static void draw_arrow(drawing *dr, game_drawstate *ds, | 3554 | static 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 | ||
3511 | static bool game_timing_state(const game_state *state, game_ui *ui) | ||
3512 | { | ||
3513 | return true; | ||
3514 | } | ||
3515 | |||
3516 | #ifndef EDITOR | 3929 | #ifndef EDITOR |
3517 | static void game_print_size(const game_params *params, float *x, float *y) | 3930 | static 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 | ||
3530 | static void game_print(drawing *dr, const game_state *state, int sz) | 3944 | static 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 | ||
3747 | const char *quis; | 4169 | static 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 | ||