2 * pattern.c: the pattern-reconstruction game known as `nonograms'.
24 #define PREFERRED_TILE_SIZE 24
25 #define TILE_SIZE (ds->tilesize)
26 #define BORDER (3 * TILE_SIZE / 4)
27 #define TLBORDER(d) ( (d) / 5 + 2 )
28 #define GUTTER (TILE_SIZE / 2)
30 #define FROMCOORD(d, x) \
31 ( ((x) - (BORDER + GUTTER + TILE_SIZE * TLBORDER(d))) / TILE_SIZE )
33 #define SIZE(d) (2*BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (d)))
34 #define GETTILESIZE(d, w) ((double)w / (2.0 + (double)TLBORDER(d) + (double)(d)))
36 #define TOCOORD(d, x) (BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (x)))
42 #define GRID_UNKNOWN 2
50 int *rowdata
, *rowlen
;
51 int completed
, cheated
;
54 #define FLASH_TIME 0.13F
56 static game_params
*default_params(void)
58 game_params
*ret
= snew(game_params
);
65 static const struct game_params pattern_presets
[] = {
75 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
80 if (i
< 0 || i
>= lenof(pattern_presets
))
83 ret
= snew(game_params
);
84 *ret
= pattern_presets
[i
];
86 sprintf(str
, "%dx%d", ret
->w
, ret
->h
);
93 static void free_params(game_params
*params
)
98 static game_params
*dup_params(game_params
*params
)
100 game_params
*ret
= snew(game_params
);
101 *ret
= *params
; /* structure copy */
105 static void decode_params(game_params
*ret
, char const *string
)
107 char const *p
= string
;
110 while (*p
&& isdigit((unsigned char)*p
)) p
++;
114 while (*p
&& isdigit((unsigned char)*p
)) p
++;
120 static char *encode_params(game_params
*params
, int full
)
125 len
= sprintf(ret
, "%dx%d", params
->w
, params
->h
);
126 assert(len
< lenof(ret
));
132 static config_item
*game_configure(game_params
*params
)
137 ret
= snewn(3, config_item
);
139 ret
[0].name
= "Width";
140 ret
[0].type
= C_STRING
;
141 sprintf(buf
, "%d", params
->w
);
142 ret
[0].sval
= dupstr(buf
);
145 ret
[1].name
= "Height";
146 ret
[1].type
= C_STRING
;
147 sprintf(buf
, "%d", params
->h
);
148 ret
[1].sval
= dupstr(buf
);
159 static game_params
*custom_params(config_item
*cfg
)
161 game_params
*ret
= snew(game_params
);
163 ret
->w
= atoi(cfg
[0].sval
);
164 ret
->h
= atoi(cfg
[1].sval
);
169 static char *validate_params(game_params
*params
, int full
)
171 if (params
->w
<= 0 || params
->h
<= 0)
172 return "Width and height must both be greater than zero";
176 /* ----------------------------------------------------------------------
177 * Puzzle generation code.
179 * For this particular puzzle, it seemed important to me to ensure
180 * a unique solution. I do this the brute-force way, by having a
181 * solver algorithm alongside the generator, and repeatedly
182 * generating a random grid until I find one whose solution is
183 * unique. It turns out that this isn't too onerous on a modern PC
184 * provided you keep grid size below around 30. Any offers of
185 * better algorithms, however, will be very gratefully received.
187 * Another annoyance of this approach is that it limits the
188 * available puzzles to those solvable by the algorithm I've used.
189 * My algorithm only ever considers a single row or column at any
190 * one time, which means it's incapable of solving the following
191 * difficult example (found by Bella Image around 1995/6, when she
192 * and I were both doing maths degrees):
206 * Obviously this cannot be solved by a one-row-or-column-at-a-time
207 * algorithm (it would require at least one row or column reading
208 * `2 1', `1 2', `3' or `4' to get started). However, it can be
209 * proved to have a unique solution: if the top left square were
210 * empty, then the only option for the top row would be to fill the
211 * two squares in the 1 columns, which would imply the squares
212 * below those were empty, leaving no place for the 2 in the second
213 * row. Contradiction. Hence the top left square is full, and the
214 * unique solution follows easily from that starting point.
216 * (The game ID for this puzzle is 4x4:2/1/2/1/1.1/2/1/1 , in case
217 * it's useful to anyone.)
220 static int float_compare(const void *av
, const void *bv
)
222 const float *a
= (const float *)av
;
223 const float *b
= (const float *)bv
;
232 static void generate(random_state
*rs
, int w
, int h
, unsigned char *retgrid
)
239 fgrid
= snewn(w
*h
, float);
241 for (i
= 0; i
< h
; i
++) {
242 for (j
= 0; j
< w
; j
++) {
243 fgrid
[i
*w
+j
] = random_upto(rs
, 100000000UL) / 100000000.F
;
248 * The above gives a completely random splattering of black and
249 * white cells. We want to gently bias this in favour of _some_
250 * reasonably thick areas of white and black, while retaining
251 * some randomness and fine detail.
253 * So we evolve the starting grid using a cellular automaton.
254 * Currently, I'm doing something very simple indeed, which is
255 * to set each square to the average of the surrounding nine
256 * cells (or the average of fewer, if we're on a corner).
258 for (step
= 0; step
< 1; step
++) {
259 fgrid2
= snewn(w
*h
, float);
261 for (i
= 0; i
< h
; i
++) {
262 for (j
= 0; j
< w
; j
++) {
267 * Compute the average of the surrounding cells.
271 for (p
= -1; p
<= +1; p
++) {
272 for (q
= -1; q
<= +1; q
++) {
273 if (i
+p
< 0 || i
+p
>= h
|| j
+q
< 0 || j
+q
>= w
)
276 * An additional special case not mentioned
277 * above: if a grid dimension is 2xn then
278 * we do not average across that dimension
279 * at all. Otherwise a 2x2 grid would
280 * contain four identical squares.
282 if ((h
==2 && p
!=0) || (w
==2 && q
!=0))
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
);
412 * The game is a bit too easy if any row or column is
413 * completely black or completely white. An exception is
414 * made for rows/columns that are under 3 squares,
415 * otherwise nothing will ever be successfully generated.
419 for (i
= 0; i
< h
; i
++) {
421 for (j
= 0; j
< w
; j
++)
422 colours
|= (grid
[i
*w
+j
] == GRID_FULL
? 2 : 1);
428 for (j
= 0; j
< w
; j
++) {
430 for (i
= 0; i
< h
; i
++)
431 colours
|= (grid
[i
*w
+j
] == GRID_FULL
? 2 : 1);
439 memset(matrix
, 0, w
*h
);
443 for (i
=0; i
<h
; i
++) {
444 rowdata
[compute_rowdata(rowdata
, grid
+i
*w
, w
, 1)] = 0;
445 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
446 matrix
+i
*w
, w
, 1, rowdata
);
448 for (i
=0; i
<w
; i
++) {
449 rowdata
[compute_rowdata(rowdata
, grid
+i
, h
, w
)] = 0;
450 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
451 matrix
+i
, h
, w
, rowdata
);
456 for (i
=0; i
<h
; i
++) {
457 for (j
=0; j
<w
; j
++) {
458 if (matrix
[i
*w
+j
] == UNKNOWN
)
470 static char *new_game_desc(game_params
*params
, random_state
*rs
,
471 char **aux
, int interactive
)
474 int i
, j
, max
, rowlen
, *rowdata
;
475 char intbuf
[80], *desc
;
476 int desclen
, descpos
;
478 grid
= generate_soluble(rs
, params
->w
, params
->h
);
479 max
= max(params
->w
, params
->h
);
480 rowdata
= snewn(max
, int);
483 * Save the solved game in aux.
486 char *ai
= snewn(params
->w
* params
->h
+ 2, char);
489 * String format is exactly the same as a solve move, so we
490 * can just dupstr this in solve_game().
495 for (i
= 0; i
< params
->w
* params
->h
; i
++)
496 ai
[i
+1] = grid
[i
] ? '1' : '0';
498 ai
[params
->w
* params
->h
+ 1] = '\0';
504 * Seed is a slash-separated list of row contents; each row
505 * contents section is a dot-separated list of integers. Row
506 * contents are listed in the order (columns left to right,
507 * then rows top to bottom).
509 * Simplest way to handle memory allocation is to make two
510 * passes, first computing the seed size and then writing it
514 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
516 rowlen
= compute_rowdata(rowdata
, grid
+i
, params
->h
, params
->w
);
518 rowlen
= compute_rowdata(rowdata
, grid
+(i
-params
->w
)*params
->w
,
521 for (j
= 0; j
< rowlen
; j
++) {
522 desclen
+= 1 + sprintf(intbuf
, "%d", rowdata
[j
]);
528 desc
= snewn(desclen
, char);
530 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
532 rowlen
= compute_rowdata(rowdata
, grid
+i
, params
->h
, params
->w
);
534 rowlen
= compute_rowdata(rowdata
, grid
+(i
-params
->w
)*params
->w
,
537 for (j
= 0; j
< rowlen
; j
++) {
538 int len
= sprintf(desc
+descpos
, "%d", rowdata
[j
]);
540 desc
[descpos
+ len
] = '.';
542 desc
[descpos
+ len
] = '/';
546 desc
[descpos
++] = '/';
549 assert(descpos
== desclen
);
550 assert(desc
[desclen
-1] == '/');
551 desc
[desclen
-1] = '\0';
557 static char *validate_desc(game_params
*params
, char *desc
)
562 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
564 rowspace
= params
->h
+ 1;
566 rowspace
= params
->w
+ 1;
568 if (*desc
&& isdigit((unsigned char)*desc
)) {
571 while (desc
&& isdigit((unsigned char)*desc
)) desc
++;
577 return "at least one column contains more numbers than will fit";
579 return "at least one row contains more numbers than will fit";
581 } while (*desc
++ == '.');
583 desc
++; /* expect a slash immediately */
586 if (desc
[-1] == '/') {
587 if (i
+1 == params
->w
+ params
->h
)
588 return "too many row/column specifications";
589 } else if (desc
[-1] == '\0') {
590 if (i
+1 < params
->w
+ params
->h
)
591 return "too few row/column specifications";
593 return "unrecognised character in game specification";
599 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
603 game_state
*state
= snew(game_state
);
605 state
->w
= params
->w
;
606 state
->h
= params
->h
;
608 state
->grid
= snewn(state
->w
* state
->h
, unsigned char);
609 memset(state
->grid
, GRID_UNKNOWN
, state
->w
* state
->h
);
611 state
->rowsize
= max(state
->w
, state
->h
);
612 state
->rowdata
= snewn(state
->rowsize
* (state
->w
+ state
->h
), int);
613 state
->rowlen
= snewn(state
->w
+ state
->h
, int);
615 state
->completed
= state
->cheated
= FALSE
;
617 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
618 state
->rowlen
[i
] = 0;
619 if (*desc
&& isdigit((unsigned char)*desc
)) {
622 while (desc
&& isdigit((unsigned char)*desc
)) desc
++;
623 state
->rowdata
[state
->rowsize
* i
+ state
->rowlen
[i
]++] =
625 } while (*desc
++ == '.');
627 desc
++; /* expect a slash immediately */
634 static game_state
*dup_game(game_state
*state
)
636 game_state
*ret
= snew(game_state
);
641 ret
->grid
= snewn(ret
->w
* ret
->h
, unsigned char);
642 memcpy(ret
->grid
, state
->grid
, ret
->w
* ret
->h
);
644 ret
->rowsize
= state
->rowsize
;
645 ret
->rowdata
= snewn(ret
->rowsize
* (ret
->w
+ ret
->h
), int);
646 ret
->rowlen
= snewn(ret
->w
+ ret
->h
, int);
647 memcpy(ret
->rowdata
, state
->rowdata
,
648 ret
->rowsize
* (ret
->w
+ ret
->h
) * sizeof(int));
649 memcpy(ret
->rowlen
, state
->rowlen
,
650 (ret
->w
+ ret
->h
) * sizeof(int));
652 ret
->completed
= state
->completed
;
653 ret
->cheated
= state
->cheated
;
658 static void free_game(game_state
*state
)
660 sfree(state
->rowdata
);
661 sfree(state
->rowlen
);
666 static char *solve_game(game_state
*state
, game_state
*currstate
,
667 char *ai
, char **error
)
669 unsigned char *matrix
;
670 int w
= state
->w
, h
= state
->h
;
674 unsigned char *workspace
;
678 * If we already have the solved state in ai, copy it out.
683 matrix
= snewn(w
*h
, unsigned char);
685 workspace
= snewn(max
*3, unsigned char);
686 rowdata
= snewn(max
+1, int);
688 memset(matrix
, 0, w
*h
);
692 for (i
=0; i
<h
; i
++) {
693 memcpy(rowdata
, state
->rowdata
+ state
->rowsize
*(w
+i
),
695 rowdata
[state
->rowlen
[w
+i
]] = 0;
696 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
697 matrix
+i
*w
, w
, 1, rowdata
);
699 for (i
=0; i
<w
; i
++) {
700 memcpy(rowdata
, state
->rowdata
+ state
->rowsize
*i
, max
*sizeof(int));
701 rowdata
[state
->rowlen
[i
]] = 0;
702 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
703 matrix
+i
, h
, w
, rowdata
);
710 for (i
= 0; i
< w
*h
; i
++) {
711 if (matrix
[i
] != BLOCK
&& matrix
[i
] != DOT
) {
713 *error
= "Solving algorithm cannot complete this puzzle";
718 ret
= snewn(w
*h
+2, char);
720 for (i
= 0; i
< w
*h
; i
++) {
721 assert(matrix
[i
] == BLOCK
|| matrix
[i
] == DOT
);
722 ret
[i
+1] = (matrix
[i
] == BLOCK
? '1' : '0');
731 static int game_can_format_as_text_now(game_params
*params
)
736 static char *game_text_format(game_state
*state
)
747 int drag
, release
, state
;
750 static game_ui
*new_ui(game_state
*state
)
755 ret
->dragging
= FALSE
;
760 static void free_ui(game_ui
*ui
)
765 static char *encode_ui(game_ui
*ui
)
770 static void decode_ui(game_ui
*ui
, char *encoding
)
774 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
775 game_state
*newstate
)
779 struct game_drawstate
{
783 unsigned char *visible
;
786 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
787 int x
, int y
, int button
)
791 x
= FROMCOORD(state
->w
, x
);
792 y
= FROMCOORD(state
->h
, y
);
794 if (x
>= 0 && x
< state
->w
&& y
>= 0 && y
< state
->h
&&
795 (button
== LEFT_BUTTON
|| button
== RIGHT_BUTTON
||
796 button
== MIDDLE_BUTTON
)) {
798 int currstate
= state
->grid
[y
* state
->w
+ x
];
803 if (button
== LEFT_BUTTON
) {
804 ui
->drag
= LEFT_DRAG
;
805 ui
->release
= LEFT_RELEASE
;
807 ui
->state
= currstate
== GRID_FULL
? GRID_UNKNOWN
: GRID_FULL
;
809 ui
->state
= GRID_FULL
;
811 } else if (button
== RIGHT_BUTTON
) {
812 ui
->drag
= RIGHT_DRAG
;
813 ui
->release
= RIGHT_RELEASE
;
815 ui
->state
= currstate
== GRID_EMPTY
? GRID_UNKNOWN
: GRID_EMPTY
;
817 ui
->state
= GRID_EMPTY
;
819 } else /* if (button == MIDDLE_BUTTON) */ {
820 ui
->drag
= MIDDLE_DRAG
;
821 ui
->release
= MIDDLE_RELEASE
;
822 ui
->state
= GRID_UNKNOWN
;
825 ui
->drag_start_x
= ui
->drag_end_x
= x
;
826 ui
->drag_start_y
= ui
->drag_end_y
= y
;
828 return ""; /* UI activity occurred */
831 if (ui
->dragging
&& button
== ui
->drag
) {
833 * There doesn't seem much point in allowing a rectangle
834 * drag; people will generally only want to drag a single
835 * horizontal or vertical line, so we make that easy by
838 * Exception: if we're _middle_-button dragging to tag
839 * things as UNKNOWN, we may well want to trash an entire
840 * area and start over!
842 if (ui
->state
!= GRID_UNKNOWN
) {
843 if (abs(x
- ui
->drag_start_x
) > abs(y
- ui
->drag_start_y
))
844 y
= ui
->drag_start_y
;
846 x
= ui
->drag_start_x
;
851 if (x
>= state
->w
) x
= state
->w
- 1;
852 if (y
>= state
->h
) y
= state
->h
- 1;
857 return ""; /* UI activity occurred */
860 if (ui
->dragging
&& button
== ui
->release
) {
861 int x1
, x2
, y1
, y2
, xx
, yy
;
862 int move_needed
= FALSE
;
864 x1
= min(ui
->drag_start_x
, ui
->drag_end_x
);
865 x2
= max(ui
->drag_start_x
, ui
->drag_end_x
);
866 y1
= min(ui
->drag_start_y
, ui
->drag_end_y
);
867 y2
= max(ui
->drag_start_y
, ui
->drag_end_y
);
869 for (yy
= y1
; yy
<= y2
; yy
++)
870 for (xx
= x1
; xx
<= x2
; xx
++)
871 if (state
->grid
[yy
* state
->w
+ xx
] != ui
->state
)
874 ui
->dragging
= FALSE
;
878 sprintf(buf
, "%c%d,%d,%d,%d",
879 (char)(ui
->state
== GRID_FULL
? 'F' :
880 ui
->state
== GRID_EMPTY
? 'E' : 'U'),
881 x1
, y1
, x2
-x1
+1, y2
-y1
+1);
884 return ""; /* UI activity occurred */
890 static game_state
*execute_move(game_state
*from
, char *move
)
893 int x1
, x2
, y1
, y2
, xx
, yy
;
896 if (move
[0] == 'S' && strlen(move
) == from
->w
* from
->h
+ 1) {
899 ret
= dup_game(from
);
901 for (i
= 0; i
< ret
->w
* ret
->h
; i
++)
902 ret
->grid
[i
] = (move
[i
+1] == '1' ? GRID_FULL
: GRID_EMPTY
);
904 ret
->completed
= ret
->cheated
= TRUE
;
907 } else if ((move
[0] == 'F' || move
[0] == 'E' || move
[0] == 'U') &&
908 sscanf(move
+1, "%d,%d,%d,%d", &x1
, &y1
, &x2
, &y2
) == 4 &&
909 x1
>= 0 && x2
>= 0 && x1
+x2
<= from
->w
&&
910 y1
>= 0 && y2
>= 0 && y1
+y2
<= from
->h
) {
914 val
= (move
[0] == 'F' ? GRID_FULL
:
915 move
[0] == 'E' ? GRID_EMPTY
: GRID_UNKNOWN
);
917 ret
= dup_game(from
);
918 for (yy
= y1
; yy
< y2
; yy
++)
919 for (xx
= x1
; xx
< x2
; xx
++)
920 ret
->grid
[yy
* ret
->w
+ xx
] = val
;
923 * An actual change, so check to see if we've completed the
926 if (!ret
->completed
) {
927 int *rowdata
= snewn(ret
->rowsize
, int);
930 ret
->completed
= TRUE
;
932 for (i
=0; i
<ret
->w
; i
++) {
933 len
= compute_rowdata(rowdata
,
934 ret
->grid
+i
, ret
->h
, ret
->w
);
935 if (len
!= ret
->rowlen
[i
] ||
936 memcmp(ret
->rowdata
+i
*ret
->rowsize
, rowdata
,
937 len
* sizeof(int))) {
938 ret
->completed
= FALSE
;
942 for (i
=0; i
<ret
->h
; i
++) {
943 len
= compute_rowdata(rowdata
,
944 ret
->grid
+i
*ret
->w
, ret
->w
, 1);
945 if (len
!= ret
->rowlen
[i
+ret
->w
] ||
946 memcmp(ret
->rowdata
+(i
+ret
->w
)*ret
->rowsize
, rowdata
,
947 len
* sizeof(int))) {
948 ret
->completed
= FALSE
;
961 /* ----------------------------------------------------------------------
965 static void game_compute_size(game_params
*params
, int tilesize
,
968 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
969 struct { int tilesize
; } ads
, *ds
= &ads
;
970 ads
.tilesize
= tilesize
;
972 *x
= SIZE(params
->w
);
973 *y
= SIZE(params
->h
);
976 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
977 game_params
*params
, int tilesize
)
979 ds
->tilesize
= tilesize
;
982 static float *game_colours(frontend
*fe
, int *ncolours
)
984 float *ret
= snewn(3 * NCOLOURS
, float);
986 frontend_default_colour(fe
, &ret
[COL_BACKGROUND
* 3]);
988 ret
[COL_GRID
* 3 + 0] = 0.3F
;
989 ret
[COL_GRID
* 3 + 1] = 0.3F
;
990 ret
[COL_GRID
* 3 + 2] = 0.3F
;
992 ret
[COL_UNKNOWN
* 3 + 0] = 0.5F
;
993 ret
[COL_UNKNOWN
* 3 + 1] = 0.5F
;
994 ret
[COL_UNKNOWN
* 3 + 2] = 0.5F
;
996 ret
[COL_TEXT
* 3 + 0] = 0.0F
;
997 ret
[COL_TEXT
* 3 + 1] = 0.0F
;
998 ret
[COL_TEXT
* 3 + 2] = 0.0F
;
1000 ret
[COL_FULL
* 3 + 0] = 0.0F
;
1001 ret
[COL_FULL
* 3 + 1] = 0.0F
;
1002 ret
[COL_FULL
* 3 + 2] = 0.0F
;
1004 ret
[COL_EMPTY
* 3 + 0] = 1.0F
;
1005 ret
[COL_EMPTY
* 3 + 1] = 1.0F
;
1006 ret
[COL_EMPTY
* 3 + 2] = 1.0F
;
1008 *ncolours
= NCOLOURS
;
1012 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1014 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1016 ds
->started
= FALSE
;
1019 ds
->visible
= snewn(ds
->w
* ds
->h
, unsigned char);
1020 ds
->tilesize
= 0; /* not decided yet */
1021 memset(ds
->visible
, 255, ds
->w
* ds
->h
);
1026 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1032 static void grid_square(drawing
*dr
, game_drawstate
*ds
,
1033 int y
, int x
, int state
)
1037 draw_rect(dr
, TOCOORD(ds
->w
, x
), TOCOORD(ds
->h
, y
),
1038 TILE_SIZE
, TILE_SIZE
, COL_GRID
);
1040 xl
= (x
% 5 == 0 ? 1 : 0);
1041 yt
= (y
% 5 == 0 ? 1 : 0);
1042 xr
= (x
% 5 == 4 || x
== ds
->w
-1 ? 1 : 0);
1043 yb
= (y
% 5 == 4 || y
== ds
->h
-1 ? 1 : 0);
1045 draw_rect(dr
, TOCOORD(ds
->w
, x
) + 1 + xl
, TOCOORD(ds
->h
, y
) + 1 + yt
,
1046 TILE_SIZE
- xl
- xr
- 1, TILE_SIZE
- yt
- yb
- 1,
1047 (state
== GRID_FULL
? COL_FULL
:
1048 state
== GRID_EMPTY
? COL_EMPTY
: COL_UNKNOWN
));
1050 draw_update(dr
, TOCOORD(ds
->w
, x
), TOCOORD(ds
->h
, y
),
1051 TILE_SIZE
, TILE_SIZE
);
1054 static void draw_numbers(drawing
*dr
, game_drawstate
*ds
, game_state
*state
,
1062 for (i
= 0; i
< state
->w
+ state
->h
; i
++) {
1063 int rowlen
= state
->rowlen
[i
];
1064 int *rowdata
= state
->rowdata
+ state
->rowsize
* i
;
1068 * Normally I space the numbers out by the same
1069 * distance as the tile size. However, if there are
1070 * more numbers than available spaces, I have to squash
1073 nfit
= max(rowlen
, TLBORDER(state
->h
))-1;
1076 for (j
= 0; j
< rowlen
; j
++) {
1081 x
= TOCOORD(state
->w
, i
);
1082 y
= BORDER
+ TILE_SIZE
* (TLBORDER(state
->h
)-1);
1083 y
-= ((rowlen
-j
-1)*TILE_SIZE
) * (TLBORDER(state
->h
)-1) / nfit
;
1085 y
= TOCOORD(state
->h
, i
- state
->w
);
1086 x
= BORDER
+ TILE_SIZE
* (TLBORDER(state
->w
)-1);
1087 x
-= ((rowlen
-j
-1)*TILE_SIZE
) * (TLBORDER(state
->h
)-1) / nfit
;
1090 sprintf(str
, "%d", rowdata
[j
]);
1091 draw_text(dr
, x
+TILE_SIZE
/2, y
+TILE_SIZE
/2, FONT_VARIABLE
,
1092 TILE_SIZE
/2, ALIGN_HCENTRE
| ALIGN_VCENTRE
, colour
, str
);
1097 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
1098 game_state
*state
, int dir
, game_ui
*ui
,
1099 float animtime
, float flashtime
)
1106 * The initial contents of the window are not guaranteed
1107 * and can vary with front ends. To be on the safe side,
1108 * all games should start by drawing a big background-
1109 * colour rectangle covering the whole window.
1111 draw_rect(dr
, 0, 0, SIZE(ds
->w
), SIZE(ds
->h
), COL_BACKGROUND
);
1116 draw_numbers(dr
, ds
, state
, COL_TEXT
);
1119 * Draw the grid outline.
1121 draw_rect(dr
, TOCOORD(ds
->w
, 0) - 1, TOCOORD(ds
->h
, 0) - 1,
1122 ds
->w
* TILE_SIZE
+ 3, ds
->h
* TILE_SIZE
+ 3,
1127 draw_update(dr
, 0, 0, SIZE(ds
->w
), SIZE(ds
->h
));
1131 x1
= min(ui
->drag_start_x
, ui
->drag_end_x
);
1132 x2
= max(ui
->drag_start_x
, ui
->drag_end_x
);
1133 y1
= min(ui
->drag_start_y
, ui
->drag_end_y
);
1134 y2
= max(ui
->drag_start_y
, ui
->drag_end_y
);
1136 x1
= x2
= y1
= y2
= -1; /* placate gcc warnings */
1140 * Now draw any grid squares which have changed since last
1143 for (i
= 0; i
< ds
->h
; i
++) {
1144 for (j
= 0; j
< ds
->w
; j
++) {
1148 * Work out what state this square should be drawn in,
1149 * taking any current drag operation into account.
1151 if (ui
->dragging
&& x1
<= j
&& j
<= x2
&& y1
<= i
&& i
<= y2
)
1154 val
= state
->grid
[i
* state
->w
+ j
];
1157 * Briefly invert everything twice during a completion
1160 if (flashtime
> 0 &&
1161 (flashtime
<= FLASH_TIME
/3 || flashtime
>= FLASH_TIME
*2/3) &&
1162 val
!= GRID_UNKNOWN
)
1163 val
= (GRID_FULL
^ GRID_EMPTY
) ^ val
;
1165 if (ds
->visible
[i
* ds
->w
+ j
] != val
) {
1166 grid_square(dr
, ds
, i
, j
, val
);
1167 ds
->visible
[i
* ds
->w
+ j
] = val
;
1173 static float game_anim_length(game_state
*oldstate
,
1174 game_state
*newstate
, int dir
, game_ui
*ui
)
1179 static float game_flash_length(game_state
*oldstate
,
1180 game_state
*newstate
, int dir
, game_ui
*ui
)
1182 if (!oldstate
->completed
&& newstate
->completed
&&
1183 !oldstate
->cheated
&& !newstate
->cheated
)
1188 static int game_timing_state(game_state
*state
, game_ui
*ui
)
1193 static void game_print_size(game_params
*params
, float *x
, float *y
)
1198 * I'll use 5mm squares by default.
1200 game_compute_size(params
, 500, &pw
, &ph
);
1205 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
1207 int w
= state
->w
, h
= state
->h
;
1208 int ink
= print_mono_colour(dr
, 0);
1211 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1212 game_drawstate ads
, *ds
= &ads
;
1213 game_set_size(dr
, ds
, NULL
, tilesize
);
1218 print_line_width(dr
, TILE_SIZE
/ 16);
1219 draw_rect_outline(dr
, TOCOORD(w
, 0), TOCOORD(h
, 0),
1220 w
*TILE_SIZE
, h
*TILE_SIZE
, ink
);
1225 for (x
= 1; x
< w
; x
++) {
1226 print_line_width(dr
, TILE_SIZE
/ (x
% 5 ? 128 : 24));
1227 draw_line(dr
, TOCOORD(w
, x
), TOCOORD(h
, 0),
1228 TOCOORD(w
, x
), TOCOORD(h
, h
), ink
);
1230 for (y
= 1; y
< h
; y
++) {
1231 print_line_width(dr
, TILE_SIZE
/ (y
% 5 ? 128 : 24));
1232 draw_line(dr
, TOCOORD(w
, 0), TOCOORD(h
, y
),
1233 TOCOORD(w
, w
), TOCOORD(h
, y
), ink
);
1239 draw_numbers(dr
, ds
, state
, ink
);
1244 print_line_width(dr
, TILE_SIZE
/ 128);
1245 for (y
= 0; y
< h
; y
++)
1246 for (x
= 0; x
< w
; x
++) {
1247 if (state
->grid
[y
*w
+x
] == GRID_FULL
)
1248 draw_rect(dr
, TOCOORD(w
, x
), TOCOORD(h
, y
),
1249 TILE_SIZE
, TILE_SIZE
, ink
);
1250 else if (state
->grid
[y
*w
+x
] == GRID_EMPTY
)
1251 draw_circle(dr
, TOCOORD(w
, x
) + TILE_SIZE
/2,
1252 TOCOORD(h
, y
) + TILE_SIZE
/2,
1253 TILE_SIZE
/12, ink
, ink
);
1258 #define thegame pattern
1261 const struct game thegame
= {
1262 "Pattern", "games.pattern", "pattern",
1269 TRUE
, game_configure
, custom_params
,
1277 FALSE
, game_can_format_as_text_now
, game_text_format
,
1285 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
1288 game_free_drawstate
,
1292 TRUE
, FALSE
, game_print_size
, game_print
,
1293 FALSE
, /* wants_statusbar */
1294 FALSE
, game_timing_state
,
1295 REQUIRE_RBUTTON
, /* flags */
1298 #ifdef STANDALONE_SOLVER
1300 int main(int argc
, char **argv
)
1304 char *id
= NULL
, *desc
, *err
;
1306 while (--argc
> 0) {
1309 fprintf(stderr
, "%s: unrecognised option `%s'\n", argv
[0], p
);
1317 fprintf(stderr
, "usage: %s <game_id>\n", argv
[0]);
1321 desc
= strchr(id
, ':');
1323 fprintf(stderr
, "%s: game id expects a colon in it\n", argv
[0]);
1328 p
= default_params();
1329 decode_params(p
, id
);
1330 err
= validate_desc(p
, desc
);
1332 fprintf(stderr
, "%s: %s\n", argv
[0], err
);
1335 s
= new_game(NULL
, p
, desc
);
1338 int w
= p
->w
, h
= p
->h
, i
, j
, done_any
, max
;
1339 unsigned char *matrix
, *workspace
;
1342 matrix
= snewn(w
*h
, unsigned char);
1344 workspace
= snewn(max
*3, unsigned char);
1345 rowdata
= snewn(max
+1, int);
1347 memset(matrix
, 0, w
*h
);
1351 for (i
=0; i
<h
; i
++) {
1352 memcpy(rowdata
, s
->rowdata
+ s
->rowsize
*(w
+i
),
1354 rowdata
[s
->rowlen
[w
+i
]] = 0;
1355 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
1356 matrix
+i
*w
, w
, 1, rowdata
);
1358 for (i
=0; i
<w
; i
++) {
1359 memcpy(rowdata
, s
->rowdata
+ s
->rowsize
*i
, max
*sizeof(int));
1360 rowdata
[s
->rowlen
[i
]] = 0;
1361 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
1362 matrix
+i
, h
, w
, rowdata
);
1366 for (i
= 0; i
< h
; i
++) {
1367 for (j
= 0; j
< w
; j
++) {
1368 int c
= (matrix
[i
*w
+j
] == UNKNOWN
? '?' :
1369 matrix
[i
*w
+j
] == BLOCK
? '#' :
1370 matrix
[i
*w
+j
] == DOT
? '.' :