2 * range.c: implementation of the Nikoli game 'Kurodoko' / 'Kuromasu'.
6 * Puzzle rules: the player is given a WxH grid of white squares, some
7 * of which contain numbers. The goal is to paint some of the squares
10 * - no cell (err, cell = square) with a number is painted black
11 * - no black cells have an adjacent (horz/vert) black cell
12 * - the white cells are all connected (through other white cells)
13 * - if a cell contains a number n, let h and v be the lengths of the
14 * maximal horizontal and vertical white sequences containing that
15 * cell. Then n must equal h + v - 1.
18 /* example instance with its encoding:
20 * +--+--+--+--+--+--+--+
22 * +--+--+--+--+--+--+--+
24 * +--+--+--+--+--+--+--+
26 * +--+--+--+--+--+--+--+
28 * +--+--+--+--+--+--+--+
30 * +--+--+--+--+--+--+--+
32 * +--+--+--+--+--+--+--+
34 * +--+--+--+--+--+--+--+
36 * 7x7:d7b3e8e5c7a7c13e4d8b4d
50 #define setmember(obj, field) ( (obj) . field = field )
52 static char *nfmtstr(int n
, char *fmt
, ...) {
54 char *ret
= snewn(n
+1, char);
56 vsprintf(ret
, fmt
, va
);
61 #define SWAP(type, lvar1, lvar2) do { \
67 /* ----------------------------------------------------------------------
68 * Game parameters, presets, states
71 typedef signed char puzzle_size
;
79 struct game_params params
;
80 unsigned int has_cheated
: 1;
81 unsigned int was_solved
: 1;
85 #define DEFAULT_PRESET 0
86 static struct game_params range_presets
[] = {{9, 6}, {12, 8}, {13, 9}, {16, 11}};
87 /* rationale: I want all four combinations of {odd/even, odd/even}, as
88 * they play out differently with respect to two-way symmetry. I also
89 * want them to be generated relatively fast yet still be large enough
90 * to be entertaining for a decent amount of time, and I want them to
91 * make good use of monitor real estate (the typical screen resolution
92 * is why I do 13x9 and not 9x13).
95 static game_params
*default_params(void)
97 game_params
*ret
= snew(game_params
);
98 *ret
= range_presets
[DEFAULT_PRESET
]; /* structure copy */
102 static game_params
*dup_params(game_params
*params
)
104 game_params
*ret
= snew(game_params
);
105 *ret
= *params
; /* structure copy */
109 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
113 if (i
< 0 || i
>= lenof(range_presets
)) return FALSE
;
115 ret
= default_params();
116 *ret
= range_presets
[i
]; /* struct copy */
119 *name
= nfmtstr(40, "%d x %d", range_presets
[i
].w
, range_presets
[i
].h
);
124 static void free_params(game_params
*params
)
129 static void decode_params(game_params
*params
, char const *string
)
131 /* FIXME check for puzzle_size overflow and decoding issues */
132 params
->w
= params
->h
= atoi(string
);
133 while (*string
&& isdigit((unsigned char) *string
)) ++string
;
134 if (*string
== 'x') {
136 params
->h
= atoi(string
);
137 while (*string
&& isdigit((unsigned char)*string
)) string
++;
141 static char *encode_params(game_params
*params
, int full
)
144 sprintf(str
, "%dx%d", params
->w
, params
->h
);
148 static config_item
*game_configure(game_params
*params
)
152 ret
= snewn(3, config_item
);
154 ret
[0].name
= "Width";
155 ret
[0].type
= C_STRING
;
156 ret
[0].sval
= nfmtstr(10, "%d", params
->w
);
159 ret
[1].name
= "Height";
160 ret
[1].type
= C_STRING
;
161 ret
[1].sval
= nfmtstr(10, "%d", params
->h
);
172 static game_params
*custom_params(config_item
*configuration
)
174 game_params
*ret
= snew(game_params
);
175 ret
->w
= atoi(configuration
[0].sval
);
176 ret
->h
= atoi(configuration
[1].sval
);
180 #define memdup(dst, src, n, type) do { \
181 dst = snewn(n, type); \
182 memcpy(dst, src, n * sizeof (type)); \
185 static game_state
*dup_game(game_state
*state
)
187 game_state
*ret
= snew(game_state
);
188 int const n
= state
->params
.w
* state
->params
.h
;
190 *ret
= *state
; /* structure copy */
192 /* copy the poin_tee_, set a new value of the poin_ter_ */
193 memdup(ret
->grid
, state
->grid
, n
, puzzle_size
);
198 static void free_game(game_state
*state
)
205 /* ----------------------------------------------------------------------
206 * The solver subsystem.
208 * The solver is used for two purposes:
209 * - To solve puzzles when the user selects `Solve'.
210 * - To test solubility of a grid as clues are being removed from it
211 * during the puzzle generation.
213 * It supports the following ways of reasoning:
215 * - A cell adjacent to a black cell must be white.
217 * - If painting a square black would bisect the white regions, that
218 * square is white (by finding biconnected components' cut points)
220 * - A cell with number n, covering at most k white squares in three
221 * directions must white-cover n-k squares in the last direction.
223 * - A cell with number n known to cover k squares, if extending the
224 * cover by one square in a given direction causes the cell to
225 * cover _more_ than n squares, that extension cell must be black.
227 * (either if the square already covers n, or if it extends into a
228 * chunk of size > n - k)
230 * - Recursion. Pick any cell and see if this leads to either a
231 * contradiction or a solution (and then act appropriately).
236 * (propagation upper limit)
237 * - If one has two numbers on the same line, the smaller limits the
238 * larger. Example: in |b|_|_|8|4|_|_|b|, only two _'s can be both
239 * white and connected to the "8" cell; so that cell will propagate
240 * at least four cells orthogonally to the displayed line (which is
241 * better than the current "at least 2").
243 * (propagation upper limit)
244 * - cells can't propagate into other cells if doing so exceeds that
245 * number. Example: in |b|4|.|.|2|b|, at most one _ can be white;
246 * otherwise, the |2| would have too many reaching white cells.
248 * (propagation lower and upper limit)
249 * - `Full Combo': in each four directions d_1 ... d_4, find a set of
250 * possible propagation distances S_1 ... S_4. For each i=1..4,
251 * for each x in S_i: if not exists (y, z, w) in the other sets
252 * such that (x+y+z+w+1 == clue value): then remove x from S_i.
253 * Repeat until this stabilizes. If any cell would contradict
256 #define idx(i, j, w) ((i)*(w) + (j))
257 #define out_of_bounds(r, c, w, h) \
258 ((r) < 0 || (r) >= h || (c) < 0 || (c) >= w)
260 typedef struct square
{
264 enum {BLACK
= -2, WHITE
, EMPTY
};
265 /* white is for pencil marks, empty is undecided */
267 static int const dr
[4] = {+1, 0, -1, 0};
268 static int const dc
[4] = { 0, +1, 0, -1};
269 static int const cursors
[4] = /* must match dr and dc */
270 {CURSOR_DOWN
, CURSOR_RIGHT
, CURSOR_UP
, CURSOR_LEFT
};
272 typedef struct move
{
274 unsigned int colour
: 1;
276 enum {M_BLACK
= 0, M_WHITE
= 1};
278 typedef move
*(reasoning
)(game_state
*state
,
283 static reasoning solver_reasoning_not_too_big
;
284 static reasoning solver_reasoning_adjacency
;
285 static reasoning solver_reasoning_connectedness
;
286 static reasoning solver_reasoning_recursion
;
295 static move
*solve_internal(game_state
*state
, move
*base
, int diff
);
297 static char *solve_game(game_state
*orig
, game_state
*curpos
,
298 char *aux
, char **error
)
300 int const n
= orig
->params
.w
* orig
->params
.h
;
301 move
*const base
= snewn(n
, move
);
302 move
*moves
= solve_internal(orig
, base
, DIFF_RECURSION
);
307 int const k
= moves
- base
;
308 char *str
= ret
= snewn(15*k
+ 2, char);
309 char colour
[2] = "BW";
313 for (it
= base
; it
< moves
; ++it
)
314 str
+= sprintf(str
, "%c,%d,%d", colour
[it
->colour
],
315 it
->square
.r
, it
->square
.c
);
316 } else *error
= "This puzzle instance contains a contradiction";
322 static square
*find_clues(game_state
*state
, int *ret_nclues
);
323 static move
*do_solve(game_state
*state
,
329 /* new_game_desc entry point in the solver subsystem */
330 static move
*solve_internal(game_state
*state
, move
*base
, int diff
)
333 square
*const clues
= find_clues(state
, &nclues
);
334 game_state
*dup
= dup_game(state
);
335 move
*const moves
= do_solve(dup
, nclues
, clues
, base
, diff
);
341 static reasoning
*const reasonings
[] = {
342 solver_reasoning_not_too_big
,
343 solver_reasoning_adjacency
,
344 solver_reasoning_connectedness
,
345 solver_reasoning_recursion
348 static move
*do_solve(game_state
*state
,
354 struct move
*buf
= move_buffer
, *oldbuf
;
359 for (i
= 0; i
< lenof(reasonings
) && i
<= difficulty
; ++i
) {
360 /* only recurse if all else fails */
361 if (i
== DIFF_RECURSION
&& buf
> oldbuf
) continue;
362 buf
= (*reasonings
[i
])(state
, nclues
, clues
, buf
);
363 if (buf
== NULL
) return NULL
;
365 } while (buf
> oldbuf
);
370 #define MASK(n) (1 << ((n) + 2))
372 static int runlength(puzzle_size r
, puzzle_size c
,
373 puzzle_size dr
, puzzle_size dc
,
374 game_state
*state
, int colourmask
)
376 int const w
= state
->params
.w
, h
= state
->params
.h
;
379 int cell
= idx(r
, c
, w
);
380 if (out_of_bounds(r
, c
, w
, h
)) break;
381 if (state
->grid
[cell
] > 0) {
382 if (!(colourmask
& ~(MASK(BLACK
) | MASK(WHITE
) | MASK(EMPTY
))))
384 } else if (!(MASK(state
->grid
[cell
]) & colourmask
)) break;
392 static void solver_makemove(puzzle_size r
, puzzle_size c
, int colour
,
393 game_state
*state
, move
**buffer_ptr
)
395 int const cell
= idx(r
, c
, state
->params
.w
);
396 if (out_of_bounds(r
, c
, state
->params
.w
, state
->params
.h
)) return;
397 if (state
->grid
[cell
] != EMPTY
) return;
398 setmember((*buffer_ptr
)->square
, r
);
399 setmember((*buffer_ptr
)->square
, c
);
400 setmember(**buffer_ptr
, colour
);
402 state
->grid
[cell
] = (colour
== M_BLACK
? BLACK
: WHITE
);
405 static move
*solver_reasoning_adjacency(game_state
*state
,
411 for (r
= 0; r
< state
->params
.h
; ++r
)
412 for (c
= 0; c
< state
->params
.w
; ++c
) {
413 int const cell
= idx(r
, c
, state
->params
.w
);
414 if (state
->grid
[cell
] != BLACK
) continue;
415 for (i
= 0; i
< 4; ++i
)
416 solver_makemove(r
+ dr
[i
], c
+ dc
[i
], M_WHITE
, state
, &buf
);
421 enum {NOT_VISITED
= -1};
423 static int dfs_biconnect_visit(puzzle_size r
, puzzle_size c
,
425 square
*dfs_parent
, int *dfs_depth
,
428 static move
*solver_reasoning_connectedness(game_state
*state
,
433 int const w
= state
->params
.w
, h
= state
->params
.h
, n
= w
* h
;
435 square
*const dfs_parent
= snewn(n
, square
);
436 int *const dfs_depth
= snewn(n
, int);
439 for (i
= 0; i
< n
; ++i
) {
440 dfs_parent
[i
].r
= NOT_VISITED
;
444 for (i
= 0; i
< n
&& state
->grid
[i
] == BLACK
; ++i
);
446 dfs_parent
[i
].r
= i
/ w
;
447 dfs_parent
[i
].c
= i
% w
; /* `dfs root`.parent == `dfs root` */
450 dfs_biconnect_visit(i
/ w
, i
% w
, state
, dfs_parent
, dfs_depth
, &buf
);
458 /* returns the `lowpoint` of (r, c) */
459 static int dfs_biconnect_visit(puzzle_size r
, puzzle_size c
,
461 square
*dfs_parent
, int *dfs_depth
,
464 const puzzle_size w
= state
->params
.w
, h
= state
->params
.h
;
465 int const i
= idx(r
, c
, w
), mydepth
= dfs_depth
[i
];
466 int lowpoint
= mydepth
, j
, nchildren
= 0;
468 for (j
= 0; j
< 4; ++j
) {
469 const puzzle_size rr
= r
+ dr
[j
], cc
= c
+ dc
[j
];
470 int const cell
= idx(rr
, cc
, w
);
472 if (out_of_bounds(rr
, cc
, w
, h
)) continue;
473 if (state
->grid
[cell
] == BLACK
) continue;
475 if (dfs_parent
[cell
].r
== NOT_VISITED
) {
477 dfs_parent
[cell
].r
= r
;
478 dfs_parent
[cell
].c
= c
;
479 dfs_depth
[cell
] = mydepth
+ 1;
480 child_lowpoint
= dfs_biconnect_visit(rr
, cc
, state
, dfs_parent
,
483 if (child_lowpoint
>= mydepth
&& mydepth
> 0)
484 solver_makemove(r
, c
, M_WHITE
, state
, buf
);
486 lowpoint
= min(lowpoint
, child_lowpoint
);
488 } else if (rr
!= dfs_parent
[i
].r
|| cc
!= dfs_parent
[i
].c
) {
489 lowpoint
= min(lowpoint
, dfs_depth
[cell
]);
493 if (mydepth
== 0 && nchildren
>= 2)
494 solver_makemove(r
, c
, M_WHITE
, state
, buf
);
499 static move
*solver_reasoning_not_too_big(game_state
*state
,
504 int const w
= state
->params
.w
, runmasks
[4] = {
505 ~(MASK(BLACK
) | MASK(EMPTY
)),
507 ~(MASK(BLACK
) | MASK(EMPTY
)),
510 enum {RUN_WHITE
, RUN_EMPTY
, RUN_BEYOND
, RUN_SPACE
};
512 int i
, runlengths
[4][4];
514 for (i
= 0; i
< nclues
; ++i
) {
515 int j
, k
, whites
, space
;
517 const puzzle_size row
= clues
[i
].r
, col
= clues
[i
].c
;
518 int const clue
= state
->grid
[idx(row
, col
, w
)];
520 for (j
= 0; j
< 4; ++j
) {
521 puzzle_size r
= row
+ dr
[j
], c
= col
+ dc
[j
];
522 runlengths
[RUN_SPACE
][j
] = 0;
523 for (k
= 0; k
<= RUN_SPACE
; ++k
) {
524 int l
= runlength(r
, c
, dr
[j
], dc
[j
], state
, runmasks
[k
]);
526 runlengths
[k
][j
] = l
;
530 runlengths
[RUN_SPACE
][j
] += l
;
535 for (j
= 0; j
< 4; ++j
) whites
+= runlengths
[RUN_WHITE
][j
];
537 for (j
= 0; j
< 4; ++j
) {
538 int const delta
= 1 + runlengths
[RUN_WHITE
][j
];
539 const puzzle_size r
= row
+ delta
* dr
[j
];
540 const puzzle_size c
= col
+ delta
* dc
[j
];
542 if (whites
== clue
) {
543 solver_makemove(r
, c
, M_BLACK
, state
, &buf
);
547 if (runlengths
[RUN_EMPTY
][j
] == 1 &&
549 + runlengths
[RUN_EMPTY
][j
]
550 + runlengths
[RUN_BEYOND
][j
]
552 solver_makemove(r
, c
, M_BLACK
, state
, &buf
);
557 + runlengths
[RUN_EMPTY
][j
]
558 + runlengths
[RUN_BEYOND
][j
]
560 runlengths
[RUN_SPACE
][j
] =
561 runlengths
[RUN_WHITE
][j
] +
562 runlengths
[RUN_EMPTY
][j
] - 1;
564 if (runlengths
[RUN_EMPTY
][j
] == 1)
565 solver_makemove(r
, c
, M_BLACK
, state
, &buf
);
570 for (j
= 0; j
< 4; ++j
) space
+= runlengths
[RUN_SPACE
][j
];
571 for (j
= 0; j
< 4; ++j
) {
572 puzzle_size r
= row
+ dr
[j
], c
= col
+ dc
[j
];
574 int k
= space
- runlengths
[RUN_SPACE
][j
];
575 if (k
>= clue
) continue;
577 for (; k
< clue
; ++k
, r
+= dr
[j
], c
+= dc
[j
])
578 solver_makemove(r
, c
, M_WHITE
, state
, &buf
);
584 static move
*solver_reasoning_recursion(game_state
*state
,
589 int const w
= state
->params
.w
, n
= w
* state
->params
.h
;
592 for (cell
= 0; cell
< n
; ++cell
) {
593 int const r
= cell
/ w
, c
= cell
% w
;
595 game_state
*newstate
;
596 move
*recursive_result
;
598 if (state
->grid
[cell
] != EMPTY
) continue;
600 /* FIXME: add enum alias for smallest and largest (or N) */
601 for (colour
= M_BLACK
; colour
<= M_WHITE
; ++colour
) {
602 newstate
= dup_game(state
);
603 newstate
->grid
[cell
] = colour
;
604 recursive_result
= do_solve(newstate
, nclues
, clues
, buf
,
607 if (recursive_result
== NULL
) {
608 solver_makemove(r
, c
, M_BLACK
+ M_WHITE
- colour
, state
, &buf
);
611 for (i
= 0; i
< n
&& newstate
->grid
[i
] != EMPTY
; ++i
);
612 if (i
== n
) return buf
;
618 static square
*find_clues(game_state
*state
, int *ret_nclues
)
620 int r
, c
, i
, nclues
= 0;
621 square
*ret
= snewn(state
->params
.w
* state
->params
.h
, struct square
);
623 for (i
= r
= 0; r
< state
->params
.h
; ++r
)
624 for (c
= 0; c
< state
->params
.w
; ++c
, ++i
)
625 if (state
->grid
[i
] > 0) {
631 *ret_nclues
= nclues
;
632 return sresize(ret
, nclues
+ (nclues
== 0), square
);
635 /* ----------------------------------------------------------------------
638 * Generating kurodoko instances is rather straightforward:
640 * - Start with a white grid and add black squares at randomly chosen
641 * locations, unless colouring that square black would violate
642 * either the adjacency or connectedness constraints.
644 * - For each white square, compute the number it would contain if it
645 * were given as a clue.
647 * - From a starting point of "give _every_ white square as a clue",
648 * for each white square (in a random order), see if the board is
649 * solvable when that square is not given as a clue. If not, don't
650 * give it as a clue, otherwise do.
652 * This never fails, but it's only _almost_ what I do. The real final
655 * - From a starting point of "give _every_ white square as a clue",
656 * first remove all clues that are two-way rotationally symmetric
657 * to a black square. If this leaves the puzzle unsolvable, throw
658 * it out and try again. Otherwise, remove all _pairs_ of clues
659 * (that are rotationally symmetric) which can be removed without
660 * rendering the puzzle unsolvable.
662 * This can fail even if one only removes the black and symmetric
663 * clues; indeed it happens often (avg. once or twice per puzzle) when
664 * generating 1xN instances. (If you add black cells they must be in
665 * the end, and if you only add one, it's ambiguous where).
668 /* forward declarations of internal calls */
669 static void newdesc_choose_black_squares(game_state
*state
,
670 const int *shuffle_1toN
);
671 static void newdesc_compute_clues(game_state
*state
);
672 static int newdesc_strip_clues(game_state
*state
, int *shuffle_1toN
);
673 static char *newdesc_encode_game_description(int n
, puzzle_size
*grid
);
675 static char *new_game_desc(game_params
*params
, random_state
*rs
,
676 char **aux
, int interactive
)
678 int const w
= params
->w
, h
= params
->h
, n
= w
* h
;
680 puzzle_size
*const grid
= snewn(n
, puzzle_size
);
681 int *const shuffle_1toN
= snewn(n
, int);
683 int i
, clues_removed
;
688 state
.params
= *params
;
691 interactive
= 0; /* I don't need it, I shouldn't use it*/
693 for (i
= 0; i
< n
; ++i
) shuffle_1toN
[i
] = i
;
696 shuffle(shuffle_1toN
, n
, sizeof (int), rs
);
697 newdesc_choose_black_squares(&state
, shuffle_1toN
);
699 newdesc_compute_clues(&state
);
701 shuffle(shuffle_1toN
, n
, sizeof (int), rs
);
702 clues_removed
= newdesc_strip_clues(&state
, shuffle_1toN
);
704 if (clues_removed
< 0) continue; else break;
707 encoding
= newdesc_encode_game_description(n
, grid
);
715 static int dfs_count_white(game_state
*state
, int cell
);
717 static void newdesc_choose_black_squares(game_state
*state
,
718 const int *shuffle_1toN
)
720 int const w
= state
->params
.w
, h
= state
->params
.h
, n
= w
* h
;
722 int k
, any_white_cell
, n_black_cells
;
724 for (k
= 0; k
< n
; ++k
) state
->grid
[k
] = WHITE
;
726 any_white_cell
= shuffle_1toN
[n
- 1];
729 /* I like the puzzles that result from n / 3, but maybe this
730 * could be made a (generation, i.e. non-full) parameter? */
731 for (k
= 0; k
< n
/ 3; ++k
) {
732 int const i
= shuffle_1toN
[k
], c
= i
% w
, r
= i
/ w
;
735 for (j
= 0; j
< 4; ++j
) {
736 int const rr
= r
+ dr
[j
], cc
= c
+ dc
[j
], cell
= idx(rr
, cc
, w
);
737 /* if you're out of bounds, we skip you */
738 if (out_of_bounds(rr
, cc
, w
, h
)) continue;
739 if (state
->grid
[cell
] == BLACK
) break; /* I can't be black */
740 } if (j
< 4) continue; /* I have black neighbour: I'm white */
742 state
->grid
[i
] = BLACK
;
745 j
= dfs_count_white(state
, any_white_cell
);
746 if (j
+ n_black_cells
< n
) {
747 state
->grid
[i
] = WHITE
;
753 static void newdesc_compute_clues(game_state
*state
)
755 int const w
= state
->params
.w
, h
= state
->params
.h
;
758 for (r
= 0; r
< h
; ++r
) {
759 int run_size
= 0, c
, cc
;
760 for (c
= 0; c
<= w
; ++c
) {
761 if (c
== w
|| state
->grid
[idx(r
, c
, w
)] == BLACK
) {
762 for (cc
= c
- run_size
; cc
< c
; ++cc
)
763 state
->grid
[idx(r
, cc
, w
)] += run_size
;
769 for (c
= 0; c
< w
; ++c
) {
770 int run_size
= 0, r
, rr
;
771 for (r
= 0; r
<= h
; ++r
) {
772 if (r
== h
|| state
->grid
[idx(r
, c
, w
)] == BLACK
) {
773 for (rr
= r
- run_size
; rr
< r
; ++rr
)
774 state
->grid
[idx(rr
, c
, w
)] += run_size
;
781 #define rotate(x) (n - 1 - (x))
783 static int newdesc_strip_clues(game_state
*state
, int *shuffle_1toN
)
785 int const w
= state
->params
.w
, n
= w
* state
->params
.h
;
787 move
*const move_buffer
= snewn(n
, move
);
789 game_state
*dupstate
;
792 * do a partition/pivot of shuffle_1toN into three groups:
793 * (1) squares rotationally-symmetric to (3)
794 * (2) squares not in (1) or (3)
797 * They go from [0, left), [left, right) and [right, n) in
798 * shuffle_1toN (and from there into state->grid[ ])
800 * Then, remove clues from the grid one by one in shuffle_1toN
801 * order, until the solver becomes unhappy. If we didn't remove
802 * all of (1), return (-1). Else, we're happy.
805 /* do the partition */
806 int clues_removed
, k
= 0, left
= 0, right
= n
;
809 while (k
< right
&& state
->grid
[shuffle_1toN
[k
]] == BLACK
) {
811 SWAP(int, shuffle_1toN
[right
], shuffle_1toN
[k
]);
812 assert(state
->grid
[shuffle_1toN
[right
]] == BLACK
);
814 if (k
>= right
) break;
816 if (state
->grid
[rotate(shuffle_1toN
[k
])] == BLACK
) {
817 SWAP(int, shuffle_1toN
[k
], shuffle_1toN
[left
]);
820 assert (state
->grid
[rotate(shuffle_1toN
[k
])] != BLACK
824 for (k
= 0; k
< left
; ++k
) {
825 assert (state
->grid
[rotate(shuffle_1toN
[k
])] == BLACK
);
826 state
->grid
[shuffle_1toN
[k
]] = EMPTY
;
828 for (k
= left
; k
< right
; ++k
) {
829 assert (state
->grid
[rotate(shuffle_1toN
[k
])] != BLACK
);
830 assert (state
->grid
[shuffle_1toN
[k
]] != BLACK
);
832 for (k
= right
; k
< n
; ++k
) {
833 assert (state
->grid
[shuffle_1toN
[k
]] == BLACK
);
834 state
->grid
[shuffle_1toN
[k
]] = EMPTY
;
837 clues_removed
= (left
- 0) + (n
- right
);
839 dupstate
= dup_game(state
);
840 buf
= solve_internal(dupstate
, move_buffer
, DIFF_RECURSION
- 1);
842 if (buf
- move_buffer
< clues_removed
) {
843 /* branch prediction: I don't think I'll go here */
848 for (k
= left
; k
< right
; ++k
) {
849 const int i
= shuffle_1toN
[k
], j
= rotate(i
);
850 int const clue
= state
->grid
[i
], clue_rot
= state
->grid
[j
];
851 if (clue
== BLACK
) continue;
852 state
->grid
[i
] = state
->grid
[j
] = EMPTY
;
853 dupstate
= dup_game(state
);
854 buf
= solve_internal(dupstate
, move_buffer
, DIFF_RECURSION
- 1);
856 clues_removed
+= 2 - (i
== j
);
857 /* if i is the center square, then i == (j = rotate(i))
858 * when i and j are one, removing i and j removes only one */
859 if (buf
- move_buffer
== clues_removed
) continue;
860 /* if the solver is sound, refilling all removed clues means
861 * we have filled all squares, i.e. solved the puzzle. */
862 state
->grid
[i
] = clue
;
863 state
->grid
[j
] = clue_rot
;
864 clues_removed
-= 2 - (i
== j
);
869 return clues_removed
;
872 static int dfs_count_rec(puzzle_size
*grid
, int r
, int c
, int w
, int h
)
874 int const cell
= idx(r
, c
, w
);
875 if (out_of_bounds(r
, c
, w
, h
)) return 0;
876 if (grid
[cell
] != WHITE
) return 0;
879 dfs_count_rec(grid
, r
+ 0, c
+ 1, w
, h
) +
880 dfs_count_rec(grid
, r
+ 0, c
- 1, w
, h
) +
881 dfs_count_rec(grid
, r
+ 1, c
+ 0, w
, h
) +
882 dfs_count_rec(grid
, r
- 1, c
+ 0, w
, h
);
885 static int dfs_count_white(game_state
*state
, int cell
)
887 int const w
= state
->params
.w
, h
= state
->params
.h
, n
= w
* h
;
888 int const r
= cell
/ w
, c
= cell
% w
;
889 int i
, k
= dfs_count_rec(state
->grid
, r
, c
, w
, h
);
890 for (i
= 0; i
< n
; ++i
)
891 if (state
->grid
[i
] == EMPTY
)
892 state
->grid
[i
] = WHITE
;
896 static char *validate_params(game_params
*params
, int full
)
898 int const w
= params
->w
, h
= params
->h
;
899 if (w
< 1) return "Error: width is less than 1";
900 if (h
< 1) return "Error: height is less than 1";
901 if (w
* h
< 1) return "Error: size is less than 1";
902 if (w
+ h
- 1 > SCHAR_MAX
) return "Error: w + h is too big";
903 /* I might be unable to store clues in my puzzle_size *grid; */
905 if (w
== 2 && h
== 2) return "Error: can't create 2x2 puzzles";
906 if (w
== 1 && h
== 2) return "Error: can't create 1x2 puzzles";
907 if (w
== 2 && h
== 1) return "Error: can't create 2x1 puzzles";
908 if (w
== 1 && h
== 1) return "Error: can't create 1x1 puzzles";
913 /* Definition: a puzzle instance is _good_ if:
914 * - it has a unique solution
915 * - the solver can find this solution without using recursion
916 * - the solution contains at least one black square
917 * - the clues are 2-way rotationally symmetric
919 * (the idea being: the generator can not output any _bad_ puzzles)
921 * Theorem: validate_params, when full != 0, discards exactly the set
922 * of parameters for which there are _no_ good puzzle instances.
924 * Proof: it's an immediate consequence of the five lemmas below.
926 * Observation: not only do puzzles on non-tiny grids exist, the
927 * generator is pretty fast about coming up with them. On my pre-2004
928 * desktop box, it generates 100 puzzles on the highest preset (16x11)
929 * in 8.383 seconds, or <= 0.1 second per puzzle.
931 * ----------------------------------------------------------------------
933 * Lemma: On a 1x1 grid, there are no good puzzles.
935 * Proof: the one square can't be a clue because at least one square
936 * is black. But both a white square and a black square satisfy the
937 * solution criteria, so the puzzle is ambiguous (and hence bad).
939 * Lemma: On a 1x2 grid, there are no good puzzles.
941 * Proof: let's name the squares l and r. Note that there can be at
942 * most one black square, or adjacency is violated. By assumption at
943 * least one square is black, so let's call that one l. By clue
944 * symmetry, neither l nor r can be given as a clue, so the puzzle
945 * instance is blank and thus ambiguous.
947 * Corollary: On a 2x1 grid, there are no good puzzles.
948 * Proof: rotate the above proof 90 degrees ;-)
950 * ----------------------------------------------------------------------
952 * Lemma: On a 2x2 grid, there are no soluble puzzles with 2-way
953 * rotational symmetric clues and at least one black square.
955 * Proof: Let's name the squares a, b, c, and d, with a and b on the
956 * top row, a and c in the left column. Let's consider the case where
957 * a is black. Then no other square can be black: b and c would both
958 * violate the adjacency constraint; d would disconnect b from c.
960 * So exactly one square is black (and by 4-way rotation symmetry of
961 * the 2x2 square, it doesn't matter which one, so let's stick to a).
962 * By 2-way rotational symmetry of the clues and the rule about not
963 * painting numbers black, neither a nor d can be clues. A blank
964 * puzzle would be ambiguous, so one of {b, c} is a clue; by symmetry,
965 * so is the other one.
967 * It is readily seen that their clue value is 2. But "a is black"
968 * and "d is black" are both valid solutions in this case, so the
969 * puzzle is ambiguous (and hence bad).
971 * ----------------------------------------------------------------------
973 * Lemma: On a wxh grid with w, h >= 1 and (w > 2 or h > 2), there is
974 * at least one good puzzle.
976 * Proof: assume that w > h (otherwise rotate the proof again). Paint
977 * the top left and bottom right corners black, and fill a clue into
978 * all the other squares. Present this board to the solver code (or
979 * player, hypothetically), except with the two black squares as blank
982 * For an Nx1 puzzle, observe that every clue is N - 2, and there are
983 * N - 2 of them in one connected sequence, so the remaining two
984 * squares can be deduced to be black, which solves the puzzle.
986 * For any other puzzle, let j be a cell in the same row as a black
987 * cell, but not in the same column (such a cell doesn't exist in 2x3
988 * puzzles, but we assume w > h and such cells exist in 3x2 puzzles).
990 * Note that the number of cells in axis parallel `rays' going out
991 * from j exceeds j's clue value by one. Only one such cell is a
992 * non-clue, so it must be black. Similarly for the other corner (let
993 * j' be a cell in the same row as the _other_ black cell, but not in
994 * the same column as _any_ black cell; repeat this argument at j').
996 * This fills the grid and satisfies all clues and the adjacency
997 * constraint and doesn't paint on top of any clues. All that is left
998 * to see is connectedness.
1000 * Observe that the white cells in each column form a single connected
1001 * `run', and each column contains a white cell adjacent to a white
1002 * cell in the column to the right, if that column exists.
1004 * Thus, any cell in the left-most column can reach any other cell:
1005 * first go to the target column (by repeatedly going to the cell in
1006 * your current column that lets you go right, then going right), then
1007 * go up or down to the desired cell.
1009 * As reachability is symmetric (in undirected graphs) and transitive,
1010 * any cell can reach any left-column cell, and from there any other
1014 /* ----------------------------------------------------------------------
1015 * Game encoding and decoding
1018 #define NDIGITS_BASE '!'
1020 static char *newdesc_encode_game_description(int area
, puzzle_size
*grid
)
1023 int desclen
= 0, descsize
= 0;
1027 for (i
= 0; i
<= area
; i
++) {
1028 int n
= (i
< area
? grid
[i
] : -1);
1033 if (descsize
< desclen
+ 40) {
1034 descsize
= desclen
* 3 / 2 + 40;
1035 desc
= sresize(desc
, descsize
, char);
1039 int c
= 'a' - 1 + run
;
1042 desc
[desclen
++] = c
;
1043 run
-= c
- ('a' - 1);
1047 * If there's a number in the very top left or
1048 * bottom right, there's no point putting an
1049 * unnecessary _ before or after it.
1051 if (desclen
> 0 && n
> 0)
1052 desc
[desclen
++] = '_';
1055 desclen
+= sprintf(desc
+desclen
, "%d", n
);
1059 desc
[desclen
] = '\0';
1063 static char *validate_desc(game_params
*params
, char *desc
)
1065 int const n
= params
->w
* params
->h
;
1067 int range
= params
->w
+ params
->h
- 1; /* maximum cell value */
1069 while (*desc
&& *desc
!= ',') {
1071 if (c
>= 'a' && c
<= 'z') {
1072 squares
+= c
- 'a' + 1;
1073 } else if (c
== '_') {
1075 } else if (c
> '0' && c
<= '9') {
1076 int val
= atoi(desc
-1);
1077 if (val
< 1 || val
> range
)
1078 return "Out-of-range number in game description";
1080 while (*desc
>= '0' && *desc
<= '9')
1083 return "Invalid character in game description";
1087 return "Not enough data to fill grid";
1090 return "Too much data to fit in grid";
1095 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
1100 int const n
= params
->w
* params
->h
;
1101 game_state
*state
= snew(game_state
);
1103 me
= NULL
; /* I don't need it, I shouldn't use it */
1105 state
->params
= *params
; /* structure copy */
1106 state
->grid
= snewn(n
, puzzle_size
);
1110 while (i
< n
&& *p
) {
1112 if (c
>= 'a' && c
<= 'z') {
1113 int squares
= c
- 'a' + 1;
1115 state
->grid
[i
++] = 0;
1116 } else if (c
== '_') {
1118 } else if (c
> '0' && c
<= '9') {
1119 int val
= atoi(p
-1);
1120 assert(val
>= 1 && val
<= params
->w
+params
->h
-1);
1121 state
->grid
[i
++] = val
;
1122 while (*p
>= '0' && *p
<= '9')
1127 state
->has_cheated
= FALSE
;
1128 state
->was_solved
= FALSE
;
1133 /* ----------------------------------------------------------------------
1134 * User interface: ascii
1137 static int game_can_format_as_text_now(game_params
*params
)
1142 static char *game_text_format(game_state
*state
)
1144 int cellsize
, r
, c
, i
, w_string
, h_string
, n_string
;
1145 char *ret
, *buf
, *gridline
;
1147 int const w
= state
->params
.w
, h
= state
->params
.h
;
1149 cellsize
= 0; /* or may be used uninitialized */
1151 for (c
= 0; c
< w
; ++c
) {
1152 for (r
= 1; r
< h
; ++r
) {
1153 puzzle_size k
= state
->grid
[idx(r
, c
, w
)];
1155 for (d
= 0; k
; k
/= 10, ++d
);
1156 cellsize
= max(cellsize
, d
);
1162 w_string
= w
* cellsize
+ 2; /* "|%d|%d|...|\n" */
1163 h_string
= 2 * h
+ 1; /* "+--+--+...+\n%s\n+--+--+...+\n" */
1164 n_string
= w_string
* h_string
;
1166 gridline
= snewn(w_string
+ 1, char); /* +1: NUL terminator */
1167 memset(gridline
, '-', w_string
);
1168 for (c
= 0; c
<= w
; ++c
) gridline
[c
* cellsize
] = '+';
1169 gridline
[w_string
- 1] = '\n';
1170 gridline
[w_string
- 0] = '\0';
1172 buf
= ret
= snewn(n_string
+ 1, char); /* +1: NUL terminator */
1173 for (i
= r
= 0; r
< h
; ++r
) {
1174 memcpy(buf
, gridline
, w_string
);
1176 for (c
= 0; c
< w
; ++c
, ++i
) {
1178 switch (state
->grid
[i
]) {
1179 case BLACK
: ch
= '#'; break;
1180 case WHITE
: ch
= '.'; break;
1181 case EMPTY
: ch
= ' '; break;
1183 buf
+= sprintf(buf
, "|%*d", cellsize
- 1, state
->grid
[i
]);
1187 memset(buf
, ch
, cellsize
- 1);
1188 buf
+= cellsize
- 1;
1190 buf
+= sprintf(buf
, "|\n");
1192 memcpy(buf
, gridline
, w_string
);
1194 assert (buf
- ret
== n_string
);
1202 /* ----------------------------------------------------------------------
1203 * User interfaces: interactive
1207 puzzle_size r
, c
; /* cursor position */
1208 unsigned int cursor_show
: 1;
1211 static game_ui
*new_ui(game_state
*state
)
1213 struct game_ui
*ui
= snew(game_ui
);
1215 ui
->cursor_show
= FALSE
;
1219 static void free_ui(game_ui
*ui
)
1224 static char *encode_ui(game_ui
*ui
)
1229 static void decode_ui(game_ui
*ui
, char *encoding
)
1233 typedef struct drawcell
{
1235 unsigned int error
: 1;
1236 unsigned int cursor
: 1;
1237 unsigned int flash
: 1;
1240 struct game_drawstate
{
1243 unsigned int started
: 1;
1246 #define TILESIZE (ds->tilesize)
1247 #define BORDER (TILESIZE / 2)
1248 #define COORD(x) ((x) * TILESIZE + BORDER)
1249 #define FROMCOORD(x) (((x) - BORDER) / TILESIZE)
1251 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
1252 int x
, int y
, int button
)
1254 enum {none
, forwards
, backwards
, hint
};
1255 int const w
= state
->params
.w
, h
= state
->params
.h
;
1256 int r
= ui
->r
, c
= ui
->c
, action
= none
, cell
;
1258 if (IS_CURSOR_SELECT(button
) && !ui
->cursor_show
) return NULL
;
1260 if (IS_MOUSE_DOWN(button
)) {
1261 r
= FROMCOORD(y
+ TILESIZE
) - 1; /* or (x, y) < TILESIZE) */
1262 c
= FROMCOORD(x
+ TILESIZE
) - 1; /* are considered inside */
1263 if (out_of_bounds(r
, c
, w
, h
)) return NULL
;
1266 ui
->cursor_show
= FALSE
;
1269 if (button
== LEFT_BUTTON
|| button
== RIGHT_BUTTON
) {
1271 * Utterly awful hack, exactly analogous to the one in Slant,
1272 * to configure the left and right mouse buttons the opposite
1275 * The original puzzle submitter thought it would be more
1276 * useful to have the left button turn an empty square into a
1277 * dotted one, on the grounds that that was what you did most
1278 * often; I (SGT) felt instinctively that the left button
1279 * ought to place black squares and the right button place
1280 * dots, on the grounds that that was consistent with many
1281 * other puzzles in which the left button fills in the data
1282 * used by the solution checker while the right button places
1283 * pencil marks for the user's convenience.
1285 * My first beta-player wasn't sure either, so I thought I'd
1286 * pre-emptively put in a 'configuration' mechanism just in
1290 static int swap_buttons
= -1;
1291 if (swap_buttons
< 0) {
1292 char *env
= getenv("RANGE_SWAP_BUTTONS");
1293 swap_buttons
= (env
&& (env
[0] == 'y' || env
[0] == 'Y'));
1296 if (button
== LEFT_BUTTON
)
1297 button
= RIGHT_BUTTON
;
1299 button
= LEFT_BUTTON
;
1305 case CURSOR_SELECT
: case LEFT_BUTTON
: action
= backwards
; break;
1306 case CURSOR_SELECT2
: case RIGHT_BUTTON
: action
= forwards
; break;
1307 case 'h': case 'H' : action
= hint
; break;
1308 case CURSOR_UP
: case CURSOR_DOWN
:
1309 case CURSOR_LEFT
: case CURSOR_RIGHT
:
1310 if (ui
->cursor_show
) {
1312 for (i
= 0; i
< 4 && cursors
[i
] != button
; ++i
);
1314 if (!out_of_bounds(ui
->r
+ dr
[i
], ui
->c
+ dc
[i
], w
, h
)) {
1318 } else ui
->cursor_show
= TRUE
;
1322 if (action
== hint
) {
1323 move
*end
, *buf
= snewn(state
->params
.w
* state
->params
.h
,
1326 end
= solve_internal(state
, buf
, DIFF_RECURSION
);
1327 if (end
!= NULL
&& end
> buf
) {
1328 ret
= nfmtstr(40, "%c,%d,%d",
1329 buf
->colour
== M_BLACK
? 'B' : 'W',
1330 buf
->square
.r
, buf
->square
.c
);
1331 /* We used to set a flag here in the game_ui indicating
1332 * that the player had used the hint function. I (SGT)
1333 * retired it, on grounds of consistency with other games
1334 * (most of these games will still flash to indicate
1335 * completion if you solved and undid it, so why not if
1336 * you got a hint?) and because the flash is as much about
1337 * checking you got it all right than about congratulating
1338 * you on a job well done. */
1344 cell
= state
->grid
[idx(r
, c
, state
->params
.w
)];
1345 if (cell
> 0) return NULL
;
1347 if (action
== forwards
) switch (cell
) {
1348 case EMPTY
: return nfmtstr(40, "W,%d,%d", r
, c
);
1349 case WHITE
: return nfmtstr(40, "B,%d,%d", r
, c
);
1350 case BLACK
: return nfmtstr(40, "E,%d,%d", r
, c
);
1353 else if (action
== backwards
) switch (cell
) {
1354 case BLACK
: return nfmtstr(40, "W,%d,%d", r
, c
);
1355 case WHITE
: return nfmtstr(40, "E,%d,%d", r
, c
);
1356 case EMPTY
: return nfmtstr(40, "B,%d,%d", r
, c
);
1362 static int find_errors(game_state
*state
, int *report
)
1364 int const w
= state
->params
.w
, h
= state
->params
.h
, n
= w
* h
;
1368 int nblack
= 0, any_white_cell
= -1;
1369 game_state
*dup
= dup_game(state
);
1371 for (i
= r
= 0; r
< h
; ++r
)
1372 for (c
= 0; c
< w
; ++c
, ++i
) {
1373 switch (state
->grid
[i
]) {
1379 for (j
= 0; j
< 4; ++j
) {
1380 int const rr
= r
+ dr
[j
], cc
= c
+ dc
[j
];
1381 if (out_of_bounds(rr
, cc
, w
, h
)) continue;
1382 if (state
->grid
[idx(rr
, cc
, w
)] != BLACK
) continue;
1383 if (!report
) goto found_error
;
1392 for (runs
= 1, j
= 0; j
< 4; ++j
) {
1393 int const rr
= r
+ dr
[j
], cc
= c
+ dc
[j
];
1394 runs
+= runlength(rr
, cc
, dr
[j
], dc
[j
], state
,
1398 if (runs
!= state
->grid
[i
]) goto found_error
;
1399 } else if (runs
< state
->grid
[i
]) report
[i
] = TRUE
;
1401 for (runs
= 1, j
= 0; j
< 4; ++j
) {
1402 int const rr
= r
+ dr
[j
], cc
= c
+ dc
[j
];
1403 runs
+= runlength(rr
, cc
, dr
[j
], dc
[j
], state
,
1404 ~(MASK(BLACK
) | MASK(EMPTY
)));
1406 if (runs
> state
->grid
[i
]) report
[i
] = TRUE
;
1410 /* note: fallthrough _into_ these cases */
1412 case WHITE
: any_white_cell
= i
;
1416 for (i
= 0; i
< n
; ++i
) if (dup
->grid
[i
] != BLACK
) dup
->grid
[i
] = WHITE
;
1417 if (nblack
+ dfs_count_white(dup
, any_white_cell
) < n
) {
1419 printf("dfs fail at %d\n", any_white_cell
);
1422 for (i
= 0; i
< n
; ++i
) if (state
->grid
[i
] != BLACK
) report
[i
] = TRUE
;
1426 return FALSE
; /* if report != NULL, this is ignored */
1433 static game_state
*execute_move(game_state
*state
, char *move
)
1435 signed int r
, c
, value
, nchars
, ntok
;
1436 signed char what_to_do
;
1441 ret
= dup_game(state
);
1445 ret
->has_cheated
= ret
->was_solved
= TRUE
;
1448 for (; *move
; move
+= nchars
) {
1449 ntok
= sscanf(move
, "%c,%d,%d%n", &what_to_do
, &r
, &c
, &nchars
);
1450 if (ntok
< 3) goto failure
;
1451 switch (what_to_do
) {
1452 case 'W': value
= WHITE
; break;
1453 case 'E': value
= EMPTY
; break;
1454 case 'B': value
= BLACK
; break;
1455 default: goto failure
;
1457 if (out_of_bounds(r
, c
, ret
->params
.w
, ret
->params
.h
)) goto failure
;
1458 ret
->grid
[idx(r
, c
, ret
->params
.w
)] = value
;
1461 if (ret
->was_solved
== FALSE
)
1462 ret
->was_solved
= !find_errors(ret
, NULL
);
1471 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
1472 game_state
*newstate
)
1476 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
1477 int dir
, game_ui
*ui
)
1482 #define FLASH_TIME 0.7F
1484 static float game_flash_length(game_state
*from
, game_state
*to
,
1485 int dir
, game_ui
*ui
)
1487 if (!from
->was_solved
&& to
->was_solved
&& !to
->has_cheated
)
1492 static int game_status(game_state
*state
)
1494 return state
->was_solved
? +1 : 0;
1497 /* ----------------------------------------------------------------------
1501 #define PREFERRED_TILE_SIZE 32
1506 COL_BLACK
= COL_GRID
,
1507 COL_TEXT
= COL_GRID
,
1508 COL_USER
= COL_GRID
,
1511 COL_HIGHLIGHT
= COL_ERROR
, /* mkhighlight needs it, I don't */
1512 COL_CURSOR
= COL_LOWLIGHT
,
1516 static void game_compute_size(game_params
*params
, int tilesize
,
1519 *x
= (1 + params
->w
) * tilesize
;
1520 *y
= (1 + params
->h
) * tilesize
;
1523 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
1524 game_params
*params
, int tilesize
)
1526 ds
->tilesize
= tilesize
;
1529 #define COLOUR(ret, i, r, g, b) \
1530 ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b)))
1532 static float *game_colours(frontend
*fe
, int *ncolours
)
1534 float *ret
= snewn(3 * NCOLOURS
, float);
1536 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
1537 COLOUR(ret
, COL_GRID
, 0.0F
, 0.0F
, 0.0F
);
1538 COLOUR(ret
, COL_ERROR
, 1.0F
, 0.0F
, 0.0F
);
1540 *ncolours
= NCOLOURS
;
1544 static drawcell
makecell(puzzle_size value
, int error
, int cursor
, int flash
)
1547 setmember(ret
, value
);
1548 setmember(ret
, error
);
1549 setmember(ret
, cursor
);
1550 setmember(ret
, flash
);
1554 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1556 int const w
= state
->params
.w
, h
= state
->params
.h
, n
= w
* h
;
1557 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1561 ds
->started
= FALSE
;
1563 ds
->grid
= snewn(n
, drawcell
);
1564 for (i
= 0; i
< n
; ++i
)
1565 ds
->grid
[i
] = makecell(w
+ h
, FALSE
, FALSE
, FALSE
);
1570 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1576 #define cmpmember(a, b, field) ((a) . field == (b) . field)
1578 static int cell_eq(drawcell a
, drawcell b
)
1581 cmpmember(a
, b
, value
) &&
1582 cmpmember(a
, b
, error
) &&
1583 cmpmember(a
, b
, cursor
) &&
1584 cmpmember(a
, b
, flash
);
1587 static void draw_cell(drawing
*dr
, game_drawstate
*ds
, int r
, int c
,
1590 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
1591 game_state
*state
, int dir
, game_ui
*ui
,
1592 float animtime
, float flashtime
)
1594 int const w
= state
->params
.w
, h
= state
->params
.h
, n
= w
* h
;
1595 int const wpx
= (w
+1) * ds
->tilesize
, hpx
= (h
+1) * ds
->tilesize
;
1596 int const flash
= ((int) (flashtime
* 5 / FLASH_TIME
)) % 2;
1600 int *errors
= snewn(n
, int);
1601 memset(errors
, FALSE
, n
* sizeof (int));
1602 find_errors(state
, errors
);
1604 assert (oldstate
== NULL
); /* only happens if animating moves */
1608 draw_rect(dr
, 0, 0, wpx
, hpx
, COL_BACKGROUND
);
1609 draw_rect(dr
, BORDER
-1, BORDER
-1,
1610 ds
->tilesize
*w
+2, ds
->tilesize
*h
+2, COL_GRID
);
1611 draw_update(dr
, 0, 0, wpx
, hpx
);
1614 for (i
= r
= 0; r
< h
; ++r
) {
1615 for (c
= 0; c
< w
; ++c
, ++i
) {
1616 drawcell cell
= makecell(state
->grid
[i
], errors
[i
], FALSE
, flash
);
1617 if (r
== ui
->r
&& c
== ui
->c
&& ui
->cursor_show
)
1619 if (!cell_eq(cell
, ds
->grid
[i
])) {
1620 draw_cell(dr
, ds
, r
, c
, cell
);
1629 static void draw_cell(drawing
*draw
, game_drawstate
*ds
, int r
, int c
,
1632 int const ts
= ds
->tilesize
;
1633 int const y
= BORDER
+ ts
* r
, x
= BORDER
+ ts
* c
;
1634 int const tx
= x
+ (ts
/ 2), ty
= y
+ (ts
/ 2);
1635 int const dotsz
= (ds
->tilesize
+ 9) / 10;
1637 int const colour
= (cell
.value
== BLACK
?
1638 cell
.error
? COL_ERROR
: COL_BLACK
:
1639 cell
.flash
|| cell
.cursor
?
1640 COL_LOWLIGHT
: COL_BACKGROUND
);
1642 draw_rect (draw
, x
, y
, ts
, ts
, colour
);
1643 draw_rect_outline(draw
, x
, y
, ts
, ts
, COL_GRID
);
1645 switch (cell
.value
) {
1646 case WHITE
: draw_rect(draw
, tx
- dotsz
/ 2, ty
- dotsz
/ 2, dotsz
, dotsz
,
1647 cell
.error
? COL_ERROR
: COL_USER
);
1651 draw_circle(draw
, tx
, ty
, dotsz
/ 2, COL_ERROR
, COL_GRID
);
1655 int const colour
= (cell
.error
? COL_ERROR
: COL_GRID
);
1656 char *msg
= nfmtstr(10, "%d", cell
.value
);
1657 draw_text(draw
, tx
, ty
, FONT_VARIABLE
, ts
* 3 / 5,
1658 ALIGN_VCENTRE
| ALIGN_HCENTRE
, colour
, msg
);
1663 draw_update(draw
, x
, y
, ts
, ts
);
1666 static int game_timing_state(game_state
*state
, game_ui
*ui
)
1668 puts("warning: game_timing_state was called (this shouldn't happen)");
1669 return FALSE
; /* the (non-existing) timer should not be running */
1672 /* ----------------------------------------------------------------------
1673 * User interface: print
1676 static void game_print_size(game_params
*params
, float *x
, float *y
)
1678 int print_width
, print_height
;
1679 game_compute_size(params
, 800, &print_width
, &print_height
);
1680 *x
= print_width
/ 100.0F
;
1681 *y
= print_height
/ 100.0F
;
1684 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
1686 int const w
= state
->params
.w
, h
= state
->params
.h
;
1687 game_drawstate ds_obj
, *ds
= &ds_obj
;
1688 int r
, c
, i
, colour
;
1690 ds
->tilesize
= tilesize
;
1692 colour
= print_mono_colour(dr
, 1); assert(colour
== COL_BACKGROUND
);
1693 colour
= print_mono_colour(dr
, 0); assert(colour
== COL_GRID
);
1694 colour
= print_mono_colour(dr
, 1); assert(colour
== COL_ERROR
);
1695 colour
= print_mono_colour(dr
, 0); assert(colour
== COL_LOWLIGHT
);
1696 colour
= print_mono_colour(dr
, 0); assert(colour
== NCOLOURS
);
1698 for (i
= r
= 0; r
< h
; ++r
)
1699 for (c
= 0; c
< w
; ++c
, ++i
)
1700 draw_cell(dr
, ds
, r
, c
,
1701 makecell(state
->grid
[i
], FALSE
, FALSE
, FALSE
));
1703 print_line_width(dr
, 3 * tilesize
/ 40);
1704 draw_rect_outline(dr
, BORDER
, BORDER
, w
*TILESIZE
, h
*TILESIZE
, COL_GRID
);
1707 /* And that's about it ;-) **************************************************/
1710 #define thegame range
1713 struct game
const thegame
= {
1714 "Range", "games.range", "range",
1721 TRUE
, game_configure
, custom_params
,
1729 TRUE
, game_can_format_as_text_now
, game_text_format
,
1737 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
1740 game_free_drawstate
,
1745 TRUE
, FALSE
, game_print_size
, game_print
,
1746 FALSE
, /* wants_statusbar */
1747 FALSE
, game_timing_state
,