chiark / gitweb /
Richard B's utterly evil `netslide': cross between Net and Sixteen.
[sgt-puzzles.git] / netslide.c
1 /*
2  * netslide.c: cross between Net and Sixteen, courtesy of Richard
3  * Boulton.
4  */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <ctype.h>
11 #include <math.h>
12
13 #include "puzzles.h"
14 #include "tree234.h"
15
16 const char *const game_name = "Netslide";
17 const int game_can_configure = TRUE;
18
19 #define PI 3.141592653589793238462643383279502884197169399
20
21 #define MATMUL(xr,yr,m,x,y) do { \
22     float rx, ry, xx = (x), yy = (y), *mat = (m); \
23     rx = mat[0] * xx + mat[2] * yy; \
24     ry = mat[1] * xx + mat[3] * yy; \
25     (xr) = rx; (yr) = ry; \
26 } while (0)
27
28 /* Direction and other bitfields */
29 #define R 0x01
30 #define U 0x02
31 #define L 0x04
32 #define D 0x08
33 #define FLASHING 0x10
34 #define ACTIVE 0x20
35 /* Corner flags go in the barriers array */
36 #define RU 0x10
37 #define UL 0x20
38 #define LD 0x40
39 #define DR 0x80
40
41 /* Get tile at given coordinate */
42 #define T(state, x, y) ( (y) * (state)->width + (x) )
43
44 /* Rotations: Anticlockwise, Clockwise, Flip, general rotate */
45 #define A(x) ( (((x) & 0x07) << 1) | (((x) & 0x08) >> 3) )
46 #define C(x) ( (((x) & 0x0E) >> 1) | (((x) & 0x01) << 3) )
47 #define F(x) ( (((x) & 0x0C) >> 2) | (((x) & 0x03) << 2) )
48 #define ROT(x, n) ( ((n)&3) == 0 ? (x) : \
49                     ((n)&3) == 1 ? A(x) : \
50                     ((n)&3) == 2 ? F(x) : C(x) )
51
52 /* X and Y displacements */
53 #define X(x) ( (x) == R ? +1 : (x) == L ? -1 : 0 )
54 #define Y(x) ( (x) == D ? +1 : (x) == U ? -1 : 0 )
55
56 /* Bit count */
57 #define COUNT(x) ( (((x) & 0x08) >> 3) + (((x) & 0x04) >> 2) + \
58                    (((x) & 0x02) >> 1) + ((x) & 0x01) )
59
60 #define TILE_SIZE 48
61 #define BORDER TILE_SIZE
62 #define TILE_BORDER 1
63 #define WINDOW_OFFSET 0
64
65 #define ANIM_TIME 0.13F
66 #define FLASH_FRAME 0.07F
67
68 enum {
69     COL_BACKGROUND,
70     COL_FLASHING,
71     COL_BORDER,
72     COL_WIRE,
73     COL_ENDPOINT,
74     COL_POWERED,
75     COL_BARRIER,
76     COL_LOWLIGHT,
77     COL_TEXT,
78     NCOLOURS
79 };
80
81 struct game_params {
82     int width;
83     int height;
84     int wrapping;
85     float barrier_probability;
86 };
87
88 struct game_state {
89     int width, height, cx, cy, wrapping, completed;
90     int move_count;
91
92     /* position (row or col number, starting at 0) of last move. */
93     int last_move_row, last_move_col;
94
95     /* direction of last move: +1 or -1 */
96     int last_move_dir;
97
98     unsigned char *tiles;
99     unsigned char *barriers;
100 };
101
102 #define OFFSET(x2,y2,x1,y1,dir,state) \
103     ( (x2) = ((x1) + (state)->width + X((dir))) % (state)->width, \
104       (y2) = ((y1) + (state)->height + Y((dir))) % (state)->height)
105
106 #define index(state, a, x, y) ( a[(y) * (state)->width + (x)] )
107 #define tile(state, x, y)     index(state, (state)->tiles, x, y)
108 #define barrier(state, x, y)  index(state, (state)->barriers, x, y)
109
110 struct xyd {
111     int x, y, direction;
112 };
113
114 static int xyd_cmp(void *av, void *bv) {
115     struct xyd *a = (struct xyd *)av;
116     struct xyd *b = (struct xyd *)bv;
117     if (a->x < b->x)
118         return -1;
119     if (a->x > b->x)
120         return +1;
121     if (a->y < b->y)
122         return -1;
123     if (a->y > b->y)
124         return +1;
125     if (a->direction < b->direction)
126         return -1;
127     if (a->direction > b->direction)
128         return +1;
129     return 0;
130 };
131
132 static struct xyd *new_xyd(int x, int y, int direction)
133 {
134     struct xyd *xyd = snew(struct xyd);
135     xyd->x = x;
136     xyd->y = y;
137     xyd->direction = direction;
138     return xyd;
139 }
140
141 void slide_col(game_state *state, int dir, int col);
142 void slide_row(game_state *state, int dir, int row);
143
144 /* ----------------------------------------------------------------------
145  * Manage game parameters.
146  */
147 game_params *default_params(void)
148 {
149     game_params *ret = snew(game_params);
150
151     ret->width = 3;
152     ret->height = 3;
153     ret->wrapping = FALSE;
154     ret->barrier_probability = 1.0;
155
156     return ret;
157 }
158
159 int game_fetch_preset(int i, char **name, game_params **params)
160 {
161     game_params *ret;
162     char str[80];
163     static const struct { int x, y, wrap, bprob; const char* desc; } values[] = {
164         {3, 3, FALSE, 1.0, " easy"},
165         {3, 3, FALSE, 0.0, " medium"},
166         {3, 3, TRUE,  0.0, " hard"},
167         {4, 4, FALSE, 1.0, " easy"},
168         {4, 4, FALSE, 0.0, " medium"},
169         {4, 4, TRUE,  0.0, " hard"},
170         {5, 5, FALSE, 1.0, " easy"},
171         {5, 5, FALSE, 0.0, " medium"},
172         {5, 5, TRUE,  0.0, " hard"},
173     };
174
175     if (i < 0 || i >= lenof(values))
176         return FALSE;
177
178     ret = snew(game_params);
179     ret->width = values[i].x;
180     ret->height = values[i].y;
181     ret->wrapping = values[i].wrap;
182     ret->barrier_probability = values[i].bprob;
183
184     sprintf(str, "%dx%d%s", ret->width, ret->height,
185             values[i].desc);
186
187     *name = dupstr(str);
188     *params = ret;
189     return TRUE;
190 }
191
192 void free_params(game_params *params)
193 {
194     sfree(params);
195 }
196
197 game_params *dup_params(game_params *params)
198 {
199     game_params *ret = snew(game_params);
200     *ret = *params;                    /* structure copy */
201     return ret;
202 }
203
204 game_params *decode_params(char const *string)
205 {
206     game_params *ret = default_params();
207     char const *p = string;
208
209     ret->wrapping = FALSE;
210     ret->barrier_probability = 0.0;
211
212     ret->width = atoi(p);
213     while (*p && isdigit(*p)) p++;
214     if (*p == 'x') {
215         p++;
216         ret->height = atoi(p);
217         while (*p && isdigit(*p)) p++;
218         if ( (ret->wrapping = (*p == 'w')) != 0 )
219             p++;
220         if (*p == 'b')
221             ret->barrier_probability = atof(p+1);
222     } else {
223         ret->height = ret->width;
224     }
225
226     return ret;
227 }
228
229 char *encode_params(game_params *params)
230 {
231     char ret[400];
232     int len;
233
234     len = sprintf(ret, "%dx%d", params->width, params->height);
235     if (params->wrapping)
236         ret[len++] = 'w';
237     if (params->barrier_probability)
238         len += sprintf(ret+len, "b%g", params->barrier_probability);
239     assert(len < lenof(ret));
240     ret[len] = '\0';
241
242     return dupstr(ret);
243 }
244
245 config_item *game_configure(game_params *params)
246 {
247     config_item *ret;
248     char buf[80];
249
250     ret = snewn(5, config_item);
251
252     ret[0].name = "Width";
253     ret[0].type = C_STRING;
254     sprintf(buf, "%d", params->width);
255     ret[0].sval = dupstr(buf);
256     ret[0].ival = 0;
257
258     ret[1].name = "Height";
259     ret[1].type = C_STRING;
260     sprintf(buf, "%d", params->height);
261     ret[1].sval = dupstr(buf);
262     ret[1].ival = 0;
263
264     ret[2].name = "Walls wrap around";
265     ret[2].type = C_BOOLEAN;
266     ret[2].sval = NULL;
267     ret[2].ival = params->wrapping;
268
269     ret[3].name = "Barrier probability";
270     ret[3].type = C_STRING;
271     sprintf(buf, "%g", params->barrier_probability);
272     ret[3].sval = dupstr(buf);
273     ret[3].ival = 0;
274
275     ret[4].name = NULL;
276     ret[4].type = C_END;
277     ret[4].sval = NULL;
278     ret[4].ival = 0;
279
280     return ret;
281 }
282
283 game_params *custom_params(config_item *cfg)
284 {
285     game_params *ret = snew(game_params);
286
287     ret->width = atoi(cfg[0].sval);
288     ret->height = atoi(cfg[1].sval);
289     ret->wrapping = cfg[2].ival;
290     ret->barrier_probability = (float)atof(cfg[3].sval);
291
292     return ret;
293 }
294
295 char *validate_params(game_params *params)
296 {
297     if (params->width <= 1 && params->height <= 1)
298         return "Width and height must both be greater than one";
299     if (params->width <= 1)
300         return "Width must be greater than one";
301     if (params->height <= 1)
302         return "Height must be greater than one";
303     if (params->barrier_probability < 0)
304         return "Barrier probability may not be negative";
305     if (params->barrier_probability > 1)
306         return "Barrier probability may not be greater than 1";
307     return NULL;
308 }
309
310 /* ----------------------------------------------------------------------
311  * Randomly select a new game seed.
312  */
313
314 char *new_game_seed(game_params *params, random_state *rs)
315 {
316     /*
317      * The full description of a Net game is far too large to
318      * encode directly in the seed, so by default we'll have to go
319      * for the simple approach of providing a random-number seed.
320      * 
321      * (This does not restrict me from _later on_ inventing a seed
322      * string syntax which can never be generated by this code -
323      * for example, strings beginning with a letter - allowing me
324      * to type in a precise game, and have new_game detect it and
325      * understand it and do something completely different.)
326      */
327     char buf[40];
328     sprintf(buf, "%lu", random_bits(rs, 32));
329     return dupstr(buf);
330 }
331
332 char *validate_seed(game_params *params, char *seed)
333 {
334     /*
335      * Since any string at all will suffice to seed the RNG, there
336      * is no validation required.
337      */
338     return NULL;
339 }
340
341 /* ----------------------------------------------------------------------
342  * Construct an initial game state, given a seed and parameters.
343  */
344
345 game_state *new_game(game_params *params, char *seed)
346 {
347     random_state *rs;
348     game_state *state;
349     tree234 *possibilities, *barriers;
350     int w, h, x, y, nbarriers;
351
352     assert(params->width > 0 && params->height > 0);
353     assert(params->width > 1 || params->height > 1);
354
355     /*
356      * Create a blank game state.
357      */
358     state = snew(game_state);
359     w = state->width = params->width;
360     h = state->height = params->height;
361     state->cx = state->width / 2;
362     state->cy = state->height / 2;
363     state->wrapping = params->wrapping;
364     state->completed = 0;
365     state->move_count = 0;
366     state->last_move_row = -1;
367     state->last_move_col = -1;
368     state->last_move_dir = 0;
369     state->tiles = snewn(state->width * state->height, unsigned char);
370     memset(state->tiles, 0, state->width * state->height);
371     state->barriers = snewn(state->width * state->height, unsigned char);
372     memset(state->barriers, 0, state->width * state->height);
373
374     /*
375      * Set up border barriers if this is a non-wrapping game.
376      */
377     if (!state->wrapping) {
378         for (x = 0; x < state->width; x++) {
379             barrier(state, x, 0) |= U;
380             barrier(state, x, state->height-1) |= D;
381         }
382         for (y = 0; y < state->height; y++) {
383             barrier(state, 0, y) |= L;
384             barrier(state, state->width-1, y) |= R;
385         }
386     }
387
388     /*
389      * Seed the internal random number generator.
390      */
391     rs = random_init(seed, strlen(seed));
392
393     /*
394      * Construct the unshuffled grid.
395      * 
396      * To do this, we simply start at the centre point, repeatedly
397      * choose a random possibility out of the available ways to
398      * extend a used square into an unused one, and do it. After
399      * extending the third line out of a square, we remove the
400      * fourth from the possibilities list to avoid any full-cross
401      * squares (which would make the game too easy because they
402      * only have one orientation).
403      * 
404      * The slightly worrying thing is the avoidance of full-cross
405      * squares. Can this cause our unsophisticated construction
406      * algorithm to paint itself into a corner, by getting into a
407      * situation where there are some unreached squares and the
408      * only way to reach any of them is to extend a T-piece into a
409      * full cross?
410      * 
411      * Answer: no it can't, and here's a proof.
412      * 
413      * Any contiguous group of such unreachable squares must be
414      * surrounded on _all_ sides by T-pieces pointing away from the
415      * group. (If not, then there is a square which can be extended
416      * into one of the `unreachable' ones, and so it wasn't
417      * unreachable after all.) In particular, this implies that
418      * each contiguous group of unreachable squares must be
419      * rectangular in shape (any deviation from that yields a
420      * non-T-piece next to an `unreachable' square).
421      * 
422      * So we have a rectangle of unreachable squares, with T-pieces
423      * forming a solid border around the rectangle. The corners of
424      * that border must be connected (since every tile connects all
425      * the lines arriving in it), and therefore the border must
426      * form a closed loop around the rectangle.
427      * 
428      * But this can't have happened in the first place, since we
429      * _know_ we've avoided creating closed loops! Hence, no such
430      * situation can ever arise, and the naive grid construction
431      * algorithm will guaranteeably result in a complete grid
432      * containing no unreached squares, no full crosses _and_ no
433      * closed loops. []
434      */
435     possibilities = newtree234(xyd_cmp);
436
437     if (state->cx+1 < state->width)
438         add234(possibilities, new_xyd(state->cx, state->cy, R));
439     if (state->cy-1 >= 0)
440         add234(possibilities, new_xyd(state->cx, state->cy, U));
441     if (state->cx-1 >= 0)
442         add234(possibilities, new_xyd(state->cx, state->cy, L));
443     if (state->cy+1 < state->height)
444         add234(possibilities, new_xyd(state->cx, state->cy, D));
445
446     while (count234(possibilities) > 0) {
447         int i;
448         struct xyd *xyd;
449         int x1, y1, d1, x2, y2, d2, d;
450
451         /*
452          * Extract a randomly chosen possibility from the list.
453          */
454         i = random_upto(rs, count234(possibilities));
455         xyd = delpos234(possibilities, i);
456         x1 = xyd->x;
457         y1 = xyd->y;
458         d1 = xyd->direction;
459         sfree(xyd);
460
461         OFFSET(x2, y2, x1, y1, d1, state);
462         d2 = F(d1);
463 #ifdef DEBUG
464         printf("picked (%d,%d,%c) <-> (%d,%d,%c)\n",
465                x1, y1, "0RU3L567D9abcdef"[d1], x2, y2, "0RU3L567D9abcdef"[d2]);
466 #endif
467
468         /*
469          * Make the connection. (We should be moving to an as yet
470          * unused tile.)
471          */
472         tile(state, x1, y1) |= d1;
473         assert(tile(state, x2, y2) == 0);
474         tile(state, x2, y2) |= d2;
475
476         /*
477          * If we have created a T-piece, remove its last
478          * possibility.
479          */
480         if (COUNT(tile(state, x1, y1)) == 3) {
481             struct xyd xyd1, *xydp;
482
483             xyd1.x = x1;
484             xyd1.y = y1;
485             xyd1.direction = 0x0F ^ tile(state, x1, y1);
486
487             xydp = find234(possibilities, &xyd1, NULL);
488
489             if (xydp) {
490 #ifdef DEBUG
491                 printf("T-piece; removing (%d,%d,%c)\n",
492                        xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
493 #endif
494                 del234(possibilities, xydp);
495                 sfree(xydp);
496             }
497         }
498
499         /*
500          * Remove all other possibilities that were pointing at the
501          * tile we've just moved into.
502          */
503         for (d = 1; d < 0x10; d <<= 1) {
504             int x3, y3, d3;
505             struct xyd xyd1, *xydp;
506
507             OFFSET(x3, y3, x2, y2, d, state);
508             d3 = F(d);
509
510             xyd1.x = x3;
511             xyd1.y = y3;
512             xyd1.direction = d3;
513
514             xydp = find234(possibilities, &xyd1, NULL);
515
516             if (xydp) {
517 #ifdef DEBUG
518                 printf("Loop avoidance; removing (%d,%d,%c)\n",
519                        xydp->x, xydp->y, "0RU3L567D9abcdef"[xydp->direction]);
520 #endif
521                 del234(possibilities, xydp);
522                 sfree(xydp);
523             }
524         }
525
526         /*
527          * Add new possibilities to the list for moving _out_ of
528          * the tile we have just moved into.
529          */
530         for (d = 1; d < 0x10; d <<= 1) {
531             int x3, y3;
532
533             if (d == d2)
534                 continue;              /* we've got this one already */
535
536             if (!state->wrapping) {
537                 if (d == U && y2 == 0)
538                     continue;
539                 if (d == D && y2 == state->height-1)
540                     continue;
541                 if (d == L && x2 == 0)
542                     continue;
543                 if (d == R && x2 == state->width-1)
544                     continue;
545             }
546
547             OFFSET(x3, y3, x2, y2, d, state);
548
549             if (tile(state, x3, y3))
550                 continue;              /* this would create a loop */
551
552 #ifdef DEBUG
553             printf("New frontier; adding (%d,%d,%c)\n",
554                    x2, y2, "0RU3L567D9abcdef"[d]);
555 #endif
556             add234(possibilities, new_xyd(x2, y2, d));
557         }
558     }
559     /* Having done that, we should have no possibilities remaining. */
560     assert(count234(possibilities) == 0);
561     freetree234(possibilities);
562
563     /*
564      * Now compute a list of the possible barrier locations.
565      */
566     barriers = newtree234(xyd_cmp);
567     for (y = 0; y < state->height; y++) {
568         for (x = 0; x < state->width; x++) {
569
570             if (!(tile(state, x, y) & R) &&
571                 (state->wrapping || x < state->width-1))
572                 add234(barriers, new_xyd(x, y, R));
573             if (!(tile(state, x, y) & D) &&
574                 (state->wrapping || y < state->height-1))
575                 add234(barriers, new_xyd(x, y, D));
576         }
577     }
578
579     /*
580      * Now shuffle the grid.
581      * FIXME - this simply does a set of random moves to shuffle the pieces.
582      * A better way would be to number all the pieces, generate a placement
583      * for all the numbers as for "sixteen", observing parity constraints if
584      * neccessary, and then place the pieces according to their numbering.
585      * BUT - I'm not sure if this will work, since we disallow movement of
586      * the middle row and column.
587      */
588     {
589         int i;
590         int cols = state->width - 1;
591         int rows = state->height - 1;
592         for (i = 0; i < cols * rows * 2; i++) {
593             /* Choose a direction: 0,1,2,3 = up, right, down, left. */
594             int dir = random_upto(rs, 4);
595             if (dir % 2 == 0) {
596                 int col = random_upto(rs, cols);
597                 if (col >= state->cx) col += 1;
598                 slide_col(state, 1 - dir, col);
599             } else {
600                 int row = random_upto(rs, rows);
601                 if (row >= state->cy) row += 1;
602                 slide_row(state, 2 - dir, row);
603             }
604         }
605     }
606
607     /*
608      * And now choose barrier locations. (We carefully do this
609      * _after_ shuffling, so that changing the barrier rate in the
610      * params while keeping the game seed the same will give the
611      * same shuffled grid and _only_ change the barrier locations.
612      * Also the way we choose barrier locations, by repeatedly
613      * choosing one possibility from the list until we have enough,
614      * is designed to ensure that raising the barrier rate while
615      * keeping the seed the same will provide a superset of the
616      * previous barrier set - i.e. if you ask for 10 barriers, and
617      * then decide that's still too hard and ask for 20, you'll get
618      * the original 10 plus 10 more, rather than getting 20 new
619      * ones and the chance of remembering your first 10.)
620      */
621     nbarriers = (int)(params->barrier_probability * count234(barriers));
622     assert(nbarriers >= 0 && nbarriers <= count234(barriers));
623
624     while (nbarriers > 0) {
625         int i;
626         struct xyd *xyd;
627         int x1, y1, d1, x2, y2, d2;
628
629         /*
630          * Extract a randomly chosen barrier from the list.
631          */
632         i = random_upto(rs, count234(barriers));
633         xyd = delpos234(barriers, i);
634
635         assert(xyd != NULL);
636
637         x1 = xyd->x;
638         y1 = xyd->y;
639         d1 = xyd->direction;
640         sfree(xyd);
641
642         OFFSET(x2, y2, x1, y1, d1, state);
643         d2 = F(d1);
644
645         barrier(state, x1, y1) |= d1;
646         barrier(state, x2, y2) |= d2;
647
648         nbarriers--;
649     }
650
651     /*
652      * Clean up the rest of the barrier list.
653      */
654     {
655         struct xyd *xyd;
656
657         while ( (xyd = delpos234(barriers, 0)) != NULL)
658             sfree(xyd);
659
660         freetree234(barriers);
661     }
662
663     /*
664      * Set up the barrier corner flags, for drawing barriers
665      * prettily when they meet.
666      */
667     for (y = 0; y < state->height; y++) {
668         for (x = 0; x < state->width; x++) {
669             int dir;
670
671             for (dir = 1; dir < 0x10; dir <<= 1) {
672                 int dir2 = A(dir);
673                 int x1, y1, x2, y2, x3, y3;
674                 int corner = FALSE;
675
676                 if (!(barrier(state, x, y) & dir))
677                     continue;
678
679                 if (barrier(state, x, y) & dir2)
680                     corner = TRUE;
681
682                 x1 = x + X(dir), y1 = y + Y(dir);
683                 if (x1 >= 0 && x1 < state->width &&
684                     y1 >= 0 && y1 < state->height &&
685                     (barrier(state, x1, y1) & dir2))
686                     corner = TRUE;
687
688                 x2 = x + X(dir2), y2 = y + Y(dir2);
689                 if (x2 >= 0 && x2 < state->width &&
690                     y2 >= 0 && y2 < state->height &&
691                     (barrier(state, x2, y2) & dir))
692                     corner = TRUE;
693
694                 if (corner) {
695                     barrier(state, x, y) |= (dir << 4);
696                     if (x1 >= 0 && x1 < state->width &&
697                         y1 >= 0 && y1 < state->height)
698                         barrier(state, x1, y1) |= (A(dir) << 4);
699                     if (x2 >= 0 && x2 < state->width &&
700                         y2 >= 0 && y2 < state->height)
701                         barrier(state, x2, y2) |= (C(dir) << 4);
702                     x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2);
703                     if (x3 >= 0 && x3 < state->width &&
704                         y3 >= 0 && y3 < state->height)
705                         barrier(state, x3, y3) |= (F(dir) << 4);
706                 }
707             }
708         }
709     }
710
711     random_free(rs);
712
713     return state;
714 }
715
716 game_state *dup_game(game_state *state)
717 {
718     game_state *ret;
719
720     ret = snew(game_state);
721     ret->width = state->width;
722     ret->height = state->height;
723     ret->cx = state->cx;
724     ret->cy = state->cy;
725     ret->wrapping = state->wrapping;
726     ret->completed = state->completed;
727     ret->move_count = state->move_count;
728     ret->last_move_row = state->last_move_row;
729     ret->last_move_col = state->last_move_col;
730     ret->last_move_dir = state->last_move_dir;
731     ret->tiles = snewn(state->width * state->height, unsigned char);
732     memcpy(ret->tiles, state->tiles, state->width * state->height);
733     ret->barriers = snewn(state->width * state->height, unsigned char);
734     memcpy(ret->barriers, state->barriers, state->width * state->height);
735
736     return ret;
737 }
738
739 void free_game(game_state *state)
740 {
741     sfree(state->tiles);
742     sfree(state->barriers);
743     sfree(state);
744 }
745
746 /* ----------------------------------------------------------------------
747  * Utility routine.
748  */
749
750 /*
751  * Compute which squares are reachable from the centre square, as a
752  * quick visual aid to determining how close the game is to
753  * completion. This is also a simple way to tell if the game _is_
754  * completed - just call this function and see whether every square
755  * is marked active.
756  *
757  * squares in the moving_row and moving_col are always inactive - this
758  * is so that "current" doesn't appear to jump across moving lines.
759  */
760 static unsigned char *compute_active(game_state *state,
761                                      int moving_row, int moving_col)
762 {
763     unsigned char *active;
764     tree234 *todo;
765     struct xyd *xyd;
766
767     active = snewn(state->width * state->height, unsigned char);
768     memset(active, 0, state->width * state->height);
769
770     /*
771      * We only store (x,y) pairs in todo, but it's easier to reuse
772      * xyd_cmp and just store direction 0 every time.
773      */
774     todo = newtree234(xyd_cmp);
775     index(state, active, state->cx, state->cy) = ACTIVE;
776     add234(todo, new_xyd(state->cx, state->cy, 0));
777
778     while ( (xyd = delpos234(todo, 0)) != NULL) {
779         int x1, y1, d1, x2, y2, d2;
780
781         x1 = xyd->x;
782         y1 = xyd->y;
783         sfree(xyd);
784
785         for (d1 = 1; d1 < 0x10; d1 <<= 1) {
786             OFFSET(x2, y2, x1, y1, d1, state);
787             d2 = F(d1);
788
789             /*
790              * If the next tile in this direction is connected to
791              * us, and there isn't a barrier in the way, and it
792              * isn't already marked active, then mark it active and
793              * add it to the to-examine list.
794              */
795             if ((x2 != moving_col && y2 != moving_row) &&
796                 (tile(state, x1, y1) & d1) &&
797                 (tile(state, x2, y2) & d2) &&
798                 !(barrier(state, x1, y1) & d1) &&
799                 !index(state, active, x2, y2)) {
800                 index(state, active, x2, y2) = ACTIVE;
801                 add234(todo, new_xyd(x2, y2, 0));
802             }
803         }
804     }
805     /* Now we expect the todo list to have shrunk to zero size. */
806     assert(count234(todo) == 0);
807     freetree234(todo);
808
809     return active;
810 }
811
812 struct game_ui {
813     int cur_x, cur_y;
814     int cur_visible;
815 };
816
817 game_ui *new_ui(game_state *state)
818 {
819     game_ui *ui = snew(game_ui);
820     ui->cur_x = state->width / 2;
821     ui->cur_y = state->height / 2;
822     ui->cur_visible = FALSE;
823
824     return ui;
825 }
826
827 void free_ui(game_ui *ui)
828 {
829     sfree(ui);
830 }
831
832 /* ----------------------------------------------------------------------
833  * Process a move.
834  */
835
836 void slide_row(game_state *state, int dir, int row)
837 {
838     int x = dir > 0 ? -1 : state->width;
839     int tx = x + dir;
840     int n = state->width - 1;
841     unsigned char endtile = state->tiles[T(state, tx, row)];
842     do {
843         x = tx;
844         tx = (x + dir + state->width) % state->width;
845         state->tiles[T(state, x, row)] = state->tiles[T(state, tx, row)];
846     } while (--n > 0);
847     state->tiles[T(state, tx, row)] = endtile;
848 }
849
850 void slide_col(game_state *state, int dir, int col)
851 {
852     int y = dir > 0 ? -1 : state->height;
853     int ty = y + dir;
854     int n = state->height - 1;
855     unsigned char endtile = state->tiles[T(state, col, ty)];
856     do {
857         y = ty;
858         ty = (y + dir + state->height) % state->height;
859         state->tiles[T(state, col, y)] = state->tiles[T(state, col, ty)];
860     } while (--n > 0);
861     state->tiles[T(state, col, ty)] = endtile;
862 }
863
864 game_state *make_move(game_state *state, game_ui *ui, int x, int y, int button)
865 {
866     int cx, cy;
867     int n, dx, dy;
868     game_state *ret;
869
870     if (button != LEFT_BUTTON && button != RIGHT_BUTTON)
871         return NULL;
872
873     cx = (x - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
874     cy = (y - (BORDER + WINDOW_OFFSET + TILE_BORDER) + 2*TILE_SIZE) / TILE_SIZE - 2;
875
876     if (cy >= 0 && cy < state->height && cy != state->cy)
877     {
878         if (cx == -1) dx = +1;
879         else if (cx == state->width) dx = -1;
880         else return NULL;
881         n = state->width;
882         dy = 0;
883     }
884     else if (cx >= 0 && cx < state->width && cx != state->cx)
885     {
886         if (cy == -1) dy = +1;
887         else if (cy == state->height) dy = -1;
888         else return NULL;
889         n = state->height;
890         dx = 0;
891     }
892     else
893         return NULL;
894
895     /* reverse direction if right hand button is pressed */
896     if (button == RIGHT_BUTTON)
897     {
898         dx = -dx;
899         dy = -dy;
900     }
901
902     ret = dup_game(state);
903
904     if (dx == 0) slide_col(ret, dy, cx);
905     else slide_row(ret, dx, cy);
906
907     ret->move_count++;
908     ret->last_move_row = dx ? cy : -1;
909     ret->last_move_col = dx ? -1 : cx;
910     ret->last_move_dir = dx + dy;
911
912     /*
913      * See if the game has been completed.
914      */
915     if (!ret->completed) {
916         unsigned char *active = compute_active(ret, -1, -1);
917         int x1, y1;
918         int complete = TRUE;
919
920         for (x1 = 0; x1 < ret->width; x1++)
921             for (y1 = 0; y1 < ret->height; y1++)
922                 if (!index(ret, active, x1, y1)) {
923                     complete = FALSE;
924                     goto break_label;  /* break out of two loops at once */
925                 }
926         break_label:
927
928         sfree(active);
929
930         if (complete)
931             ret->completed = ret->move_count;
932     }
933
934     return ret;
935 }
936
937 /* ----------------------------------------------------------------------
938  * Routines for drawing the game position on the screen.
939  */
940
941 struct game_drawstate {
942     int started;
943     int width, height;
944     unsigned char *visible;
945 };
946
947 game_drawstate *game_new_drawstate(game_state *state)
948 {
949     game_drawstate *ds = snew(game_drawstate);
950
951     ds->started = FALSE;
952     ds->width = state->width;
953     ds->height = state->height;
954     ds->visible = snewn(state->width * state->height, unsigned char);
955     memset(ds->visible, 0xFF, state->width * state->height);
956
957     return ds;
958 }
959
960 void game_free_drawstate(game_drawstate *ds)
961 {
962     sfree(ds->visible);
963     sfree(ds);
964 }
965
966 void game_size(game_params *params, int *x, int *y)
967 {
968     *x = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->width + TILE_BORDER;
969     *y = BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * params->height + TILE_BORDER;
970 }
971
972 float *game_colours(frontend *fe, game_state *state, int *ncolours)
973 {
974     float *ret;
975
976     ret = snewn(NCOLOURS * 3, float);
977     *ncolours = NCOLOURS;
978
979     /*
980      * Basic background colour is whatever the front end thinks is
981      * a sensible default.
982      */
983     frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
984
985     /*
986      * Wires are black.
987      */
988     ret[COL_WIRE * 3 + 0] = 0.0F;
989     ret[COL_WIRE * 3 + 1] = 0.0F;
990     ret[COL_WIRE * 3 + 2] = 0.0F;
991
992     /*
993      * Powered wires and powered endpoints are cyan.
994      */
995     ret[COL_POWERED * 3 + 0] = 0.0F;
996     ret[COL_POWERED * 3 + 1] = 1.0F;
997     ret[COL_POWERED * 3 + 2] = 1.0F;
998
999     /*
1000      * Barriers are red.
1001      */
1002     ret[COL_BARRIER * 3 + 0] = 1.0F;
1003     ret[COL_BARRIER * 3 + 1] = 0.0F;
1004     ret[COL_BARRIER * 3 + 2] = 0.0F;
1005
1006     /*
1007      * Unpowered endpoints are blue.
1008      */
1009     ret[COL_ENDPOINT * 3 + 0] = 0.0F;
1010     ret[COL_ENDPOINT * 3 + 1] = 0.0F;
1011     ret[COL_ENDPOINT * 3 + 2] = 1.0F;
1012
1013     /*
1014      * Tile borders are a darker grey than the background.
1015      */
1016     ret[COL_BORDER * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1017     ret[COL_BORDER * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1018     ret[COL_BORDER * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
1019
1020     /*
1021      * Flashing tiles are a grey in between those two.
1022      */
1023     ret[COL_FLASHING * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
1024     ret[COL_FLASHING * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
1025     ret[COL_FLASHING * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
1026
1027     ret[COL_LOWLIGHT * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
1028     ret[COL_LOWLIGHT * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.8F;
1029     ret[COL_LOWLIGHT * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.8F;
1030     ret[COL_TEXT * 3 + 0] = 0.0;
1031     ret[COL_TEXT * 3 + 1] = 0.0;
1032     ret[COL_TEXT * 3 + 2] = 0.0;
1033
1034     return ret;
1035 }
1036
1037 static void draw_thick_line(frontend *fe, int x1, int y1, int x2, int y2,
1038                             int colour)
1039 {
1040     draw_line(fe, x1-1, y1, x2-1, y2, COL_WIRE);
1041     draw_line(fe, x1+1, y1, x2+1, y2, COL_WIRE);
1042     draw_line(fe, x1, y1-1, x2, y2-1, COL_WIRE);
1043     draw_line(fe, x1, y1+1, x2, y2+1, COL_WIRE);
1044     draw_line(fe, x1, y1, x2, y2, colour);
1045 }
1046
1047 static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2,
1048                              int colour)
1049 {
1050     int mx = (x1 < x2 ? x1 : x2);
1051     int my = (y1 < y2 ? y1 : y2);
1052     int dx = (x2 + x1 - 2*mx + 1);
1053     int dy = (y2 + y1 - 2*my + 1);
1054
1055     draw_rect(fe, mx, my, dx, dy, colour);
1056 }
1057
1058 static void draw_barrier_corner(frontend *fe, int x, int y, int dir, int phase)
1059 {
1060     int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1061     int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1062     int x1, y1, dx, dy, dir2;
1063
1064     dir >>= 4;
1065
1066     dir2 = A(dir);
1067     dx = X(dir) + X(dir2);
1068     dy = Y(dir) + Y(dir2);
1069     x1 = (dx > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
1070     y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
1071
1072     if (phase == 0) {
1073         draw_rect_coords(fe, bx+x1, by+y1,
1074                          bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy,
1075                          COL_WIRE);
1076         draw_rect_coords(fe, bx+x1, by+y1,
1077                          bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
1078                          COL_WIRE);
1079     } else {
1080         draw_rect_coords(fe, bx+x1, by+y1,
1081                          bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy,
1082                          COL_BARRIER);
1083     }
1084 }
1085
1086 static void draw_barrier(frontend *fe, int x, int y, int dir, int phase)
1087 {
1088     int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1089     int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1090     int x1, y1, w, h;
1091
1092     x1 = (X(dir) > 0 ? TILE_SIZE : X(dir) == 0 ? TILE_BORDER : 0);
1093     y1 = (Y(dir) > 0 ? TILE_SIZE : Y(dir) == 0 ? TILE_BORDER : 0);
1094     w = (X(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
1095     h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
1096
1097     if (phase == 0) {
1098         draw_rect(fe, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
1099     } else {
1100         draw_rect(fe, bx+x1, by+y1, w, h, COL_BARRIER);
1101     }
1102 }
1103
1104 static void draw_tile(frontend *fe, game_state *state, int x, int y, int tile,
1105                       float xshift, float yshift)
1106 {
1107     int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x + (xshift * TILE_SIZE);
1108     int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y + (yshift * TILE_SIZE);
1109     float cx, cy, ex, ey;
1110     int dir, col;
1111
1112     /*
1113      * When we draw a single tile, we must draw everything up to
1114      * and including the borders around the tile. This means that
1115      * if the neighbouring tiles have connections to those borders,
1116      * we must draw those connections on the borders themselves.
1117      *
1118      * This would be terribly fiddly if we ever had to draw a tile
1119      * while its neighbour was in mid-rotate, because we'd have to
1120      * arrange to _know_ that the neighbour was being rotated and
1121      * hence had an anomalous effect on the redraw of this tile.
1122      * Fortunately, the drawing algorithm avoids ever calling us in
1123      * this circumstance: we're either drawing lots of straight
1124      * tiles at game start or after a move is complete, or we're
1125      * repeatedly drawing only the rotating tile. So no problem.
1126      */
1127
1128     /*
1129      * So. First blank the tile out completely: draw a big
1130      * rectangle in border colour, and a smaller rectangle in
1131      * background colour to fill it in.
1132      */
1133     draw_rect(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER,
1134               COL_BORDER);
1135     draw_rect(fe, bx+TILE_BORDER, by+TILE_BORDER,
1136               TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER,
1137               tile & FLASHING ? COL_FLASHING : COL_BACKGROUND);
1138
1139     /*
1140      * Draw the wires.
1141      */
1142     cx = cy = TILE_BORDER + (TILE_SIZE-TILE_BORDER) / 2.0F - 0.5F;
1143     col = (tile & ACTIVE ? COL_POWERED : COL_WIRE);
1144     for (dir = 1; dir < 0x10; dir <<= 1) {
1145         if (tile & dir) {
1146             ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
1147             ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
1148             draw_thick_line(fe, bx+(int)cx, by+(int)cy,
1149                             bx+(int)(cx+ex), by+(int)(cy+ey),
1150                             COL_WIRE);
1151         }
1152     }
1153     for (dir = 1; dir < 0x10; dir <<= 1) {
1154         if (tile & dir) {
1155             ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
1156             ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
1157             draw_line(fe, bx+(int)cx, by+(int)cy,
1158                       bx+(int)(cx+ex), by+(int)(cy+ey), col);
1159         }
1160     }
1161
1162     /*
1163      * Draw the box in the middle. We do this in blue if the tile
1164      * is an unpowered endpoint, in cyan if the tile is a powered
1165      * endpoint, in black if the tile is the centrepiece, and
1166      * otherwise not at all.
1167      */
1168     col = -1;
1169     if (x == state->cx && y == state->cy)
1170         col = COL_WIRE;
1171     else if (COUNT(tile) == 1) {
1172         col = (tile & ACTIVE ? COL_POWERED : COL_ENDPOINT);
1173     }
1174     if (col >= 0) {
1175         int i, points[8];
1176
1177         points[0] = +1; points[1] = +1;
1178         points[2] = +1; points[3] = -1;
1179         points[4] = -1; points[5] = -1;
1180         points[6] = -1; points[7] = +1;
1181
1182         for (i = 0; i < 8; i += 2) {
1183             ex = (TILE_SIZE * 0.24F) * points[i];
1184             ey = (TILE_SIZE * 0.24F) * points[i+1];
1185             points[i] = bx+(int)(cx+ex);
1186             points[i+1] = by+(int)(cy+ey);
1187         }
1188
1189         draw_polygon(fe, points, 4, TRUE, col);
1190         draw_polygon(fe, points, 4, FALSE, COL_WIRE);
1191     }
1192
1193     /*
1194      * Draw the points on the border if other tiles are connected
1195      * to us.
1196      */
1197     for (dir = 1; dir < 0x10; dir <<= 1) {
1198         int dx, dy, px, py, lx, ly, vx, vy, ox, oy;
1199
1200         dx = X(dir);
1201         dy = Y(dir);
1202
1203         ox = x + dx;
1204         oy = y + dy;
1205
1206         if (ox < 0 || ox >= state->width || oy < 0 || oy >= state->height)
1207             continue;
1208
1209         if (!(tile(state, ox, oy) & F(dir)))
1210             continue;
1211
1212         px = bx + (int)(dx>0 ? TILE_SIZE + TILE_BORDER - 1 : dx<0 ? 0 : cx);
1213         py = by + (int)(dy>0 ? TILE_SIZE + TILE_BORDER - 1 : dy<0 ? 0 : cy);
1214         lx = dx * (TILE_BORDER-1);
1215         ly = dy * (TILE_BORDER-1);
1216         vx = (dy ? 1 : 0);
1217         vy = (dx ? 1 : 0);
1218
1219         if (xshift == 0.0 && yshift == 0.0 && (tile & dir)) {
1220             /*
1221              * If we are fully connected to the other tile, we must
1222              * draw right across the tile border. (We can use our
1223              * own ACTIVE state to determine what colour to do this
1224              * in: if we are fully connected to the other tile then
1225              * the two ACTIVE states will be the same.)
1226              */
1227             draw_rect_coords(fe, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
1228             draw_rect_coords(fe, px, py, px+lx, py+ly,
1229                              (tile & ACTIVE) ? COL_POWERED : COL_WIRE);
1230         } else {
1231             /*
1232              * The other tile extends into our border, but isn't
1233              * actually connected to us. Just draw a single black
1234              * dot.
1235              */
1236             draw_rect_coords(fe, px, py, px, py, COL_WIRE);
1237         }
1238     }
1239
1240     draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
1241 }
1242
1243 static void draw_tile_barriers(frontend *fe, game_state *state, int x, int y)
1244 {
1245     int phase;
1246     int dir;
1247     int bx = BORDER + WINDOW_OFFSET + TILE_SIZE * x;
1248     int by = BORDER + WINDOW_OFFSET + TILE_SIZE * y;
1249     /*
1250      * Draw barrier corners, and then barriers.
1251      */
1252     for (phase = 0; phase < 2; phase++) {
1253         for (dir = 1; dir < 0x10; dir <<= 1)
1254             if (barrier(state, x, y) & (dir << 4))
1255                 draw_barrier_corner(fe, x, y, dir << 4, phase);
1256         for (dir = 1; dir < 0x10; dir <<= 1)
1257             if (barrier(state, x, y) & dir)
1258                 draw_barrier(fe, x, y, dir, phase);
1259     }
1260
1261     draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
1262 }
1263
1264 static void draw_arrow(frontend *fe, int x, int y, int xdx, int xdy)
1265 {
1266     int coords[14];
1267     int ydy = -xdx, ydx = xdy;
1268
1269     x = x * TILE_SIZE + BORDER + WINDOW_OFFSET;
1270     y = y * TILE_SIZE + BORDER + WINDOW_OFFSET;
1271
1272 #define POINT(n, xx, yy) ( \
1273     coords[2*(n)+0] = x + (xx)*xdx + (yy)*ydx, \
1274     coords[2*(n)+1] = y + (xx)*xdy + (yy)*ydy)
1275
1276     POINT(0, TILE_SIZE / 2, 3 * TILE_SIZE / 4);   /* top of arrow */
1277     POINT(1, 3 * TILE_SIZE / 4, TILE_SIZE / 2);   /* right corner */
1278     POINT(2, 5 * TILE_SIZE / 8, TILE_SIZE / 2);   /* right concave */
1279     POINT(3, 5 * TILE_SIZE / 8, TILE_SIZE / 4);   /* bottom right */
1280     POINT(4, 3 * TILE_SIZE / 8, TILE_SIZE / 4);   /* bottom left */
1281     POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2);   /* left concave */
1282     POINT(6,     TILE_SIZE / 4, TILE_SIZE / 2);   /* left corner */
1283
1284     draw_polygon(fe, coords, 7, TRUE, COL_LOWLIGHT);
1285     draw_polygon(fe, coords, 7, FALSE, COL_TEXT);
1286 }
1287
1288 void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
1289                  game_state *state, game_ui *ui, float t, float ft)
1290 {
1291     int x, y, tx, ty, frame;
1292     unsigned char *active;
1293     float xshift = 0.0;
1294     float yshift = 0.0;
1295
1296     /*
1297      * Clear the screen and draw the exterior barrier lines if this
1298      * is our first call.
1299      */
1300     if (!ds->started) {
1301         int phase;
1302
1303         ds->started = TRUE;
1304
1305         draw_rect(fe, 0, 0, 
1306                   BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
1307                   BORDER * 2 + WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
1308                   COL_BACKGROUND);
1309         draw_update(fe, 0, 0, 
1310                     BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
1311                     BORDER * 2 + WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
1312
1313         for (phase = 0; phase < 2; phase++) {
1314
1315             for (x = 0; x < ds->width; x++) {
1316                 if (barrier(state, x, 0) & UL)
1317                     draw_barrier_corner(fe, x, -1, LD, phase);
1318                 if (barrier(state, x, 0) & RU)
1319                     draw_barrier_corner(fe, x, -1, DR, phase);
1320                 if (barrier(state, x, 0) & U)
1321                     draw_barrier(fe, x, -1, D, phase);
1322                 if (barrier(state, x, ds->height-1) & DR)
1323                     draw_barrier_corner(fe, x, ds->height, RU, phase);
1324                 if (barrier(state, x, ds->height-1) & LD)
1325                     draw_barrier_corner(fe, x, ds->height, UL, phase);
1326                 if (barrier(state, x, ds->height-1) & D)
1327                     draw_barrier(fe, x, ds->height, U, phase);
1328             }
1329
1330             for (y = 0; y < ds->height; y++) {
1331                 if (barrier(state, 0, y) & UL)
1332                     draw_barrier_corner(fe, -1, y, RU, phase);
1333                 if (barrier(state, 0, y) & LD)
1334                     draw_barrier_corner(fe, -1, y, DR, phase);
1335                 if (barrier(state, 0, y) & L)
1336                     draw_barrier(fe, -1, y, R, phase);
1337                 if (barrier(state, ds->width-1, y) & RU)
1338                     draw_barrier_corner(fe, ds->width, y, UL, phase);
1339                 if (barrier(state, ds->width-1, y) & DR)
1340                     draw_barrier_corner(fe, ds->width, y, LD, phase);
1341                 if (barrier(state, ds->width-1, y) & R)
1342                     draw_barrier(fe, ds->width, y, L, phase);
1343             }
1344         }
1345
1346         /*
1347          * Arrows for making moves.
1348          */
1349         for (x = 0; x < ds->width; x++) {
1350             if (x == state->cx) continue;
1351             draw_arrow(fe, x, 0, +1, 0);
1352             draw_arrow(fe, x+1, ds->height, -1, 0);
1353         }
1354         for (y = 0; y < ds->height; y++) {
1355             if (y == state->cy) continue;
1356             draw_arrow(fe, ds->width, y, 0, +1);
1357             draw_arrow(fe, 0, y+1, 0, -1);
1358         }
1359     }
1360
1361     /* Check if this is an undo.  If so, we will need to run any animation
1362      * backwards.
1363      */
1364     if (oldstate && oldstate->move_count > state->move_count) {
1365         game_state * tmpstate = state;
1366         state = oldstate;
1367         oldstate = tmpstate;
1368         t = ANIM_TIME - t;
1369     }
1370
1371     tx = ty = -1;
1372     if (oldstate && (t < ANIM_TIME)) {
1373         /*
1374          * We're animating a slide, of row/column number
1375          * state->last_move_pos, in direction
1376          * state->last_move_dir
1377          */
1378         xshift = state->last_move_row == -1 ? 0.0 :
1379                 (1 - t / ANIM_TIME) * state->last_move_dir;
1380         yshift = state->last_move_col == -1 ? 0.0 :
1381                 (1 - t / ANIM_TIME) * state->last_move_dir;
1382     }
1383     
1384     frame = -1;
1385     if (ft > 0) {
1386         /*
1387          * We're animating a completion flash. Find which frame
1388          * we're at.
1389          */
1390         frame = (int)(ft / FLASH_FRAME);
1391     }
1392
1393     /*
1394      * Draw any tile which differs from the way it was last drawn.
1395      */
1396     if (xshift != 0.0 || yshift != 0.0) {
1397         active = compute_active(state,
1398                                 state->last_move_row, state->last_move_col);
1399     } else {
1400         active = compute_active(state, -1, -1);
1401     }
1402
1403     clip(fe,
1404          BORDER + WINDOW_OFFSET, BORDER + WINDOW_OFFSET,
1405          TILE_SIZE * state->width + TILE_BORDER,
1406          TILE_SIZE * state->height + TILE_BORDER);
1407     
1408     for (x = 0; x < ds->width; x++)
1409         for (y = 0; y < ds->height; y++) {
1410             unsigned char c = tile(state, x, y) | index(state, active, x, y);
1411
1412             /*
1413              * In a completion flash, we adjust the FLASHING bit
1414              * depending on our distance from the centre point and
1415              * the frame number.
1416              */
1417             if (frame >= 0) {
1418                 int xdist, ydist, dist;
1419                 xdist = (x < state->cx ? state->cx - x : x - state->cx);
1420                 ydist = (y < state->cy ? state->cy - y : y - state->cy);
1421                 dist = (xdist > ydist ? xdist : ydist);
1422
1423                 if (frame >= dist && frame < dist+4) {
1424                     int flash = (frame - dist) & 1;
1425                     flash = flash ? FLASHING : 0;
1426                     c = (c &~ FLASHING) | flash;
1427                 }
1428             }
1429
1430             if (index(state, ds->visible, x, y) != c ||
1431                 index(state, ds->visible, x, y) == 0xFF ||
1432                 (x == state->last_move_col || y == state->last_move_row))
1433             {
1434                 float xs = (y == state->last_move_row ? xshift : 0.0);
1435                 float ys = (x == state->last_move_col ? yshift : 0.0);
1436
1437                 draw_tile(fe, state, x, y, c, xs, ys);
1438                 if (xs < 0 && x == 0)
1439                     draw_tile(fe, state, state->width, y, c, xs, ys);
1440                 else if (xs > 0 && x == state->width - 1)
1441                     draw_tile(fe, state, -1, y, c, xs, ys);
1442                 else if (ys < 0 && y == 0)
1443                     draw_tile(fe, state, x, state->height, c, xs, ys);
1444                 else if (ys > 0 && y == state->height - 1)
1445                     draw_tile(fe, state, x, -1, c, xs, ys);
1446
1447                 if (x == state->last_move_col || y == state->last_move_row)
1448                     index(state, ds->visible, x, y) = 0xFF;
1449                 else
1450                     index(state, ds->visible, x, y) = c;
1451             }
1452         }
1453
1454     for (x = 0; x < ds->width; x++)
1455         for (y = 0; y < ds->height; y++)
1456             draw_tile_barriers(fe, state, x, y);
1457
1458     unclip(fe);
1459
1460     /*
1461      * Update the status bar.
1462      */
1463     {
1464         char statusbuf[256];
1465         int i, n, a;
1466
1467         n = state->width * state->height;
1468         for (i = a = 0; i < n; i++)
1469             if (active[i])
1470                 a++;
1471
1472         sprintf(statusbuf, "%sMoves: %d Active: %d/%d",
1473                 (state->completed ? "COMPLETED! " : ""),
1474                 (state->completed ? state->completed : state->move_count),
1475                 a, n);
1476
1477         status_bar(fe, statusbuf);
1478     }
1479
1480     sfree(active);
1481 }
1482
1483 float game_anim_length(game_state *oldstate, game_state *newstate)
1484 {
1485     return ANIM_TIME;
1486 }
1487
1488 float game_flash_length(game_state *oldstate, game_state *newstate)
1489 {
1490     /*
1491      * If the game has just been completed, we display a completion
1492      * flash.
1493      */
1494     if (!oldstate->completed && newstate->completed) {
1495         int size;
1496         size = 0;
1497         if (size < newstate->cx+1)
1498             size = newstate->cx+1;
1499         if (size < newstate->cy+1)
1500             size = newstate->cy+1;
1501         if (size < newstate->width - newstate->cx)
1502             size = newstate->width - newstate->cx;
1503         if (size < newstate->height - newstate->cy)
1504             size = newstate->height - newstate->cy;
1505         return FLASH_FRAME * (size+4);
1506     }
1507
1508     return 0.0F;
1509 }
1510
1511 int game_wants_statusbar(void)
1512 {
1513     return TRUE;
1514 }