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