chiark / gitweb /
Tents: mark squares as non-tents with {Shift,Control}-cursor keys.
[sgt-puzzles.git] / bridges.c
1 /*
2  * bridges.c: Implementation of the Nikoli game 'Bridges'.
3  *
4  * Things still to do:
5  *
6  *  - The solver's algorithmic design is not really ideal. It makes
7  *    use of the same data representation as gameplay uses, which
8  *    often looks like a tempting reuse of code but isn't always a
9  *    good idea. In this case, it's unpleasant that each edge of the
10  *    graph ends up represented as multiple squares on a grid, with
11  *    flags indicating when edges and non-edges cross; that's useful
12  *    when the result can be directly translated into positions of
13  *    graphics on the display, but in purely internal work it makes
14  *    even simple manipulations during solving more painful than they
15  *    should be, and complex ones have no choice but to modify the
16  *    data structures temporarily, test things, and put them back. I
17  *    envisage a complete solver rewrite along the following lines:
18  *     + We have a collection of vertices (islands) and edges
19  *       (potential bridge locations, i.e. pairs of horizontal or
20  *       vertical islands with no other island in between).
21  *     + Each edge has an associated list of edges that cross it, and
22  *       hence with which it is mutually exclusive.
23  *     + For each edge, we track the min and max number of bridges we
24  *       currently think possible.
25  *     + For each vertex, we track the number of _liberties_ it has,
26  *       i.e. its clue number minus the min bridge count for each edge
27  *       out of it.
28  *     + We also maintain a dsf that identifies sets of vertices which
29  *       are connected components of the puzzle so far, and for each
30  *       equivalence class we track the total number of liberties for
31  *       that component. (The dsf mechanism will also already track
32  *       the size of each component, i.e. number of islands.)
33  *     + So incrementing the min for an edge requires processing along
34  *       the lines of:
35  *        - set the max for all edges crossing that one to zero
36  *        - decrement the liberty count for the vertex at each end,
37  *          and also for each vertex's equivalence class (NB they may
38  *          be the same class)
39  *        - unify the two equivalence classes if they're not already,
40  *          and if so, set the liberty count for the new class to be
41  *          the sum of the previous two.
42  *     + Decrementing the max is much easier, however.
43  *     + With this data structure the really fiddly stuff in stage3()
44  *       becomes more or less trivial, because it's now a quick job to
45  *       find out whether an island would form an isolated subgraph if
46  *       connected to a given subset of its neighbours:
47  *        - identify the connected components containing the test
48  *          vertex and its putative new neighbours (but be careful not
49  *          to count a component more than once if two or more of the
50  *          vertices involved are already in the same one)
51  *        - find the sum of those components' liberty counts, and also
52  *          the total number of islands involved
53  *        - if the total liberty count of the connected components is
54  *          exactly equal to twice the number of edges we'd be adding
55  *          (of course each edge destroys two liberties, one at each
56  *          end) then these components would become a subgraph with
57  *          zero liberties if connected together.
58  *        - therefore, if that subgraph also contains fewer than the
59  *          total number of islands, it's disallowed.
60  *        - As mentioned in stage3(), once we've identified such a
61  *          disallowed pattern, we have two choices for what to do
62  *          with it: if the candidate set of neighbours has size 1 we
63  *          can reduce the max for the edge to that one neighbour,
64  *          whereas if its complement has size 1 we can increase the
65  *          min for the edge to the _omitted_ neighbour.
66  *
67  *  - write a recursive solver?
68  */
69
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <assert.h>
74 #include <ctype.h>
75 #include <math.h>
76
77 #include "puzzles.h"
78
79 /* Turn this on for hints about which lines are considered possibilities. */
80 #undef DRAW_GRID
81
82 /* --- structures for params, state, etc. --- */
83
84 #define MAX_BRIDGES     4
85
86 #define PREFERRED_TILE_SIZE 24
87 #define TILE_SIZE       (ds->tilesize)
88 #define BORDER          (TILE_SIZE / 2)
89
90 #define COORD(x)  ( (x) * TILE_SIZE + BORDER )
91 #define FROMCOORD(x)  ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
92
93 #define FLASH_TIME 0.50F
94
95 enum {
96     COL_BACKGROUND,
97     COL_FOREGROUND,
98     COL_HIGHLIGHT, COL_LOWLIGHT,
99     COL_SELECTED, COL_MARK,
100     COL_HINT, COL_GRID,
101     COL_WARNING,
102     COL_CURSOR,
103     NCOLOURS
104 };
105
106 struct game_params {
107     int w, h, maxb;
108     int islands, expansion;     /* %age of island squares, %age chance of expansion */
109     int allowloops, difficulty;
110 };
111
112 /* general flags used by all structs */
113 #define G_ISLAND        0x0001
114 #define G_LINEV         0x0002     /* contains a vert. line */
115 #define G_LINEH         0x0004     /* contains a horiz. line (mutex with LINEV) */
116 #define G_LINE          (G_LINEV|G_LINEH)
117 #define G_MARKV         0x0008
118 #define G_MARKH         0x0010
119 #define G_MARK          (G_MARKV|G_MARKH)
120 #define G_NOLINEV       0x0020
121 #define G_NOLINEH       0x0040
122 #define G_NOLINE        (G_NOLINEV|G_NOLINEH)
123
124 /* flags used by the error checker */
125 #define G_WARN          0x0080
126
127 /* flags used by the solver etc. */
128 #define G_SWEEP         0x1000
129
130 #define G_FLAGSH        (G_LINEH|G_MARKH|G_NOLINEH)
131 #define G_FLAGSV        (G_LINEV|G_MARKV|G_NOLINEV)
132
133 typedef unsigned int grid_type; /* change me later if we invent > 16 bits of flags. */
134
135 struct solver_state {
136     int *dsf, *comptspaces;
137     int *tmpdsf, *tmpcompspaces;
138     int refcount;
139 };
140
141 /* state->gridi is an optimisation; it stores the pointer to the island
142  * structs indexed by (x,y). It's not strictly necessary (we could use
143  * find234 instead), but Purify showed that board generation (mostly the solver)
144  * was spending 60% of its time in find234. */
145
146 struct surrounds { /* cloned from lightup.c */
147     struct { int x, y, dx, dy, off; } points[4];
148     int npoints, nislands;
149 };
150
151 struct island {
152   game_state *state;
153   int x, y, count;
154   struct surrounds adj;
155 };
156
157 struct game_state {
158     int w, h, completed, solved, allowloops, maxb;
159     grid_type *grid, *scratch;
160     struct island *islands;
161     int n_islands, n_islands_alloc;
162     game_params params; /* used by the aux solver. */
163 #define N_WH_ARRAYS 5
164     char *wha, *possv, *possh, *lines, *maxv, *maxh;
165     struct island **gridi;
166     struct solver_state *solver; /* refcounted */
167 };
168
169 #define GRIDSZ(s) ((s)->w * (s)->h * sizeof(grid_type))
170
171 #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
172
173 #define DINDEX(x,y) ((y)*state->w + (x))
174
175 #define INDEX(s,g,x,y) ((s)->g[(y)*((s)->w) + (x)])
176 #define IDX(s,g,i) ((s)->g[(i)])
177 #define GRID(s,x,y) INDEX(s,grid,x,y)
178 #define SCRATCH(s,x,y) INDEX(s,scratch,x,y)
179 #define POSSIBLES(s,dx,x,y) ((dx) ? (INDEX(s,possh,x,y)) : (INDEX(s,possv,x,y)))
180 #define MAXIMUM(s,dx,x,y) ((dx) ? (INDEX(s,maxh,x,y)) : (INDEX(s,maxv,x,y)))
181
182 #define GRIDCOUNT(s,x,y,f) ((GRID(s,x,y) & (f)) ? (INDEX(s,lines,x,y)) : 0)
183
184 #define WITHIN2(x,min,max) (((x) < (min)) ? 0 : (((x) > (max)) ? 0 : 1))
185 #define WITHIN(x,min,max) ((min) > (max) ? \
186                            WITHIN2(x,max,min) : WITHIN2(x,min,max))
187
188 /* --- island struct and tree support functions --- */
189
190 #define ISLAND_ORTH(is,j,f,df) \
191     (is->f + (is->adj.points[(j)].off*is->adj.points[(j)].df))
192
193 #define ISLAND_ORTHX(is,j) ISLAND_ORTH(is,j,x,dx)
194 #define ISLAND_ORTHY(is,j) ISLAND_ORTH(is,j,y,dy)
195
196 static void fixup_islands_for_realloc(game_state *state)
197 {
198     int i;
199
200     for (i = 0; i < state->w*state->h; i++) state->gridi[i] = NULL;
201     for (i = 0; i < state->n_islands; i++) {
202         struct island *is = &state->islands[i];
203         is->state = state;
204         INDEX(state, gridi, is->x, is->y) = is;
205     }
206 }
207
208 static int game_can_format_as_text_now(const game_params *params)
209 {
210     return TRUE;
211 }
212
213 static char *game_text_format(const game_state *state)
214 {
215     int x, y, len, nl;
216     char *ret, *p;
217     struct island *is;
218     grid_type grid;
219
220     len = (state->h) * (state->w+1) + 1;
221     ret = snewn(len, char);
222     p = ret;
223
224     for (y = 0; y < state->h; y++) {
225         for (x = 0; x < state->w; x++) {
226             grid = GRID(state,x,y);
227             nl = INDEX(state,lines,x,y);
228             is = INDEX(state, gridi, x, y);
229             if (is) {
230                 *p++ = '0' + is->count;
231             } else if (grid & G_LINEV) {
232                 *p++ = (nl > 1) ? '"' : (nl == 1) ? '|' : '!'; /* gaah, want a double-bar. */
233             } else if (grid & G_LINEH) {
234                 *p++ = (nl > 1) ? '=' : (nl == 1) ? '-' : '~';
235             } else {
236                 *p++ = '.';
237             }
238         }
239         *p++ = '\n';
240     }
241     *p++ = '\0';
242
243     assert(p - ret == len);
244     return ret;
245 }
246
247 static void debug_state(game_state *state)
248 {
249     char *textversion = game_text_format(state);
250     debug(("%s", textversion));
251     sfree(textversion);
252 }
253
254 /*static void debug_possibles(game_state *state)
255 {
256     int x, y;
257     debug(("possh followed by possv\n"));
258     for (y = 0; y < state->h; y++) {
259         for (x = 0; x < state->w; x++) {
260             debug(("%d", POSSIBLES(state, 1, x, y)));
261         }
262         debug((" "));
263         for (x = 0; x < state->w; x++) {
264             debug(("%d", POSSIBLES(state, 0, x, y)));
265         }
266         debug(("\n"));
267     }
268     debug(("\n"));
269         for (y = 0; y < state->h; y++) {
270         for (x = 0; x < state->w; x++) {
271             debug(("%d", MAXIMUM(state, 1, x, y)));
272         }
273         debug((" "));
274         for (x = 0; x < state->w; x++) {
275             debug(("%d", MAXIMUM(state, 0, x, y)));
276         }
277         debug(("\n"));
278     }
279     debug(("\n"));
280 }*/
281
282 static void island_set_surrounds(struct island *is)
283 {
284     assert(INGRID(is->state,is->x,is->y));
285     is->adj.npoints = is->adj.nislands = 0;
286 #define ADDPOINT(cond,ddx,ddy) do {\
287     if (cond) { \
288         is->adj.points[is->adj.npoints].x = is->x+(ddx); \
289         is->adj.points[is->adj.npoints].y = is->y+(ddy); \
290         is->adj.points[is->adj.npoints].dx = (ddx); \
291         is->adj.points[is->adj.npoints].dy = (ddy); \
292         is->adj.points[is->adj.npoints].off = 0; \
293         is->adj.npoints++; \
294     } } while(0)
295     ADDPOINT(is->x > 0,                -1,  0);
296     ADDPOINT(is->x < (is->state->w-1), +1,  0);
297     ADDPOINT(is->y > 0,                 0, -1);
298     ADDPOINT(is->y < (is->state->h-1),  0, +1);
299 }
300
301 static void island_find_orthogonal(struct island *is)
302 {
303     /* fills in the rest of the 'surrounds' structure, assuming
304      * all other islands are now in place. */
305     int i, x, y, dx, dy, off;
306
307     is->adj.nislands = 0;
308     for (i = 0; i < is->adj.npoints; i++) {
309         dx = is->adj.points[i].dx;
310         dy = is->adj.points[i].dy;
311         x = is->x + dx;
312         y = is->y + dy;
313         off = 1;
314         is->adj.points[i].off = 0;
315         while (INGRID(is->state, x, y)) {
316             if (GRID(is->state, x, y) & G_ISLAND) {
317                 is->adj.points[i].off = off;
318                 is->adj.nislands++;
319                 /*debug(("island (%d,%d) has orth is. %d*(%d,%d) away at (%d,%d).\n",
320                        is->x, is->y, off, dx, dy,
321                        ISLAND_ORTHX(is,i), ISLAND_ORTHY(is,i)));*/
322                 goto foundisland;
323             }
324             off++; x += dx; y += dy;
325         }
326 foundisland:
327         ;
328     }
329 }
330
331 static int island_hasbridge(struct island *is, int direction)
332 {
333     int x = is->adj.points[direction].x;
334     int y = is->adj.points[direction].y;
335     grid_type gline = is->adj.points[direction].dx ? G_LINEH : G_LINEV;
336
337     if (GRID(is->state, x, y) & gline) return 1;
338     return 0;
339 }
340
341 static struct island *island_find_connection(struct island *is, int adjpt)
342 {
343     struct island *is_r;
344
345     assert(adjpt < is->adj.npoints);
346     if (!is->adj.points[adjpt].off) return NULL;
347     if (!island_hasbridge(is, adjpt)) return NULL;
348
349     is_r = INDEX(is->state, gridi,
350                  ISLAND_ORTHX(is, adjpt), ISLAND_ORTHY(is, adjpt));
351     assert(is_r);
352
353     return is_r;
354 }
355
356 static struct island *island_add(game_state *state, int x, int y, int count)
357 {
358     struct island *is;
359     int realloced = 0;
360
361     assert(!(GRID(state,x,y) & G_ISLAND));
362     GRID(state,x,y) |= G_ISLAND;
363
364     state->n_islands++;
365     if (state->n_islands > state->n_islands_alloc) {
366         state->n_islands_alloc = state->n_islands * 2;
367         state->islands =
368             sresize(state->islands, state->n_islands_alloc, struct island);
369         realloced = 1;
370     }
371     is = &state->islands[state->n_islands-1];
372
373     memset(is, 0, sizeof(struct island));
374     is->state = state;
375     is->x = x;
376     is->y = y;
377     is->count = count;
378     island_set_surrounds(is);
379
380     if (realloced)
381         fixup_islands_for_realloc(state);
382     else
383         INDEX(state, gridi, x, y) = is;
384
385     return is;
386 }
387
388
389 /* n = -1 means 'flip NOLINE flags [and set line to 0].' */
390 static void island_join(struct island *i1, struct island *i2, int n, int is_max)
391 {
392     game_state *state = i1->state;
393     int s, e, x, y;
394
395     assert(i1->state == i2->state);
396     assert(n >= -1 && n <= i1->state->maxb);
397
398     if (i1->x == i2->x) {
399         x = i1->x;
400         if (i1->y < i2->y) {
401             s = i1->y+1; e = i2->y-1;
402         } else {
403             s = i2->y+1; e = i1->y-1;
404         }
405         for (y = s; y <= e; y++) {
406             if (is_max) {
407                 INDEX(state,maxv,x,y) = n;
408             } else {
409                 if (n < 0) {
410                     GRID(state,x,y) ^= G_NOLINEV;
411                 } else if (n == 0) {
412                     GRID(state,x,y) &= ~G_LINEV;
413                 } else {
414                     GRID(state,x,y) |= G_LINEV;
415                     INDEX(state,lines,x,y) = n;
416                 }
417             }
418         }
419     } else if (i1->y == i2->y) {
420         y = i1->y;
421         if (i1->x < i2->x) {
422             s = i1->x+1; e = i2->x-1;
423         } else {
424             s = i2->x+1; e = i1->x-1;
425         }
426         for (x = s; x <= e; x++) {
427             if (is_max) {
428                 INDEX(state,maxh,x,y) = n;
429             } else {
430                 if (n < 0) {
431                     GRID(state,x,y) ^= G_NOLINEH;
432                 } else if (n == 0) {
433                     GRID(state,x,y) &= ~G_LINEH;
434                 } else {
435                     GRID(state,x,y) |= G_LINEH;
436                     INDEX(state,lines,x,y) = n;
437                 }
438             }
439         }
440     } else {
441         assert(!"island_join: islands not orthogonal.");
442     }
443 }
444
445 /* Counts the number of bridges currently attached to the island. */
446 static int island_countbridges(struct island *is)
447 {
448     int i, c = 0;
449
450     for (i = 0; i < is->adj.npoints; i++) {
451         c += GRIDCOUNT(is->state,
452                        is->adj.points[i].x, is->adj.points[i].y,
453                        is->adj.points[i].dx ? G_LINEH : G_LINEV);
454     }
455     /*debug(("island count for (%d,%d) is %d.\n", is->x, is->y, c));*/
456     return c;
457 }
458
459 static int island_adjspace(struct island *is, int marks, int missing,
460                            int direction)
461 {
462     int x, y, poss, curr, dx;
463     grid_type gline, mline;
464
465     x = is->adj.points[direction].x;
466     y = is->adj.points[direction].y;
467     dx = is->adj.points[direction].dx;
468     gline = dx ? G_LINEH : G_LINEV;
469
470     if (marks) {
471         mline = dx ? G_MARKH : G_MARKV;
472         if (GRID(is->state,x,y) & mline) return 0;
473     }
474     poss = POSSIBLES(is->state, dx, x, y);
475     poss = min(poss, missing);
476
477     curr = GRIDCOUNT(is->state, x, y, gline);
478     poss = min(poss, MAXIMUM(is->state, dx, x, y) - curr);
479
480     return poss;
481 }
482
483 /* Counts the number of bridge spaces left around the island;
484  * expects the possibles to be up-to-date. */
485 static int island_countspaces(struct island *is, int marks)
486 {
487     int i, c = 0, missing;
488
489     missing = is->count - island_countbridges(is);
490     if (missing < 0) return 0;
491
492     for (i = 0; i < is->adj.npoints; i++) {
493         c += island_adjspace(is, marks, missing, i);
494     }
495     return c;
496 }
497
498 static int island_isadj(struct island *is, int direction)
499 {
500     int x, y;
501     grid_type gline, mline;
502
503     x = is->adj.points[direction].x;
504     y = is->adj.points[direction].y;
505
506     mline = is->adj.points[direction].dx ? G_MARKH : G_MARKV;
507     gline = is->adj.points[direction].dx ? G_LINEH : G_LINEV;
508     if (GRID(is->state, x, y) & mline) {
509         /* If we're marked (i.e. the thing to attach to is complete)
510          * only count an adjacency if we're already attached. */
511         return GRIDCOUNT(is->state, x, y, gline);
512     } else {
513         /* If we're unmarked, count possible adjacency iff it's
514          * flagged as POSSIBLE. */
515         return POSSIBLES(is->state, is->adj.points[direction].dx, x, y);
516     }
517     return 0;
518 }
519
520 /* Counts the no. of possible adjacent islands (including islands
521  * we're already connected to). */
522 static int island_countadj(struct island *is)
523 {
524     int i, nadj = 0;
525
526     for (i = 0; i < is->adj.npoints; i++) {
527         if (island_isadj(is, i)) nadj++;
528     }
529     return nadj;
530 }
531
532 static void island_togglemark(struct island *is)
533 {
534     int i, j, x, y, o;
535     struct island *is_loop;
536
537     /* mark the island... */
538     GRID(is->state, is->x, is->y) ^= G_MARK;
539
540     /* ...remove all marks on non-island squares... */
541     for (x = 0; x < is->state->w; x++) {
542         for (y = 0; y < is->state->h; y++) {
543             if (!(GRID(is->state, x, y) & G_ISLAND))
544                 GRID(is->state, x, y) &= ~G_MARK;
545         }
546     }
547
548     /* ...and add marks to squares around marked islands. */
549     for (i = 0; i < is->state->n_islands; i++) {
550         is_loop = &is->state->islands[i];
551         if (!(GRID(is_loop->state, is_loop->x, is_loop->y) & G_MARK))
552             continue;
553
554         for (j = 0; j < is_loop->adj.npoints; j++) {
555             /* if this direction takes us to another island, mark all
556              * squares between the two islands. */
557             if (!is_loop->adj.points[j].off) continue;
558             assert(is_loop->adj.points[j].off > 1);
559             for (o = 1; o < is_loop->adj.points[j].off; o++) {
560                 GRID(is_loop->state,
561                      is_loop->x + is_loop->adj.points[j].dx*o,
562                      is_loop->y + is_loop->adj.points[j].dy*o) |=
563                     is_loop->adj.points[j].dy ? G_MARKV : G_MARKH;
564             }
565         }
566     }
567 }
568
569 static int island_impossible(struct island *is, int strict)
570 {
571     int curr = island_countbridges(is), nspc = is->count - curr, nsurrspc;
572     int i, poss;
573     struct island *is_orth;
574
575     if (nspc < 0) {
576         debug(("island at (%d,%d) impossible because full.\n", is->x, is->y));
577         return 1;        /* too many bridges */
578     } else if ((curr + island_countspaces(is, 0)) < is->count) {
579         debug(("island at (%d,%d) impossible because not enough spaces.\n", is->x, is->y));
580         return 1;        /* impossible to create enough bridges */
581     } else if (strict && curr < is->count) {
582         debug(("island at (%d,%d) impossible because locked.\n", is->x, is->y));
583         return 1;        /* not enough bridges and island is locked */
584     }
585
586     /* Count spaces in surrounding islands. */
587     nsurrspc = 0;
588     for (i = 0; i < is->adj.npoints; i++) {
589         int ifree, dx = is->adj.points[i].dx;
590
591         if (!is->adj.points[i].off) continue;
592         poss = POSSIBLES(is->state, dx,
593                          is->adj.points[i].x, is->adj.points[i].y);
594         if (poss == 0) continue;
595         is_orth = INDEX(is->state, gridi,
596                         ISLAND_ORTHX(is,i), ISLAND_ORTHY(is,i));
597         assert(is_orth);
598
599         ifree = is_orth->count - island_countbridges(is_orth);
600         if (ifree > 0) {
601             /*
602              * ifree is the number of bridges unfilled in the other
603              * island, which is clearly an upper bound on the number
604              * of extra bridges this island may run to it.
605              *
606              * Another upper bound is the number of bridges unfilled
607              * on the specific line between here and there. We must
608              * take the minimum of both.
609              */
610             int bmax = MAXIMUM(is->state, dx,
611                                is->adj.points[i].x, is->adj.points[i].y);
612             int bcurr = GRIDCOUNT(is->state,
613                                   is->adj.points[i].x, is->adj.points[i].y,
614                                   dx ? G_LINEH : G_LINEV);
615             assert(bcurr <= bmax);
616             nsurrspc += min(ifree, bmax - bcurr);
617         }
618     }
619     if (nsurrspc < nspc) {
620         debug(("island at (%d,%d) impossible: surr. islands %d spc, need %d.\n",
621                is->x, is->y, nsurrspc, nspc));
622         return 1;       /* not enough spaces around surrounding islands to fill this one. */
623     }
624
625     return 0;
626 }
627
628 /* --- Game parameter functions --- */
629
630 #define DEFAULT_PRESET 0
631
632 const struct game_params bridges_presets[] = {
633   { 7, 7, 2, 30, 10, 1, 0 },
634   { 7, 7, 2, 30, 10, 1, 1 },
635   { 7, 7, 2, 30, 10, 1, 2 },
636   { 10, 10, 2, 30, 10, 1, 0 },
637   { 10, 10, 2, 30, 10, 1, 1 },
638   { 10, 10, 2, 30, 10, 1, 2 },
639   { 15, 15, 2, 30, 10, 1, 0 },
640   { 15, 15, 2, 30, 10, 1, 1 },
641   { 15, 15, 2, 30, 10, 1, 2 },
642 };
643
644 static game_params *default_params(void)
645 {
646     game_params *ret = snew(game_params);
647     *ret = bridges_presets[DEFAULT_PRESET];
648
649     return ret;
650 }
651
652 static int game_fetch_preset(int i, char **name, game_params **params)
653 {
654     game_params *ret;
655     char buf[80];
656
657     if (i < 0 || i >= lenof(bridges_presets))
658         return FALSE;
659
660     ret = default_params();
661     *ret = bridges_presets[i];
662     *params = ret;
663
664     sprintf(buf, "%dx%d %s", ret->w, ret->h,
665             ret->difficulty == 0 ? "easy" :
666             ret->difficulty == 1 ? "medium" : "hard");
667     *name = dupstr(buf);
668
669     return TRUE;
670 }
671
672 static void free_params(game_params *params)
673 {
674     sfree(params);
675 }
676
677 static game_params *dup_params(const game_params *params)
678 {
679     game_params *ret = snew(game_params);
680     *ret = *params;                    /* structure copy */
681     return ret;
682 }
683
684 #define EATNUM(x) do { \
685     (x) = atoi(string); \
686     while (*string && isdigit((unsigned char)*string)) string++; \
687 } while(0)
688
689 static void decode_params(game_params *params, char const *string)
690 {
691     EATNUM(params->w);
692     params->h = params->w;
693     if (*string == 'x') {
694         string++;
695         EATNUM(params->h);
696     }
697     if (*string == 'i') {
698         string++;
699         EATNUM(params->islands);
700     }
701     if (*string == 'e') {
702         string++;
703         EATNUM(params->expansion);
704     }
705     if (*string == 'm') {
706         string++;
707         EATNUM(params->maxb);
708     }
709     params->allowloops = 1;
710     if (*string == 'L') {
711         string++;
712         params->allowloops = 0;
713     }
714     if (*string == 'd') {
715         string++;
716         EATNUM(params->difficulty);
717     }
718 }
719
720 static char *encode_params(const game_params *params, int full)
721 {
722     char buf[80];
723
724     if (full) {
725         sprintf(buf, "%dx%di%de%dm%d%sd%d",
726                 params->w, params->h, params->islands, params->expansion,
727                 params->maxb, params->allowloops ? "" : "L",
728                 params->difficulty);
729     } else {
730         sprintf(buf, "%dx%dm%d%s", params->w, params->h,
731                 params->maxb, params->allowloops ? "" : "L");
732     }
733     return dupstr(buf);
734 }
735
736 static config_item *game_configure(const game_params *params)
737 {
738     config_item *ret;
739     char buf[80];
740
741     ret = snewn(8, config_item);
742
743     ret[0].name = "Width";
744     ret[0].type = C_STRING;
745     sprintf(buf, "%d", params->w);
746     ret[0].sval = dupstr(buf);
747     ret[0].ival = 0;
748
749     ret[1].name = "Height";
750     ret[1].type = C_STRING;
751     sprintf(buf, "%d", params->h);
752     ret[1].sval = dupstr(buf);
753     ret[1].ival = 0;
754
755     ret[2].name = "Difficulty";
756     ret[2].type = C_CHOICES;
757     ret[2].sval = ":Easy:Medium:Hard";
758     ret[2].ival = params->difficulty;
759
760     ret[3].name = "Allow loops";
761     ret[3].type = C_BOOLEAN;
762     ret[3].sval = NULL;
763     ret[3].ival = params->allowloops;
764
765     ret[4].name = "Max. bridges per direction";
766     ret[4].type = C_CHOICES;
767     ret[4].sval = ":1:2:3:4"; /* keep up-to-date with MAX_BRIDGES */
768     ret[4].ival = params->maxb - 1;
769
770     ret[5].name = "%age of island squares";
771     ret[5].type = C_CHOICES;
772     ret[5].sval = ":5%:10%:15%:20%:25%:30%";
773     ret[5].ival = (params->islands / 5)-1;
774
775     ret[6].name = "Expansion factor (%age)";
776     ret[6].type = C_CHOICES;
777     ret[6].sval = ":0%:10%:20%:30%:40%:50%:60%:70%:80%:90%:100%";
778     ret[6].ival = params->expansion / 10;
779
780     ret[7].name = NULL;
781     ret[7].type = C_END;
782     ret[7].sval = NULL;
783     ret[7].ival = 0;
784
785     return ret;
786 }
787
788 static game_params *custom_params(const config_item *cfg)
789 {
790     game_params *ret = snew(game_params);
791
792     ret->w          = atoi(cfg[0].sval);
793     ret->h          = atoi(cfg[1].sval);
794     ret->difficulty = cfg[2].ival;
795     ret->allowloops = cfg[3].ival;
796     ret->maxb       = cfg[4].ival + 1;
797     ret->islands    = (cfg[5].ival + 1) * 5;
798     ret->expansion  = cfg[6].ival * 10;
799
800     return ret;
801 }
802
803 static char *validate_params(const game_params *params, int full)
804 {
805     if (params->w < 3 || params->h < 3)
806         return "Width and height must be at least 3";
807     if (params->maxb < 1 || params->maxb > MAX_BRIDGES)
808         return "Too many bridges.";
809     if (full) {
810         if (params->islands <= 0 || params->islands > 30)
811             return "%age of island squares must be between 1% and 30%";
812         if (params->expansion < 0 || params->expansion > 100)
813             return "Expansion factor must be between 0 and 100";
814     }
815     return NULL;
816 }
817
818 /* --- Game encoding and differences --- */
819
820 static char *encode_game(game_state *state)
821 {
822     char *ret, *p;
823     int wh = state->w*state->h, run, x, y;
824     struct island *is;
825
826     ret = snewn(wh + 1, char);
827     p = ret;
828     run = 0;
829     for (y = 0; y < state->h; y++) {
830         for (x = 0; x < state->w; x++) {
831             is = INDEX(state, gridi, x, y);
832             if (is) {
833                 if (run) {
834                     *p++ = ('a'-1) + run;
835                     run = 0;
836                 }
837                 if (is->count < 10)
838                     *p++ = '0' + is->count;
839                 else
840                     *p++ = 'A' + (is->count - 10);
841             } else {
842                 if (run == 26) {
843                     *p++ = ('a'-1) + run;
844                     run = 0;
845                 }
846                 run++;
847             }
848         }
849     }
850     if (run) {
851         *p++ = ('a'-1) + run;
852         run = 0;
853     }
854     *p = '\0';
855     assert(p - ret <= wh);
856
857     return ret;
858 }
859
860 static char *game_state_diff(const game_state *src, const game_state *dest)
861 {
862     int movesize = 256, movelen = 0;
863     char *move = snewn(movesize, char), buf[80];
864     int i, d, x, y, len;
865     grid_type gline, nline;
866     struct island *is_s, *is_d, *is_orth;
867
868 #define APPEND do {                                     \
869     if (movelen + len >= movesize) {                    \
870         movesize = movelen + len + 256;                 \
871         move = sresize(move, movesize, char);           \
872     }                                                   \
873     strcpy(move + movelen, buf);                        \
874     movelen += len;                                     \
875 } while(0)
876
877     move[movelen++] = 'S';
878     move[movelen] = '\0';
879
880     assert(src->n_islands == dest->n_islands);
881
882     for (i = 0; i < src->n_islands; i++) {
883         is_s = &src->islands[i];
884         is_d = &dest->islands[i];
885         assert(is_s->x == is_d->x);
886         assert(is_s->y == is_d->y);
887         assert(is_s->adj.npoints == is_d->adj.npoints); /* more paranoia */
888
889         for (d = 0; d < is_s->adj.npoints; d++) {
890             if (is_s->adj.points[d].dx == -1 ||
891                 is_s->adj.points[d].dy == -1) continue;
892
893             x = is_s->adj.points[d].x;
894             y = is_s->adj.points[d].y;
895             gline = is_s->adj.points[d].dx ? G_LINEH : G_LINEV;
896             nline = is_s->adj.points[d].dx ? G_NOLINEH : G_NOLINEV;
897             is_orth = INDEX(dest, gridi,
898                             ISLAND_ORTHX(is_d, d), ISLAND_ORTHY(is_d, d));
899
900             if (GRIDCOUNT(src, x, y, gline) != GRIDCOUNT(dest, x, y, gline)) {
901                 assert(is_orth);
902                 len = sprintf(buf, ";L%d,%d,%d,%d,%d",
903                               is_s->x, is_s->y, is_orth->x, is_orth->y,
904                               GRIDCOUNT(dest, x, y, gline));
905                 APPEND;
906             }
907             if ((GRID(src,x,y) & nline) != (GRID(dest, x, y) & nline)) {
908                 assert(is_orth);
909                 len = sprintf(buf, ";N%d,%d,%d,%d",
910                               is_s->x, is_s->y, is_orth->x, is_orth->y);
911                 APPEND;
912             }
913         }
914         if ((GRID(src, is_s->x, is_s->y) & G_MARK) !=
915             (GRID(dest, is_d->x, is_d->y) & G_MARK)) {
916             len = sprintf(buf, ";M%d,%d", is_s->x, is_s->y);
917             APPEND;
918         }
919     }
920     return move;
921 }
922
923 /* --- Game setup and solving utilities --- */
924
925 /* This function is optimised; a Quantify showed that lots of grid-generation time
926  * (>50%) was spent in here. Hence the IDX() stuff. */
927
928 static void map_update_possibles(game_state *state)
929 {
930     int x, y, s, e, bl, i, np, maxb, w = state->w, idx;
931     struct island *is_s = NULL, *is_f = NULL;
932
933     /* Run down vertical stripes [un]setting possv... */
934     for (x = 0; x < state->w; x++) {
935         idx = x;
936         s = e = -1;
937         bl = 0;
938         maxb = state->params.maxb;     /* placate optimiser */
939         /* Unset possible flags until we find an island. */
940         for (y = 0; y < state->h; y++) {
941             is_s = IDX(state, gridi, idx);
942             if (is_s) {
943                 maxb = is_s->count;
944                 break;
945             }
946
947             IDX(state, possv, idx) = 0;
948             idx += w;
949         }
950         for (; y < state->h; y++) {
951             maxb = min(maxb, IDX(state, maxv, idx));
952             is_f = IDX(state, gridi, idx);
953             if (is_f) {
954                 assert(is_s);
955                 np = min(maxb, is_f->count);
956
957                 if (s != -1) {
958                     for (i = s; i <= e; i++) {
959                         INDEX(state, possv, x, i) = bl ? 0 : np;
960                     }
961                 }
962                 s = y+1;
963                 bl = 0;
964                 is_s = is_f;
965                 maxb = is_s->count;
966             } else {
967                 e = y;
968                 if (IDX(state,grid,idx) & (G_LINEH|G_NOLINEV)) bl = 1;
969             }
970             idx += w;
971         }
972         if (s != -1) {
973             for (i = s; i <= e; i++)
974                 INDEX(state, possv, x, i) = 0;
975         }
976     }
977
978     /* ...and now do horizontal stripes [un]setting possh. */
979     /* can we lose this clone'n'hack? */
980     for (y = 0; y < state->h; y++) {
981         idx = y*w;
982         s = e = -1;
983         bl = 0;
984         maxb = state->params.maxb;     /* placate optimiser */
985         for (x = 0; x < state->w; x++) {
986             is_s = IDX(state, gridi, idx);
987             if (is_s) {
988                 maxb = is_s->count;
989                 break;
990             }
991
992             IDX(state, possh, idx) = 0;
993             idx += 1;
994         }
995         for (; x < state->w; x++) {
996             maxb = min(maxb, IDX(state, maxh, idx));
997             is_f = IDX(state, gridi, idx);
998             if (is_f) {
999                 assert(is_s);
1000                 np = min(maxb, is_f->count);
1001
1002                 if (s != -1) {
1003                     for (i = s; i <= e; i++) {
1004                         INDEX(state, possh, i, y) = bl ? 0 : np;
1005                     }
1006                 }
1007                 s = x+1;
1008                 bl = 0;
1009                 is_s = is_f;
1010                 maxb = is_s->count;
1011             } else {
1012                 e = x;
1013                 if (IDX(state,grid,idx) & (G_LINEV|G_NOLINEH)) bl = 1;
1014             }
1015             idx += 1;
1016         }
1017         if (s != -1) {
1018             for (i = s; i <= e; i++)
1019                 INDEX(state, possh, i, y) = 0;
1020         }
1021     }
1022 }
1023
1024 static void map_count(game_state *state)
1025 {
1026     int i, n, ax, ay;
1027     grid_type flag, grid;
1028     struct island *is;
1029
1030     for (i = 0; i < state->n_islands; i++) {
1031         is = &state->islands[i];
1032         is->count = 0;
1033         for (n = 0; n < is->adj.npoints; n++) {
1034             ax = is->adj.points[n].x;
1035             ay = is->adj.points[n].y;
1036             flag = (ax == is->x) ? G_LINEV : G_LINEH;
1037             grid = GRID(state,ax,ay);
1038             if (grid & flag) {
1039                 is->count += INDEX(state,lines,ax,ay);
1040             }
1041         }
1042     }
1043 }
1044
1045 static void map_find_orthogonal(game_state *state)
1046 {
1047     int i;
1048
1049     for (i = 0; i < state->n_islands; i++) {
1050         island_find_orthogonal(&state->islands[i]);
1051     }
1052 }
1053
1054 static int grid_degree(game_state *state, int x, int y, int *nx_r, int *ny_r)
1055 {
1056     grid_type grid = SCRATCH(state, x, y), gline = grid & G_LINE;
1057     struct island *is;
1058     int x1, y1, x2, y2, c = 0, i, nx, ny;
1059
1060     nx = ny = -1; /* placate optimiser */
1061     is = INDEX(state, gridi, x, y);
1062     if (is) {
1063         for (i = 0; i < is->adj.npoints; i++) {
1064             gline = is->adj.points[i].dx ? G_LINEH : G_LINEV;
1065             if (SCRATCH(state,
1066                         is->adj.points[i].x,
1067                         is->adj.points[i].y) & gline) {
1068                 nx = is->adj.points[i].x;
1069                 ny = is->adj.points[i].y;
1070                 c++;
1071             }
1072         }
1073     } else if (gline) {
1074         if (gline & G_LINEV) {
1075             x1 = x2 = x;
1076             y1 = y-1; y2 = y+1;
1077         } else {
1078             x1 = x-1; x2 = x+1;
1079             y1 = y2 = y;
1080         }
1081         /* Non-island squares with edges in should never be pointing off the
1082          * edge of the grid. */
1083         assert(INGRID(state, x1, y1));
1084         assert(INGRID(state, x2, y2));
1085         if (SCRATCH(state, x1, y1) & (gline | G_ISLAND)) {
1086             nx = x1; ny = y1; c++;
1087         }
1088         if (SCRATCH(state, x2, y2) & (gline | G_ISLAND)) {
1089             nx = x2; ny = y2; c++;
1090         }
1091     }
1092     if (c == 1) {
1093         assert(nx != -1 && ny != -1); /* paranoia */
1094         *nx_r = nx; *ny_r = ny;
1095     }
1096     return c;
1097 }
1098
1099 static int map_hasloops(game_state *state, int mark)
1100 {
1101     int x, y, ox, oy, nx = 0, ny = 0, loop = 0;
1102
1103     memcpy(state->scratch, state->grid, GRIDSZ(state));
1104
1105     /* This algorithm is actually broken; if there are two loops connected
1106      * by bridges this will also highlight bridges. The correct algorithm
1107      * uses a dsf and a two-pass edge-detection algorithm (see check_correct
1108      * in slant.c); this is BALGE for now, especially since disallow-loops
1109      * is not the default for this puzzle. If we want to fix this later then
1110      * copy the alg in slant.c to the empty statement in map_group. */
1111
1112     /* Remove all 1-degree edges. */
1113     for (y = 0; y < state->h; y++) {
1114         for (x = 0; x < state->w; x++) {
1115             ox = x; oy = y;
1116             while (grid_degree(state, ox, oy, &nx, &ny) == 1) {
1117                 /*debug(("hasloops: removing 1-degree at (%d,%d).\n", ox, oy));*/
1118                 SCRATCH(state, ox, oy) &= ~(G_LINE|G_ISLAND);
1119                 ox = nx; oy = ny;
1120             }
1121         }
1122     }
1123     /* Mark any remaining edges as G_WARN, if required. */
1124     for (x = 0; x < state->w; x++) {
1125         for (y = 0; y < state->h; y++) {
1126             if (GRID(state,x,y) & G_ISLAND) continue;
1127
1128             if (SCRATCH(state, x, y) & G_LINE) {
1129                 if (mark) {
1130                     /*debug(("hasloops: marking loop square at (%d,%d).\n",
1131                            x, y));*/
1132                     GRID(state,x,y) |= G_WARN;
1133                     loop = 1;
1134                 } else
1135                     return 1; /* short-cut as soon as we find one */
1136             } else {
1137                 if (mark)
1138                     GRID(state,x,y) &= ~G_WARN;
1139             }
1140         }
1141     }
1142     return loop;
1143 }
1144
1145 static void map_group(game_state *state)
1146 {
1147     int i, wh = state->w*state->h, d1, d2;
1148     int x, y, x2, y2;
1149     int *dsf = state->solver->dsf;
1150     struct island *is, *is_join;
1151
1152     /* Initialise dsf. */
1153     dsf_init(dsf, wh);
1154
1155     /* For each island, find connected islands right or down
1156      * and merge the dsf for the island squares as well as the
1157      * bridge squares. */
1158     for (x = 0; x < state->w; x++) {
1159         for (y = 0; y < state->h; y++) {
1160             GRID(state,x,y) &= ~(G_SWEEP|G_WARN); /* for group_full. */
1161
1162             is = INDEX(state, gridi, x, y);
1163             if (!is) continue;
1164             d1 = DINDEX(x,y);
1165             for (i = 0; i < is->adj.npoints; i++) {
1166                 /* only want right/down */
1167                 if (is->adj.points[i].dx == -1 ||
1168                     is->adj.points[i].dy == -1) continue;
1169
1170                 is_join = island_find_connection(is, i);
1171                 if (!is_join) continue;
1172
1173                 d2 = DINDEX(is_join->x, is_join->y);
1174                 if (dsf_canonify(dsf,d1) == dsf_canonify(dsf,d2)) {
1175                     ; /* we have a loop. See comment in map_hasloops. */
1176                     /* However, we still want to merge all squares joining
1177                      * this side-that-makes-a-loop. */
1178                 }
1179                 /* merge all squares between island 1 and island 2. */
1180                 for (x2 = x; x2 <= is_join->x; x2++) {
1181                     for (y2 = y; y2 <= is_join->y; y2++) {
1182                         d2 = DINDEX(x2,y2);
1183                         if (d1 != d2) dsf_merge(dsf,d1,d2);
1184                     }
1185                 }
1186             }
1187         }
1188     }
1189 }
1190
1191 static int map_group_check(game_state *state, int canon, int warn,
1192                            int *nislands_r)
1193 {
1194     int *dsf = state->solver->dsf, nislands = 0;
1195     int x, y, i, allfull = 1;
1196     struct island *is;
1197
1198     for (i = 0; i < state->n_islands; i++) {
1199         is = &state->islands[i];
1200         if (dsf_canonify(dsf, DINDEX(is->x,is->y)) != canon) continue;
1201
1202         GRID(state, is->x, is->y) |= G_SWEEP;
1203         nislands++;
1204         if (island_countbridges(is) != is->count)
1205             allfull = 0;
1206     }
1207     if (warn && allfull && nislands != state->n_islands) {
1208         /* we're full and this island group isn't the whole set.
1209          * Mark all squares with this dsf canon as ERR. */
1210         for (x = 0; x < state->w; x++) {
1211             for (y = 0; y < state->h; y++) {
1212                 if (dsf_canonify(dsf, DINDEX(x,y)) == canon) {
1213                     GRID(state,x,y) |= G_WARN;
1214                 }
1215             }
1216         }
1217
1218     }
1219     if (nislands_r) *nislands_r = nislands;
1220     return allfull;
1221 }
1222
1223 static int map_group_full(game_state *state, int *ngroups_r)
1224 {
1225     int *dsf = state->solver->dsf, ngroups = 0;
1226     int i, anyfull = 0;
1227     struct island *is;
1228
1229     /* NB this assumes map_group (or sth else) has cleared G_SWEEP. */
1230
1231     for (i = 0; i < state->n_islands; i++) {
1232         is = &state->islands[i];
1233         if (GRID(state,is->x,is->y) & G_SWEEP) continue;
1234
1235         ngroups++;
1236         if (map_group_check(state, dsf_canonify(dsf, DINDEX(is->x,is->y)),
1237                             1, NULL))
1238             anyfull = 1;
1239     }
1240
1241     *ngroups_r = ngroups;
1242     return anyfull;
1243 }
1244
1245 static int map_check(game_state *state)
1246 {
1247     int ngroups;
1248
1249     /* Check for loops, if necessary. */
1250     if (!state->allowloops) {
1251         if (map_hasloops(state, 1))
1252             return 0;
1253     }
1254
1255     /* Place islands into island groups and check for early
1256      * satisfied-groups. */
1257     map_group(state); /* clears WARN and SWEEP */
1258     if (map_group_full(state, &ngroups)) {
1259         if (ngroups == 1) return 1;
1260     }
1261     return 0;
1262 }
1263
1264 static void map_clear(game_state *state)
1265 {
1266     int x, y;
1267
1268     for (x = 0; x < state->w; x++) {
1269         for (y = 0; y < state->h; y++) {
1270             /* clear most flags; might want to be slightly more careful here. */
1271             GRID(state,x,y) &= G_ISLAND;
1272         }
1273     }
1274 }
1275
1276 static void solve_join(struct island *is, int direction, int n, int is_max)
1277 {
1278     struct island *is_orth;
1279     int d1, d2, *dsf = is->state->solver->dsf;
1280     game_state *state = is->state; /* for DINDEX */
1281
1282     is_orth = INDEX(is->state, gridi,
1283                     ISLAND_ORTHX(is, direction),
1284                     ISLAND_ORTHY(is, direction));
1285     assert(is_orth);
1286     /*debug(("...joining (%d,%d) to (%d,%d) with %d bridge(s).\n",
1287            is->x, is->y, is_orth->x, is_orth->y, n));*/
1288     island_join(is, is_orth, n, is_max);
1289
1290     if (n > 0 && !is_max) {
1291         d1 = DINDEX(is->x, is->y);
1292         d2 = DINDEX(is_orth->x, is_orth->y);
1293         if (dsf_canonify(dsf, d1) != dsf_canonify(dsf, d2))
1294             dsf_merge(dsf, d1, d2);
1295     }
1296 }
1297
1298 static int solve_fillone(struct island *is)
1299 {
1300     int i, nadded = 0;
1301
1302     debug(("solve_fillone for island (%d,%d).\n", is->x, is->y));
1303
1304     for (i = 0; i < is->adj.npoints; i++) {
1305         if (island_isadj(is, i)) {
1306             if (island_hasbridge(is, i)) {
1307                 /* already attached; do nothing. */;
1308             } else {
1309                 solve_join(is, i, 1, 0);
1310                 nadded++;
1311             }
1312         }
1313     }
1314     return nadded;
1315 }
1316
1317 static int solve_fill(struct island *is)
1318 {
1319     /* for each unmarked adjacent, make sure we convert every possible bridge
1320      * to a real one, and then work out the possibles afresh. */
1321     int i, nnew, ncurr, nadded = 0, missing;
1322
1323     debug(("solve_fill for island (%d,%d).\n", is->x, is->y));
1324
1325     missing = is->count - island_countbridges(is);
1326     if (missing < 0) return 0;
1327
1328     /* very like island_countspaces. */
1329     for (i = 0; i < is->adj.npoints; i++) {
1330         nnew = island_adjspace(is, 1, missing, i);
1331         if (nnew) {
1332             ncurr = GRIDCOUNT(is->state,
1333                               is->adj.points[i].x, is->adj.points[i].y,
1334                               is->adj.points[i].dx ? G_LINEH : G_LINEV);
1335
1336             solve_join(is, i, nnew + ncurr, 0);
1337             nadded += nnew;
1338         }
1339     }
1340     return nadded;
1341 }
1342
1343 static int solve_island_stage1(struct island *is, int *didsth_r)
1344 {
1345     int bridges = island_countbridges(is);
1346     int nspaces = island_countspaces(is, 1);
1347     int nadj = island_countadj(is);
1348     int didsth = 0;
1349
1350     assert(didsth_r);
1351
1352     /*debug(("island at (%d,%d) filled %d/%d (%d spc) nadj %d\n",
1353            is->x, is->y, bridges, is->count, nspaces, nadj));*/
1354     if (bridges > is->count) {
1355         /* We only ever add bridges when we're sure they fit, or that's
1356          * the only place they can go. If we've added bridges such that
1357          * another island has become wrong, the puzzle must not have had
1358          * a solution. */
1359         debug(("...island at (%d,%d) is overpopulated!\n", is->x, is->y));
1360         return 0;
1361     } else if (bridges == is->count) {
1362         /* This island is full. Make sure it's marked (and update
1363          * possibles if we did). */
1364         if (!(GRID(is->state, is->x, is->y) & G_MARK)) {
1365             debug(("...marking island (%d,%d) as full.\n", is->x, is->y));
1366             island_togglemark(is);
1367             didsth = 1;
1368         }
1369     } else if (GRID(is->state, is->x, is->y) & G_MARK) {
1370         debug(("...island (%d,%d) is marked but unfinished!\n",
1371                is->x, is->y));
1372         return 0; /* island has been marked unfinished; no solution from here. */
1373     } else {
1374         /* This is the interesting bit; we try and fill in more information
1375          * about this island. */
1376         if (is->count == bridges + nspaces) {
1377             if (solve_fill(is) > 0) didsth = 1;
1378         } else if (is->count > ((nadj-1) * is->state->maxb)) {
1379             /* must have at least one bridge in each possible direction. */
1380             if (solve_fillone(is) > 0) didsth = 1;
1381         }
1382     }
1383     if (didsth) {
1384         map_update_possibles(is->state);
1385         *didsth_r = 1;
1386     }
1387     return 1;
1388 }
1389
1390 /* returns non-zero if a new line here would cause a loop. */
1391 static int solve_island_checkloop(struct island *is, int direction)
1392 {
1393     struct island *is_orth;
1394     int *dsf = is->state->solver->dsf, d1, d2;
1395     game_state *state = is->state;
1396
1397     if (is->state->allowloops) return 0; /* don't care anyway */
1398     if (island_hasbridge(is, direction)) return 0; /* already has a bridge */
1399     if (island_isadj(is, direction) == 0) return 0; /* no adj island */
1400
1401     is_orth = INDEX(is->state, gridi,
1402                     ISLAND_ORTHX(is,direction),
1403                     ISLAND_ORTHY(is,direction));
1404     if (!is_orth) return 0;
1405
1406     d1 = DINDEX(is->x, is->y);
1407     d2 = DINDEX(is_orth->x, is_orth->y);
1408     if (dsf_canonify(dsf, d1) == dsf_canonify(dsf, d2)) {
1409         /* two islands are connected already; don't join them. */
1410         return 1;
1411     }
1412     return 0;
1413 }
1414
1415 static int solve_island_stage2(struct island *is, int *didsth_r)
1416 {
1417     int added = 0, removed = 0, navail = 0, nadj, i;
1418
1419     assert(didsth_r);
1420
1421     for (i = 0; i < is->adj.npoints; i++) {
1422         if (solve_island_checkloop(is, i)) {
1423             debug(("removing possible loop at (%d,%d) direction %d.\n",
1424                    is->x, is->y, i));
1425             solve_join(is, i, -1, 0);
1426             map_update_possibles(is->state);
1427             removed = 1;
1428         } else {
1429             navail += island_isadj(is, i);
1430             /*debug(("stage2: navail for (%d,%d) direction (%d,%d) is %d.\n",
1431                    is->x, is->y,
1432                    is->adj.points[i].dx, is->adj.points[i].dy,
1433                    island_isadj(is, i)));*/
1434         }
1435     }
1436
1437     /*debug(("island at (%d,%d) navail %d: checking...\n", is->x, is->y, navail));*/
1438
1439     for (i = 0; i < is->adj.npoints; i++) {
1440         if (!island_hasbridge(is, i)) {
1441             nadj = island_isadj(is, i);
1442             if (nadj > 0 && (navail - nadj) < is->count) {
1443                 /* we couldn't now complete the island without at
1444                  * least one bridge here; put it in. */
1445                 /*debug(("nadj %d, navail %d, is->count %d.\n",
1446                        nadj, navail, is->count));*/
1447                 debug(("island at (%d,%d) direction (%d,%d) must have 1 bridge\n",
1448                        is->x, is->y,
1449                        is->adj.points[i].dx, is->adj.points[i].dy));
1450                 solve_join(is, i, 1, 0);
1451                 added = 1;
1452                 /*debug_state(is->state);
1453                 debug_possibles(is->state);*/
1454             }
1455         }
1456     }
1457     if (added) map_update_possibles(is->state);
1458     if (added || removed) *didsth_r = 1;
1459     return 1;
1460 }
1461
1462 static int solve_island_subgroup(struct island *is, int direction)
1463 {
1464     struct island *is_join;
1465     int nislands, *dsf = is->state->solver->dsf;
1466     game_state *state = is->state;
1467
1468     debug(("..checking subgroups.\n"));
1469
1470     /* if is isn't full, return 0. */
1471     if (island_countbridges(is) < is->count) {
1472         debug(("...orig island (%d,%d) not full.\n", is->x, is->y));
1473         return 0;
1474     }
1475
1476     if (direction >= 0) {
1477         is_join = INDEX(state, gridi,
1478                         ISLAND_ORTHX(is, direction),
1479                         ISLAND_ORTHY(is, direction));
1480         assert(is_join);
1481
1482         /* if is_join isn't full, return 0. */
1483         if (island_countbridges(is_join) < is_join->count) {
1484             debug(("...dest island (%d,%d) not full.\n",
1485                    is_join->x, is_join->y));
1486             return 0;
1487         }
1488     }
1489
1490     /* Check group membership for is->dsf; if it's full return 1. */
1491     if (map_group_check(state, dsf_canonify(dsf, DINDEX(is->x,is->y)),
1492                         0, &nislands)) {
1493         if (nislands < state->n_islands) {
1494             /* we have a full subgroup that isn't the whole set.
1495              * This isn't allowed. */
1496             debug(("island at (%d,%d) makes full subgroup, disallowing.\n",
1497                    is->x, is->y));
1498             return 1;
1499         } else {
1500             debug(("...has finished puzzle.\n"));
1501         }
1502     }
1503     return 0;
1504 }
1505
1506 static int solve_island_impossible(game_state *state)
1507 {
1508     struct island *is;
1509     int i;
1510
1511     /* If any islands are impossible, return 1. */
1512     for (i = 0; i < state->n_islands; i++) {
1513         is = &state->islands[i];
1514         if (island_impossible(is, 0)) {
1515             debug(("island at (%d,%d) has become impossible, disallowing.\n",
1516                    is->x, is->y));
1517             return 1;
1518         }
1519     }
1520     return 0;
1521 }
1522
1523 /* Bear in mind that this function is really rather inefficient. */
1524 static int solve_island_stage3(struct island *is, int *didsth_r)
1525 {
1526     int i, n, x, y, missing, spc, curr, maxb, didsth = 0;
1527     int wh = is->state->w * is->state->h;
1528     struct solver_state *ss = is->state->solver;
1529
1530     assert(didsth_r);
1531
1532     missing = is->count - island_countbridges(is);
1533     if (missing <= 0) return 1;
1534
1535     for (i = 0; i < is->adj.npoints; i++) {
1536         x = is->adj.points[i].x;
1537         y = is->adj.points[i].y;
1538         spc = island_adjspace(is, 1, missing, i);
1539         if (spc == 0) continue;
1540
1541         curr = GRIDCOUNT(is->state, x, y,
1542                          is->adj.points[i].dx ? G_LINEH : G_LINEV);
1543         debug(("island at (%d,%d) s3, trying %d - %d bridges.\n",
1544                is->x, is->y, curr+1, curr+spc));
1545
1546         /* Now we know that this island could have more bridges,
1547          * to bring the total from curr+1 to curr+spc. */
1548         maxb = -1;
1549         /* We have to squirrel the dsf away and restore it afterwards;
1550          * it is additive only, and can't be removed from. */
1551         memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int));
1552         for (n = curr+1; n <= curr+spc; n++) {
1553             solve_join(is, i, n, 0);
1554             map_update_possibles(is->state);
1555
1556             if (solve_island_subgroup(is, i) ||
1557                 solve_island_impossible(is->state)) {
1558                 maxb = n-1;
1559                 debug(("island at (%d,%d) d(%d,%d) new max of %d bridges:\n",
1560                        is->x, is->y,
1561                        is->adj.points[i].dx, is->adj.points[i].dy,
1562                        maxb));
1563                 break;
1564             }
1565         }
1566         solve_join(is, i, curr, 0); /* put back to before. */
1567         memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int));
1568
1569         if (maxb != -1) {
1570             /*debug_state(is->state);*/
1571             if (maxb == 0) {
1572                 debug(("...adding NOLINE.\n"));
1573                 solve_join(is, i, -1, 0); /* we can't have any bridges here. */
1574             } else {
1575                 debug(("...setting maximum\n"));
1576                 solve_join(is, i, maxb, 1);
1577             }
1578             didsth = 1;
1579         }
1580         map_update_possibles(is->state);
1581     }
1582
1583     for (i = 0; i < is->adj.npoints; i++) {
1584         /*
1585          * Now check to see if any currently empty direction must have
1586          * at least one bridge in order to avoid forming an isolated
1587          * subgraph. This differs from the check above in that it
1588          * considers multiple target islands. For example:
1589          *
1590          *   2   2    4
1591          *                                  1     3     2
1592          *       3
1593          *                                        4
1594          *
1595          * The example on the left can be handled by the above loop:
1596          * it will observe that connecting the central 2 twice to the
1597          * left would form an isolated subgraph, and hence it will
1598          * restrict that 2 to at most one bridge in that direction.
1599          * But the example on the right won't be handled by that loop,
1600          * because the deduction requires us to imagine connecting the
1601          * 3 to _both_ the 1 and 2 at once to form an isolated
1602          * subgraph.
1603          *
1604          * This pass is necessary _as well_ as the above one, because
1605          * neither can do the other's job. In the left one,
1606          * restricting the direction which _would_ cause trouble can
1607          * be done even if it's not yet clear which of the remaining
1608          * directions has to have a compensatory bridge; whereas the
1609          * pass below that can handle the right-hand example does need
1610          * to know what direction to point the necessary bridge in.
1611          *
1612          * Neither pass can handle the most general case, in which we
1613          * observe that an arbitrary subset of an island's neighbours
1614          * would form an isolated subgraph with it if it connected
1615          * maximally to them, and hence that at least one bridge must
1616          * point to some neighbour outside that subset but we don't
1617          * know which neighbour. To handle that, we'd have to have a
1618          * richer data format for the solver, which could cope with
1619          * recording the idea that at least one of two edges must have
1620          * a bridge.
1621          */
1622         int got = 0;
1623         int before[4];
1624         int j;
1625
1626         spc = island_adjspace(is, 1, missing, i);
1627         if (spc == 0) continue;
1628
1629         for (j = 0; j < is->adj.npoints; j++)
1630             before[j] = GRIDCOUNT(is->state,
1631                                   is->adj.points[j].x,
1632                                   is->adj.points[j].y,
1633                                   is->adj.points[j].dx ? G_LINEH : G_LINEV);
1634         if (before[i] != 0) continue;  /* this idea is pointless otherwise */
1635
1636         memcpy(ss->tmpdsf, ss->dsf, wh*sizeof(int));
1637
1638         for (j = 0; j < is->adj.npoints; j++) {
1639             spc = island_adjspace(is, 1, missing, j);
1640             if (spc == 0) continue;
1641             if (j == i) continue;
1642             solve_join(is, j, before[j] + spc, 0);
1643         }
1644         map_update_possibles(is->state);
1645
1646         if (solve_island_subgroup(is, -1))
1647             got = 1;
1648
1649         for (j = 0; j < is->adj.npoints; j++)
1650             solve_join(is, j, before[j], 0);
1651         memcpy(ss->dsf, ss->tmpdsf, wh*sizeof(int));
1652
1653         if (got) {
1654             debug(("island at (%d,%d) must connect in direction (%d,%d) to"
1655                    " avoid full subgroup.\n",
1656                    is->x, is->y, is->adj.points[i].dx, is->adj.points[i].dy));
1657             solve_join(is, i, 1, 0);
1658             didsth = 1;
1659         }
1660
1661         map_update_possibles(is->state);
1662     }
1663
1664     if (didsth) *didsth_r = didsth;
1665     return 1;
1666 }
1667
1668 #define CONTINUE_IF_FULL do {                           \
1669 if (GRID(state, is->x, is->y) & G_MARK) {            \
1670     /* island full, don't try fixing it */           \
1671     continue;                                        \
1672 } } while(0)
1673
1674 static int solve_sub(game_state *state, int difficulty, int depth)
1675 {
1676     struct island *is;
1677     int i, didsth;
1678
1679     while (1) {
1680         didsth = 0;
1681
1682         /* First island iteration: things we can work out by looking at
1683          * properties of the island as a whole. */
1684         for (i = 0; i < state->n_islands; i++) {
1685             is = &state->islands[i];
1686             if (!solve_island_stage1(is, &didsth)) return 0;
1687         }
1688         if (didsth) continue;
1689         else if (difficulty < 1) break;
1690
1691         /* Second island iteration: thing we can work out by looking at
1692          * properties of individual island connections. */
1693         for (i = 0; i < state->n_islands; i++) {
1694             is = &state->islands[i];
1695             CONTINUE_IF_FULL;
1696             if (!solve_island_stage2(is, &didsth)) return 0;
1697         }
1698         if (didsth) continue;
1699         else if (difficulty < 2) break;
1700
1701         /* Third island iteration: things we can only work out by looking
1702          * at groups of islands. */
1703         for (i = 0; i < state->n_islands; i++) {
1704             is = &state->islands[i];
1705             if (!solve_island_stage3(is, &didsth)) return 0;
1706         }
1707         if (didsth) continue;
1708         else if (difficulty < 3) break;
1709
1710         /* If we can be bothered, write a recursive solver to finish here. */
1711         break;
1712     }
1713     if (map_check(state)) return 1; /* solved it */
1714     return 0;
1715 }
1716
1717 static void solve_for_hint(game_state *state)
1718 {
1719     map_group(state);
1720     solve_sub(state, 10, 0);
1721 }
1722
1723 static int solve_from_scratch(game_state *state, int difficulty)
1724 {
1725     map_clear(state);
1726     map_group(state);
1727     map_update_possibles(state);
1728     return solve_sub(state, difficulty, 0);
1729 }
1730
1731 /* --- New game functions --- */
1732
1733 static game_state *new_state(const game_params *params)
1734 {
1735     game_state *ret = snew(game_state);
1736     int wh = params->w * params->h, i;
1737
1738     ret->w = params->w;
1739     ret->h = params->h;
1740     ret->allowloops = params->allowloops;
1741     ret->maxb = params->maxb;
1742     ret->params = *params;
1743
1744     ret->grid = snewn(wh, grid_type);
1745     memset(ret->grid, 0, GRIDSZ(ret));
1746     ret->scratch = snewn(wh, grid_type);
1747     memset(ret->scratch, 0, GRIDSZ(ret));
1748
1749     ret->wha = snewn(wh*N_WH_ARRAYS, char);
1750     memset(ret->wha, 0, wh*N_WH_ARRAYS*sizeof(char));
1751
1752     ret->possv = ret->wha;
1753     ret->possh = ret->wha + wh;
1754     ret->lines = ret->wha + wh*2;
1755     ret->maxv = ret->wha + wh*3;
1756     ret->maxh = ret->wha + wh*4;
1757
1758     memset(ret->maxv, ret->maxb, wh*sizeof(char));
1759     memset(ret->maxh, ret->maxb, wh*sizeof(char));
1760
1761     ret->islands = NULL;
1762     ret->n_islands = 0;
1763     ret->n_islands_alloc = 0;
1764
1765     ret->gridi = snewn(wh, struct island *);
1766     for (i = 0; i < wh; i++) ret->gridi[i] = NULL;
1767
1768     ret->solved = ret->completed = 0;
1769
1770     ret->solver = snew(struct solver_state);
1771     ret->solver->dsf = snew_dsf(wh);
1772     ret->solver->tmpdsf = snewn(wh, int);
1773
1774     ret->solver->refcount = 1;
1775
1776     return ret;
1777 }
1778
1779 static game_state *dup_game(const game_state *state)
1780 {
1781     game_state *ret = snew(game_state);
1782     int wh = state->w*state->h;
1783
1784     ret->w = state->w;
1785     ret->h = state->h;
1786     ret->allowloops = state->allowloops;
1787     ret->maxb = state->maxb;
1788     ret->params = state->params;
1789
1790     ret->grid = snewn(wh, grid_type);
1791     memcpy(ret->grid, state->grid, GRIDSZ(ret));
1792     ret->scratch = snewn(wh, grid_type);
1793     memcpy(ret->scratch, state->scratch, GRIDSZ(ret));
1794
1795     ret->wha = snewn(wh*N_WH_ARRAYS, char);
1796     memcpy(ret->wha, state->wha, wh*N_WH_ARRAYS*sizeof(char));
1797
1798     ret->possv = ret->wha;
1799     ret->possh = ret->wha + wh;
1800     ret->lines = ret->wha + wh*2;
1801     ret->maxv = ret->wha + wh*3;
1802     ret->maxh = ret->wha + wh*4;
1803
1804     ret->islands = snewn(state->n_islands, struct island);
1805     memcpy(ret->islands, state->islands, state->n_islands * sizeof(struct island));
1806     ret->n_islands = ret->n_islands_alloc = state->n_islands;
1807
1808     ret->gridi = snewn(wh, struct island *);
1809     fixup_islands_for_realloc(ret);
1810
1811     ret->solved = state->solved;
1812     ret->completed = state->completed;
1813
1814     ret->solver = state->solver;
1815     ret->solver->refcount++;
1816
1817     return ret;
1818 }
1819
1820 static void free_game(game_state *state)
1821 {
1822     if (--state->solver->refcount <= 0) {
1823         sfree(state->solver->dsf);
1824         sfree(state->solver->tmpdsf);
1825         sfree(state->solver);
1826     }
1827
1828     sfree(state->islands);
1829     sfree(state->gridi);
1830
1831     sfree(state->wha);
1832
1833     sfree(state->scratch);
1834     sfree(state->grid);
1835     sfree(state);
1836 }
1837
1838 #define MAX_NEWISLAND_TRIES     50
1839 #define MIN_SENSIBLE_ISLANDS    3
1840
1841 #define ORDER(a,b) do { if (a < b) { int tmp=a; int a=b; int b=tmp; } } while(0)
1842
1843 static char *new_game_desc(const game_params *params, random_state *rs,
1844                            char **aux, int interactive)
1845 {
1846     game_state *tobuild  = NULL;
1847     int i, j, wh = params->w * params->h, x, y, dx, dy;
1848     int minx, miny, maxx, maxy, joinx, joiny, newx, newy, diffx, diffy;
1849     int ni_req = max((params->islands * wh) / 100, MIN_SENSIBLE_ISLANDS), ni_curr, ni_bad;
1850     struct island *is, *is2;
1851     char *ret;
1852     unsigned int echeck;
1853
1854     /* pick a first island position randomly. */
1855 generate:
1856     if (tobuild) free_game(tobuild);
1857     tobuild = new_state(params);
1858
1859     x = random_upto(rs, params->w);
1860     y = random_upto(rs, params->h);
1861     island_add(tobuild, x, y, 0);
1862     ni_curr = 1;
1863     ni_bad = 0;
1864     debug(("Created initial island at (%d,%d).\n", x, y));
1865
1866     while (ni_curr < ni_req) {
1867         /* Pick a random island to try and extend from. */
1868         i = random_upto(rs, tobuild->n_islands);
1869         is = &tobuild->islands[i];
1870
1871         /* Pick a random direction to extend in. */
1872         j = random_upto(rs, is->adj.npoints);
1873         dx = is->adj.points[j].x - is->x;
1874         dy = is->adj.points[j].y - is->y;
1875
1876         /* Find out limits of where we could put a new island. */
1877         joinx = joiny = -1;
1878         minx = is->x + 2*dx; miny = is->y + 2*dy; /* closest is 2 units away. */
1879         x = is->x+dx; y = is->y+dy;
1880         if (GRID(tobuild,x,y) & (G_LINEV|G_LINEH)) {
1881             /* already a line next to the island, continue. */
1882             goto bad;
1883         }
1884         while (1) {
1885             if (x < 0 || x >= params->w || y < 0 || y >= params->h) {
1886                 /* got past the edge; put a possible at the island
1887                  * and exit. */
1888                 maxx = x-dx; maxy = y-dy;
1889                 goto foundmax;
1890             }
1891             if (GRID(tobuild,x,y) & G_ISLAND) {
1892                 /* could join up to an existing island... */
1893                 joinx = x; joiny = y;
1894                 /* ... or make a new one 2 spaces away. */
1895                 maxx = x - 2*dx; maxy = y - 2*dy;
1896                 goto foundmax;
1897             } else if (GRID(tobuild,x,y) & (G_LINEV|G_LINEH)) {
1898                 /* could make a new one 1 space away from the line. */
1899                 maxx = x - dx; maxy = y - dy;
1900                 goto foundmax;
1901             }
1902             x += dx; y += dy;
1903         }
1904
1905 foundmax:
1906         debug(("Island at (%d,%d) with d(%d,%d) has new positions "
1907                "(%d,%d) -> (%d,%d), join (%d,%d).\n",
1908                is->x, is->y, dx, dy, minx, miny, maxx, maxy, joinx, joiny));
1909         /* Now we know where we could either put a new island
1910          * (between min and max), or (if loops are allowed) could join on
1911          * to an existing island (at join). */
1912         if (params->allowloops && joinx != -1 && joiny != -1) {
1913             if (random_upto(rs, 100) < (unsigned long)params->expansion) {
1914                 is2 = INDEX(tobuild, gridi, joinx, joiny);
1915                 debug(("Joining island at (%d,%d) to (%d,%d).\n",
1916                        is->x, is->y, is2->x, is2->y));
1917                 goto join;
1918             }
1919         }
1920         diffx = (maxx - minx) * dx;
1921         diffy = (maxy - miny) * dy;
1922         if (diffx < 0 || diffy < 0)  goto bad;
1923         if (random_upto(rs,100) < (unsigned long)params->expansion) {
1924             newx = maxx; newy = maxy;
1925             debug(("Creating new island at (%d,%d) (expanded).\n", newx, newy));
1926         } else {
1927             newx = minx + random_upto(rs,diffx+1)*dx;
1928             newy = miny + random_upto(rs,diffy+1)*dy;
1929             debug(("Creating new island at (%d,%d).\n", newx, newy));
1930         }
1931         /* check we're not next to island in the other orthogonal direction. */
1932         if ((INGRID(tobuild,newx+dy,newy+dx) && (GRID(tobuild,newx+dy,newy+dx) & G_ISLAND)) ||
1933             (INGRID(tobuild,newx-dy,newy-dx) && (GRID(tobuild,newx-dy,newy-dx) & G_ISLAND))) {
1934             debug(("New location is adjacent to island, skipping.\n"));
1935             goto bad;
1936         }
1937         is2 = island_add(tobuild, newx, newy, 0);
1938         /* Must get is again at this point; the array might have
1939          * been realloced by island_add... */
1940         is = &tobuild->islands[i]; /* ...but order will not change. */
1941
1942         ni_curr++; ni_bad = 0;
1943 join:
1944         island_join(is, is2, random_upto(rs, tobuild->maxb)+1, 0);
1945         debug_state(tobuild);
1946         continue;
1947
1948 bad:
1949         ni_bad++;
1950         if (ni_bad > MAX_NEWISLAND_TRIES) {
1951             debug(("Unable to create any new islands after %d tries; "
1952                    "created %d [%d%%] (instead of %d [%d%%] requested).\n",
1953                    MAX_NEWISLAND_TRIES,
1954                    ni_curr, ni_curr * 100 / wh,
1955                    ni_req, ni_req * 100 / wh));
1956             goto generated;
1957         }
1958     }
1959
1960 generated:
1961     if (ni_curr == 1) {
1962         debug(("Only generated one island (!), retrying.\n"));
1963         goto generate;
1964     }
1965     /* Check we have at least one island on each extremity of the grid. */
1966     echeck = 0;
1967     for (x = 0; x < params->w; x++) {
1968         if (INDEX(tobuild, gridi, x, 0))           echeck |= 1;
1969         if (INDEX(tobuild, gridi, x, params->h-1)) echeck |= 2;
1970     }
1971     for (y = 0; y < params->h; y++) {
1972         if (INDEX(tobuild, gridi, 0,           y)) echeck |= 4;
1973         if (INDEX(tobuild, gridi, params->w-1, y)) echeck |= 8;
1974     }
1975     if (echeck != 15) {
1976         debug(("Generated grid doesn't fill to sides, retrying.\n"));
1977         goto generate;
1978     }
1979
1980     map_count(tobuild);
1981     map_find_orthogonal(tobuild);
1982
1983     if (params->difficulty > 0) {
1984         if ((ni_curr > MIN_SENSIBLE_ISLANDS) &&
1985             (solve_from_scratch(tobuild, params->difficulty-1) > 0)) {
1986             debug(("Grid is solvable at difficulty %d (too easy); retrying.\n",
1987                    params->difficulty-1));
1988             goto generate;
1989         }
1990     }
1991
1992     if (solve_from_scratch(tobuild, params->difficulty) == 0) {
1993         debug(("Grid not solvable at difficulty %d, (too hard); retrying.\n",
1994                params->difficulty));
1995         goto generate;
1996     }
1997
1998     /* ... tobuild is now solved. We rely on this making the diff for aux. */
1999     debug_state(tobuild);
2000     ret = encode_game(tobuild);
2001     {
2002         game_state *clean = dup_game(tobuild);
2003         map_clear(clean);
2004         map_update_possibles(clean);
2005         *aux = game_state_diff(clean, tobuild);
2006         free_game(clean);
2007     }
2008     free_game(tobuild);
2009
2010     return ret;
2011 }
2012
2013 static char *validate_desc(const game_params *params, const char *desc)
2014 {
2015     int i, wh = params->w * params->h;
2016
2017     for (i = 0; i < wh; i++) {
2018         if (*desc >= '1' && *desc <= '9')
2019             /* OK */;
2020         else if (*desc >= 'a' && *desc <= 'z')
2021             i += *desc - 'a'; /* plus the i++ */
2022         else if (*desc >= 'A' && *desc <= 'G')
2023             /* OK */;
2024         else if (*desc == 'V' || *desc == 'W' ||
2025                  *desc == 'X' || *desc == 'Y' ||
2026                  *desc == 'H' || *desc == 'I' ||
2027                  *desc == 'J' || *desc == 'K')
2028             /* OK */;
2029         else if (!*desc)
2030             return "Game description shorter than expected";
2031         else
2032             return "Game description contains unexpected character";
2033         desc++;
2034     }
2035     if (*desc || i > wh)
2036         return "Game description longer than expected";
2037
2038     return NULL;
2039 }
2040
2041 static game_state *new_game_sub(const game_params *params, const char *desc)
2042 {
2043     game_state *state = new_state(params);
2044     int x, y, run = 0;
2045
2046     debug(("new_game[_sub]: desc = '%s'.\n", desc));
2047
2048     for (y = 0; y < params->h; y++) {
2049         for (x = 0; x < params->w; x++) {
2050             char c = '\0';
2051
2052             if (run == 0) {
2053                 c = *desc++;
2054                 assert(c != 'S');
2055                 if (c >= 'a' && c <= 'z')
2056                     run = c - 'a' + 1;
2057             }
2058
2059             if (run > 0) {
2060                 c = 'S';
2061                 run--;
2062             }
2063
2064             switch (c) {
2065             case '1': case '2': case '3': case '4':
2066             case '5': case '6': case '7': case '8': case '9':
2067                 island_add(state, x, y, (c - '0'));
2068                 break;
2069
2070             case 'A': case 'B': case 'C': case 'D':
2071             case 'E': case 'F': case 'G':
2072                 island_add(state, x, y, (c - 'A') + 10);
2073                 break;
2074
2075             case 'S':
2076                 /* empty square */
2077                 break;
2078
2079             default:
2080                 assert(!"Malformed desc.");
2081                 break;
2082             }
2083         }
2084     }
2085     if (*desc) assert(!"Over-long desc.");
2086
2087     map_find_orthogonal(state);
2088     map_update_possibles(state);
2089
2090     return state;
2091 }
2092
2093 static game_state *new_game(midend *me, const game_params *params,
2094                             const char *desc)
2095 {
2096     return new_game_sub(params, desc);
2097 }
2098
2099 struct game_ui {
2100     int dragx_src, dragy_src;   /* source; -1 means no drag */
2101     int dragx_dst, dragy_dst;   /* src's closest orth island. */
2102     grid_type todraw;
2103     int dragging, drag_is_noline, nlines;
2104
2105     int cur_x, cur_y, cur_visible;      /* cursor position */
2106     int show_hints;
2107 };
2108
2109 static char *ui_cancel_drag(game_ui *ui)
2110 {
2111     ui->dragx_src = ui->dragy_src = -1;
2112     ui->dragx_dst = ui->dragy_dst = -1;
2113     ui->dragging = 0;
2114     return "";
2115 }
2116
2117 static game_ui *new_ui(const game_state *state)
2118 {
2119     game_ui *ui = snew(game_ui);
2120     ui_cancel_drag(ui);
2121     ui->cur_x = state->islands[0].x;
2122     ui->cur_y = state->islands[0].y;
2123     ui->cur_visible = 0;
2124     ui->show_hints = 0;
2125     return ui;
2126 }
2127
2128 static void free_ui(game_ui *ui)
2129 {
2130     sfree(ui);
2131 }
2132
2133 static char *encode_ui(const game_ui *ui)
2134 {
2135     return NULL;
2136 }
2137
2138 static void decode_ui(game_ui *ui, const char *encoding)
2139 {
2140 }
2141
2142 static void game_changed_state(game_ui *ui, const game_state *oldstate,
2143                                const game_state *newstate)
2144 {
2145 }
2146
2147 struct game_drawstate {
2148     int tilesize;
2149     int w, h;
2150     unsigned long *grid, *newgrid;
2151     int *lv, *lh;
2152     int started, dragging;
2153 };
2154
2155 /*
2156  * The contents of ds->grid are complicated, because of the circular
2157  * islands which overlap their own grid square into neighbouring
2158  * squares. An island square can contain pieces of the bridges in all
2159  * directions, and conversely a bridge square can be intruded on by
2160  * islands from any direction.
2161  *
2162  * So we define one group of flags describing what's important about
2163  * an island, and another describing a bridge. Island squares' entries
2164  * in ds->grid contain one of the former and four of the latter; bridge
2165  * squares, four of the former and _two_ of the latter - because a
2166  * horizontal and vertical 'bridge' can cross, when one of them is a
2167  * 'no bridge here' pencil mark.
2168  *
2169  * Bridge flags need to indicate 0-4 actual bridges (3 bits), a 'no
2170  * bridge' row of crosses, or a grey hint line; that's 7
2171  * possibilities, so 3 bits suffice. But then we also need to vary the
2172  * colours: the bridges can turn COL_WARNING if they're part of a loop
2173  * in no-loops mode, COL_HIGHLIGHT during a victory flash, or
2174  * COL_SELECTED if they're the bridge the user is currently dragging,
2175  * so that's 2 more bits for foreground colour. Also bridges can be
2176  * backed by COL_MARK if they're locked by the user, so that's one
2177  * more bit, making 6 bits per bridge direction.
2178  *
2179  * Island flags omit the actual island clue (it never changes during
2180  * the game, so doesn't have to be stored in ds->grid to check against
2181  * the previous version), so they just need to include 2 bits for
2182  * foreground colour (an island can be normal, COL_HIGHLIGHT during
2183  * victory, COL_WARNING if its clue is unsatisfiable, or COL_SELECTED
2184  * if it's part of the user's drag) and 2 bits for background (normal,
2185  * COL_MARK for a locked island, COL_CURSOR for the keyboard cursor).
2186  * That's 4 bits per island direction. We must also indicate whether
2187  * no island is present at all (in the case where the island is
2188  * potentially intruding into the side of a line square), which we do
2189  * using the unused 4th value of the background field.
2190  *
2191  * So an island square needs 4 + 4*6 = 28 bits, while a bridge square
2192  * needs 4*4 + 2*6 = 28 bits too. Both only just fit in 32 bits, which
2193  * is handy, because otherwise we'd have to faff around forever with
2194  * little structs!
2195  */
2196 /* Flags for line data */
2197 #define DL_COUNTMASK    0x07
2198 #define DL_COUNT_CROSS  0x06
2199 #define DL_COUNT_HINT   0x07
2200 #define DL_COLMASK      0x18
2201 #define DL_COL_NORMAL   0x00
2202 #define DL_COL_WARNING  0x08
2203 #define DL_COL_FLASH    0x10
2204 #define DL_COL_SELECTED 0x18
2205 #define DL_LOCK         0x20
2206 #define DL_MASK         0x3F
2207 /* Flags for island data */
2208 #define DI_COLMASK      0x03
2209 #define DI_COL_NORMAL   0x00
2210 #define DI_COL_FLASH    0x01
2211 #define DI_COL_WARNING  0x02
2212 #define DI_COL_SELECTED 0x03
2213 #define DI_BGMASK       0x0C
2214 #define DI_BG_NO_ISLAND 0x00
2215 #define DI_BG_NORMAL    0x04
2216 #define DI_BG_MARK      0x08
2217 #define DI_BG_CURSOR    0x0C
2218 #define DI_MASK         0x0F
2219 /* Shift counts for the format of a 32-bit word in an island square */
2220 #define D_I_ISLAND_SHIFT 0
2221 #define D_I_LINE_SHIFT_L 4
2222 #define D_I_LINE_SHIFT_R 10
2223 #define D_I_LINE_SHIFT_U 16
2224 #define D_I_LINE_SHIFT_D 24
2225 /* Shift counts for the format of a 32-bit word in a line square */
2226 #define D_L_ISLAND_SHIFT_L 0
2227 #define D_L_ISLAND_SHIFT_R 4
2228 #define D_L_ISLAND_SHIFT_U 8
2229 #define D_L_ISLAND_SHIFT_D 12
2230 #define D_L_LINE_SHIFT_H 16
2231 #define D_L_LINE_SHIFT_V 22
2232
2233 static char *update_drag_dst(const game_state *state, game_ui *ui,
2234                              const game_drawstate *ds, int nx, int ny)
2235 {
2236     int ox, oy, dx, dy, i, currl, maxb;
2237     struct island *is;
2238     grid_type gtype, ntype, mtype, curr;
2239
2240     if (ui->dragx_src == -1 || ui->dragy_src == -1) return NULL;
2241
2242     ui->dragx_dst = -1;
2243     ui->dragy_dst = -1;
2244
2245     /* work out which of the four directions we're closest to... */
2246     ox = COORD(ui->dragx_src) + TILE_SIZE/2;
2247     oy = COORD(ui->dragy_src) + TILE_SIZE/2;
2248
2249     if (abs(nx-ox) < abs(ny-oy)) {
2250         dx = 0;
2251         dy = (ny-oy) < 0 ? -1 : 1;
2252         gtype = G_LINEV; ntype = G_NOLINEV; mtype = G_MARKV;
2253         maxb = INDEX(state, maxv, ui->dragx_src+dx, ui->dragy_src+dy);
2254     } else {
2255         dy = 0;
2256         dx = (nx-ox) < 0 ? -1 : 1;
2257         gtype = G_LINEH; ntype = G_NOLINEH; mtype = G_MARKH;
2258         maxb = INDEX(state, maxh, ui->dragx_src+dx, ui->dragy_src+dy);
2259     }
2260     if (ui->drag_is_noline) {
2261         ui->todraw = ntype;
2262     } else {
2263         curr = GRID(state, ui->dragx_src+dx, ui->dragy_src+dy);
2264         currl = INDEX(state, lines, ui->dragx_src+dx, ui->dragy_src+dy);
2265
2266         if (curr & gtype) {
2267             if (currl == maxb) {
2268                 ui->todraw = 0;
2269                 ui->nlines = 0;
2270             } else {
2271                 ui->todraw = gtype;
2272                 ui->nlines = currl + 1;
2273             }
2274         } else {
2275             ui->todraw = gtype;
2276             ui->nlines = 1;
2277         }
2278     }
2279
2280     /* ... and see if there's an island off in that direction. */
2281     is = INDEX(state, gridi, ui->dragx_src, ui->dragy_src);
2282     for (i = 0; i < is->adj.npoints; i++) {
2283         if (is->adj.points[i].off == 0) continue;
2284         curr = GRID(state, is->x+dx, is->y+dy);
2285         if (curr & mtype) continue; /* don't allow changes to marked lines. */
2286         if (ui->drag_is_noline) {
2287             if (curr & gtype) continue; /* no no-line where already a line */
2288         } else {
2289             if (POSSIBLES(state, dx, is->x+dx, is->y+dy) == 0) continue; /* no line if !possible. */
2290             if (curr & ntype) continue; /* can't have a bridge where there's a no-line. */
2291         }
2292
2293         if (is->adj.points[i].dx == dx &&
2294             is->adj.points[i].dy == dy) {
2295             ui->dragx_dst = ISLAND_ORTHX(is,i);
2296             ui->dragy_dst = ISLAND_ORTHY(is,i);
2297         }
2298     }
2299     /*debug(("update_drag src (%d,%d) d(%d,%d) dst (%d,%d)\n",
2300            ui->dragx_src, ui->dragy_src, dx, dy,
2301            ui->dragx_dst, ui->dragy_dst));*/
2302     return "";
2303 }
2304
2305 static char *finish_drag(const game_state *state, game_ui *ui)
2306 {
2307     char buf[80];
2308
2309     if (ui->dragx_src == -1 || ui->dragy_src == -1)
2310         return NULL;
2311     if (ui->dragx_dst == -1 || ui->dragy_dst == -1)
2312         return ui_cancel_drag(ui);
2313
2314     if (ui->drag_is_noline) {
2315         sprintf(buf, "N%d,%d,%d,%d",
2316                 ui->dragx_src, ui->dragy_src,
2317                 ui->dragx_dst, ui->dragy_dst);
2318     } else {
2319         sprintf(buf, "L%d,%d,%d,%d,%d",
2320                 ui->dragx_src, ui->dragy_src,
2321                 ui->dragx_dst, ui->dragy_dst, ui->nlines);
2322     }
2323
2324     ui_cancel_drag(ui);
2325
2326     return dupstr(buf);
2327 }
2328
2329 static char *interpret_move(const game_state *state, game_ui *ui,
2330                             const game_drawstate *ds,
2331                             int x, int y, int button)
2332 {
2333     int gx = FROMCOORD(x), gy = FROMCOORD(y);
2334     char buf[80], *ret;
2335     grid_type ggrid = INGRID(state,gx,gy) ? GRID(state,gx,gy) : 0;
2336     int shift = button & MOD_SHFT, control = button & MOD_CTRL;
2337     button &= ~MOD_MASK;
2338
2339     if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2340         if (!INGRID(state, gx, gy)) return NULL;
2341         ui->cur_visible = 0;
2342         if ((ggrid & G_ISLAND) && !(ggrid & G_MARK)) {
2343             ui->dragx_src = gx;
2344             ui->dragy_src = gy;
2345             return "";
2346         } else
2347             return ui_cancel_drag(ui);
2348     } else if (button == LEFT_DRAG || button == RIGHT_DRAG) {
2349         if (gx != ui->dragx_src || gy != ui->dragy_src) {
2350             ui->dragging = 1;
2351             ui->drag_is_noline = (button == RIGHT_DRAG) ? 1 : 0;
2352             return update_drag_dst(state, ui, ds, x, y);
2353         } else {
2354             /* cancel a drag when we go back to the starting point */
2355             ui->dragx_dst = -1;
2356             ui->dragy_dst = -1;
2357             return "";
2358         }
2359     } else if (button == LEFT_RELEASE || button == RIGHT_RELEASE) {
2360         if (ui->dragging) {
2361             return finish_drag(state, ui);
2362         } else {
2363             ui_cancel_drag(ui);
2364             if (!INGRID(state, gx, gy)) return NULL;
2365             if (!(GRID(state, gx, gy) & G_ISLAND)) return NULL;
2366             sprintf(buf, "M%d,%d", gx, gy);
2367             return dupstr(buf);
2368         }
2369     } else if (button == 'h' || button == 'H') {
2370         game_state *solved = dup_game(state);
2371         solve_for_hint(solved);
2372         ret = game_state_diff(state, solved);
2373         free_game(solved);
2374         return ret;
2375     } else if (IS_CURSOR_MOVE(button)) {
2376         ui->cur_visible = 1;
2377         if (control || shift) {
2378             ui->dragx_src = ui->cur_x;
2379             ui->dragy_src = ui->cur_y;
2380             ui->dragging = TRUE;
2381             ui->drag_is_noline = !control;
2382         }
2383         if (ui->dragging) {
2384             int nx = ui->cur_x, ny = ui->cur_y;
2385
2386             move_cursor(button, &nx, &ny, state->w, state->h, 0);
2387             if (nx == ui->cur_x && ny == ui->cur_y)
2388                 return NULL;
2389             update_drag_dst(state, ui, ds,
2390                              COORD(nx)+TILE_SIZE/2,
2391                              COORD(ny)+TILE_SIZE/2);
2392             return finish_drag(state, ui);
2393         } else {
2394             int dx = (button == CURSOR_RIGHT) ? +1 : (button == CURSOR_LEFT) ? -1 : 0;
2395             int dy = (button == CURSOR_DOWN)  ? +1 : (button == CURSOR_UP)   ? -1 : 0;
2396             int dorthx = 1 - abs(dx), dorthy = 1 - abs(dy);
2397             int dir, orth, nx = x, ny = y;
2398
2399             /* 'orthorder' is a tweak to ensure that if you press RIGHT and
2400              * happen to move upwards, when you press LEFT you then tend
2401              * downwards (rather than upwards again). */
2402             int orthorder = (button == CURSOR_LEFT || button == CURSOR_UP) ? 1 : -1;
2403
2404             /* This attempts to find an island in the direction you're
2405              * asking for, broadly speaking. If you ask to go right, for
2406              * example, it'll look for islands to the right and slightly
2407              * above or below your current horiz. position, allowing
2408              * further above/below the further away it searches. */
2409
2410             assert(GRID(state, ui->cur_x, ui->cur_y) & G_ISLAND);
2411             /* currently this is depth-first (so orthogonally-adjacent
2412              * islands across the other side of the grid will be moved to
2413              * before closer islands slightly offset). Swap the order of
2414              * these two loops to change to breadth-first search. */
2415             for (orth = 0; ; orth++) {
2416                 int oingrid = 0;
2417                 for (dir = 1; ; dir++) {
2418                     int dingrid = 0;
2419
2420                     if (orth > dir) continue; /* only search in cone outwards. */
2421
2422                     nx = ui->cur_x + dir*dx + orth*dorthx*orthorder;
2423                     ny = ui->cur_y + dir*dy + orth*dorthy*orthorder;
2424                     if (INGRID(state, nx, ny)) {
2425                         dingrid = oingrid = 1;
2426                         if (GRID(state, nx, ny) & G_ISLAND) goto found;
2427                     }
2428
2429                     nx = ui->cur_x + dir*dx - orth*dorthx*orthorder;
2430                     ny = ui->cur_y + dir*dy - orth*dorthy*orthorder;
2431                     if (INGRID(state, nx, ny)) {
2432                         dingrid = oingrid = 1;
2433                         if (GRID(state, nx, ny) & G_ISLAND) goto found;
2434                     }
2435
2436                     if (!dingrid) break;
2437                 }
2438                 if (!oingrid) return "";
2439             }
2440             /* not reached */
2441
2442 found:
2443             ui->cur_x = nx;
2444             ui->cur_y = ny;
2445             return "";
2446         }
2447     } else if (IS_CURSOR_SELECT(button)) {
2448         if (!ui->cur_visible) {
2449             ui->cur_visible = 1;
2450             return "";
2451         }
2452         if (ui->dragging || button == CURSOR_SELECT2) {
2453             ui_cancel_drag(ui);
2454             if (ui->dragx_dst == -1 && ui->dragy_dst == -1) {
2455                 sprintf(buf, "M%d,%d", ui->cur_x, ui->cur_y);
2456                 return dupstr(buf);
2457             } else
2458                 return "";
2459         } else {
2460             grid_type v = GRID(state, ui->cur_x, ui->cur_y);
2461             if (v & G_ISLAND) {
2462                 ui->dragging = 1;
2463                 ui->dragx_src = ui->cur_x;
2464                 ui->dragy_src = ui->cur_y;
2465                 ui->dragx_dst = ui->dragy_dst = -1;
2466                 ui->drag_is_noline = (button == CURSOR_SELECT2) ? 1 : 0;
2467                 return "";
2468             }
2469         }
2470     } else if ((button >= '0' && button <= '9') ||
2471                (button >= 'a' && button <= 'f') ||
2472                (button >= 'A' && button <= 'F')) {
2473         /* jump to island with .count == number closest to cur_{x,y} */
2474         int best_x = -1, best_y = -1, best_sqdist = -1, number = -1, i;
2475
2476         if (button >= '0' && button <= '9')
2477             number = (button == '0' ? 16 : button - '0');
2478         else if (button >= 'a' && button <= 'f')
2479             number = 10 + button - 'a';
2480         else if (button >= 'A' && button <= 'F')
2481             number = 10 + button - 'A';
2482
2483         if (!ui->cur_visible) {
2484             ui->cur_visible = 1;
2485             return "";
2486         }
2487
2488         for (i = 0; i < state->n_islands; ++i) {
2489             int x = state->islands[i].x, y = state->islands[i].y;
2490             int dx = x - ui->cur_x, dy = y - ui->cur_y;
2491             int sqdist = dx*dx + dy*dy;
2492
2493             if (state->islands[i].count != number)
2494                 continue;
2495             if (x == ui->cur_x && y == ui->cur_y)
2496                 continue;
2497
2498             /* new_game() reads the islands in row-major order, so by
2499              * breaking ties in favor of `first in state->islands' we
2500              * also break ties by `lexicographically smallest (y, x)'.
2501              * Thus, there's a stable pattern to how ties are broken
2502              * which the user can learn and use to navigate faster. */
2503             if (best_sqdist == -1 || sqdist < best_sqdist) {
2504                 best_x = x;
2505                 best_y = y;
2506                 best_sqdist = sqdist;
2507             }
2508         }
2509         if (best_x != -1 && best_y != -1) {
2510             ui->cur_x = best_x;
2511             ui->cur_y = best_y;
2512             return "";
2513         } else
2514             return NULL;
2515     } else if (button == 'g' || button == 'G') {
2516         ui->show_hints = 1 - ui->show_hints;
2517         return "";
2518     }
2519
2520     return NULL;
2521 }
2522
2523 static game_state *execute_move(const game_state *state, const char *move)
2524 {
2525     game_state *ret = dup_game(state);
2526     int x1, y1, x2, y2, nl, n;
2527     struct island *is1, *is2;
2528     char c;
2529
2530     debug(("execute_move: %s\n", move));
2531
2532     if (!*move) goto badmove;
2533     while (*move) {
2534         c = *move++;
2535         if (c == 'S') {
2536             ret->solved = TRUE;
2537             n = 0;
2538         } else if (c == 'L') {
2539             if (sscanf(move, "%d,%d,%d,%d,%d%n",
2540                        &x1, &y1, &x2, &y2, &nl, &n) != 5)
2541                 goto badmove;
2542             if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2543                 goto badmove;
2544             is1 = INDEX(ret, gridi, x1, y1);
2545             is2 = INDEX(ret, gridi, x2, y2);
2546             if (!is1 || !is2) goto badmove;
2547             if (nl < 0 || nl > state->maxb) goto badmove;
2548             island_join(is1, is2, nl, 0);
2549         } else if (c == 'N') {
2550             if (sscanf(move, "%d,%d,%d,%d%n",
2551                        &x1, &y1, &x2, &y2, &n) != 4)
2552                 goto badmove;
2553             if (!INGRID(ret, x1, y1) || !INGRID(ret, x2, y2))
2554                 goto badmove;
2555             is1 = INDEX(ret, gridi, x1, y1);
2556             is2 = INDEX(ret, gridi, x2, y2);
2557             if (!is1 || !is2) goto badmove;
2558             island_join(is1, is2, -1, 0);
2559         } else if (c == 'M') {
2560             if (sscanf(move, "%d,%d%n",
2561                        &x1, &y1, &n) != 2)
2562                 goto badmove;
2563             if (!INGRID(ret, x1, y1))
2564                 goto badmove;
2565             is1 = INDEX(ret, gridi, x1, y1);
2566             if (!is1) goto badmove;
2567             island_togglemark(is1);
2568         } else
2569             goto badmove;
2570
2571         move += n;
2572         if (*move == ';')
2573             move++;
2574         else if (*move) goto badmove;
2575     }
2576
2577     map_update_possibles(ret);
2578     if (map_check(ret)) {
2579         debug(("Game completed.\n"));
2580         ret->completed = 1;
2581     }
2582     return ret;
2583
2584 badmove:
2585     debug(("%s: unrecognised move.\n", move));
2586     free_game(ret);
2587     return NULL;
2588 }
2589
2590 static char *solve_game(const game_state *state, const game_state *currstate,
2591                         const char *aux, char **error)
2592 {
2593     char *ret;
2594     game_state *solved;
2595
2596     if (aux) {
2597         debug(("solve_game: aux = %s\n", aux));
2598         solved = execute_move(state, aux);
2599         if (!solved) {
2600             *error = "Generated aux string is not a valid move (!).";
2601             return NULL;
2602         }
2603     } else {
2604         solved = dup_game(state);
2605         /* solve with max strength... */
2606         if (solve_from_scratch(solved, 10) == 0) {
2607             free_game(solved);
2608             *error = "Game does not have a (non-recursive) solution.";
2609             return NULL;
2610         }
2611     }
2612     ret = game_state_diff(currstate, solved);
2613     free_game(solved);
2614     debug(("solve_game: ret = %s\n", ret));
2615     return ret;
2616 }
2617
2618 /* ----------------------------------------------------------------------
2619  * Drawing routines.
2620  */
2621
2622 static void game_compute_size(const game_params *params, int tilesize,
2623                               int *x, int *y)
2624 {
2625     /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2626     struct { int tilesize; } ads, *ds = &ads;
2627     ads.tilesize = tilesize;
2628
2629     *x = TILE_SIZE * params->w + 2 * BORDER;
2630     *y = TILE_SIZE * params->h + 2 * BORDER;
2631 }
2632
2633 static void game_set_size(drawing *dr, game_drawstate *ds,
2634                           const game_params *params, int tilesize)
2635 {
2636     ds->tilesize = tilesize;
2637 }
2638
2639 static float *game_colours(frontend *fe, int *ncolours)
2640 {
2641     float *ret = snewn(3 * NCOLOURS, float);
2642     int i;
2643
2644     game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
2645
2646     for (i = 0; i < 3; i++) {
2647         ret[COL_FOREGROUND * 3 + i] = 0.0F;
2648         ret[COL_HINT * 3 + i] = ret[COL_LOWLIGHT * 3 + i];
2649         ret[COL_GRID * 3 + i] =
2650             (ret[COL_HINT * 3 + i] + ret[COL_BACKGROUND * 3 + i]) * 0.5F;
2651         ret[COL_MARK * 3 + i] = ret[COL_HIGHLIGHT * 3 + i];
2652     }
2653     ret[COL_WARNING * 3 + 0] = 1.0F;
2654     ret[COL_WARNING * 3 + 1] = 0.25F;
2655     ret[COL_WARNING * 3 + 2] = 0.25F;
2656
2657     ret[COL_SELECTED * 3 + 0] = 0.25F;
2658     ret[COL_SELECTED * 3 + 1] = 1.00F;
2659     ret[COL_SELECTED * 3 + 2] = 0.25F;
2660
2661     ret[COL_CURSOR * 3 + 0] = min(ret[COL_BACKGROUND * 3 + 0] * 1.4F, 1.0F);
2662     ret[COL_CURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.8F;
2663     ret[COL_CURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.8F;
2664
2665     *ncolours = NCOLOURS;
2666     return ret;
2667 }
2668
2669 static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2670 {
2671     struct game_drawstate *ds = snew(struct game_drawstate);
2672     int wh = state->w*state->h;
2673     int i;
2674
2675     ds->tilesize = 0;
2676     ds->w = state->w;
2677     ds->h = state->h;
2678     ds->started = 0;
2679     ds->dragging = 0;
2680     ds->grid = snewn(wh, unsigned long);
2681     for (i = 0; i < wh; i++)
2682         ds->grid[i] = ~0UL;
2683     ds->newgrid = snewn(wh, unsigned long);
2684     ds->lv = snewn(wh, int);
2685     ds->lh = snewn(wh, int);
2686     memset(ds->lv, 0, wh*sizeof(int));
2687     memset(ds->lh, 0, wh*sizeof(int));
2688
2689     return ds;
2690 }
2691
2692 static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2693 {
2694     sfree(ds->lv);
2695     sfree(ds->lh);
2696     sfree(ds->newgrid);
2697     sfree(ds->grid);
2698     sfree(ds);
2699 }
2700
2701 #define LINE_WIDTH (TILE_SIZE/8)
2702 #define TS8(x) (((x)*TILE_SIZE)/8)
2703
2704 #define OFFSET(thing) ((TILE_SIZE/2) - ((thing)/2))
2705
2706 static int between_island(const game_state *state, int sx, int sy,
2707                           int dx, int dy)
2708 {
2709     int x = sx - dx, y = sy - dy;
2710
2711     while (INGRID(state, x, y)) {
2712         if (GRID(state, x, y) & G_ISLAND) goto found;
2713         x -= dx; y -= dy;
2714     }
2715     return 0;
2716 found:
2717     x = sx + dx, y = sy + dy;
2718     while (INGRID(state, x, y)) {
2719         if (GRID(state, x, y) & G_ISLAND) return 1;
2720         x += dx; y += dy;
2721     }
2722     return 0;
2723 }
2724
2725 static void lines_lvlh(const game_state *state, const game_ui *ui,
2726                        int x, int y, grid_type v, int *lv_r, int *lh_r)
2727 {
2728     int lh = 0, lv = 0;
2729
2730     if (v & G_LINEV) lv = INDEX(state,lines,x,y);
2731     if (v & G_LINEH) lh = INDEX(state,lines,x,y);
2732
2733     if (ui->show_hints) {
2734         if (between_island(state, x, y, 0, 1) && !lv) lv = 1;
2735         if (between_island(state, x, y, 1, 0) && !lh) lh = 1;
2736     }
2737     /*debug(("lvlh: (%d,%d) v 0x%x lv %d lh %d.\n", x, y, v, lv, lh));*/
2738     *lv_r = lv; *lh_r = lh;
2739 }
2740
2741 static void draw_cross(drawing *dr, game_drawstate *ds,
2742                        int ox, int oy, int col)
2743 {
2744     int off = TS8(2);
2745     draw_line(dr, ox,     oy, ox+off, oy+off, col);
2746     draw_line(dr, ox+off, oy, ox,     oy+off, col);
2747 }
2748
2749 static void draw_general_line(drawing *dr, game_drawstate *ds,
2750                               int ox, int oy, int fx, int fy, int ax, int ay,
2751                               int len, unsigned long ldata, int which)
2752 {
2753     /*
2754      * Draw one direction of lines in a square. To permit the same
2755      * code to handle horizontal and vertical lines, fx,fy are the
2756      * 'forward' direction (along the lines) and ax,ay are the
2757      * 'across' direction.
2758      *
2759      * We draw the white background for a locked bridge if (which &
2760      * 1), and draw the bridges themselves if (which & 2). This
2761      * permits us to get two overlapping locked bridges right without
2762      * one of them erasing part of the other.
2763      */
2764     int fg;
2765
2766     fg = ((ldata & DL_COUNTMASK) == DL_COUNT_HINT ? COL_HINT :
2767           (ldata & DL_COLMASK) == DL_COL_SELECTED ? COL_SELECTED :
2768           (ldata & DL_COLMASK) == DL_COL_FLASH ? COL_HIGHLIGHT :
2769           (ldata & DL_COLMASK) == DL_COL_WARNING ? COL_WARNING :
2770           COL_FOREGROUND);
2771
2772     if ((ldata & DL_COUNTMASK) == DL_COUNT_CROSS) {
2773         draw_cross(dr, ds,
2774                    ox + TS8(1)*fx + TS8(3)*ax,
2775                    oy + TS8(1)*fy + TS8(3)*ay, fg);
2776         draw_cross(dr, ds,
2777                    ox + TS8(5)*fx + TS8(3)*ax,
2778                    oy + TS8(5)*fy + TS8(3)*ay, fg);
2779     } else if ((ldata & DL_COUNTMASK) != 0) {
2780         int lh, lw, gw, bw, i, loff;
2781
2782         lh = (ldata & DL_COUNTMASK);
2783         if (lh == DL_COUNT_HINT)
2784             lh = 1;
2785
2786         lw = gw = LINE_WIDTH;
2787         while ((bw = lw * lh + gw * (lh+1)) > TILE_SIZE)
2788             gw--;
2789
2790         loff = OFFSET(bw);
2791
2792         if (which & 1) {
2793             if ((ldata & DL_LOCK) && fg != COL_HINT)
2794                 draw_rect(dr, ox + loff*ax, oy + loff*ay,
2795                           len*fx+bw*ax, len*fy+bw*ay, COL_MARK);
2796         }
2797         if (which & 2) {
2798             for (i = 0; i < lh; i++, loff += lw + gw)
2799                 draw_rect(dr, ox + (loff+gw)*ax, oy + (loff+gw)*ay,
2800                           len*fx+lw*ax, len*fy+lw*ay, fg);
2801         }
2802     }
2803 }
2804
2805 static void draw_hline(drawing *dr, game_drawstate *ds,
2806                        int ox, int oy, int w, unsigned long vdata, int which)
2807 {
2808     draw_general_line(dr, ds, ox, oy, 1, 0, 0, 1, w, vdata, which);
2809 }
2810
2811 static void draw_vline(drawing *dr, game_drawstate *ds,
2812                        int ox, int oy, int h, unsigned long vdata, int which)
2813 {
2814     draw_general_line(dr, ds, ox, oy, 0, 1, 1, 0, h, vdata, which);
2815 }
2816
2817 #define ISLAND_RADIUS ((TILE_SIZE*12)/20)
2818 #define ISLAND_NUMSIZE(clue) \
2819     (((clue) < 10) ? (TILE_SIZE*7)/10 : (TILE_SIZE*5)/10)
2820
2821 static void draw_island(drawing *dr, game_drawstate *ds,
2822                         int ox, int oy, int clue, unsigned long idata)
2823 {
2824     int half, orad, irad, fg, bg;
2825
2826     if ((idata & DI_BGMASK) == DI_BG_NO_ISLAND)
2827         return;
2828
2829     half = TILE_SIZE/2;
2830     orad = ISLAND_RADIUS;
2831     irad = orad - LINE_WIDTH;
2832     fg = ((idata & DI_COLMASK) == DI_COL_SELECTED ? COL_SELECTED :
2833           (idata & DI_COLMASK) == DI_COL_WARNING ? COL_WARNING :
2834           (idata & DI_COLMASK) == DI_COL_FLASH ? COL_HIGHLIGHT :
2835           COL_FOREGROUND);
2836     bg = ((idata & DI_BGMASK) == DI_BG_CURSOR ? COL_CURSOR :
2837           (idata & DI_BGMASK) == DI_BG_MARK ? COL_MARK :
2838           COL_BACKGROUND);
2839
2840     /* draw a thick circle */
2841     draw_circle(dr, ox+half, oy+half, orad, fg, fg);
2842     draw_circle(dr, ox+half, oy+half, irad, bg, bg);
2843
2844     if (clue > 0) {
2845         char str[32];
2846         int textcolour = (fg == COL_SELECTED ? COL_FOREGROUND : fg);
2847         sprintf(str, "%d", clue);
2848         draw_text(dr, ox+half, oy+half, FONT_VARIABLE, ISLAND_NUMSIZE(clue),
2849                   ALIGN_VCENTRE | ALIGN_HCENTRE, textcolour, str);
2850     }
2851 }
2852
2853 static void draw_island_tile(drawing *dr, game_drawstate *ds,
2854                              int x, int y, int clue, unsigned long data)
2855 {
2856     int ox = COORD(x), oy = COORD(y);
2857     int which;
2858
2859     clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2860     draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
2861
2862     /*
2863      * Because of the possibility of incoming bridges just about
2864      * meeting at one corner, we must split the line-drawing into
2865      * background and foreground segments.
2866      */
2867     for (which = 1; which <= 2; which <<= 1) {
2868         draw_hline(dr, ds, ox, oy, TILE_SIZE/2,
2869                    (data >> D_I_LINE_SHIFT_L) & DL_MASK, which);
2870         draw_hline(dr, ds, ox + TILE_SIZE - TILE_SIZE/2, oy, TILE_SIZE/2,
2871                    (data >> D_I_LINE_SHIFT_R) & DL_MASK, which);
2872         draw_vline(dr, ds, ox, oy, TILE_SIZE/2,
2873                    (data >> D_I_LINE_SHIFT_U) & DL_MASK, which);
2874         draw_vline(dr, ds, ox, oy + TILE_SIZE - TILE_SIZE/2, TILE_SIZE/2,
2875                    (data >> D_I_LINE_SHIFT_D) & DL_MASK, which);
2876     }
2877     draw_island(dr, ds, ox, oy, clue, (data >> D_I_ISLAND_SHIFT) & DI_MASK);
2878
2879     unclip(dr);
2880     draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2881 }
2882
2883 static void draw_line_tile(drawing *dr, game_drawstate *ds,
2884                            int x, int y, unsigned long data)
2885 {
2886     int ox = COORD(x), oy = COORD(y);
2887     unsigned long hdata, vdata;
2888
2889     clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2890     draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
2891
2892     /*
2893      * We have to think about which of the horizontal and vertical
2894      * line to draw first, if both exist.
2895      *
2896      * The rule is that hint lines are drawn at the bottom, then
2897      * NOLINE crosses, then actual bridges. The enumeration in the
2898      * DL_COUNTMASK field is set up so that this drops out of a
2899      * straight comparison between the two.
2900      *
2901      * Since lines crossing in this type of square cannot both be
2902      * actual bridges, there's no need to pass a nontrivial 'which'
2903      * parameter to draw_[hv]line.
2904      */
2905     hdata = (data >> D_L_LINE_SHIFT_H) & DL_MASK;
2906     vdata = (data >> D_L_LINE_SHIFT_V) & DL_MASK;
2907     if ((hdata & DL_COUNTMASK) > (vdata & DL_COUNTMASK)) {
2908         draw_hline(dr, ds, ox, oy, TILE_SIZE, hdata, 3);
2909         draw_vline(dr, ds, ox, oy, TILE_SIZE, vdata, 3);
2910     } else {
2911         draw_vline(dr, ds, ox, oy, TILE_SIZE, vdata, 3);
2912         draw_hline(dr, ds, ox, oy, TILE_SIZE, hdata, 3);
2913     }
2914
2915     /*
2916      * The islands drawn at the edges of a line tile don't need clue
2917      * numbers.
2918      */
2919     draw_island(dr, ds, ox - TILE_SIZE, oy, -1,
2920                 (data >> D_L_ISLAND_SHIFT_L) & DI_MASK);
2921     draw_island(dr, ds, ox + TILE_SIZE, oy, -1,
2922                 (data >> D_L_ISLAND_SHIFT_R) & DI_MASK);
2923     draw_island(dr, ds, ox, oy - TILE_SIZE, -1,
2924                 (data >> D_L_ISLAND_SHIFT_U) & DI_MASK);
2925     draw_island(dr, ds, ox, oy + TILE_SIZE, -1,
2926                 (data >> D_L_ISLAND_SHIFT_D) & DI_MASK);
2927
2928     unclip(dr);
2929     draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2930 }
2931
2932 static void draw_edge_tile(drawing *dr, game_drawstate *ds,
2933                            int x, int y, int dx, int dy, unsigned long data)
2934 {
2935     int ox = COORD(x), oy = COORD(y);
2936     int cx = ox, cy = oy, cw = TILE_SIZE, ch = TILE_SIZE;
2937
2938     if (dy) {
2939         if (dy > 0)
2940             cy += TILE_SIZE/2;
2941         ch -= TILE_SIZE/2;
2942     } else {
2943         if (dx > 0)
2944             cx += TILE_SIZE/2;
2945         cw -= TILE_SIZE/2;
2946     }
2947     clip(dr, cx, cy, cw, ch);
2948     draw_rect(dr, cx, cy, cw, ch, COL_BACKGROUND);
2949
2950     draw_island(dr, ds, ox + TILE_SIZE*dx, oy + TILE_SIZE*dy, -1,
2951                 (data >> D_I_ISLAND_SHIFT) & DI_MASK);
2952
2953     unclip(dr);
2954     draw_update(dr, cx, cy, cw, ch);
2955 }
2956
2957 static void game_redraw(drawing *dr, game_drawstate *ds,
2958                         const game_state *oldstate, const game_state *state,
2959                         int dir, const game_ui *ui,
2960                         float animtime, float flashtime)
2961 {
2962     int x, y, lv, lh;
2963     grid_type v, flash = 0;
2964     struct island *is, *is_drag_src = NULL, *is_drag_dst = NULL;
2965
2966     if (flashtime) {
2967         int f = (int)(flashtime * 5 / FLASH_TIME);
2968         if (f == 1 || f == 3) flash = TRUE;
2969     }
2970
2971     /* Clear screen, if required. */
2972     if (!ds->started) {
2973         draw_rect(dr, 0, 0,
2974                   TILE_SIZE * ds->w + 2 * BORDER,
2975                   TILE_SIZE * ds->h + 2 * BORDER, COL_BACKGROUND);
2976 #ifdef DRAW_GRID
2977         draw_rect_outline(dr,
2978                           COORD(0)-1, COORD(0)-1,
2979                           TILE_SIZE * ds->w + 2, TILE_SIZE * ds->h + 2,
2980                           COL_GRID);
2981 #endif
2982         draw_update(dr, 0, 0,
2983                     TILE_SIZE * ds->w + 2 * BORDER,
2984                     TILE_SIZE * ds->h + 2 * BORDER);
2985         ds->started = 1;
2986     }
2987
2988     if (ui->dragx_src != -1 && ui->dragy_src != -1) {
2989         ds->dragging = 1;
2990         is_drag_src = INDEX(state, gridi, ui->dragx_src, ui->dragy_src);
2991         assert(is_drag_src);
2992         if (ui->dragx_dst != -1 && ui->dragy_dst != -1) {
2993             is_drag_dst = INDEX(state, gridi, ui->dragx_dst, ui->dragy_dst);
2994             assert(is_drag_dst);
2995         }
2996     } else
2997         ds->dragging = 0;
2998
2999     /*
3000      * Set up ds->newgrid with the current grid contents.
3001      */
3002     for (x = 0; x < ds->w; x++)
3003         for (y = 0; y < ds->h; y++)
3004             INDEX(ds,newgrid,x,y) = 0;
3005
3006     for (x = 0; x < ds->w; x++) {
3007         for (y = 0; y < ds->h; y++) {
3008             v = GRID(state, x, y);
3009
3010             if (v & G_ISLAND) {
3011                 /*
3012                  * An island square. Compute the drawing data for the
3013                  * island, and put it in this square and surrounding
3014                  * squares.
3015                  */
3016                 unsigned long idata = 0;
3017
3018                 is = INDEX(state, gridi, x, y);
3019
3020                 if (flash)
3021                     idata |= DI_COL_FLASH;
3022                 if (is_drag_src && (is == is_drag_src ||
3023                                     (is_drag_dst && is == is_drag_dst)))
3024                     idata |= DI_COL_SELECTED;
3025                 else if (island_impossible(is, v & G_MARK) || (v & G_WARN))
3026                     idata |= DI_COL_WARNING;
3027                 else
3028                     idata |= DI_COL_NORMAL;
3029
3030                 if (ui->cur_visible &&
3031                     ui->cur_x == is->x && ui->cur_y == is->y)
3032                     idata |= DI_BG_CURSOR;
3033                 else if (v & G_MARK)
3034                     idata |= DI_BG_MARK;
3035                 else
3036                     idata |= DI_BG_NORMAL;
3037
3038                 INDEX(ds,newgrid,x,y) |= idata << D_I_ISLAND_SHIFT;
3039                 if (x > 0 && !(GRID(state,x-1,y) & G_ISLAND))
3040                     INDEX(ds,newgrid,x-1,y) |= idata << D_L_ISLAND_SHIFT_R;
3041                 if (x+1 < state->w && !(GRID(state,x+1,y) & G_ISLAND))
3042                     INDEX(ds,newgrid,x+1,y) |= idata << D_L_ISLAND_SHIFT_L;
3043                 if (y > 0 && !(GRID(state,x,y-1) & G_ISLAND))
3044                     INDEX(ds,newgrid,x,y-1) |= idata << D_L_ISLAND_SHIFT_D;
3045                 if (y+1 < state->h && !(GRID(state,x,y+1) & G_ISLAND))
3046                     INDEX(ds,newgrid,x,y+1) |= idata << D_L_ISLAND_SHIFT_U;
3047             } else {
3048                 unsigned long hdata, vdata;
3049                 int selh = FALSE, selv = FALSE;
3050
3051                 /*
3052                  * A line (non-island) square. Compute the drawing
3053                  * data for any horizontal and vertical lines in the
3054                  * square, and put them in this square's entry and
3055                  * optionally those for neighbouring islands too.
3056                  */
3057
3058                 if (is_drag_dst &&
3059                     WITHIN(x,is_drag_src->x, is_drag_dst->x) &&
3060                     WITHIN(y,is_drag_src->y, is_drag_dst->y)) {
3061                     if (is_drag_src->x != is_drag_dst->x)
3062                         selh = TRUE;
3063                     else
3064                         selv = TRUE;
3065                 }
3066                 lines_lvlh(state, ui, x, y, v, &lv, &lh);
3067
3068                 hdata = (v & G_NOLINEH ? DL_COUNT_CROSS :
3069                          v & G_LINEH ? lh :
3070                          (ui->show_hints &&
3071                           between_island(state,x,y,1,0)) ? DL_COUNT_HINT : 0);
3072                 vdata = (v & G_NOLINEV ? DL_COUNT_CROSS :
3073                          v & G_LINEV ? lv :
3074                          (ui->show_hints &&
3075                           between_island(state,x,y,0,1)) ? DL_COUNT_HINT : 0);
3076
3077                 hdata |= (flash ? DL_COL_FLASH :
3078                           v & G_WARN ? DL_COL_WARNING :
3079                           selh ? DL_COL_SELECTED :
3080                           DL_COL_NORMAL);
3081                 vdata |= (flash ? DL_COL_FLASH :
3082                           v & G_WARN ? DL_COL_WARNING :
3083                           selv ? DL_COL_SELECTED :
3084                           DL_COL_NORMAL);
3085
3086                 if (v & G_MARKH)
3087                     hdata |= DL_LOCK;
3088                 if (v & G_MARKV)
3089                     vdata |= DL_LOCK;
3090
3091                 INDEX(ds,newgrid,x,y) |= hdata << D_L_LINE_SHIFT_H;
3092                 INDEX(ds,newgrid,x,y) |= vdata << D_L_LINE_SHIFT_V;
3093                 if (x > 0 && (GRID(state,x-1,y) & G_ISLAND))
3094                     INDEX(ds,newgrid,x-1,y) |= hdata << D_I_LINE_SHIFT_R;
3095                 if (x+1 < state->w && (GRID(state,x+1,y) & G_ISLAND))
3096                     INDEX(ds,newgrid,x+1,y) |= hdata << D_I_LINE_SHIFT_L;
3097                 if (y > 0 && (GRID(state,x,y-1) & G_ISLAND))
3098                     INDEX(ds,newgrid,x,y-1) |= vdata << D_I_LINE_SHIFT_D;
3099                 if (y+1 < state->h && (GRID(state,x,y+1) & G_ISLAND))
3100                     INDEX(ds,newgrid,x,y+1) |= vdata << D_I_LINE_SHIFT_U;
3101             }
3102         }
3103     }
3104
3105     /*
3106      * Now go through and draw any changed grid square.
3107      */
3108     for (x = 0; x < ds->w; x++) {
3109         for (y = 0; y < ds->h; y++) {
3110             unsigned long newval = INDEX(ds,newgrid,x,y);
3111             if (INDEX(ds,grid,x,y) != newval) {
3112                 v = GRID(state, x, y);
3113                 if (v & G_ISLAND) {
3114                     is = INDEX(state, gridi, x, y);
3115                     draw_island_tile(dr, ds, x, y, is->count, newval);
3116
3117                     /*
3118                      * If this tile is right at the edge of the grid,
3119                      * we must also draw the part of the island that
3120                      * goes completely out of bounds. We don't bother
3121                      * keeping separate entries in ds->newgrid for
3122                      * these tiles; it's easier just to redraw them
3123                      * iff we redraw their parent island tile.
3124                      */
3125                     if (x == 0)
3126                         draw_edge_tile(dr, ds, x-1, y, +1, 0, newval);
3127                     if (y == 0)
3128                         draw_edge_tile(dr, ds, x, y-1, 0, +1, newval);
3129                     if (x == state->w-1)
3130                         draw_edge_tile(dr, ds, x+1, y, -1, 0, newval);
3131                     if (y == state->h-1)
3132                         draw_edge_tile(dr, ds, x, y+1, 0, -1, newval);
3133                 } else {
3134                     draw_line_tile(dr, ds, x, y, newval);
3135                 }
3136                 INDEX(ds,grid,x,y) = newval;
3137             }
3138         }
3139     }
3140 }
3141
3142 static float game_anim_length(const game_state *oldstate,
3143                               const game_state *newstate, int dir, game_ui *ui)
3144 {
3145     return 0.0F;
3146 }
3147
3148 static float game_flash_length(const game_state *oldstate,
3149                                const game_state *newstate, int dir, game_ui *ui)
3150 {
3151     if (!oldstate->completed && newstate->completed &&
3152         !oldstate->solved && !newstate->solved)
3153         return FLASH_TIME;
3154
3155     return 0.0F;
3156 }
3157
3158 static int game_status(const game_state *state)
3159 {
3160     return state->completed ? +1 : 0;
3161 }
3162
3163 static int game_timing_state(const game_state *state, game_ui *ui)
3164 {
3165     return TRUE;
3166 }
3167
3168 static void game_print_size(const game_params *params, float *x, float *y)
3169 {
3170     int pw, ph;
3171
3172     /* 10mm squares by default. */
3173     game_compute_size(params, 1000, &pw, &ph);
3174     *x = pw / 100.0F;
3175     *y = ph / 100.0F;
3176 }
3177
3178 static void game_print(drawing *dr, const game_state *state, int ts)
3179 {
3180     int ink = print_mono_colour(dr, 0);
3181     int paper = print_mono_colour(dr, 1);
3182     int x, y, cx, cy, i, nl;
3183     int loff;
3184     grid_type grid;
3185
3186     /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3187     game_drawstate ads, *ds = &ads;
3188     ads.tilesize = ts;
3189
3190     /* I don't think this wants a border. */
3191
3192     /* Bridges */
3193     loff = ts / (8 * sqrt((state->params.maxb - 1)));
3194     print_line_width(dr, ts / 12);
3195     for (x = 0; x < state->w; x++) {
3196         for (y = 0; y < state->h; y++) {
3197             cx = COORD(x); cy = COORD(y);
3198             grid = GRID(state,x,y);
3199             nl = INDEX(state,lines,x,y);
3200
3201             if (grid & G_ISLAND) continue;
3202             if (grid & G_LINEV) {
3203                 for (i = 0; i < nl; i++)
3204                     draw_line(dr, cx+ts/2+(2*i-nl+1)*loff, cy,
3205                               cx+ts/2+(2*i-nl+1)*loff, cy+ts, ink);
3206             }
3207             if (grid & G_LINEH) {
3208                 for (i = 0; i < nl; i++)
3209                     draw_line(dr, cx, cy+ts/2+(2*i-nl+1)*loff,
3210                               cx+ts, cy+ts/2+(2*i-nl+1)*loff, ink);
3211             }
3212         }
3213     }
3214
3215     /* Islands */
3216     for (i = 0; i < state->n_islands; i++) {
3217         char str[32];
3218         struct island *is = &state->islands[i];
3219         grid = GRID(state, is->x, is->y);
3220         cx = COORD(is->x) + ts/2;
3221         cy = COORD(is->y) + ts/2;
3222
3223         draw_circle(dr, cx, cy, ISLAND_RADIUS, paper, ink);
3224
3225         sprintf(str, "%d", is->count);
3226         draw_text(dr, cx, cy, FONT_VARIABLE, ISLAND_NUMSIZE(is->count),
3227                   ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
3228     }
3229 }
3230
3231 #ifdef COMBINED
3232 #define thegame bridges
3233 #endif
3234
3235 const struct game thegame = {
3236     "Bridges", "games.bridges", "bridges",
3237     default_params,
3238     game_fetch_preset,
3239     decode_params,
3240     encode_params,
3241     free_params,
3242     dup_params,
3243     TRUE, game_configure, custom_params,
3244     validate_params,
3245     new_game_desc,
3246     validate_desc,
3247     new_game,
3248     dup_game,
3249     free_game,
3250     TRUE, solve_game,
3251     TRUE, game_can_format_as_text_now, game_text_format,
3252     new_ui,
3253     free_ui,
3254     encode_ui,
3255     decode_ui,
3256     game_changed_state,
3257     interpret_move,
3258     execute_move,
3259     PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3260     game_colours,
3261     game_new_drawstate,
3262     game_free_drawstate,
3263     game_redraw,
3264     game_anim_length,
3265     game_flash_length,
3266     game_status,
3267     TRUE, FALSE, game_print_size, game_print,
3268     FALSE,                             /* wants_statusbar */
3269     FALSE, game_timing_state,
3270     REQUIRE_RBUTTON,                   /* flags */
3271 };
3272
3273 /* vim: set shiftwidth=4 tabstop=8: */