A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/*
2 * range.c: implementation of the Nikoli game 'Kurodoko' / 'Kuromasu'.
3 */
4
5/*
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
8 * black, such that:
9 *
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.
16 */
17
18/* example instance with its encoding and textual representation, both
19 * solved and unsolved (made by thegame.solve and thegame.text_format)
20 *
21 * +--+--+--+--+--+--+--+
22 * | | | | | 7| | |
23 * +--+--+--+--+--+--+--+
24 * | 3| | | | | | 8|
25 * +--+--+--+--+--+--+--+
26 * | | | | | | 5| |
27 * +--+--+--+--+--+--+--+
28 * | | | 7| | 7| | |
29 * +--+--+--+--+--+--+--+
30 * | |13| | | | | |
31 * +--+--+--+--+--+--+--+
32 * | 4| | | | | | 8|
33 * +--+--+--+--+--+--+--+
34 * | | | 4| | | | |
35 * +--+--+--+--+--+--+--+
36 *
37 * 7x7:d7b3e8e5c7a7c13e4d8b4d
38 *
39 * +--+--+--+--+--+--+--+
40 * |..|..|..|..| 7|..|..|
41 * +--+--+--+--+--+--+--+
42 * | 3|..|##|..|##|..| 8|
43 * +--+--+--+--+--+--+--+
44 * |##|..|..|##|..| 5|..|
45 * +--+--+--+--+--+--+--+
46 * |..|..| 7|..| 7|##|..|
47 * +--+--+--+--+--+--+--+
48 * |..|13|..|..|..|..|..|
49 * +--+--+--+--+--+--+--+
50 * | 4|..|##|..|##|..| 8|
51 * +--+--+--+--+--+--+--+
52 * |##|..| 4|..|..|##|..|
53 * +--+--+--+--+--+--+--+
54 */
55
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <assert.h>
60#include <ctype.h>
61#ifdef NO_TGMATH_H
62# include <math.h>
63#else
64# include <tgmath.h>
65#endif
66
67#include "puzzles.h"
68
69#include <stdarg.h>
70
71#define setmember(obj, field) ( (obj) . field = field )
72
73static char *nfmtstr(int n, const char *fmt, ...) {
74 va_list va;
75 char *ret = snewn(n+1, char);
76 va_start(va, fmt);
77 vsprintf(ret, fmt, va);
78 va_end(va);
79 return ret;
80}
81
82#define SWAP(type, lvar1, lvar2) do { \
83 type tmp = (lvar1); \
84 (lvar1) = (lvar2); \
85 (lvar2) = tmp; \
86} while (0)
87
88/* ----------------------------------------------------------------------
89 * Game parameters, presets, states
90 */
91
92typedef signed char puzzle_size;
93
94struct game_params {
95 puzzle_size w;
96 puzzle_size h;
97};
98
99enum {
100 PREF_MOUSE_BUTTON_ORDER,
101 N_PREF_ITEMS
102};
103
104struct game_state {
105 struct game_params params;
106 bool has_cheated, was_solved;
107 puzzle_size *grid;
108};
109
110#define DEFAULT_PRESET 0
111static struct game_params range_presets[] = {{9, 6}, {12, 8}, {13, 9}, {16, 11}};
112/* rationale: I want all four combinations of {odd/even, odd/even}, as
113 * they play out differently with respect to two-way symmetry. I also
114 * want them to be generated relatively fast yet still be large enough
115 * to be entertaining for a decent amount of time, and I want them to
116 * make good use of monitor real estate (the typical screen resolution
117 * is why I do 13x9 and not 9x13).
118 */
119
120static game_params *default_params(void)
121{
122 game_params *ret = snew(game_params);
123 *ret = range_presets[DEFAULT_PRESET]; /* structure copy */
124 return ret;
125}
126
127static game_params *dup_params(const game_params *params)
128{
129 game_params *ret = snew(game_params);
130 *ret = *params; /* structure copy */
131 return ret;
132}
133
134static bool game_fetch_preset(int i, char **name, game_params **params)
135{
136 game_params *ret;
137
138 if (i < 0 || i >= lenof(range_presets)) return false;
139
140 ret = default_params();
141 *ret = range_presets[i]; /* struct copy */
142 *params = ret;
143
144 *name = nfmtstr(40, "%d x %d", range_presets[i].w, range_presets[i].h);
145
146 return true;
147}
148
149static void free_params(game_params *params)
150{
151 sfree(params);
152}
153
154static void decode_params(game_params *params, char const *string)
155{
156 /* FIXME check for puzzle_size overflow and decoding issues */
157 params->w = params->h = atoi(string);
158 while (*string && isdigit((unsigned char) *string)) ++string;
159 if (*string == 'x') {
160 string++;
161 params->h = atoi(string);
162 while (*string && isdigit((unsigned char)*string)) string++;
163 }
164}
165
166static char *encode_params(const game_params *params, bool full)
167{
168 char str[80];
169 sprintf(str, "%dx%d", params->w, params->h);
170 return dupstr(str);
171}
172
173static config_item *game_configure(const game_params *params)
174{
175 config_item *ret;
176
177 ret = snewn(3, config_item);
178
179 ret[0].name = "Width";
180 ret[0].type = C_STRING;
181 ret[0].u.string.sval = nfmtstr(10, "%d", params->w);
182
183 ret[1].name = "Height";
184 ret[1].type = C_STRING;
185 ret[1].u.string.sval = nfmtstr(10, "%d", params->h);
186
187 ret[2].name = NULL;
188 ret[2].type = C_END;
189
190 return ret;
191}
192
193static game_params *custom_params(const config_item *configuration)
194{
195 game_params *ret = snew(game_params);
196 ret->w = atoi(configuration[0].u.string.sval);
197 ret->h = atoi(configuration[1].u.string.sval);
198 return ret;
199}
200
201#define memdup(dst, src, n, type) do { \
202 dst = snewn(n, type); \
203 memcpy(dst, src, n * sizeof (type)); \
204} while (0)
205
206static game_state *dup_game(const game_state *state)
207{
208 game_state *ret = snew(game_state);
209 int const n = state->params.w * state->params.h;
210
211 *ret = *state; /* structure copy */
212
213 /* copy the poin_tee_, set a new value of the poin_ter_ */
214 memdup(ret->grid, state->grid, n, puzzle_size);
215
216 return ret;
217}
218
219static void free_game(game_state *state)
220{
221 sfree(state->grid);
222 sfree(state);
223}
224
225
226/* ----------------------------------------------------------------------
227 * The solver subsystem.
228 *
229 * The solver is used for two purposes:
230 * - To solve puzzles when the user selects `Solve'.
231 * - To test solubility of a grid as clues are being removed from it
232 * during the puzzle generation.
233 *
234 * It supports the following ways of reasoning:
235 *
236 * - A cell adjacent to a black cell must be white.
237 *
238 * - If painting a square black would bisect the white regions, that
239 * square is white (by finding biconnected components' cut points)
240 *
241 * - A cell with number n, covering at most k white squares in three
242 * directions must white-cover n-k squares in the last direction.
243 *
244 * - A cell with number n known to cover k squares, if extending the
245 * cover by one square in a given direction causes the cell to
246 * cover _more_ than n squares, that extension cell must be black.
247 *
248 * (either if the square already covers n, or if it extends into a
249 * chunk of size > n - k)
250 *
251 * - Recursion. Pick any cell and see if this leads to either a
252 * contradiction or a solution (and then act appropriately).
253 *
254 *
255 * TODO:
256 *
257 * (propagation upper limit)
258 * - If one has two numbers on the same line, the smaller limits the
259 * larger. Example: in |b|_|_|8|4|_|_|b|, only two _'s can be both
260 * white and connected to the "8" cell; so that cell will propagate
261 * at least four cells orthogonally to the displayed line (which is
262 * better than the current "at least 2").
263 *
264 * (propagation upper limit)
265 * - cells can't propagate into other cells if doing so exceeds that
266 * number. Example: in |b|4|.|.|2|b|, at most one _ can be white;
267 * otherwise, the |2| would have too many reaching white cells.
268 *
269 * (propagation lower and upper limit)
270 * - `Full Combo': in each four directions d_1 ... d_4, find a set of
271 * possible propagation distances S_1 ... S_4. For each i=1..4,
272 * for each x in S_i: if not exists (y, z, w) in the other sets
273 * such that (x+y+z+w+1 == clue value): then remove x from S_i.
274 * Repeat until this stabilizes. If any cell would contradict
275 */
276
277#define idx(i, j, w) ((i)*(w) + (j))
278#define out_of_bounds(r, c, w, h) \
279 ((r) < 0 || (r) >= h || (c) < 0 || (c) >= w)
280
281typedef struct square {
282 puzzle_size r, c;
283} square;
284
285enum {BLACK = -2, WHITE, EMPTY};
286/* white is for pencil marks, empty is undecided */
287
288static int const dr[4] = {+1, 0, -1, 0};
289static int const dc[4] = { 0, +1, 0, -1};
290static int const cursors[4] = /* must match dr and dc */
291{CURSOR_DOWN, CURSOR_RIGHT, CURSOR_UP, CURSOR_LEFT};
292
293typedef struct move {
294 square square;
295 unsigned int colour: 1;
296} move;
297enum {M_BLACK = 0, M_WHITE = 1};
298
299typedef move *(reasoning)(game_state *state,
300 int nclues,
301 const square *clues,
302 move *buf);
303
304static reasoning solver_reasoning_not_too_big;
305static reasoning solver_reasoning_adjacency;
306static reasoning solver_reasoning_connectedness;
307static reasoning solver_reasoning_recursion;
308
309enum {
310 DIFF_NOT_TOO_BIG,
311 DIFF_ADJACENCY,
312 DIFF_CONNECTEDNESS,
313 DIFF_RECURSION
314};
315
316static move *solve_internal(const game_state *state, move *base, int diff);
317
318static char *solve_game(const game_state *orig, const game_state *curpos,
319 const char *aux, const char **error)
320{
321 int const n = orig->params.w * orig->params.h;
322 move *const base = snewn(n, move);
323 move *moves = solve_internal(orig, base, DIFF_RECURSION);
324
325 char *ret = NULL;
326
327 if (moves != NULL) {
328 int const k = moves - base;
329 char *str = ret = snewn(15*k + 2, char);
330 char colour[2] = "BW";
331 move *it;
332 *str++ = 'S';
333 *str = '\0';
334 for (it = base; it < moves; ++it)
335 str += sprintf(str, "%c,%d,%d", colour[it->colour],
336 it->square.r, it->square.c);
337 } else *error = "This puzzle instance contains a contradiction";
338
339 sfree(base);
340 return ret;
341}
342
343static square *find_clues(const game_state *state, int *ret_nclues);
344static move *do_solve(game_state *state,
345 int nclues,
346 const square *clues,
347 move *move_buffer,
348 int difficulty);
349
350/* new_game_desc entry point in the solver subsystem */
351static move *solve_internal(const game_state *state, move *base, int diff)
352{
353 int nclues;
354 square *const clues = find_clues(state, &nclues);
355 game_state *dup = dup_game(state);
356 move *const moves = do_solve(dup, nclues, clues, base, diff);
357 free_game(dup);
358 sfree(clues);
359 return moves;
360}
361
362static reasoning *const reasonings[] = {
363 solver_reasoning_not_too_big,
364 solver_reasoning_adjacency,
365 solver_reasoning_connectedness,
366 solver_reasoning_recursion
367};
368
369static move *do_solve(game_state *state,
370 int nclues,
371 const square *clues,
372 move *move_buffer,
373 int difficulty)
374{
375 struct move *buf = move_buffer, *oldbuf;
376 int i;
377
378 do {
379 oldbuf = buf;
380 for (i = 0; i < lenof(reasonings) && i <= difficulty; ++i) {
381 /* only recurse if all else fails */
382 if (i == DIFF_RECURSION && buf > oldbuf) continue;
383 buf = (*reasonings[i])(state, nclues, clues, buf);
384 if (buf == NULL) return NULL;
385 }
386 } while (buf > oldbuf);
387
388 return buf;
389}
390
391#define MASK(n) (1 << ((n) + 2))
392
393static int runlength(puzzle_size r, puzzle_size c,
394 puzzle_size dr, puzzle_size dc,
395 const game_state *state, int colourmask)
396{
397 int const w = state->params.w, h = state->params.h;
398 int sz = 0;
399 while (true) {
400 int cell = idx(r, c, w);
401 if (out_of_bounds(r, c, w, h)) break;
402 if (state->grid[cell] > 0) {
403 if (!(colourmask & ~(MASK(BLACK) | MASK(WHITE) | MASK(EMPTY))))
404 break;
405 } else if (!(MASK(state->grid[cell]) & colourmask)) break;
406 ++sz;
407 r += dr;
408 c += dc;
409 }
410 return sz;
411}
412
413static void solver_makemove(puzzle_size r, puzzle_size c, int colour,
414 game_state *state, move **buffer_ptr)
415{
416 int const cell = idx(r, c, state->params.w);
417 if (out_of_bounds(r, c, state->params.w, state->params.h)) return;
418 if (state->grid[cell] != EMPTY) return;
419 setmember((*buffer_ptr)->square, r);
420 setmember((*buffer_ptr)->square, c);
421 setmember(**buffer_ptr, colour);
422 ++*buffer_ptr;
423 state->grid[cell] = (colour == M_BLACK ? BLACK : WHITE);
424}
425
426static move *solver_reasoning_adjacency(game_state *state,
427 int nclues,
428 const square *clues,
429 move *buf)
430{
431 int r, c, i;
432 for (r = 0; r < state->params.h; ++r)
433 for (c = 0; c < state->params.w; ++c) {
434 int const cell = idx(r, c, state->params.w);
435 if (state->grid[cell] != BLACK) continue;
436 for (i = 0; i < 4; ++i)
437 solver_makemove(r + dr[i], c + dc[i], M_WHITE, state, &buf);
438 }
439 return buf;
440}
441
442enum {NOT_VISITED = -1};
443
444static int dfs_biconnect_visit(puzzle_size r, puzzle_size c,
445 game_state *state,
446 square *dfs_parent, int *dfs_depth,
447 move **buf);
448
449static move *solver_reasoning_connectedness(game_state *state,
450 int nclues,
451 const square *clues,
452 move *buf)
453{
454 int const w = state->params.w, h = state->params.h, n = w * h;
455
456 square *const dfs_parent = snewn(n, square);
457 int *const dfs_depth = snewn(n, int);
458
459 int i;
460 for (i = 0; i < n; ++i) {
461 dfs_parent[i].r = NOT_VISITED;
462 dfs_depth[i] = -n;
463 }
464
465 for (i = 0; i < n && state->grid[i] == BLACK; ++i);
466
467 dfs_parent[i].r = i / w;
468 dfs_parent[i].c = i % w; /* `dfs root`.parent == `dfs root` */
469 dfs_depth[i] = 0;
470
471 dfs_biconnect_visit(i / w, i % w, state, dfs_parent, dfs_depth, &buf);
472
473 sfree(dfs_parent);
474 sfree(dfs_depth);
475
476 return buf;
477}
478
479/* returns the `lowpoint` of (r, c) */
480static int dfs_biconnect_visit(puzzle_size r, puzzle_size c,
481 game_state *state,
482 square *dfs_parent, int *dfs_depth,
483 move **buf)
484{
485 const puzzle_size w = state->params.w, h = state->params.h;
486 int const i = idx(r, c, w), mydepth = dfs_depth[i];
487 int lowpoint = mydepth, j, nchildren = 0;
488
489 for (j = 0; j < 4; ++j) {
490 const puzzle_size rr = r + dr[j], cc = c + dc[j];
491 int const cell = idx(rr, cc, w);
492
493 if (out_of_bounds(rr, cc, w, h)) continue;
494 if (state->grid[cell] == BLACK) continue;
495
496 if (dfs_parent[cell].r == NOT_VISITED) {
497 int child_lowpoint;
498 dfs_parent[cell].r = r;
499 dfs_parent[cell].c = c;
500 dfs_depth[cell] = mydepth + 1;
501 child_lowpoint = dfs_biconnect_visit(rr, cc, state, dfs_parent,
502 dfs_depth, buf);
503
504 if (child_lowpoint >= mydepth && mydepth > 0)
505 solver_makemove(r, c, M_WHITE, state, buf);
506
507 lowpoint = min(lowpoint, child_lowpoint);
508 ++nchildren;
509 } else if (rr != dfs_parent[i].r || cc != dfs_parent[i].c) {
510 lowpoint = min(lowpoint, dfs_depth[cell]);
511 }
512 }
513
514 if (mydepth == 0 && nchildren >= 2)
515 solver_makemove(r, c, M_WHITE, state, buf);
516
517 return lowpoint;
518}
519
520static move *solver_reasoning_not_too_big(game_state *state,
521 int nclues,
522 const square *clues,
523 move *buf)
524{
525 int const w = state->params.w, runmasks[4] = {
526 ~(MASK(BLACK) | MASK(EMPTY)),
527 MASK(EMPTY),
528 ~(MASK(BLACK) | MASK(EMPTY)),
529 ~(MASK(BLACK))
530 };
531 enum {RUN_WHITE, RUN_EMPTY, RUN_BEYOND, RUN_SPACE};
532
533 int i, runlengths[4][4];
534
535 for (i = 0; i < nclues; ++i) {
536 int j, k, whites, space;
537
538 const puzzle_size row = clues[i].r, col = clues[i].c;
539 int const clue = state->grid[idx(row, col, w)];
540
541 for (j = 0; j < 4; ++j) {
542 puzzle_size r = row + dr[j], c = col + dc[j];
543 runlengths[RUN_SPACE][j] = 0;
544 for (k = 0; k <= RUN_SPACE; ++k) {
545 int l = runlength(r, c, dr[j], dc[j], state, runmasks[k]);
546 if (k < RUN_SPACE) {
547 runlengths[k][j] = l;
548 r += dr[j] * l;
549 c += dc[j] * l;
550 }
551 runlengths[RUN_SPACE][j] += l;
552 }
553 }
554
555 whites = 1;
556 for (j = 0; j < 4; ++j) whites += runlengths[RUN_WHITE][j];
557
558 for (j = 0; j < 4; ++j) {
559 int const delta = 1 + runlengths[RUN_WHITE][j];
560 const puzzle_size r = row + delta * dr[j];
561 const puzzle_size c = col + delta * dc[j];
562
563 if (whites == clue) {
564 solver_makemove(r, c, M_BLACK, state, &buf);
565 continue;
566 }
567
568 if (runlengths[RUN_EMPTY][j] == 1 &&
569 whites
570 + runlengths[RUN_EMPTY][j]
571 + runlengths[RUN_BEYOND][j]
572 > clue) {
573 solver_makemove(r, c, M_BLACK, state, &buf);
574 continue;
575 }
576
577 if (whites
578 + runlengths[RUN_EMPTY][j]
579 + runlengths[RUN_BEYOND][j]
580 > clue) {
581 runlengths[RUN_SPACE][j] =
582 runlengths[RUN_WHITE][j] +
583 runlengths[RUN_EMPTY][j] - 1;
584
585 if (runlengths[RUN_EMPTY][j] == 1)
586 solver_makemove(r, c, M_BLACK, state, &buf);
587 }
588 }
589
590 space = 1;
591 for (j = 0; j < 4; ++j) space += runlengths[RUN_SPACE][j];
592 for (j = 0; j < 4; ++j) {
593 puzzle_size r = row + dr[j], c = col + dc[j];
594
595 int k = space - runlengths[RUN_SPACE][j];
596 if (k >= clue) continue;
597
598 for (; k < clue; ++k, r += dr[j], c += dc[j])
599 solver_makemove(r, c, M_WHITE, state, &buf);
600 }
601 }
602 return buf;
603}
604
605static move *solver_reasoning_recursion(game_state *state,
606 int nclues,
607 const square *clues,
608 move *buf)
609{
610 int const w = state->params.w, n = w * state->params.h;
611 int cell, colour;
612
613 for (cell = 0; cell < n; ++cell) {
614 int const r = cell / w, c = cell % w;
615 int i;
616 game_state *newstate;
617 move *recursive_result;
618
619 if (state->grid[cell] != EMPTY) continue;
620
621 /* FIXME: add enum alias for smallest and largest (or N) */
622 for (colour = M_BLACK; colour <= M_WHITE; ++colour) {
623 newstate = dup_game(state);
624 newstate->grid[cell] = colour == M_BLACK ? BLACK : WHITE;
625 recursive_result = do_solve(newstate, nclues, clues, buf,
626 DIFF_RECURSION);
627 if (recursive_result == NULL) {
628 free_game(newstate);
629 solver_makemove(r, c, M_BLACK + M_WHITE - colour, state, &buf);
630 return buf;
631 }
632 for (i = 0; i < n && newstate->grid[i] != EMPTY; ++i);
633 free_game(newstate);
634 if (i == n) return buf;
635 }
636 }
637 return buf;
638}
639
640static square *find_clues(const game_state *state, int *ret_nclues)
641{
642 int r, c, i, nclues = 0;
643 square *ret = snewn(state->params.w * state->params.h, struct square);
644
645 for (i = r = 0; r < state->params.h; ++r)
646 for (c = 0; c < state->params.w; ++c, ++i)
647 if (state->grid[i] > 0) {
648 ret[nclues].r = r;
649 ret[nclues].c = c;
650 ++nclues;
651 }
652
653 *ret_nclues = nclues;
654 return sresize(ret, nclues + (nclues == 0), square);
655}
656
657/* ----------------------------------------------------------------------
658 * Puzzle generation
659 *
660 * Generating kurodoko instances is rather straightforward:
661 *
662 * - Start with a white grid and add black squares at randomly chosen
663 * locations, unless colouring that square black would violate
664 * either the adjacency or connectedness constraints.
665 *
666 * - For each white square, compute the number it would contain if it
667 * were given as a clue.
668 *
669 * - From a starting point of "give _every_ white square as a clue",
670 * for each white square (in a random order), see if the board is
671 * solvable when that square is not given as a clue. If not, don't
672 * give it as a clue, otherwise do.
673 *
674 * This never fails, but it's only _almost_ what I do. The real final
675 * step is this:
676 *
677 * - From a starting point of "give _every_ white square as a clue",
678 * first remove all clues that are two-way rotationally symmetric
679 * to a black square. If this leaves the puzzle unsolvable, throw
680 * it out and try again. Otherwise, remove all _pairs_ of clues
681 * (that are rotationally symmetric) which can be removed without
682 * rendering the puzzle unsolvable.
683 *
684 * This can fail even if one only removes the black and symmetric
685 * clues; indeed it happens often (avg. once or twice per puzzle) when
686 * generating 1xN instances. (If you add black cells they must be in
687 * the end, and if you only add one, it's ambiguous where).
688 */
689
690/* forward declarations of internal calls */
691static void newdesc_choose_black_squares(game_state *state,
692 const int *shuffle_1toN);
693static void newdesc_compute_clues(game_state *state);
694static int newdesc_strip_clues(game_state *state, int *shuffle_1toN);
695static char *newdesc_encode_game_description(int n, puzzle_size *grid);
696
697static char *new_game_desc(const game_params *params, random_state *rs,
698 char **aux, bool interactive)
699{
700 int const w = params->w, h = params->h, n = w * h;
701
702 puzzle_size *const grid = snewn(n, puzzle_size);
703 int *const shuffle_1toN = snewn(n, int);
704
705 int i, clues_removed;
706
707 char *encoding;
708
709 game_state state;
710 state.params = *params;
711 state.grid = grid;
712
713 interactive = false; /* I don't need it, I shouldn't use it*/
714
715 for (i = 0; i < n; ++i) shuffle_1toN[i] = i;
716
717 while (true) {
718 shuffle(shuffle_1toN, n, sizeof (int), rs);
719 newdesc_choose_black_squares(&state, shuffle_1toN);
720
721 newdesc_compute_clues(&state);
722
723 shuffle(shuffle_1toN, n, sizeof (int), rs);
724 clues_removed = newdesc_strip_clues(&state, shuffle_1toN);
725
726 if (clues_removed < 0) continue; else break;
727 }
728
729 encoding = newdesc_encode_game_description(n, grid);
730
731 sfree(grid);
732 sfree(shuffle_1toN);
733
734 return encoding;
735}
736
737static int dfs_count_white(game_state *state, int cell);
738
739static void newdesc_choose_black_squares(game_state *state,
740 const int *shuffle_1toN)
741{
742 int const w = state->params.w, h = state->params.h, n = w * h;
743
744 int k, any_white_cell, n_black_cells;
745
746 for (k = 0; k < n; ++k) state->grid[k] = WHITE;
747
748 any_white_cell = shuffle_1toN[n - 1];
749 n_black_cells = 0;
750
751 /* I like the puzzles that result from n / 3, but maybe this
752 * could be made a (generation, i.e. non-full) parameter? */
753 for (k = 0; k < n / 3; ++k) {
754 int const i = shuffle_1toN[k], c = i % w, r = i / w;
755
756 int j;
757 for (j = 0; j < 4; ++j) {
758 int const rr = r + dr[j], cc = c + dc[j], cell = idx(rr, cc, w);
759 /* if you're out of bounds, we skip you */
760 if (out_of_bounds(rr, cc, w, h)) continue;
761 if (state->grid[cell] == BLACK) break; /* I can't be black */
762 } if (j < 4) continue; /* I have black neighbour: I'm white */
763
764 state->grid[i] = BLACK;
765 ++n_black_cells;
766
767 j = dfs_count_white(state, any_white_cell);
768 if (j + n_black_cells < n) {
769 state->grid[i] = WHITE;
770 --n_black_cells;
771 }
772 }
773}
774
775static void newdesc_compute_clues(game_state *state)
776{
777 int const w = state->params.w, h = state->params.h;
778 int r, c;
779
780 for (r = 0; r < h; ++r) {
781 int run_size = 0, c, cc;
782 for (c = 0; c <= w; ++c) {
783 if (c == w || state->grid[idx(r, c, w)] == BLACK) {
784 for (cc = c - run_size; cc < c; ++cc)
785 state->grid[idx(r, cc, w)] += run_size;
786 run_size = 0;
787 } else ++run_size;
788 }
789 }
790
791 for (c = 0; c < w; ++c) {
792 int run_size = 0, r, rr;
793 for (r = 0; r <= h; ++r) {
794 if (r == h || state->grid[idx(r, c, w)] == BLACK) {
795 for (rr = r - run_size; rr < r; ++rr)
796 state->grid[idx(rr, c, w)] += run_size;
797 run_size = 0;
798 } else ++run_size;
799 }
800 }
801}
802
803#define rotate(x) (n - 1 - (x))
804
805static int newdesc_strip_clues(game_state *state, int *shuffle_1toN)
806{
807 int const w = state->params.w, n = w * state->params.h;
808
809 move *const move_buffer = snewn(n, move);
810 move *buf;
811 game_state *dupstate;
812
813 /*
814 * do a partition/pivot of shuffle_1toN into three groups:
815 * (1) squares rotationally-symmetric to (3)
816 * (2) squares not in (1) or (3)
817 * (3) black squares
818 *
819 * They go from [0, left), [left, right) and [right, n) in
820 * shuffle_1toN (and from there into state->grid[ ])
821 *
822 * Then, remove clues from the grid one by one in shuffle_1toN
823 * order, until the solver becomes unhappy. If we didn't remove
824 * all of (1), return (-1). Else, we're happy.
825 */
826
827 /* do the partition */
828 int clues_removed, k = 0, left = 0, right = n;
829
830 for (;; ++k) {
831 while (k < right && state->grid[shuffle_1toN[k]] == BLACK) {
832 --right;
833 SWAP(int, shuffle_1toN[right], shuffle_1toN[k]);
834 assert(state->grid[shuffle_1toN[right]] == BLACK);
835 }
836 if (k >= right) break;
837 assert (k >= left);
838 if (state->grid[rotate(shuffle_1toN[k])] == BLACK) {
839 SWAP(int, shuffle_1toN[k], shuffle_1toN[left]);
840 ++left;
841 }
842 assert (state->grid[rotate(shuffle_1toN[k])] != BLACK
843 || k == left - 1);
844 }
845
846 for (k = 0; k < left; ++k) {
847 assert (state->grid[rotate(shuffle_1toN[k])] == BLACK);
848 state->grid[shuffle_1toN[k]] = EMPTY;
849 }
850 for (k = left; k < right; ++k) {
851 assert (state->grid[rotate(shuffle_1toN[k])] != BLACK);
852 assert (state->grid[shuffle_1toN[k]] != BLACK);
853 }
854 for (k = right; k < n; ++k) {
855 assert (state->grid[shuffle_1toN[k]] == BLACK);
856 state->grid[shuffle_1toN[k]] = EMPTY;
857 }
858
859 clues_removed = (left - 0) + (n - right);
860
861 dupstate = dup_game(state);
862 buf = solve_internal(dupstate, move_buffer, DIFF_RECURSION - 1);
863 free_game(dupstate);
864 if (buf - move_buffer < clues_removed) {
865 /* branch prediction: I don't think I'll go here */
866 clues_removed = -1;
867 goto ret;
868 }
869
870 for (k = left; k < right; ++k) {
871 const int i = shuffle_1toN[k], j = rotate(i);
872 int const clue = state->grid[i], clue_rot = state->grid[j];
873 if (clue == BLACK) continue;
874 state->grid[i] = state->grid[j] = EMPTY;
875 dupstate = dup_game(state);
876 buf = solve_internal(dupstate, move_buffer, DIFF_RECURSION - 1);
877 free_game(dupstate);
878 clues_removed += 2 - (i == j);
879 /* if i is the center square, then i == (j = rotate(i))
880 * when i and j are one, removing i and j removes only one */
881 if (buf - move_buffer == clues_removed) continue;
882 /* if the solver is sound, refilling all removed clues means
883 * we have filled all squares, i.e. solved the puzzle. */
884 state->grid[i] = clue;
885 state->grid[j] = clue_rot;
886 clues_removed -= 2 - (i == j);
887 }
888
889ret:
890 sfree(move_buffer);
891 return clues_removed;
892}
893
894static int dfs_count_rec(puzzle_size *grid, int r, int c, int w, int h)
895{
896 int const cell = idx(r, c, w);
897 if (out_of_bounds(r, c, w, h)) return 0;
898 if (grid[cell] != WHITE) return 0;
899 grid[cell] = EMPTY;
900 return 1 +
901 dfs_count_rec(grid, r + 0, c + 1, w, h) +
902 dfs_count_rec(grid, r + 0, c - 1, w, h) +
903 dfs_count_rec(grid, r + 1, c + 0, w, h) +
904 dfs_count_rec(grid, r - 1, c + 0, w, h);
905}
906
907static int dfs_count_white(game_state *state, int cell)
908{
909 int const w = state->params.w, h = state->params.h, n = w * h;
910 int const r = cell / w, c = cell % w;
911 int i, k = dfs_count_rec(state->grid, r, c, w, h);
912 for (i = 0; i < n; ++i)
913 if (state->grid[i] == EMPTY)
914 state->grid[i] = WHITE;
915 return k;
916}
917
918static const char *validate_params(const game_params *params, bool full)
919{
920 int const w = params->w, h = params->h;
921 if (w < 1) return "Error: width is less than 1";
922 if (h < 1) return "Error: height is less than 1";
923 if (w > SCHAR_MAX - (h - 1)) return "Error: w + h is too big";
924 if (w * h < 1) return "Error: size is less than 1";
925 /* I might be unable to store clues in my puzzle_size *grid; */
926 if (full) {
927 if (w == 2 && h == 2) return "Error: can't create 2x2 puzzles";
928 if (w == 1 && h == 2) return "Error: can't create 1x2 puzzles";
929 if (w == 2 && h == 1) return "Error: can't create 2x1 puzzles";
930 if (w == 1 && h == 1) return "Error: can't create 1x1 puzzles";
931 }
932 return NULL;
933}
934
935/* Definition: a puzzle instance is _good_ if:
936 * - it has a unique solution
937 * - the solver can find this solution without using recursion
938 * - the solution contains at least one black square
939 * - the clues are 2-way rotationally symmetric
940 *
941 * (the idea being: the generator can not output any _bad_ puzzles)
942 *
943 * Theorem: validate_params, when full != 0, discards exactly the set
944 * of parameters for which there are _no_ good puzzle instances.
945 *
946 * Proof: it's an immediate consequence of the five lemmas below.
947 *
948 * Observation: not only do puzzles on non-tiny grids exist, the
949 * generator is pretty fast about coming up with them. On my pre-2004
950 * desktop box, it generates 100 puzzles on the highest preset (16x11)
951 * in 8.383 seconds, or <= 0.1 second per puzzle.
952 *
953 * ----------------------------------------------------------------------
954 *
955 * Lemma: On a 1x1 grid, there are no good puzzles.
956 *
957 * Proof: the one square can't be a clue because at least one square
958 * is black. But both a white square and a black square satisfy the
959 * solution criteria, so the puzzle is ambiguous (and hence bad).
960 *
961 * Lemma: On a 1x2 grid, there are no good puzzles.
962 *
963 * Proof: let's name the squares l and r. Note that there can be at
964 * most one black square, or adjacency is violated. By assumption at
965 * least one square is black, so let's call that one l. By clue
966 * symmetry, neither l nor r can be given as a clue, so the puzzle
967 * instance is blank and thus ambiguous.
968 *
969 * Corollary: On a 2x1 grid, there are no good puzzles.
970 * Proof: rotate the above proof 90 degrees ;-)
971 *
972 * ----------------------------------------------------------------------
973 *
974 * Lemma: On a 2x2 grid, there are no soluble puzzles with 2-way
975 * rotational symmetric clues and at least one black square.
976 *
977 * Proof: Let's name the squares a, b, c, and d, with a and b on the
978 * top row, a and c in the left column. Let's consider the case where
979 * a is black. Then no other square can be black: b and c would both
980 * violate the adjacency constraint; d would disconnect b from c.
981 *
982 * So exactly one square is black (and by 4-way rotation symmetry of
983 * the 2x2 square, it doesn't matter which one, so let's stick to a).
984 * By 2-way rotational symmetry of the clues and the rule about not
985 * painting numbers black, neither a nor d can be clues. A blank
986 * puzzle would be ambiguous, so one of {b, c} is a clue; by symmetry,
987 * so is the other one.
988 *
989 * It is readily seen that their clue value is 2. But "a is black"
990 * and "d is black" are both valid solutions in this case, so the
991 * puzzle is ambiguous (and hence bad).
992 *
993 * ----------------------------------------------------------------------
994 *
995 * Lemma: On a wxh grid with w, h >= 1 and (w > 2 or h > 2), there is
996 * at least one good puzzle.
997 *
998 * Proof: assume that w > h (otherwise rotate the proof again). Paint
999 * the top left and bottom right corners black, and fill a clue into
1000 * all the other squares. Present this board to the solver code (or
1001 * player, hypothetically), except with the two black squares as blank
1002 * squares.
1003 *
1004 * For an Nx1 puzzle, observe that every clue is N - 2, and there are
1005 * N - 2 of them in one connected sequence, so the remaining two
1006 * squares can be deduced to be black, which solves the puzzle.
1007 *
1008 * For any other puzzle, let j be a cell in the same row as a black
1009 * cell, but not in the same column (such a cell doesn't exist in 2x3
1010 * puzzles, but we assume w > h and such cells exist in 3x2 puzzles).
1011 *
1012 * Note that the number of cells in axis parallel `rays' going out
1013 * from j exceeds j's clue value by one. Only one such cell is a
1014 * non-clue, so it must be black. Similarly for the other corner (let
1015 * j' be a cell in the same row as the _other_ black cell, but not in
1016 * the same column as _any_ black cell; repeat this argument at j').
1017 *
1018 * This fills the grid and satisfies all clues and the adjacency
1019 * constraint and doesn't paint on top of any clues. All that is left
1020 * to see is connectedness.
1021 *
1022 * Observe that the white cells in each column form a single connected
1023 * `run', and each column contains a white cell adjacent to a white
1024 * cell in the column to the right, if that column exists.
1025 *
1026 * Thus, any cell in the left-most column can reach any other cell:
1027 * first go to the target column (by repeatedly going to the cell in
1028 * your current column that lets you go right, then going right), then
1029 * go up or down to the desired cell.
1030 *
1031 * As reachability is symmetric (in undirected graphs) and transitive,
1032 * any cell can reach any left-column cell, and from there any other
1033 * cell.
1034 */
1035
1036/* ----------------------------------------------------------------------
1037 * Game encoding and decoding
1038 */
1039
1040#define NDIGITS_BASE '!'
1041
1042static char *newdesc_encode_game_description(int area, puzzle_size *grid)
1043{
1044 char *desc = NULL;
1045 int desclen = 0, descsize = 0;
1046 int run, i;
1047
1048 run = 0;
1049 for (i = 0; i <= area; i++) {
1050 int n = (i < area ? grid[i] : -1);
1051
1052 if (!n)
1053 run++;
1054 else {
1055 if (descsize < desclen + 40) {
1056 descsize = desclen * 3 / 2 + 40;
1057 desc = sresize(desc, descsize, char);
1058 }
1059 if (run) {
1060 while (run > 0) {
1061 int c = 'a' - 1 + run;
1062 if (run > 26)
1063 c = 'z';
1064 desc[desclen++] = c;
1065 run -= c - ('a' - 1);
1066 }
1067 } else {
1068 /*
1069 * If there's a number in the very top left or
1070 * bottom right, there's no point putting an
1071 * unnecessary _ before or after it.
1072 */
1073 if (desclen > 0 && n > 0)
1074 desc[desclen++] = '_';
1075 }
1076 if (n > 0)
1077 desclen += sprintf(desc+desclen, "%d", n);
1078 run = 0;
1079 }
1080 }
1081 desc[desclen] = '\0';
1082 return desc;
1083}
1084
1085static const char *validate_desc(const game_params *params, const char *desc)
1086{
1087 int const n = params->w * params->h;
1088 int squares = 0;
1089 int range = params->w + params->h - 1; /* maximum cell value */
1090
1091 while (*desc && *desc != ',') {
1092 int c = *desc++;
1093 if (c >= 'a' && c <= 'z') {
1094 squares += c - 'a' + 1;
1095 } else if (c == '_') {
1096 /* do nothing */;
1097 } else if (c > '0' && c <= '9') {
1098 int val = atoi(desc-1);
1099 if (val < 1 || val > range)
1100 return "Out-of-range number in game description";
1101 squares++;
1102 while (*desc >= '0' && *desc <= '9')
1103 desc++;
1104 } else
1105 return "Invalid character in game description";
1106 }
1107
1108 if (squares < n)
1109 return "Not enough data to fill grid";
1110
1111 if (squares > n)
1112 return "Too much data to fit in grid";
1113
1114 return NULL;
1115}
1116
1117static game_state *new_game(midend *me, const game_params *params,
1118 const char *desc)
1119{
1120 int i;
1121 const char *p;
1122
1123 int const n = params->w * params->h;
1124 game_state *state = snew(game_state);
1125
1126 me = NULL; /* I don't need it, I shouldn't use it */
1127
1128 state->params = *params; /* structure copy */
1129 state->grid = snewn(n, puzzle_size);
1130
1131 p = desc;
1132 i = 0;
1133 while (i < n && *p) {
1134 int c = *p++;
1135 if (c >= 'a' && c <= 'z') {
1136 int squares = c - 'a' + 1;
1137 while (squares--)
1138 state->grid[i++] = 0;
1139 } else if (c == '_') {
1140 /* do nothing */;
1141 } else if (c > '0' && c <= '9') {
1142 int val = atoi(p-1);
1143 assert(val >= 1 && val <= params->w+params->h-1);
1144 state->grid[i++] = val;
1145 while (*p >= '0' && *p <= '9')
1146 p++;
1147 }
1148 }
1149 assert(i == n);
1150 state->has_cheated = false;
1151 state->was_solved = false;
1152
1153 return state;
1154}
1155
1156/* ----------------------------------------------------------------------
1157 * User interface: ascii
1158 */
1159
1160static bool game_can_format_as_text_now(const game_params *params)
1161{
1162 return true;
1163}
1164
1165static char *game_text_format(const game_state *state)
1166{
1167 int r, c, i, w_string, h_string, n_string;
1168 char cellsize;
1169 char *ret, *buf, *gridline;
1170
1171 int const w = state->params.w, h = state->params.h;
1172
1173 cellsize = 0; /* or may be used uninitialized */
1174
1175 for (c = 0; c < w; ++c) {
1176 for (r = 0; r < h; ++r) {
1177 puzzle_size k = state->grid[idx(r, c, w)];
1178 int d;
1179 for (d = 0; k; k /= 10, ++d);
1180 cellsize = max(cellsize, d);
1181 }
1182 }
1183
1184 ++cellsize;
1185
1186 w_string = w * cellsize + 2; /* "|%d|%d|...|\n" */
1187 h_string = 2 * h + 1; /* "+--+--+...+\n%s\n+--+--+...+\n" */
1188 n_string = w_string * h_string;
1189
1190 gridline = snewn(w_string + 1, char); /* +1: NUL terminator */
1191 memset(gridline, '-', w_string);
1192 for (c = 0; c <= w; ++c) gridline[c * cellsize] = '+';
1193 gridline[w_string - 1] = '\n';
1194 gridline[w_string - 0] = '\0';
1195
1196 buf = ret = snewn(n_string + 1, char); /* +1: NUL terminator */
1197 for (i = r = 0; r < h; ++r) {
1198 memcpy(buf, gridline, w_string);
1199 buf += w_string;
1200 for (c = 0; c < w; ++c, ++i) {
1201 char ch;
1202 switch (state->grid[i]) {
1203 case BLACK: ch = '#'; break;
1204 case WHITE: ch = '.'; break;
1205 case EMPTY: ch = ' '; break;
1206 default:
1207 buf += sprintf(buf, "|%*d", cellsize - 1, state->grid[i]);
1208 continue;
1209 }
1210 *buf++ = '|';
1211 memset(buf, ch, cellsize - 1);
1212 buf += cellsize - 1;
1213 }
1214 buf += sprintf(buf, "|\n");
1215 }
1216 memcpy(buf, gridline, w_string);
1217 buf += w_string;
1218 assert (buf - ret == n_string);
1219 *buf = '\0';
1220
1221 sfree(gridline);
1222
1223 return ret;
1224}
1225
1226/* ----------------------------------------------------------------------
1227 * User interfaces: interactive
1228 */
1229
1230struct game_ui {
1231 puzzle_size r, c; /* cursor position */
1232 bool cursor_show;
1233
1234 /*
1235 * User preference option to swap the left and right mouse
1236 * buttons.
1237 *
1238 * The original puzzle submitter thought it would be more useful
1239 * to have the left button turn an empty square into a dotted one,
1240 * on the grounds that that was what you did most often; I (SGT)
1241 * felt instinctively that the left button ought to place black
1242 * squares and the right button place dots, on the grounds that
1243 * that was consistent with many other puzzles in which the left
1244 * button fills in the data used by the solution checker while the
1245 * right button places pencil marks for the user's convenience.
1246 *
1247 * My first beta-player wasn't sure either, so I thought I'd
1248 * pre-emptively put in a 'configuration' mechanism just in case.
1249 */
1250 bool swap_buttons;
1251};
1252
1253static void legacy_prefs_override(struct game_ui *ui_out)
1254{
1255 static int initialised = false;
1256 static int swap_buttons = -1;
1257
1258 if (!initialised) {
1259 initialised = true;
1260 swap_buttons = getenv_bool("RANGE_SWAP_BUTTONS", -1);
1261 }
1262
1263 if (swap_buttons != -1)
1264 ui_out->swap_buttons = swap_buttons;
1265}
1266
1267static game_ui *new_ui(const game_state *state)
1268{
1269 struct game_ui *ui = snew(game_ui);
1270 ui->r = ui->c = 0;
1271 ui->cursor_show = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1272
1273 ui->swap_buttons = false;
1274 legacy_prefs_override(ui);
1275
1276 return ui;
1277}
1278
1279static config_item *get_prefs(game_ui *ui)
1280{
1281 config_item *ret;
1282
1283 ret = snewn(N_PREF_ITEMS+1, config_item);
1284
1285 ret[PREF_MOUSE_BUTTON_ORDER].name = "Mouse button order";
1286 ret[PREF_MOUSE_BUTTON_ORDER].kw = "left-mouse-button";
1287 ret[PREF_MOUSE_BUTTON_ORDER].type = C_CHOICES;
1288 ret[PREF_MOUSE_BUTTON_ORDER].u.choices.choicenames =
1289 ":Left to fill, right to dot:Left to dot, right to fill";
1290 ret[PREF_MOUSE_BUTTON_ORDER].u.choices.choicekws = ":fill:dot";
1291 ret[PREF_MOUSE_BUTTON_ORDER].u.choices.selected = ui->swap_buttons;
1292
1293 ret[N_PREF_ITEMS].name = NULL;
1294 ret[N_PREF_ITEMS].type = C_END;
1295
1296 return ret;
1297}
1298
1299static void set_prefs(game_ui *ui, const config_item *cfg)
1300{
1301 ui->swap_buttons = cfg[PREF_MOUSE_BUTTON_ORDER].u.choices.selected;
1302}
1303
1304static void free_ui(game_ui *ui)
1305{
1306 sfree(ui);
1307}
1308
1309static const char *current_key_label(const game_ui *ui,
1310 const game_state *state, int button)
1311{
1312 int cell;
1313
1314 if (IS_CURSOR_SELECT(button)) {
1315 cell = state->grid[idx(ui->r, ui->c, state->params.w)];
1316 if (!ui->cursor_show || cell > 0) return "";
1317 switch (cell) {
1318 case EMPTY:
1319 return button == CURSOR_SELECT ? "Fill" : "Dot";
1320 case WHITE:
1321 return button == CURSOR_SELECT ? "Empty" : "Fill";
1322 case BLACK:
1323 return button == CURSOR_SELECT ? "Dot" : "Empty";
1324 }
1325 }
1326 return "";
1327
1328}
1329
1330typedef struct drawcell {
1331 puzzle_size value;
1332 bool error, cursor, flash;
1333} drawcell;
1334
1335struct game_drawstate {
1336 int tilesize;
1337 drawcell *grid;
1338};
1339
1340#define TILESIZE (ds->tilesize)
1341#define BORDER (TILESIZE / 2)
1342#define COORD(x) ((x) * TILESIZE + BORDER)
1343#define FROMCOORD(x) (((x) - BORDER) / TILESIZE)
1344
1345static char *interpret_move(const game_state *state, game_ui *ui,
1346 const game_drawstate *ds,
1347 int x, int y, int button)
1348{
1349 enum {none, forwards, backwards, hint};
1350 int const w = state->params.w, h = state->params.h;
1351 int r = ui->r, c = ui->c, action = none, cell;
1352 bool shift = button & MOD_SHFT;
1353 button &= ~MOD_SHFT;
1354
1355 if (IS_CURSOR_SELECT(button) && !ui->cursor_show) return NULL;
1356
1357 if (IS_MOUSE_DOWN(button)) {
1358 r = FROMCOORD(y + TILESIZE) - 1; /* or (x, y) < TILESIZE) */
1359 c = FROMCOORD(x + TILESIZE) - 1; /* are considered inside */
1360 if (out_of_bounds(r, c, w, h)) return NULL;
1361 ui->r = r;
1362 ui->c = c;
1363 ui->cursor_show = false;
1364 }
1365
1366 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
1367 if (ui->swap_buttons) {
1368 if (button == LEFT_BUTTON)
1369 button = RIGHT_BUTTON;
1370 else
1371 button = LEFT_BUTTON;
1372 }
1373 }
1374
1375 switch (button) {
1376 case CURSOR_SELECT : case LEFT_BUTTON: action = backwards; break;
1377 case CURSOR_SELECT2: case RIGHT_BUTTON: action = forwards; break;
1378 case 'h': case 'H' : action = hint; break;
1379 case CURSOR_UP: case CURSOR_DOWN:
1380 case CURSOR_LEFT: case CURSOR_RIGHT:
1381 if (ui->cursor_show) {
1382 int i;
1383 for (i = 0; i < 4 && cursors[i] != button; ++i);
1384 assert (i < 4);
1385 if (shift) {
1386 int pre_r = r, pre_c = c;
1387 bool do_pre, do_post;
1388 cell = state->grid[idx(r, c, state->params.w)];
1389 do_pre = (cell == EMPTY);
1390
1391 if (out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) {
1392 if (do_pre)
1393 return nfmtstr(40, "W,%d,%d", pre_r, pre_c);
1394 else
1395 return NULL;
1396 }
1397
1398 ui->r += dr[i];
1399 ui->c += dc[i];
1400
1401 cell = state->grid[idx(ui->r, ui->c, state->params.w)];
1402 do_post = (cell == EMPTY);
1403
1404 /* (do_pre ? "..." : "") concat (do_post ? "..." : "") */
1405 if (do_pre && do_post)
1406 return nfmtstr(80, "W,%d,%dW,%d,%d",
1407 pre_r, pre_c, ui->r, ui->c);
1408 else if (do_pre)
1409 return nfmtstr(40, "W,%d,%d", pre_r, pre_c);
1410 else if (do_post)
1411 return nfmtstr(40, "W,%d,%d", ui->r, ui->c);
1412 else
1413 return MOVE_UI_UPDATE;
1414
1415 } else if (!out_of_bounds(ui->r + dr[i], ui->c + dc[i], w, h)) {
1416 ui->r += dr[i];
1417 ui->c += dc[i];
1418 }
1419 } else ui->cursor_show = true;
1420 return MOVE_UI_UPDATE;
1421 }
1422
1423 if (action == hint) {
1424 move *end, *buf = snewn(state->params.w * state->params.h,
1425 struct move);
1426 char *ret = NULL;
1427 end = solve_internal(state, buf, DIFF_RECURSION);
1428 if (end != NULL && end > buf) {
1429 ret = nfmtstr(40, "%c,%d,%d",
1430 buf->colour == M_BLACK ? 'B' : 'W',
1431 buf->square.r, buf->square.c);
1432 /* We used to set a flag here in the game_ui indicating
1433 * that the player had used the hint function. I (SGT)
1434 * retired it, on grounds of consistency with other games
1435 * (most of these games will still flash to indicate
1436 * completion if you solved and undid it, so why not if
1437 * you got a hint?) and because the flash is as much about
1438 * checking you got it all right than about congratulating
1439 * you on a job well done. */
1440 }
1441 sfree(buf);
1442 return ret;
1443 }
1444
1445 cell = state->grid[idx(r, c, state->params.w)];
1446 if (cell > 0) return NULL;
1447
1448 if (action == forwards) switch (cell) {
1449 case EMPTY: return nfmtstr(40, "W,%d,%d", r, c);
1450 case WHITE: return nfmtstr(40, "B,%d,%d", r, c);
1451 case BLACK: return nfmtstr(40, "E,%d,%d", r, c);
1452 }
1453
1454 else if (action == backwards) switch (cell) {
1455 case BLACK: return nfmtstr(40, "W,%d,%d", r, c);
1456 case WHITE: return nfmtstr(40, "E,%d,%d", r, c);
1457 case EMPTY: return nfmtstr(40, "B,%d,%d", r, c);
1458 }
1459
1460 return NULL;
1461}
1462
1463static bool find_errors(const game_state *state, bool *report)
1464{
1465 int const w = state->params.w, h = state->params.h, n = w * h;
1466 DSF *dsf;
1467
1468 int r, c, i;
1469
1470 int nblack = 0, any_white_cell = -1;
1471 game_state *dup = dup_game(state);
1472
1473 for (i = r = 0; r < h; ++r)
1474 for (c = 0; c < w; ++c, ++i) {
1475 switch (state->grid[i]) {
1476
1477 case BLACK:
1478 {
1479 int j;
1480 ++nblack;
1481 for (j = 0; j < 4; ++j) {
1482 int const rr = r + dr[j], cc = c + dc[j];
1483 if (out_of_bounds(rr, cc, w, h)) continue;
1484 if (state->grid[idx(rr, cc, w)] != BLACK) continue;
1485 if (!report) goto found_error;
1486 report[i] = true;
1487 break;
1488 }
1489 }
1490 break;
1491 default:
1492 {
1493 int j, runs;
1494 for (runs = 1, j = 0; j < 4; ++j) {
1495 int const rr = r + dr[j], cc = c + dc[j];
1496 runs += runlength(rr, cc, dr[j], dc[j], state,
1497 ~MASK(BLACK));
1498 }
1499 if (!report) {
1500 if (runs != state->grid[i]) goto found_error;
1501 } else if (runs < state->grid[i]) report[i] = true;
1502 else {
1503 for (runs = 1, j = 0; j < 4; ++j) {
1504 int const rr = r + dr[j], cc = c + dc[j];
1505 runs += runlength(rr, cc, dr[j], dc[j], state,
1506 ~(MASK(BLACK) | MASK(EMPTY)));
1507 }
1508 if (runs > state->grid[i]) report[i] = true;
1509 }
1510 }
1511
1512 /* note: fallthrough _into_ these cases */
1513 case EMPTY:
1514 case WHITE: any_white_cell = i;
1515 }
1516 }
1517
1518 /*
1519 * Check that all the white cells form a single connected component.
1520 */
1521 dsf = dsf_new(n);
1522 for (r = 0; r < h-1; ++r)
1523 for (c = 0; c < w; ++c)
1524 if (state->grid[r*w+c] != BLACK &&
1525 state->grid[(r+1)*w+c] != BLACK)
1526 dsf_merge(dsf, r*w+c, (r+1)*w+c);
1527 for (r = 0; r < h; ++r)
1528 for (c = 0; c < w-1; ++c)
1529 if (state->grid[r*w+c] != BLACK &&
1530 state->grid[r*w+(c+1)] != BLACK)
1531 dsf_merge(dsf, r*w+c, r*w+(c+1));
1532 if (any_white_cell != -1 &&
1533 nblack + dsf_size(dsf, any_white_cell) < n) {
1534 int biggest, canonical;
1535
1536 if (!report) {
1537 dsf_free(dsf);
1538 goto found_error;
1539 }
1540
1541 /*
1542 * Report this error by choosing one component to be the
1543 * canonical one (we pick the largest, arbitrarily
1544 * tie-breaking towards lower array indices) and highlighting
1545 * as an error any square in a different component.
1546 */
1547 canonical = -1;
1548 biggest = 0;
1549 for (i = 0; i < n; ++i)
1550 if (state->grid[i] != BLACK) {
1551 int size = dsf_size(dsf, i);
1552 if (size > biggest) {
1553 biggest = size;
1554 canonical = dsf_canonify(dsf, i);
1555 }
1556 }
1557
1558 for (i = 0; i < n; ++i)
1559 if (state->grid[i] != BLACK && dsf_canonify(dsf, i) != canonical)
1560 report[i] = true;
1561 }
1562 dsf_free(dsf);
1563
1564 free_game(dup);
1565 return false; /* if report != NULL, this is ignored */
1566
1567found_error:
1568 free_game(dup);
1569 return true;
1570}
1571
1572static game_state *execute_move(const game_state *state, const char *move)
1573{
1574 signed int r, c, value, nchars, ntok;
1575 signed char what_to_do;
1576 game_state *ret;
1577
1578 assert (move);
1579
1580 ret = dup_game(state);
1581
1582 if (*move == 'S') {
1583 ++move;
1584 ret->has_cheated = ret->was_solved = true;
1585 }
1586
1587 for (; *move; move += nchars) {
1588 ntok = sscanf(move, "%c,%d,%d%n", &what_to_do, &r, &c, &nchars);
1589 if (ntok < 3) goto failure;
1590 switch (what_to_do) {
1591 case 'W': value = WHITE; break;
1592 case 'E': value = EMPTY; break;
1593 case 'B': value = BLACK; break;
1594 default: goto failure;
1595 }
1596 if (out_of_bounds(r, c, ret->params.w, ret->params.h)) goto failure;
1597 ret->grid[idx(r, c, ret->params.w)] = value;
1598 }
1599
1600 if (!ret->was_solved)
1601 ret->was_solved = !find_errors(ret, NULL);
1602
1603 return ret;
1604
1605failure:
1606 free_game(ret);
1607 return NULL;
1608}
1609
1610static void game_changed_state(game_ui *ui, const game_state *oldstate,
1611 const game_state *newstate)
1612{
1613}
1614
1615static float game_anim_length(const game_state *oldstate,
1616 const game_state *newstate, int dir, game_ui *ui)
1617{
1618 return 0.0F;
1619}
1620
1621#define FLASH_TIME 0.7F
1622
1623static float game_flash_length(const game_state *from,
1624 const game_state *to, int dir, game_ui *ui)
1625{
1626 if (!from->was_solved && to->was_solved && !to->has_cheated)
1627 return FLASH_TIME;
1628 return 0.0F;
1629}
1630
1631static void game_get_cursor_location(const game_ui *ui,
1632 const game_drawstate *ds,
1633 const game_state *state,
1634 const game_params *params,
1635 int *x, int *y, int *w, int *h)
1636{
1637 if(ui->cursor_show) {
1638 *x = BORDER + TILESIZE * ui->c;
1639 *y = BORDER + TILESIZE * ui->r;
1640 *w = *h = TILESIZE;
1641 }
1642}
1643
1644static int game_status(const game_state *state)
1645{
1646 return state->was_solved ? +1 : 0;
1647}
1648
1649/* ----------------------------------------------------------------------
1650 * Drawing routines.
1651 */
1652
1653#define PREFERRED_TILE_SIZE 32
1654
1655enum {
1656 COL_BACKGROUND = 0,
1657 COL_GRID,
1658 COL_BLACK = COL_GRID,
1659 COL_TEXT = COL_GRID,
1660 COL_USER = COL_GRID,
1661 COL_ERROR,
1662 COL_LOWLIGHT,
1663 COL_CURSOR = COL_LOWLIGHT,
1664 NCOLOURS
1665};
1666
1667static void game_compute_size(const game_params *params, int tilesize,
1668 const game_ui *ui, int *x, int *y)
1669{
1670 *x = (1 + params->w) * tilesize;
1671 *y = (1 + params->h) * tilesize;
1672}
1673
1674static void game_set_size(drawing *dr, game_drawstate *ds,
1675 const game_params *params, int tilesize)
1676{
1677 ds->tilesize = tilesize;
1678}
1679
1680#define COLOUR(ret, i, r, g, b) \
1681 ((ret[3*(i)+0] = (r)), (ret[3*(i)+1] = (g)), (ret[3*(i)+2] = (b)))
1682
1683static float *game_colours(frontend *fe, int *ncolours)
1684{
1685 float *ret = snewn(3 * NCOLOURS, float);
1686
1687 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_LOWLIGHT);
1688 COLOUR(ret, COL_GRID, 0.0F, 0.0F, 0.0F);
1689 COLOUR(ret, COL_ERROR, 1.0F, 0.0F, 0.0F);
1690
1691 *ncolours = NCOLOURS;
1692 return ret;
1693}
1694
1695static drawcell makecell(puzzle_size value,
1696 bool error, bool cursor, bool flash)
1697{
1698 drawcell ret;
1699 setmember(ret, value);
1700 setmember(ret, error);
1701 setmember(ret, cursor);
1702 setmember(ret, flash);
1703 return ret;
1704}
1705
1706static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1707{
1708 int const w = state->params.w, h = state->params.h, n = w * h;
1709 struct game_drawstate *ds = snew(struct game_drawstate);
1710 int i;
1711
1712 ds->tilesize = 0;
1713
1714 ds->grid = snewn(n, drawcell);
1715 for (i = 0; i < n; ++i)
1716 ds->grid[i] = makecell(w + h, false, false, false);
1717
1718 return ds;
1719}
1720
1721static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1722{
1723 sfree(ds->grid);
1724 sfree(ds);
1725}
1726
1727#define cmpmember(a, b, field) ((a) . field == (b) . field)
1728
1729static bool cell_eq(drawcell a, drawcell b)
1730{
1731 return
1732 cmpmember(a, b, value) &&
1733 cmpmember(a, b, error) &&
1734 cmpmember(a, b, cursor) &&
1735 cmpmember(a, b, flash);
1736}
1737
1738static void draw_cell(drawing *dr, game_drawstate *ds, int r, int c,
1739 drawcell cell);
1740
1741static void game_redraw(drawing *dr, game_drawstate *ds,
1742 const game_state *oldstate, const game_state *state,
1743 int dir, const game_ui *ui,
1744 float animtime, float flashtime)
1745{
1746 int const w = state->params.w, h = state->params.h, n = w * h;
1747 int const flash = ((int) (flashtime * 5 / FLASH_TIME)) % 2;
1748
1749 int r, c, i;
1750
1751 bool *errors = snewn(n, bool);
1752 memset(errors, 0, n * sizeof (bool));
1753 find_errors(state, errors);
1754
1755 assert (oldstate == NULL); /* only happens if animating moves */
1756
1757 for (i = r = 0; r < h; ++r) {
1758 for (c = 0; c < w; ++c, ++i) {
1759 drawcell cell = makecell(state->grid[i], errors[i], false, flash);
1760 if (r == ui->r && c == ui->c && ui->cursor_show)
1761 cell.cursor = true;
1762 if (!cell_eq(cell, ds->grid[i])) {
1763 draw_cell(dr, ds, r, c, cell);
1764 ds->grid[i] = cell;
1765 }
1766 }
1767 }
1768
1769 sfree(errors);
1770}
1771
1772static void draw_cell(drawing *draw, game_drawstate *ds, int r, int c,
1773 drawcell cell)
1774{
1775 int const ts = ds->tilesize;
1776 int const y = BORDER + ts * r, x = BORDER + ts * c;
1777 int const tx = x + (ts / 2), ty = y + (ts / 2);
1778 int const dotsz = (ds->tilesize + 9) / 10;
1779
1780 int const colour = (cell.value == BLACK ?
1781 cell.error ? COL_ERROR : COL_BLACK :
1782 cell.flash || cell.cursor ?
1783 COL_LOWLIGHT : COL_BACKGROUND);
1784
1785 draw_rect_outline(draw, x, y, ts + 1, ts + 1, COL_GRID);
1786 draw_rect (draw, x + 1, y + 1, ts - 1, ts - 1, colour);
1787 if (cell.error)
1788 draw_rect_outline(draw, x + 1, y + 1, ts - 1, ts - 1, COL_ERROR);
1789
1790 switch (cell.value) {
1791 case WHITE: draw_rect(draw, tx - dotsz / 2, ty - dotsz / 2, dotsz, dotsz,
1792 cell.error ? COL_ERROR : COL_USER);
1793 case BLACK: case EMPTY: break;
1794 default:
1795 {
1796 int const colour = (cell.error ? COL_ERROR : COL_GRID);
1797 char *msg = nfmtstr(10, "%d", cell.value);
1798 draw_text(draw, tx, ty, FONT_VARIABLE, ts * 3 / 5,
1799 ALIGN_VCENTRE | ALIGN_HCENTRE, colour, msg);
1800 sfree(msg);
1801 }
1802 }
1803
1804 draw_update(draw, x, y, ts + 1, ts + 1);
1805}
1806
1807/* ----------------------------------------------------------------------
1808 * User interface: print
1809 */
1810
1811static void game_print_size(const game_params *params, const game_ui *ui,
1812 float *x, float *y)
1813{
1814 int print_width, print_height;
1815 game_compute_size(params, 800, ui, &print_width, &print_height);
1816 *x = print_width / 100.0F;
1817 *y = print_height / 100.0F;
1818}
1819
1820static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1821 int tilesize)
1822{
1823 int const w = state->params.w, h = state->params.h;
1824 game_drawstate ds_obj, *ds = &ds_obj;
1825 int r, c, i, colour;
1826
1827 ds->tilesize = tilesize;
1828
1829 colour = print_mono_colour(dr, 1); assert(colour == COL_BACKGROUND);
1830 colour = print_mono_colour(dr, 0); assert(colour == COL_GRID);
1831 colour = print_mono_colour(dr, 1); assert(colour == COL_ERROR);
1832 colour = print_mono_colour(dr, 0); assert(colour == COL_LOWLIGHT);
1833 colour = print_mono_colour(dr, 0); assert(colour == NCOLOURS);
1834
1835 for (i = r = 0; r < h; ++r)
1836 for (c = 0; c < w; ++c, ++i)
1837 draw_cell(dr, ds, r, c,
1838 makecell(state->grid[i], false, false, false));
1839
1840 print_line_width(dr, 3 * tilesize / 40);
1841 draw_rect_outline(dr, BORDER, BORDER, w*TILESIZE, h*TILESIZE, COL_GRID);
1842}
1843
1844/* And that's about it ;-) **************************************************/
1845
1846#ifdef COMBINED
1847#define thegame range
1848#endif
1849
1850struct game const thegame = {
1851 "Range", "games.range", "range",
1852 default_params,
1853 game_fetch_preset, NULL,
1854 decode_params,
1855 encode_params,
1856 free_params,
1857 dup_params,
1858 true, game_configure, custom_params,
1859 validate_params,
1860 new_game_desc,
1861 validate_desc,
1862 new_game,
1863 dup_game,
1864 free_game,
1865 true, solve_game,
1866 true, game_can_format_as_text_now, game_text_format,
1867 get_prefs, set_prefs,
1868 new_ui,
1869 free_ui,
1870 NULL, /* encode_ui */
1871 NULL, /* decode_ui */
1872 NULL, /* game_request_keys */
1873 game_changed_state,
1874 current_key_label,
1875 interpret_move,
1876 execute_move,
1877 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1878 game_colours,
1879 game_new_drawstate,
1880 game_free_drawstate,
1881 game_redraw,
1882 game_anim_length,
1883 game_flash_length,
1884 game_get_cursor_location,
1885 game_status,
1886 true, false, game_print_size, game_print,
1887 false, /* wants_statusbar */
1888 false, NULL, /* timing_state */
1889 0, /* flags */
1890};