2 * pattern.c: the pattern-reconstruction game known as `nonograms'.
6 * - make some sort of stab at number-of-numbers judgment
18 #define max(x,y) ( (x)>(y) ? (x):(y) )
19 #define min(x,y) ( (x)<(y) ? (x):(y) )
31 #define TLBORDER(d) ( (d) / 5 + 2 )
35 #define FROMCOORD(d, x) \
36 ( ((x) - (BORDER + GUTTER + TILE_SIZE * TLBORDER(d))) / TILE_SIZE )
38 #define SIZE(d) (2*BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (d)))
40 #define TOCOORD(d, x) (BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (x)))
46 #define GRID_UNKNOWN 2
54 int *rowdata
, *rowlen
;
58 #define FLASH_TIME 0.13F
60 static game_params
*default_params(void)
62 game_params
*ret
= snew(game_params
);
69 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
73 static const struct { int x
, y
; } values
[] = {
81 if (i
< 0 || i
>= lenof(values
))
84 ret
= snew(game_params
);
88 sprintf(str
, "%dx%d", ret
->w
, ret
->h
);
95 static void free_params(game_params
*params
)
100 static game_params
*dup_params(game_params
*params
)
102 game_params
*ret
= snew(game_params
);
103 *ret
= *params
; /* structure copy */
107 static game_params
*decode_params(char const *string
)
109 game_params
*ret
= default_params();
110 char const *p
= string
;
113 while (*p
&& isdigit(*p
)) p
++;
117 while (*p
&& isdigit(*p
)) p
++;
125 static char *encode_params(game_params
*params
)
130 len
= sprintf(ret
, "%dx%d", params
->w
, params
->h
);
131 assert(len
< lenof(ret
));
137 static config_item
*game_configure(game_params
*params
)
142 ret
= snewn(3, config_item
);
144 ret
[0].name
= "Width";
145 ret
[0].type
= C_STRING
;
146 sprintf(buf
, "%d", params
->w
);
147 ret
[0].sval
= dupstr(buf
);
150 ret
[1].name
= "Height";
151 ret
[1].type
= C_STRING
;
152 sprintf(buf
, "%d", params
->h
);
153 ret
[1].sval
= dupstr(buf
);
164 static game_params
*custom_params(config_item
*cfg
)
166 game_params
*ret
= snew(game_params
);
168 ret
->w
= atoi(cfg
[0].sval
);
169 ret
->h
= atoi(cfg
[1].sval
);
174 static char *validate_params(game_params
*params
)
176 if (params
->w
<= 0 && params
->h
<= 0)
177 return "Width and height must both be greater than zero";
179 return "Width must be greater than zero";
181 return "Height must be greater than zero";
185 /* ----------------------------------------------------------------------
186 * Puzzle generation code.
188 * For this particular puzzle, it seemed important to me to ensure
189 * a unique solution. I do this the brute-force way, by having a
190 * solver algorithm alongside the generator, and repeatedly
191 * generating a random grid until I find one whose solution is
192 * unique. It turns out that this isn't too onerous on a modern PC
193 * provided you keep grid size below around 30. Any offers of
194 * better algorithms, however, will be very gratefully received.
196 * Another annoyance of this approach is that it limits the
197 * available puzzles to those solvable by the algorithm I've used.
198 * My algorithm only ever considers a single row or column at any
199 * one time, which means it's incapable of solving the following
200 * difficult example (found by Bella Image around 1995/6, when she
201 * and I were both doing maths degrees):
215 * Obviously this cannot be solved by a one-row-or-column-at-a-time
216 * algorithm (it would require at least one row or column reading
217 * `2 1', `1 2', `3' or `4' to get started). However, it can be
218 * proved to have a unique solution: if the top left square were
219 * empty, then the only option for the top row would be to fill the
220 * two squares in the 1 columns, which would imply the squares
221 * below those were empty, leaving no place for the 2 in the second
222 * row. Contradiction. Hence the top left square is full, and the
223 * unique solution follows easily from that starting point.
225 * (The game ID for this puzzle is 4x4:2/1/2/1/1.1/2/1/1 , in case
226 * it's useful to anyone.)
229 static int float_compare(const void *av
, const void *bv
)
231 const float *a
= (const float *)av
;
232 const float *b
= (const float *)bv
;
241 static void generate(random_state
*rs
, int w
, int h
, unsigned char *retgrid
)
248 fgrid
= snewn(w
*h
, float);
250 for (i
= 0; i
< h
; i
++) {
251 for (j
= 0; j
< w
; j
++) {
252 fgrid
[i
*w
+j
] = random_upto(rs
, 100000000UL) / 100000000.F
;
257 * The above gives a completely random splattering of black and
258 * white cells. We want to gently bias this in favour of _some_
259 * reasonably thick areas of white and black, while retaining
260 * some randomness and fine detail.
262 * So we evolve the starting grid using a cellular automaton.
263 * Currently, I'm doing something very simple indeed, which is
264 * to set each square to the average of the surrounding nine
265 * cells (or the average of fewer, if we're on a corner).
267 for (step
= 0; step
< 1; step
++) {
268 fgrid2
= snewn(w
*h
, float);
270 for (i
= 0; i
< h
; i
++) {
271 for (j
= 0; j
< w
; j
++) {
276 * Compute the average of the surrounding cells.
280 for (p
= -1; p
<= +1; p
++) {
281 for (q
= -1; q
<= +1; q
++) {
282 if (i
+p
< 0 || i
+p
>= h
|| j
+q
< 0 || j
+q
>= w
)
285 sx
+= fgrid
[(i
+p
)*w
+(j
+q
)];
290 fgrid2
[i
*w
+j
] = xbar
;
298 fgrid2
= snewn(w
*h
, float);
299 memcpy(fgrid2
, fgrid
, w
*h
*sizeof(float));
300 qsort(fgrid2
, w
*h
, sizeof(float), float_compare
);
301 threshold
= fgrid2
[w
*h
/2];
304 for (i
= 0; i
< h
; i
++) {
305 for (j
= 0; j
< w
; j
++) {
306 retgrid
[i
*w
+j
] = (fgrid
[i
*w
+j
] > threshold ? GRID_FULL
:
314 static int compute_rowdata(int *ret
, unsigned char *start
, int len
, int step
)
320 for (i
= 0; i
< len
; i
++) {
321 if (start
[i
*step
] == GRID_FULL
) {
323 while (i
+runlen
< len
&& start
[(i
+runlen
)*step
] == GRID_FULL
)
329 if (i
< len
&& start
[i
*step
] == GRID_UNKNOWN
)
339 #define STILL_UNKNOWN 3
341 static void do_recurse(unsigned char *known
, unsigned char *deduced
,
342 unsigned char *row
, int *data
, int len
,
343 int freespace
, int ndone
, int lowest
)
348 for (i
=0; i
<=freespace
; i
++) {
350 for (k
=0; k
<i
; k
++) row
[j
++] = DOT
;
351 for (k
=0; k
<data
[ndone
]; k
++) row
[j
++] = BLOCK
;
352 if (j
< len
) row
[j
++] = DOT
;
353 do_recurse(known
, deduced
, row
, data
, len
,
354 freespace
-i
, ndone
+1, j
);
357 for (i
=lowest
; i
<len
; i
++)
359 for (i
=0; i
<len
; i
++)
360 if (known
[i
] && known
[i
] != row
[i
])
362 for (i
=0; i
<len
; i
++)
363 deduced
[i
] |= row
[i
];
367 static int do_row(unsigned char *known
, unsigned char *deduced
,
369 unsigned char *start
, int len
, int step
, int *data
)
371 int rowlen
, i
, freespace
, done_any
;
374 for (rowlen
= 0; data
[rowlen
]; rowlen
++)
375 freespace
-= data
[rowlen
]+1;
377 for (i
= 0; i
< len
; i
++) {
378 known
[i
] = start
[i
*step
];
382 do_recurse(known
, deduced
, row
, data
, len
, freespace
, 0, 0);
384 for (i
=0; i
<len
; i
++)
385 if (deduced
[i
] && deduced
[i
] != STILL_UNKNOWN
&& !known
[i
]) {
386 start
[i
*step
] = deduced
[i
];
392 static unsigned char *generate_soluble(random_state
*rs
, int w
, int h
)
394 int i
, j
, done_any
, ok
, ntries
, max
;
395 unsigned char *grid
, *matrix
, *workspace
;
398 grid
= snewn(w
*h
, unsigned char);
399 matrix
= snewn(w
*h
, unsigned char);
401 workspace
= snewn(max
*3, unsigned char);
402 rowdata
= snewn(max
+1, int);
409 generate(rs
, w
, h
, grid
);
411 memset(matrix
, 0, w
*h
);
415 for (i
=0; i
<h
; i
++) {
416 rowdata
[compute_rowdata(rowdata
, grid
+i
*w
, w
, 1)] = 0;
417 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
418 matrix
+i
*w
, w
, 1, rowdata
);
420 for (i
=0; i
<w
; i
++) {
421 rowdata
[compute_rowdata(rowdata
, grid
+i
, h
, w
)] = 0;
422 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
423 matrix
+i
, h
, w
, rowdata
);
428 for (i
=0; i
<h
; i
++) {
429 for (j
=0; j
<w
; j
++) {
430 if (matrix
[i
*w
+j
] == UNKNOWN
)
442 static char *new_game_seed(game_params
*params
, random_state
*rs
)
445 int i
, j
, max
, rowlen
, *rowdata
;
446 char intbuf
[80], *seed
;
447 int seedlen
, seedpos
;
449 grid
= generate_soluble(rs
, params
->w
, params
->h
);
450 max
= max(params
->w
, params
->h
);
451 rowdata
= snewn(max
, int);
454 * Seed is a slash-separated list of row contents; each row
455 * contents section is a dot-separated list of integers. Row
456 * contents are listed in the order (columns left to right,
457 * then rows top to bottom).
459 * Simplest way to handle memory allocation is to make two
460 * passes, first computing the seed size and then writing it
464 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
466 rowlen
= compute_rowdata(rowdata
, grid
+i
, params
->h
, params
->w
);
468 rowlen
= compute_rowdata(rowdata
, grid
+(i
-params
->w
)*params
->w
,
471 for (j
= 0; j
< rowlen
; j
++) {
472 seedlen
+= 1 + sprintf(intbuf
, "%d", rowdata
[j
]);
478 seed
= snewn(seedlen
, char);
480 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
482 rowlen
= compute_rowdata(rowdata
, grid
+i
, params
->h
, params
->w
);
484 rowlen
= compute_rowdata(rowdata
, grid
+(i
-params
->w
)*params
->w
,
487 for (j
= 0; j
< rowlen
; j
++) {
488 int len
= sprintf(seed
+seedpos
, "%d", rowdata
[j
]);
490 seed
[seedpos
+ len
] = '.';
492 seed
[seedpos
+ len
] = '/';
496 seed
[seedpos
++] = '/';
499 assert(seedpos
== seedlen
);
500 assert(seed
[seedlen
-1] == '/');
501 seed
[seedlen
-1] = '\0';
506 static char *validate_seed(game_params
*params
, char *seed
)
511 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
513 rowspace
= params
->h
+ 1;
515 rowspace
= params
->w
+ 1;
517 if (*seed
&& isdigit((unsigned char)*seed
)) {
520 while (seed
&& isdigit((unsigned char)*seed
)) seed
++;
526 return "at least one column contains more numbers than will fit";
528 return "at least one row contains more numbers than will fit";
530 } while (*seed
++ == '.');
532 seed
++; /* expect a slash immediately */
535 if (seed
[-1] == '/') {
536 if (i
+1 == params
->w
+ params
->h
)
537 return "too many row/column specifications";
538 } else if (seed
[-1] == '\0') {
539 if (i
+1 < params
->w
+ params
->h
)
540 return "too few row/column specifications";
542 return "unrecognised character in game specification";
548 static game_state
*new_game(game_params
*params
, char *seed
)
552 game_state
*state
= snew(game_state
);
554 state
->w
= params
->w
;
555 state
->h
= params
->h
;
557 state
->grid
= snewn(state
->w
* state
->h
, unsigned char);
558 memset(state
->grid
, GRID_UNKNOWN
, state
->w
* state
->h
);
560 state
->rowsize
= max(state
->w
, state
->h
);
561 state
->rowdata
= snewn(state
->rowsize
* (state
->w
+ state
->h
), int);
562 state
->rowlen
= snewn(state
->w
+ state
->h
, int);
564 state
->completed
= FALSE
;
566 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
567 state
->rowlen
[i
] = 0;
568 if (*seed
&& isdigit((unsigned char)*seed
)) {
571 while (seed
&& isdigit((unsigned char)*seed
)) seed
++;
572 state
->rowdata
[state
->rowsize
* i
+ state
->rowlen
[i
]++] =
574 } while (*seed
++ == '.');
576 seed
++; /* expect a slash immediately */
583 static game_state
*dup_game(game_state
*state
)
585 game_state
*ret
= snew(game_state
);
590 ret
->grid
= snewn(ret
->w
* ret
->h
, unsigned char);
591 memcpy(ret
->grid
, state
->grid
, ret
->w
* ret
->h
);
593 ret
->rowsize
= state
->rowsize
;
594 ret
->rowdata
= snewn(ret
->rowsize
* (ret
->w
+ ret
->h
), int);
595 ret
->rowlen
= snewn(ret
->w
+ ret
->h
, int);
596 memcpy(ret
->rowdata
, state
->rowdata
,
597 ret
->rowsize
* (ret
->w
+ ret
->h
) * sizeof(int));
598 memcpy(ret
->rowlen
, state
->rowlen
,
599 (ret
->w
+ ret
->h
) * sizeof(int));
601 ret
->completed
= state
->completed
;
606 static void free_game(game_state
*state
)
608 sfree(state
->rowdata
);
609 sfree(state
->rowlen
);
620 int drag
, release
, state
;
623 static game_ui
*new_ui(game_state
*state
)
628 ret
->dragging
= FALSE
;
633 static void free_ui(game_ui
*ui
)
638 static game_state
*make_move(game_state
*from
, game_ui
*ui
,
639 int x
, int y
, int button
)
643 x
= FROMCOORD(from
->w
, x
);
644 y
= FROMCOORD(from
->h
, y
);
646 if (x
>= 0 && x
< from
->w
&& y
>= 0 && y
< from
->h
&&
647 (button
== LEFT_BUTTON
|| button
== RIGHT_BUTTON
||
648 button
== MIDDLE_BUTTON
)) {
652 if (button
== LEFT_BUTTON
) {
653 ui
->drag
= LEFT_DRAG
;
654 ui
->release
= LEFT_RELEASE
;
655 ui
->state
= GRID_FULL
;
656 } else if (button
== RIGHT_BUTTON
) {
657 ui
->drag
= RIGHT_DRAG
;
658 ui
->release
= RIGHT_RELEASE
;
659 ui
->state
= GRID_EMPTY
;
660 } else /* if (button == MIDDLE_BUTTON) */ {
661 ui
->drag
= MIDDLE_DRAG
;
662 ui
->release
= MIDDLE_RELEASE
;
663 ui
->state
= GRID_UNKNOWN
;
666 ui
->drag_start_x
= ui
->drag_end_x
= x
;
667 ui
->drag_start_y
= ui
->drag_end_y
= y
;
669 return from
; /* UI activity occurred */
672 if (ui
->dragging
&& button
== ui
->drag
) {
674 * There doesn't seem much point in allowing a rectangle
675 * drag; people will generally only want to drag a single
676 * horizontal or vertical line, so we make that easy by
679 * Exception: if we're _middle_-button dragging to tag
680 * things as UNKNOWN, we may well want to trash an entire
681 * area and start over!
683 if (ui
->state
!= GRID_UNKNOWN
) {
684 if (abs(x
- ui
->drag_start_x
) > abs(y
- ui
->drag_start_y
))
685 y
= ui
->drag_start_y
;
687 x
= ui
->drag_start_x
;
692 if (x
>= from
->w
) x
= from
->w
- 1;
693 if (y
>= from
->h
) y
= from
->h
- 1;
698 return from
; /* UI activity occurred */
701 if (ui
->dragging
&& button
== ui
->release
) {
702 int x1
, x2
, y1
, y2
, xx
, yy
;
703 int move_needed
= FALSE
;
705 x1
= min(ui
->drag_start_x
, ui
->drag_end_x
);
706 x2
= max(ui
->drag_start_x
, ui
->drag_end_x
);
707 y1
= min(ui
->drag_start_y
, ui
->drag_end_y
);
708 y2
= max(ui
->drag_start_y
, ui
->drag_end_y
);
710 for (yy
= y1
; yy
<= y2
; yy
++)
711 for (xx
= x1
; xx
<= x2
; xx
++)
712 if (from
->grid
[yy
* from
->w
+ xx
] != ui
->state
)
715 ui
->dragging
= FALSE
;
718 ret
= dup_game(from
);
719 for (yy
= y1
; yy
<= y2
; yy
++)
720 for (xx
= x1
; xx
<= x2
; xx
++)
721 ret
->grid
[yy
* ret
->w
+ xx
] = ui
->state
;
724 * An actual change, so check to see if we've completed
727 if (!ret
->completed
) {
728 int *rowdata
= snewn(ret
->rowsize
, int);
731 ret
->completed
= TRUE
;
733 for (i
=0; i
<ret
->w
; i
++) {
734 len
= compute_rowdata(rowdata
,
735 ret
->grid
+i
, ret
->h
, ret
->w
);
736 if (len
!= ret
->rowlen
[i
] ||
737 memcmp(ret
->rowdata
+i
*ret
->rowsize
, rowdata
,
738 len
* sizeof(int))) {
739 ret
->completed
= FALSE
;
743 for (i
=0; i
<ret
->h
; i
++) {
744 len
= compute_rowdata(rowdata
,
745 ret
->grid
+i
*ret
->w
, ret
->w
, 1);
746 if (len
!= ret
->rowlen
[i
+ret
->w
] ||
747 memcmp(ret
->rowdata
+(i
+ret
->w
)*ret
->rowsize
, rowdata
,
748 len
* sizeof(int))) {
749 ret
->completed
= FALSE
;
759 return from
; /* UI activity occurred */
765 /* ----------------------------------------------------------------------
769 struct game_drawstate
{
772 unsigned char *visible
;
775 static void game_size(game_params
*params
, int *x
, int *y
)
777 *x
= SIZE(params
->w
);
778 *y
= SIZE(params
->h
);
781 static float *game_colours(frontend
*fe
, game_state
*state
, int *ncolours
)
783 float *ret
= snewn(3 * NCOLOURS
, float);
785 frontend_default_colour(fe
, &ret
[COL_BACKGROUND
* 3]);
787 ret
[COL_GRID
* 3 + 0] = 0.3F
;
788 ret
[COL_GRID
* 3 + 1] = 0.3F
;
789 ret
[COL_GRID
* 3 + 2] = 0.3F
;
791 ret
[COL_UNKNOWN
* 3 + 0] = 0.5F
;
792 ret
[COL_UNKNOWN
* 3 + 1] = 0.5F
;
793 ret
[COL_UNKNOWN
* 3 + 2] = 0.5F
;
795 ret
[COL_FULL
* 3 + 0] = 0.0F
;
796 ret
[COL_FULL
* 3 + 1] = 0.0F
;
797 ret
[COL_FULL
* 3 + 2] = 0.0F
;
799 ret
[COL_EMPTY
* 3 + 0] = 1.0F
;
800 ret
[COL_EMPTY
* 3 + 1] = 1.0F
;
801 ret
[COL_EMPTY
* 3 + 2] = 1.0F
;
803 *ncolours
= NCOLOURS
;
807 static game_drawstate
*game_new_drawstate(game_state
*state
)
809 struct game_drawstate
*ds
= snew(struct game_drawstate
);
814 ds
->visible
= snewn(ds
->w
* ds
->h
, unsigned char);
815 memset(ds
->visible
, 255, ds
->w
* ds
->h
);
820 static void game_free_drawstate(game_drawstate
*ds
)
826 static void grid_square(frontend
*fe
, game_drawstate
*ds
,
827 int y
, int x
, int state
)
831 draw_rect(fe
, TOCOORD(ds
->w
, x
), TOCOORD(ds
->h
, y
),
832 TILE_SIZE
, TILE_SIZE
, COL_GRID
);
834 xl
= (x
% 5 == 0 ?
1 : 0);
835 yt
= (y
% 5 == 0 ?
1 : 0);
836 xr
= (x
% 5 == 4 || x
== ds
->w
-1 ?
1 : 0);
837 yb
= (y
% 5 == 4 || y
== ds
->h
-1 ?
1 : 0);
839 draw_rect(fe
, TOCOORD(ds
->w
, x
) + 1 + xl
, TOCOORD(ds
->h
, y
) + 1 + yt
,
840 TILE_SIZE
- xl
- xr
- 1, TILE_SIZE
- yt
- yb
- 1,
841 (state
== GRID_FULL ? COL_FULL
:
842 state
== GRID_EMPTY ? COL_EMPTY
: COL_UNKNOWN
));
844 draw_update(fe
, TOCOORD(ds
->w
, x
), TOCOORD(ds
->h
, y
),
845 TILE_SIZE
, TILE_SIZE
);
848 static void game_redraw(frontend
*fe
, game_drawstate
*ds
, game_state
*oldstate
,
849 game_state
*state
, int dir
, game_ui
*ui
,
850 float animtime
, float flashtime
)
857 * The initial contents of the window are not guaranteed
858 * and can vary with front ends. To be on the safe side,
859 * all games should start by drawing a big background-
860 * colour rectangle covering the whole window.
862 draw_rect(fe
, 0, 0, SIZE(ds
->w
), SIZE(ds
->h
), COL_BACKGROUND
);
867 for (i
= 0; i
< ds
->w
+ ds
->h
; i
++) {
868 int rowlen
= state
->rowlen
[i
];
869 int *rowdata
= state
->rowdata
+ state
->rowsize
* i
;
873 * Normally I space the numbers out by the same
874 * distance as the tile size. However, if there are
875 * more numbers than available spaces, I have to squash
878 nfit
= max(rowlen
, TLBORDER(ds
->h
))-1;
881 for (j
= 0; j
< rowlen
; j
++) {
886 x
= TOCOORD(ds
->w
, i
);
887 y
= BORDER
+ TILE_SIZE
* (TLBORDER(ds
->h
)-1);
888 y
-= ((rowlen
-j
-1)*TILE_SIZE
) * (TLBORDER(ds
->h
)-1) / nfit
;
890 y
= TOCOORD(ds
->h
, i
- ds
->w
);
891 x
= BORDER
+ TILE_SIZE
* (TLBORDER(ds
->w
)-1);
892 x
-= ((rowlen
-j
-1)*TILE_SIZE
) * (TLBORDER(ds
->h
)-1) / nfit
;
895 sprintf(str
, "%d", rowdata
[j
]);
896 draw_text(fe
, x
+TILE_SIZE
/2, y
+TILE_SIZE
/2, FONT_VARIABLE
,
897 TILE_SIZE
/2, ALIGN_HCENTRE
| ALIGN_VCENTRE
,
898 COL_FULL
, str
); /* FIXME: COL_TEXT */
903 * Draw the grid outline.
905 draw_rect(fe
, TOCOORD(ds
->w
, 0) - 1, TOCOORD(ds
->h
, 0) - 1,
906 ds
->w
* TILE_SIZE
+ 3, ds
->h
* TILE_SIZE
+ 3,
911 draw_update(fe
, 0, 0, SIZE(ds
->w
), SIZE(ds
->h
));
915 x1
= min(ui
->drag_start_x
, ui
->drag_end_x
);
916 x2
= max(ui
->drag_start_x
, ui
->drag_end_x
);
917 y1
= min(ui
->drag_start_y
, ui
->drag_end_y
);
918 y2
= max(ui
->drag_start_y
, ui
->drag_end_y
);
920 x1
= x2
= y1
= y2
= -1; /* placate gcc warnings */
924 * Now draw any grid squares which have changed since last
927 for (i
= 0; i
< ds
->h
; i
++) {
928 for (j
= 0; j
< ds
->w
; j
++) {
932 * Work out what state this square should be drawn in,
933 * taking any current drag operation into account.
935 if (ui
->dragging
&& x1
<= j
&& j
<= x2
&& y1
<= i
&& i
<= y2
)
938 val
= state
->grid
[i
* state
->w
+ j
];
941 * Briefly invert everything twice during a completion
945 (flashtime
<= FLASH_TIME
/3 || flashtime
>= FLASH_TIME
*2/3) &&
947 val
= (GRID_FULL
^ GRID_EMPTY
) ^ val
;
949 if (ds
->visible
[i
* ds
->w
+ j
] != val
) {
950 grid_square(fe
, ds
, i
, j
, val
);
951 ds
->visible
[i
* ds
->w
+ j
] = val
;
957 static float game_anim_length(game_state
*oldstate
,
958 game_state
*newstate
, int dir
)
963 static float game_flash_length(game_state
*oldstate
,
964 game_state
*newstate
, int dir
)
966 if (!oldstate
->completed
&& newstate
->completed
)
971 static int game_wants_statusbar(void)
977 #define thegame pattern
980 const struct game thegame
= {
981 "Pattern", "games.pattern", TRUE
,
1002 game_free_drawstate
,
1006 game_wants_statusbar
,