2 * pattern.c: the pattern-reconstruction game known as `nonograms'.
23 #define PREFERRED_TILE_SIZE 24
24 #define TILE_SIZE (ds->tilesize)
25 #define BORDER (3 * TILE_SIZE / 4)
26 #define TLBORDER(d) ( (d) / 5 + 2 )
27 #define GUTTER (TILE_SIZE / 2)
29 #define FROMCOORD(d, x) \
30 ( ((x) - (BORDER + GUTTER + TILE_SIZE * TLBORDER(d))) / TILE_SIZE )
32 #define SIZE(d) (2*BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (d)))
33 #define GETTILESIZE(d, w) (w / (2 + TLBORDER(d) + (d)))
35 #define TOCOORD(d, x) (BORDER + GUTTER + TILE_SIZE * (TLBORDER(d) + (x)))
41 #define GRID_UNKNOWN 2
49 int *rowdata
, *rowlen
;
50 int completed
, cheated
;
53 #define FLASH_TIME 0.13F
55 static game_params
*default_params(void)
57 game_params
*ret
= snew(game_params
);
64 static const struct game_params pattern_presets
[] = {
74 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
79 if (i
< 0 || i
>= lenof(pattern_presets
))
82 ret
= snew(game_params
);
83 *ret
= pattern_presets
[i
];
85 sprintf(str
, "%dx%d", ret
->w
, ret
->h
);
92 static void free_params(game_params
*params
)
97 static game_params
*dup_params(game_params
*params
)
99 game_params
*ret
= snew(game_params
);
100 *ret
= *params
; /* structure copy */
104 static void decode_params(game_params
*ret
, char const *string
)
106 char const *p
= string
;
109 while (*p
&& isdigit(*p
)) p
++;
113 while (*p
&& isdigit(*p
)) p
++;
119 static char *encode_params(game_params
*params
, int full
)
124 len
= sprintf(ret
, "%dx%d", params
->w
, params
->h
);
125 assert(len
< lenof(ret
));
131 static config_item
*game_configure(game_params
*params
)
136 ret
= snewn(3, config_item
);
138 ret
[0].name
= "Width";
139 ret
[0].type
= C_STRING
;
140 sprintf(buf
, "%d", params
->w
);
141 ret
[0].sval
= dupstr(buf
);
144 ret
[1].name
= "Height";
145 ret
[1].type
= C_STRING
;
146 sprintf(buf
, "%d", params
->h
);
147 ret
[1].sval
= dupstr(buf
);
158 static game_params
*custom_params(config_item
*cfg
)
160 game_params
*ret
= snew(game_params
);
162 ret
->w
= atoi(cfg
[0].sval
);
163 ret
->h
= atoi(cfg
[1].sval
);
168 static char *validate_params(game_params
*params
)
170 if (params
->w
<= 0 || params
->h
<= 0)
171 return "Width and height must both be greater than zero";
175 /* ----------------------------------------------------------------------
176 * Puzzle generation code.
178 * For this particular puzzle, it seemed important to me to ensure
179 * a unique solution. I do this the brute-force way, by having a
180 * solver algorithm alongside the generator, and repeatedly
181 * generating a random grid until I find one whose solution is
182 * unique. It turns out that this isn't too onerous on a modern PC
183 * provided you keep grid size below around 30. Any offers of
184 * better algorithms, however, will be very gratefully received.
186 * Another annoyance of this approach is that it limits the
187 * available puzzles to those solvable by the algorithm I've used.
188 * My algorithm only ever considers a single row or column at any
189 * one time, which means it's incapable of solving the following
190 * difficult example (found by Bella Image around 1995/6, when she
191 * and I were both doing maths degrees):
205 * Obviously this cannot be solved by a one-row-or-column-at-a-time
206 * algorithm (it would require at least one row or column reading
207 * `2 1', `1 2', `3' or `4' to get started). However, it can be
208 * proved to have a unique solution: if the top left square were
209 * empty, then the only option for the top row would be to fill the
210 * two squares in the 1 columns, which would imply the squares
211 * below those were empty, leaving no place for the 2 in the second
212 * row. Contradiction. Hence the top left square is full, and the
213 * unique solution follows easily from that starting point.
215 * (The game ID for this puzzle is 4x4:2/1/2/1/1.1/2/1/1 , in case
216 * it's useful to anyone.)
219 static int float_compare(const void *av
, const void *bv
)
221 const float *a
= (const float *)av
;
222 const float *b
= (const float *)bv
;
231 static void generate(random_state
*rs
, int w
, int h
, unsigned char *retgrid
)
238 fgrid
= snewn(w
*h
, float);
240 for (i
= 0; i
< h
; i
++) {
241 for (j
= 0; j
< w
; j
++) {
242 fgrid
[i
*w
+j
] = random_upto(rs
, 100000000UL) / 100000000.F
;
247 * The above gives a completely random splattering of black and
248 * white cells. We want to gently bias this in favour of _some_
249 * reasonably thick areas of white and black, while retaining
250 * some randomness and fine detail.
252 * So we evolve the starting grid using a cellular automaton.
253 * Currently, I'm doing something very simple indeed, which is
254 * to set each square to the average of the surrounding nine
255 * cells (or the average of fewer, if we're on a corner).
257 for (step
= 0; step
< 1; step
++) {
258 fgrid2
= snewn(w
*h
, float);
260 for (i
= 0; i
< h
; i
++) {
261 for (j
= 0; j
< w
; j
++) {
266 * Compute the average of the surrounding cells.
270 for (p
= -1; p
<= +1; p
++) {
271 for (q
= -1; q
<= +1; q
++) {
272 if (i
+p
< 0 || i
+p
>= h
|| j
+q
< 0 || j
+q
>= w
)
275 * An additional special case not mentioned
276 * above: if a grid dimension is 2xn then
277 * we do not average across that dimension
278 * at all. Otherwise a 2x2 grid would
279 * contain four identical squares.
281 if ((h
==2 && p
!=0) || (w
==2 && q
!=0))
284 sx
+= fgrid
[(i
+p
)*w
+(j
+q
)];
289 fgrid2
[i
*w
+j
] = xbar
;
297 fgrid2
= snewn(w
*h
, float);
298 memcpy(fgrid2
, fgrid
, w
*h
*sizeof(float));
299 qsort(fgrid2
, w
*h
, sizeof(float), float_compare
);
300 threshold
= fgrid2
[w
*h
/2];
303 for (i
= 0; i
< h
; i
++) {
304 for (j
= 0; j
< w
; j
++) {
305 retgrid
[i
*w
+j
] = (fgrid
[i
*w
+j
] >= threshold ? GRID_FULL
:
313 static int compute_rowdata(int *ret
, unsigned char *start
, int len
, int step
)
319 for (i
= 0; i
< len
; i
++) {
320 if (start
[i
*step
] == GRID_FULL
) {
322 while (i
+runlen
< len
&& start
[(i
+runlen
)*step
] == GRID_FULL
)
328 if (i
< len
&& start
[i
*step
] == GRID_UNKNOWN
)
338 #define STILL_UNKNOWN 3
340 static void do_recurse(unsigned char *known
, unsigned char *deduced
,
341 unsigned char *row
, int *data
, int len
,
342 int freespace
, int ndone
, int lowest
)
347 for (i
=0; i
<=freespace
; i
++) {
349 for (k
=0; k
<i
; k
++) row
[j
++] = DOT
;
350 for (k
=0; k
<data
[ndone
]; k
++) row
[j
++] = BLOCK
;
351 if (j
< len
) row
[j
++] = DOT
;
352 do_recurse(known
, deduced
, row
, data
, len
,
353 freespace
-i
, ndone
+1, j
);
356 for (i
=lowest
; i
<len
; i
++)
358 for (i
=0; i
<len
; i
++)
359 if (known
[i
] && known
[i
] != row
[i
])
361 for (i
=0; i
<len
; i
++)
362 deduced
[i
] |= row
[i
];
366 static int do_row(unsigned char *known
, unsigned char *deduced
,
368 unsigned char *start
, int len
, int step
, int *data
)
370 int rowlen
, i
, freespace
, done_any
;
373 for (rowlen
= 0; data
[rowlen
]; rowlen
++)
374 freespace
-= data
[rowlen
]+1;
376 for (i
= 0; i
< len
; i
++) {
377 known
[i
] = start
[i
*step
];
381 do_recurse(known
, deduced
, row
, data
, len
, freespace
, 0, 0);
383 for (i
=0; i
<len
; i
++)
384 if (deduced
[i
] && deduced
[i
] != STILL_UNKNOWN
&& !known
[i
]) {
385 start
[i
*step
] = deduced
[i
];
391 static unsigned char *generate_soluble(random_state
*rs
, int w
, int h
)
393 int i
, j
, done_any
, ok
, ntries
, max
;
394 unsigned char *grid
, *matrix
, *workspace
;
397 grid
= snewn(w
*h
, unsigned char);
398 matrix
= snewn(w
*h
, unsigned char);
400 workspace
= snewn(max
*3, unsigned char);
401 rowdata
= snewn(max
+1, int);
408 generate(rs
, w
, h
, grid
);
411 * The game is a bit too easy if any row or column is
412 * completely black or completely white. An exception is
413 * made for rows/columns that are under 3 squares,
414 * otherwise nothing will ever be successfully generated.
418 for (i
= 0; i
< h
; i
++) {
420 for (j
= 0; j
< w
; j
++)
421 colours
|= (grid
[i
*w
+j
] == GRID_FULL ?
2 : 1);
427 for (j
= 0; j
< w
; j
++) {
429 for (i
= 0; i
< h
; i
++)
430 colours
|= (grid
[i
*w
+j
] == GRID_FULL ?
2 : 1);
438 memset(matrix
, 0, w
*h
);
442 for (i
=0; i
<h
; i
++) {
443 rowdata
[compute_rowdata(rowdata
, grid
+i
*w
, w
, 1)] = 0;
444 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
445 matrix
+i
*w
, w
, 1, rowdata
);
447 for (i
=0; i
<w
; i
++) {
448 rowdata
[compute_rowdata(rowdata
, grid
+i
, h
, w
)] = 0;
449 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
450 matrix
+i
, h
, w
, rowdata
);
455 for (i
=0; i
<h
; i
++) {
456 for (j
=0; j
<w
; j
++) {
457 if (matrix
[i
*w
+j
] == UNKNOWN
)
469 struct game_aux_info
{
474 static char *new_game_desc(game_params
*params
, random_state
*rs
,
475 game_aux_info
**aux
, int interactive
)
478 int i
, j
, max
, rowlen
, *rowdata
;
479 char intbuf
[80], *desc
;
480 int desclen
, descpos
;
482 grid
= generate_soluble(rs
, params
->w
, params
->h
);
483 max
= max(params
->w
, params
->h
);
484 rowdata
= snewn(max
, int);
487 * Save the solved game in an aux_info.
490 game_aux_info
*ai
= snew(game_aux_info
);
500 * Seed is a slash-separated list of row contents; each row
501 * contents section is a dot-separated list of integers. Row
502 * contents are listed in the order (columns left to right,
503 * then rows top to bottom).
505 * Simplest way to handle memory allocation is to make two
506 * passes, first computing the seed size and then writing it
510 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
512 rowlen
= compute_rowdata(rowdata
, grid
+i
, params
->h
, params
->w
);
514 rowlen
= compute_rowdata(rowdata
, grid
+(i
-params
->w
)*params
->w
,
517 for (j
= 0; j
< rowlen
; j
++) {
518 desclen
+= 1 + sprintf(intbuf
, "%d", rowdata
[j
]);
524 desc
= snewn(desclen
, char);
526 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
528 rowlen
= compute_rowdata(rowdata
, grid
+i
, params
->h
, params
->w
);
530 rowlen
= compute_rowdata(rowdata
, grid
+(i
-params
->w
)*params
->w
,
533 for (j
= 0; j
< rowlen
; j
++) {
534 int len
= sprintf(desc
+descpos
, "%d", rowdata
[j
]);
536 desc
[descpos
+ len
] = '.';
538 desc
[descpos
+ len
] = '/';
542 desc
[descpos
++] = '/';
545 assert(descpos
== desclen
);
546 assert(desc
[desclen
-1] == '/');
547 desc
[desclen
-1] = '\0';
552 static void game_free_aux_info(game_aux_info
*aux
)
558 static char *validate_desc(game_params
*params
, char *desc
)
563 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
565 rowspace
= params
->h
+ 1;
567 rowspace
= params
->w
+ 1;
569 if (*desc
&& isdigit((unsigned char)*desc
)) {
572 while (desc
&& isdigit((unsigned char)*desc
)) desc
++;
578 return "at least one column contains more numbers than will fit";
580 return "at least one row contains more numbers than will fit";
582 } while (*desc
++ == '.');
584 desc
++; /* expect a slash immediately */
587 if (desc
[-1] == '/') {
588 if (i
+1 == params
->w
+ params
->h
)
589 return "too many row/column specifications";
590 } else if (desc
[-1] == '\0') {
591 if (i
+1 < params
->w
+ params
->h
)
592 return "too few row/column specifications";
594 return "unrecognised character in game specification";
600 static game_state
*new_game(midend_data
*me
, game_params
*params
, char *desc
)
604 game_state
*state
= snew(game_state
);
606 state
->w
= params
->w
;
607 state
->h
= params
->h
;
609 state
->grid
= snewn(state
->w
* state
->h
, unsigned char);
610 memset(state
->grid
, GRID_UNKNOWN
, state
->w
* state
->h
);
612 state
->rowsize
= max(state
->w
, state
->h
);
613 state
->rowdata
= snewn(state
->rowsize
* (state
->w
+ state
->h
), int);
614 state
->rowlen
= snewn(state
->w
+ state
->h
, int);
616 state
->completed
= state
->cheated
= FALSE
;
618 for (i
= 0; i
< params
->w
+ params
->h
; i
++) {
619 state
->rowlen
[i
] = 0;
620 if (*desc
&& isdigit((unsigned char)*desc
)) {
623 while (desc
&& isdigit((unsigned char)*desc
)) desc
++;
624 state
->rowdata
[state
->rowsize
* i
+ state
->rowlen
[i
]++] =
626 } while (*desc
++ == '.');
628 desc
++; /* expect a slash immediately */
635 static game_state
*dup_game(game_state
*state
)
637 game_state
*ret
= snew(game_state
);
642 ret
->grid
= snewn(ret
->w
* ret
->h
, unsigned char);
643 memcpy(ret
->grid
, state
->grid
, ret
->w
* ret
->h
);
645 ret
->rowsize
= state
->rowsize
;
646 ret
->rowdata
= snewn(ret
->rowsize
* (ret
->w
+ ret
->h
), int);
647 ret
->rowlen
= snewn(ret
->w
+ ret
->h
, int);
648 memcpy(ret
->rowdata
, state
->rowdata
,
649 ret
->rowsize
* (ret
->w
+ ret
->h
) * sizeof(int));
650 memcpy(ret
->rowlen
, state
->rowlen
,
651 (ret
->w
+ ret
->h
) * sizeof(int));
653 ret
->completed
= state
->completed
;
654 ret
->cheated
= state
->cheated
;
659 static void free_game(game_state
*state
)
661 sfree(state
->rowdata
);
662 sfree(state
->rowlen
);
667 static game_state
*solve_game(game_state
*state
, game_aux_info
*ai
,
672 ret
= dup_game(state
);
673 ret
->completed
= ret
->cheated
= TRUE
;
676 * If we already have the solved state in an aux_info, copy it
681 assert(ret
->w
== ai
->w
);
682 assert(ret
->h
== ai
->h
);
683 memcpy(ret
->grid
, ai
->grid
, ai
->w
* ai
->h
);
686 int w
= state
->w
, h
= state
->h
, i
, j
, done_any
, max
;
687 unsigned char *matrix
, *workspace
;
690 matrix
= snewn(w
*h
, unsigned char);
692 workspace
= snewn(max
*3, unsigned char);
693 rowdata
= snewn(max
+1, int);
695 memset(matrix
, 0, w
*h
);
699 for (i
=0; i
<h
; i
++) {
700 memcpy(rowdata
, state
->rowdata
+ state
->rowsize
*(w
+i
),
702 rowdata
[state
->rowlen
[w
+i
]] = 0;
703 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
704 matrix
+i
*w
, w
, 1, rowdata
);
706 for (i
=0; i
<w
; i
++) {
707 memcpy(rowdata
, state
->rowdata
+ state
->rowsize
*i
, max
*sizeof(int));
708 rowdata
[state
->rowlen
[i
]] = 0;
709 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
710 matrix
+i
, h
, w
, rowdata
);
714 for (i
= 0; i
< h
; i
++) {
715 for (j
= 0; j
< w
; j
++) {
716 int c
= (matrix
[i
*w
+j
] == BLOCK ? GRID_FULL
:
717 matrix
[i
*w
+j
] == DOT ? GRID_EMPTY
: GRID_UNKNOWN
);
718 ret
->grid
[i
*w
+j
] = c
;
719 if (c
== GRID_UNKNOWN
)
720 ret
->completed
= FALSE
;
724 if (!ret
->completed
) {
726 *error
= "Solving algorithm cannot complete this puzzle";
734 static char *game_text_format(game_state
*state
)
745 int drag
, release
, state
;
748 static game_ui
*new_ui(game_state
*state
)
753 ret
->dragging
= FALSE
;
758 static void free_ui(game_ui
*ui
)
763 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
764 game_state
*newstate
)
768 struct game_drawstate
{
772 unsigned char *visible
;
775 static game_state
*make_move(game_state
*from
, game_ui
*ui
, game_drawstate
*ds
,
776 int x
, int y
, int button
) {
781 x
= FROMCOORD(from
->w
, x
);
782 y
= FROMCOORD(from
->h
, y
);
784 if (x
>= 0 && x
< from
->w
&& y
>= 0 && y
< from
->h
&&
785 (button
== LEFT_BUTTON
|| button
== RIGHT_BUTTON
||
786 button
== MIDDLE_BUTTON
)) {
790 if (button
== LEFT_BUTTON
) {
791 ui
->drag
= LEFT_DRAG
;
792 ui
->release
= LEFT_RELEASE
;
793 ui
->state
= GRID_FULL
;
794 } else if (button
== RIGHT_BUTTON
) {
795 ui
->drag
= RIGHT_DRAG
;
796 ui
->release
= RIGHT_RELEASE
;
797 ui
->state
= GRID_EMPTY
;
798 } else /* if (button == MIDDLE_BUTTON) */ {
799 ui
->drag
= MIDDLE_DRAG
;
800 ui
->release
= MIDDLE_RELEASE
;
801 ui
->state
= GRID_UNKNOWN
;
804 ui
->drag_start_x
= ui
->drag_end_x
= x
;
805 ui
->drag_start_y
= ui
->drag_end_y
= y
;
807 return from
; /* UI activity occurred */
810 if (ui
->dragging
&& button
== ui
->drag
) {
812 * There doesn't seem much point in allowing a rectangle
813 * drag; people will generally only want to drag a single
814 * horizontal or vertical line, so we make that easy by
817 * Exception: if we're _middle_-button dragging to tag
818 * things as UNKNOWN, we may well want to trash an entire
819 * area and start over!
821 if (ui
->state
!= GRID_UNKNOWN
) {
822 if (abs(x
- ui
->drag_start_x
) > abs(y
- ui
->drag_start_y
))
823 y
= ui
->drag_start_y
;
825 x
= ui
->drag_start_x
;
830 if (x
>= from
->w
) x
= from
->w
- 1;
831 if (y
>= from
->h
) y
= from
->h
- 1;
836 return from
; /* UI activity occurred */
839 if (ui
->dragging
&& button
== ui
->release
) {
840 int x1
, x2
, y1
, y2
, xx
, yy
;
841 int move_needed
= FALSE
;
843 x1
= min(ui
->drag_start_x
, ui
->drag_end_x
);
844 x2
= max(ui
->drag_start_x
, ui
->drag_end_x
);
845 y1
= min(ui
->drag_start_y
, ui
->drag_end_y
);
846 y2
= max(ui
->drag_start_y
, ui
->drag_end_y
);
848 for (yy
= y1
; yy
<= y2
; yy
++)
849 for (xx
= x1
; xx
<= x2
; xx
++)
850 if (from
->grid
[yy
* from
->w
+ xx
] != ui
->state
)
853 ui
->dragging
= FALSE
;
856 ret
= dup_game(from
);
857 for (yy
= y1
; yy
<= y2
; yy
++)
858 for (xx
= x1
; xx
<= x2
; xx
++)
859 ret
->grid
[yy
* ret
->w
+ xx
] = ui
->state
;
862 * An actual change, so check to see if we've completed
865 if (!ret
->completed
) {
866 int *rowdata
= snewn(ret
->rowsize
, int);
869 ret
->completed
= TRUE
;
871 for (i
=0; i
<ret
->w
; i
++) {
872 len
= compute_rowdata(rowdata
,
873 ret
->grid
+i
, ret
->h
, ret
->w
);
874 if (len
!= ret
->rowlen
[i
] ||
875 memcmp(ret
->rowdata
+i
*ret
->rowsize
, rowdata
,
876 len
* sizeof(int))) {
877 ret
->completed
= FALSE
;
881 for (i
=0; i
<ret
->h
; i
++) {
882 len
= compute_rowdata(rowdata
,
883 ret
->grid
+i
*ret
->w
, ret
->w
, 1);
884 if (len
!= ret
->rowlen
[i
+ret
->w
] ||
885 memcmp(ret
->rowdata
+(i
+ret
->w
)*ret
->rowsize
, rowdata
,
886 len
* sizeof(int))) {
887 ret
->completed
= FALSE
;
897 return from
; /* UI activity occurred */
903 /* ----------------------------------------------------------------------
907 static void game_size(game_params
*params
, game_drawstate
*ds
,
908 int *x
, int *y
, int expand
)
912 ts
= min(GETTILESIZE(params
->w
, *x
), GETTILESIZE(params
->h
, *y
));
916 ds
->tilesize
= min(ts
, PREFERRED_TILE_SIZE
);
918 *x
= SIZE(params
->w
);
919 *y
= SIZE(params
->h
);
922 static float *game_colours(frontend
*fe
, game_state
*state
, int *ncolours
)
924 float *ret
= snewn(3 * NCOLOURS
, float);
926 frontend_default_colour(fe
, &ret
[COL_BACKGROUND
* 3]);
928 ret
[COL_GRID
* 3 + 0] = 0.3F
;
929 ret
[COL_GRID
* 3 + 1] = 0.3F
;
930 ret
[COL_GRID
* 3 + 2] = 0.3F
;
932 ret
[COL_UNKNOWN
* 3 + 0] = 0.5F
;
933 ret
[COL_UNKNOWN
* 3 + 1] = 0.5F
;
934 ret
[COL_UNKNOWN
* 3 + 2] = 0.5F
;
936 ret
[COL_FULL
* 3 + 0] = 0.0F
;
937 ret
[COL_FULL
* 3 + 1] = 0.0F
;
938 ret
[COL_FULL
* 3 + 2] = 0.0F
;
940 ret
[COL_EMPTY
* 3 + 0] = 1.0F
;
941 ret
[COL_EMPTY
* 3 + 1] = 1.0F
;
942 ret
[COL_EMPTY
* 3 + 2] = 1.0F
;
944 *ncolours
= NCOLOURS
;
948 static game_drawstate
*game_new_drawstate(game_state
*state
)
950 struct game_drawstate
*ds
= snew(struct game_drawstate
);
955 ds
->visible
= snewn(ds
->w
* ds
->h
, unsigned char);
956 ds
->tilesize
= 0; /* not decided yet */
957 memset(ds
->visible
, 255, ds
->w
* ds
->h
);
962 static void game_free_drawstate(game_drawstate
*ds
)
968 static void grid_square(frontend
*fe
, game_drawstate
*ds
,
969 int y
, int x
, int state
)
973 draw_rect(fe
, TOCOORD(ds
->w
, x
), TOCOORD(ds
->h
, y
),
974 TILE_SIZE
, TILE_SIZE
, COL_GRID
);
976 xl
= (x
% 5 == 0 ?
1 : 0);
977 yt
= (y
% 5 == 0 ?
1 : 0);
978 xr
= (x
% 5 == 4 || x
== ds
->w
-1 ?
1 : 0);
979 yb
= (y
% 5 == 4 || y
== ds
->h
-1 ?
1 : 0);
981 draw_rect(fe
, TOCOORD(ds
->w
, x
) + 1 + xl
, TOCOORD(ds
->h
, y
) + 1 + yt
,
982 TILE_SIZE
- xl
- xr
- 1, TILE_SIZE
- yt
- yb
- 1,
983 (state
== GRID_FULL ? COL_FULL
:
984 state
== GRID_EMPTY ? COL_EMPTY
: COL_UNKNOWN
));
986 draw_update(fe
, TOCOORD(ds
->w
, x
), TOCOORD(ds
->h
, y
),
987 TILE_SIZE
, TILE_SIZE
);
990 static void game_redraw(frontend
*fe
, game_drawstate
*ds
, game_state
*oldstate
,
991 game_state
*state
, int dir
, game_ui
*ui
,
992 float animtime
, float flashtime
)
999 * The initial contents of the window are not guaranteed
1000 * and can vary with front ends. To be on the safe side,
1001 * all games should start by drawing a big background-
1002 * colour rectangle covering the whole window.
1004 draw_rect(fe
, 0, 0, SIZE(ds
->w
), SIZE(ds
->h
), COL_BACKGROUND
);
1009 for (i
= 0; i
< ds
->w
+ ds
->h
; i
++) {
1010 int rowlen
= state
->rowlen
[i
];
1011 int *rowdata
= state
->rowdata
+ state
->rowsize
* i
;
1015 * Normally I space the numbers out by the same
1016 * distance as the tile size. However, if there are
1017 * more numbers than available spaces, I have to squash
1020 nfit
= max(rowlen
, TLBORDER(ds
->h
))-1;
1023 for (j
= 0; j
< rowlen
; j
++) {
1028 x
= TOCOORD(ds
->w
, i
);
1029 y
= BORDER
+ TILE_SIZE
* (TLBORDER(ds
->h
)-1);
1030 y
-= ((rowlen
-j
-1)*TILE_SIZE
) * (TLBORDER(ds
->h
)-1) / nfit
;
1032 y
= TOCOORD(ds
->h
, i
- ds
->w
);
1033 x
= BORDER
+ TILE_SIZE
* (TLBORDER(ds
->w
)-1);
1034 x
-= ((rowlen
-j
-1)*TILE_SIZE
) * (TLBORDER(ds
->h
)-1) / nfit
;
1037 sprintf(str
, "%d", rowdata
[j
]);
1038 draw_text(fe
, x
+TILE_SIZE
/2, y
+TILE_SIZE
/2, FONT_VARIABLE
,
1039 TILE_SIZE
/2, ALIGN_HCENTRE
| ALIGN_VCENTRE
,
1040 COL_FULL
, str
); /* FIXME: COL_TEXT */
1045 * Draw the grid outline.
1047 draw_rect(fe
, TOCOORD(ds
->w
, 0) - 1, TOCOORD(ds
->h
, 0) - 1,
1048 ds
->w
* TILE_SIZE
+ 3, ds
->h
* TILE_SIZE
+ 3,
1053 draw_update(fe
, 0, 0, SIZE(ds
->w
), SIZE(ds
->h
));
1057 x1
= min(ui
->drag_start_x
, ui
->drag_end_x
);
1058 x2
= max(ui
->drag_start_x
, ui
->drag_end_x
);
1059 y1
= min(ui
->drag_start_y
, ui
->drag_end_y
);
1060 y2
= max(ui
->drag_start_y
, ui
->drag_end_y
);
1062 x1
= x2
= y1
= y2
= -1; /* placate gcc warnings */
1066 * Now draw any grid squares which have changed since last
1069 for (i
= 0; i
< ds
->h
; i
++) {
1070 for (j
= 0; j
< ds
->w
; j
++) {
1074 * Work out what state this square should be drawn in,
1075 * taking any current drag operation into account.
1077 if (ui
->dragging
&& x1
<= j
&& j
<= x2
&& y1
<= i
&& i
<= y2
)
1080 val
= state
->grid
[i
* state
->w
+ j
];
1083 * Briefly invert everything twice during a completion
1086 if (flashtime
> 0 &&
1087 (flashtime
<= FLASH_TIME
/3 || flashtime
>= FLASH_TIME
*2/3) &&
1088 val
!= GRID_UNKNOWN
)
1089 val
= (GRID_FULL
^ GRID_EMPTY
) ^ val
;
1091 if (ds
->visible
[i
* ds
->w
+ j
] != val
) {
1092 grid_square(fe
, ds
, i
, j
, val
);
1093 ds
->visible
[i
* ds
->w
+ j
] = val
;
1099 static float game_anim_length(game_state
*oldstate
,
1100 game_state
*newstate
, int dir
, game_ui
*ui
)
1105 static float game_flash_length(game_state
*oldstate
,
1106 game_state
*newstate
, int dir
, game_ui
*ui
)
1108 if (!oldstate
->completed
&& newstate
->completed
&&
1109 !oldstate
->cheated
&& !newstate
->cheated
)
1114 static int game_wants_statusbar(void)
1119 static int game_timing_state(game_state
*state
)
1125 #define thegame pattern
1128 const struct game thegame
= {
1129 "Pattern", "games.pattern",
1136 TRUE
, game_configure
, custom_params
,
1145 FALSE
, game_text_format
,
1153 game_free_drawstate
,
1157 game_wants_statusbar
,
1158 FALSE
, game_timing_state
,
1159 0, /* mouse_priorities */
1162 #ifdef STANDALONE_SOLVER
1165 * gcc -DSTANDALONE_SOLVER -o patternsolver pattern.c malloc.c
1170 void frontend_default_colour(frontend
*fe
, float *output
) {}
1171 void draw_text(frontend
*fe
, int x
, int y
, int fonttype
, int fontsize
,
1172 int align
, int colour
, char *text
) {}
1173 void draw_rect(frontend
*fe
, int x
, int y
, int w
, int h
, int colour
) {}
1174 void draw_line(frontend
*fe
, int x1
, int y1
, int x2
, int y2
, int colour
) {}
1175 void draw_polygon(frontend
*fe
, int *coords
, int npoints
,
1176 int fill
, int colour
) {}
1177 void clip(frontend
*fe
, int x
, int y
, int w
, int h
) {}
1178 void unclip(frontend
*fe
) {}
1179 void start_draw(frontend
*fe
) {}
1180 void draw_update(frontend
*fe
, int x
, int y
, int w
, int h
) {}
1181 void end_draw(frontend
*fe
) {}
1182 unsigned long random_upto(random_state
*state
, unsigned long limit
)
1183 { assert(!"Shouldn't get randomness"); return 0; }
1185 void fatal(char *fmt
, ...)
1189 fprintf(stderr
, "fatal error: ");
1192 vfprintf(stderr
, fmt
, ap
);
1195 fprintf(stderr
, "\n");
1199 int main(int argc
, char **argv
)
1204 char *id
= NULL
, *desc
, *err
;
1208 while (--argc
> 0) {
1211 fprintf(stderr
, "%s: unrecognised option `%s'\n", argv
[0]);
1219 fprintf(stderr
, "usage: %s <game_id>\n", argv
[0]);
1223 desc
= strchr(id
, ':');
1225 fprintf(stderr
, "%s: game id expects a colon in it\n", argv
[0]);
1230 p
= default_params();
1231 decode_params(p
, id
);
1232 err
= validate_desc(p
, desc
);
1234 fprintf(stderr
, "%s: %s\n", argv
[0], err
);
1237 s
= new_game(NULL
, p
, desc
);
1240 int w
= p
->w
, h
= p
->h
, i
, j
, done_any
, max
;
1241 unsigned char *matrix
, *workspace
;
1244 matrix
= snewn(w
*h
, unsigned char);
1246 workspace
= snewn(max
*3, unsigned char);
1247 rowdata
= snewn(max
+1, int);
1249 memset(matrix
, 0, w
*h
);
1253 for (i
=0; i
<h
; i
++) {
1254 memcpy(rowdata
, s
->rowdata
+ s
->rowsize
*(w
+i
),
1256 rowdata
[s
->rowlen
[w
+i
]] = 0;
1257 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
1258 matrix
+i
*w
, w
, 1, rowdata
);
1260 for (i
=0; i
<w
; i
++) {
1261 memcpy(rowdata
, s
->rowdata
+ s
->rowsize
*i
, max
*sizeof(int));
1262 rowdata
[s
->rowlen
[i
]] = 0;
1263 done_any
|= do_row(workspace
, workspace
+max
, workspace
+2*max
,
1264 matrix
+i
, h
, w
, rowdata
);
1268 for (i
= 0; i
< h
; i
++) {
1269 for (j
= 0; j
< w
; j
++) {
1270 int c
= (matrix
[i
*w
+j
] == UNKNOWN ?
'?' :
1271 matrix
[i
*w
+j
] == BLOCK ?
'#' :
1272 matrix
[i
*w
+j
] == DOT ?
'.' :