chiark / gitweb /
It's actually vitally important, it turns out, to have all of the
[sgt-puzzles.git] / rect.c
1 /*
2  * rect.c: Puzzle from nikoli.co.jp. You have a square grid with
3  * numbers in some squares; you must divide the square grid up into
4  * variously sized rectangles, such that every rectangle contains
5  * exactly one numbered square and the area of each rectangle is
6  * equal to the number contained in it.
7  */
8
9 /*
10  * TODO:
11  * 
12  *  - Improve on singleton removal by making an aesthetic choice
13  *    about which of the options to take.
14  * 
15  *  - When doing the 3x3 trick in singleton removal, limit the size
16  *    of the generated rectangles in accordance with the max
17  *    rectangle size.
18  * 
19  *  - It might be interesting to deliberately try to place
20  *    numbers so as to reduce alternative solution patterns. I
21  *    doubt we can do a perfect job of this, but we can make a
22  *    start by, for example, noticing pairs of 2-rects
23  *    alongside one another and _not_ putting their numbers at
24  *    opposite ends.
25  *
26  *  - If we start by sorting the rectlist in descending order
27  *    of area, we might be able to bias our random number
28  *    selection to produce a few large rectangles more often
29  *    than oodles of small ones? Unsure, but might be worth a
30  *    try.
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <assert.h>
37 #include <ctype.h>
38 #include <math.h>
39
40 #include "puzzles.h"
41
42 enum {
43     COL_BACKGROUND,
44     COL_CORRECT,
45     COL_LINE,
46     COL_TEXT,
47     COL_GRID,
48     COL_DRAG,
49     NCOLOURS
50 };
51
52 struct game_params {
53     int w, h;
54     float expandfactor;
55 };
56
57 #define INDEX(state, x, y)    (((y) * (state)->w) + (x))
58 #define index(state, a, x, y) ((a) [ INDEX(state,x,y) ])
59 #define grid(state,x,y)       index(state, (state)->grid, x, y)
60 #define vedge(state,x,y)      index(state, (state)->vedge, x, y)
61 #define hedge(state,x,y)      index(state, (state)->hedge, x, y)
62
63 #define CRANGE(state,x,y,dx,dy) ( (x) >= dx && (x) < (state)->w && \
64                                 (y) >= dy && (y) < (state)->h )
65 #define RANGE(state,x,y)  CRANGE(state,x,y,0,0)
66 #define HRANGE(state,x,y) CRANGE(state,x,y,0,1)
67 #define VRANGE(state,x,y) CRANGE(state,x,y,1,0)
68
69 #define TILE_SIZE 24
70 #define BORDER 18
71
72 #define CORNER_TOLERANCE 0.15F
73 #define CENTRE_TOLERANCE 0.15F
74
75 #define FLASH_TIME 0.13F
76
77 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
78 #define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE )
79
80 struct game_state {
81     int w, h;
82     int *grid;                         /* contains the numbers */
83     unsigned char *vedge;              /* (w+1) x h */
84     unsigned char *hedge;              /* w x (h+1) */
85     int completed;
86 };
87
88 static game_params *default_params(void)
89 {
90     game_params *ret = snew(game_params);
91
92     ret->w = ret->h = 7;
93     ret->expandfactor = 0.0F;
94
95     return ret;
96 }
97
98 static int game_fetch_preset(int i, char **name, game_params **params)
99 {
100     game_params *ret;
101     int w, h;
102     char buf[80];
103
104     switch (i) {
105       case 0: w = 7, h = 7; break;
106       case 1: w = 11, h = 11; break;
107       case 2: w = 15, h = 15; break;
108       case 3: w = 19, h = 19; break;
109       default: return FALSE;
110     }
111
112     sprintf(buf, "%dx%d", w, h);
113     *name = dupstr(buf);
114     *params = ret = snew(game_params);
115     ret->w = w;
116     ret->h = h;
117     ret->expandfactor = 0.0F;
118     return TRUE;
119 }
120
121 static void free_params(game_params *params)
122 {
123     sfree(params);
124 }
125
126 static game_params *dup_params(game_params *params)
127 {
128     game_params *ret = snew(game_params);
129     *ret = *params;                    /* structure copy */
130     return ret;
131 }
132
133 static game_params *decode_params(char const *string)
134 {
135     game_params *ret = default_params();
136
137     ret->w = ret->h = atoi(string);
138     ret->expandfactor = 0.0F;
139     while (*string && isdigit((unsigned char)*string)) string++;
140     if (*string == 'x') {
141         string++;
142         ret->h = atoi(string);
143         while (*string && isdigit((unsigned char)*string)) string++;
144     }
145     if (*string == 'e') {
146         string++;
147         ret->expandfactor = atof(string);
148     }
149
150     return ret;
151 }
152
153 static char *encode_params(game_params *params)
154 {
155     char data[256];
156
157     sprintf(data, "%dx%d", params->w, params->h);
158
159     return dupstr(data);
160 }
161
162 static config_item *game_configure(game_params *params)
163 {
164     config_item *ret;
165     char buf[80];
166
167     ret = snewn(5, config_item);
168
169     ret[0].name = "Width";
170     ret[0].type = C_STRING;
171     sprintf(buf, "%d", params->w);
172     ret[0].sval = dupstr(buf);
173     ret[0].ival = 0;
174
175     ret[1].name = "Height";
176     ret[1].type = C_STRING;
177     sprintf(buf, "%d", params->h);
178     ret[1].sval = dupstr(buf);
179     ret[1].ival = 0;
180
181     ret[2].name = "Expansion factor";
182     ret[2].type = C_STRING;
183     sprintf(buf, "%g", params->expandfactor);
184     ret[2].sval = dupstr(buf);
185     ret[2].ival = 0;
186
187     ret[3].name = NULL;
188     ret[3].type = C_END;
189     ret[3].sval = NULL;
190     ret[3].ival = 0;
191
192     return ret;
193 }
194
195 static game_params *custom_params(config_item *cfg)
196 {
197     game_params *ret = snew(game_params);
198
199     ret->w = atoi(cfg[0].sval);
200     ret->h = atoi(cfg[1].sval);
201     ret->expandfactor = atof(cfg[2].sval);
202
203     return ret;
204 }
205
206 static char *validate_params(game_params *params)
207 {
208     if (params->w <= 0 && params->h <= 0)
209         return "Width and height must both be greater than zero";
210     if (params->w < 2 && params->h < 2)
211         return "Grid area must be greater than one";
212     if (params->expandfactor < 0.0F)
213         return "Expansion factor may not be negative";
214     return NULL;
215 }
216
217 struct rect {
218     int x, y;
219     int w, h;
220 };
221
222 struct rectlist {
223     struct rect *rects;
224     int n;
225 };
226
227 static struct rectlist *get_rectlist(game_params *params, int *grid)
228 {
229     int rw, rh;
230     int x, y;
231     int maxarea;
232     struct rect *rects = NULL;
233     int nrects = 0, rectsize = 0;
234
235     /*
236      * Maximum rectangle area is 1/6 of total grid size, unless
237      * this means we can't place any rectangles at all in which
238      * case we set it to 2 at minimum.
239      */
240     maxarea = params->w * params->h / 6;
241     if (maxarea < 2)
242         maxarea = 2;
243
244     for (rw = 1; rw <= params->w; rw++)
245         for (rh = 1; rh <= params->h; rh++) {
246             if (rw * rh > maxarea)
247                 continue;
248             if (rw * rh == 1)
249                 continue;
250             for (x = 0; x <= params->w - rw; x++)
251                 for (y = 0; y <= params->h - rh; y++) {
252                     if (nrects >= rectsize) {
253                         rectsize = nrects + 256;
254                         rects = sresize(rects, rectsize, struct rect);
255                     }
256
257                     rects[nrects].x = x;
258                     rects[nrects].y = y;
259                     rects[nrects].w = rw;
260                     rects[nrects].h = rh;
261                     nrects++;
262                 }
263         }
264
265     if (nrects > 0) {
266         struct rectlist *ret;
267         ret = snew(struct rectlist);
268         ret->rects = rects;
269         ret->n = nrects;
270         return ret;
271     } else {
272         assert(rects == NULL);         /* hence no need to free */
273         return NULL;
274     }
275 }
276
277 static void free_rectlist(struct rectlist *list)
278 {
279     sfree(list->rects);
280     sfree(list);
281 }
282
283 static void place_rect(game_params *params, int *grid, struct rect r)
284 {
285     int idx = INDEX(params, r.x, r.y);
286     int x, y;
287
288     for (x = r.x; x < r.x+r.w; x++)
289         for (y = r.y; y < r.y+r.h; y++) {
290             index(params, grid, x, y) = idx;
291         }
292 #ifdef GENERATION_DIAGNOSTICS
293     printf("    placing rectangle at (%d,%d) size %d x %d\n",
294            r.x, r.y, r.w, r.h);
295 #endif
296 }
297
298 static struct rect find_rect(game_params *params, int *grid, int x, int y)
299 {
300     int idx, w, h;
301     struct rect r;
302
303     /*
304      * Find the top left of the rectangle.
305      */
306     idx = index(params, grid, x, y);
307
308     if (idx < 0) {
309         r.x = x;
310         r.y = y;
311         r.w = r.h = 1;
312         return r;                      /* 1x1 singleton here */
313     }
314
315     y = idx / params->w;
316     x = idx % params->w;
317
318     /*
319      * Find the width and height of the rectangle.
320      */
321     for (w = 1;
322          (x+w < params->w && index(params,grid,x+w,y)==idx);
323          w++);
324     for (h = 1;
325          (y+h < params->h && index(params,grid,x,y+h)==idx);
326          h++);
327
328     r.x = x;
329     r.y = y;
330     r.w = w;
331     r.h = h;
332
333     return r;
334 }
335
336 #ifdef GENERATION_DIAGNOSTICS
337 static void display_grid(game_params *params, int *grid, int *numbers, int all)
338 {
339     unsigned char *egrid = snewn((params->w*2+3) * (params->h*2+3),
340                                  unsigned char);
341     int x, y;
342     int r = (params->w*2+3);
343
344     memset(egrid, 0, (params->w*2+3) * (params->h*2+3));
345
346     for (x = 0; x < params->w; x++)
347         for (y = 0; y < params->h; y++) {
348             int i = index(params, grid, x, y);
349             if (x == 0 || index(params, grid, x-1, y) != i)
350                 egrid[(2*y+2) * r + (2*x+1)] = 1;
351             if (x == params->w-1 || index(params, grid, x+1, y) != i)
352                 egrid[(2*y+2) * r + (2*x+3)] = 1;
353             if (y == 0 || index(params, grid, x, y-1) != i)
354                 egrid[(2*y+1) * r + (2*x+2)] = 1;
355             if (y == params->h-1 || index(params, grid, x, y+1) != i)
356                 egrid[(2*y+3) * r + (2*x+2)] = 1;
357         }
358
359     for (y = 1; y < 2*params->h+2; y++) {
360         for (x = 1; x < 2*params->w+2; x++) {
361             if (!((y|x)&1)) {
362                 int k = numbers ? index(params, numbers, x/2-1, y/2-1) : 0;
363                 if (k || (all && numbers)) printf("%2d", k); else printf("  ");
364             } else if (!((y&x)&1)) {
365                 int v = egrid[y*r+x];
366                 if ((y&1) && v) v = '-';
367                 if ((x&1) && v) v = '|';
368                 if (!v) v = ' ';
369                 putchar(v);
370                 if (!(x&1)) putchar(v);
371             } else {
372                 int c, d = 0;
373                 if (egrid[y*r+(x+1)]) d |= 1;
374                 if (egrid[(y-1)*r+x]) d |= 2;
375                 if (egrid[y*r+(x-1)]) d |= 4;
376                 if (egrid[(y+1)*r+x]) d |= 8;
377                 c = " ??+?-++?+|+++++"[d];
378                 putchar(c);
379                 if (!(x&1)) putchar(c);
380             }
381         }
382         putchar('\n');
383     }
384
385     sfree(egrid);
386 }
387 #endif
388
389 static char *new_game_seed(game_params *params, random_state *rs)
390 {
391     int *grid, *numbers;
392     struct rectlist *list;
393     int x, y, y2, y2last, yx, run, i;
394     char *seed, *p;
395     game_params params2real, *params2 = &params2real;
396
397     /*
398      * Set up the smaller width and height which we will use to
399      * generate the base grid.
400      */
401     params2->w = params->w / (1.0F + params->expandfactor);
402     if (params2->w < 2 && params->w >= 2) params2->w = 2;
403     params2->h = params->h / (1.0F + params->expandfactor);
404     if (params2->h < 2 && params->h >= 2) params2->h = 2;
405
406     grid = snewn(params2->w * params2->h, int);
407
408     for (y = 0; y < params2->h; y++)
409         for (x = 0; x < params2->w; x++) {
410             index(params2, grid, x, y) = -1;
411         }
412
413     list = get_rectlist(params2, grid);
414     assert(list != NULL);
415
416     /*
417      * Place rectangles until we can't any more.
418      */
419     while (list->n > 0) {
420         int i, m;
421         struct rect r;
422
423         /*
424          * Pick a random rectangle.
425          */
426         i = random_upto(rs, list->n);
427         r = list->rects[i];
428
429         /*
430          * Place it.
431          */
432         place_rect(params2, grid, r);
433
434         /*
435          * Winnow the list by removing any rectangles which
436          * overlap this one.
437          */
438         m = 0;
439         for (i = 0; i < list->n; i++) {
440             struct rect s = list->rects[i];
441             if (s.x+s.w <= r.x || r.x+r.w <= s.x ||
442                 s.y+s.h <= r.y || r.y+r.h <= s.y)
443                 list->rects[m++] = s;
444         }
445         list->n = m;
446     }
447
448     free_rectlist(list);
449
450     /*
451      * Deal with singleton spaces remaining in the grid, one by
452      * one.
453      * 
454      * We do this by making a local change to the layout. There are
455      * several possibilities:
456      * 
457      *     +-----+-----+    Here, we can remove the singleton by
458      *     |     |     |    extending the 1x2 rectangle below it
459      *     +--+--+-----+    into a 1x3.
460      *     |  |  |     |
461      *     |  +--+     |
462      *     |  |  |     |
463      *     |  |  |     |
464      *     |  |  |     |
465      *     +--+--+-----+
466      * 
467      *     +--+--+--+       Here, that trick doesn't work: there's no
468      *     |     |  |       1 x n rectangle with the singleton at one
469      *     |     |  |       end. Instead, we extend a 1 x n rectangle
470      *     |     |  |       _out_ from the singleton, shaving a layer
471      *     +--+--+  |       off the end of another rectangle. So if we
472      *     |  |  |  |       extended up, we'd make our singleton part
473      *     |  +--+--+       of a 1x3 and generate a 1x2 where the 2x2
474      *     |  |     |       used to be; or we could extend right into
475      *     +--+-----+       a 2x1, turning the 1x3 into a 1x2.
476      * 
477      *     +-----+--+       Here, we can't even do _that_, since any
478      *     |     |  |       direction we choose to extend the singleton
479      *     +--+--+  |       will produce a new singleton as a result of
480      *     |  |  |  |       truncating one of the size-2 rectangles.
481      *     |  +--+--+       Fortunately, this case can _only_ occur when
482      *     |  |     |       a singleton is surrounded by four size-2s
483      *     +--+-----+       in this fashion; so instead we can simply
484      *                      replace the whole section with a single 3x3.
485      */
486     for (x = 0; x < params2->w; x++) {
487         for (y = 0; y < params2->h; y++) {
488             if (index(params2, grid, x, y) < 0) {
489                 int dirs[4], ndirs;
490
491 #ifdef GENERATION_DIAGNOSTICS
492                 display_grid(params2, grid, NULL, FALSE);
493                 printf("singleton at %d,%d\n", x, y);
494 #endif
495
496                 /*
497                  * Check in which directions we can feasibly extend
498                  * the singleton. We can extend in a particular
499                  * direction iff either:
500                  * 
501                  *  - the rectangle on that side of the singleton
502                  *    is not 2x1, and we are at one end of the edge
503                  *    of it we are touching
504                  * 
505                  *  - it is 2x1 but we are on its short side.
506                  * 
507                  * FIXME: we could plausibly choose between these
508                  * based on the sizes of the rectangles they would
509                  * create?
510                  */
511                 ndirs = 0;
512                 if (x < params2->w-1) {
513                     struct rect r = find_rect(params2, grid, x+1, y);
514                     if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1)
515                         dirs[ndirs++] = 1;   /* right */
516                 }
517                 if (y > 0) {
518                     struct rect r = find_rect(params2, grid, x, y-1);
519                     if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1)
520                         dirs[ndirs++] = 2;   /* up */
521                 }
522                 if (x > 0) {
523                     struct rect r = find_rect(params2, grid, x-1, y);
524                     if ((r.w * r.h > 2 && (r.y==y || r.y+r.h-1==y)) || r.h==1)
525                         dirs[ndirs++] = 4;   /* left */
526                 }
527                 if (y < params2->h-1) {
528                     struct rect r = find_rect(params2, grid, x, y+1);
529                     if ((r.w * r.h > 2 && (r.x==x || r.x+r.w-1==x)) || r.w==1)
530                         dirs[ndirs++] = 8;   /* down */
531                 }
532
533                 if (ndirs > 0) {
534                     int which, dir;
535                     struct rect r1, r2;
536
537                     which = random_upto(rs, ndirs);
538                     dir = dirs[which];
539
540                     switch (dir) {
541                       case 1:          /* right */
542                         assert(x < params2->w+1);
543 #ifdef GENERATION_DIAGNOSTICS
544                         printf("extending right\n");
545 #endif
546                         r1 = find_rect(params2, grid, x+1, y);
547                         r2.x = x;
548                         r2.y = y;
549                         r2.w = 1 + r1.w;
550                         r2.h = 1;
551                         if (r1.y == y)
552                             r1.y++;
553                         r1.h--;
554                         break;
555                       case 2:          /* up */
556                         assert(y > 0);
557 #ifdef GENERATION_DIAGNOSTICS
558                         printf("extending up\n");
559 #endif
560                         r1 = find_rect(params2, grid, x, y-1);
561                         r2.x = x;
562                         r2.y = r1.y;
563                         r2.w = 1;
564                         r2.h = 1 + r1.h;
565                         if (r1.x == x)
566                             r1.x++;
567                         r1.w--;
568                         break;
569                       case 4:          /* left */
570                         assert(x > 0);
571 #ifdef GENERATION_DIAGNOSTICS
572                         printf("extending left\n");
573 #endif
574                         r1 = find_rect(params2, grid, x-1, y);
575                         r2.x = r1.x;
576                         r2.y = y;
577                         r2.w = 1 + r1.w;
578                         r2.h = 1;
579                         if (r1.y == y)
580                             r1.y++;
581                         r1.h--;
582                         break;
583                       case 8:          /* down */
584                         assert(y < params2->h+1);
585 #ifdef GENERATION_DIAGNOSTICS
586                         printf("extending down\n");
587 #endif
588                         r1 = find_rect(params2, grid, x, y+1);
589                         r2.x = x;
590                         r2.y = y;
591                         r2.w = 1;
592                         r2.h = 1 + r1.h;
593                         if (r1.x == x)
594                             r1.x++;
595                         r1.w--;
596                         break;
597                     }
598                     if (r1.h > 0 && r1.w > 0)
599                         place_rect(params2, grid, r1);
600                     place_rect(params2, grid, r2);
601                 } else {
602 #ifndef NDEBUG
603                     /*
604                      * Sanity-check that there really is a 3x3
605                      * rectangle surrounding this singleton and it
606                      * contains absolutely everything we could
607                      * possibly need.
608                      */
609                     {
610                         int xx, yy;
611                         assert(x > 0 && x < params2->w-1);
612                         assert(y > 0 && y < params2->h-1);
613
614                         for (xx = x-1; xx <= x+1; xx++)
615                             for (yy = y-1; yy <= y+1; yy++) {
616                                 struct rect r = find_rect(params2,grid,xx,yy);
617                                 assert(r.x >= x-1);
618                                 assert(r.y >= y-1);
619                                 assert(r.x+r.w-1 <= x+1);
620                                 assert(r.y+r.h-1 <= y+1);
621                             }
622                     }
623 #endif
624                     
625 #ifdef GENERATION_DIAGNOSTICS
626                     printf("need the 3x3 trick\n");
627 #endif
628
629                     /*
630                      * FIXME: If the maximum rectangle area for
631                      * this grid is less than 9, we ought to
632                      * subdivide the 3x3 in some fashion. There are
633                      * five other possibilities:
634                      * 
635                      *  - a 6 and a 3
636                      *  - a 4, a 3 and a 2
637                      *  - three 3s
638                      *  - a 3 and three 2s (two different arrangements).
639                      */
640
641                     {
642                         struct rect r;
643                         r.x = x-1;
644                         r.y = y-1;
645                         r.w = r.h = 3;
646                         place_rect(params2, grid, r);
647                     }
648                 }
649             }
650         }
651     }
652
653     /*
654      * We have now constructed a grid of the size specified in
655      * params2. Now we extend it into a grid of the size specified
656      * in params. We do this in two passes: we extend it vertically
657      * until it's the right height, then we transpose it, then
658      * extend it vertically again (getting it effectively the right
659      * width), then finally transpose again.
660      */
661     for (i = 0; i < 2; i++) {
662         int *grid2, *expand, *where;
663         game_params params3real, *params3 = &params3real;
664
665 #ifdef GENERATION_DIAGNOSTICS
666         printf("before expansion:\n");
667         display_grid(params2, grid, NULL, TRUE);
668 #endif
669
670         /*
671          * Set up the new grid.
672          */
673         grid2 = snewn(params2->w * params->h, int);
674         expand = snewn(params2->h-1, int);
675         where = snewn(params2->w, int);
676         params3->w = params2->w;
677         params3->h = params->h;
678
679         /*
680          * Decide which horizontal edges are going to get expanded,
681          * and by how much.
682          */
683         for (y = 0; y < params2->h-1; y++)
684             expand[y] = 0;
685         for (y = params2->h; y < params->h; y++) {
686             x = random_upto(rs, params2->h-1);
687             expand[x]++;
688         }
689
690 #ifdef GENERATION_DIAGNOSTICS
691         printf("expand[] = {");
692         for (y = 0; y < params2->h-1; y++)
693             printf(" %d", expand[y]);
694         printf(" }\n");
695 #endif
696
697         /*
698          * Perform the expansion. The way this works is that we
699          * alternately:
700          * 
701          *  - copy a row from grid into grid2
702          * 
703          *  - invent some number of additional rows in grid2 where
704          *    there was previously only a horizontal line between
705          *    rows in grid, and make random decisions about where
706          *    among these to place each rectangle edge that ran
707          *    along this line.
708          */
709         for (y = y2 = y2last = 0; y < params2->h; y++) {
710             /*
711              * Copy a single line from row y of grid into row y2 of
712              * grid2.
713              */
714             for (x = 0; x < params2->w; x++) {
715                 int val = index(params2, grid, x, y);
716                 if (val / params2->w == y &&   /* rect starts on this line */
717                     (y2 == 0 ||        /* we're at the very top, or... */
718                      index(params3, grid2, x, y2-1) / params3->w < y2last
719                                        /* this rect isn't already started */))
720                     index(params3, grid2, x, y2) =
721                     INDEX(params3, val % params2->w, y2);
722                 else
723                     index(params3, grid2, x, y2) =
724                     index(params3, grid2, x, y2-1);
725             }
726
727             /*
728              * If that was the last line, terminate the loop early.
729              */
730             if (++y2 == params3->h)
731                 break;
732
733             y2last = y2;
734
735             /*
736              * Invent some number of additional lines. First walk
737              * along this line working out where to put all the
738              * edges that coincide with it.
739              */
740             yx = -1;
741             for (x = 0; x < params2->w; x++) {
742                 if (index(params2, grid, x, y) !=
743                     index(params2, grid, x, y+1)) {
744                     /*
745                      * This is a horizontal edge, so it needs
746                      * placing.
747                      */
748                     if (x == 0 ||
749                         (index(params2, grid, x-1, y) !=
750                          index(params2, grid, x, y) &&
751                          index(params2, grid, x-1, y+1) !=
752                          index(params2, grid, x, y+1))) {
753                         /*
754                          * Here we have the chance to make a new
755                          * decision.
756                          */
757                         yx = random_upto(rs, expand[y]+1);
758                     } else {
759                         /*
760                          * Here we just reuse the previous value of
761                          * yx.
762                          */
763                     }
764                 } else
765                     yx = -1;
766                 where[x] = yx;
767             }
768
769             for (yx = 0; yx < expand[y]; yx++) {
770                 /*
771                  * Invent a single row. For each square in the row,
772                  * we copy the grid entry from the square above it,
773                  * unless we're starting the new rectangle here.
774                  */
775                 for (x = 0; x < params2->w; x++) {
776                     if (yx == where[x]) {
777                         int val = index(params2, grid, x, y+1);
778                         val %= params2->w;
779                         val = INDEX(params3, val, y2);
780                         index(params3, grid2, x, y2) = val;
781                     } else
782                         index(params3, grid2, x, y2) = 
783                         index(params3, grid2, x, y2-1);
784                 }
785
786                 y2++;
787             }
788         }
789
790         sfree(expand);
791         sfree(where);
792
793 #ifdef GENERATION_DIAGNOSTICS
794         printf("after expansion:\n");
795         display_grid(params3, grid2, NULL, TRUE);
796 #endif
797         /*
798          * Transpose.
799          */
800         params2->w = params3->h;
801         params2->h = params3->w;
802         sfree(grid);
803         grid = snewn(params2->w * params2->h, int);
804         for (x = 0; x < params2->w; x++)
805             for (y = 0; y < params2->h; y++) {
806                 int idx1 = INDEX(params2, x, y);
807                 int idx2 = INDEX(params3, y, x);
808                 int tmp;
809
810                 tmp = grid2[idx2];
811                 tmp = (tmp % params3->w) * params2->w + (tmp / params3->w);
812                 grid[idx1] = tmp;
813             }
814
815         sfree(grid2);
816
817         {
818             int tmp;
819             tmp = params->w;
820             params->w = params->h;
821             params->h = tmp;
822         }
823
824 #ifdef GENERATION_DIAGNOSTICS
825         printf("after transposition:\n");
826         display_grid(params2, grid, NULL, TRUE);
827 #endif
828     }
829
830     /*
831      * Place numbers.
832      */
833     numbers = snewn(params->w * params->h, int);
834
835     for (y = 0; y < params->h; y++)
836         for (x = 0; x < params->w; x++) {
837             index(params, numbers, x, y) = 0;
838         }
839
840     for (x = 0; x < params->w; x++) {
841         for (y = 0; y < params->h; y++) {
842             int idx = INDEX(params, x, y);
843             if (index(params, grid, x, y) == idx) {
844                 struct rect r = find_rect(params, grid, x, y);
845                 int n, xx, yy;
846
847                 /*
848                  * Decide where to put the number.
849                  */
850                 n = random_upto(rs, r.w*r.h);
851                 yy = n / r.w;
852                 xx = n % r.w;
853                 index(params,numbers,x+xx,y+yy) = r.w*r.h;
854             }
855         }
856     }
857
858 #ifdef GENERATION_DIAGNOSTICS
859     display_grid(params, grid, numbers, FALSE);
860 #endif
861
862     seed = snewn(11 * params->w * params->h, char);
863     p = seed;
864     run = 0;
865     for (i = 0; i <= params->w * params->h; i++) {
866         int n = (i < params->w * params->h ? numbers[i] : -1);
867
868         if (!n)
869             run++;
870         else {
871             if (run) {
872                 while (run > 0) {
873                     int c = 'a' - 1 + run;
874                     if (run > 26)
875                         c = 'z';
876                     *p++ = c;
877                     run -= c - ('a' - 1);
878                 }
879             } else {
880                 /*
881                  * If there's a number in the very top left or
882                  * bottom right, there's no point putting an
883                  * unnecessary _ before or after it.
884                  */
885                 if (p > seed && n > 0)
886                     *p++ = '_';
887             }
888             if (n > 0)
889                 p += sprintf(p, "%d", n);
890             run = 0;
891         }
892     }
893     *p = '\0';
894
895     sfree(grid);
896     sfree(numbers);
897
898     return seed;
899 }
900
901 static char *validate_seed(game_params *params, char *seed)
902 {
903     int area = params->w * params->h;
904     int squares = 0;
905
906     while (*seed) {
907         int n = *seed++;
908         if (n >= 'a' && n <= 'z') {
909             squares += n - 'a' + 1;
910         } else if (n == '_') {
911             /* do nothing */;
912         } else if (n > '0' && n <= '9') {
913             squares++;
914             while (*seed >= '0' && *seed <= '9')
915                 seed++;
916         } else
917             return "Invalid character in game specification";
918     }
919
920     if (squares < area)
921         return "Not enough data to fill grid";
922
923     if (squares > area)
924         return "Too much data to fit in grid";
925
926     return NULL;
927 }
928
929 static game_state *new_game(game_params *params, char *seed)
930 {
931     game_state *state = snew(game_state);
932     int x, y, i, area;
933
934     state->w = params->w;
935     state->h = params->h;
936
937     area = state->w * state->h;
938
939     state->grid = snewn(area, int);
940     state->vedge = snewn(area, unsigned char);
941     state->hedge = snewn(area, unsigned char);
942     state->completed = FALSE;
943
944     i = 0;
945     while (*seed) {
946         int n = *seed++;
947         if (n >= 'a' && n <= 'z') {
948             int run = n - 'a' + 1;
949             assert(i + run <= area);
950             while (run-- > 0)
951                 state->grid[i++] = 0;
952         } else if (n == '_') {
953             /* do nothing */;
954         } else if (n > '0' && n <= '9') {
955             assert(i < area);
956             state->grid[i++] = atoi(seed-1);
957             while (*seed >= '0' && *seed <= '9')
958                 seed++;
959         } else {
960             assert(!"We can't get here");
961         }
962     }
963     assert(i == area);
964
965     for (y = 0; y < state->h; y++)
966         for (x = 0; x < state->w; x++)
967             vedge(state,x,y) = hedge(state,x,y) = 0;
968
969     return state;
970 }
971
972 static game_state *dup_game(game_state *state)
973 {
974     game_state *ret = snew(game_state);
975
976     ret->w = state->w;
977     ret->h = state->h;
978
979     ret->vedge = snewn(state->w * state->h, unsigned char);
980     ret->hedge = snewn(state->w * state->h, unsigned char);
981     ret->grid = snewn(state->w * state->h, int);
982
983     ret->completed = state->completed;
984
985     memcpy(ret->grid, state->grid, state->w * state->h * sizeof(int));
986     memcpy(ret->vedge, state->vedge, state->w*state->h*sizeof(unsigned char));
987     memcpy(ret->hedge, state->hedge, state->w*state->h*sizeof(unsigned char));
988
989     return ret;
990 }
991
992 static void free_game(game_state *state)
993 {
994     sfree(state->grid);
995     sfree(state->vedge);
996     sfree(state->hedge);
997     sfree(state);
998 }
999
1000 static char *game_text_format(game_state *state)
1001 {
1002     char *ret, *p, buf[80];
1003     int i, x, y, col, maxlen;
1004
1005     /*
1006      * First determine the number of spaces required to display a
1007      * number. We'll use at least two, because one looks a bit
1008      * silly.
1009      */
1010     col = 2;
1011     for (i = 0; i < state->w * state->h; i++) {
1012         x = sprintf(buf, "%d", state->grid[i]);
1013         if (col < x) col = x;
1014     }
1015
1016     /*
1017      * Now we know the exact total size of the grid we're going to
1018      * produce: it's got 2*h+1 rows, each containing w lots of col,
1019      * w+1 boundary characters and a trailing newline.
1020      */
1021     maxlen = (2*state->h+1) * (state->w * (col+1) + 2);
1022
1023     ret = snewn(maxlen, char);
1024     p = ret;
1025
1026     for (y = 0; y <= 2*state->h; y++) {
1027         for (x = 0; x <= 2*state->w; x++) {
1028             if (x & y & 1) {
1029                 /*
1030                  * Display a number.
1031                  */
1032                 int v = grid(state, x/2, y/2);
1033                 if (v)
1034                     sprintf(buf, "%*d", col, v);
1035                 else
1036                     sprintf(buf, "%*s", col, "");
1037                 memcpy(p, buf, col);
1038                 p += col;
1039             } else if (x & 1) {
1040                 /*
1041                  * Display a horizontal edge or nothing.
1042                  */
1043                 int h = (y==0 || y==2*state->h ? 1 :
1044                          HRANGE(state, x/2, y/2) && hedge(state, x/2, y/2));
1045                 int i;
1046                 if (h)
1047                     h = '-';
1048                 else
1049                     h = ' ';
1050                 for (i = 0; i < col; i++)
1051                     *p++ = h;
1052             } else if (y & 1) {
1053                 /*
1054                  * Display a vertical edge or nothing.
1055                  */
1056                 int v = (x==0 || x==2*state->w ? 1 :
1057                          VRANGE(state, x/2, y/2) && vedge(state, x/2, y/2));
1058                 if (v)
1059                     *p++ = '|';
1060                 else
1061                     *p++ = ' ';
1062             } else {
1063                 /*
1064                  * Display a corner, or a vertical edge, or a
1065                  * horizontal edge, or nothing.
1066                  */
1067                 int hl = (y==0 || y==2*state->h ? 1 :
1068                           HRANGE(state, (x-1)/2, y/2) && hedge(state, (x-1)/2, y/2));
1069                 int hr = (y==0 || y==2*state->h ? 1 :
1070                           HRANGE(state, (x+1)/2, y/2) && hedge(state, (x+1)/2, y/2));
1071                 int vu = (x==0 || x==2*state->w ? 1 :
1072                           VRANGE(state, x/2, (y-1)/2) && vedge(state, x/2, (y-1)/2));
1073                 int vd = (x==0 || x==2*state->w ? 1 :
1074                           VRANGE(state, x/2, (y+1)/2) && vedge(state, x/2, (y+1)/2));
1075                 if (!hl && !hr && !vu && !vd)
1076                     *p++ = ' ';
1077                 else if (hl && hr && !vu && !vd)
1078                     *p++ = '-';
1079                 else if (!hl && !hr && vu && vd)
1080                     *p++ = '|';
1081                 else
1082                     *p++ = '+';
1083             }
1084         }
1085         *p++ = '\n';
1086     }
1087
1088     assert(p - ret == maxlen);
1089     *p = '\0';
1090     return ret;
1091 }
1092
1093 static unsigned char *get_correct(game_state *state)
1094 {
1095     unsigned char *ret;
1096     int x, y;
1097
1098     ret = snewn(state->w * state->h, unsigned char);
1099     memset(ret, 0xFF, state->w * state->h);
1100
1101     for (x = 0; x < state->w; x++)
1102         for (y = 0; y < state->h; y++)
1103             if (index(state,ret,x,y) == 0xFF) {
1104                 int rw, rh;
1105                 int xx, yy;
1106                 int num, area, valid;
1107
1108                 /*
1109                  * Find a rectangle starting at this point.
1110                  */
1111                 rw = 1;
1112                 while (x+rw < state->w && !vedge(state,x+rw,y))
1113                     rw++;
1114                 rh = 1;
1115                 while (y+rh < state->h && !hedge(state,x,y+rh))
1116                     rh++;
1117
1118                 /*
1119                  * We know what the dimensions of the rectangle
1120                  * should be if it's there at all. Find out if we
1121                  * really have a valid rectangle.
1122                  */
1123                 valid = TRUE;
1124                 /* Check the horizontal edges. */
1125                 for (xx = x; xx < x+rw; xx++) {
1126                     for (yy = y; yy <= y+rh; yy++) {
1127                         int e = !HRANGE(state,xx,yy) || hedge(state,xx,yy);
1128                         int ec = (yy == y || yy == y+rh);
1129                         if (e != ec)
1130                             valid = FALSE;
1131                     }
1132                 }
1133                 /* Check the vertical edges. */
1134                 for (yy = y; yy < y+rh; yy++) {
1135                     for (xx = x; xx <= x+rw; xx++) {
1136                         int e = !VRANGE(state,xx,yy) || vedge(state,xx,yy);
1137                         int ec = (xx == x || xx == x+rw);
1138                         if (e != ec)
1139                             valid = FALSE;
1140                     }
1141                 }
1142
1143                 /*
1144                  * If this is not a valid rectangle with no other
1145                  * edges inside it, we just mark this square as not
1146                  * complete and proceed to the next square.
1147                  */
1148                 if (!valid) {
1149                     index(state, ret, x, y) = 0;
1150                     continue;
1151                 }
1152
1153                 /*
1154                  * We have a rectangle. Now see what its area is,
1155                  * and how many numbers are in it.
1156                  */
1157                 num = 0;
1158                 area = 0;
1159                 for (xx = x; xx < x+rw; xx++) {
1160                     for (yy = y; yy < y+rh; yy++) {
1161                         area++;
1162                         if (grid(state,xx,yy)) {
1163                             if (num > 0)
1164                                 valid = FALSE;   /* two numbers */
1165                             num = grid(state,xx,yy);
1166                         }
1167                     }
1168                 }
1169                 if (num != area)
1170                     valid = FALSE;
1171
1172                 /*
1173                  * Now fill in the whole rectangle based on the
1174                  * value of `valid'.
1175                  */
1176                 for (xx = x; xx < x+rw; xx++) {
1177                     for (yy = y; yy < y+rh; yy++) {
1178                         index(state, ret, xx, yy) = valid;
1179                     }
1180                 }
1181             }
1182
1183     return ret;
1184 }
1185
1186 struct game_ui {
1187     /*
1188      * These coordinates are 2 times the obvious grid coordinates.
1189      * Hence, the top left of the grid is (0,0), the grid point to
1190      * the right of that is (2,0), the one _below that_ is (2,2)
1191      * and so on. This is so that we can specify a drag start point
1192      * on an edge (one odd coordinate) or in the middle of a square
1193      * (two odd coordinates) rather than always at a corner.
1194      * 
1195      * -1,-1 means no drag is in progress.
1196      */
1197     int drag_start_x;
1198     int drag_start_y;
1199     int drag_end_x;
1200     int drag_end_y;
1201     /*
1202      * This flag is set as soon as a dragging action moves the
1203      * mouse pointer away from its starting point, so that even if
1204      * the pointer _returns_ to its starting point the action is
1205      * treated as a small drag rather than a click.
1206      */
1207     int dragged;
1208 };
1209
1210 static game_ui *new_ui(game_state *state)
1211 {
1212     game_ui *ui = snew(game_ui);
1213     ui->drag_start_x = -1;
1214     ui->drag_start_y = -1;
1215     ui->drag_end_x = -1;
1216     ui->drag_end_y = -1;
1217     ui->dragged = FALSE;
1218     return ui;
1219 }
1220
1221 static void free_ui(game_ui *ui)
1222 {
1223     sfree(ui);
1224 }
1225
1226 static void coord_round(float x, float y, int *xr, int *yr)
1227 {
1228     float xs, ys, xv, yv, dx, dy, dist;
1229
1230     /*
1231      * Find the nearest square-centre.
1232      */
1233     xs = (float)floor(x) + 0.5F;
1234     ys = (float)floor(y) + 0.5F;
1235
1236     /*
1237      * And find the nearest grid vertex.
1238      */
1239     xv = (float)floor(x + 0.5F);
1240     yv = (float)floor(y + 0.5F);
1241
1242     /*
1243      * We allocate clicks in parts of the grid square to either
1244      * corners, edges or square centres, as follows:
1245      * 
1246      *   +--+--------+--+
1247      *   |  |        |  |
1248      *   +--+        +--+
1249      *   |   `.    ,'   |
1250      *   |     +--+     |
1251      *   |     |  |     |
1252      *   |     +--+     |
1253      *   |   ,'    `.   |
1254      *   +--+        +--+
1255      *   |  |        |  |
1256      *   +--+--------+--+
1257      * 
1258      * (Not to scale!)
1259      * 
1260      * In other words: we measure the square distance (i.e.
1261      * max(dx,dy)) from the click to the nearest corner, and if
1262      * it's within CORNER_TOLERANCE then we return a corner click.
1263      * We measure the square distance from the click to the nearest
1264      * centre, and if that's within CENTRE_TOLERANCE we return a
1265      * centre click. Failing that, we find which of the two edge
1266      * centres is nearer to the click and return that edge.
1267      */
1268
1269     /*
1270      * Check for corner click.
1271      */
1272     dx = (float)fabs(x - xv);
1273     dy = (float)fabs(y - yv);
1274     dist = (dx > dy ? dx : dy);
1275     if (dist < CORNER_TOLERANCE) {
1276         *xr = 2 * (int)xv;
1277         *yr = 2 * (int)yv;
1278     } else {
1279         /*
1280          * Check for centre click.
1281          */
1282         dx = (float)fabs(x - xs);
1283         dy = (float)fabs(y - ys);
1284         dist = (dx > dy ? dx : dy);
1285         if (dist < CENTRE_TOLERANCE) {
1286             *xr = 1 + 2 * (int)xs;
1287             *yr = 1 + 2 * (int)ys;
1288         } else {
1289             /*
1290              * Failing both of those, see which edge we're closer to.
1291              * Conveniently, this is simply done by testing the relative
1292              * magnitude of dx and dy (which are currently distances from
1293              * the square centre).
1294              */
1295             if (dx > dy) {
1296                 /* Vertical edge: x-coord of corner,
1297                  * y-coord of square centre. */
1298                 *xr = 2 * (int)xv;
1299                 *yr = 1 + 2 * (int)ys;
1300             } else {
1301                 /* Horizontal edge: x-coord of square centre,
1302                  * y-coord of corner. */
1303                 *xr = 1 + 2 * (int)xs;
1304                 *yr = 2 * (int)yv;
1305             }
1306         }
1307     }
1308 }
1309
1310 static void ui_draw_rect(game_state *state, game_ui *ui,
1311                          unsigned char *hedge, unsigned char *vedge, int c)
1312 {
1313     int x1, x2, y1, y2, x, y, t;
1314
1315     x1 = ui->drag_start_x;
1316     x2 = ui->drag_end_x;
1317     if (x2 < x1) { t = x1; x1 = x2; x2 = t; }
1318
1319     y1 = ui->drag_start_y;
1320     y2 = ui->drag_end_y;
1321     if (y2 < y1) { t = y1; y1 = y2; y2 = t; }
1322
1323     x1 = x1 / 2;               /* rounds down */
1324     x2 = (x2+1) / 2;           /* rounds up */
1325     y1 = y1 / 2;               /* rounds down */
1326     y2 = (y2+1) / 2;           /* rounds up */
1327
1328     /*
1329      * Draw horizontal edges of rectangles.
1330      */
1331     for (x = x1; x < x2; x++)
1332         for (y = y1; y <= y2; y++)
1333             if (HRANGE(state,x,y)) {
1334                 int val = index(state,hedge,x,y);
1335                 if (y == y1 || y == y2)
1336                     val = c;
1337                 else if (c == 1)
1338                     val = 0;
1339                 index(state,hedge,x,y) = val;
1340             }
1341
1342     /*
1343      * Draw vertical edges of rectangles.
1344      */
1345     for (y = y1; y < y2; y++)
1346         for (x = x1; x <= x2; x++)
1347             if (VRANGE(state,x,y)) {
1348                 int val = index(state,vedge,x,y);
1349                 if (x == x1 || x == x2)
1350                     val = c;
1351                 else if (c == 1)
1352                     val = 0;
1353                 index(state,vedge,x,y) = val;
1354             }
1355 }
1356
1357 static game_state *make_move(game_state *from, game_ui *ui,
1358                              int x, int y, int button)
1359 {
1360     int xc, yc;
1361     int startdrag = FALSE, enddrag = FALSE, active = FALSE;
1362     game_state *ret;
1363
1364     if (button == LEFT_BUTTON) {
1365         startdrag = TRUE;
1366     } else if (button == LEFT_RELEASE) {
1367         enddrag = TRUE;
1368     } else if (button != LEFT_DRAG) {
1369         return NULL;
1370     }
1371
1372     coord_round(FROMCOORD((float)x), FROMCOORD((float)y), &xc, &yc);
1373
1374     if (startdrag) {
1375         ui->drag_start_x = xc;
1376         ui->drag_start_y = yc;
1377         ui->drag_end_x = xc;
1378         ui->drag_end_y = yc;
1379         ui->dragged = FALSE;
1380         active = TRUE;
1381     }
1382
1383     if (xc != ui->drag_end_x || yc != ui->drag_end_y) {
1384         ui->drag_end_x = xc;
1385         ui->drag_end_y = yc;
1386         ui->dragged = TRUE;
1387         active = TRUE;
1388     }
1389
1390     ret = NULL;
1391
1392     if (enddrag) {
1393         if (xc >= 0 && xc <= 2*from->w &&
1394             yc >= 0 && yc <= 2*from->h) {
1395             ret = dup_game(from);
1396
1397             if (ui->dragged) {
1398                 ui_draw_rect(ret, ui, ret->hedge, ret->vedge, 1);
1399             } else {
1400                 if ((xc & 1) && !(yc & 1) && HRANGE(from,xc/2,yc/2)) {
1401                     hedge(ret,xc/2,yc/2) = !hedge(ret,xc/2,yc/2);
1402                 }
1403                 if ((yc & 1) && !(xc & 1) && VRANGE(from,xc/2,yc/2)) {
1404                     vedge(ret,xc/2,yc/2) = !vedge(ret,xc/2,yc/2);
1405                 }
1406             }
1407
1408             if (!memcmp(ret->hedge, from->hedge, from->w*from->h) &&
1409                 !memcmp(ret->vedge, from->vedge, from->w*from->h)) {
1410                 free_game(ret);
1411                 ret = NULL;
1412             }
1413
1414             /*
1415              * We've made a real change to the grid. Check to see
1416              * if the game has been completed.
1417              */
1418             if (ret && !ret->completed) {
1419                 int x, y, ok;
1420                 unsigned char *correct = get_correct(ret);
1421
1422                 ok = TRUE;
1423                 for (x = 0; x < ret->w; x++)
1424                     for (y = 0; y < ret->h; y++)
1425                         if (!index(ret, correct, x, y))
1426                             ok = FALSE;
1427
1428                 sfree(correct);
1429
1430                 if (ok)
1431                     ret->completed = TRUE;
1432             }
1433         }
1434
1435         ui->drag_start_x = -1;
1436         ui->drag_start_y = -1;
1437         ui->drag_end_x = -1;
1438         ui->drag_end_y = -1;
1439         ui->dragged = FALSE;
1440         active = TRUE;
1441     }
1442
1443     if (ret)
1444         return ret;                    /* a move has been made */
1445     else if (active)
1446         return from;                   /* UI activity has occurred */
1447     else
1448         return NULL;
1449 }
1450
1451 /* ----------------------------------------------------------------------
1452  * Drawing routines.
1453  */
1454
1455 #define CORRECT 65536
1456
1457 #define COLOUR(k) ( (k)==1 ? COL_LINE : COL_DRAG )
1458 #define MAX(x,y) ( (x)>(y) ? (x) : (y) )
1459 #define MAX4(x,y,z,w) ( MAX(MAX(x,y),MAX(z,w)) )
1460
1461 struct game_drawstate {
1462     int started;
1463     int w, h;
1464     unsigned int *visible;
1465 };
1466
1467 static void game_size(game_params *params, int *x, int *y)
1468 {
1469     *x = params->w * TILE_SIZE + 2*BORDER + 1;
1470     *y = params->h * TILE_SIZE + 2*BORDER + 1;
1471 }
1472
1473 static float *game_colours(frontend *fe, game_state *state, int *ncolours)
1474 {
1475     float *ret = snewn(3 * NCOLOURS, float);
1476
1477     frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
1478
1479     ret[COL_GRID * 3 + 0] = 0.5F * ret[COL_BACKGROUND * 3 + 0];
1480     ret[COL_GRID * 3 + 1] = 0.5F * ret[COL_BACKGROUND * 3 + 1];
1481     ret[COL_GRID * 3 + 2] = 0.5F * ret[COL_BACKGROUND * 3 + 2];
1482
1483     ret[COL_DRAG * 3 + 0] = 1.0F;
1484     ret[COL_DRAG * 3 + 1] = 0.0F;
1485     ret[COL_DRAG * 3 + 2] = 0.0F;
1486
1487     ret[COL_CORRECT * 3 + 0] = 0.75F * ret[COL_BACKGROUND * 3 + 0];
1488     ret[COL_CORRECT * 3 + 1] = 0.75F * ret[COL_BACKGROUND * 3 + 1];
1489     ret[COL_CORRECT * 3 + 2] = 0.75F * ret[COL_BACKGROUND * 3 + 2];
1490
1491     ret[COL_LINE * 3 + 0] = 0.0F;
1492     ret[COL_LINE * 3 + 1] = 0.0F;
1493     ret[COL_LINE * 3 + 2] = 0.0F;
1494
1495     ret[COL_TEXT * 3 + 0] = 0.0F;
1496     ret[COL_TEXT * 3 + 1] = 0.0F;
1497     ret[COL_TEXT * 3 + 2] = 0.0F;
1498
1499     *ncolours = NCOLOURS;
1500     return ret;
1501 }
1502
1503 static game_drawstate *game_new_drawstate(game_state *state)
1504 {
1505     struct game_drawstate *ds = snew(struct game_drawstate);
1506     int i;
1507
1508     ds->started = FALSE;
1509     ds->w = state->w;
1510     ds->h = state->h;
1511     ds->visible = snewn(ds->w * ds->h, unsigned int);
1512     for (i = 0; i < ds->w * ds->h; i++)
1513         ds->visible[i] = 0xFFFF;
1514
1515     return ds;
1516 }
1517
1518 static void game_free_drawstate(game_drawstate *ds)
1519 {
1520     sfree(ds->visible);
1521     sfree(ds);
1522 }
1523
1524 static void draw_tile(frontend *fe, game_state *state, int x, int y,
1525                unsigned char *hedge, unsigned char *vedge,
1526                unsigned char *corners, int correct)
1527 {
1528     int cx = COORD(x), cy = COORD(y);
1529     char str[80];
1530
1531     draw_rect(fe, cx, cy, TILE_SIZE+1, TILE_SIZE+1, COL_GRID);
1532     draw_rect(fe, cx+1, cy+1, TILE_SIZE-1, TILE_SIZE-1,
1533               correct ? COL_CORRECT : COL_BACKGROUND);
1534
1535     if (grid(state,x,y)) {
1536         sprintf(str, "%d", grid(state,x,y));
1537         draw_text(fe, cx+TILE_SIZE/2, cy+TILE_SIZE/2, FONT_VARIABLE,
1538                   TILE_SIZE/2, ALIGN_HCENTRE | ALIGN_VCENTRE, COL_TEXT, str);
1539     }
1540
1541     /*
1542      * Draw edges.
1543      */
1544     if (!HRANGE(state,x,y) || index(state,hedge,x,y))
1545         draw_rect(fe, cx, cy, TILE_SIZE+1, 2,
1546                   HRANGE(state,x,y) ? COLOUR(index(state,hedge,x,y)) :
1547                   COL_LINE);
1548     if (!HRANGE(state,x,y+1) || index(state,hedge,x,y+1))
1549         draw_rect(fe, cx, cy+TILE_SIZE-1, TILE_SIZE+1, 2,
1550                   HRANGE(state,x,y+1) ? COLOUR(index(state,hedge,x,y+1)) :
1551                   COL_LINE);
1552     if (!VRANGE(state,x,y) || index(state,vedge,x,y))
1553         draw_rect(fe, cx, cy, 2, TILE_SIZE+1,
1554                   VRANGE(state,x,y) ? COLOUR(index(state,vedge,x,y)) :
1555                   COL_LINE);
1556     if (!VRANGE(state,x+1,y) || index(state,vedge,x+1,y))
1557         draw_rect(fe, cx+TILE_SIZE-1, cy, 2, TILE_SIZE+1,
1558                   VRANGE(state,x+1,y) ? COLOUR(index(state,vedge,x+1,y)) :
1559                   COL_LINE);
1560
1561     /*
1562      * Draw corners.
1563      */
1564     if (index(state,corners,x,y))
1565         draw_rect(fe, cx, cy, 2, 2,
1566                   COLOUR(index(state,corners,x,y)));
1567     if (x+1 < state->w && index(state,corners,x+1,y))
1568         draw_rect(fe, cx+TILE_SIZE-1, cy, 2, 2,
1569                   COLOUR(index(state,corners,x+1,y)));
1570     if (y+1 < state->h && index(state,corners,x,y+1))
1571         draw_rect(fe, cx, cy+TILE_SIZE-1, 2, 2,
1572                   COLOUR(index(state,corners,x,y+1)));
1573     if (x+1 < state->w && y+1 < state->h && index(state,corners,x+1,y+1))
1574         draw_rect(fe, cx+TILE_SIZE-1, cy+TILE_SIZE-1, 2, 2,
1575                   COLOUR(index(state,corners,x+1,y+1)));
1576
1577     draw_update(fe, cx, cy, TILE_SIZE+1, TILE_SIZE+1);
1578 }
1579
1580 static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
1581                  game_state *state, int dir, game_ui *ui,
1582                  float animtime, float flashtime)
1583 {
1584     int x, y;
1585     unsigned char *correct;
1586     unsigned char *hedge, *vedge, *corners;
1587
1588     correct = get_correct(state);
1589
1590     if (ui->dragged) {
1591         hedge = snewn(state->w*state->h, unsigned char);
1592         vedge = snewn(state->w*state->h, unsigned char);
1593         memcpy(hedge, state->hedge, state->w*state->h);
1594         memcpy(vedge, state->vedge, state->w*state->h);
1595         ui_draw_rect(state, ui, hedge, vedge, 2);
1596     } else {
1597         hedge = state->hedge;
1598         vedge = state->vedge;
1599     }
1600
1601     corners = snewn(state->w * state->h, unsigned char);
1602     memset(corners, 0, state->w * state->h);
1603     for (x = 0; x < state->w; x++)
1604         for (y = 0; y < state->h; y++) {
1605             if (x > 0) {
1606                 int e = index(state, vedge, x, y);
1607                 if (index(state,corners,x,y) < e)
1608                     index(state,corners,x,y) = e;
1609                 if (y+1 < state->h &&
1610                     index(state,corners,x,y+1) < e)
1611                     index(state,corners,x,y+1) = e;
1612             }
1613             if (y > 0) {
1614                 int e = index(state, hedge, x, y);
1615                 if (index(state,corners,x,y) < e)
1616                     index(state,corners,x,y) = e;
1617                 if (x+1 < state->w &&
1618                     index(state,corners,x+1,y) < e)
1619                     index(state,corners,x+1,y) = e;
1620             }
1621         }
1622
1623     if (!ds->started) {
1624         draw_rect(fe, 0, 0,
1625                   state->w * TILE_SIZE + 2*BORDER + 1,
1626                   state->h * TILE_SIZE + 2*BORDER + 1, COL_BACKGROUND);
1627         draw_rect(fe, COORD(0)-1, COORD(0)-1,
1628                   ds->w*TILE_SIZE+3, ds->h*TILE_SIZE+3, COL_LINE);
1629         ds->started = TRUE;
1630         draw_update(fe, 0, 0,
1631                     state->w * TILE_SIZE + 2*BORDER + 1,
1632                     state->h * TILE_SIZE + 2*BORDER + 1);
1633     }
1634
1635     for (x = 0; x < state->w; x++)
1636         for (y = 0; y < state->h; y++) {
1637             unsigned int c = 0;
1638
1639             if (HRANGE(state,x,y))
1640                 c |= index(state,hedge,x,y);
1641             if (HRANGE(state,x,y+1))
1642                 c |= index(state,hedge,x,y+1) << 2;
1643             if (VRANGE(state,x,y))
1644                 c |= index(state,vedge,x,y) << 4;
1645             if (VRANGE(state,x+1,y))
1646                 c |= index(state,vedge,x+1,y) << 6;
1647             c |= index(state,corners,x,y) << 8;
1648             if (x+1 < state->w)
1649                 c |= index(state,corners,x+1,y) << 10;
1650             if (y+1 < state->h)
1651                 c |= index(state,corners,x,y+1) << 12;
1652             if (x+1 < state->w && y+1 < state->h)
1653                 c |= index(state,corners,x+1,y+1) << 14;
1654             if (index(state, correct, x, y) && !flashtime)
1655                 c |= CORRECT;
1656
1657             if (index(ds,ds->visible,x,y) != c) {
1658                 draw_tile(fe, state, x, y, hedge, vedge, corners, c & CORRECT);
1659                 index(ds,ds->visible,x,y) = c;
1660             }
1661         }
1662
1663     if (hedge != state->hedge) {
1664         sfree(hedge);
1665         sfree(vedge);
1666    }
1667
1668     sfree(corners);
1669     sfree(correct);
1670 }
1671
1672 static float game_anim_length(game_state *oldstate,
1673                               game_state *newstate, int dir)
1674 {
1675     return 0.0F;
1676 }
1677
1678 static float game_flash_length(game_state *oldstate,
1679                                game_state *newstate, int dir)
1680 {
1681     if (!oldstate->completed && newstate->completed)
1682         return FLASH_TIME;
1683     return 0.0F;
1684 }
1685
1686 static int game_wants_statusbar(void)
1687 {
1688     return FALSE;
1689 }
1690
1691 #ifdef COMBINED
1692 #define thegame rect
1693 #endif
1694
1695 const struct game thegame = {
1696     "Rectangles", "games.rectangles",
1697     default_params,
1698     game_fetch_preset,
1699     decode_params,
1700     encode_params,
1701     free_params,
1702     dup_params,
1703     TRUE, game_configure, custom_params,
1704     validate_params,
1705     new_game_seed,
1706     validate_seed,
1707     new_game,
1708     dup_game,
1709     free_game,
1710     TRUE, game_text_format,
1711     new_ui,
1712     free_ui,
1713     make_move,
1714     game_size,
1715     game_colours,
1716     game_new_drawstate,
1717     game_free_drawstate,
1718     game_redraw,
1719     game_anim_length,
1720     game_flash_length,
1721     game_wants_statusbar,
1722 };