A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/*
2 * galaxies.c: implementation of 'Tentai Show' from Nikoli,
3 * also sometimes called 'Spiral Galaxies'.
4 *
5 * Notes:
6 *
7 * Grid is stored as size (2n-1), holding edges as well as spaces
8 * (and thus vertices too, at edge intersections).
9 *
10 * Any dot will thus be positioned at one of our grid points,
11 * which saves any faffing with half-of-a-square stuff.
12 *
13 * Edges have on/off state; obviously the actual edges of the
14 * board are fixed to on, and everything else starts as off.
15 *
16 * Future solver directions:
17 *
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.
56 *
57 * Bugs:
58 *
59 * Notable puzzle IDs:
60 *
61 * Nikoli's example [web site has wrong highlighting]
62 * (at http://www.nikoli.co.jp/en/puzzles/astronomical_show/):
63 * 5x5:eBbbMlaBbOEnf
64 *
65 * The 'spiral galaxies puzzles are NP-complete' paper
66 * (at http://www.stetson.edu/~efriedma/papers/spiral.pdf):
67 * 7x7:chpgdqqqoezdddki
68 *
69 * Puzzle competition pdf examples
70 * (at http://www.puzzleratings.org/Yurekli2006puz.pdf):
71 * 6x6:EDbaMucCohbrecEi
72 * 10x10:beFbufEEzowDlxldibMHezBQzCdcFzjlci
73 * 13x13:dCemIHFFkJajjgDfdbdBzdzEgjccoPOcztHjBczLDjczqktJjmpreivvNcggFi
74 *
75 */
76
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80#include <assert.h>
81#include <ctype.h>
82#include <limits.h>
83#ifdef NO_TGMATH_H
84# include <math.h>
85#else
86# include <tgmath.h>
87#endif
88
89#include "puzzles.h"
90
91#ifdef DEBUGGING
92#define solvep debug
93#elif defined STANDALONE_SOLVER
94static bool solver_show_working;
95#define solvep(x) do { if (solver_show_working) { printf x; } } while(0)
96#else
97#define solvep(x) ((void)0)
98#endif
99
100#ifdef STANDALONE_PICTURE_GENERATOR
101/*
102 * Dirty hack to enable the generator to construct a game ID which
103 * solves to a specified black-and-white bitmap. We define a global
104 * variable here which gives the desired colour of each square, and
105 * we arrange that the grid generator never merges squares of
106 * different colours.
107 *
108 * The bitmap as stored here is a simple int array (at these sizes
109 * it isn't worth doing fiddly bit-packing). picture[y*w+x] is 1
110 * iff the pixel at (x,y) is intended to be black.
111 *
112 * (It might be nice to be able to specify some pixels as
113 * don't-care, to give the generator more leeway. But that might be
114 * fiddly.)
115 */
116static int *picture;
117#endif
118
119enum {
120 COL_BACKGROUND,
121 COL_WHITEBG,
122 COL_BLACKBG,
123 COL_WHITEDOT,
124 COL_BLACKDOT,
125 COL_GRID,
126 COL_EDGE,
127 COL_ARROW,
128 COL_CURSOR,
129 NCOLOURS
130};
131
132#define DIFFLIST(A) \
133 A(NORMAL,Normal,n) \
134 A(UNREASONABLE,Unreasonable,u)
135
136#define ENUM(upper,title,lower) DIFF_ ## upper,
137#define TITLE(upper,title,lower) #title,
138#define ENCODE(upper,title,lower) #lower
139#define CONFIG(upper,title,lower) ":" #title
140enum { DIFFLIST(ENUM)
141 DIFF_IMPOSSIBLE, DIFF_AMBIGUOUS, DIFF_UNFINISHED, DIFF_MAX };
142static char const *const galaxies_diffnames[] = {
143 DIFFLIST(TITLE) "Impossible", "Ambiguous", "Unfinished" };
144static char const galaxies_diffchars[] = DIFFLIST(ENCODE);
145#define DIFFCONFIG DIFFLIST(CONFIG)
146
147struct game_params {
148 /* X and Y is the area of the board as seen by
149 * the user, not the (2n+1) area the game uses. */
150 int w, h, diff;
151};
152
153enum { s_tile, s_edge, s_vertex };
154
155#define F_DOT 1 /* there's a dot here */
156#define F_EDGE_SET 2 /* the edge is set */
157#define F_TILE_ASSOC 4 /* this tile is associated with a dot. */
158#define F_DOT_BLACK 8 /* (ui only) dot is black. */
159#define F_MARK 16 /* scratch flag */
160#define F_REACHABLE 32
161#define F_SCRATCH 64
162#define F_MULTIPLE 128
163#define F_DOT_HOLD 256
164#define F_GOOD 512
165
166typedef struct space {
167 int x, y; /* its position */
168 int type;
169 unsigned int flags;
170 int dotx, doty; /* if flags & F_TILE_ASSOC */
171 int nassoc; /* if flags & F_DOT */
172} space;
173
174#define INGRID(s,x,y) ((x) >= 0 && (y) >= 0 && \
175 (x) < (state)->sx && (y) < (state)->sy)
176#define INUI(s,x,y) ((x) > 0 && (y) > 0 && \
177 (x) < ((state)->sx-1) && (y) < ((state)->sy-1))
178
179#define GRID(s,g,x,y) ((s)->g[((y)*(s)->sx)+(x)])
180#define SPACE(s,x,y) GRID(s,grid,x,y)
181
182struct game_state {
183 int w, h; /* size from params */
184 int sx, sy; /* allocated size, (2x-1)*(2y-1) */
185 space *grid;
186 bool completed, used_solve;
187 int ndots;
188 space **dots;
189
190 midend *me; /* to call supersede_game_desc */
191 int cdiff; /* difficulty of current puzzle (for status bar),
192 or -1 if stale. */
193};
194
195static bool check_complete(const game_state *state, DSF *dsf, int *colours);
196static int solver_state_inner(game_state *state, int maxdiff, int depth);
197static int solver_state(game_state *state, int maxdiff);
198static int solver_obvious(game_state *state);
199static int solver_obvious_dot(game_state *state, space *dot);
200static space *space_opposite_dot(const game_state *state, const space *sp,
201 const space *dot);
202static space *tile_opposite(const game_state *state, const space *sp);
203static game_state *execute_move(const game_state *state, const char *move);
204
205/* ----------------------------------------------------------
206 * Game parameters and presets
207 */
208
209/* make up some sensible default sizes */
210
211#define DEFAULT_PRESET 0
212
213static const game_params galaxies_presets[] = {
214 { 7, 7, DIFF_NORMAL },
215 { 7, 7, DIFF_UNREASONABLE },
216 { 10, 10, DIFF_NORMAL },
217 { 10, 10, DIFF_UNREASONABLE },
218 { 15, 15, DIFF_NORMAL },
219 { 15, 15, DIFF_UNREASONABLE },
220};
221
222static bool game_fetch_preset(int i, char **name, game_params **params)
223{
224 game_params *ret;
225 char buf[80];
226
227 if (i < 0 || i >= lenof(galaxies_presets))
228 return false;
229
230 ret = snew(game_params);
231 *ret = galaxies_presets[i]; /* structure copy */
232
233 sprintf(buf, "%dx%d %s", ret->w, ret->h,
234 galaxies_diffnames[ret->diff]);
235
236 if (name) *name = dupstr(buf);
237 *params = ret;
238 return true;
239}
240
241static game_params *default_params(void)
242{
243 game_params *ret;
244 game_fetch_preset(DEFAULT_PRESET, NULL, &ret);
245 return ret;
246}
247
248static void free_params(game_params *params)
249{
250 sfree(params);
251}
252
253static game_params *dup_params(const game_params *params)
254{
255 game_params *ret = snew(game_params);
256 *ret = *params; /* structure copy */
257 return ret;
258}
259
260static void decode_params(game_params *params, char const *string)
261{
262 params->h = params->w = atoi(string);
263 params->diff = DIFF_NORMAL;
264 while (*string && isdigit((unsigned char)*string)) string++;
265 if (*string == 'x') {
266 string++;
267 params->h = atoi(string);
268 while (*string && isdigit((unsigned char)*string)) string++;
269 }
270 if (*string == 'd') {
271 int i;
272 string++;
273 for (i = 0; i <= DIFF_UNREASONABLE; i++)
274 if (*string == galaxies_diffchars[i])
275 params->diff = i;
276 if (*string) string++;
277 }
278}
279
280static char *encode_params(const game_params *params, bool full)
281{
282 char str[80];
283 sprintf(str, "%dx%d", params->w, params->h);
284 if (full)
285 sprintf(str + strlen(str), "d%c", galaxies_diffchars[params->diff]);
286 return dupstr(str);
287}
288
289static config_item *game_configure(const game_params *params)
290{
291 config_item *ret;
292 char buf[80];
293
294 ret = snewn(4, config_item);
295
296 ret[0].name = "Width";
297 ret[0].type = C_STRING;
298 sprintf(buf, "%d", params->w);
299 ret[0].u.string.sval = dupstr(buf);
300
301 ret[1].name = "Height";
302 ret[1].type = C_STRING;
303 sprintf(buf, "%d", params->h);
304 ret[1].u.string.sval = dupstr(buf);
305
306 ret[2].name = "Difficulty";
307 ret[2].type = C_CHOICES;
308 ret[2].u.choices.choicenames = DIFFCONFIG;
309 ret[2].u.choices.selected = params->diff;
310
311 ret[3].name = NULL;
312 ret[3].type = C_END;
313
314 return ret;
315}
316
317static game_params *custom_params(const config_item *cfg)
318{
319 game_params *ret = snew(game_params);
320
321 ret->w = atoi(cfg[0].u.string.sval);
322 ret->h = atoi(cfg[1].u.string.sval);
323 ret->diff = cfg[2].u.choices.selected;
324
325 return ret;
326}
327
328static const char *validate_params(const game_params *params, bool full)
329{
330 if (params->w < 3 || params->h < 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
336 /*
337 * This shouldn't be able to happen at all, since decode_params
338 * and custom_params will never generate anything that isn't
339 * within range.
340 */
341 assert(params->diff <= DIFF_UNREASONABLE);
342
343 return NULL;
344}
345
346/* ----------------------------------------------------------
347 * Game utility functions.
348 */
349
350static void add_dot(space *space) {
351 assert(!(space->flags & F_DOT));
352 space->flags |= F_DOT;
353 space->nassoc = 0;
354}
355
356static void remove_dot(space *space) {
357 assert(space->flags & F_DOT);
358 space->flags &= ~F_DOT;
359}
360
361static void remove_assoc(const game_state *state, space *tile) {
362 if (tile->flags & F_TILE_ASSOC) {
363 SPACE(state, tile->dotx, tile->doty).nassoc--;
364 tile->flags &= ~F_TILE_ASSOC;
365 tile->dotx = -1;
366 tile->doty = -1;
367 }
368}
369
370static void remove_assoc_with_opposite(game_state *state, space *tile) {
371 space *opposite;
372
373 if (!(tile->flags & F_TILE_ASSOC)) {
374 return;
375 }
376
377 opposite = tile_opposite(state, tile);
378 remove_assoc(state, tile);
379
380 if (opposite != NULL && opposite != tile) {
381 remove_assoc(state, opposite);
382 }
383}
384
385static void add_assoc(const game_state *state, space *tile, space *dot) {
386 remove_assoc(state, tile);
387
388#ifdef STANDALONE_PICTURE_GENERATOR
389 if (picture)
390 assert(!picture[(tile->y/2) * state->w + (tile->x/2)] ==
391 !(dot->flags & F_DOT_BLACK));
392#endif
393 tile->flags |= F_TILE_ASSOC;
394 tile->dotx = dot->x;
395 tile->doty = dot->y;
396 dot->nassoc++;
397 /*debug(("add_assoc sp %d %d --> dot %d,%d, new nassoc %d.\n",
398 tile->x, tile->y, dot->x, dot->y, dot->nassoc));*/
399}
400
401static bool ok_to_add_assoc_with_opposite_internal(
402 const game_state *state, space *tile, space *opposite)
403{
404 int *colors;
405 bool toret;
406
407 if (tile->type != s_tile)
408 return false;
409 if (tile->flags & F_DOT)
410 return false;
411 if (opposite == NULL)
412 return false;
413 if (opposite->flags & F_DOT)
414 return false;
415
416 toret = true;
417 colors = snewn(state->w * state->h, int);
418 check_complete(state, NULL, colors);
419
420 if (colors[(tile->y - 1)/2 * state->w + (tile->x - 1)/2])
421 toret = false;
422 if (colors[(opposite->y - 1)/2 * state->w + (opposite->x - 1)/2])
423 toret = false;
424
425 sfree(colors);
426 return toret;
427}
428
429#ifndef EDITOR
430static bool ok_to_add_assoc_with_opposite(
431 const game_state *state, space *tile, space *dot)
432{
433 space *opposite = space_opposite_dot(state, tile, dot);
434 return ok_to_add_assoc_with_opposite_internal(state, tile, opposite);
435}
436#endif
437
438static void add_assoc_with_opposite(game_state *state, space *tile, space *dot) {
439 space *opposite = space_opposite_dot(state, tile, dot);
440
441 if(opposite && ok_to_add_assoc_with_opposite_internal(
442 state, tile, opposite))
443 {
444 remove_assoc_with_opposite(state, tile);
445 add_assoc(state, tile, dot);
446 remove_assoc_with_opposite(state, opposite);
447 add_assoc(state, opposite, dot);
448 }
449}
450
451#ifndef EDITOR
452static space *sp2dot(const game_state *state, int x, int y)
453{
454 space *sp = &SPACE(state, x, y);
455 if (!(sp->flags & F_TILE_ASSOC)) return NULL;
456 return &SPACE(state, sp->dotx, sp->doty);
457}
458#endif
459
460#define IS_VERTICAL_EDGE(x) ((x % 2) == 0)
461
462static bool game_can_format_as_text_now(const game_params *params)
463{
464 return true;
465}
466
467static char *encode_game(const game_state *state);
468
469static char *game_text_format(const game_state *state)
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
485 int maxlen = (state->sx+1)*state->sy, x, y;
486 char *ret, *p;
487 space *sp;
488
489 ret = snewn(maxlen+1, char);
490 p = ret;
491
492 for (y = 0; y < state->sy; y++) {
493 for (x = 0; x < state->sx; x++) {
494 sp = &SPACE(state, x, y);
495 if (sp->flags & F_DOT)
496 *p++ = 'o';
497#if 0
498 else if (sp->flags & (F_REACHABLE|F_MULTIPLE|F_MARK))
499 *p++ = (sp->flags & F_MULTIPLE) ? 'M' :
500 (sp->flags & F_REACHABLE) ? 'R' : 'X';
501#endif
502 else {
503 switch (sp->type) {
504 case s_tile:
505 if (sp->flags & F_TILE_ASSOC) {
506 space *dot = sp2dot(state, sp->x, sp->y);
507 if (dot && dot->flags & F_DOT)
508 *p++ = (dot->flags & F_DOT_BLACK) ? 'B' : 'W';
509 else
510 *p++ = '?'; /* association with not-a-dot. */
511 } else
512 *p++ = ' ';
513 break;
514
515 case s_vertex:
516 *p++ = '+';
517 break;
518
519 case s_edge:
520 if (sp->flags & F_EDGE_SET)
521 *p++ = (IS_VERTICAL_EDGE(x)) ? '|' : '-';
522 else
523 *p++ = ' ';
524 break;
525
526 default:
527 assert(!"shouldn't get here!");
528 }
529 }
530 }
531 *p++ = '\n';
532 }
533
534 assert(p - ret == maxlen);
535 *p = '\0';
536
537 return ret;
538#endif
539}
540
541static void dbg_state(const game_state *state)
542{
543#ifdef DEBUGGING
544 char *temp = game_text_format(state);
545 debug(("%s\n", temp));
546 sfree(temp);
547#endif
548}
549
550/* Space-enumeration callbacks should all return 1 for 'progress made',
551 * -1 for 'impossible', and 0 otherwise. */
552typedef int (*space_cb)(game_state *state, space *sp, void *ctx);
553
554#define IMPOSSIBLE_QUITS 1
555
556static int foreach_sub(game_state *state, space_cb cb, unsigned int f,
557 void *ctx, int startx, int starty)
558{
559 int x, y, ret;
560 bool progress = false, impossible = false;
561 space *sp;
562
563 for (y = starty; y < state->sy; y += 2) {
564 sp = &SPACE(state, startx, y);
565 for (x = startx; x < state->sx; x += 2) {
566 ret = cb(state, sp, ctx);
567 if (ret == -1) {
568 if (f & IMPOSSIBLE_QUITS) return -1;
569 impossible = true;
570 } else if (ret == 1) {
571 progress = true;
572 }
573 sp += 2;
574 }
575 }
576 return impossible ? -1 : progress ? 1 : 0;
577}
578
579static int foreach_tile(game_state *state, space_cb cb, unsigned int f,
580 void *ctx)
581{
582 return foreach_sub(state, cb, f, ctx, 1, 1);
583}
584
585static int foreach_edge(game_state *state, space_cb cb, unsigned int f,
586 void *ctx)
587{
588 int ret1, ret2;
589
590 ret1 = foreach_sub(state, cb, f, ctx, 0, 1);
591 ret2 = foreach_sub(state, cb, f, ctx, 1, 0);
592
593 if (ret1 == -1 || ret2 == -1) return -1;
594 return (ret1 || ret2) ? 1 : 0;
595}
596
597#if 0
598static int foreach_vertex(game_state *state, space_cb cb, unsigned int f,
599 void *ctx)
600{
601 return foreach_sub(state, cb, f, ctx, 0, 0);
602}
603#endif
604
605#if 0
606static int is_same_assoc(game_state *state,
607 int x1, int y1, int x2, int y2)
608{
609 space *s1, *s2;
610
611 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2))
612 return 0;
613
614 s1 = &SPACE(state, x1, y1);
615 s2 = &SPACE(state, x2, y2);
616 assert(s1->type == s_tile && s2->type == s_tile);
617 if ((s1->flags & F_TILE_ASSOC) && (s2->flags & F_TILE_ASSOC) &&
618 s1->dotx == s2->dotx && s1->doty == s2->doty)
619 return 1;
620 return 0; /* 0 if not same or not both associated. */
621}
622#endif
623
624#if 0
625static int edges_into_vertex(game_state *state,
626 int x, int y)
627{
628 int dx, dy, nx, ny, count = 0;
629
630 assert(SPACE(state, x, y).type == s_vertex);
631 for (dx = -1; dx <= 1; dx++) {
632 for (dy = -1; dy <= 1; dy++) {
633 if (dx != 0 && dy != 0) continue;
634 if (dx == 0 && dy == 0) continue;
635
636 nx = x+dx; ny = y+dy;
637 if (!INGRID(state, nx, ny)) continue;
638 assert(SPACE(state, nx, ny).type == s_edge);
639 if (SPACE(state, nx, ny).flags & F_EDGE_SET)
640 count++;
641 }
642 }
643 return count;
644}
645#endif
646
647static space *space_opposite_dot(const game_state *state, const space *sp,
648 const space *dot)
649{
650 int dx, dy, tx, ty;
651 space *sp2;
652
653 dx = sp->x - dot->x;
654 dy = sp->y - dot->y;
655 tx = dot->x - dx;
656 ty = dot->y - dy;
657 if (!INGRID(state, tx, ty)) return NULL;
658
659 sp2 = &SPACE(state, tx, ty);
660 assert(sp2->type == sp->type);
661 return sp2;
662}
663
664static space *tile_opposite(const game_state *state, const space *sp)
665{
666 space *dot;
667
668 assert(sp->flags & F_TILE_ASSOC);
669 dot = &SPACE(state, sp->dotx, sp->doty);
670 return space_opposite_dot(state, sp, dot);
671}
672
673static bool dotfortile(game_state *state, space *tile, space *dot)
674{
675 space *tile_opp = space_opposite_dot(state, tile, dot);
676
677 if (!tile_opp) return false; /* opposite would be off grid */
678 if (tile_opp->flags & F_TILE_ASSOC &&
679 (tile_opp->dotx != dot->x || tile_opp->doty != dot->y))
680 return false; /* opposite already associated with diff. dot */
681 return true;
682}
683
684static void adjacencies(game_state *state, space *sp, space **a1s, space **a2s)
685{
686 int dxs[4] = {-1, 1, 0, 0}, dys[4] = {0, 0, -1, 1};
687 int n, x, y;
688
689 /* this function needs optimising. */
690
691 for (n = 0; n < 4; n++) {
692 x = sp->x+dxs[n];
693 y = sp->y+dys[n];
694
695 if (INGRID(state, x, y)) {
696 a1s[n] = &SPACE(state, x, y);
697
698 x += dxs[n]; y += dys[n];
699
700 if (INGRID(state, x, y))
701 a2s[n] = &SPACE(state, x, y);
702 else
703 a2s[n] = NULL;
704 } else {
705 a1s[n] = a2s[n] = NULL;
706 }
707 }
708}
709
710static bool outline_tile_fordot(game_state *state, space *tile, bool mark)
711{
712 space *tadj[4], *eadj[4];
713 int i;
714 bool didsth = false, edge, same;
715
716 assert(tile->type == s_tile);
717 adjacencies(state, tile, eadj, tadj);
718 for (i = 0; i < 4; i++) {
719 if (!eadj[i]) continue;
720
721 edge = eadj[i]->flags & F_EDGE_SET;
722 if (tadj[i]) {
723 if (!(tile->flags & F_TILE_ASSOC))
724 same = !(tadj[i]->flags & F_TILE_ASSOC);
725 else
726 same = ((tadj[i]->flags & F_TILE_ASSOC) &&
727 tile->dotx == tadj[i]->dotx &&
728 tile->doty == tadj[i]->doty);
729 } else
730 same = false;
731
732 if (!edge && !same) {
733 if (mark) eadj[i]->flags |= F_EDGE_SET;
734 didsth = true;
735 } else if (edge && same) {
736 if (mark) eadj[i]->flags &= ~F_EDGE_SET;
737 didsth = true;
738 }
739 }
740 return didsth;
741}
742
743static void tiles_from_edge(game_state *state, space *sp, space **ts)
744{
745 int xs[2], ys[2];
746
747 if (IS_VERTICAL_EDGE(sp->x)) {
748 xs[0] = sp->x-1; ys[0] = sp->y;
749 xs[1] = sp->x+1; ys[1] = sp->y;
750 } else {
751 xs[0] = sp->x; ys[0] = sp->y-1;
752 xs[1] = sp->x; ys[1] = sp->y+1;
753 }
754 ts[0] = INGRID(state, xs[0], ys[0]) ? &SPACE(state, xs[0], ys[0]) : NULL;
755 ts[1] = INGRID(state, xs[1], ys[1]) ? &SPACE(state, xs[1], ys[1]) : NULL;
756}
757
758/* Returns a move string for use by 'solve', including the initial
759 * 'S' if issolve is true. */
760static char *diff_game(const game_state *src, const game_state *dest,
761 bool issolve, int set_cdiff)
762{
763 int movelen = 0, movesize = 256, x, y, len;
764 char *move = snewn(movesize, char), buf[80];
765 const char *sep = "";
766 char achar = issolve ? 'a' : 'A';
767 space *sps, *spd;
768
769 assert(src->sx == dest->sx && src->sy == dest->sy);
770
771 if (issolve) {
772 move[movelen++] = 'S';
773 sep = ";";
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
795 move[movelen] = '\0';
796 for (x = 0; x < src->sx; x++) {
797 for (y = 0; y < src->sy; y++) {
798 sps = &SPACE(src, x, y);
799 spd = &SPACE(dest, x, y);
800
801 assert(sps->type == spd->type);
802
803 len = 0;
804 if (sps->type == s_tile) {
805 if ((sps->flags & F_TILE_ASSOC) &&
806 (spd->flags & F_TILE_ASSOC)) {
807 if (sps->dotx != spd->dotx ||
808 sps->doty != spd->doty)
809 /* Both associated; change association, if different */
810 len = sprintf(buf, "%s%c%d,%d,%d,%d", sep,
811 (int)achar, x, y, spd->dotx, spd->doty);
812 } else if (sps->flags & F_TILE_ASSOC)
813 /* Only src associated; remove. */
814 len = sprintf(buf, "%sU%d,%d", sep, x, y);
815 else if (spd->flags & F_TILE_ASSOC)
816 /* Only dest associated; add. */
817 len = sprintf(buf, "%s%c%d,%d,%d,%d", sep,
818 (int)achar, x, y, spd->dotx, spd->doty);
819 } else if (sps->type == s_edge) {
820 if ((sps->flags & F_EDGE_SET) != (spd->flags & F_EDGE_SET))
821 /* edge flags are different; flip them. */
822 len = sprintf(buf, "%sE%d,%d", sep, x, y);
823 }
824 if (len) {
825 if (movelen + len >= movesize) {
826 movesize = movelen + len + 256;
827 move = sresize(move, movesize, char);
828 }
829 strcpy(move + movelen, buf);
830 movelen += len;
831 sep = ";";
832 }
833 }
834 }
835 debug(("diff_game src then dest:\n"));
836 dbg_state(src);
837 dbg_state(dest);
838 debug(("diff string %s\n", move));
839 return move;
840}
841
842/* Returns true if a dot here would not be too close to any other dots
843 * (and would avoid other game furniture). */
844static bool dot_is_possible(const game_state *state, space *sp,
845 bool allow_assoc)
846{
847 int bx = 0, by = 0, dx, dy;
848 space *adj;
849#ifdef STANDALONE_PICTURE_GENERATOR
850 int col = -1;
851#endif
852
853 switch (sp->type) {
854 case s_tile:
855 bx = by = 1; break;
856 case s_edge:
857 if (IS_VERTICAL_EDGE(sp->x)) {
858 bx = 2; by = 1;
859 } else {
860 bx = 1; by = 2;
861 }
862 break;
863 case s_vertex:
864 bx = by = 2; break;
865 }
866
867 for (dx = -bx; dx <= bx; dx++) {
868 for (dy = -by; dy <= by; dy++) {
869 if (!INGRID(state, sp->x+dx, sp->y+dy)) continue;
870
871 adj = &SPACE(state, sp->x+dx, sp->y+dy);
872
873#ifdef STANDALONE_PICTURE_GENERATOR
874 /*
875 * Check that all the squares we're looking at have the
876 * same colour.
877 */
878 if (picture) {
879 if (adj->type == s_tile) {
880 int c = picture[(adj->y / 2) * state->w + (adj->x / 2)];
881 if (col < 0)
882 col = c;
883 if (c != col)
884 return false; /* colour mismatch */
885 }
886 }
887#endif
888
889 if (!allow_assoc && (adj->flags & F_TILE_ASSOC))
890 return false;
891
892 if (dx != 0 || dy != 0) {
893 /* Other than our own square, no dots nearby. */
894 if (adj->flags & (F_DOT))
895 return false;
896 }
897
898 /* We don't want edges within our rectangle
899 * (but don't care about edges on the edge) */
900 if (abs(dx) < bx && abs(dy) < by &&
901 adj->flags & F_EDGE_SET)
902 return false;
903 }
904 }
905 return true;
906}
907
908/* ----------------------------------------------------------
909 * Game generation, structure creation, and descriptions.
910 */
911
912static game_state *blank_game(int w, int h)
913{
914 game_state *state = snew(game_state);
915 int x, y;
916
917 state->w = w;
918 state->h = h;
919
920 state->sx = (w*2)+1;
921 state->sy = (h*2)+1;
922 state->grid = snewn(state->sx * state->sy, space);
923 state->completed = false;
924 state->used_solve = false;
925
926 for (x = 0; x < state->sx; x++) {
927 for (y = 0; y < state->sy; y++) {
928 space *sp = &SPACE(state, x, y);
929 memset(sp, 0, sizeof(space));
930 sp->x = x;
931 sp->y = y;
932 if ((x % 2) == 0 && (y % 2) == 0)
933 sp->type = s_vertex;
934 else if ((x % 2) == 0 || (y % 2) == 0) {
935 sp->type = s_edge;
936 if (x == 0 || y == 0 || x == state->sx-1 || y == state->sy-1)
937 sp->flags |= F_EDGE_SET;
938 } else
939 sp->type = s_tile;
940 }
941 }
942
943 state->ndots = 0;
944 state->dots = NULL;
945
946 state->me = NULL; /* filled in by new_game. */
947 state->cdiff = -1;
948
949 return state;
950}
951
952static void game_update_dots(game_state *state)
953{
954 int i, n, sz = state->sx * state->sy;
955
956 if (state->dots) sfree(state->dots);
957 state->ndots = 0;
958
959 for (i = 0; i < sz; i++) {
960 if (state->grid[i].flags & F_DOT) state->ndots++;
961 }
962 state->dots = snewn(state->ndots, space *);
963 n = 0;
964 for (i = 0; i < sz; i++) {
965 if (state->grid[i].flags & F_DOT)
966 state->dots[n++] = &state->grid[i];
967 }
968}
969
970static void clear_game(game_state *state, bool cleardots)
971{
972 int x, y;
973
974 /* don't erase edge flags around outline! */
975 for (x = 1; x < state->sx-1; x++) {
976 for (y = 1; y < state->sy-1; y++) {
977 if (cleardots)
978 SPACE(state, x, y).flags = 0;
979 else
980 SPACE(state, x, y).flags &= (F_DOT|F_DOT_BLACK);
981 }
982 }
983 if (cleardots) game_update_dots(state);
984}
985
986static game_state *dup_game(const game_state *state)
987{
988 game_state *ret = blank_game(state->w, state->h);
989
990 ret->completed = state->completed;
991 ret->used_solve = state->used_solve;
992
993 memcpy(ret->grid, state->grid,
994 ret->sx*ret->sy*sizeof(space));
995
996 game_update_dots(ret);
997
998 ret->me = state->me;
999 ret->cdiff = state->cdiff;
1000
1001 return ret;
1002}
1003
1004static void free_game(game_state *state)
1005{
1006 if (state->dots) sfree(state->dots);
1007 sfree(state->grid);
1008 sfree(state);
1009}
1010
1011/* Game description is a sequence of letters representing the number
1012 * of spaces (a = 0, y = 24) before the next dot; a-y for a white dot,
1013 * and A-Y for a black dot. 'z' is 25 spaces (and no dot).
1014 *
1015 * I know it's a bitch to generate by hand, so we provide
1016 * an edit mode.
1017 */
1018
1019static char *encode_game(const game_state *state)
1020{
1021 char *desc, *p;
1022 int run, x, y, area;
1023 unsigned int f;
1024
1025 area = (state->sx-2) * (state->sy-2);
1026
1027 desc = snewn(area, char);
1028 p = desc;
1029 run = 0;
1030 for (y = 1; y < state->sy-1; y++) {
1031 for (x = 1; x < state->sx-1; x++) {
1032 f = SPACE(state, x, y).flags;
1033
1034 /* a/A is 0 spaces between, b/B is 1 space, ...
1035 * y/Y is 24 spaces, za/zA is 25 spaces, ...
1036 * It's easier to count from 0 because we then
1037 * don't have to special-case the top left-hand corner
1038 * (which could be a dot with 0 spaces before it). */
1039 if (!(f & F_DOT))
1040 run++;
1041 else {
1042 while (run > 24) {
1043 *p++ = 'z';
1044 run -= 25;
1045 }
1046 *p++ = ((f & F_DOT_BLACK) ? 'A' : 'a') + run;
1047 run = 0;
1048 }
1049 }
1050 }
1051 assert(p - desc < area);
1052 *p++ = '\0';
1053 desc = sresize(desc, p - desc, char);
1054
1055 return desc;
1056}
1057
1058struct movedot {
1059 int op;
1060 space *olddot, *newdot;
1061};
1062
1063enum { MD_CHECK, MD_MOVE };
1064
1065static int movedot_cb(game_state *state, space *tile, void *vctx)
1066{
1067 struct movedot *md = (struct movedot *)vctx;
1068 space *newopp = NULL;
1069
1070 assert(tile->type == s_tile);
1071 assert(md->olddot && md->newdot);
1072
1073 if (!(tile->flags & F_TILE_ASSOC)) return 0;
1074 if (tile->dotx != md->olddot->x || tile->doty != md->olddot->y)
1075 return 0;
1076
1077 newopp = space_opposite_dot(state, tile, md->newdot);
1078
1079 switch (md->op) {
1080 case MD_CHECK:
1081 /* If the tile is associated with the old dot, check its
1082 * opposite wrt the _new_ dot is empty or same assoc. */
1083 if (!newopp) return -1; /* no new opposite */
1084 if (newopp->flags & F_TILE_ASSOC) {
1085 if (newopp->dotx != md->olddot->x ||
1086 newopp->doty != md->olddot->y)
1087 return -1; /* associated, but wrong dot. */
1088 }
1089#ifdef STANDALONE_PICTURE_GENERATOR
1090 if (picture) {
1091 /*
1092 * Reject if either tile and the dot don't match in colour.
1093 */
1094 if (!(picture[(tile->y/2) * state->w + (tile->x/2)]) ^
1095 !(md->newdot->flags & F_DOT_BLACK))
1096 return -1;
1097 if (!(picture[(newopp->y/2) * state->w + (newopp->x/2)]) ^
1098 !(md->newdot->flags & F_DOT_BLACK))
1099 return -1;
1100 }
1101#endif
1102 break;
1103
1104 case MD_MOVE:
1105 /* Move dot associations: anything that was associated
1106 * with the old dot, and its opposite wrt the new dot,
1107 * become associated with the new dot. */
1108 assert(newopp);
1109 debug(("Associating %d,%d and %d,%d with new dot %d,%d.\n",
1110 tile->x, tile->y, newopp->x, newopp->y,
1111 md->newdot->x, md->newdot->y));
1112 add_assoc(state, tile, md->newdot);
1113 add_assoc(state, newopp, md->newdot);
1114 return 1; /* we did something! */
1115 }
1116 return 0;
1117}
1118
1119/* For the given dot, first see if we could expand it into all the given
1120 * extra spaces (by checking for empty spaces on the far side), and then
1121 * see if we can move the dot to shift the CoG to include the new spaces.
1122 */
1123static bool dot_expand_or_move(game_state *state, space *dot,
1124 space **toadd, int nadd)
1125{
1126 space *tileopp;
1127 int i, ret, nnew, cx, cy;
1128 struct movedot md;
1129
1130 debug(("dot_expand_or_move: %d tiles for dot %d,%d\n",
1131 nadd, dot->x, dot->y));
1132 for (i = 0; i < nadd; i++)
1133 debug(("dot_expand_or_move: dot %d,%d\n",
1134 toadd[i]->x, toadd[i]->y));
1135 assert(dot->flags & F_DOT);
1136
1137#ifdef STANDALONE_PICTURE_GENERATOR
1138 if (picture) {
1139 /*
1140 * Reject the expansion totally if any of the new tiles are
1141 * the wrong colour.
1142 */
1143 for (i = 0; i < nadd; i++) {
1144 if (!(picture[(toadd[i]->y/2) * state->w + (toadd[i]->x/2)]) ^
1145 !(dot->flags & F_DOT_BLACK))
1146 return false;
1147 }
1148 }
1149#endif
1150
1151 /* First off, could we just expand the current dot's tile to cover
1152 * the space(s) passed in and their opposites? */
1153 for (i = 0; i < nadd; i++) {
1154 tileopp = space_opposite_dot(state, toadd[i], dot);
1155 if (!tileopp) goto noexpand;
1156 if (tileopp->flags & F_TILE_ASSOC) goto noexpand;
1157#ifdef STANDALONE_PICTURE_GENERATOR
1158 if (picture) {
1159 /*
1160 * The opposite tiles have to be the right colour as well.
1161 */
1162 if (!(picture[(tileopp->y/2) * state->w + (tileopp->x/2)]) ^
1163 !(dot->flags & F_DOT_BLACK))
1164 goto noexpand;
1165 }
1166#endif
1167 }
1168 /* OK, all spaces have valid empty opposites: associate spaces and
1169 * opposites with our dot. */
1170 for (i = 0; i < nadd; i++) {
1171 tileopp = space_opposite_dot(state, toadd[i], dot);
1172 add_assoc(state, toadd[i], dot);
1173 add_assoc(state, tileopp, dot);
1174 debug(("Added associations %d,%d and %d,%d --> %d,%d\n",
1175 toadd[i]->x, toadd[i]->y,
1176 tileopp->x, tileopp->y,
1177 dot->x, dot->y));
1178 dbg_state(state);
1179 }
1180 return true;
1181
1182noexpand:
1183 /* Otherwise, try to move dot so as to encompass given spaces: */
1184 /* first, calculate the 'centre of gravity' of the new dot. */
1185 nnew = dot->nassoc + nadd; /* number of tiles assoc. with new dot. */
1186 cx = dot->x * dot->nassoc;
1187 cy = dot->y * dot->nassoc;
1188 for (i = 0; i < nadd; i++) {
1189 cx += toadd[i]->x;
1190 cy += toadd[i]->y;
1191 }
1192 /* If the CoG isn't a whole number, it's not possible. */
1193 if ((cx % nnew) != 0 || (cy % nnew) != 0) {
1194 debug(("Unable to move dot %d,%d, CoG not whole number.\n",
1195 dot->x, dot->y));
1196 return false;
1197 }
1198 cx /= nnew; cy /= nnew;
1199
1200 /* Check whether all spaces in the old tile would have a good
1201 * opposite wrt the new dot. */
1202 md.olddot = dot;
1203 md.newdot = &SPACE(state, cx, cy);
1204 md.op = MD_CHECK;
1205 ret = foreach_tile(state, movedot_cb, IMPOSSIBLE_QUITS, &md);
1206 if (ret == -1) {
1207 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1208 dot->x, dot->y));
1209 return false;
1210 }
1211 /* Also check whether all spaces we're adding would have a good
1212 * opposite wrt the new dot. */
1213 for (i = 0; i < nadd; i++) {
1214 tileopp = space_opposite_dot(state, toadd[i], md.newdot);
1215 if (tileopp && (tileopp->flags & F_TILE_ASSOC) &&
1216 (tileopp->dotx != dot->x || tileopp->doty != dot->y)) {
1217 tileopp = NULL;
1218 }
1219 if (!tileopp) {
1220 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1221 dot->x, dot->y));
1222 return false;
1223 }
1224#ifdef STANDALONE_PICTURE_GENERATOR
1225 if (picture) {
1226 if (!(picture[(tileopp->y/2) * state->w + (tileopp->x/2)]) ^
1227 !(dot->flags & F_DOT_BLACK))
1228 return false;
1229 }
1230#endif
1231 }
1232
1233 /* If we've got here, we're ok. First, associate all of 'toadd'
1234 * with the _old_ dot (so they'll get fixed up, with their opposites,
1235 * in the next step). */
1236 for (i = 0; i < nadd; i++) {
1237 debug(("Associating to-add %d,%d with old dot %d,%d.\n",
1238 toadd[i]->x, toadd[i]->y, dot->x, dot->y));
1239 add_assoc(state, toadd[i], dot);
1240 }
1241
1242 /* Finally, move the dot and fix up all the old associations. */
1243 debug(("Moving dot at %d,%d to %d,%d\n",
1244 dot->x, dot->y, md.newdot->x, md.newdot->y));
1245 {
1246#ifdef STANDALONE_PICTURE_GENERATOR
1247 int f = dot->flags & F_DOT_BLACK;
1248#endif
1249 remove_dot(dot);
1250 add_dot(md.newdot);
1251#ifdef STANDALONE_PICTURE_GENERATOR
1252 md.newdot->flags |= f;
1253#endif
1254 }
1255
1256 md.op = MD_MOVE;
1257 ret = foreach_tile(state, movedot_cb, 0, &md);
1258 assert(ret == 1);
1259 dbg_state(state);
1260
1261 return true;
1262}
1263
1264/* Hard-code to a max. of 2x2 squares, for speed (less malloc) */
1265#define MAX_TOADD 4
1266#define MAX_OUTSIDE 8
1267
1268#define MAX_TILE_PERC 20
1269
1270static bool generate_try_block(game_state *state, random_state *rs,
1271 int x1, int y1, int x2, int y2)
1272{
1273 int x, y, nadd = 0, nout = 0, i, maxsz;
1274 space *sp, *toadd[MAX_TOADD], *outside[MAX_OUTSIDE], *dot;
1275
1276 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2)) return false;
1277
1278 /* We limit the maximum size of tiles to be ~2*sqrt(area); so,
1279 * a 5x5 grid shouldn't have anything >10 tiles, a 20x20 grid
1280 * nothing >40 tiles. */
1281 maxsz = (int)sqrt((double)(state->w * state->h)) * 2;
1282 debug(("generate_try_block, maxsz %d\n", maxsz));
1283
1284 /* Make a static list of the spaces; if any space is already
1285 * associated then quit immediately. */
1286 for (x = x1; x <= x2; x += 2) {
1287 for (y = y1; y <= y2; y += 2) {
1288 assert(nadd < MAX_TOADD);
1289 sp = &SPACE(state, x, y);
1290 assert(sp->type == s_tile);
1291 if (sp->flags & F_TILE_ASSOC) return false;
1292 toadd[nadd++] = sp;
1293 }
1294 }
1295
1296 /* Make a list of the spaces outside of our block, and shuffle it. */
1297#define OUTSIDE(x, y) do { \
1298 if (INGRID(state, (x), (y))) { \
1299 assert(nout < MAX_OUTSIDE); \
1300 outside[nout++] = &SPACE(state, (x), (y)); \
1301 } \
1302} while(0)
1303 for (x = x1; x <= x2; x += 2) {
1304 OUTSIDE(x, y1-2);
1305 OUTSIDE(x, y2+2);
1306 }
1307 for (y = y1; y <= y2; y += 2) {
1308 OUTSIDE(x1-2, y);
1309 OUTSIDE(x2+2, y);
1310 }
1311 shuffle(outside, nout, sizeof(space *), rs);
1312
1313 for (i = 0; i < nout; i++) {
1314 if (!(outside[i]->flags & F_TILE_ASSOC)) continue;
1315 dot = &SPACE(state, outside[i]->dotx, outside[i]->doty);
1316 if (dot->nassoc >= maxsz) {
1317 debug(("Not adding to dot %d,%d, large enough (%d) already.\n",
1318 dot->x, dot->y, dot->nassoc));
1319 continue;
1320 }
1321 if (dot_expand_or_move(state, dot, toadd, nadd)) return true;
1322 }
1323 return false;
1324}
1325
1326#ifdef STANDALONE_SOLVER
1327static bool one_try; /* override for soak testing */
1328#endif
1329
1330#define GP_DOTS 1
1331
1332static void generate_pass(game_state *state, random_state *rs, int *scratch,
1333 int perc, unsigned int flags)
1334{
1335 int sz = state->sx*state->sy, nspc, i, ret;
1336
1337 /* Random list of squares to try and process, one-by-one. */
1338 for (i = 0; i < sz; i++) scratch[i] = i;
1339 shuffle(scratch, sz, sizeof(int), rs);
1340
1341 /* This bug took me a, er, little while to track down. On PalmOS,
1342 * which has 16-bit signed ints, puzzles over about 9x9 started
1343 * failing to generate because the nspc calculation would start
1344 * to overflow, causing the dots not to be filled in properly. */
1345 nspc = (int)(((long)perc * (long)sz) / 100L);
1346 debug(("generate_pass: %d%% (%d of %dx%d) squares, flags 0x%x\n",
1347 perc, nspc, state->sx, state->sy, flags));
1348
1349 for (i = 0; i < nspc; i++) {
1350 space *sp = &state->grid[scratch[i]];
1351 int x1 = sp->x, y1 = sp->y, x2 = sp->x, y2 = sp->y;
1352
1353 if (sp->type == s_edge) {
1354 if (IS_VERTICAL_EDGE(sp->x)) {
1355 x1--; x2++;
1356 } else {
1357 y1--; y2++;
1358 }
1359 }
1360 if (sp->type != s_vertex) {
1361 /* heuristic; expanding from vertices tends to generate lots of
1362 * too-big regions of tiles. */
1363 if (generate_try_block(state, rs, x1, y1, x2, y2))
1364 continue; /* we expanded successfully. */
1365 }
1366
1367 if (!(flags & GP_DOTS)) continue;
1368
1369 if ((sp->type == s_edge) && (i % 2)) {
1370 debug(("Omitting edge %d,%d as half-of.\n", sp->x, sp->y));
1371 continue;
1372 }
1373
1374 /* If we've got here we might want to put a dot down. Check
1375 * if we can, and add one if so. */
1376 if (dot_is_possible(state, sp, false)) {
1377 add_dot(sp);
1378#ifdef STANDALONE_PICTURE_GENERATOR
1379 if (picture) {
1380 if (picture[(sp->y/2) * state->w + (sp->x/2)])
1381 sp->flags |= F_DOT_BLACK;
1382 }
1383#endif
1384 ret = solver_obvious_dot(state, sp);
1385 assert(ret != -1);
1386 debug(("Added dot (and obvious associations) at %d,%d\n",
1387 sp->x, sp->y));
1388 dbg_state(state);
1389 }
1390 }
1391 dbg_state(state);
1392}
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
1445static char *new_game_desc(const game_params *params, random_state *rs,
1446 char **aux, bool interactive)
1447{
1448 game_state *state = blank_game(params->w, params->h), *copy;
1449 char *desc;
1450 int *scratch, sz = state->sx*state->sy, i;
1451 int diff, best_wiggliness;
1452 bool cc;
1453
1454 scratch = snewn(sz, int);
1455
1456generate:
1457 best_wiggliness = -1;
1458 copy = NULL;
1459 for (i = 0; i < GENERATE_TRIES; i++) {
1460 int this_wiggliness;
1461
1462 do {
1463 clear_game(state, true);
1464 generate_pass(state, rs, scratch, 100, GP_DOTS);
1465 game_update_dots(state);
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;
1482
1483#ifdef DEBUGGING
1484 {
1485 char *tmp = encode_game(state);
1486 debug(("new_game_desc state %dx%d:%s\n", params->w, params->h, tmp));
1487 sfree(tmp);
1488 }
1489#endif
1490
1491 for (i = 0; i < state->sx*state->sy; i++)
1492 if (state->grid[i].type == s_tile)
1493 outline_tile_fordot(state, &state->grid[i], true);
1494 cc = check_complete(state, NULL, NULL);
1495 assert(cc);
1496
1497 copy = dup_game(state);
1498 clear_game(copy, false);
1499 dbg_state(copy);
1500 diff = solver_state(copy, params->diff);
1501 free_game(copy);
1502
1503 assert(diff != DIFF_IMPOSSIBLE);
1504 if (diff != params->diff) {
1505 /*
1506 * If the puzzle was insoluble at this difficulty level (i.e.
1507 * too hard), _or_ soluble at a lower level (too easy), go
1508 * round again.
1509 *
1510 * An exception is in soak-testing mode, where we return the
1511 * first puzzle we got regardless.
1512 */
1513#ifdef STANDALONE_SOLVER
1514 if (!one_try)
1515#endif
1516 goto generate;
1517 }
1518
1519#ifdef STANDALONE_PICTURE_GENERATOR
1520 /*
1521 * Postprocessing pass to prevent excessive numbers of adjacent
1522 * singletons. Iterate over all edges in random shuffled order;
1523 * for each edge that separates two regions, investigate
1524 * whether removing that edge and merging the regions would
1525 * still yield a valid and soluble puzzle. (The two regions
1526 * must also be the same colour, of course.) If so, do it.
1527 *
1528 * This postprocessing pass is slow (due to repeated solver
1529 * invocations), and seems to be unnecessary during normal
1530 * unconstrained game generation. However, when generating a
1531 * game under colour constraints, excessive singletons seem to
1532 * turn up more often, so it's worth doing this.
1533 */
1534 {
1535 int *posns, nposns;
1536 int i, j, newdiff;
1537 game_state *copy2;
1538
1539 nposns = params->w * (params->h+1) + params->h * (params->w+1);
1540 posns = snewn(nposns, int);
1541 for (i = j = 0; i < state->sx*state->sy; i++)
1542 if (state->grid[i].type == s_edge)
1543 posns[j++] = i;
1544 assert(j == nposns);
1545
1546 shuffle(posns, nposns, sizeof(*posns), rs);
1547
1548 for (i = 0; i < nposns; i++) {
1549 int x, y, x0, y0, x1, y1, cx, cy, cn, cx0, cy0, cx1, cy1, tx, ty;
1550 space *s0, *s1, *ts, *d0, *d1, *dn;
1551 bool ok;
1552
1553 /* Coordinates of edge space */
1554 x = posns[i] % state->sx;
1555 y = posns[i] / state->sx;
1556
1557 /* Coordinates of square spaces on either side of edge */
1558 x0 = ((x+1) & ~1) - 1; /* round down to next odd number */
1559 y0 = ((y+1) & ~1) - 1;
1560 x1 = 2*x-x0; /* and reflect about x to get x1 */
1561 y1 = 2*y-y0;
1562
1563 if (!INGRID(state, x0, y0) || !INGRID(state, x1, y1))
1564 continue; /* outermost edge of grid */
1565 s0 = &SPACE(state, x0, y0);
1566 s1 = &SPACE(state, x1, y1);
1567 assert(s0->type == s_tile && s1->type == s_tile);
1568
1569 if (s0->dotx == s1->dotx && s0->doty == s1->doty)
1570 continue; /* tiles _already_ owned by same dot */
1571
1572 d0 = &SPACE(state, s0->dotx, s0->doty);
1573 d1 = &SPACE(state, s1->dotx, s1->doty);
1574
1575 if ((d0->flags ^ d1->flags) & F_DOT_BLACK)
1576 continue; /* different colours: cannot merge */
1577
1578 /*
1579 * Work out where the centre of gravity of the new
1580 * region would be.
1581 */
1582 cx = d0->nassoc * d0->x + d1->nassoc * d1->x;
1583 cy = d0->nassoc * d0->y + d1->nassoc * d1->y;
1584 cn = d0->nassoc + d1->nassoc;
1585 if (cx % cn || cy % cn)
1586 continue; /* CoG not at integer coordinates */
1587 cx /= cn;
1588 cy /= cn;
1589 assert(INUI(state, cx, cy));
1590
1591 /*
1592 * Ensure that the CoG would actually be _in_ the new
1593 * region, by verifying that all its surrounding tiles
1594 * belong to one or other of our two dots.
1595 */
1596 cx0 = ((cx+1) & ~1) - 1; /* round down to next odd number */
1597 cy0 = ((cy+1) & ~1) - 1;
1598 cx1 = 2*cx-cx0; /* and reflect about cx to get cx1 */
1599 cy1 = 2*cy-cy0;
1600 ok = true;
1601 for (ty = cy0; ty <= cy1; ty += 2)
1602 for (tx = cx0; tx <= cx1; tx += 2) {
1603 ts = &SPACE(state, tx, ty);
1604 assert(ts->type == s_tile);
1605 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1606 (ts->dotx != d1->x || ts->doty != d1->y))
1607 ok = false;
1608 }
1609 if (!ok)
1610 continue;
1611
1612 /*
1613 * Verify that for every tile in either source region,
1614 * that tile's image in the new CoG is also in one of
1615 * the two source regions.
1616 */
1617 for (ty = 1; ty < state->sy; ty += 2) {
1618 for (tx = 1; tx < state->sx; tx += 2) {
1619 int tx1, ty1;
1620
1621 ts = &SPACE(state, tx, ty);
1622 assert(ts->type == s_tile);
1623 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1624 (ts->dotx != d1->x || ts->doty != d1->y))
1625 continue; /* not part of these tiles anyway */
1626 tx1 = 2*cx-tx;
1627 ty1 = 2*cy-ty;
1628 if (!INGRID(state, tx1, ty1)) {
1629 ok = false;
1630 break;
1631 }
1632 ts = &SPACE(state, cx+cx-tx, cy+cy-ty);
1633 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1634 (ts->dotx != d1->x || ts->doty != d1->y)) {
1635 ok = false;
1636 break;
1637 }
1638 }
1639 if (!ok)
1640 break;
1641 }
1642 if (!ok)
1643 continue;
1644
1645 /*
1646 * Now we're clear to attempt the merge. We take a copy
1647 * of the game state first, so we can revert it easily
1648 * if the resulting puzzle turns out to have become
1649 * insoluble.
1650 */
1651 copy2 = dup_game(state);
1652
1653 remove_dot(d0);
1654 remove_dot(d1);
1655 dn = &SPACE(state, cx, cy);
1656 add_dot(dn);
1657 dn->flags |= (d0->flags & F_DOT_BLACK);
1658 for (ty = 1; ty < state->sy; ty += 2) {
1659 for (tx = 1; tx < state->sx; tx += 2) {
1660 ts = &SPACE(state, tx, ty);
1661 assert(ts->type == s_tile);
1662 if ((ts->dotx != d0->x || ts->doty != d0->y) &&
1663 (ts->dotx != d1->x || ts->doty != d1->y))
1664 continue; /* not part of these tiles anyway */
1665 add_assoc(state, ts, dn);
1666 }
1667 }
1668
1669 copy = dup_game(state);
1670 clear_game(copy, false);
1671 dbg_state(copy);
1672 newdiff = solver_state(copy, params->diff);
1673 free_game(copy);
1674 if (diff == newdiff) {
1675 /* Still just as soluble. Let the merge stand. */
1676 free_game(copy2);
1677 } else {
1678 /* Became insoluble. Revert. */
1679 free_game(state);
1680 state = copy2;
1681 }
1682 }
1683 sfree(posns);
1684 }
1685#endif
1686
1687 desc = encode_game(state);
1688#ifndef STANDALONE_SOLVER
1689 debug(("new_game_desc generated: \n"));
1690 dbg_state(state);
1691#endif
1692
1693 game_state *blank = blank_game(params->w, params->h);
1694 *aux = diff_game(blank, state, true, -1);
1695 free_game(blank);
1696
1697 free_game(state);
1698 sfree(scratch);
1699
1700 return desc;
1701}
1702
1703static bool dots_too_close(game_state *state)
1704{
1705 /* Quick-and-dirty check, using half the solver:
1706 * solver_obvious will only fail if the dots are
1707 * too close together, so dot-proximity associations
1708 * overlap. */
1709 game_state *tmp = dup_game(state);
1710 int ret = solver_obvious(tmp);
1711 free_game(tmp);
1712 return ret == -1;
1713}
1714
1715static game_state *load_game(const game_params *params, const char *desc,
1716 const char **why_r)
1717{
1718 game_state *state = blank_game(params->w, params->h);
1719 const char *why = NULL;
1720 int i, x, y, n;
1721 unsigned int df;
1722
1723 i = 0;
1724 while (*desc) {
1725 n = *desc++;
1726 if (n == 'z') {
1727 i += 25;
1728 continue;
1729 }
1730 if (n >= 'a' && n <= 'y') {
1731 i += n - 'a';
1732 df = 0;
1733 } else if (n >= 'A' && n <= 'Y') {
1734 i += n - 'A';
1735 df = F_DOT_BLACK;
1736 } else {
1737 why = "Invalid characters in game description"; goto fail;
1738 }
1739 /* if we got here we incremented i and have a dot to add. */
1740 y = (i / (state->sx-2)) + 1;
1741 x = (i % (state->sx-2)) + 1;
1742 if (!INUI(state, x, y)) {
1743 why = "Too much data to fit in grid"; goto fail;
1744 }
1745 add_dot(&SPACE(state, x, y));
1746 SPACE(state, x, y).flags |= df;
1747 i++;
1748 }
1749 game_update_dots(state);
1750
1751 if (dots_too_close(state)) {
1752 why = "Dots too close together"; goto fail;
1753 }
1754
1755 return state;
1756
1757fail:
1758 free_game(state);
1759 if (why_r) *why_r = why;
1760 return NULL;
1761}
1762
1763static const char *validate_desc(const game_params *params, const char *desc)
1764{
1765 const char *why = NULL;
1766 game_state *dummy = load_game(params, desc, &why);
1767 if (dummy) {
1768 free_game(dummy);
1769 assert(!why);
1770 } else
1771 assert(why);
1772 return why;
1773}
1774
1775static game_state *new_game(midend *me, const game_params *params,
1776 const char *desc)
1777{
1778 game_state *state = load_game(params, desc, NULL);
1779 if (!state) {
1780 assert("Unable to load ?validated game.");
1781 return NULL;
1782 }
1783#ifdef EDITOR
1784 state->me = me;
1785#endif
1786 return state;
1787}
1788
1789/* ----------------------------------------------------------
1790 * Solver and all its little wizards.
1791 */
1792
1793#if defined DEBUGGING || defined STANDALONE_SOLVER
1794static int solver_recurse_depth;
1795#define STATIC_RECURSION_DEPTH
1796#endif
1797
1798typedef struct solver_ctx {
1799 game_state *state;
1800 int sz; /* state->sx * state->sy */
1801 space **scratch; /* size sz */
1802 DSF *dsf; /* size sz */
1803 int *iscratch; /* size sz */
1804} solver_ctx;
1805
1806static solver_ctx *new_solver(game_state *state)
1807{
1808 solver_ctx *sctx = snew(solver_ctx);
1809 sctx->state = state;
1810 sctx->sz = state->sx*state->sy;
1811 sctx->scratch = snewn(sctx->sz, space *);
1812 sctx->dsf = dsf_new(sctx->sz);
1813 sctx->iscratch = snewn(sctx->sz, int);
1814 return sctx;
1815}
1816
1817static void free_solver(solver_ctx *sctx)
1818{
1819 sfree(sctx->scratch);
1820 dsf_free(sctx->dsf);
1821 sfree(sctx->iscratch);
1822 sfree(sctx);
1823}
1824
1825 /* Solver ideas so far:
1826 *
1827 * For any empty space, work out how many dots it could associate
1828 * with:
1829 * it needs line-of-sight
1830 * it needs an empty space on the far side
1831 * any adjacent lines need corresponding line possibilities.
1832 */
1833
1834/* The solver_ctx should keep a list of dot positions, for quicker looping.
1835 *
1836 * Solver techniques, in order of difficulty:
1837 * obvious adjacency to dots
1838 * transferring tiles to opposite side
1839 * transferring lines to opposite side
1840 * one possible dot for a given tile based on opposite availability
1841 * tile with 3 definite edges next to an associated tile must associate
1842 with same dot.
1843 *
1844 * one possible dot for a given tile based on line-of-sight
1845 */
1846
1847static int solver_add_assoc(game_state *state, space *tile, int dx, int dy,
1848 const char *why)
1849{
1850 space *dot, *tile_opp;
1851
1852 dot = &SPACE(state, dx, dy);
1853 tile_opp = space_opposite_dot(state, tile, dot);
1854
1855 assert(tile->type == s_tile);
1856 if (tile->flags & F_TILE_ASSOC) {
1857 if ((tile->dotx != dx) || (tile->doty != dy)) {
1858 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1859 "already --> %d,%d.\n",
1860 solver_recurse_depth*4, "",
1861 tile->x, tile->y, dx, dy, why,
1862 tile->dotx, tile->doty));
1863 return -1;
1864 }
1865 return 0; /* no-op */
1866 }
1867 if (!tile_opp) {
1868 solvep(("%*s%d,%d --> %d,%d impossible, no opposite tile.\n",
1869 solver_recurse_depth*4, "", tile->x, tile->y, dx, dy));
1870 return -1;
1871 }
1872 if (tile_opp->flags & F_TILE_ASSOC &&
1873 (tile_opp->dotx != dx || tile_opp->doty != dy)) {
1874 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1875 "opposite already --> %d,%d.\n",
1876 solver_recurse_depth*4, "",
1877 tile->x, tile->y, dx, dy, why,
1878 tile_opp->dotx, tile_opp->doty));
1879 return -1;
1880 }
1881
1882 add_assoc(state, tile, dot);
1883 add_assoc(state, tile_opp, dot);
1884 solvep(("%*sSetting %d,%d --> %d,%d (%s).\n",
1885 solver_recurse_depth*4, "",
1886 tile->x, tile->y,dx, dy, why));
1887 solvep(("%*sSetting %d,%d --> %d,%d (%s, opposite).\n",
1888 solver_recurse_depth*4, "",
1889 tile_opp->x, tile_opp->y, dx, dy, why));
1890 return 1;
1891}
1892
1893static int solver_obvious_dot(game_state *state, space *dot)
1894{
1895 int dx, dy, ret, didsth = 0;
1896 space *tile;
1897
1898 debug(("%*ssolver_obvious_dot for %d,%d.\n",
1899 solver_recurse_depth*4, "", dot->x, dot->y));
1900
1901 assert(dot->flags & F_DOT);
1902 for (dx = -1; dx <= 1; dx++) {
1903 for (dy = -1; dy <= 1; dy++) {
1904 if (!INGRID(state, dot->x+dx, dot->y+dy)) continue;
1905
1906 tile = &SPACE(state, dot->x+dx, dot->y+dy);
1907 if (tile->type == s_tile) {
1908 ret = solver_add_assoc(state, tile, dot->x, dot->y,
1909 "next to dot");
1910 if (ret < 0) return -1;
1911 if (ret > 0) didsth = 1;
1912 }
1913 }
1914 }
1915 return didsth;
1916}
1917
1918static int solver_obvious(game_state *state)
1919{
1920 int i, didsth = 0, ret;
1921
1922 debug(("%*ssolver_obvious.\n", solver_recurse_depth*4, ""));
1923
1924 for (i = 0; i < state->ndots; i++) {
1925 ret = solver_obvious_dot(state, state->dots[i]);
1926 if (ret < 0) return -1;
1927 if (ret > 0) didsth = 1;
1928 }
1929 return didsth;
1930}
1931
1932static int solver_lines_opposite_cb(game_state *state, space *edge, void *ctx)
1933{
1934 int didsth = 0, n, dx, dy;
1935 space *tiles[2], *tile_opp, *edge_opp;
1936
1937 assert(edge->type == s_edge);
1938
1939 tiles_from_edge(state, edge, tiles);
1940
1941 /* if tiles[0] && tiles[1] && they're both associated
1942 * and they're both associated with different dots,
1943 * ensure the line is set. */
1944 if (!(edge->flags & F_EDGE_SET) &&
1945 tiles[0] && tiles[1] &&
1946 (tiles[0]->flags & F_TILE_ASSOC) &&
1947 (tiles[1]->flags & F_TILE_ASSOC) &&
1948 (tiles[0]->dotx != tiles[1]->dotx ||
1949 tiles[0]->doty != tiles[1]->doty)) {
1950 /* No edge, but the two adjacent tiles are both
1951 * associated with different dots; add the edge. */
1952 solvep(("%*sSetting edge %d,%d - tiles different dots.\n",
1953 solver_recurse_depth*4, "", edge->x, edge->y));
1954 edge->flags |= F_EDGE_SET;
1955 didsth = 1;
1956 }
1957
1958 if (!(edge->flags & F_EDGE_SET)) return didsth;
1959 for (n = 0; n < 2; n++) {
1960 if (!tiles[n]) continue;
1961 assert(tiles[n]->type == s_tile);
1962 if (!(tiles[n]->flags & F_TILE_ASSOC)) continue;
1963
1964 tile_opp = tile_opposite(state, tiles[n]);
1965 if (!tile_opp) {
1966 solvep(("%*simpossible: edge %d,%d has assoc. tile %d,%d"
1967 " with no opposite.\n",
1968 solver_recurse_depth*4, "",
1969 edge->x, edge->y, tiles[n]->x, tiles[n]->y));
1970 /* edge of tile has no opposite edge (off grid?);
1971 * this is impossible. */
1972 return -1;
1973 }
1974
1975 dx = tiles[n]->x - edge->x;
1976 dy = tiles[n]->y - edge->y;
1977 assert(INGRID(state, tile_opp->x+dx, tile_opp->y+dy));
1978 edge_opp = &SPACE(state, tile_opp->x+dx, tile_opp->y+dy);
1979 if (!(edge_opp->flags & F_EDGE_SET)) {
1980 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n",
1981 solver_recurse_depth*4, "",
1982 tile_opp->x+dx, tile_opp->y+dy, edge->x, edge->y));
1983 edge_opp->flags |= F_EDGE_SET;
1984 didsth = 1;
1985 }
1986 }
1987 return didsth;
1988}
1989
1990static int solver_spaces_oneposs_cb(game_state *state, space *tile, void *ctx)
1991{
1992 int n, eset, ret;
1993 space *edgeadj[4], *tileadj[4];
1994 int dotx, doty;
1995
1996 assert(tile->type == s_tile);
1997 if (tile->flags & F_TILE_ASSOC) return 0;
1998
1999 adjacencies(state, tile, edgeadj, tileadj);
2000
2001 /* Empty tile. If each edge is either set, or associated with
2002 * the same dot, we must also associate with dot. */
2003 eset = 0; dotx = -1; doty = -1;
2004 for (n = 0; n < 4; n++) {
2005 assert(edgeadj[n]);
2006 assert(edgeadj[n]->type == s_edge);
2007 if (edgeadj[n]->flags & F_EDGE_SET) {
2008 eset++;
2009 } else {
2010 assert(tileadj[n]);
2011 assert(tileadj[n]->type == s_tile);
2012
2013 /* If an adjacent tile is empty we can't make any deductions.*/
2014 if (!(tileadj[n]->flags & F_TILE_ASSOC))
2015 return 0;
2016
2017 /* If an adjacent tile is assoc. with a different dot
2018 * we can't make any deductions. */
2019 if (dotx != -1 && doty != -1 &&
2020 (tileadj[n]->dotx != dotx ||
2021 tileadj[n]->doty != doty))
2022 return 0;
2023
2024 dotx = tileadj[n]->dotx;
2025 doty = tileadj[n]->doty;
2026 }
2027 }
2028 if (eset == 4) {
2029 solvep(("%*simpossible: empty tile %d,%d has 4 edges\n",
2030 solver_recurse_depth*4, "",
2031 tile->x, tile->y));
2032 return -1;
2033 }
2034 assert(dotx != -1 && doty != -1);
2035
2036 ret = solver_add_assoc(state, tile, dotx, doty, "rest are edges");
2037 if (ret == -1) return -1;
2038 assert(ret != 0); /* really should have done something. */
2039
2040 return 1;
2041}
2042
2043/* Improved algorithm for tracking line-of-sight from dots, and not spaces.
2044 *
2045 * The solver_ctx already stores a list of dots: the algorithm proceeds by
2046 * expanding outwards from each dot in turn, expanding first to the boundary
2047 * of its currently-connected tile and then to all empty tiles that could see
2048 * it. Empty tiles will be flagged with a 'can see dot <x,y>' sticker.
2049 *
2050 * Expansion will happen by (symmetrically opposite) pairs of squares; if
2051 * a square hasn't an opposite number there's no point trying to expand through
2052 * it. Empty tiles will therefore also be tagged in pairs.
2053 *
2054 * If an empty tile already has a 'can see dot <x,y>' tag from a previous dot,
2055 * it (and its partner) gets untagged (or, rather, a 'can see two dots' tag)
2056 * because we're looking for single-dot possibilities.
2057 *
2058 * Once we've gone through all the dots, any which still have a 'can see dot'
2059 * tag get associated with that dot (because it must have been the only one);
2060 * any without any tag (i.e. that could see _no_ dots) cause an impossibility
2061 * marked.
2062 *
2063 * The expansion will happen each time with a stored list of (space *) pairs,
2064 * rather than a mark-and-sweep idea; that's horrifically inefficient.
2065 *
2066 * expansion algorithm:
2067 *
2068 * * allocate list of (space *) the size of s->sx*s->sy.
2069 * * allocate second grid for (flags, dotx, doty) size of sx*sy.
2070 *
2071 * clear second grid (flags = 0, all dotx and doty = 0)
2072 * flags: F_REACHABLE, F_MULTIPLE
2073 *
2074 *
2075 * * for each dot, start with one pair of tiles that are associated with it --
2076 * * vertex --> (dx+1, dy+1), (dx-1, dy-1)
2077 * * edge --> (adj1, adj2)
2078 * * tile --> (tile, tile) ???
2079 * * mark that pair of tiles with F_MARK, clear all other F_MARKs.
2080 * * add two tiles to start of list.
2081 *
2082 * set start = 0, end = next = 2
2083 *
2084 * from (start to end-1, step 2) {
2085 * * we have two tiles (t1, t2), opposites wrt our dot.
2086 * * for each (at1) sensible adjacent tile to t1 (i.e. not past an edge):
2087 * * work out at2 as the opposite to at1
2088 * * assert at1 and at2 have the same F_MARK values.
2089 * * if at1 & F_MARK ignore it (we've been there on a previous sweep)
2090 * * if either are associated with a different dot
2091 * * mark both with F_MARK (so we ignore them later)
2092 * * otherwise (assoc. with our dot, or empty):
2093 * * mark both with F_MARK
2094 * * add their space * values to the end of the list, set next += 2.
2095 * }
2096 *
2097 * if (end == next)
2098 * * we didn't add any new squares; exit the loop.
2099 * else
2100 * * set start = next+1, end = next. go round again
2101 *
2102 * We've finished expanding from the dot. Now, for each square we have
2103 * in our list (--> each square with F_MARK):
2104 * * if the tile is empty:
2105 * * if F_REACHABLE was already set
2106 * * set F_MULTIPLE
2107 * * otherwise
2108 * * set F_REACHABLE, set dotx and doty to our dot.
2109 *
2110 * Then, continue the whole thing for each dot in turn.
2111 *
2112 * Once we've done for each dot, go through the entire grid looking for
2113 * empty tiles: for each empty tile:
2114 * if F_REACHABLE and not F_MULTIPLE, set that dot (and its double)
2115 * if !F_REACHABLE, return as impossible.
2116 *
2117 */
2118
2119/* Returns true if this tile is either already associated with this dot,
2120 * or blank. */
2121static bool solver_expand_checkdot(space *tile, space *dot)
2122{
2123 if (!(tile->flags & F_TILE_ASSOC)) return true;
2124 if (tile->dotx == dot->x && tile->doty == dot->y) return true;
2125 return false;
2126}
2127
2128static void solver_expand_fromdot(game_state *state, space *dot, solver_ctx *sctx)
2129{
2130 int i, j, x, y, start, end, next;
2131 space *sp;
2132
2133 /* Clear the grid of the (space) flags we'll use. */
2134
2135 /* This is well optimised; analysis showed that:
2136 for (i = 0; i < sctx->sz; i++) state->grid[i].flags &= ~F_MARK;
2137 took up ~85% of the total function time! */
2138 for (y = 1; y < state->sy; y += 2) {
2139 sp = &SPACE(state, 1, y);
2140 for (x = 1; x < state->sx; x += 2, sp += 2)
2141 sp->flags &= ~F_MARK;
2142 }
2143
2144 /* Seed the list of marked squares with two that must be associated
2145 * with our dot (possibly the same space) */
2146 if (dot->type == s_tile) {
2147 sctx->scratch[0] = sctx->scratch[1] = dot;
2148 } else if (dot->type == s_edge) {
2149 tiles_from_edge(state, dot, sctx->scratch);
2150 } else if (dot->type == s_vertex) {
2151 /* pick two of the opposite ones arbitrarily. */
2152 sctx->scratch[0] = &SPACE(state, dot->x-1, dot->y-1);
2153 sctx->scratch[1] = &SPACE(state, dot->x+1, dot->y+1);
2154 }
2155 assert(sctx->scratch[0]->flags & F_TILE_ASSOC);
2156 assert(sctx->scratch[1]->flags & F_TILE_ASSOC);
2157
2158 sctx->scratch[0]->flags |= F_MARK;
2159 sctx->scratch[1]->flags |= F_MARK;
2160
2161 debug(("%*sexpand from dot %d,%d seeded with %d,%d and %d,%d.\n",
2162 solver_recurse_depth*4, "", dot->x, dot->y,
2163 sctx->scratch[0]->x, sctx->scratch[0]->y,
2164 sctx->scratch[1]->x, sctx->scratch[1]->y));
2165
2166 start = 0; end = 2; next = 2;
2167
2168expand:
2169 debug(("%*sexpand: start %d, end %d, next %d\n",
2170 solver_recurse_depth*4, "", start, end, next));
2171 for (i = start; i < end; i += 2) {
2172 space *t1 = sctx->scratch[i]/*, *t2 = sctx->scratch[i+1]*/;
2173 space *edges[4], *tileadj[4], *tileadj2;
2174
2175 adjacencies(state, t1, edges, tileadj);
2176
2177 for (j = 0; j < 4; j++) {
2178 assert(edges[j]);
2179 if (edges[j]->flags & F_EDGE_SET) continue;
2180 assert(tileadj[j]);
2181
2182 if (tileadj[j]->flags & F_MARK) continue; /* seen before. */
2183
2184 /* We have a tile adjacent to t1; find its opposite. */
2185 tileadj2 = space_opposite_dot(state, tileadj[j], dot);
2186 if (!tileadj2) {
2187 debug(("%*sMarking %d,%d, no opposite.\n",
2188 solver_recurse_depth*4, "",
2189 tileadj[j]->x, tileadj[j]->y));
2190 tileadj[j]->flags |= F_MARK;
2191 continue; /* no opposite, so mark for next time. */
2192 }
2193 /* If the tile had an opposite we should have either seen both of
2194 * these, or neither of these, before. */
2195 assert(!(tileadj2->flags & F_MARK));
2196
2197 if (solver_expand_checkdot(tileadj[j], dot) &&
2198 solver_expand_checkdot(tileadj2, dot)) {
2199 /* Both tiles could associate with this dot; add them to
2200 * our list. */
2201 debug(("%*sAdding %d,%d and %d,%d to possibles list.\n",
2202 solver_recurse_depth*4, "",
2203 tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y));
2204 sctx->scratch[next++] = tileadj[j];
2205 sctx->scratch[next++] = tileadj2;
2206 }
2207 /* Either way, we've seen these tiles already so mark them. */
2208 debug(("%*sMarking %d,%d and %d,%d.\n",
2209 solver_recurse_depth*4, "",
2210 tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y));
2211 tileadj[j]->flags |= F_MARK;
2212 tileadj2->flags |= F_MARK;
2213 }
2214 }
2215 if (next > end) {
2216 /* We added more squares; go back and try again. */
2217 start = end; end = next; goto expand;
2218 }
2219
2220 /* We've expanded as far as we can go. Now we update the main flags
2221 * on all tiles we've expanded into -- if they were empty, we have
2222 * found possible associations for this dot. */
2223 for (i = 0; i < end; i++) {
2224 if (sctx->scratch[i]->flags & F_TILE_ASSOC) continue;
2225 if (sctx->scratch[i]->flags & F_REACHABLE) {
2226 /* This is (at least) the second dot this tile could
2227 * associate with. */
2228 debug(("%*sempty tile %d,%d could assoc. other dot %d,%d\n",
2229 solver_recurse_depth*4, "",
2230 sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y));
2231 sctx->scratch[i]->flags |= F_MULTIPLE;
2232 } else {
2233 /* This is the first (possibly only) dot. */
2234 debug(("%*sempty tile %d,%d could assoc. 1st dot %d,%d\n",
2235 solver_recurse_depth*4, "",
2236 sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y));
2237 sctx->scratch[i]->flags |= F_REACHABLE;
2238 sctx->scratch[i]->dotx = dot->x;
2239 sctx->scratch[i]->doty = dot->y;
2240 }
2241 }
2242 dbg_state(state);
2243}
2244
2245static int solver_expand_postcb(game_state *state, space *tile, void *ctx)
2246{
2247 assert(tile->type == s_tile);
2248
2249 if (tile->flags & F_TILE_ASSOC) return 0;
2250
2251 if (!(tile->flags & F_REACHABLE)) {
2252 solvep(("%*simpossible: space (%d,%d) can reach no dots.\n",
2253 solver_recurse_depth*4, "", tile->x, tile->y));
2254 return -1;
2255 }
2256 if (tile->flags & F_MULTIPLE) return 0;
2257
2258 return solver_add_assoc(state, tile, tile->dotx, tile->doty,
2259 "single possible dot after expansion");
2260}
2261
2262static int solver_expand_dots(game_state *state, solver_ctx *sctx)
2263{
2264 int i;
2265
2266 for (i = 0; i < sctx->sz; i++)
2267 state->grid[i].flags &= ~(F_REACHABLE|F_MULTIPLE);
2268
2269 for (i = 0; i < state->ndots; i++)
2270 solver_expand_fromdot(state, state->dots[i], sctx);
2271
2272 return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx);
2273}
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
2446struct recurse_ctx {
2447 space *best;
2448 int bestn;
2449};
2450
2451static int solver_recurse_cb(game_state *state, space *tile, void *ctx)
2452{
2453 struct recurse_ctx *rctx = (struct recurse_ctx *)ctx;
2454 int i, n = 0;
2455
2456 assert(tile->type == s_tile);
2457 if (tile->flags & F_TILE_ASSOC) return 0;
2458
2459 /* We're unassociated: count up all the dots we could associate with. */
2460 for (i = 0; i < state->ndots; i++) {
2461 if (dotfortile(state, tile, state->dots[i]))
2462 n++;
2463 }
2464 if (n > rctx->bestn) {
2465 rctx->bestn = n;
2466 rctx->best = tile;
2467 }
2468 return 0;
2469}
2470
2471#define MAXRECURSE 5
2472
2473static int solver_recurse(game_state *state, int maxdiff, int depth)
2474{
2475 int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy;
2476 space *ingrid, *outgrid = NULL, *bestopp;
2477 struct recurse_ctx rctx;
2478
2479 if (depth >= MAXRECURSE) {
2480 solvep(("Limiting recursion to %d, returning.\n", MAXRECURSE));
2481 return DIFF_UNFINISHED;
2482 }
2483
2484 /* Work out the cell to recurse on; go through all unassociated tiles
2485 * and find which one has the most possible dots it could associate
2486 * with. */
2487 rctx.best = NULL;
2488 rctx.bestn = 0;
2489
2490 foreach_tile(state, solver_recurse_cb, 0, &rctx);
2491 if (rctx.bestn == 0) return DIFF_IMPOSSIBLE; /* or assert? */
2492 assert(rctx.best);
2493
2494 solvep(("%*sRecursing around %d,%d, with %d possible dots.\n",
2495 solver_recurse_depth*4, "",
2496 rctx.best->x, rctx.best->y, rctx.bestn));
2497
2498 ingrid = snewn(gsz, space);
2499 memcpy(ingrid, state->grid, gsz * sizeof(space));
2500
2501 for (n = 0; n < state->ndots; n++) {
2502 memcpy(state->grid, ingrid, gsz * sizeof(space));
2503
2504 if (!dotfortile(state, rctx.best, state->dots[n])) continue;
2505
2506 /* set cell (temporarily) pointing to that dot. */
2507 solver_add_assoc(state, rctx.best,
2508 state->dots[n]->x, state->dots[n]->y,
2509 "Attempting for recursion");
2510
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
2516
2517 if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) {
2518 /* we found our first solved grid; copy it away. */
2519 assert(!outgrid);
2520 outgrid = snewn(gsz, space);
2521 memcpy(outgrid, state->grid, gsz * sizeof(space));
2522 }
2523 /* reset cell back to unassociated. */
2524 bestopp = tile_opposite(state, rctx.best);
2525 assert(bestopp && bestopp->flags & F_TILE_ASSOC);
2526
2527 remove_assoc(state, rctx.best);
2528 remove_assoc(state, bestopp);
2529
2530 if (ret == DIFF_AMBIGUOUS || ret == DIFF_UNFINISHED)
2531 diff = ret;
2532 else if (ret == DIFF_IMPOSSIBLE)
2533 /* no change */;
2534 else {
2535 /* precisely one solution */
2536 if (diff == DIFF_IMPOSSIBLE)
2537 diff = DIFF_UNREASONABLE;
2538 else
2539 diff = DIFF_AMBIGUOUS;
2540 }
2541 /* if we've found >1 solution, or ran out of recursion,
2542 * give up immediately. */
2543 if (diff == DIFF_AMBIGUOUS || diff == DIFF_UNFINISHED)
2544 break;
2545 }
2546
2547 if (outgrid) {
2548 /* we found (at least one) soln; copy it back to state */
2549 memcpy(state->grid, outgrid, gsz * sizeof(space));
2550 sfree(outgrid);
2551 }
2552 sfree(ingrid);
2553 return diff;
2554}
2555
2556static int solver_state_inner(game_state *state, int maxdiff, int depth)
2557{
2558 solver_ctx *sctx = new_solver(state);
2559 int ret, diff = DIFF_NORMAL;
2560
2561#ifdef STANDALONE_PICTURE_GENERATOR
2562 /* hack, hack: set picture to NULL during solving so that add_assoc
2563 * won't complain when we attempt recursive guessing and guess wrong */
2564 int *savepic = picture;
2565 picture = NULL;
2566#endif
2567
2568#ifdef STATIC_RECURSION_DEPTH
2569 solver_recurse_depth = depth;
2570#endif
2571
2572 ret = solver_obvious(state);
2573 if (ret < 0) {
2574 diff = DIFF_IMPOSSIBLE;
2575 goto got_result;
2576 }
2577
2578#define CHECKRET(d) do { \
2579 if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } \
2580 if (ret > 0) { diff = max(diff, (d)); goto cont; } \
2581} while(0)
2582
2583 while (1) {
2584cont:
2585 ret = foreach_edge(state, solver_lines_opposite_cb,
2586 IMPOSSIBLE_QUITS, sctx);
2587 CHECKRET(DIFF_NORMAL);
2588
2589 ret = foreach_tile(state, solver_spaces_oneposs_cb,
2590 IMPOSSIBLE_QUITS, sctx);
2591 CHECKRET(DIFF_NORMAL);
2592
2593 ret = solver_expand_dots(state, sctx);
2594 CHECKRET(DIFF_NORMAL);
2595
2596 ret = solver_extend_exclaves(state, sctx);
2597 CHECKRET(DIFF_NORMAL);
2598
2599 if (maxdiff <= DIFF_NORMAL)
2600 break;
2601
2602 /* harder still? */
2603
2604 /* if we reach here, we've made no deductions, so we terminate. */
2605 break;
2606 }
2607
2608 if (check_complete(state, NULL, NULL)) goto got_result;
2609
2610 diff = (maxdiff >= DIFF_UNREASONABLE) ?
2611 solver_recurse(state, maxdiff, depth) : DIFF_UNFINISHED;
2612
2613got_result:
2614 free_solver(sctx);
2615#ifndef STANDALONE_SOLVER
2616 debug(("solver_state ends, diff %s:\n", galaxies_diffnames[diff]));
2617 dbg_state(state);
2618#endif
2619
2620#ifdef STANDALONE_PICTURE_GENERATOR
2621 picture = savepic;
2622#endif
2623
2624 return diff;
2625}
2626
2627static int solver_state(game_state *state, int maxdiff)
2628{
2629 return solver_state_inner(state, maxdiff, 0);
2630}
2631
2632#ifndef EDITOR
2633static char *solve_game(const game_state *state, const game_state *currstate,
2634 const char *aux, const char **error)
2635{
2636 game_state *tosolve;
2637 char *ret;
2638 int i;
2639 int diff;
2640
2641 if (aux) {
2642 tosolve = execute_move(state, aux);
2643 goto solved;
2644 } else {
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);
2652
2653 tosolve = dup_game(state);
2654 diff = solver_state(tosolve, DIFF_UNREASONABLE);
2655 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2656 debug(("solve_game solved with original state.\n"));
2657 goto solved;
2658 }
2659 free_game(tosolve);
2660 }
2661
2662 return NULL;
2663
2664solved:
2665 /*
2666 * Clear tile associations: the solution will only include the
2667 * edges.
2668 */
2669 for (i = 0; i < tosolve->sx*tosolve->sy; i++)
2670 tosolve->grid[i].flags &= ~F_TILE_ASSOC;
2671 ret = diff_game(currstate, tosolve, true, -1);
2672 free_game(tosolve);
2673 return ret;
2674}
2675#endif
2676
2677/* ----------------------------------------------------------
2678 * User interface.
2679 */
2680
2681struct game_ui {
2682 bool dragging;
2683 int dx, dy; /* pixel coords of drag pos. */
2684 int dotx, doty; /* grid coords of dot we're dragging from. */
2685 int srcx, srcy; /* grid coords of drag start */
2686 int cur_x, cur_y;
2687 bool cur_visible;
2688};
2689
2690static game_ui *new_ui(const game_state *state)
2691{
2692 game_ui *ui = snew(game_ui);
2693 ui->dragging = false;
2694 ui->cur_x = ui->cur_y = 1;
2695 ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
2696 return ui;
2697}
2698
2699static void free_ui(game_ui *ui)
2700{
2701 sfree(ui);
2702}
2703
2704static void game_changed_state(game_ui *ui, const game_state *oldstate,
2705 const game_state *newstate)
2706{
2707}
2708
2709#define FLASH_TIME 0.15F
2710
2711#define PREFERRED_TILE_SIZE 32
2712#define TILE_SIZE (ds->tilesize)
2713#define DOT_SIZE (TILE_SIZE / 4)
2714#define EDGE_THICKNESS (max(TILE_SIZE / 16, 2))
2715#define BORDER TILE_SIZE
2716
2717#define COORD(x) ( (x) * TILE_SIZE + BORDER )
2718#define SCOORD(x) ( ((x) * TILE_SIZE)/2 + BORDER )
2719#define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE )
2720
2721#define DRAW_WIDTH (BORDER * 2 + ds->w * TILE_SIZE)
2722#define DRAW_HEIGHT (BORDER * 2 + ds->h * TILE_SIZE)
2723
2724#define CURSOR_SIZE DOT_SIZE
2725
2726struct game_drawstate {
2727 bool started;
2728 int w, h;
2729 int tilesize;
2730 unsigned long *grid;
2731 int *dx, *dy;
2732 blitter *bl;
2733 blitter *blmirror;
2734
2735 bool dragging;
2736 int dragx, dragy, oppx, oppy;
2737
2738 int *colour_scratch;
2739
2740 int cx, cy;
2741 bool cur_visible;
2742 blitter *cur_bl;
2743};
2744
2745#define CORNER_TOLERANCE 0.15F
2746#define CENTRE_TOLERANCE 0.15F
2747
2748/*
2749 * Round FP coordinates to the centre of the nearest edge.
2750 */
2751#ifndef EDITOR
2752static void coord_round_to_edge(float x, float y, int *xr, int *yr)
2753{
2754 float xs, ys, xv, yv, dx, dy;
2755
2756 /*
2757 * Find the nearest square-centre.
2758 */
2759 xs = (float)floor(x) + 0.5F;
2760 ys = (float)floor(y) + 0.5F;
2761
2762 /*
2763 * Find the nearest grid vertex.
2764 */
2765 xv = (float)floor(x + 0.5F);
2766 yv = (float)floor(y + 0.5F);
2767
2768 /*
2769 * Determine whether the horizontal or vertical edge from that
2770 * vertex alongside that square is closer to us, by comparing
2771 * distances from the square cente.
2772 */
2773 dx = (float)fabs(x - xs);
2774 dy = (float)fabs(y - ys);
2775 if (dx > dy) {
2776 /* Vertical edge: x-coord of corner,
2777 * y-coord of square centre. */
2778 *xr = 2 * (int)xv;
2779 *yr = 1 + 2 * (int)floor(ys);
2780 } else {
2781 /* Horizontal edge: x-coord of square centre,
2782 * y-coord of corner. */
2783 *xr = 1 + 2 * (int)floor(xs);
2784 *yr = 2 * (int)yv;
2785 }
2786}
2787#endif
2788
2789#ifdef EDITOR
2790static char *interpret_move(const game_state *state, game_ui *ui,
2791 const game_drawstate *ds,
2792 int x, int y, int button)
2793{
2794 char buf[80];
2795 int px, py;
2796 space *sp;
2797
2798 px = 2*FROMCOORD((float)x) + 0.5F;
2799 py = 2*FROMCOORD((float)y) + 0.5F;
2800
2801 if (button == 'C' || button == 'c') return dupstr("C");
2802
2803 if (button == 'S' || button == 's') {
2804 char *ret;
2805 game_state *tmp = dup_game(state);
2806 int cdiff = solver_state(tmp, DIFF_UNREASONABLE-1);
2807 ret = diff_game(state, tmp, 0, cdiff);
2808 free_game(tmp);
2809 return ret;
2810 }
2811
2812 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2813 if (!INUI(state, px, py)) return MOVE_UNUSED;
2814 sp = &SPACE(state, px, py);
2815 if (!dot_is_possible(state, sp, 1)) return MOVE_NO_EFFECT;
2816 sprintf(buf, "%c%d,%d",
2817 (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py);
2818 return dupstr(buf);
2819 }
2820
2821 return MOVE_UNUSED;
2822}
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
2866static char *interpret_move(const game_state *state, game_ui *ui,
2867 const game_drawstate *ds,
2868 int x, int y, int button)
2869{
2870 /* UI operations (play mode):
2871 *
2872 * Toggle edge (set/unset) (left-click on edge)
2873 * Associate space with dot (left-drag from dot)
2874 * Unassociate space (left-drag from space off grid)
2875 * Autofill lines around shape? (right-click?)
2876 *
2877 * (edit mode; will clear all lines/associations)
2878 *
2879 * Add or remove dot (left-click)
2880 */
2881 char buf[80];
2882 const char *sep = "";
2883 int px, py;
2884 space *sp, *dot;
2885
2886 buf[0] = '\0';
2887
2888 if (button == 'H' || button == 'h') {
2889 char *ret;
2890 game_state *tmp = dup_game(state);
2891 solver_obvious(tmp);
2892 ret = diff_game(state, tmp, false, -1);
2893 free_game(tmp);
2894 return ret;
2895 }
2896
2897 if (button == LEFT_BUTTON) {
2898 ui->cur_visible = false;
2899 coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y),
2900 &px, &py);
2901
2902 if (!INUI(state, px, py)) return MOVE_UNUSED;
2903 if (!edge_placement_legal(state, px, py))
2904 return MOVE_NO_EFFECT;
2905
2906 sprintf(buf, "E%d,%d", px, py);
2907 return dupstr(buf);
2908 } else if (button == RIGHT_BUTTON) {
2909 int px1, py1;
2910
2911 ui->cur_visible = false;
2912
2913 px = (int)(2*FROMCOORD((float)x) + 0.5F);
2914 py = (int)(2*FROMCOORD((float)y) + 0.5F);
2915
2916 dot = NULL;
2917
2918 /*
2919 * If there's a dot anywhere nearby, we pick up an arrow
2920 * pointing at that dot.
2921 */
2922 for (py1 = py-1; py1 <= py+1; py1++)
2923 for (px1 = px-1; px1 <= px+1; px1++) {
2924 if (px1 >= 0 && px1 < state->sx &&
2925 py1 >= 0 && py1 < state->sy &&
2926 x >= SCOORD(px1-1) && x < SCOORD(px1+1) &&
2927 y >= SCOORD(py1-1) && y < SCOORD(py1+1) &&
2928 SPACE(state, px1, py1).flags & F_DOT) {
2929 /*
2930 * Found a dot. Begin a drag from it.
2931 */
2932 dot = &SPACE(state, px1, py1);
2933 ui->srcx = px1;
2934 ui->srcy = py1;
2935 goto done; /* multi-level break */
2936 }
2937 }
2938
2939 /*
2940 * Otherwise, find the nearest _square_, and pick up the
2941 * same arrow as it's got on it, if any.
2942 */
2943 if (!dot) {
2944 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2945 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2946 if (px >= 0 && px < state->sx && py >= 0 && py < state->sy) {
2947 sp = &SPACE(state, px, py);
2948 if (sp->flags & F_TILE_ASSOC) {
2949 dot = &SPACE(state, sp->dotx, sp->doty);
2950 ui->srcx = px;
2951 ui->srcy = py;
2952 }
2953 }
2954 }
2955
2956 done:
2957 /*
2958 * Now, if we've managed to find a dot, begin a drag.
2959 */
2960 if (dot) {
2961 ui->dragging = true;
2962 ui->dx = x;
2963 ui->dy = y;
2964 ui->dotx = dot->x;
2965 ui->doty = dot->y;
2966 return MOVE_UI_UPDATE;
2967 }
2968 return MOVE_NO_EFFECT;
2969 } else if (button == RIGHT_DRAG && ui->dragging) {
2970 /* just move the drag coords. */
2971 ui->dx = x;
2972 ui->dy = y;
2973 return MOVE_UI_UPDATE;
2974 } else if (button == RIGHT_RELEASE && ui->dragging) {
2975 /*
2976 * Drags are always targeted at a single square.
2977 */
2978 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2979 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2980
2981 dropped: /* We arrive here from the end of a keyboard drag. */
2982 ui->dragging = false;
2983 /*
2984 * Dragging an arrow on to the same square it started from
2985 * is a null move; just update the ui and finish.
2986 */
2987 if (px == ui->srcx && py == ui->srcy)
2988 return MOVE_UI_UPDATE;
2989
2990 /*
2991 * Otherwise, we remove the arrow from its starting
2992 * square if we didn't start from a dot...
2993 */
2994 if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) &&
2995 SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) {
2996 sprintf(buf + strlen(buf), "%sU%d,%d", sep, ui->srcx, ui->srcy);
2997 sep = ";";
2998 }
2999
3000 /*
3001 * ... and if the square we're moving it _to_ is valid, we
3002 * add one there instead.
3003 */
3004 if (INUI(state, px, py)) {
3005 sp = &SPACE(state, px, py);
3006 dot = &SPACE(state, ui->dotx, ui->doty);
3007
3008 /*
3009 * Exception: if it's not actually legal to add an arrow
3010 * and its opposite at this position, we don't try,
3011 * because otherwise we'd append an empty entry to the
3012 * undo chain.
3013 */
3014 if (ok_to_add_assoc_with_opposite(state, sp, dot))
3015 sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
3016 sep, px, py, ui->dotx, ui->doty);
3017 }
3018
3019 if (buf[0])
3020 return dupstr(buf);
3021 else
3022 return MOVE_UI_UPDATE;
3023 } else if (IS_CURSOR_MOVE(button)) {
3024 int cx = ui->cur_x - 1, cy = ui->cur_y - 1;
3025 char *ret = move_cursor(button, &cx, &cy, state->sx-2, state->sy-2,
3026 false, &ui->cur_visible);
3027 ui->cur_x = cx + 1, ui->cur_y = cy + 1;
3028 if (ui->dragging) {
3029 ui->dx = SCOORD(ui->cur_x);
3030 ui->dy = SCOORD(ui->cur_y);
3031 }
3032 return ret;
3033 } else if (IS_CURSOR_SELECT(button)) {
3034 if (!ui->cur_visible) {
3035 ui->cur_visible = true;
3036 return MOVE_UI_UPDATE;
3037 }
3038 sp = &SPACE(state, ui->cur_x, ui->cur_y);
3039 if (ui->dragging) {
3040 px = ui->cur_x; py = ui->cur_y;
3041 goto dropped;
3042 } else if (sp->flags & F_DOT) {
3043 ui->dragging = true;
3044 ui->dx = SCOORD(ui->cur_x);
3045 ui->dy = SCOORD(ui->cur_y);
3046 ui->dotx = ui->srcx = ui->cur_x;
3047 ui->doty = ui->srcy = ui->cur_y;
3048 return MOVE_UI_UPDATE;
3049 } else if (sp->flags & F_TILE_ASSOC) {
3050 assert(sp->type == s_tile);
3051 ui->dragging = true;
3052 ui->dx = SCOORD(ui->cur_x);
3053 ui->dy = SCOORD(ui->cur_y);
3054 ui->dotx = sp->dotx;
3055 ui->doty = sp->doty;
3056 ui->srcx = ui->cur_x;
3057 ui->srcy = ui->cur_y;
3058 return MOVE_UI_UPDATE;
3059 } else if (sp->type == s_edge &&
3060 edge_placement_legal(state, ui->cur_x, ui->cur_y)) {
3061 sprintf(buf, "E%d,%d", ui->cur_x, ui->cur_y);
3062 return dupstr(buf);
3063 }
3064 }
3065
3066 return MOVE_UNUSED;
3067}
3068#endif
3069
3070static bool check_complete(const game_state *state, DSF *dsf, int *colours)
3071{
3072 int w = state->w, h = state->h;
3073 int x, y, i;
3074 bool ret;
3075
3076 bool free_dsf;
3077 struct sqdata {
3078 int minx, miny, maxx, maxy;
3079 int cx, cy;
3080 bool valid;
3081 int colour;
3082 } *sqdata;
3083
3084 if (!dsf) {
3085 dsf = dsf_new(w*h);
3086 free_dsf = true;
3087 } else {
3088 dsf_reinit(dsf);
3089 free_dsf = false;
3090 }
3091
3092 /*
3093 * During actual game play, completion checking is done on the
3094 * basis of the edges rather than the square associations. So
3095 * first we must go through the grid figuring out the connected
3096 * components into which the edges divide it.
3097 */
3098 for (y = 0; y < h; y++)
3099 for (x = 0; x < w; x++) {
3100 if (y+1 < h && !(SPACE(state, 2*x+1, 2*y+2).flags & F_EDGE_SET))
3101 dsf_merge(dsf, y*w+x, (y+1)*w+x);
3102 if (x+1 < w && !(SPACE(state, 2*x+2, 2*y+1).flags & F_EDGE_SET))
3103 dsf_merge(dsf, y*w+x, y*w+(x+1));
3104 }
3105
3106 /*
3107 * That gives us our connected components. Now, for each
3108 * component, decide whether it's _valid_. A valid component is
3109 * one which:
3110 *
3111 * - is 180-degree rotationally symmetric
3112 * - has a dot at its centre of symmetry
3113 * - has no other dots anywhere within it (including on its
3114 * boundary)
3115 * - contains no internal edges (i.e. edges separating two
3116 * squares which are both part of the component).
3117 */
3118
3119 /*
3120 * First, go through the grid finding the bounding box of each
3121 * component.
3122 */
3123 sqdata = snewn(w*h, struct sqdata);
3124 for (i = 0; i < w*h; i++) {
3125 sqdata[i].minx = w+1;
3126 sqdata[i].miny = h+1;
3127 sqdata[i].maxx = sqdata[i].maxy = -1;
3128 sqdata[i].valid = false;
3129 }
3130 for (y = 0; y < h; y++)
3131 for (x = 0; x < w; x++) {
3132 i = dsf_canonify(dsf, y*w+x);
3133 if (sqdata[i].minx > x)
3134 sqdata[i].minx = x;
3135 if (sqdata[i].maxx < x)
3136 sqdata[i].maxx = x;
3137 if (sqdata[i].miny > y)
3138 sqdata[i].miny = y;
3139 if (sqdata[i].maxy < y)
3140 sqdata[i].maxy = y;
3141 sqdata[i].valid = true;
3142 }
3143
3144 /*
3145 * Now we're in a position to loop over each actual component
3146 * and figure out where its centre of symmetry has to be if
3147 * it's anywhere.
3148 */
3149 for (i = 0; i < w*h; i++)
3150 if (sqdata[i].valid) {
3151 int cx, cy;
3152 cx = sqdata[i].cx = sqdata[i].minx + sqdata[i].maxx + 1;
3153 cy = sqdata[i].cy = sqdata[i].miny + sqdata[i].maxy + 1;
3154 if (!(SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT))
3155 sqdata[i].valid = false; /* no dot at centre of symmetry */
3156 if (dsf_canonify(dsf, (cy-1)/2*w+(cx-1)/2) != i ||
3157 dsf_canonify(dsf, (cy)/2*w+(cx-1)/2) != i ||
3158 dsf_canonify(dsf, (cy-1)/2*w+(cx)/2) != i ||
3159 dsf_canonify(dsf, (cy)/2*w+(cx)/2) != i)
3160 sqdata[i].valid = false; /* dot at cx,cy isn't ours */
3161 if (SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT_BLACK)
3162 sqdata[i].colour = 2;
3163 else
3164 sqdata[i].colour = 1;
3165 }
3166
3167 /*
3168 * Now we loop over the whole grid again, this time finding
3169 * extraneous dots (any dot which wholly or partially overlaps
3170 * a square and is not at the centre of symmetry of that
3171 * square's component disqualifies the component from validity)
3172 * and extraneous edges (any edge separating two squares
3173 * belonging to the same component also disqualifies that
3174 * component).
3175 */
3176 for (y = 1; y < state->sy-1; y++)
3177 for (x = 1; x < state->sx-1; x++) {
3178 space *sp = &SPACE(state, x, y);
3179
3180 if (sp->flags & F_DOT) {
3181 /*
3182 * There's a dot here. Use it to disqualify any
3183 * component which deserves it.
3184 */
3185 int cx, cy;
3186 for (cy = (y-1) >> 1; cy <= y >> 1; cy++)
3187 for (cx = (x-1) >> 1; cx <= x >> 1; cx++) {
3188 i = dsf_canonify(dsf, cy*w+cx);
3189 if (x != sqdata[i].cx || y != sqdata[i].cy)
3190 sqdata[i].valid = false;
3191 }
3192 }
3193
3194 if (sp->flags & F_EDGE_SET) {
3195 /*
3196 * There's an edge here. Use it to disqualify a
3197 * component if necessary.
3198 */
3199 int cx1 = (x-1) >> 1, cx2 = x >> 1;
3200 int cy1 = (y-1) >> 1, cy2 = y >> 1;
3201 assert((cx1==cx2) ^ (cy1==cy2));
3202 i = dsf_canonify(dsf, cy1*w+cx1);
3203 if (i == dsf_canonify(dsf, cy2*w+cx2))
3204 sqdata[i].valid = false;
3205 }
3206 }
3207
3208 /*
3209 * And finally we test rotational symmetry: for each square in
3210 * the grid, find which component it's in, test that that
3211 * component also has a square in the symmetric position, and
3212 * disqualify it if it doesn't.
3213 */
3214 for (y = 0; y < h; y++)
3215 for (x = 0; x < w; x++) {
3216 int x2, y2;
3217
3218 i = dsf_canonify(dsf, y*w+x);
3219
3220 x2 = sqdata[i].cx - 1 - x;
3221 y2 = sqdata[i].cy - 1 - y;
3222 if (i != dsf_canonify(dsf, y2*w+x2))
3223 sqdata[i].valid = false;
3224 }
3225
3226 /*
3227 * That's it. We now have all the connected components marked
3228 * as valid or not valid. So now we return a `colours' array if
3229 * we were asked for one, and also we return an overall
3230 * true/false value depending on whether _every_ square in the
3231 * grid is part of a valid component.
3232 */
3233 ret = true;
3234 for (i = 0; i < w*h; i++) {
3235 int ci = dsf_canonify(dsf, i);
3236 bool thisok = sqdata[ci].valid;
3237 if (colours)
3238 colours[i] = thisok ? sqdata[ci].colour : 0;
3239 ret = ret && thisok;
3240 }
3241
3242 sfree(sqdata);
3243 if (free_dsf)
3244 dsf_free(dsf);
3245
3246 return ret;
3247}
3248
3249static game_state *execute_move(const game_state *state, const char *move)
3250{
3251 int x, y, ax, ay, n, dx, dy;
3252 game_state *ret = dup_game(state);
3253 space *sp, *dot;
3254 bool currently_solving = false;
3255
3256 debug(("%s\n", move));
3257
3258 while (*move) {
3259 char c = *move;
3260 if (c == 'E' || c == 'U' || c == 'M'
3261#ifdef EDITOR
3262 || c == 'D' || c == 'd'
3263#endif
3264 ) {
3265 move++;
3266 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
3267 !INUI(ret, x, y))
3268 goto badmove;
3269
3270 sp = &SPACE(ret, x, y);
3271#ifdef EDITOR
3272 if (c == 'D' || c == 'd') {
3273 unsigned int currf, newf, maskf;
3274
3275 if (!dot_is_possible(ret, sp, 1)) goto badmove;
3276
3277 newf = F_DOT | (c == 'd' ? F_DOT_BLACK : 0);
3278 currf = GRID(ret, grid, x, y).flags;
3279 maskf = F_DOT | F_DOT_BLACK;
3280 /* if we clicked 'white dot':
3281 * white --> empty, empty --> white, black --> white.
3282 * if we clicked 'black dot':
3283 * black --> empty, empty --> black, white --> black.
3284 */
3285 if (currf & maskf) {
3286 sp->flags &= ~maskf;
3287 if ((currf & maskf) != newf)
3288 sp->flags |= newf;
3289 } else
3290 sp->flags |= newf;
3291 sp->nassoc = 0; /* edit-mode disallows associations. */
3292 game_update_dots(ret);
3293 } else
3294#endif
3295 if (c == 'E') {
3296 if (sp->type != s_edge) goto badmove;
3297 sp->flags ^= F_EDGE_SET;
3298 } else if (c == 'U') {
3299 if (sp->type != s_tile || !(sp->flags & F_TILE_ASSOC))
3300 goto badmove;
3301 /* The solver doesn't assume we'll mirror things */
3302 if (currently_solving)
3303 remove_assoc(ret, sp);
3304 else
3305 remove_assoc_with_opposite(ret, sp);
3306 } else if (c == 'M') {
3307 if (!(sp->flags & F_DOT)) goto badmove;
3308 sp->flags ^= F_DOT_HOLD;
3309 }
3310 move += n;
3311 } else if (c == 'A' || c == 'a') {
3312 move++;
3313 if (sscanf(move, "%d,%d,%d,%d%n", &x, &y, &ax, &ay, &n) != 4 ||
3314 x < 1 || y < 1 || x >= (ret->sx-1) || y >= (ret->sy-1) ||
3315 ax < 1 || ay < 1 || ax >= (ret->sx-1) || ay >= (ret->sy-1))
3316 goto badmove;
3317
3318 dot = &GRID(ret, grid, ax, ay);
3319 if (!(dot->flags & F_DOT))goto badmove;
3320 if (dot->flags & F_DOT_HOLD) goto badmove;
3321
3322 for (dx = -1; dx <= 1; dx++) {
3323 for (dy = -1; dy <= 1; dy++) {
3324 sp = &GRID(ret, grid, x+dx, y+dy);
3325 if (sp->type != s_tile) continue;
3326 if (sp->flags & F_TILE_ASSOC) {
3327 space *dot = &SPACE(ret, sp->dotx, sp->doty);
3328 if (dot->flags & F_DOT_HOLD) continue;
3329 }
3330 /* The solver doesn't assume we'll mirror things */
3331 if (currently_solving)
3332 add_assoc(ret, sp, dot);
3333 else
3334 add_assoc_with_opposite(ret, sp, dot);
3335 }
3336 }
3337 move += n;
3338#ifdef EDITOR
3339 } else if (c == 'C') {
3340 move++;
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++;
3371#endif
3372 } else if (c == 'S') {
3373 move++;
3374 ret->used_solve = true;
3375 currently_solving = true;
3376 } else
3377 goto badmove;
3378
3379 if (*move == ';')
3380 move++;
3381 else if (*move)
3382 goto badmove;
3383 }
3384 if (check_complete(ret, NULL, NULL))
3385 ret->completed = true;
3386 return ret;
3387
3388badmove:
3389 free_game(ret);
3390 return NULL;
3391}
3392
3393/* ----------------------------------------------------------------------
3394 * Drawing routines.
3395 */
3396
3397/* Lines will be much smaller size than squares; say, 1/8 the size?
3398 *
3399 * Need a 'top-left corner of location XxY' to take this into account;
3400 * alternaticaly, that could give the middle of that location, and the
3401 * drawing code would just know the expected dimensions.
3402 *
3403 * We also need something to take a click and work out what it was
3404 * we were interested in. Clicking on vertices is required because
3405 * we may want to drag from them, for example.
3406 */
3407
3408static void game_compute_size(const game_params *params, int sz,
3409 const game_ui *ui, int *x, int *y)
3410{
3411 struct { int tilesize, w, h; } ads, *ds = &ads;
3412
3413 ds->tilesize = sz;
3414 ds->w = params->w;
3415 ds->h = params->h;
3416
3417 *x = DRAW_WIDTH;
3418 *y = DRAW_HEIGHT;
3419}
3420
3421static void game_set_size(drawing *dr, game_drawstate *ds,
3422 const game_params *params, int sz)
3423{
3424 ds->tilesize = sz;
3425
3426 assert(TILE_SIZE > 0);
3427
3428 assert(!ds->bl);
3429 ds->bl = blitter_new(dr, TILE_SIZE, TILE_SIZE);
3430
3431 assert(!ds->blmirror);
3432 ds->blmirror = blitter_new(dr, TILE_SIZE, TILE_SIZE);
3433
3434 assert(!ds->cur_bl);
3435 ds->cur_bl = blitter_new(dr, TILE_SIZE, TILE_SIZE);
3436}
3437
3438static float *game_colours(frontend *fe, int *ncolours)
3439{
3440 float *ret = snewn(3 * NCOLOURS, float);
3441 int i;
3442
3443 /*
3444 * We call game_mkhighlight to ensure the background colour
3445 * isn't completely white. We don't actually use the high- and
3446 * lowlight colours it generates.
3447 */
3448 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_WHITEBG, COL_BLACKBG);
3449
3450 for (i = 0; i < 3; i++) {
3451 /*
3452 * Currently, white dots and white-background squares are
3453 * both pure white.
3454 */
3455 ret[COL_WHITEDOT * 3 + i] = 1.0F;
3456 ret[COL_WHITEBG * 3 + i] = 1.0F;
3457
3458 /*
3459 * But black-background squares are a dark grey, whereas
3460 * black dots are really black.
3461 */
3462 ret[COL_BLACKDOT * 3 + i] = 0.0F;
3463 ret[COL_BLACKBG * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.3F;
3464
3465 /*
3466 * In unfilled squares, we draw a faint gridwork.
3467 */
3468 ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.8F;
3469
3470 /*
3471 * Edges and arrows are filled in in pure black.
3472 */
3473 ret[COL_EDGE * 3 + i] = 0.0F;
3474 ret[COL_ARROW * 3 + i] = 0.0F;
3475 }
3476
3477#ifdef EDITOR
3478 /* tinge the edit background to bluey */
3479 ret[COL_BACKGROUND * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3480 ret[COL_BACKGROUND * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3481 ret[COL_BACKGROUND * 3 + 2] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F);
3482#endif
3483
3484 ret[COL_CURSOR * 3 + 0] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F);
3485 ret[COL_CURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3486 ret[COL_CURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
3487
3488 *ncolours = NCOLOURS;
3489 return ret;
3490}
3491
3492static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
3493{
3494 struct game_drawstate *ds = snew(struct game_drawstate);
3495 int i;
3496
3497 ds->started = false;
3498 ds->w = state->w;
3499 ds->h = state->h;
3500
3501 ds->grid = snewn(ds->w*ds->h, unsigned long);
3502 for (i = 0; i < ds->w*ds->h; i++)
3503 ds->grid[i] = 0xFFFFFFFFUL;
3504 ds->dx = snewn(ds->w*ds->h, int);
3505 ds->dy = snewn(ds->w*ds->h, int);
3506
3507 ds->bl = NULL;
3508 ds->blmirror = NULL;
3509 ds->dragging = false;
3510 ds->dragx = ds->dragy = ds->oppx = ds->oppy = 0;
3511
3512 ds->colour_scratch = snewn(ds->w * ds->h, int);
3513
3514 ds->cur_bl = NULL;
3515 ds->cx = ds->cy = 0;
3516 ds->cur_visible = false;
3517
3518 return ds;
3519}
3520
3521static void game_free_drawstate(drawing *dr, game_drawstate *ds)
3522{
3523 if (ds->cur_bl) blitter_free(dr, ds->cur_bl);
3524 sfree(ds->colour_scratch);
3525 if (ds->blmirror) blitter_free(dr, ds->blmirror);
3526 if (ds->bl) blitter_free(dr, ds->bl);
3527 sfree(ds->dx);
3528 sfree(ds->dy);
3529 sfree(ds->grid);
3530 sfree(ds);
3531}
3532
3533#define DRAW_EDGE_L 0x0001
3534#define DRAW_EDGE_R 0x0002
3535#define DRAW_EDGE_U 0x0004
3536#define DRAW_EDGE_D 0x0008
3537#define DRAW_CORNER_UL 0x0010
3538#define DRAW_CORNER_UR 0x0020
3539#define DRAW_CORNER_DL 0x0040
3540#define DRAW_CORNER_DR 0x0080
3541#define DRAW_WHITE 0x0100
3542#define DRAW_BLACK 0x0200
3543#define DRAW_ARROW 0x0400
3544#define DRAW_CURSOR 0x0800
3545#define DOT_SHIFT_C 12
3546#define DOT_SHIFT_M 2
3547#define DOT_WHITE 1UL
3548#define DOT_BLACK 2UL
3549
3550/*
3551 * Draw an arrow centred on (cx,cy), pointing in the direction
3552 * (ddx,ddy). (I.e. pointing at the point (cx+ddx, cy+ddy).
3553 */
3554static void draw_arrow(drawing *dr, game_drawstate *ds,
3555 int cx, int cy, int ddx, int ddy, int col)
3556{
3557 int sqdist = ddx*ddx+ddy*ddy;
3558 if (sqdist == 0)
3559 return; /* avoid division by zero */
3560 float vlen = (float)sqrt(sqdist);
3561 float xdx = ddx/vlen, xdy = ddy/vlen;
3562 float ydx = -xdy, ydy = xdx;
3563 int e1x = cx + (int)(xdx*TILE_SIZE/3), e1y = cy + (int)(xdy*TILE_SIZE/3);
3564 int e2x = cx - (int)(xdx*TILE_SIZE/3), e2y = cy - (int)(xdy*TILE_SIZE/3);
3565 int adx = (int)((ydx-xdx)*TILE_SIZE/8), ady = (int)((ydy-xdy)*TILE_SIZE/8);
3566 int adx2 = (int)((-ydx-xdx)*TILE_SIZE/8), ady2 = (int)((-ydy-xdy)*TILE_SIZE/8);
3567
3568 draw_line(dr, e1x, e1y, e2x, e2y, col);
3569 draw_line(dr, e1x, e1y, e1x+adx, e1y+ady, col);
3570 draw_line(dr, e1x, e1y, e1x+adx2, e1y+ady2, col);
3571}
3572
3573static void draw_square(drawing *dr, game_drawstate *ds, int x, int y,
3574 unsigned long flags, int ddx, int ddy)
3575{
3576 int lx = COORD(x), ly = COORD(y);
3577 int dx, dy;
3578 int gridcol;
3579
3580 clip(dr, lx, ly, TILE_SIZE, TILE_SIZE);
3581
3582 /*
3583 * Draw the tile background.
3584 */
3585 draw_rect(dr, lx, ly, TILE_SIZE, TILE_SIZE,
3586 (flags & DRAW_WHITE ? COL_WHITEBG :
3587 flags & DRAW_BLACK ? COL_BLACKBG : COL_BACKGROUND));
3588
3589 /*
3590 * Draw the grid.
3591 */
3592 gridcol = (flags & DRAW_BLACK ? COL_BLACKDOT : COL_GRID);
3593 draw_rect(dr, lx, ly, 1, TILE_SIZE, gridcol);
3594 draw_rect(dr, lx, ly, TILE_SIZE, 1, gridcol);
3595
3596 /*
3597 * Draw the arrow, if present, or the cursor, if here.
3598 */
3599 if (flags & DRAW_ARROW)
3600 draw_arrow(dr, ds, lx + TILE_SIZE/2, ly + TILE_SIZE/2, ddx, ddy,
3601 (flags & DRAW_CURSOR) ? COL_CURSOR : COL_ARROW);
3602 else if (flags & DRAW_CURSOR)
3603 draw_rect_outline(dr,
3604 lx + TILE_SIZE/2 - CURSOR_SIZE,
3605 ly + TILE_SIZE/2 - CURSOR_SIZE,
3606 2*CURSOR_SIZE+1, 2*CURSOR_SIZE+1,
3607 COL_CURSOR);
3608
3609 /*
3610 * Draw the edges.
3611 */
3612 if (flags & DRAW_EDGE_L)
3613 draw_rect(dr, lx, ly, EDGE_THICKNESS, TILE_SIZE, COL_EDGE);
3614 if (flags & DRAW_EDGE_R)
3615 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly,
3616 EDGE_THICKNESS - 1, TILE_SIZE, COL_EDGE);
3617 if (flags & DRAW_EDGE_U)
3618 draw_rect(dr, lx, ly, TILE_SIZE, EDGE_THICKNESS, COL_EDGE);
3619 if (flags & DRAW_EDGE_D)
3620 draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1,
3621 TILE_SIZE, EDGE_THICKNESS - 1, COL_EDGE);
3622 if (flags & DRAW_CORNER_UL)
3623 draw_rect(dr, lx, ly, EDGE_THICKNESS, EDGE_THICKNESS, COL_EDGE);
3624 if (flags & DRAW_CORNER_UR)
3625 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly,
3626 EDGE_THICKNESS - 1, EDGE_THICKNESS, COL_EDGE);
3627 if (flags & DRAW_CORNER_DL)
3628 draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1,
3629 EDGE_THICKNESS, EDGE_THICKNESS - 1, COL_EDGE);
3630 if (flags & DRAW_CORNER_DR)
3631 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1,
3632 ly + TILE_SIZE - EDGE_THICKNESS + 1,
3633 EDGE_THICKNESS - 1, EDGE_THICKNESS - 1, COL_EDGE);
3634
3635 /*
3636 * Draw the dots.
3637 */
3638 for (dy = 0; dy < 3; dy++)
3639 for (dx = 0; dx < 3; dx++) {
3640 int dotval = (flags >> (DOT_SHIFT_C + DOT_SHIFT_M*(dy*3+dx)));
3641 dotval &= (1 << DOT_SHIFT_M)-1;
3642
3643 if (dotval)
3644 draw_circle(dr, lx+dx*TILE_SIZE/2, ly+dy*TILE_SIZE/2,
3645 DOT_SIZE,
3646 (dotval == 1 ? COL_WHITEDOT : COL_BLACKDOT),
3647 COL_BLACKDOT);
3648 }
3649
3650 unclip(dr);
3651 draw_update(dr, lx, ly, TILE_SIZE, TILE_SIZE);
3652}
3653
3654static void calculate_opposite_point(const game_ui *ui,
3655 const game_drawstate *ds, const int x,
3656 const int y, int *oppositex,
3657 int *oppositey)
3658{
3659 /* oppositex - dotx = dotx - x <=> oppositex = 2 * dotx - x */
3660 *oppositex = 2 * SCOORD(ui->dotx) - x;
3661 *oppositey = 2 * SCOORD(ui->doty) - y;
3662}
3663
3664static void game_redraw(drawing *dr, game_drawstate *ds,
3665 const game_state *oldstate, const game_state *state,
3666 int dir, const game_ui *ui,
3667 float animtime, float flashtime)
3668{
3669 int w = ds->w, h = ds->h;
3670 int x, y;
3671 bool flashing = false;
3672
3673 if (flashtime > 0) {
3674 int frame = (int)(flashtime / FLASH_TIME);
3675 flashing = (frame % 2 == 0);
3676 }
3677
3678 if (ds->dragging) {
3679 assert(ds->bl);
3680 assert(ds->blmirror);
3681 blitter_load(dr, ds->blmirror, ds->oppx, ds->oppy);
3682 draw_update(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3683 blitter_load(dr, ds->bl, ds->dragx, ds->dragy);
3684 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3685 ds->dragging = false;
3686 }
3687 if (ds->cur_visible) {
3688 assert(ds->cur_bl);
3689 blitter_load(dr, ds->cur_bl, ds->cx, ds->cy);
3690 draw_update(dr, ds->cx, ds->cy, CURSOR_SIZE*2+1, CURSOR_SIZE*2+1);
3691 ds->cur_visible = false;
3692 }
3693
3694 if (!ds->started) {
3695 draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1,
3696 w*TILE_SIZE + EDGE_THICKNESS*2 - 1,
3697 h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE);
3698 draw_update(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT);
3699 ds->started = true;
3700 }
3701
3702 check_complete(state, NULL, ds->colour_scratch);
3703
3704 for (y = 0; y < h; y++)
3705 for (x = 0; x < w; x++) {
3706 unsigned long flags = 0;
3707 int ddx = 0, ddy = 0;
3708 space *sp, *opp;
3709 int dx, dy;
3710
3711 /*
3712 * Set up the flags for this square. Firstly, see if we
3713 * have edges.
3714 */
3715 if (SPACE(state, x*2, y*2+1).flags & F_EDGE_SET)
3716 flags |= DRAW_EDGE_L;
3717 if (SPACE(state, x*2+2, y*2+1).flags & F_EDGE_SET)
3718 flags |= DRAW_EDGE_R;
3719 if (SPACE(state, x*2+1, y*2).flags & F_EDGE_SET)
3720 flags |= DRAW_EDGE_U;
3721 if (SPACE(state, x*2+1, y*2+2).flags & F_EDGE_SET)
3722 flags |= DRAW_EDGE_D;
3723
3724 /*
3725 * Also, mark corners of neighbouring edges.
3726 */
3727 if ((x > 0 && SPACE(state, x*2-1, y*2).flags & F_EDGE_SET) ||
3728 (y > 0 && SPACE(state, x*2, y*2-1).flags & F_EDGE_SET))
3729 flags |= DRAW_CORNER_UL;
3730 if ((x+1 < w && SPACE(state, x*2+3, y*2).flags & F_EDGE_SET) ||
3731 (y > 0 && SPACE(state, x*2+2, y*2-1).flags & F_EDGE_SET))
3732 flags |= DRAW_CORNER_UR;
3733 if ((x > 0 && SPACE(state, x*2-1, y*2+2).flags & F_EDGE_SET) ||
3734 (y+1 < h && SPACE(state, x*2, y*2+3).flags & F_EDGE_SET))
3735 flags |= DRAW_CORNER_DL;
3736 if ((x+1 < w && SPACE(state, x*2+3, y*2+2).flags & F_EDGE_SET) ||
3737 (y+1 < h && SPACE(state, x*2+2, y*2+3).flags & F_EDGE_SET))
3738 flags |= DRAW_CORNER_DR;
3739
3740 /*
3741 * If this square is part of a valid region, paint it
3742 * that region's colour. Exception: if we're flashing,
3743 * everything goes briefly back to background colour.
3744 */
3745 sp = &SPACE(state, x*2+1, y*2+1);
3746 if (sp->flags & F_TILE_ASSOC) {
3747 opp = tile_opposite(state, sp);
3748 } else {
3749 opp = NULL;
3750 }
3751 if (ds->colour_scratch[y*w+x] && !flashing) {
3752 flags |= (ds->colour_scratch[y*w+x] == 2 ?
3753 DRAW_BLACK : DRAW_WHITE);
3754 }
3755
3756 /*
3757 * If this square is associated with a dot but it isn't
3758 * part of a valid region, draw an arrow in it pointing
3759 * in the direction of that dot.
3760 *
3761 * Exception: if this is the source point of an active
3762 * drag, we don't draw the arrow.
3763 */
3764 if ((sp->flags & F_TILE_ASSOC) && !ds->colour_scratch[y*w+x]) {
3765 if (ui->dragging && ui->srcx == x*2+1 && ui->srcy == y*2+1) {
3766 /* tile is the source, don't do it */
3767 } else if (ui->dragging && opp && ui->srcx == opp->x && ui->srcy == opp->y) {
3768 /* opposite tile is the source, don't do it */
3769 } else if (sp->doty != y*2+1 || sp->dotx != x*2+1) {
3770 flags |= DRAW_ARROW;
3771 ddy = sp->doty - (y*2+1);
3772 ddx = sp->dotx - (x*2+1);
3773 }
3774 }
3775
3776 /*
3777 * Now go through the nine possible places we could
3778 * have dots.
3779 */
3780 for (dy = 0; dy < 3; dy++)
3781 for (dx = 0; dx < 3; dx++) {
3782 sp = &SPACE(state, x*2+dx, y*2+dy);
3783 if (sp->flags & F_DOT) {
3784 unsigned long dotval = (sp->flags & F_DOT_BLACK ?
3785 DOT_BLACK : DOT_WHITE);
3786 flags |= dotval << (DOT_SHIFT_C +
3787 DOT_SHIFT_M*(dy*3+dx));
3788 }
3789 }
3790
3791 /*
3792 * Now work out if we have to draw a cursor for this square;
3793 * cursors-on-lines are taken care of below.
3794 */
3795 if (ui->cur_visible &&
3796 ui->cur_x == x*2+1 && ui->cur_y == y*2+1 &&
3797 !(SPACE(state, x*2+1, y*2+1).flags & F_DOT))
3798 flags |= DRAW_CURSOR;
3799
3800 /*
3801 * Now we have everything we're going to need. Draw the
3802 * square.
3803 */
3804 if (ds->grid[y*w+x] != flags ||
3805 ds->dx[y*w+x] != ddx ||
3806 ds->dy[y*w+x] != ddy) {
3807 draw_square(dr, ds, x, y, flags, ddx, ddy);
3808 ds->grid[y*w+x] = flags;
3809 ds->dx[y*w+x] = ddx;
3810 ds->dy[y*w+x] = ddy;
3811 }
3812 }
3813
3814 /*
3815 * Draw a cursor. This secondary blitter is much less invasive than trying
3816 * to fix up all of the rest of the code with sufficient flags to be able to
3817 * display this sensibly.
3818 */
3819 if (ui->cur_visible) {
3820 space *sp = &SPACE(state, ui->cur_x, ui->cur_y);
3821 ds->cur_visible = true;
3822 ds->cx = SCOORD(ui->cur_x) - CURSOR_SIZE;
3823 ds->cy = SCOORD(ui->cur_y) - CURSOR_SIZE;
3824 blitter_save(dr, ds->cur_bl, ds->cx, ds->cy);
3825 if (sp->flags & F_DOT) {
3826 /* draw a red dot (over the top of whatever would be there already) */
3827 draw_circle(dr, SCOORD(ui->cur_x), SCOORD(ui->cur_y), DOT_SIZE,
3828 COL_CURSOR, COL_BLACKDOT);
3829 } else if (sp->type != s_tile) {
3830 /* draw an edge/vertex square; tile cursors are dealt with above. */
3831 int dx = (ui->cur_x % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3832 int dy = (ui->cur_y % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3833 int x1 = SCOORD(ui->cur_x)-dx, y1 = SCOORD(ui->cur_y)-dy;
3834 int xs = dx*2+1, ys = dy*2+1;
3835
3836 draw_rect(dr, x1, y1, xs, ys, COL_CURSOR);
3837 }
3838 draw_update(dr, ds->cx, ds->cy, CURSOR_SIZE*2+1, CURSOR_SIZE*2+1);
3839 }
3840
3841 if (ui->dragging) {
3842 int oppx, oppy;
3843
3844 ds->dragging = true;
3845 ds->dragx = ui->dx - TILE_SIZE/2;
3846 ds->dragy = ui->dy - TILE_SIZE/2;
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
3851 blitter_save(dr, ds->bl, ds->dragx, ds->dragy);
3852 clip(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
3853 draw_arrow(dr, ds, ui->dx, ui->dy, SCOORD(ui->dotx) - ui->dx,
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);
3860 draw_arrow(dr, ds, oppx, oppy, SCOORD(ui->dotx) - oppx,
3861 SCOORD(ui->doty) - oppy, COL_ARROW);
3862 unclip(dr);
3863 draw_update(dr, ds->oppx, ds->oppy, TILE_SIZE, TILE_SIZE);
3864 }
3865#ifdef EDITOR
3866 {
3867 char buf[256];
3868 if (state->cdiff != -1)
3869 sprintf(buf, "Puzzle is %s.", galaxies_diffnames[state->cdiff]);
3870 else
3871 buf[0] = '\0';
3872 status_bar(dr, buf);
3873 }
3874#endif
3875}
3876
3877static float game_anim_length(const game_state *oldstate,
3878 const game_state *newstate, int dir, game_ui *ui)
3879{
3880 return 0.0F;
3881}
3882
3883static float game_flash_length(const game_state *oldstate,
3884 const game_state *newstate, int dir, game_ui *ui)
3885{
3886 if ((!oldstate->completed && newstate->completed) &&
3887 !(newstate->used_solve))
3888 return 3 * FLASH_TIME;
3889 else
3890 return 0.0F;
3891}
3892
3893static void game_get_cursor_location(const game_ui *ui,
3894 const game_drawstate *ds,
3895 const game_state *state,
3896 const game_params *params,
3897 int *x, int *y, int *w, int *h)
3898{
3899 if(ui->cur_visible) {
3900 space *sp = &SPACE(state, ui->cur_x, ui->cur_y);
3901
3902 if(sp->flags & F_DOT) {
3903 *x = SCOORD(ui->cur_x) - DOT_SIZE;
3904 *y = SCOORD(ui->cur_y) - DOT_SIZE;
3905 *w = *h = 2 * DOT_SIZE + 1;
3906 } else if(sp->type != s_tile) {
3907 int dx = (ui->cur_x % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3908 int dy = (ui->cur_y % 2) ? CURSOR_SIZE : CURSOR_SIZE/3;
3909 int x1 = SCOORD(ui->cur_x)-dx, y1 = SCOORD(ui->cur_y)-dy;
3910 int xs = dx*2+1, ys = dy*2+1;
3911
3912 *x = x1;
3913 *y = y1;
3914 *w = xs;
3915 *h = ys;
3916 } else {
3917 *x = SCOORD(ui->cur_x) - CURSOR_SIZE;
3918 *y = SCOORD(ui->cur_y) - CURSOR_SIZE;
3919 *w = *h = 2 * CURSOR_SIZE + 1;
3920 }
3921 }
3922}
3923
3924static int game_status(const game_state *state)
3925{
3926 return state->completed ? +1 : 0;
3927}
3928
3929#ifndef EDITOR
3930static void game_print_size(const game_params *params, const game_ui *ui,
3931 float *x, float *y)
3932{
3933 int pw, ph;
3934
3935 /*
3936 * 8mm squares by default. (There isn't all that much detail
3937 * that needs to go in each square.)
3938 */
3939 game_compute_size(params, 800, ui, &pw, &ph);
3940 *x = pw / 100.0F;
3941 *y = ph / 100.0F;
3942}
3943
3944static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
3945 int sz)
3946{
3947 int w = state->w, h = state->h;
3948 int white, black, blackish;
3949 int x, y, i, j;
3950 int *colours;
3951 DSF *dsf;
3952 int *coords = NULL;
3953 int ncoords = 0, coordsize = 0;
3954
3955 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3956 game_drawstate ads, *ds = &ads;
3957 ds->tilesize = sz;
3958
3959 white = print_mono_colour(dr, 1);
3960 black = print_mono_colour(dr, 0);
3961 blackish = print_hatched_colour(dr, HATCH_X);
3962
3963 /*
3964 * Get the completion information.
3965 */
3966 dsf = dsf_new(w * h);
3967 colours = snewn(w * h, int);
3968 check_complete(state, dsf, colours);
3969
3970 /*
3971 * Draw the grid.
3972 */
3973 print_line_width(dr, TILE_SIZE / 64);
3974 for (x = 1; x < w; x++)
3975 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
3976 for (y = 1; y < h; y++)
3977 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
3978
3979 /*
3980 * Shade the completed regions. Just in case any particular
3981 * printing platform deals badly with adjacent
3982 * similarly-hatched regions, we'll fill each one as a single
3983 * polygon.
3984 */
3985 for (i = 0; i < w*h; i++) {
3986 j = dsf_canonify(dsf, i);
3987 if (colours[j] != 0) {
3988 int dx, dy, t;
3989
3990 /*
3991 * This is the first square we've run into belonging to
3992 * this polyomino, which means an edge of the polyomino
3993 * is certain to be to our left. (After we finish
3994 * tracing round it, we'll set the colours[] entry to
3995 * zero to prevent accidentally doing it again.)
3996 */
3997
3998 x = i % w;
3999 y = i / w;
4000 dx = -1;
4001 dy = 0;
4002 ncoords = 0;
4003 while (1) {
4004 /*
4005 * We are currently sitting on square (x,y), which
4006 * we know to be in our polyomino, and we also know
4007 * that (x+dx,y+dy) is not. The way I visualise
4008 * this is that we're standing to the right of a
4009 * boundary line, stretching our left arm out to
4010 * point to the exterior square on the far side.
4011 */
4012
4013 /*
4014 * First, check if we've gone round the entire
4015 * polyomino.
4016 */
4017 if (ncoords > 0 &&
4018 (x == i%w && y == i/w && dx == -1 && dy == 0))
4019 break;
4020
4021 /*
4022 * Add to our coordinate list the coordinate
4023 * backwards and to the left of where we are.
4024 */
4025 if (ncoords + 2 > coordsize) {
4026 coordsize = (ncoords * 3 / 2) + 64;
4027 coords = sresize(coords, coordsize, int);
4028 }
4029 coords[ncoords++] = COORD((2*x+1 + dx + dy) / 2);
4030 coords[ncoords++] = COORD((2*y+1 + dy - dx) / 2);
4031
4032 /*
4033 * Follow the edge round. If the square directly in
4034 * front of us is not part of the polyomino, we
4035 * turn right; if it is and so is the square in
4036 * front of (x+dx,y+dy), we turn left; otherwise we
4037 * go straight on.
4038 */
4039 if (x-dy < 0 || x-dy >= w || y+dx < 0 || y+dx >= h ||
4040 dsf_canonify(dsf, (y+dx)*w+(x-dy)) != j) {
4041 /* Turn right. */
4042 t = dx;
4043 dx = -dy;
4044 dy = t;
4045 } else if (x+dx-dy >= 0 && x+dx-dy < w &&
4046 y+dy+dx >= 0 && y+dy+dx < h &&
4047 dsf_canonify(dsf, (y+dy+dx)*w+(x+dx-dy)) == j) {
4048 /* Turn left. */
4049 x += dx;
4050 y += dy;
4051 t = dx;
4052 dx = dy;
4053 dy = -t;
4054 x -= dx;
4055 y -= dy;
4056 } else {
4057 /* Straight on. */
4058 x -= dy;
4059 y += dx;
4060 }
4061 }
4062
4063 /*
4064 * Now we have our polygon complete, so fill it.
4065 */
4066 draw_polygon(dr, coords, ncoords/2,
4067 colours[j] == 2 ? blackish : -1, black);
4068
4069 /*
4070 * And mark this polyomino as done.
4071 */
4072 colours[j] = 0;
4073 }
4074 }
4075
4076 /*
4077 * Draw the edges.
4078 */
4079 for (y = 0; y <= h; y++)
4080 for (x = 0; x <= w; x++) {
4081 if (x < w && SPACE(state, x*2+1, y*2).flags & F_EDGE_SET)
4082 draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS,
4083 EDGE_THICKNESS * 2 + TILE_SIZE, EDGE_THICKNESS * 2,
4084 black);
4085 if (y < h && SPACE(state, x*2, y*2+1).flags & F_EDGE_SET)
4086 draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS,
4087 EDGE_THICKNESS * 2, EDGE_THICKNESS * 2 + TILE_SIZE,
4088 black);
4089 }
4090
4091 /*
4092 * Draw the dots.
4093 */
4094 for (y = 0; y <= 2*h; y++)
4095 for (x = 0; x <= 2*w; x++)
4096 if (SPACE(state, x, y).flags & F_DOT) {
4097 draw_circle(dr, (int)COORD(x/2.0), (int)COORD(y/2.0), DOT_SIZE,
4098 (SPACE(state, x, y).flags & F_DOT_BLACK ?
4099 black : white), black);
4100 }
4101
4102 dsf_free(dsf);
4103 sfree(colours);
4104 sfree(coords);
4105}
4106#endif
4107
4108#ifdef COMBINED
4109#define thegame galaxies
4110#endif
4111
4112const struct game thegame = {
4113 "Galaxies", "games.galaxies", "galaxies",
4114 default_params,
4115 game_fetch_preset, NULL,
4116 decode_params,
4117 encode_params,
4118 free_params,
4119 dup_params,
4120 true, game_configure, custom_params,
4121 validate_params,
4122 new_game_desc,
4123 validate_desc,
4124 new_game,
4125 dup_game,
4126 free_game,
4127#ifdef EDITOR
4128 false, NULL,
4129#else
4130 true, solve_game,
4131#endif
4132 true, game_can_format_as_text_now, game_text_format,
4133 NULL, NULL, /* get_prefs, set_prefs */
4134 new_ui,
4135 free_ui,
4136 NULL, /* encode_ui */
4137 NULL, /* decode_ui */
4138 NULL, /* game_request_keys */
4139 game_changed_state,
4140#ifdef EDITOR
4141 NULL,
4142#else
4143 current_key_label,
4144#endif
4145 interpret_move,
4146 execute_move,
4147 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
4148 game_colours,
4149 game_new_drawstate,
4150 game_free_drawstate,
4151 game_redraw,
4152 game_anim_length,
4153 game_flash_length,
4154 game_get_cursor_location,
4155 game_status,
4156#ifdef EDITOR
4157 false, false, NULL, NULL,
4158 true, /* wants_statusbar */
4159#else
4160 true, false, game_print_size, game_print,
4161 false, /* wants_statusbar */
4162#endif
4163 false, NULL, /* timing_state */
4164 REQUIRE_RBUTTON, /* flags */
4165};
4166
4167#ifdef STANDALONE_SOLVER
4168
4169static const char *quis;
4170
4171#include <time.h>
4172
4173static void usage_exit(const char *msg)
4174{
4175 if (msg)
4176 fprintf(stderr, "%s: %s\n", quis, msg);
4177 fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
4178 exit(1);
4179}
4180
4181static void dump_state(game_state *state)
4182{
4183 char *temp = game_text_format(state);
4184 printf("%s\n", temp);
4185 sfree(temp);
4186}
4187
4188static int gen(game_params *p, random_state *rs, bool debug)
4189{
4190 char *desc;
4191 int diff;
4192 game_state *state;
4193
4194#ifndef DEBUGGING
4195 solver_show_working = debug;
4196#endif
4197 printf("Generating a %dx%d %s puzzle.\n",
4198 p->w, p->h, galaxies_diffnames[p->diff]);
4199
4200 desc = new_game_desc(p, rs, NULL, false);
4201 state = new_game(NULL, p, desc);
4202 dump_state(state);
4203
4204 diff = solver_state(state, DIFF_UNREASONABLE);
4205 printf("Generated %s game %dx%d:%s\n",
4206 galaxies_diffnames[diff], p->w, p->h, desc);
4207 dump_state(state);
4208
4209 free_game(state);
4210 sfree(desc);
4211
4212 return diff;
4213}
4214
4215static void soak(game_params *p, random_state *rs)
4216{
4217 time_t tt_start, tt_now, tt_last;
4218 char *desc;
4219 game_state *st;
4220 int diff, n = 0, i, diffs[DIFF_MAX], ndots = 0, nspaces = 0;
4221
4222#ifndef DEBUGGING
4223 solver_show_working = false;
4224#endif
4225 tt_start = tt_now = time(NULL);
4226 for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0;
4227 one_try = true;
4228
4229 printf("Soak-generating a %dx%d grid, max. diff %s.\n",
4230 p->w, p->h, galaxies_diffnames[p->diff]);
4231 printf(" [");
4232 for (i = 0; i < DIFF_MAX; i++)
4233 printf("%s%s", (i == 0) ? "" : ", ", galaxies_diffnames[i]);
4234 printf("]\n");
4235
4236 while (1) {
4237 char *aux;
4238 desc = new_game_desc(p, rs, &aux, false);
4239 sfree(aux);
4240 st = new_game(NULL, p, desc);
4241 diff = solver_state(st, p->diff);
4242 nspaces += st->w*st->h;
4243 for (i = 0; i < st->sx*st->sy; i++)
4244 if (st->grid[i].flags & F_DOT) ndots++;
4245 free_game(st);
4246 sfree(desc);
4247
4248 diffs[diff]++;
4249 n++;
4250 tt_last = time(NULL);
4251 if (tt_last > tt_now) {
4252 tt_now = tt_last;
4253 printf("%d total, %3.1f/s, [",
4254 n, (double)n / ((double)tt_now - tt_start));
4255 for (i = 0; i < DIFF_MAX; i++)
4256 printf("%s%.1f%%", (i == 0) ? "" : ", ",
4257 100.0 * ((double)diffs[i] / (double)n));
4258 printf("], %.1f%% dots\n",
4259 100.0 * ((double)ndots / (double)nspaces));
4260 }
4261 }
4262}
4263
4264int main(int argc, char **argv)
4265{
4266 game_params *p;
4267 char *id = NULL, *desc;
4268 const char *err;
4269 game_state *s;
4270 int diff;
4271 bool do_soak = false, verbose = false;
4272 random_state *rs;
4273 time_t seed = time(NULL);
4274
4275 quis = argv[0];
4276 while (--argc > 0) {
4277 char *p = *++argv;
4278 if (!strcmp(p, "-v")) {
4279 verbose = true;
4280 } else if (!strcmp(p, "--seed")) {
4281 if (argc == 0) usage_exit("--seed needs an argument");
4282 seed = (time_t)atoi(*++argv);
4283 argc--;
4284 } else if (!strcmp(p, "--soak")) {
4285 do_soak = true;
4286 } else if (*p == '-') {
4287 usage_exit("unrecognised option");
4288 } else {
4289 id = p;
4290 }
4291 }
4292
4293 p = default_params();
4294 rs = random_new((void*)&seed, sizeof(time_t));
4295
4296 if (do_soak) {
4297 if (!id) usage_exit("need one argument for --soak");
4298 decode_params(p, *argv);
4299 soak(p, rs);
4300 return 0;
4301 }
4302
4303 if (!id) {
4304 while (1) {
4305 p->w = random_upto(rs, 15) + 3;
4306 p->h = random_upto(rs, 15) + 3;
4307 p->diff = random_upto(rs, DIFF_UNREASONABLE);
4308 diff = gen(p, rs, false);
4309 }
4310 return 0;
4311 }
4312
4313 desc = strchr(id, ':');
4314 if (!desc) {
4315 decode_params(p, id);
4316 gen(p, rs, verbose);
4317 } else {
4318#ifndef DEBUGGING
4319 solver_show_working = true;
4320#endif
4321 *desc++ = '\0';
4322 decode_params(p, id);
4323 err = validate_desc(p, desc);
4324 if (err) {
4325 fprintf(stderr, "%s: %s\n", argv[0], err);
4326 exit(1);
4327 }
4328 s = new_game(NULL, p, desc);
4329 diff = solver_state(s, DIFF_UNREASONABLE);
4330 dump_state(s);
4331 printf("Puzzle is %s.\n", galaxies_diffnames[diff]);
4332 free_game(s);
4333 }
4334
4335 free_params(p);
4336
4337 return 0;
4338}
4339
4340#endif
4341
4342#ifdef STANDALONE_PICTURE_GENERATOR
4343
4344/*
4345 * Main program for the standalone picture generator. To use it,
4346 * simply provide it with an XBM-format bitmap file (note XBM, not
4347 * XPM) on standard input, and it will output a game ID in return.
4348 * For example:
4349 *
4350 * $ ./galaxiespicture < badly-drawn-cat.xbm
4351 * 11x11:eloMBLzFeEzLNMWifhaWYdDbixCymBbBMLoDdewGg
4352 *
4353 * If you want a puzzle with a non-standard difficulty level, pass
4354 * a partial parameters string as a command-line argument (e.g.
4355 * `./galaxiespicture du < foo.xbm', where `du' is the same suffix
4356 * which if it appeared in a random-seed game ID would set the
4357 * difficulty level to Unreasonable). However, be aware that if the
4358 * generator fails to produce an adequately difficult puzzle too
4359 * many times then it will give up and return an easier one (just
4360 * as it does during normal GUI play). To be sure you really have
4361 * the difficulty you asked for, use galaxiessolver to
4362 * double-check.
4363 *
4364 * (Perhaps I ought to include an option to make this standalone
4365 * generator carry on looping until it really does get the right
4366 * difficulty. Hmmm.)
4367 */
4368
4369#include <time.h>
4370
4371int main(int argc, char **argv)
4372{
4373 game_params *par;
4374 char *params, *desc;
4375 random_state *rs;
4376 time_t seed = time(NULL);
4377 char buf[4096];
4378 int i;
4379 int x, y;
4380
4381 par = default_params();
4382 if (argc > 1)
4383 decode_params(par, argv[1]); /* get difficulty */
4384 par->w = par->h = -1;
4385
4386 /*
4387 * Now read an XBM file from standard input. This is simple and
4388 * hacky and will do very little error detection, so don't feed
4389 * it bogus data.
4390 */
4391 picture = NULL;
4392 x = y = 0;
4393 while (fgets(buf, sizeof(buf), stdin)) {
4394 buf[strcspn(buf, "\r\n")] = '\0';
4395 if (!strncmp(buf, "#define", 7)) {
4396 /*
4397 * Lines starting `#define' give the width and height.
4398 */
4399 char *num = buf + strlen(buf);
4400 char *symend;
4401
4402 while (num > buf && isdigit((unsigned char)num[-1]))
4403 num--;
4404 symend = num;
4405 while (symend > buf && isspace((unsigned char)symend[-1]))
4406 symend--;
4407
4408 if (symend-5 >= buf && !strncmp(symend-5, "width", 5))
4409 par->w = atoi(num);
4410 else if (symend-6 >= buf && !strncmp(symend-6, "height", 6))
4411 par->h = atoi(num);
4412 } else {
4413 /*
4414 * Otherwise, break the string up into words and take
4415 * any word of the form `0x' plus hex digits to be a
4416 * byte.
4417 */
4418 char *p, *wordstart;
4419
4420 if (!picture) {
4421 if (par->w < 0 || par->h < 0) {
4422 printf("failed to read width and height\n");
4423 return 1;
4424 }
4425 picture = snewn(par->w * par->h, int);
4426 for (i = 0; i < par->w * par->h; i++)
4427 picture[i] = -1;
4428 }
4429
4430 p = buf;
4431 while (*p) {
4432 while (*p && (*p == ',' || isspace((unsigned char)*p)))
4433 p++;
4434 wordstart = p;
4435 while (*p && !(*p == ',' || *p == '}' ||
4436 isspace((unsigned char)*p)))
4437 p++;
4438 if (*p)
4439 *p++ = '\0';
4440
4441 if (wordstart[0] == '0' &&
4442 (wordstart[1] == 'x' || wordstart[1] == 'X') &&
4443 !wordstart[2 + strspn(wordstart+2,
4444 "0123456789abcdefABCDEF")]) {
4445 unsigned long byte = strtoul(wordstart+2, NULL, 16);
4446 for (i = 0; i < 8; i++) {
4447 int bit = (byte >> i) & 1;
4448 if (y < par->h && x < par->w)
4449 picture[y * par->w + x] = bit;
4450 x++;
4451 }
4452
4453 if (x >= par->w) {
4454 x = 0;
4455 y++;
4456 }
4457 }
4458 }
4459 }
4460 }
4461
4462 for (i = 0; i < par->w * par->h; i++)
4463 if (picture[i] < 0) {
4464 fprintf(stderr, "failed to read enough bitmap data\n");
4465 return 1;
4466 }
4467
4468 rs = random_new((void*)&seed, sizeof(time_t));
4469
4470 desc = new_game_desc(par, rs, NULL, false);
4471 params = encode_params(par, false);
4472 printf("%s:%s\n", params, desc);
4473
4474 sfree(desc);
4475 sfree(params);
4476 free_params(par);
4477 random_free(rs);
4478
4479 return 0;
4480}
4481
4482#endif
4483
4484/* vim: set shiftwidth=4 tabstop=8: */