chiark / gitweb /
Update changelog for 20170923.ff218728-0+iwj2~3.gbpc58e0c release
[sgt-puzzles.git] / unfinished / slide.c
1 /*
2  * slide.c: Implementation of the block-sliding puzzle `Klotski'.
3  */
4
5 /*
6  * TODO:
7  * 
8  *  - Improve the generator.
9  *     * actually, we seem to be mostly sensible already now. I
10  *       want more choice over the type of main block and location
11  *       of the exit/target, and I think I probably ought to give
12  *       up on compactness and just bite the bullet and have the
13  *       target area right outside the main wall, but mostly I
14  *       think it's OK.
15  *     * the move limit tends to make the game _slower_ to
16  *       generate, which is odd. Perhaps investigate why.
17  * 
18  *  - Improve the graphics.
19  *     * All the colours are a bit wishy-washy. _Some_ dark
20  *       colours would surely not be excessive? Probably darken
21  *       the tiles, the walls and the main block, and leave the
22  *       target marker pale.
23  *     * The cattle grid effect is still disgusting. Think of
24  *       something completely different.
25  *     * The highlight for next-piece-to-move in the solver is
26  *       excessive, and the shadow blends in too well with the
27  *       piece lowlights. Adjust both.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <ctype.h>
35 #include <math.h>
36
37 #include "puzzles.h"
38 #include "tree234.h"
39
40 /*
41  * The implementation of this game revolves around the insight
42  * which makes an exhaustive-search solver feasible: although
43  * there are many blocks which can be rearranged in many ways, any
44  * two blocks of the same shape are _indistinguishable_ and hence
45  * the number of _distinct_ board layouts is generally much
46  * smaller. So we adopt a representation for board layouts which
47  * is inherently canonical, i.e. there are no two distinct
48  * representations which encode indistinguishable layouts.
49  *
50  * The way we do this is to encode each square of the board, in
51  * the normal left-to-right top-to-bottom order, as being one of
52  * the following things:
53  *  - the first square (in the given order) of a block (`anchor')
54  *  - special case of the above: the anchor for the _main_ block
55  *    (i.e. the one which the aim of the game is to get to the
56  *    target position)
57  *  - a subsequent square of a block whose previous square was N
58  *    squares ago
59  *  - an impassable wall
60  * 
61  * (We also separately store data about which board positions are
62  * forcefields only passable by the main block. We can't encode
63  * that in the main board data, because then the main block would
64  * destroy forcefields as it went over them.)
65  *
66  * Hence, for example, a 2x2 square block would be encoded as
67  * ANCHOR, followed by DIST(1), and w-2 squares later on there
68  * would be DIST(w-1) followed by DIST(1). So if you start at the
69  * last of those squares, the DIST numbers give you a linked list
70  * pointing back through all the other squares in the same block.
71  *
72  * So the solver simply does a bfs over all reachable positions,
73  * encoding them in this format and storing them in a tree234 to
74  * ensure it doesn't ever revisit an already-analysed position.
75  */
76
77 enum {
78     /*
79      * The colours are arranged here so that every base colour is
80      * directly followed by its highlight colour and then its
81      * lowlight colour. Do not break this, or draw_tile() will get
82      * confused.
83      */
84     COL_BACKGROUND,
85     COL_HIGHLIGHT,
86     COL_LOWLIGHT,
87     COL_DRAGGING,
88     COL_DRAGGING_HIGHLIGHT,
89     COL_DRAGGING_LOWLIGHT,
90     COL_MAIN,
91     COL_MAIN_HIGHLIGHT,
92     COL_MAIN_LOWLIGHT,
93     COL_MAIN_DRAGGING,
94     COL_MAIN_DRAGGING_HIGHLIGHT,
95     COL_MAIN_DRAGGING_LOWLIGHT,
96     COL_TARGET,
97     COL_TARGET_HIGHLIGHT,
98     COL_TARGET_LOWLIGHT,
99     NCOLOURS
100 };
101
102 /*
103  * Board layout is a simple array of bytes. Each byte holds:
104  */
105 #define ANCHOR      255                /* top-left-most square of some piece */
106 #define MAINANCHOR  254                /* anchor of _main_ piece */
107 #define EMPTY       253                /* empty square */
108 #define WALL        252                /* immovable wall */
109 #define MAXDIST     251
110 /* all other values indicate distance back to previous square of same block */
111 #define ISDIST(x) ( (unsigned char)((x)-1) <= MAXDIST-1 )
112 #define DIST(x) (x)
113 #define ISANCHOR(x) ( (x)==ANCHOR || (x)==MAINANCHOR )
114 #define ISBLOCK(x) ( ISANCHOR(x) || ISDIST(x) )
115
116 /*
117  * MAXDIST is the largest DIST value we can encode. This must
118  * therefore also be the maximum puzzle width in theory (although
119  * solver running time will dictate a much smaller limit in
120  * practice).
121  */
122 #define MAXWID MAXDIST
123
124 struct game_params {
125     int w, h;
126     int maxmoves;
127 };
128
129 struct game_immutable_state {
130     int refcount;
131     unsigned char *forcefield;
132 };
133
134 struct game_solution {
135     int nmoves;
136     int *moves;                        /* just like from solve_board() */
137     int refcount;
138 };
139
140 struct game_state {
141     int w, h;
142     unsigned char *board;
143     int tx, ty;                        /* target coords for MAINANCHOR */
144     int minmoves;                      /* for display only */
145     int lastmoved, lastmoved_pos;      /* for move counting */
146     int movecount;
147     int completed;
148     int cheated;
149     struct game_immutable_state *imm;
150     struct game_solution *soln;
151     int soln_index;
152 };
153
154 static game_params *default_params(void)
155 {
156     game_params *ret = snew(game_params);
157
158     ret->w = 7;
159     ret->h = 6;
160     ret->maxmoves = 40;
161
162     return ret;
163 }
164
165 static const struct game_params slide_presets[] = {
166     {7, 6, 25},
167     {7, 6, -1},
168     {8, 6, -1},
169 };
170
171 static int game_fetch_preset(int i, char **name, game_params **params)
172 {
173     game_params *ret;
174     char str[80];
175
176     if (i < 0 || i >= lenof(slide_presets))
177         return FALSE;
178
179     ret = snew(game_params);
180     *ret = slide_presets[i];
181
182     sprintf(str, "%dx%d", ret->w, ret->h);
183     if (ret->maxmoves >= 0)
184         sprintf(str + strlen(str), ", max %d moves", ret->maxmoves);
185     else
186         sprintf(str + strlen(str), ", no move limit");
187
188     *name = dupstr(str);
189     *params = ret;
190     return TRUE;
191 }
192
193 static void free_params(game_params *params)
194 {
195     sfree(params);
196 }
197
198 static game_params *dup_params(const game_params *params)
199 {
200     game_params *ret = snew(game_params);
201     *ret = *params;                    /* structure copy */
202     return ret;
203 }
204
205 static void decode_params(game_params *params, char const *string)
206 {
207     params->w = params->h = atoi(string);
208     while (*string && isdigit((unsigned char)*string)) string++;
209     if (*string == 'x') {
210         string++;
211         params->h = atoi(string);
212         while (*string && isdigit((unsigned char)*string)) string++;
213     }
214     if (*string == 'm') {
215         string++;
216         params->maxmoves = atoi(string);
217         while (*string && isdigit((unsigned char)*string)) string++;
218     } else if (*string == 'u') {
219         string++;
220         params->maxmoves = -1;
221     }
222 }
223
224 static char *encode_params(const game_params *params, int full)
225 {
226     char data[256];
227
228     sprintf(data, "%dx%d", params->w, params->h);
229     if (params->maxmoves >= 0)
230         sprintf(data + strlen(data), "m%d", params->maxmoves);
231     else
232         sprintf(data + strlen(data), "u");
233
234     return dupstr(data);
235 }
236
237 static config_item *game_configure(const game_params *params)
238 {
239     config_item *ret;
240     char buf[80];
241
242     ret = snewn(4, config_item);
243
244     ret[0].name = "Width";
245     ret[0].type = C_STRING;
246     sprintf(buf, "%d", params->w);
247     ret[0].u.string.sval = dupstr(buf);
248
249     ret[1].name = "Height";
250     ret[1].type = C_STRING;
251     sprintf(buf, "%d", params->h);
252     ret[1].u.string.sval = dupstr(buf);
253
254     ret[2].name = "Solution length limit";
255     ret[2].type = C_STRING;
256     sprintf(buf, "%d", params->maxmoves);
257     ret[2].u.string.sval = dupstr(buf);
258
259     ret[3].name = NULL;
260     ret[3].type = C_END;
261
262     return ret;
263 }
264
265 static game_params *custom_params(const config_item *cfg)
266 {
267     game_params *ret = snew(game_params);
268
269     ret->w = atoi(cfg[0].u.string.sval);
270     ret->h = atoi(cfg[1].u.string.sval);
271     ret->maxmoves = atoi(cfg[2].u.string.sval);
272
273     return ret;
274 }
275
276 static const char *validate_params(const game_params *params, int full)
277 {
278     if (params->w > MAXWID)
279         return "Width must be at most " STR(MAXWID);
280
281     if (params->w < 5)
282         return "Width must be at least 5";
283     if (params->h < 4)
284         return "Height must be at least 4";
285
286     return NULL;
287 }
288
289 static char *board_text_format(int w, int h, unsigned char *data,
290                                unsigned char *forcefield)
291 {
292     int wh = w*h;
293     int *dsf = snew_dsf(wh);
294     int i, x, y;
295     int retpos, retlen = (w*2+2)*(h*2+1)+1;
296     char *ret = snewn(retlen, char);
297
298     for (i = 0; i < wh; i++)
299         if (ISDIST(data[i]))
300             dsf_merge(dsf, i - data[i], i);
301     retpos = 0;
302     for (y = 0; y < 2*h+1; y++) {
303         for (x = 0; x < 2*w+1; x++) {
304             int v;
305             int i = (y/2)*w+(x/2);
306
307 #define dtype(i) (ISBLOCK(data[i]) ? \
308                   dsf_canonify(dsf, i) : data[i])
309 #define dchar(t) ((t)==EMPTY ? ' ' : (t)==WALL ? '#' : \
310                   data[t] == MAINANCHOR ? '*' : '%')
311
312             if (y % 2 && x % 2) {
313                 int j = dtype(i);
314                 v = dchar(j);
315             } else if (y % 2 && !(x % 2)) {
316                 int j1 = (x > 0 ? dtype(i-1) : -1);
317                 int j2 = (x < 2*w ? dtype(i) : -1);
318                 if (j1 != j2)
319                     v = '|';
320                 else
321                     v = dchar(j1);
322             } else if (!(y % 2) && (x % 2)) {
323                 int j1 = (y > 0 ? dtype(i-w) : -1);
324                 int j2 = (y < 2*h ? dtype(i) : -1);
325                 if (j1 != j2)
326                     v = '-';
327                 else
328                     v = dchar(j1);
329             } else {
330                 int j1 = (x > 0 && y > 0 ? dtype(i-w-1) : -1);
331                 int j2 = (x > 0 && y < 2*h ? dtype(i-1) : -1);
332                 int j3 = (x < 2*w && y > 0 ? dtype(i-w) : -1);
333                 int j4 = (x < 2*w && y < 2*h ? dtype(i) : -1);
334                 if (j1 == j2 && j2 == j3 && j3 == j4)
335                     v = dchar(j1);
336                 else if (j1 == j2 && j3 == j4)
337                     v = '|';
338                 else if (j1 == j3 && j2 == j4)
339                     v = '-';
340                 else
341                     v = '+';
342             }
343
344             assert(retpos < retlen);
345             ret[retpos++] = v;
346         }
347         assert(retpos < retlen);
348         ret[retpos++] = '\n';
349     }
350     assert(retpos < retlen);
351     ret[retpos++] = '\0';
352     assert(retpos == retlen);
353
354     return ret;
355 }
356
357 /* ----------------------------------------------------------------------
358  * Solver.
359  */
360
361 /*
362  * During solver execution, the set of visited board positions is
363  * stored as a tree234 of the following structures. `w', `h' and
364  * `data' are obvious in meaning; `dist' represents the minimum
365  * distance to reach this position from the starting point.
366  * 
367  * `prev' links each board to the board position from which it was
368  * most efficiently derived.
369  */
370 struct board {
371     int w, h;
372     int dist;
373     struct board *prev;
374     unsigned char *data;
375 };
376
377 static int boardcmp(void *av, void *bv)
378 {
379     struct board *a = (struct board *)av;
380     struct board *b = (struct board *)bv;
381     return memcmp(a->data, b->data, a->w * a->h);
382 }
383
384 static struct board *newboard(int w, int h, unsigned char *data)
385 {
386     struct board *b = malloc(sizeof(struct board) + w*h);
387     b->data = (unsigned char *)b + sizeof(struct board);
388     memcpy(b->data, data, w*h);
389     b->w = w;
390     b->h = h;
391     b->dist = -1;
392     b->prev = NULL;
393     return b;
394 }
395
396 /*
397  * The actual solver. Given a board, attempt to find the minimum
398  * length of move sequence which moves MAINANCHOR to (tx,ty), or
399  * -1 if no solution exists. Returns that minimum length.
400  * 
401  * Also, if `moveout' is provided, writes out the moves in the
402  * form of a sequence of pairs of integers indicating the source
403  * and destination points of the anchor of the moved piece in each
404  * move. Exactly twice as many integers are written as the number
405  * returned from solve_board(), and `moveout' receives an int *
406  * which is a pointer to a dynamically allocated array.
407  */
408 static int solve_board(int w, int h, unsigned char *board,
409                        unsigned char *forcefield, int tx, int ty,
410                        int movelimit, int **moveout)
411 {
412     int wh = w*h;
413     struct board *b, *b2, *b3;
414     int *next, *anchors, *which;
415     int *movereached, *movequeue, mqhead, mqtail;
416     tree234 *sorted, *queue;
417     int i, j, dir;
418     int qlen, lastdist;
419     int ret;
420
421 #ifdef SOLVER_DIAGNOSTICS
422     {
423         char *t = board_text_format(w, h, board);
424         for (i = 0; i < h; i++) {
425             for (j = 0; j < w; j++) {
426                 int c = board[i*w+j];
427                 if (ISDIST(c))
428                     printf("D%-3d", c);
429                 else if (c == MAINANCHOR)
430                     printf("M   ");
431                 else if (c == ANCHOR)
432                     printf("A   ");
433                 else if (c == WALL)
434                     printf("W   ");
435                 else if (c == EMPTY)
436                     printf("E   ");
437             }
438             printf("\n");
439         }
440         
441         printf("Starting solver for:\n%s\n", t);
442         sfree(t);
443     }
444 #endif
445
446     sorted = newtree234(boardcmp);
447     queue = newtree234(NULL);
448
449     b = newboard(w, h, board);
450     b->dist = 0;
451     add234(sorted, b);
452     addpos234(queue, b, 0);
453     qlen = 1;
454
455     next = snewn(wh, int);
456     anchors = snewn(wh, int);
457     which = snewn(wh, int);
458     movereached = snewn(wh, int);
459     movequeue = snewn(wh, int);
460     lastdist = -1;
461
462     while ((b = delpos234(queue, 0)) != NULL) {
463         qlen--;
464         if (movelimit >= 0 && b->dist >= movelimit) {
465             /*
466              * The problem is not soluble in under `movelimit'
467              * moves, so we can quit right now.
468              */
469             b2 = NULL;
470             goto done;
471         }
472         if (b->dist != lastdist) {
473 #ifdef SOLVER_DIAGNOSTICS
474             printf("dist %d (%d)\n", b->dist, count234(sorted));
475 #endif
476             lastdist = b->dist;
477         }
478         /*
479          * Find all the anchors and form a linked list of the
480          * squares within each block.
481          */
482         for (i = 0; i < wh; i++) {
483             next[i] = -1;
484             anchors[i] = FALSE;
485             which[i] = -1;
486             if (ISANCHOR(b->data[i])) {
487                 anchors[i] = TRUE;
488                 which[i] = i;
489             } else if (ISDIST(b->data[i])) {
490                 j = i - b->data[i];
491                 next[j] = i;
492                 which[i] = which[j];
493             }
494         }
495
496         /*
497          * For each anchor, do an array-based BFS to find all the
498          * places we can slide it to.
499          */
500         for (i = 0; i < wh; i++) {
501             if (!anchors[i])
502                 continue;
503
504             mqhead = mqtail = 0;
505             for (j = 0; j < wh; j++)
506                 movereached[j] = FALSE;
507             movequeue[mqtail++] = i;
508             while (mqhead < mqtail) {
509                 int pos = movequeue[mqhead++];
510
511                 /*
512                  * Try to move in each direction from here.
513                  */
514                 for (dir = 0; dir < 4; dir++) {
515                     int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
516                     int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
517                     int offset = dy*w + dx;
518                     int newpos = pos + offset;
519                     int d = newpos - i;
520
521                     /*
522                      * For each square involved in this block,
523                      * check to see if the square d spaces away
524                      * from it is either empty or part of the same
525                      * block.
526                      */
527                     for (j = i; j >= 0; j = next[j]) {
528                         int jy = (pos+j-i) / w + dy, jx = (pos+j-i) % w + dx;
529                         if (jy >= 0 && jy < h && jx >= 0 && jx < w &&
530                             ((b->data[j+d] == EMPTY || which[j+d] == i) &&
531                              (b->data[i] == MAINANCHOR || !forcefield[j+d])))
532                             /* ok */;
533                         else
534                             break;
535                     }
536                     if (j >= 0)
537                         continue;              /* this direction wasn't feasible */
538
539                     /*
540                      * If we've already tried moving this piece
541                      * here, leave it.
542                      */
543                     if (movereached[newpos])
544                         continue;
545                     movereached[newpos] = TRUE;
546                     movequeue[mqtail++] = newpos;
547
548                     /*
549                      * We have a viable move. Make it.
550                      */
551                     b2 = newboard(w, h, b->data);
552                     for (j = i; j >= 0; j = next[j])
553                         b2->data[j] = EMPTY;
554                     for (j = i; j >= 0; j = next[j])
555                         b2->data[j+d] = b->data[j];
556
557                     b3 = add234(sorted, b2);
558                     if (b3 != b2) {
559                         sfree(b2);             /* we already got one */
560                     } else {
561                         b2->dist = b->dist + 1;
562                         b2->prev = b;
563                         addpos234(queue, b2, qlen++);
564                         if (b2->data[ty*w+tx] == MAINANCHOR)
565                             goto done;     /* search completed! */
566                     }
567                 }
568             }
569         }
570     }
571     b2 = NULL;
572
573     done:
574
575     if (b2) {
576         ret = b2->dist;
577         if (moveout) {
578             /*
579              * Now b2 represents the solved position. Backtrack to
580              * output the solution.
581              */
582             *moveout = snewn(ret * 2, int);
583             j = ret * 2;
584
585             while (b2->prev) {
586                 int from = -1, to = -1;
587
588                 b = b2->prev;
589
590                 /*
591                  * Scan b and b2 to find out which piece has
592                  * moved.
593                  */
594                 for (i = 0; i < wh; i++) {
595                     if (ISANCHOR(b->data[i]) && !ISANCHOR(b2->data[i])) {
596                         assert(from == -1);
597                         from = i;
598                     } else if (!ISANCHOR(b->data[i]) && ISANCHOR(b2->data[i])){
599                         assert(to == -1);
600                         to = i;
601                     }
602                 }
603
604                 assert(from >= 0 && to >= 0);
605                 assert(j >= 2);
606                 (*moveout)[--j] = to;
607                 (*moveout)[--j] = from;
608
609                 b2 = b;
610             }
611             assert(j == 0);
612         }
613     } else {
614         ret = -1;                      /* no solution */
615         if (moveout)
616             *moveout = NULL;
617     }
618
619     freetree234(queue);
620
621     while ((b = delpos234(sorted, 0)) != NULL)
622         sfree(b);
623     freetree234(sorted);
624
625     sfree(next);
626     sfree(anchors);
627     sfree(movereached);
628     sfree(movequeue);
629     sfree(which);
630
631     return ret;
632 }
633
634 /* ----------------------------------------------------------------------
635  * Random board generation.
636  */
637
638 static void generate_board(int w, int h, int *rtx, int *rty, int *minmoves,
639                            random_state *rs, unsigned char **rboard,
640                            unsigned char **rforcefield, int movelimit)
641 {
642     int wh = w*h;
643     unsigned char *board, *board2, *forcefield;
644     unsigned char *tried_merge;
645     int *dsf;
646     int *list, nlist, pos;
647     int tx, ty;
648     int i, j;
649     int moves = 0;                     /* placate optimiser */
650
651     /*
652      * Set up a board and fill it with singletons, except for a
653      * border of walls.
654      */
655     board = snewn(wh, unsigned char);
656     forcefield = snewn(wh, unsigned char);
657     board2 = snewn(wh, unsigned char);
658     memset(board, ANCHOR, wh);
659     memset(forcefield, FALSE, wh);
660     for (i = 0; i < w; i++)
661         board[i] = board[i+w*(h-1)] = WALL;
662     for (i = 0; i < h; i++)
663         board[i*w] = board[i*w+(w-1)] = WALL;
664
665     tried_merge = snewn(wh * wh, unsigned char);
666     memset(tried_merge, 0, wh*wh);
667     dsf = snew_dsf(wh);
668
669     /*
670      * Invent a main piece at one extreme. (FIXME: vary the
671      * extreme, and the piece.)
672      */
673     board[w+1] = MAINANCHOR;
674     board[w+2] = DIST(1);
675     board[w*2+1] = DIST(w-1);
676     board[w*2+2] = DIST(1);
677
678     /*
679      * Invent a target position. (FIXME: vary this too.)
680      */
681     tx = w-2;
682     ty = h-3;
683     forcefield[ty*w+tx+1] = forcefield[(ty+1)*w+tx+1] = TRUE;
684     board[ty*w+tx+1] = board[(ty+1)*w+tx+1] = EMPTY;
685
686     /*
687      * Gradually remove singletons until the game becomes soluble.
688      */
689     for (j = w; j-- > 0 ;)
690         for (i = h; i-- > 0 ;)
691             if (board[i*w+j] == ANCHOR) {
692                 /*
693                  * See if the board is already soluble.
694                  */
695                 if ((moves = solve_board(w, h, board, forcefield,
696                                          tx, ty, movelimit, NULL)) >= 0)
697                     goto soluble;
698
699                 /*
700                  * Otherwise, remove this piece.
701                  */
702                 board[i*w+j] = EMPTY;
703             }
704     assert(!"We shouldn't get here");
705     soluble:
706
707     /*
708      * Make a list of all the inter-block edges on the board.
709      */
710     list = snewn(wh*2, int);
711     nlist = 0;
712     for (i = 0; i+1 < w; i++)
713         for (j = 0; j < h; j++)
714             list[nlist++] = (j*w+i) * 2 + 0;   /* edge to the right of j*w+i */
715     for (j = 0; j+1 < h; j++)
716         for (i = 0; i < w; i++)
717             list[nlist++] = (j*w+i) * 2 + 1;   /* edge below j*w+i */
718
719     /*
720      * Now go through that list in random order, trying to merge
721      * the blocks on each side of each edge.
722      */
723     shuffle(list, nlist, sizeof(*list), rs);
724     while (nlist > 0) {
725         int x1, y1, p1, c1;
726         int x2, y2, p2, c2;
727
728         pos = list[--nlist];
729         y1 = y2 = pos / (w*2);
730         x1 = x2 = (pos / 2) % w;
731         if (pos % 2)
732             y2++;
733         else
734             x2++;
735         p1 = y1*w+x1;
736         p2 = y2*w+x2;
737
738         /*
739          * Immediately abandon the attempt if we've already tried
740          * to merge the same pair of blocks along a different
741          * edge.
742          */
743         c1 = dsf_canonify(dsf, p1);
744         c2 = dsf_canonify(dsf, p2);
745         if (tried_merge[c1 * wh + c2])
746             continue;
747
748         /*
749          * In order to be mergeable, these two squares must each
750          * either be, or belong to, a non-main anchor, and their
751          * anchors must also be distinct.
752          */
753         if (!ISBLOCK(board[p1]) || !ISBLOCK(board[p2]))
754             continue;
755         while (ISDIST(board[p1]))
756             p1 -= board[p1];
757         while (ISDIST(board[p2]))
758             p2 -= board[p2];
759         if (board[p1] == MAINANCHOR || board[p2] == MAINANCHOR || p1 == p2)
760             continue;
761
762         /*
763          * We can merge these blocks. Try it, and see if the
764          * puzzle remains soluble.
765          */
766         memcpy(board2, board, wh);
767         j = -1;
768         while (p1 < wh || p2 < wh) {
769             /*
770              * p1 and p2 are the squares at the head of each block
771              * list. Pick the smaller one and put it on the output
772              * block list.
773              */
774             i = min(p1, p2);
775             if (j < 0) {
776                 board[i] = ANCHOR;
777             } else {
778                 assert(i - j <= MAXDIST);
779                 board[i] = DIST(i - j);
780             }
781             j = i;
782
783             /*
784              * Now advance whichever list that came from.
785              */
786             if (i == p1) {
787                 do {
788                     p1++;
789                 } while (p1 < wh && board[p1] != DIST(p1-i));
790             } else {
791                 do {
792                     p2++;
793                 } while (p2 < wh && board[p2] != DIST(p2-i));
794             }
795         }
796         j = solve_board(w, h, board, forcefield, tx, ty, movelimit, NULL);
797         if (j < 0) {
798             /*
799              * Didn't work. Revert the merge.
800              */
801             memcpy(board, board2, wh);
802             tried_merge[c1 * wh + c2] = tried_merge[c2 * wh + c1] = TRUE;
803         } else {
804             int c;
805
806             moves = j;
807
808             dsf_merge(dsf, c1, c2);
809             c = dsf_canonify(dsf, c1);
810             for (i = 0; i < wh; i++)
811                 tried_merge[c*wh+i] = (tried_merge[c1*wh+i] |
812                                        tried_merge[c2*wh+i]);
813             for (i = 0; i < wh; i++)
814                 tried_merge[i*wh+c] = (tried_merge[i*wh+c1] |
815                                        tried_merge[i*wh+c2]);
816         }
817     }
818
819     sfree(dsf);
820     sfree(list);
821     sfree(tried_merge);
822     sfree(board2);
823
824     *rtx = tx;
825     *rty = ty;
826     *rboard = board;
827     *rforcefield = forcefield;
828     *minmoves = moves;
829 }
830
831 /* ----------------------------------------------------------------------
832  * End of solver/generator code.
833  */
834
835 static char *new_game_desc(const game_params *params, random_state *rs,
836                            char **aux, int interactive)
837 {
838     int w = params->w, h = params->h, wh = w*h;
839     int tx, ty, minmoves;
840     unsigned char *board, *forcefield;
841     char *ret, *p;
842     int i;
843
844     generate_board(params->w, params->h, &tx, &ty, &minmoves, rs,
845                    &board, &forcefield, params->maxmoves);
846 #ifdef GENERATOR_DIAGNOSTICS
847     {
848         char *t = board_text_format(params->w, params->h, board);
849         printf("%s\n", t);
850         sfree(t);
851     }
852 #endif
853
854     /*
855      * Encode as a game ID.
856      */
857     ret = snewn(wh * 6 + 40, char);
858     p = ret;
859     i = 0;
860     while (i < wh) {
861         if (ISDIST(board[i])) {
862             p += sprintf(p, "d%d", board[i]);
863             i++;
864         } else {
865             int count = 1;
866             int b = board[i], f = forcefield[i];
867             int c = (b == ANCHOR ? 'a' :
868                      b == MAINANCHOR ? 'm' :
869                      b == EMPTY ? 'e' :
870                      /* b == WALL ? */ 'w');
871             if (f) *p++ = 'f';
872             *p++ = c;
873             i++;
874             while (i < wh && board[i] == b && forcefield[i] == f)
875                 i++, count++;
876             if (count > 1)
877                 p += sprintf(p, "%d", count);
878         }
879     }
880     p += sprintf(p, ",%d,%d,%d", tx, ty, minmoves);
881     ret = sresize(ret, p+1 - ret, char);
882
883     sfree(board);
884     sfree(forcefield);
885
886     return ret;
887 }
888
889 static const char *validate_desc(const game_params *params, const char *desc)
890 {
891     int w = params->w, h = params->h, wh = w*h;
892     int *active, *link;
893     int mains = 0;
894     int i, tx, ty, minmoves;
895     char *ret;
896
897     active = snewn(wh, int);
898     link = snewn(wh, int);
899     i = 0;
900
901     while (*desc && *desc != ',') {
902         if (i >= wh) {
903             ret = "Too much data in game description";
904             goto done;
905         }
906         link[i] = -1;
907         active[i] = FALSE;
908         if (*desc == 'f' || *desc == 'F') {
909             desc++;
910             if (!*desc) {
911                 ret = "Expected another character after 'f' in game "
912                     "description";
913                 goto done;
914             }
915         }
916
917         if (*desc == 'd' || *desc == 'D') {
918             int dist;
919
920             desc++;
921             if (!isdigit((unsigned char)*desc)) {
922                 ret = "Expected a number after 'd' in game description";
923                 goto done;
924             }
925             dist = atoi(desc);
926             while (*desc && isdigit((unsigned char)*desc)) desc++;
927
928             if (dist <= 0 || dist > i) {
929                 ret = "Out-of-range number after 'd' in game description";
930                 goto done;
931             }
932
933             if (!active[i - dist]) {
934                 ret = "Invalid back-reference in game description";
935                 goto done;
936             }
937
938             link[i] = i - dist;
939
940             active[i] = TRUE;
941             active[link[i]] = FALSE;
942             i++;
943         } else {
944             int c = *desc++;
945             int count = 1;
946
947             if (!strchr("aAmMeEwW", c)) {
948                 ret = "Invalid character in game description";
949                 goto done;
950             }
951             if (isdigit((unsigned char)*desc)) {
952                 count = atoi(desc);
953                 while (*desc && isdigit((unsigned char)*desc)) desc++;
954             }
955             if (i + count > wh) {
956                 ret = "Too much data in game description";
957                 goto done;
958             }
959             while (count-- > 0) {
960                 active[i] = (strchr("aAmM", c) != NULL);
961                 link[i] = -1;
962                 if (strchr("mM", c) != NULL) {
963                     mains++;
964                 }
965                 i++;
966             }
967         }
968     }
969     if (mains != 1) {
970         ret = (mains == 0 ? "No main piece specified in game description" :
971                "More than one main piece specified in game description");
972         goto done;
973     }
974     if (i < wh) {
975         ret = "Not enough data in game description";
976         goto done;
977     }
978
979     /*
980      * Now read the target coordinates.
981      */
982     i = sscanf(desc, ",%d,%d,%d", &tx, &ty, &minmoves);
983     if (i < 2) {
984         ret = "No target coordinates specified";
985         goto done;
986         /*
987          * (but minmoves is optional)
988          */
989     }
990
991     ret = NULL;
992
993     done:
994     sfree(active);
995     sfree(link);
996     return ret;
997 }
998
999 static game_state *new_game(midend *me, const game_params *params,
1000                             const char *desc)
1001 {
1002     int w = params->w, h = params->h, wh = w*h;
1003     game_state *state;
1004     int i;
1005
1006     state = snew(game_state);
1007     state->w = w;
1008     state->h = h;
1009     state->board = snewn(wh, unsigned char);
1010     state->lastmoved = state->lastmoved_pos = -1;
1011     state->movecount = 0;
1012     state->imm = snew(struct game_immutable_state);
1013     state->imm->refcount = 1;
1014     state->imm->forcefield = snewn(wh, unsigned char);
1015
1016     i = 0;
1017
1018     while (*desc && *desc != ',') {
1019         int f = FALSE;
1020
1021         assert(i < wh);
1022
1023         if (*desc == 'f') {
1024             f = TRUE;
1025             desc++;
1026             assert(*desc);
1027         }
1028
1029         if (*desc == 'd' || *desc == 'D') {
1030             int dist;
1031
1032             desc++;
1033             dist = atoi(desc);
1034             while (*desc && isdigit((unsigned char)*desc)) desc++;
1035
1036             state->board[i] = DIST(dist);
1037             state->imm->forcefield[i] = f;
1038
1039             i++;
1040         } else {
1041             int c = *desc++;
1042             int count = 1;
1043
1044             if (isdigit((unsigned char)*desc)) {
1045                 count = atoi(desc);
1046                 while (*desc && isdigit((unsigned char)*desc)) desc++;
1047             }
1048             assert(i + count <= wh);
1049
1050             c = (c == 'a' || c == 'A' ? ANCHOR :
1051                  c == 'm' || c == 'M' ? MAINANCHOR :
1052                  c == 'e' || c == 'E' ? EMPTY :
1053                  /* c == 'w' || c == 'W' ? */ WALL);             
1054
1055             while (count-- > 0) {
1056                 state->board[i] = c;
1057                 state->imm->forcefield[i] = f;
1058                 i++;
1059             }
1060         }
1061     }
1062
1063     /*
1064      * Now read the target coordinates.
1065      */
1066     state->tx = state->ty = 0;
1067     state->minmoves = -1;
1068     i = sscanf(desc, ",%d,%d,%d", &state->tx, &state->ty, &state->minmoves);
1069
1070     if (state->board[state->ty*w+state->tx] == MAINANCHOR)
1071         state->completed = 0;          /* already complete! */
1072     else
1073         state->completed = -1;
1074
1075     state->cheated = FALSE;
1076     state->soln = NULL;
1077     state->soln_index = -1;
1078
1079     return state;
1080 }
1081
1082 static game_state *dup_game(const game_state *state)
1083 {
1084     int w = state->w, h = state->h, wh = w*h;
1085     game_state *ret = snew(game_state);
1086
1087     ret->w = state->w;
1088     ret->h = state->h;
1089     ret->board = snewn(wh, unsigned char);
1090     memcpy(ret->board, state->board, wh);
1091     ret->tx = state->tx;
1092     ret->ty = state->ty;
1093     ret->minmoves = state->minmoves;
1094     ret->lastmoved = state->lastmoved;
1095     ret->lastmoved_pos = state->lastmoved_pos;
1096     ret->movecount = state->movecount;
1097     ret->completed = state->completed;
1098     ret->cheated = state->cheated;
1099     ret->imm = state->imm;
1100     ret->imm->refcount++;
1101     ret->soln = state->soln;
1102     ret->soln_index = state->soln_index;
1103     if (ret->soln)
1104         ret->soln->refcount++;
1105
1106     return ret;
1107 }
1108
1109 static void free_game(game_state *state)
1110 {
1111     if (--state->imm->refcount <= 0) {
1112         sfree(state->imm->forcefield);
1113         sfree(state->imm);
1114     }
1115     if (state->soln && --state->soln->refcount <= 0) {
1116         sfree(state->soln->moves);
1117         sfree(state->soln);
1118     }
1119     sfree(state->board);
1120     sfree(state);
1121 }
1122
1123 static char *solve_game(const game_state *state, const game_state *currstate,
1124                         const char *aux, const char **error)
1125 {
1126     int *moves;
1127     int nmoves;
1128     int i;
1129     char *ret, *p, sep;
1130
1131     /*
1132      * Run the solver and attempt to find the shortest solution
1133      * from the current position.
1134      */
1135     nmoves = solve_board(state->w, state->h, state->board,
1136                          state->imm->forcefield, state->tx, state->ty,
1137                          -1, &moves);
1138
1139     if (nmoves < 0) {
1140         *error = "Unable to find a solution to this puzzle";
1141         return NULL;
1142     }
1143     if (nmoves == 0) {
1144         *error = "Puzzle is already solved";
1145         return NULL;
1146     }
1147
1148     /*
1149      * Encode the resulting solution as a move string.
1150      */
1151     ret = snewn(nmoves * 40, char);
1152     p = ret;
1153     sep = 'S';
1154
1155     for (i = 0; i < nmoves; i++) {
1156         p += sprintf(p, "%c%d-%d", sep, moves[i*2], moves[i*2+1]);
1157         sep = ',';
1158     }
1159
1160     sfree(moves);
1161     assert(p - ret < nmoves * 40);
1162     ret = sresize(ret, p+1 - ret, char);
1163
1164     return ret;
1165 }
1166
1167 static int game_can_format_as_text_now(const game_params *params)
1168 {
1169     return TRUE;
1170 }
1171
1172 static char *game_text_format(const game_state *state)
1173 {
1174     return board_text_format(state->w, state->h, state->board,
1175                              state->imm->forcefield);
1176 }
1177
1178 struct game_ui {
1179     int dragging;
1180     int drag_anchor;
1181     int drag_offset_x, drag_offset_y;
1182     int drag_currpos;
1183     unsigned char *reachable;
1184     int *bfs_queue;                    /* used as scratch in interpret_move */
1185 };
1186
1187 static game_ui *new_ui(const game_state *state)
1188 {
1189     int w = state->w, h = state->h, wh = w*h;
1190     game_ui *ui = snew(game_ui);
1191
1192     ui->dragging = FALSE;
1193     ui->drag_anchor = ui->drag_currpos = -1;
1194     ui->drag_offset_x = ui->drag_offset_y = -1;
1195     ui->reachable = snewn(wh, unsigned char);
1196     memset(ui->reachable, 0, wh);
1197     ui->bfs_queue = snewn(wh, int);
1198
1199     return ui;
1200 }
1201
1202 static void free_ui(game_ui *ui)
1203 {
1204     sfree(ui->bfs_queue);
1205     sfree(ui->reachable);
1206     sfree(ui);
1207 }
1208
1209 static char *encode_ui(const game_ui *ui)
1210 {
1211     return NULL;
1212 }
1213
1214 static void decode_ui(game_ui *ui, const char *encoding)
1215 {
1216 }
1217
1218 static void game_changed_state(game_ui *ui, const game_state *oldstate,
1219                                const game_state *newstate)
1220 {
1221 }
1222
1223 #define PREFERRED_TILESIZE 32
1224 #define TILESIZE (ds->tilesize)
1225 #define BORDER (TILESIZE/2)
1226 #define COORD(x)  ( (x) * TILESIZE + BORDER )
1227 #define FROMCOORD(x)  ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
1228 #define BORDER_WIDTH (1 + TILESIZE/20)
1229 #define HIGHLIGHT_WIDTH (1 + TILESIZE/16)
1230
1231 #define FLASH_INTERVAL 0.10F
1232 #define FLASH_TIME 3*FLASH_INTERVAL
1233
1234 struct game_drawstate {
1235     int tilesize;
1236     int w, h;
1237     unsigned long *grid;               /* what's currently displayed */
1238     int started;
1239 };
1240
1241 static char *interpret_move(const game_state *state, game_ui *ui,
1242                             const game_drawstate *ds,
1243                             int x, int y, int button)
1244 {
1245     int w = state->w, h = state->h, wh = w*h;
1246     int tx, ty, i, j;
1247     int qhead, qtail;
1248
1249     if (button == LEFT_BUTTON) {
1250         tx = FROMCOORD(x);
1251         ty = FROMCOORD(y);
1252
1253         if (tx < 0 || tx >= w || ty < 0 || ty >= h ||
1254             !ISBLOCK(state->board[ty*w+tx]))
1255             return NULL;               /* this click has no effect */
1256
1257         /*
1258          * User has clicked on a block. Find the block's anchor
1259          * and register that we've started dragging it.
1260          */
1261         i = ty*w+tx;
1262         while (ISDIST(state->board[i]))
1263             i -= state->board[i];
1264         assert(i >= 0 && i < wh);
1265
1266         ui->dragging = TRUE;
1267         ui->drag_anchor = i;
1268         ui->drag_offset_x = tx - (i % w);
1269         ui->drag_offset_y = ty - (i / w);
1270         ui->drag_currpos = i;
1271
1272         /*
1273          * Now we immediately bfs out from the current location of
1274          * the anchor, to find all the places to which this block
1275          * can be dragged.
1276          */
1277         memset(ui->reachable, FALSE, wh);
1278         qhead = qtail = 0;
1279         ui->reachable[i] = TRUE;
1280         ui->bfs_queue[qtail++] = i;
1281         for (j = i; j < wh; j++)
1282             if (state->board[j] == DIST(j - i))
1283                 i = j;
1284         while (qhead < qtail) {
1285             int pos = ui->bfs_queue[qhead++];
1286             int x = pos % w, y = pos / w;
1287             int dir;
1288
1289             for (dir = 0; dir < 4; dir++) {
1290                 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
1291                 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
1292                 int newpos;
1293
1294                 if (x + dx < 0 || x + dx >= w ||
1295                     y + dy < 0 || y + dy >= h)
1296                     continue;
1297
1298                 newpos = pos + dy*w + dx;
1299                 if (ui->reachable[newpos])
1300                     continue;          /* already done this one */
1301
1302                 /*
1303                  * Now search the grid to see if the block we're
1304                  * dragging could fit into this space.
1305                  */
1306                 for (j = i; j >= 0; j = (ISDIST(state->board[j]) ?
1307                                          j - state->board[j] : -1)) {
1308                     int jx = (j+pos-ui->drag_anchor) % w;
1309                     int jy = (j+pos-ui->drag_anchor) / w;
1310                     int j2;
1311
1312                     if (jx + dx < 0 || jx + dx >= w ||
1313                         jy + dy < 0 || jy + dy >= h)
1314                         break;         /* this position isn't valid at all */
1315
1316                     j2 = (j+pos-ui->drag_anchor) + dy*w + dx;
1317
1318                     if (state->board[j2] == EMPTY &&
1319                         (!state->imm->forcefield[j2] ||
1320                          state->board[ui->drag_anchor] == MAINANCHOR))
1321                         continue;
1322                     while (ISDIST(state->board[j2]))
1323                         j2 -= state->board[j2];
1324                     assert(j2 >= 0 && j2 < wh);
1325                     if (j2 == ui->drag_anchor)
1326                         continue;
1327                     else
1328                         break;
1329                 }
1330
1331                 if (j < 0) {
1332                     /*
1333                      * If we got to the end of that loop without
1334                      * disqualifying this position, mark it as
1335                      * reachable for this drag.
1336                      */
1337                     ui->reachable[newpos] = TRUE;
1338                     ui->bfs_queue[qtail++] = newpos;
1339                 }
1340             }
1341         }
1342
1343         /*
1344          * And that's it. Update the display to reflect the start
1345          * of a drag.
1346          */
1347         return UI_UPDATE;
1348     } else if (button == LEFT_DRAG && ui->dragging) {
1349         int dist, distlimit, dx, dy, s, px, py;
1350
1351         tx = FROMCOORD(x);
1352         ty = FROMCOORD(y);
1353
1354         tx -= ui->drag_offset_x;
1355         ty -= ui->drag_offset_y;
1356
1357         /*
1358          * Now search outwards from (tx,ty), in order of Manhattan
1359          * distance, until we find a reachable square.
1360          */
1361         distlimit = w+tx;
1362         distlimit = max(distlimit, h+ty);
1363         distlimit = max(distlimit, tx);
1364         distlimit = max(distlimit, ty);
1365         for (dist = 0; dist <= distlimit; dist++) {
1366             for (dx = -dist; dx <= dist; dx++)
1367                 for (s = -1; s <= +1; s += 2) {
1368                     dy = s * (dist - abs(dx));
1369                     px = tx + dx;
1370                     py = ty + dy;
1371                     if (px >= 0 && px < w && py >= 0 && py < h &&
1372                         ui->reachable[py*w+px]) {
1373                         ui->drag_currpos = py*w+px;
1374                         return UI_UPDATE;
1375                     }
1376                 }
1377         }
1378         return NULL;                   /* give up - this drag has no effect */
1379     } else if (button == LEFT_RELEASE && ui->dragging) {
1380         char data[256], *str;
1381
1382         /*
1383          * Terminate the drag, and if the piece has actually moved
1384          * then return a move string quoting the old and new
1385          * locations of the piece's anchor.
1386          */
1387         if (ui->drag_anchor != ui->drag_currpos) {
1388             sprintf(data, "M%d-%d", ui->drag_anchor, ui->drag_currpos);
1389             str = dupstr(data);
1390         } else
1391             str = "";                  /* null move; just update the UI */
1392         
1393         ui->dragging = FALSE;
1394         ui->drag_anchor = ui->drag_currpos = -1;
1395         ui->drag_offset_x = ui->drag_offset_y = -1;
1396         memset(ui->reachable, 0, wh);
1397
1398         return str;
1399     } else if (button == ' ' && state->soln) {
1400         /*
1401          * Make the next move in the stored solution.
1402          */
1403         char data[256];
1404         int a1, a2;
1405
1406         a1 = state->soln->moves[state->soln_index*2];
1407         a2 = state->soln->moves[state->soln_index*2+1];
1408         if (a1 == state->lastmoved_pos)
1409             a1 = state->lastmoved;
1410
1411         sprintf(data, "M%d-%d", a1, a2);
1412         return dupstr(data);
1413     }
1414
1415     return NULL;
1416 }
1417
1418 static int move_piece(int w, int h, const unsigned char *src,
1419                       unsigned char *dst, unsigned char *ff, int from, int to)
1420 {
1421     int wh = w*h;
1422     int i, j;
1423
1424     if (!ISANCHOR(dst[from]))
1425         return FALSE;
1426
1427     /*
1428      * Scan to the far end of the piece's linked list.
1429      */
1430     for (i = j = from; j < wh; j++)
1431         if (src[j] == DIST(j - i))
1432             i = j;
1433
1434     /*
1435      * Remove the piece from its old location in the new
1436      * game state.
1437      */
1438     for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1))
1439         dst[j] = EMPTY;
1440
1441     /*
1442      * And put it back in at the new location.
1443      */
1444     for (j = i; j >= 0; j = (ISDIST(src[j]) ? j - src[j] : -1)) {
1445         int jn = j + to - from;
1446         if (jn < 0 || jn >= wh)
1447             return FALSE;
1448         if (dst[jn] == EMPTY && (!ff[jn] || src[from] == MAINANCHOR)) {
1449             dst[jn] = src[j];
1450         } else {
1451             return FALSE;
1452         }
1453     }
1454
1455     return TRUE;
1456 }
1457
1458 static game_state *execute_move(const game_state *state, const char *move)
1459 {
1460     int w = state->w, h = state->h /* , wh = w*h */;
1461     char c;
1462     int a1, a2, n, movesize;
1463     game_state *ret = dup_game(state);
1464
1465     while (*move) {
1466         c = *move;
1467         if (c == 'S') {
1468             /*
1469              * This is a solve move, so we just set up a stored
1470              * solution path.
1471              */
1472             if (ret->soln && --ret->soln->refcount <= 0) {
1473                 sfree(ret->soln->moves);
1474                 sfree(ret->soln);
1475             }
1476             ret->soln = snew(struct game_solution);
1477             ret->soln->nmoves = 0;
1478             ret->soln->moves = NULL;
1479             ret->soln->refcount = 1;
1480             ret->soln_index = 0;
1481             ret->cheated = TRUE;
1482
1483             movesize = 0;
1484             move++;
1485             while (1) {
1486                 if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2) {
1487                     free_game(ret);
1488                     return NULL;
1489                 }
1490
1491                 /*
1492                  * Special case: if the first move in the solution
1493                  * involves the piece for which we already have a
1494                  * partial stored move, adjust the source point to
1495                  * the original starting point of that piece.
1496                  */
1497                 if (ret->soln->nmoves == 0 && a1 == ret->lastmoved)
1498                     a1 = ret->lastmoved_pos;
1499
1500                 if (ret->soln->nmoves >= movesize) {
1501                     movesize = (ret->soln->nmoves + 48) * 4 / 3;
1502                     ret->soln->moves = sresize(ret->soln->moves,
1503                                                2*movesize, int);
1504                 }
1505
1506                 ret->soln->moves[2*ret->soln->nmoves] = a1;
1507                 ret->soln->moves[2*ret->soln->nmoves+1] = a2;
1508                 ret->soln->nmoves++;
1509                 move += n;
1510                 if (*move != ',')
1511                     break;
1512                 move++;                /* eat comma */
1513             }
1514         } else if (c == 'M') {
1515             move++;
1516             if (sscanf(move, "%d-%d%n", &a1, &a2, &n) != 2 ||
1517                 !move_piece(w, h, state->board, ret->board,
1518                             state->imm->forcefield, a1, a2)) {
1519                 free_game(ret);
1520                 return NULL;
1521             }
1522             if (a1 == ret->lastmoved) {
1523                 /*
1524                  * If the player has moved the same piece as they
1525                  * moved last time, don't increment the move
1526                  * count. In fact, if they've put the piece back
1527                  * where it started from, _decrement_ the move
1528                  * count.
1529                  */
1530                 if (a2 == ret->lastmoved_pos) {
1531                     ret->movecount--;  /* reverted last move */
1532                     ret->lastmoved = ret->lastmoved_pos = -1;
1533                 } else {
1534                     ret->lastmoved = a2;
1535                     /* don't change lastmoved_pos */
1536                 }
1537             } else {
1538                 ret->lastmoved = a2;
1539                 ret->lastmoved_pos = a1;
1540                 ret->movecount++;
1541             }
1542
1543             /*
1544              * If we have a stored solution path, see if we've
1545              * strayed from it or successfully made the next move
1546              * along it.
1547              */
1548             if (ret->soln && ret->lastmoved_pos >= 0) {
1549                 if (ret->lastmoved_pos !=
1550                     ret->soln->moves[ret->soln_index*2]) {
1551                     /* strayed from the path */
1552                     ret->soln->refcount--;
1553                     assert(ret->soln->refcount > 0);
1554                                        /* `state' at least still exists */
1555                     ret->soln = NULL;
1556                     ret->soln_index = -1;
1557                 } else if (ret->lastmoved ==
1558                            ret->soln->moves[ret->soln_index*2+1]) {
1559                     /* advanced along the path */
1560                     ret->soln_index++;
1561                     if (ret->soln_index >= ret->soln->nmoves) {
1562                         /* finished the path! */
1563                         ret->soln->refcount--;
1564                         assert(ret->soln->refcount > 0);
1565                                        /* `state' at least still exists */
1566                         ret->soln = NULL;
1567                         ret->soln_index = -1;
1568                     }
1569                 }
1570             }
1571
1572             if (ret->board[a2] == MAINANCHOR &&
1573                 a2 == ret->ty * w + ret->tx && ret->completed < 0)
1574                 ret->completed = ret->movecount;
1575             move += n;
1576         } else {
1577             free_game(ret);
1578             return NULL;
1579         }
1580         if (*move == ';')
1581             move++;
1582         else if (*move) {
1583             free_game(ret);
1584             return NULL;
1585         }
1586     }
1587
1588     return ret;
1589 }
1590
1591 /* ----------------------------------------------------------------------
1592  * Drawing routines.
1593  */
1594
1595 static void game_compute_size(const game_params *params, int tilesize,
1596                               int *x, int *y)
1597 {
1598     /* fool the macros */
1599     struct dummy { int tilesize; } dummy, *ds = &dummy;
1600     dummy.tilesize = tilesize;
1601
1602     *x = params->w * TILESIZE + 2*BORDER;
1603     *y = params->h * TILESIZE + 2*BORDER;
1604 }
1605
1606 static void game_set_size(drawing *dr, game_drawstate *ds,
1607                           const game_params *params, int tilesize)
1608 {
1609     ds->tilesize = tilesize;
1610 }
1611
1612 static void raise_colour(float *target, float *src, float *limit)
1613 {
1614     int i;
1615     for (i = 0; i < 3; i++)
1616         target[i] = (2*src[i] + limit[i]) / 3;
1617 }
1618
1619 static float *game_colours(frontend *fe, int *ncolours)
1620 {
1621     float *ret = snewn(3 * NCOLOURS, float);
1622
1623     game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1624
1625     /*
1626      * When dragging a tile, we light it up a bit.
1627      */
1628     raise_colour(ret+3*COL_DRAGGING,
1629                  ret+3*COL_BACKGROUND, ret+3*COL_HIGHLIGHT);
1630     raise_colour(ret+3*COL_DRAGGING_HIGHLIGHT,
1631                  ret+3*COL_HIGHLIGHT, ret+3*COL_HIGHLIGHT);
1632     raise_colour(ret+3*COL_DRAGGING_LOWLIGHT,
1633                  ret+3*COL_LOWLIGHT, ret+3*COL_HIGHLIGHT);
1634
1635     /*
1636      * The main tile is tinted blue.
1637      */
1638     ret[COL_MAIN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1639     ret[COL_MAIN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1];
1640     ret[COL_MAIN * 3 + 2] = ret[COL_HIGHLIGHT * 3 + 2];
1641     game_mkhighlight_specific(fe, ret, COL_MAIN,
1642                               COL_MAIN_HIGHLIGHT, COL_MAIN_LOWLIGHT);
1643
1644     /*
1645      * And we light that up a bit too when dragging.
1646      */
1647     raise_colour(ret+3*COL_MAIN_DRAGGING,
1648                  ret+3*COL_MAIN, ret+3*COL_MAIN_HIGHLIGHT);
1649     raise_colour(ret+3*COL_MAIN_DRAGGING_HIGHLIGHT,
1650                  ret+3*COL_MAIN_HIGHLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1651     raise_colour(ret+3*COL_MAIN_DRAGGING_LOWLIGHT,
1652                  ret+3*COL_MAIN_LOWLIGHT, ret+3*COL_MAIN_HIGHLIGHT);
1653
1654     /*
1655      * The target area on the floor is tinted green.
1656      */
1657     ret[COL_TARGET * 3 + 0] = ret[COL_BACKGROUND * 3 + 0];
1658     ret[COL_TARGET * 3 + 1] = ret[COL_HIGHLIGHT * 3 + 1];
1659     ret[COL_TARGET * 3 + 2] = ret[COL_BACKGROUND * 3 + 2];
1660     game_mkhighlight_specific(fe, ret, COL_TARGET,
1661                               COL_TARGET_HIGHLIGHT, COL_TARGET_LOWLIGHT);
1662
1663     *ncolours = NCOLOURS;
1664     return ret;
1665 }
1666
1667 static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1668 {
1669     int w = state->w, h = state->h, wh = w*h;
1670     struct game_drawstate *ds = snew(struct game_drawstate);
1671     int i;
1672
1673     ds->tilesize = 0;
1674     ds->w = w;
1675     ds->h = h;
1676     ds->started = FALSE;
1677     ds->grid = snewn(wh, unsigned long);
1678     for (i = 0; i < wh; i++)
1679         ds->grid[i] = ~(unsigned long)0;
1680
1681     return ds;
1682 }
1683
1684 static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1685 {
1686     sfree(ds->grid);
1687     sfree(ds);
1688 }
1689
1690 #define BG_NORMAL       0x00000001UL
1691 #define BG_TARGET       0x00000002UL
1692 #define BG_FORCEFIELD   0x00000004UL
1693 #define FLASH_LOW       0x00000008UL
1694 #define FLASH_HIGH      0x00000010UL
1695 #define FG_WALL         0x00000020UL
1696 #define FG_MAIN         0x00000040UL
1697 #define FG_NORMAL       0x00000080UL
1698 #define FG_DRAGGING     0x00000100UL
1699 #define FG_SHADOW       0x00000200UL
1700 #define FG_SOLVEPIECE   0x00000400UL
1701 #define FG_MAINPIECESH  11
1702 #define FG_SHADOWSH     19
1703
1704 #define PIECE_LBORDER   0x00000001UL
1705 #define PIECE_TBORDER   0x00000002UL
1706 #define PIECE_RBORDER   0x00000004UL
1707 #define PIECE_BBORDER   0x00000008UL
1708 #define PIECE_TLCORNER  0x00000010UL
1709 #define PIECE_TRCORNER  0x00000020UL
1710 #define PIECE_BLCORNER  0x00000040UL
1711 #define PIECE_BRCORNER  0x00000080UL
1712 #define PIECE_MASK      0x000000FFUL
1713
1714 /*
1715  * Utility function.
1716  */
1717 #define TYPE_MASK 0xF000
1718 #define COL_MASK 0x0FFF
1719 #define TYPE_RECT 0x0000
1720 #define TYPE_TLCIRC 0x4000
1721 #define TYPE_TRCIRC 0x5000
1722 #define TYPE_BLCIRC 0x6000
1723 #define TYPE_BRCIRC 0x7000
1724 static void maybe_rect(drawing *dr, int x, int y, int w, int h,
1725                        int coltype, int col2)
1726 {
1727     int colour = coltype & COL_MASK, type = coltype & TYPE_MASK;
1728
1729     if (colour > NCOLOURS)
1730         return;
1731     if (type == TYPE_RECT) {
1732         draw_rect(dr, x, y, w, h, colour);
1733     } else {
1734         int cx, cy, r;
1735
1736         clip(dr, x, y, w, h);
1737
1738         cx = x;
1739         cy = y;
1740         r = w-1;
1741         if (type & 0x1000)
1742             cx += r;
1743         if (type & 0x2000)
1744             cy += r;
1745
1746         if (col2 == -1 || col2 == coltype) {
1747             assert(w == h);
1748             draw_circle(dr, cx, cy, r, colour, colour);
1749         } else {
1750             /*
1751              * We aim to draw a quadrant of a circle in two
1752              * different colours. We do this using Bresenham's
1753              * algorithm directly, because the Puzzles drawing API
1754              * doesn't have a draw-sector primitive.
1755              */
1756             int bx, by, bd, bd2;
1757             int xm = (type & 0x1000 ? -1 : +1);
1758             int ym = (type & 0x2000 ? -1 : +1);
1759
1760             by = r;
1761             bx = 0;
1762             bd = 0;
1763             while (by >= bx) {
1764                 /*
1765                  * Plot the point.
1766                  */
1767                 {
1768                     int x1 = cx+xm*bx, y1 = cy+ym*bx;
1769                     int x2, y2;
1770
1771                     x2 = cx+xm*by; y2 = y1;
1772                     draw_rect(dr, min(x1,x2), min(y1,y2),
1773                               abs(x1-x2)+1, abs(y1-y2)+1, colour);
1774                     x2 = x1; y2 = cy+ym*by;
1775                     draw_rect(dr, min(x1,x2), min(y1,y2),
1776                               abs(x1-x2)+1, abs(y1-y2)+1, col2);
1777                 }
1778
1779                 bd += 2*bx + 1;
1780                 bd2 = bd - (2*by - 1);
1781                 if (abs(bd2) < abs(bd)) {
1782                     bd = bd2;
1783                     by--;
1784                 }
1785                 bx++;
1786             }
1787         }
1788
1789         unclip(dr);
1790     }
1791 }
1792
1793 static void draw_wallpart(drawing *dr, game_drawstate *ds,
1794                           int tx, int ty, unsigned long val,
1795                           int cl, int cc, int ch)
1796 {
1797     int coords[6];
1798
1799     draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
1800     if (val & PIECE_LBORDER)
1801         draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, TILESIZE,
1802                   ch);
1803     if (val & PIECE_RBORDER)
1804         draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1805                   HIGHLIGHT_WIDTH, TILESIZE, cl);
1806     if (val & PIECE_TBORDER)
1807         draw_rect(dr, tx, ty, TILESIZE, HIGHLIGHT_WIDTH, ch);
1808     if (val & PIECE_BBORDER)
1809         draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1810                   TILESIZE, HIGHLIGHT_WIDTH, cl);
1811     if (!((PIECE_BBORDER | PIECE_LBORDER) &~ val)) {
1812         draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1813                   HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1814         clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1815              HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1816         coords[0] = tx - 1;
1817         coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1818         coords[2] = tx + HIGHLIGHT_WIDTH;
1819         coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1820         coords[4] = tx - 1;
1821         coords[5] = ty + TILESIZE;
1822         draw_polygon(dr, coords, 3, ch, ch);
1823         unclip(dr);
1824     } else if (val & PIECE_BLCORNER) {
1825         draw_rect(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1826                   HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1827         clip(dr, tx, ty+TILESIZE-HIGHLIGHT_WIDTH,
1828              HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1829         coords[0] = tx - 1;
1830         coords[1] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1831         coords[2] = tx + HIGHLIGHT_WIDTH;
1832         coords[3] = ty + TILESIZE - HIGHLIGHT_WIDTH - 1;
1833         coords[4] = tx - 1;
1834         coords[5] = ty + TILESIZE;
1835         draw_polygon(dr, coords, 3, cl, cl);
1836         unclip(dr);
1837     }
1838     if (!((PIECE_TBORDER | PIECE_RBORDER) &~ val)) {
1839         draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1840                   HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1841         clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1842              HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1843         coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1844         coords[1] = ty - 1;
1845         coords[2] = tx + TILESIZE;
1846         coords[3] = ty - 1;
1847         coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1848         coords[5] = ty + HIGHLIGHT_WIDTH;
1849         draw_polygon(dr, coords, 3, ch, ch);
1850         unclip(dr);
1851     } else if (val & PIECE_TRCORNER) {
1852         draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1853                   HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1854         clip(dr, tx+TILESIZE-HIGHLIGHT_WIDTH, ty,
1855              HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH);
1856         coords[0] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1857         coords[1] = ty - 1;
1858         coords[2] = tx + TILESIZE;
1859         coords[3] = ty - 1;
1860         coords[4] = tx + TILESIZE - HIGHLIGHT_WIDTH - 1;
1861         coords[5] = ty + HIGHLIGHT_WIDTH;
1862         draw_polygon(dr, coords, 3, cl, cl);
1863         unclip(dr);
1864     }
1865     if (val & PIECE_TLCORNER)
1866         draw_rect(dr, tx, ty, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, ch);
1867     if (val & PIECE_BRCORNER)
1868         draw_rect(dr, tx+TILESIZE-HIGHLIGHT_WIDTH,
1869                   ty+TILESIZE-HIGHLIGHT_WIDTH,
1870                   HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, cl);
1871 }
1872
1873 static void draw_piecepart(drawing *dr, game_drawstate *ds,
1874                            int tx, int ty, unsigned long val,
1875                            int cl, int cc, int ch)
1876 {
1877     int x[6], y[6];
1878
1879     /*
1880      * Drawing the blocks is hellishly fiddly. The blocks don't
1881      * stretch to the full size of the tile; there's a border
1882      * around them of size BORDER_WIDTH. Then they have bevelled
1883      * borders of size HIGHLIGHT_WIDTH, and also rounded corners.
1884      *
1885      * I tried for some time to find a clean and clever way to
1886      * figure out what needed drawing from the corner and border
1887      * flags, but in the end the cleanest way I could find was the
1888      * following. We divide the grid square into 25 parts by
1889      * ruling four horizontal and four vertical lines across it;
1890      * those lines are at BORDER_WIDTH and BORDER_WIDTH +
1891      * HIGHLIGHT_WIDTH from the top, from the bottom, from the
1892      * left and from the right. Then we carefully consider each of
1893      * the resulting 25 sections of square, and decide separately
1894      * what needs to go in it based on the flags. In complicated
1895      * cases there can be up to five possibilities affecting any
1896      * given section (no corner or border flags, just the corner
1897      * flag, one border flag, the other border flag, both border
1898      * flags). So there's a lot of very fiddly logic here and all
1899      * I could really think to do was give it my best shot and
1900      * then test it and correct all the typos. Not fun to write,
1901      * and I'm sure it isn't fun to read either, but it seems to
1902      * work.
1903      */
1904
1905     x[0] = tx;
1906     x[1] = x[0] + BORDER_WIDTH;
1907     x[2] = x[1] + HIGHLIGHT_WIDTH;
1908     x[5] = tx + TILESIZE;
1909     x[4] = x[5] - BORDER_WIDTH;
1910     x[3] = x[4] - HIGHLIGHT_WIDTH;
1911
1912     y[0] = ty;
1913     y[1] = y[0] + BORDER_WIDTH;
1914     y[2] = y[1] + HIGHLIGHT_WIDTH;
1915     y[5] = ty + TILESIZE;
1916     y[4] = y[5] - BORDER_WIDTH;
1917     y[3] = y[4] - HIGHLIGHT_WIDTH;
1918
1919 #define RECT(p,q) x[p], y[q], x[(p)+1]-x[p], y[(q)+1]-y[q]
1920
1921     maybe_rect(dr, RECT(0,0),
1922                (val & (PIECE_TLCORNER | PIECE_TBORDER |
1923                        PIECE_LBORDER)) ? -1 : cc, -1);
1924     maybe_rect(dr, RECT(1,0),
1925                (val & PIECE_TLCORNER) ? ch : (val & PIECE_TBORDER) ? -1 :
1926                (val & PIECE_LBORDER) ? ch : cc, -1);
1927     maybe_rect(dr, RECT(2,0),
1928                (val & PIECE_TBORDER) ? -1 : cc, -1);
1929     maybe_rect(dr, RECT(3,0),
1930                (val & PIECE_TRCORNER) ? cl : (val & PIECE_TBORDER) ? -1 :
1931                (val & PIECE_RBORDER) ? cl : cc, -1);
1932     maybe_rect(dr, RECT(4,0),
1933                (val & (PIECE_TRCORNER | PIECE_TBORDER |
1934                        PIECE_RBORDER)) ? -1 : cc, -1);
1935     maybe_rect(dr, RECT(0,1),
1936                (val & PIECE_TLCORNER) ? ch : (val & PIECE_LBORDER) ? -1 :
1937                (val & PIECE_TBORDER) ? ch : cc, -1);
1938     maybe_rect(dr, RECT(1,1),
1939                (val & PIECE_TLCORNER) ? cc : -1, -1);
1940     maybe_rect(dr, RECT(1,1),
1941                (val & PIECE_TLCORNER) ? ch | TYPE_TLCIRC :
1942                !((PIECE_TBORDER | PIECE_LBORDER) &~ val) ? ch | TYPE_BRCIRC :
1943                (val & (PIECE_TBORDER | PIECE_LBORDER)) ? ch : cc, -1);
1944     maybe_rect(dr, RECT(2,1),
1945                (val & PIECE_TBORDER) ? ch : cc, -1);
1946     maybe_rect(dr, RECT(3,1),
1947                (val & PIECE_TRCORNER) ? cc : -1, -1);
1948     maybe_rect(dr, RECT(3,1),
1949                (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_TBORDER ? ch :
1950                (val & (PIECE_TBORDER | PIECE_RBORDER)) == PIECE_RBORDER ? cl :
1951                !((PIECE_TBORDER|PIECE_RBORDER) &~ val) ? cl | TYPE_BLCIRC :
1952                (val & PIECE_TRCORNER) ? cl | TYPE_TRCIRC :
1953                cc, ch);
1954     maybe_rect(dr, RECT(4,1),
1955                (val & PIECE_TRCORNER) ? ch : (val & PIECE_RBORDER) ? -1 :
1956                (val & PIECE_TBORDER) ? ch : cc, -1);
1957     maybe_rect(dr, RECT(0,2),
1958                (val & PIECE_LBORDER) ? -1 : cc, -1);
1959     maybe_rect(dr, RECT(1,2),
1960                (val & PIECE_LBORDER) ? ch : cc, -1);
1961     maybe_rect(dr, RECT(2,2),
1962                cc, -1);
1963     maybe_rect(dr, RECT(3,2),
1964                (val & PIECE_RBORDER) ? cl : cc, -1);
1965     maybe_rect(dr, RECT(4,2),
1966                (val & PIECE_RBORDER) ? -1 : cc, -1);
1967     maybe_rect(dr, RECT(0,3),
1968                (val & PIECE_BLCORNER) ? cl : (val & PIECE_LBORDER) ? -1 :
1969                (val & PIECE_BBORDER) ? cl : cc, -1);
1970     maybe_rect(dr, RECT(1,3),
1971                (val & PIECE_BLCORNER) ? cc : -1, -1);
1972     maybe_rect(dr, RECT(1,3),
1973                (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_BBORDER ? cl :
1974                (val & (PIECE_BBORDER | PIECE_LBORDER)) == PIECE_LBORDER ? ch :
1975                !((PIECE_BBORDER|PIECE_LBORDER) &~ val) ? ch | TYPE_TRCIRC :
1976                (val & PIECE_BLCORNER) ? ch | TYPE_BLCIRC :
1977                cc, cl);
1978     maybe_rect(dr, RECT(2,3),
1979                (val & PIECE_BBORDER) ? cl : cc, -1);
1980     maybe_rect(dr, RECT(3,3),
1981                (val & PIECE_BRCORNER) ? cc : -1, -1);
1982     maybe_rect(dr, RECT(3,3),
1983                (val & PIECE_BRCORNER) ? cl | TYPE_BRCIRC :
1984                !((PIECE_BBORDER | PIECE_RBORDER) &~ val) ? cl | TYPE_TLCIRC :
1985                (val & (PIECE_BBORDER | PIECE_RBORDER)) ? cl : cc, -1);
1986     maybe_rect(dr, RECT(4,3),
1987                (val & PIECE_BRCORNER) ? cl : (val & PIECE_RBORDER) ? -1 :
1988                (val & PIECE_BBORDER) ? cl : cc, -1);
1989     maybe_rect(dr, RECT(0,4),
1990                (val & (PIECE_BLCORNER | PIECE_BBORDER |
1991                        PIECE_LBORDER)) ? -1 : cc, -1);
1992     maybe_rect(dr, RECT(1,4),
1993                (val & PIECE_BLCORNER) ? ch : (val & PIECE_BBORDER) ? -1 :
1994                (val & PIECE_LBORDER) ? ch : cc, -1);
1995     maybe_rect(dr, RECT(2,4),
1996                (val & PIECE_BBORDER) ? -1 : cc, -1);
1997     maybe_rect(dr, RECT(3,4),
1998                (val & PIECE_BRCORNER) ? cl : (val & PIECE_BBORDER) ? -1 :
1999                (val & PIECE_RBORDER) ? cl : cc, -1);
2000     maybe_rect(dr, RECT(4,4),
2001                (val & (PIECE_BRCORNER | PIECE_BBORDER |
2002                        PIECE_RBORDER)) ? -1 : cc, -1);
2003
2004 #undef RECT
2005 }
2006
2007 static void draw_tile(drawing *dr, game_drawstate *ds,
2008                       int x, int y, unsigned long val)
2009 {
2010     int tx = COORD(x), ty = COORD(y);
2011     int cc, ch, cl;
2012
2013     /*
2014      * Draw the tile background.
2015      */
2016     if (val & BG_TARGET)
2017         cc = COL_TARGET;
2018     else
2019         cc = COL_BACKGROUND;
2020     ch = cc+1;
2021     cl = cc+2;
2022     if (val & FLASH_LOW)
2023         cc = cl;
2024     else if (val & FLASH_HIGH)
2025         cc = ch;
2026
2027     draw_rect(dr, tx, ty, TILESIZE, TILESIZE, cc);
2028     if (val & BG_FORCEFIELD) {
2029         /*
2030          * Cattle-grid effect to indicate that nothing but the
2031          * main block can slide over this square.
2032          */
2033         int n = 3 * (TILESIZE / (3*HIGHLIGHT_WIDTH));
2034         int i;
2035
2036         for (i = 1; i < n; i += 3) {
2037             draw_rect(dr, tx,ty+(TILESIZE*i/n), TILESIZE,HIGHLIGHT_WIDTH, cl);
2038             draw_rect(dr, tx+(TILESIZE*i/n),ty, HIGHLIGHT_WIDTH,TILESIZE, cl);
2039         }
2040     }
2041
2042     /*
2043      * Draw the tile midground: a shadow of a block, for
2044      * displaying partial solutions.
2045      */
2046     if (val & FG_SHADOW) {
2047         draw_piecepart(dr, ds, tx, ty, (val >> FG_SHADOWSH) & PIECE_MASK,
2048                        cl, cl, cl);
2049     }
2050
2051     /*
2052      * Draw the tile foreground, i.e. some section of a block or
2053      * wall.
2054      */
2055     if (val & FG_WALL) {
2056         cc = COL_BACKGROUND;
2057         ch = cc+1;
2058         cl = cc+2;
2059         if (val & FLASH_LOW)
2060             cc = cl;
2061         else if (val & FLASH_HIGH)
2062             cc = ch;
2063
2064         draw_wallpart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2065                       cl, cc, ch);
2066     } else if (val & (FG_MAIN | FG_NORMAL)) {
2067         if (val & FG_DRAGGING)
2068             cc = (val & FG_MAIN ? COL_MAIN_DRAGGING : COL_DRAGGING);
2069         else
2070             cc = (val & FG_MAIN ? COL_MAIN : COL_BACKGROUND);
2071         ch = cc+1;
2072         cl = cc+2;
2073
2074         if (val & FLASH_LOW)
2075             cc = cl;
2076         else if (val & (FLASH_HIGH | FG_SOLVEPIECE))
2077             cc = ch;
2078
2079         draw_piecepart(dr, ds, tx, ty, (val >> FG_MAINPIECESH) & PIECE_MASK,
2080                        cl, cc, ch);
2081     }
2082
2083     draw_update(dr, tx, ty, TILESIZE, TILESIZE);
2084 }
2085
2086 static unsigned long find_piecepart(int w, int h, int *dsf, int x, int y)
2087 {
2088     int i = y*w+x;
2089     int canon = dsf_canonify(dsf, i);
2090     unsigned long val = 0;
2091
2092     if (x == 0 || canon != dsf_canonify(dsf, i-1))
2093         val |= PIECE_LBORDER;
2094     if (y== 0 || canon != dsf_canonify(dsf, i-w))
2095         val |= PIECE_TBORDER;
2096     if (x == w-1 || canon != dsf_canonify(dsf, i+1))
2097         val |= PIECE_RBORDER;
2098     if (y == h-1 || canon != dsf_canonify(dsf, i+w))
2099         val |= PIECE_BBORDER;
2100     if (!(val & (PIECE_TBORDER | PIECE_LBORDER)) &&
2101         canon != dsf_canonify(dsf, i-1-w))
2102         val |= PIECE_TLCORNER;
2103     if (!(val & (PIECE_TBORDER | PIECE_RBORDER)) &&
2104         canon != dsf_canonify(dsf, i+1-w))
2105         val |= PIECE_TRCORNER;
2106     if (!(val & (PIECE_BBORDER | PIECE_LBORDER)) &&
2107         canon != dsf_canonify(dsf, i-1+w))
2108         val |= PIECE_BLCORNER;
2109     if (!(val & (PIECE_BBORDER | PIECE_RBORDER)) &&
2110         canon != dsf_canonify(dsf, i+1+w))
2111         val |= PIECE_BRCORNER;
2112     return val;
2113 }
2114
2115 static void game_redraw(drawing *dr, game_drawstate *ds,
2116                         const game_state *oldstate, const game_state *state,
2117                         int dir, const game_ui *ui,
2118                         float animtime, float flashtime)
2119 {
2120     int w = state->w, h = state->h, wh = w*h;
2121     unsigned char *board;
2122     int *dsf;
2123     int x, y, mainanchor, mainpos, dragpos, solvepos, solvesrc, solvedst;
2124
2125     if (!ds->started) {
2126         /*
2127          * The initial contents of the window are not guaranteed
2128          * and can vary with front ends. To be on the safe side,
2129          * all games should start by drawing a big
2130          * background-colour rectangle covering the whole window.
2131          */
2132         draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND);
2133         ds->started = TRUE;
2134     }
2135
2136     /*
2137      * Construct the board we'll be displaying (which may be
2138      * different from the one in state if ui describes a drag in
2139      * progress).
2140      */
2141     board = snewn(wh, unsigned char);
2142     memcpy(board, state->board, wh);
2143     if (ui->dragging) {
2144         int mpret = move_piece(w, h, state->board, board,
2145                                state->imm->forcefield,
2146                                ui->drag_anchor, ui->drag_currpos);
2147         assert(mpret);
2148     }
2149
2150     if (state->soln) {
2151         solvesrc = state->soln->moves[state->soln_index*2];
2152         solvedst = state->soln->moves[state->soln_index*2+1];
2153         if (solvesrc == state->lastmoved_pos)
2154             solvesrc = state->lastmoved;
2155         if (solvesrc == ui->drag_anchor)
2156             solvesrc = ui->drag_currpos;
2157     } else
2158         solvesrc = solvedst = -1;
2159
2160     /*
2161      * Build a dsf out of that board, so we can conveniently tell
2162      * which edges are connected and which aren't.
2163      */
2164     dsf = snew_dsf(wh);
2165     mainanchor = -1;
2166     for (y = 0; y < h; y++)
2167         for (x = 0; x < w; x++) {
2168             int i = y*w+x;
2169
2170             if (ISDIST(board[i]))
2171                 dsf_merge(dsf, i, i - board[i]);
2172             if (board[i] == MAINANCHOR)
2173                 mainanchor = i;
2174             if (board[i] == WALL) {
2175                 if (x > 0 && board[i-1] == WALL)
2176                     dsf_merge(dsf, i, i-1);
2177                 if (y > 0 && board[i-w] == WALL)
2178                     dsf_merge(dsf, i, i-w);
2179             }
2180         }
2181     assert(mainanchor >= 0);
2182     mainpos = dsf_canonify(dsf, mainanchor);
2183     dragpos = ui->drag_currpos > 0 ? dsf_canonify(dsf, ui->drag_currpos) : -1;
2184     solvepos = solvesrc >= 0 ? dsf_canonify(dsf, solvesrc) : -1;
2185
2186     /*
2187      * Now we can construct the data about what we want to draw.
2188      */
2189     for (y = 0; y < h; y++)
2190         for (x = 0; x < w; x++) {
2191             int i = y*w+x;
2192             int j;
2193             unsigned long val;
2194             int canon;
2195
2196             /*
2197              * See if this square is part of the target area.
2198              */
2199             j = i + mainanchor - (state->ty * w + state->tx);
2200             while (j >= 0 && j < wh && ISDIST(board[j]))
2201                 j -= board[j];
2202             if (j == mainanchor)
2203                 val = BG_TARGET;
2204             else
2205                 val = BG_NORMAL;
2206
2207             if (state->imm->forcefield[i])
2208                 val |= BG_FORCEFIELD;
2209
2210             if (flashtime > 0) {
2211                 int flashtype = (int)(flashtime / FLASH_INTERVAL) & 1;
2212                 val |= (flashtype ? FLASH_LOW : FLASH_HIGH);
2213             }
2214
2215             if (board[i] != EMPTY) {
2216                 canon = dsf_canonify(dsf, i);
2217
2218                 if (board[i] == WALL)
2219                     val |= FG_WALL;
2220                 else if (canon == mainpos)
2221                     val |= FG_MAIN;
2222                 else
2223                     val |= FG_NORMAL;
2224                 if (canon == dragpos)
2225                     val |= FG_DRAGGING;
2226                 if (canon == solvepos)
2227                     val |= FG_SOLVEPIECE;
2228
2229                 /*
2230                  * Now look around to see if other squares
2231                  * belonging to the same block are adjacent to us.
2232                  */
2233                 val |= find_piecepart(w, h, dsf, x, y) << FG_MAINPIECESH;
2234             }
2235
2236             /*
2237              * If we're in the middle of showing a solution,
2238              * display a shadow piece for the target of the
2239              * current move.
2240              */
2241             if (solvepos >= 0) {
2242                 int si = i - solvedst + solvesrc;
2243                 if (si >= 0 && si < wh && dsf_canonify(dsf, si) == solvepos) {
2244                     val |= find_piecepart(w, h, dsf,
2245                                           si % w, si / w) << FG_SHADOWSH;
2246                     val |= FG_SHADOW;
2247                 }
2248             }
2249
2250             if (val != ds->grid[i]) {
2251                 draw_tile(dr, ds, x, y, val);
2252                 ds->grid[i] = val;
2253             }
2254         }
2255
2256     /*
2257      * Update the status bar.
2258      */
2259     {
2260         char statusbuf[256];
2261
2262         sprintf(statusbuf, "%sMoves: %d",
2263                 (state->completed >= 0 ?
2264                  (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
2265                  (state->cheated ? "Auto-solver used. " : "")),
2266                 (state->completed >= 0 ? state->completed : state->movecount));
2267         if (state->minmoves >= 0)
2268             sprintf(statusbuf+strlen(statusbuf), " (min %d)",
2269                     state->minmoves);
2270
2271         status_bar(dr, statusbuf);
2272     }
2273
2274     sfree(dsf);
2275     sfree(board);
2276 }
2277
2278 static float game_anim_length(const game_state *oldstate,
2279                               const game_state *newstate, int dir, game_ui *ui)
2280 {
2281     return 0.0F;
2282 }
2283
2284 static float game_flash_length(const game_state *oldstate,
2285                                const game_state *newstate, int dir, game_ui *ui)
2286 {
2287     if (oldstate->completed < 0 && newstate->completed >= 0)
2288         return FLASH_TIME;
2289
2290     return 0.0F;
2291 }
2292
2293 static int game_status(const game_state *state)
2294 {
2295     return state->completed ? +1 : 0;
2296 }
2297
2298 static int game_timing_state(const game_state *state, game_ui *ui)
2299 {
2300     return TRUE;
2301 }
2302
2303 static void game_print_size(const game_params *params, float *x, float *y)
2304 {
2305 }
2306
2307 static void game_print(drawing *dr, const game_state *state, int tilesize)
2308 {
2309 }
2310
2311 #ifdef COMBINED
2312 #define thegame slide
2313 #endif
2314
2315 const struct game thegame = {
2316     "Slide", NULL, NULL,
2317     default_params,
2318     game_fetch_preset, NULL,
2319     decode_params,
2320     encode_params,
2321     free_params,
2322     dup_params,
2323     TRUE, game_configure, custom_params,
2324     validate_params,
2325     new_game_desc,
2326     validate_desc,
2327     new_game,
2328     dup_game,
2329     free_game,
2330     TRUE, solve_game,
2331     TRUE, game_can_format_as_text_now, game_text_format,
2332     new_ui,
2333     free_ui,
2334     encode_ui,
2335     decode_ui,
2336     game_changed_state,
2337     interpret_move,
2338     execute_move,
2339     PREFERRED_TILESIZE, game_compute_size, game_set_size,
2340     game_colours,
2341     game_new_drawstate,
2342     game_free_drawstate,
2343     game_redraw,
2344     game_anim_length,
2345     game_flash_length,
2346     game_status,
2347     FALSE, FALSE, game_print_size, game_print,
2348     TRUE,                              /* wants_statusbar */
2349     FALSE, game_timing_state,
2350     0,                                 /* flags */
2351 };
2352
2353 #ifdef STANDALONE_SOLVER
2354
2355 #include <stdarg.h>
2356
2357 int main(int argc, char **argv)
2358 {
2359     game_params *p;
2360     game_state *s;
2361     char *id = NULL, *desc, *err;
2362     int count = FALSE;
2363     int ret;
2364     int *moves;
2365
2366     while (--argc > 0) {
2367         char *p = *++argv;
2368         /*
2369         if (!strcmp(p, "-v")) {
2370             verbose = TRUE;
2371         } else
2372         */
2373         if (!strcmp(p, "-c")) {
2374             count = TRUE;
2375         } else if (*p == '-') {
2376             fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2377             return 1;
2378         } else {
2379             id = p;
2380         }
2381     }
2382
2383     if (!id) {
2384         fprintf(stderr, "usage: %s [-c | -v] <game_id>\n", argv[0]);
2385         return 1;
2386     }
2387
2388     desc = strchr(id, ':');
2389     if (!desc) {
2390         fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
2391         return 1;
2392     }
2393     *desc++ = '\0';
2394
2395     p = default_params();
2396     decode_params(p, id);
2397     err = validate_desc(p, desc);
2398     if (err) {
2399         fprintf(stderr, "%s: %s\n", argv[0], err);
2400         return 1;
2401     }
2402     s = new_game(NULL, p, desc);
2403
2404     ret = solve_board(s->w, s->h, s->board, s->imm->forcefield,
2405                       s->tx, s->ty, -1, &moves);
2406     if (ret < 0) {
2407         printf("No solution found\n");
2408     } else {
2409         int index = 0;
2410         if (count) {
2411             printf("%d moves required\n", ret);
2412             return 0;
2413         }
2414         while (1) {
2415             int moveret;
2416             char *text = board_text_format(s->w, s->h, s->board,
2417                                            s->imm->forcefield);
2418             game_state *s2;
2419
2420             printf("position %d:\n%s", index, text);
2421
2422             if (index >= ret)
2423                 break;
2424
2425             s2 = dup_game(s);
2426             moveret = move_piece(s->w, s->h, s->board,
2427                                  s2->board, s->imm->forcefield,
2428                                  moves[index*2], moves[index*2+1]);
2429             assert(moveret);
2430
2431             free_game(s);
2432             s = s2;
2433             index++;
2434         }
2435     }
2436
2437     return 0;
2438 }
2439
2440 #endif