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