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