A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/*
2 * singles.c: implementation of Hitori ('let me alone') from Nikoli.
3 *
4 * Make single-get able to fetch a specific puzzle ID from menneske.no?
5 *
6 * www.menneske.no solving methods:
7 *
8 * Done:
9 * SC: if you circle a cell, any cells in same row/col with same no --> black
10 * -- solver_op_circle
11 * SB: if you make a cell black, any cells around it --> white
12 * -- solver_op_blacken
13 * ST: 3 identical cells in row, centre is white and outer two black.
14 * SP: 2 identical cells with single-cell gap, middle cell is white.
15 * -- solver_singlesep (both ST and SP)
16 * PI: if you have a pair of same number in row/col, any other
17 * cells of same number must be black.
18 * -- solve_doubles
19 * CC: if you have a black on edge one cell away from corner, cell
20 * on edge diag. adjacent must be white.
21 * CE: if you have 2 black cells of triangle on edge, third cell must
22 * be white.
23 * QM: if you have 3 black cells of diagonal square in middle, fourth
24 * cell must be white.
25 * -- solve_allblackbutone (CC, CE, and QM).
26 * QC: a corner with 4 identical numbers (or 2 and 2) must have the
27 * corner cell (and cell diagonal to that) black.
28 * TC: a corner with 3 identical numbers (with the L either way)
29 * must have the apex of L black, and other two white.
30 * DC: a corner with 2 identical numbers in domino can set a white
31 * cell along wall.
32 * -- solve_corners (QC, TC, DC)
33 * IP: pair with one-offset-pair force whites by offset pair
34 * -- solve_offsetpair
35 * MC: any cells diag. adjacent to black cells that would split board
36 * into separate white regions must be white.
37 * -- solve_removesplits
38 *
39 * Still to do:
40 *
41 * TEP: 3 pairs of dominos parallel to side, can mark 4 white cells
42 * alongside.
43 * DEP: 2 pairs of dominos parallel to side, can mark 2 white cells.
44 * FI: if you have two sets of double-cells packed together, singles
45 * in that row/col must be white (qv. PI)
46 * QuM: four identical cells (or 2 and 2) in middle of grid only have
47 * two possible solutions each.
48 * FDE: doubles one row/column away from edge can force a white cell.
49 * FDM: doubles in centre (next to bits of diag. square) can force a white cell.
50 * MP: two pairs with same number between force number to black.
51 * CnC: if circling a cell leads to impossible board, cell is black.
52 * MC: if we have two possiblilities, can we force a white circle?
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#include "latin.h"
69
70#ifdef STANDALONE_SOLVER
71static bool verbose = false;
72#endif
73
74#define PREFERRED_TILE_SIZE 32
75#define TILE_SIZE (ds->tilesize)
76#define BORDER (TILE_SIZE / 2)
77
78#define CRAD ((TILE_SIZE / 2) - 1)
79#define TEXTSZ ((14*CRAD/10) - 1) /* 2 * sqrt(2) of CRAD */
80
81#define COORD(x) ( (x) * TILE_SIZE + BORDER )
82#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
83
84#define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
85
86#define FLASH_TIME 0.7F
87
88enum {
89 COL_BACKGROUND, COL_UNUSED1, COL_LOWLIGHT,
90 COL_BLACK, COL_WHITE, COL_BLACKNUM, COL_GRID,
91 COL_CURSOR, COL_ERROR,
92 NCOLOURS
93};
94
95enum {
96 PREF_SHOW_BLACK_NUMS,
97 N_PREF_ITEMS
98};
99
100struct game_params {
101 int w, h, diff;
102};
103
104#define F_BLACK 0x1
105#define F_CIRCLE 0x2
106#define F_ERROR 0x4
107#define F_SCRATCH 0x8
108
109struct game_state {
110 int w, h, n, o; /* n = w*h; o = max(w, h) */
111 bool completed, used_solve, impossible;
112 int *nums; /* size w*h */
113 unsigned int *flags; /* size w*h */
114};
115
116/* top, right, bottom, left */
117static const int dxs[4] = { 0, 1, 0, -1 };
118static const int dys[4] = { -1, 0, 1, 0 };
119
120/* --- Game parameters and preset functions --- */
121
122#define DIFFLIST(A) \
123 A(EASY,Easy,e) \
124 A(TRICKY,Tricky,k)
125
126#define ENUM(upper,title,lower) DIFF_ ## upper,
127#define TITLE(upper,title,lower) #title,
128#define ENCODE(upper,title,lower) #lower
129#define CONFIG(upper,title,lower) ":" #title
130
131enum { DIFFLIST(ENUM) DIFF_MAX, DIFF_ANY };
132static char const *const singles_diffnames[] = { DIFFLIST(TITLE) };
133static char const singles_diffchars[] = DIFFLIST(ENCODE);
134#define DIFFCOUNT lenof(singles_diffchars)
135#define DIFFCONFIG DIFFLIST(CONFIG)
136
137static game_params *default_params(void)
138{
139 game_params *ret = snew(game_params);
140 ret->w = ret->h = 5;
141 ret->diff = DIFF_EASY;
142
143 return ret;
144}
145
146static const struct game_params singles_presets[] = {
147 { 5, 5, DIFF_EASY },
148 { 5, 5, DIFF_TRICKY },
149 { 6, 6, DIFF_EASY },
150 { 6, 6, DIFF_TRICKY },
151 { 8, 8, DIFF_EASY },
152 { 8, 8, DIFF_TRICKY },
153 { 10, 10, DIFF_EASY },
154 { 10, 10, DIFF_TRICKY },
155 { 12, 12, DIFF_EASY },
156 { 12, 12, DIFF_TRICKY }
157};
158
159static bool game_fetch_preset(int i, char **name, game_params **params)
160{
161 game_params *ret;
162 char buf[80];
163
164 if (i < 0 || i >= lenof(singles_presets))
165 return false;
166
167 ret = default_params();
168 *ret = singles_presets[i];
169 *params = ret;
170
171 sprintf(buf, "%dx%d %s", ret->w, ret->h, singles_diffnames[ret->diff]);
172 *name = dupstr(buf);
173
174 return true;
175}
176
177static void free_params(game_params *params)
178{
179 sfree(params);
180}
181
182static game_params *dup_params(const game_params *params)
183{
184 game_params *ret = snew(game_params);
185 *ret = *params; /* structure copy */
186 return ret;
187}
188
189static void decode_params(game_params *ret, char const *string)
190{
191 char const *p = string;
192 int i;
193
194 ret->w = ret->h = atoi(p);
195 while (*p && isdigit((unsigned char)*p)) p++;
196 if (*p == 'x') {
197 p++;
198 ret->h = atoi(p);
199 while (*p && isdigit((unsigned char)*p)) p++;
200 }
201 if (*p == 'd') {
202 ret->diff = DIFF_MAX; /* which is invalid */
203 p++;
204 for (i = 0; i < DIFFCOUNT; i++) {
205 if (*p == singles_diffchars[i])
206 ret->diff = i;
207 }
208 p++;
209 }
210}
211
212static char *encode_params(const game_params *params, bool full)
213{
214 char data[256];
215
216 if (full)
217 sprintf(data, "%dx%dd%c", params->w, params->h, singles_diffchars[params->diff]);
218 else
219 sprintf(data, "%dx%d", params->w, params->h);
220
221 return dupstr(data);
222}
223
224static config_item *game_configure(const game_params *params)
225{
226 config_item *ret;
227 char buf[80];
228
229 ret = snewn(4, config_item);
230
231 ret[0].name = "Width";
232 ret[0].type = C_STRING;
233 sprintf(buf, "%d", params->w);
234 ret[0].u.string.sval = dupstr(buf);
235
236 ret[1].name = "Height";
237 ret[1].type = C_STRING;
238 sprintf(buf, "%d", params->h);
239 ret[1].u.string.sval = dupstr(buf);
240
241 ret[2].name = "Difficulty";
242 ret[2].type = C_CHOICES;
243 ret[2].u.choices.choicenames = DIFFCONFIG;
244 ret[2].u.choices.selected = params->diff;
245
246 ret[3].name = NULL;
247 ret[3].type = C_END;
248
249 return ret;
250}
251
252static game_params *custom_params(const config_item *cfg)
253{
254 game_params *ret = snew(game_params);
255
256 ret->w = atoi(cfg[0].u.string.sval);
257 ret->h = atoi(cfg[1].u.string.sval);
258 ret->diff = cfg[2].u.choices.selected;
259
260 return ret;
261}
262
263static const char *validate_params(const game_params *params, bool full)
264{
265 if (params->w < 2 || params->h < 2)
266 return "Width and height must be at least two";
267 if (params->w > 10+26+26 || params->h > 10+26+26)
268 return "Puzzle is too large";
269 if (full) {
270 if (params->diff < 0 || params->diff >= DIFF_MAX)
271 return "Unknown difficulty rating";
272 }
273
274 return NULL;
275}
276
277/* --- Game description string generation and unpicking --- */
278
279static game_state *blank_game(int w, int h)
280{
281 game_state *state = snew(game_state);
282
283 memset(state, 0, sizeof(game_state));
284 state->w = w;
285 state->h = h;
286 state->n = w*h;
287 state->o = max(w,h);
288
289 state->completed = false;
290 state->used_solve = false;
291 state->impossible = false;
292
293 state->nums = snewn(state->n, int);
294 state->flags = snewn(state->n, unsigned int);
295
296 memset(state->nums, 0, state->n*sizeof(int));
297 memset(state->flags, 0, state->n*sizeof(unsigned int));
298
299 return state;
300}
301
302static game_state *dup_game(const game_state *state)
303{
304 game_state *ret = blank_game(state->w, state->h);
305
306 ret->completed = state->completed;
307 ret->used_solve = state->used_solve;
308 ret->impossible = state->impossible;
309
310 memcpy(ret->nums, state->nums, state->n*sizeof(int));
311 memcpy(ret->flags, state->flags, state->n*sizeof(unsigned int));
312
313 return ret;
314}
315
316static void free_game(game_state *state)
317{
318 sfree(state->nums);
319 sfree(state->flags);
320 sfree(state);
321}
322
323static char n2c(int num) {
324 if (num < 10)
325 return '0' + num;
326 else if (num < 10+26)
327 return 'a' + num - 10;
328 else
329 return 'A' + num - 10 - 26;
330 return '?';
331}
332
333static int c2n(char c) {
334 if (isdigit((unsigned char)c))
335 return (int)(c - '0');
336 else if (c >= 'a' && c <= 'z')
337 return (int)(c - 'a' + 10);
338 else if (c >= 'A' && c <= 'Z')
339 return (int)(c - 'A' + 10 + 26);
340 return -1;
341}
342
343static void unpick_desc(const game_params *params, const char *desc,
344 game_state **sout, const char **mout)
345{
346 game_state *state = blank_game(params->w, params->h);
347 const char *msg = NULL;
348 int num = 0, i = 0;
349
350 if (strlen(desc) != state->n) {
351 msg = "Game description is wrong length";
352 goto done;
353 }
354 for (i = 0; i < state->n; i++) {
355 num = c2n(desc[i]);
356 if (num <= 0 || num > state->o) {
357 msg = "Game description contains unexpected characters";
358 goto done;
359 }
360 state->nums[i] = num;
361 }
362done:
363 if (msg) { /* sth went wrong. */
364 if (mout) *mout = msg;
365 free_game(state);
366 } else {
367 if (mout) *mout = NULL;
368 if (sout) *sout = state;
369 else free_game(state);
370 }
371}
372
373static char *generate_desc(game_state *state, bool issolve)
374{
375 char *ret = snewn(state->n+1+(issolve?1:0), char);
376 int i, p=0;
377
378 if (issolve)
379 ret[p++] = 'S';
380 for (i = 0; i < state->n; i++)
381 ret[p++] = n2c(state->nums[i]);
382 ret[p] = '\0';
383 return ret;
384}
385
386/* --- Useful game functions (completion, etc.) --- */
387
388static bool game_can_format_as_text_now(const game_params *params)
389{
390 return true;
391}
392
393static char *game_text_format(const game_state *state)
394{
395 int len, x, y, i;
396 char *ret, *p;
397
398 len = (state->w)*2; /* one row ... */
399 len = len * (state->h*2); /* ... h rows, including gaps ... */
400 len += 1; /* ... final NL */
401 p = ret = snewn(len, char);
402
403 for (y = 0; y < state->h; y++) {
404 for (x = 0; x < state->w; x++) {
405 i = y*state->w + x;
406 if (x > 0) *p++ = ' ';
407 *p++ = (state->flags[i] & F_BLACK) ? '*' : n2c(state->nums[i]);
408 }
409 *p++ = '\n';
410 for (x = 0; x < state->w; x++) {
411 i = y*state->w + x;
412 if (x > 0) *p++ = ' ';
413 *p++ = (state->flags[i] & F_CIRCLE) ? '~' : ' ';
414 }
415 *p++ = '\n';
416 }
417 *p++ = '\0';
418 assert(p - ret == len);
419
420 return ret;
421}
422
423static void debug_state(const char *desc, game_state *state) {
424 char *dbg = game_text_format(state);
425 debug(("%s:\n%s", desc, dbg));
426 sfree(dbg);
427}
428
429static void connect_if_same(game_state *state, DSF *dsf, int i1, int i2)
430{
431 int c1, c2;
432
433 if ((state->flags[i1] & F_BLACK) != (state->flags[i2] & F_BLACK))
434 return;
435
436 c1 = dsf_canonify(dsf, i1);
437 c2 = dsf_canonify(dsf, i2);
438 dsf_merge(dsf, c1, c2);
439}
440
441static void connect_dsf(game_state *state, DSF *dsf)
442{
443 int x, y, i;
444
445 /* Construct a dsf array for connected blocks; connections
446 * tracked to right and down. */
447 dsf_reinit(dsf);
448 for (x = 0; x < state->w; x++) {
449 for (y = 0; y < state->h; y++) {
450 i = y*state->w + x;
451
452 if (x < state->w-1)
453 connect_if_same(state, dsf, i, i+1); /* right */
454 if (y < state->h-1)
455 connect_if_same(state, dsf, i, i+state->w); /* down */
456 }
457 }
458}
459
460#define CC_MARK_ERRORS 1
461#define CC_MUST_FILL 2
462
463static int check_rowcol(game_state *state, int starti, int di, int sz, unsigned flags)
464{
465 int nerr = 0, n, m, i, j;
466
467 /* if any circled numbers have identical non-circled numbers on
468 * same row/column, error (non-circled)
469 * if any circled numbers in same column are same number, highlight them.
470 * if any rows/columns have >1 of same number, not complete. */
471
472 for (n = 0, i = starti; n < sz; n++, i += di) {
473 if (state->flags[i] & F_BLACK) continue;
474 for (m = n+1, j = i+di; m < sz; m++, j += di) {
475 if (state->flags[j] & F_BLACK) continue;
476 if (state->nums[i] != state->nums[j]) continue;
477
478 nerr++; /* ok, we have two numbers the same in a row. */
479 if (!(flags & CC_MARK_ERRORS)) continue;
480
481 /* If we have two circles in the same row around
482 * two identical numbers, they are _both_ wrong. */
483 if ((state->flags[i] & F_CIRCLE) &&
484 (state->flags[j] & F_CIRCLE)) {
485 state->flags[i] |= F_ERROR;
486 state->flags[j] |= F_ERROR;
487 }
488 /* Otherwise, if we have a circle, any other identical
489 * numbers in that row are obviously wrong. We don't
490 * highlight this, however, since it makes the process
491 * of solving the puzzle too easy (you circle a number
492 * and it promptly tells you which numbers to blacken! */
493#if 0
494 else if (state->flags[i] & F_CIRCLE)
495 state->flags[j] |= F_ERROR;
496 else if (state->flags[j] & F_CIRCLE)
497 state->flags[i] |= F_ERROR;
498#endif
499 }
500 }
501 return nerr;
502}
503
504static bool check_complete(game_state *state, unsigned flags)
505{
506 DSF *dsf = dsf_new(state->n);
507 int x, y, i, error = 0, nwhite, w = state->w, h = state->h;
508
509 if (flags & CC_MARK_ERRORS) {
510 for (i = 0; i < state->n; i++)
511 state->flags[i] &= ~F_ERROR;
512 }
513 connect_dsf(state, dsf);
514
515 /* If we're the solver we need the grid all to be definitively
516 * black or definitively white (i.e. circled) otherwise the solver
517 * has found an ambiguous grid. */
518 if (flags & CC_MUST_FILL) {
519 for (i = 0; i < state->n; i++) {
520 if (!(state->flags[i] & F_BLACK) && !(state->flags[i] & F_CIRCLE))
521 error += 1;
522 }
523 }
524
525 /* Mark any black squares in groups of >1 as errors.
526 * Count number of white squares. */
527 nwhite = 0;
528 for (i = 0; i < state->n; i++) {
529 if (state->flags[i] & F_BLACK) {
530 if (dsf_size(dsf, i) > 1) {
531 error += 1;
532 if (flags & CC_MARK_ERRORS)
533 state->flags[i] |= F_ERROR;
534 }
535 } else
536 nwhite += 1;
537 }
538
539 /* Check attributes of white squares, row- and column-wise. */
540 for (x = 0; x < w; x++) /* check cols from (x,0) */
541 error += check_rowcol(state, x, w, h, flags);
542 for (y = 0; y < h; y++) /* check rows from (0,y) */
543 error += check_rowcol(state, y*w, 1, w, flags);
544
545 /* If there's more than one white region, pick the largest one to
546 * be the canonical one (arbitrarily tie-breaking towards lower
547 * array indices), and mark all the others as erroneous. */
548 {
549 int largest = 0, canonical = -1;
550 for (i = 0; i < state->n; i++)
551 if (!(state->flags[i] & F_BLACK)) {
552 int size = dsf_size(dsf, i);
553 if (largest < size) {
554 largest = size;
555 canonical = dsf_canonify(dsf, i);
556 }
557 }
558
559 if (largest < nwhite) {
560 for (i = 0; i < state->n; i++)
561 if (!(state->flags[i] & F_BLACK) &&
562 dsf_canonify(dsf, i) != canonical) {
563 error += 1;
564 if (flags & CC_MARK_ERRORS)
565 state->flags[i] |= F_ERROR;
566 }
567 }
568 }
569
570 dsf_free(dsf);
571 return !(error > 0);
572}
573
574static char *game_state_diff(const game_state *src, const game_state *dst,
575 bool issolve)
576{
577 char *ret = NULL, buf[80], c;
578 int retlen = 0, x, y, i, k;
579 unsigned int fmask = F_BLACK | F_CIRCLE;
580
581 assert(src->n == dst->n);
582
583 if (issolve) {
584 ret = sresize(ret, 3, char);
585 ret[0] = 'S'; ret[1] = ';'; ret[2] = '\0';
586 retlen += 2;
587 }
588
589 for (x = 0; x < dst->w; x++) {
590 for (y = 0; y < dst->h; y++) {
591 i = y*dst->w + x;
592 if ((src->flags[i] & fmask) != (dst->flags[i] & fmask)) {
593 assert((dst->flags[i] & fmask) != fmask);
594 if (dst->flags[i] & F_BLACK)
595 c = 'B';
596 else if (dst->flags[i] & F_CIRCLE)
597 c = 'C';
598 else
599 c = 'E';
600 k = sprintf(buf, "%c%d,%d;", (int)c, x, y);
601 ret = sresize(ret, retlen + k + 1, char);
602 strcpy(ret + retlen, buf);
603 retlen += k;
604 }
605 }
606 }
607 return ret;
608}
609
610/* --- Solver --- */
611
612enum { BLACK, CIRCLE };
613
614struct solver_op {
615 int x, y, op; /* op one of BLACK or CIRCLE. */
616 const char *desc; /* must be non-malloced. */
617};
618
619struct solver_state {
620 struct solver_op *ops;
621 int n_ops, n_alloc;
622 int *scratch;
623};
624
625static struct solver_state *solver_state_new(game_state *state)
626{
627 struct solver_state *ss = snew(struct solver_state);
628
629 ss->ops = NULL;
630 ss->n_ops = ss->n_alloc = 0;
631 ss->scratch = snewn(state->n, int);
632
633 return ss;
634}
635
636static void solver_state_free(struct solver_state *ss)
637{
638 sfree(ss->scratch);
639 if (ss->ops) sfree(ss->ops);
640 sfree(ss);
641}
642
643static void solver_op_add(struct solver_state *ss, int x, int y, int op, const char *desc)
644{
645 struct solver_op *sop;
646
647 if (ss->n_alloc < ss->n_ops + 1) {
648 ss->n_alloc = (ss->n_alloc + 1) * 2;
649 ss->ops = sresize(ss->ops, ss->n_alloc, struct solver_op);
650 }
651 sop = &(ss->ops[ss->n_ops++]);
652 sop->x = x; sop->y = y; sop->op = op; sop->desc = desc;
653 debug(("added solver op %s ('%s') at (%d,%d)\n",
654 op == BLACK ? "BLACK" : "CIRCLE", desc, x, y));
655}
656
657static void solver_op_circle(game_state *state, struct solver_state *ss,
658 int x, int y)
659{
660 int i = y*state->w + x;
661
662 if (!INGRID(state, x, y)) return;
663 if (state->flags[i] & F_BLACK) {
664 debug(("... solver wants to add auto-circle on black (%d,%d)\n", x, y));
665 state->impossible = true;
666 return;
667 }
668 /* Only add circle op if it's not already circled. */
669 if (!(state->flags[i] & F_CIRCLE)) {
670 solver_op_add(ss, x, y, CIRCLE, "SB - adjacent to black square");
671 }
672}
673
674static void solver_op_blacken(game_state *state, struct solver_state *ss,
675 int x, int y, int num)
676{
677 int i = y*state->w + x;
678
679 if (!INGRID(state, x, y)) return;
680 if (state->nums[i] != num) return;
681 if (state->flags[i] & F_CIRCLE) {
682 debug(("... solver wants to add auto-black on circled(%d,%d)\n", x, y));
683 state->impossible = true;
684 return;
685 }
686 /* Only add black op if it's not already black. */
687 if (!(state->flags[i] & F_BLACK)) {
688 solver_op_add(ss, x, y, BLACK, "SC - number on same row/col as circled");
689 }
690}
691
692static int solver_ops_do(game_state *state, struct solver_state *ss)
693{
694 int next_op = 0, i, x, y, n_ops = 0;
695 struct solver_op op;
696
697 /* Care here: solver_op_* may call solver_op_add which may extend the
698 * ss->n_ops. */
699
700 while (next_op < ss->n_ops) {
701 op = ss->ops[next_op++]; /* copy this away, it may get reallocated. */
702 i = op.y*state->w + op.x;
703
704 if (op.op == BLACK) {
705 if (state->flags[i] & F_CIRCLE) {
706 debug(("Solver wants to blacken circled square (%d,%d)!\n", op.x, op.y));
707 state->impossible = true;
708 return n_ops;
709 }
710 if (!(state->flags[i] & F_BLACK)) {
711 debug(("... solver adding black at (%d,%d): %s\n", op.x, op.y, op.desc));
712#ifdef STANDALONE_SOLVER
713 if (verbose)
714 printf("Adding black at (%d,%d): %s\n", op.x, op.y, op.desc);
715#endif
716 state->flags[i] |= F_BLACK;
717 /*debug_state("State after adding black", state);*/
718 n_ops++;
719 solver_op_circle(state, ss, op.x-1, op.y);
720 solver_op_circle(state, ss, op.x+1, op.y);
721 solver_op_circle(state, ss, op.x, op.y-1);
722 solver_op_circle(state, ss, op.x, op.y+1);
723 }
724 } else {
725 if (state->flags[i] & F_BLACK) {
726 debug(("Solver wants to circle blackened square (%d,%d)!\n", op.x, op.y));
727 state->impossible = true;
728 return n_ops;
729 }
730 if (!(state->flags[i] & F_CIRCLE)) {
731 debug(("... solver adding circle at (%d,%d): %s\n", op.x, op.y, op.desc));
732#ifdef STANDALONE_SOLVER
733 if (verbose)
734 printf("Adding circle at (%d,%d): %s\n", op.x, op.y, op.desc);
735#endif
736 state->flags[i] |= F_CIRCLE;
737 /*debug_state("State after adding circle", state);*/
738 n_ops++;
739 for (x = 0; x < state->w; x++) {
740 if (x != op.x)
741 solver_op_blacken(state, ss, x, op.y, state->nums[i]);
742 }
743 for (y = 0; y < state->h; y++) {
744 if (y != op.y)
745 solver_op_blacken(state, ss, op.x, y, state->nums[i]);
746 }
747 }
748 }
749 }
750 ss->n_ops = 0;
751 return n_ops;
752}
753
754/* If the grid has two identical numbers with one cell between them, the inner
755 * cell _must_ be white (and thus circled); (at least) one of the two must be
756 * black (since they're in the same column or row) and thus the middle cell is
757 * next to a black cell. */
758static int solve_singlesep(game_state *state, struct solver_state *ss)
759{
760 int x, y, i, ir, irr, id, idd, n_ops = ss->n_ops;
761
762 for (x = 0; x < state->w; x++) {
763 for (y = 0; y < state->h; y++) {
764 i = y*state->w + x;
765
766 /* Cell two to our right? */
767 ir = i + 1; irr = ir + 1;
768 if (x < (state->w-2) &&
769 state->nums[i] == state->nums[irr] &&
770 !(state->flags[ir] & F_CIRCLE)) {
771 solver_op_add(ss, x+1, y, CIRCLE, "SP/ST - between identical nums");
772 }
773 /* Cell two below us? */
774 id = i + state->w; idd = id + state->w;
775 if (y < (state->h-2) &&
776 state->nums[i] == state->nums[idd] &&
777 !(state->flags[id] & F_CIRCLE)) {
778 solver_op_add(ss, x, y+1, CIRCLE, "SP/ST - between identical nums");
779 }
780 }
781 }
782 return ss->n_ops - n_ops;
783}
784
785/* If we have two identical numbers next to each other (in a row or column),
786 * any other identical numbers in that column must be black. */
787static int solve_doubles(game_state *state, struct solver_state *ss)
788{
789 int x, y, i, ii, n_ops = ss->n_ops, xy;
790
791 for (y = 0, i = 0; y < state->h; y++) {
792 for (x = 0; x < state->w; x++, i++) {
793 assert(i == y*state->w+x);
794 if (state->flags[i] & F_BLACK) continue;
795
796 ii = i+1; /* check cell to our right. */
797 if (x < (state->w-1) &&
798 !(state->flags[ii] & F_BLACK) &&
799 state->nums[i] == state->nums[ii]) {
800 for (xy = 0; xy < state->w; xy++) {
801 if (xy == x || xy == (x+1)) continue;
802 if (state->nums[y*state->w + xy] == state->nums[i] &&
803 !(state->flags[y*state->w + xy] & F_BLACK))
804 solver_op_add(ss, xy, y, BLACK, "PI - same row as pair");
805 }
806 }
807
808 ii = i+state->w; /* check cell below us */
809 if (y < (state->h-1) &&
810 !(state->flags[ii] & F_BLACK) &&
811 state->nums[i] == state->nums[ii]) {
812 for (xy = 0; xy < state->h; xy++) {
813 if (xy == y || xy == (y+1)) continue;
814 if (state->nums[xy*state->w + x] == state->nums[i] &&
815 !(state->flags[xy*state->w + x] & F_BLACK))
816 solver_op_add(ss, x, xy, BLACK, "PI - same col as pair");
817 }
818 }
819 }
820 }
821 return ss->n_ops - n_ops;
822}
823
824/* If a white square has all-but-one possible adjacent squares black, the
825 * one square left over must be white. */
826static int solve_allblackbutone(game_state *state, struct solver_state *ss)
827{
828 int x, y, i, n_ops = ss->n_ops, xd, yd, id, ifree;
829 int dis[4], d;
830
831 dis[0] = -state->w;
832 dis[1] = 1;
833 dis[2] = state->w;
834 dis[3] = -1;
835
836 for (y = 0, i = 0; y < state->h; y++) {
837 for (x = 0; x < state->w; x++, i++) {
838 assert(i == y*state->w+x);
839 if (state->flags[i] & F_BLACK) continue;
840
841 ifree = -1;
842 for (d = 0; d < 4; d++) {
843 xd = x + dxs[d]; yd = y + dys[d]; id = i + dis[d];
844 if (!INGRID(state, xd, yd)) continue;
845
846 if (state->flags[id] & F_CIRCLE)
847 goto skip; /* this cell already has a way out */
848 if (!(state->flags[id] & F_BLACK)) {
849 if (ifree != -1)
850 goto skip; /* this cell has >1 white cell around it. */
851 ifree = id;
852 }
853 }
854 if (ifree != -1)
855 solver_op_add(ss, ifree%state->w, ifree/state->w, CIRCLE,
856 "CC/CE/QM: white cell with single non-black around it");
857 else {
858 debug(("White cell with no escape at (%d,%d)\n", x, y));
859 state->impossible = true;
860 return 0;
861 }
862skip: ;
863 }
864 }
865 return ss->n_ops - n_ops;
866}
867
868/* If we have 4 numbers the same in a 2x2 corner, the far corner and the
869 * diagonally-adjacent square must both be black.
870 * If we have 3 numbers the same in a 2x2 corner, the apex of the L
871 * thus formed must be black.
872 * If we have 2 numbers the same in a 2x2 corner, the non-same cell
873 * one away from the corner must be white. */
874static void solve_corner(game_state *state, struct solver_state *ss,
875 int x, int y, int dx, int dy)
876{
877 int is[4], ns[4], xx, yy, w = state->w;
878
879 for (yy = 0; yy < 2; yy++) {
880 for (xx = 0; xx < 2; xx++) {
881 is[yy*2+xx] = (y + dy*yy) * w + (x + dx*xx);
882 ns[yy*2+xx] = state->nums[is[yy*2+xx]];
883 }
884 } /* order is now (corner, side 1, side 2, inner) */
885
886 if (ns[0] == ns[1] && ns[0] == ns[2] && ns[0] == ns[3]) {
887 solver_op_add(ss, is[0]%w, is[0]/w, BLACK, "QC: corner with 4 matching");
888 solver_op_add(ss, is[3]%w, is[3]/w, BLACK, "QC: corner with 4 matching");
889 } else if (ns[0] == ns[1] && ns[0] == ns[2]) {
890 /* corner and 2 sides: apex is corner. */
891 solver_op_add(ss, is[0]%w, is[0]/w, BLACK, "TC: corner apex from 3 matching");
892 } else if (ns[1] == ns[2] && ns[1] == ns[3]) {
893 /* side, side, fourth: apex is fourth. */
894 solver_op_add(ss, is[3]%w, is[3]/w, BLACK, "TC: inside apex from 3 matching");
895 } else if (ns[0] == ns[1] || ns[1] == ns[3]) {
896 /* either way here we match the non-identical side. */
897 solver_op_add(ss, is[2]%w, is[2]/w, CIRCLE, "DC: corner with 2 matching");
898 } else if (ns[0] == ns[2] || ns[2] == ns[3]) {
899 /* ditto */
900 solver_op_add(ss, is[1]%w, is[1]/w, CIRCLE, "DC: corner with 2 matching");
901 }
902}
903
904static int solve_corners(game_state *state, struct solver_state *ss)
905{
906 int n_ops = ss->n_ops;
907
908 solve_corner(state, ss, 0, 0, 1, 1);
909 solve_corner(state, ss, state->w-1, 0, -1, 1);
910 solve_corner(state, ss, state->w-1, state->h-1, -1, -1);
911 solve_corner(state, ss, 0, state->h-1, 1, -1);
912
913 return ss->n_ops - n_ops;
914}
915
916/* If you have the following situation:
917 * ...
918 * ...x A x x y A x...
919 * ...x B x x B y x...
920 * ...
921 * then both squares marked 'y' must be white. One of the left-most A or B must
922 * be white (since two side-by-side black cells are disallowed), which means
923 * that the corresponding right-most A or B must be black (since you can't
924 * have two of the same number on one line); thus, the adjacent squares
925 * to that right-most A or B must be white, which include the two marked 'y'
926 * in either case.
927 * Obviously this works in any row or column. It also works if A == B.
928 * It doesn't work for the degenerate case:
929 * ...x A A x x
930 * ...x B y x x
931 * where the square marked 'y' isn't necessarily white (consider the left-most A
932 * is black).
933 *
934 * */
935static void solve_offsetpair_pair(game_state *state, struct solver_state *ss,
936 int x1, int y1, int x2, int y2)
937{
938 int ox, oy, w = state->w, ax, ay, an, d, dx[2], dy[2], dn, xd, yd;
939
940 if (x1 == x2) { /* same column */
941 ox = 1; oy = 0;
942 } else {
943 assert(y1 == y2);
944 ox = 0; oy = 1;
945 }
946
947 /* We try adjacent to (x1,y1) and the two diag. adjacent to (x2, y2).
948 * We expect to be called twice, once each way around. */
949 ax = x1+ox; ay = y1+oy;
950 assert(INGRID(state, ax, ay));
951 an = state->nums[ay*w + ax];
952
953 dx[0] = x2 + ox + oy; dx[1] = x2 + ox - oy;
954 dy[0] = y2 + oy + ox; dy[1] = y2 + oy - ox;
955
956 for (d = 0; d < 2; d++) {
957 if (INGRID(state, dx[d], dy[d]) && (dx[d] != ax || dy[d] != ay)) {
958 /* The 'dx != ax || dy != ay' removes the degenerate case,
959 * mentioned above. */
960 dn = state->nums[dy[d]*w + dx[d]];
961 if (an == dn) {
962 /* We have a match; so (WLOG) the 'A' marked above are at
963 * (x1,y1) and (x2,y2), and the 'B' are at (ax,ay) and (dx,dy). */
964 debug(("Found offset-pair: %d at (%d,%d) and (%d,%d)\n",
965 state->nums[y1*w + x1], x1, y1, x2, y2));
966 debug((" and: %d at (%d,%d) and (%d,%d)\n",
967 an, ax, ay, dx[d], dy[d]));
968
969 xd = dx[d] - x2; yd = dy[d] - y2;
970 solver_op_add(ss, x2 + xd, y2, CIRCLE, "IP: next to offset-pair");
971 solver_op_add(ss, x2, y2 + yd, CIRCLE, "IP: next to offset-pair");
972 }
973 }
974 }
975}
976
977static int solve_offsetpair(game_state *state, struct solver_state *ss)
978{
979 int n_ops = ss->n_ops, x, xx, y, yy, n1, n2;
980
981 for (x = 0; x < state->w-1; x++) {
982 for (y = 0; y < state->h; y++) {
983 n1 = state->nums[y*state->w + x];
984 for (yy = y+1; yy < state->h; yy++) {
985 n2 = state->nums[yy*state->w + x];
986 if (n1 == n2) {
987 solve_offsetpair_pair(state, ss, x, y, x, yy);
988 solve_offsetpair_pair(state, ss, x, yy, x, y);
989 }
990 }
991 }
992 }
993 for (y = 0; y < state->h-1; y++) {
994 for (x = 0; x < state->w; x++) {
995 n1 = state->nums[y*state->w + x];
996 for (xx = x+1; xx < state->w; xx++) {
997 n2 = state->nums[y*state->w + xx];
998 if (n1 == n2) {
999 solve_offsetpair_pair(state, ss, x, y, xx, y);
1000 solve_offsetpair_pair(state, ss, xx, y, x, y);
1001 }
1002 }
1003 }
1004 }
1005 return ss->n_ops - n_ops;
1006}
1007
1008static bool solve_hassinglewhiteregion(
1009 game_state *state, struct solver_state *ss)
1010{
1011 int i, j, nwhite = 0, lwhite = -1, szwhite, start, end, next, a, d, x, y;
1012
1013 for (i = 0; i < state->n; i++) {
1014 if (!(state->flags[i] & F_BLACK)) {
1015 nwhite++;
1016 lwhite = i;
1017 }
1018 state->flags[i] &= ~F_SCRATCH;
1019 }
1020 if (lwhite == -1) {
1021 debug(("solve_hassinglewhite: no white squares found!\n"));
1022 state->impossible = true;
1023 return false;
1024 }
1025 /* We don't use connect_dsf here; it's too slow, and there's a quicker
1026 * algorithm if all we want is the size of one region. */
1027 /* Having written this, this algorithm is only about 5% faster than
1028 * using a dsf. */
1029 memset(ss->scratch, -1, state->n * sizeof(int));
1030 ss->scratch[0] = lwhite;
1031 state->flags[lwhite] |= F_SCRATCH;
1032 start = 0; end = next = 1;
1033 while (start < end) {
1034 for (a = start; a < end; a++) {
1035 i = ss->scratch[a]; assert(i != -1);
1036 for (d = 0; d < 4; d++) {
1037 x = (i % state->w) + dxs[d];
1038 y = (i / state->w) + dys[d];
1039 j = y*state->w + x;
1040 if (!INGRID(state, x, y)) continue;
1041 if (state->flags[j] & (F_BLACK | F_SCRATCH)) continue;
1042 ss->scratch[next++] = j;
1043 state->flags[j] |= F_SCRATCH;
1044 }
1045 }
1046 start = end; end = next;
1047 }
1048 szwhite = next;
1049 return (szwhite == nwhite);
1050}
1051
1052static void solve_removesplits_check(game_state *state, struct solver_state *ss,
1053 int x, int y)
1054{
1055 int i = y*state->w + x;
1056 bool issingle;
1057
1058 if (!INGRID(state, x, y)) return;
1059 if ((state->flags[i] & F_CIRCLE) || (state->flags[i] & F_BLACK))
1060 return;
1061
1062 /* If putting a black square at (x,y) would make the white region
1063 * non-contiguous, it must be circled. */
1064 state->flags[i] |= F_BLACK;
1065 issingle = solve_hassinglewhiteregion(state, ss);
1066 state->flags[i] &= ~F_BLACK;
1067
1068 if (!issingle)
1069 solver_op_add(ss, x, y, CIRCLE, "MC: black square here would split white region");
1070}
1071
1072/* For all black squares, search in squares diagonally adjacent to see if
1073 * we can rule out putting a black square there (because it would make the
1074 * white region non-contiguous). */
1075/* This function is likely to be somewhat slow. */
1076static int solve_removesplits(game_state *state, struct solver_state *ss)
1077{
1078 int i, x, y, n_ops = ss->n_ops;
1079
1080 if (!solve_hassinglewhiteregion(state, ss)) {
1081 debug(("solve_removesplits: white region is not contiguous at start!\n"));
1082 state->impossible = true;
1083 return 0;
1084 }
1085
1086 for (i = 0; i < state->n; i++) {
1087 if (!(state->flags[i] & F_BLACK)) continue;
1088
1089 x = i%state->w; y = i/state->w;
1090 solve_removesplits_check(state, ss, x-1, y-1);
1091 solve_removesplits_check(state, ss, x+1, y-1);
1092 solve_removesplits_check(state, ss, x+1, y+1);
1093 solve_removesplits_check(state, ss, x-1, y+1);
1094 }
1095 return ss->n_ops - n_ops;
1096}
1097
1098/*
1099 * This function performs a solver step that isn't implicit in the rules
1100 * of the game and is thus treated somewhat differently.
1101 *
1102 * It marks cells whose number does not exist elsewhere in its row/column
1103 * with circles. As it happens the game generator here does mean that this
1104 * is always correct, but it's a solving method that people should not have
1105 * to rely upon (except in the hidden 'sneaky' difficulty setting) and so
1106 * all grids at 'tricky' and above are checked to make sure that the grid
1107 * is no easier if this solving step is performed beforehand.
1108 *
1109 * Calling with ss=NULL just returns the number of sneaky deductions that
1110 * would have been made.
1111 */
1112static int solve_sneaky(game_state *state, struct solver_state *ss)
1113{
1114 int i, ii, x, xx, y, yy, nunique = 0;
1115
1116 /* Clear SCRATCH flags. */
1117 for (i = 0; i < state->n; i++) state->flags[i] &= ~F_SCRATCH;
1118
1119 for (x = 0; x < state->w; x++) {
1120 for (y = 0; y < state->h; y++) {
1121 i = y*state->w + x;
1122
1123 /* Check for duplicate numbers on our row, mark (both) if so */
1124 for (xx = x; xx < state->w; xx++) {
1125 ii = y*state->w + xx;
1126 if (i == ii) continue;
1127
1128 if (state->nums[i] == state->nums[ii]) {
1129 state->flags[i] |= F_SCRATCH;
1130 state->flags[ii] |= F_SCRATCH;
1131 }
1132 }
1133
1134 /* Check for duplicate numbers on our col, mark (both) if so */
1135 for (yy = y; yy < state->h; yy++) {
1136 ii = yy*state->w + x;
1137 if (i == ii) continue;
1138
1139 if (state->nums[i] == state->nums[ii]) {
1140 state->flags[i] |= F_SCRATCH;
1141 state->flags[ii] |= F_SCRATCH;
1142 }
1143 }
1144 }
1145 }
1146
1147 /* Any cell with no marking has no duplicates on its row or column:
1148 * set its CIRCLE. */
1149 for (i = 0; i < state->n; i++) {
1150 if (!(state->flags[i] & F_SCRATCH)) {
1151 if (ss) solver_op_add(ss, i%state->w, i/state->w, CIRCLE,
1152 "SNEAKY: only one of its number in row and col");
1153 nunique += 1;
1154 } else
1155 state->flags[i] &= ~F_SCRATCH;
1156 }
1157 return nunique;
1158}
1159
1160static int solve_specific(game_state *state, int diff, bool sneaky)
1161{
1162 struct solver_state *ss = solver_state_new(state);
1163
1164 if (sneaky) solve_sneaky(state, ss);
1165
1166 /* Some solver operations we only have to perform once --
1167 * they're only based on the numbers available, and not black
1168 * squares or circles which may be added later. */
1169
1170 solve_singlesep(state, ss); /* never sets impossible */
1171 solve_doubles(state, ss); /* ditto */
1172 solve_corners(state, ss); /* ditto */
1173
1174 if (diff >= DIFF_TRICKY)
1175 solve_offsetpair(state, ss); /* ditto */
1176
1177 while (1) {
1178 if (ss->n_ops > 0) solver_ops_do(state, ss);
1179 if (state->impossible) break;
1180
1181 if (solve_allblackbutone(state, ss) > 0) continue;
1182 if (state->impossible) break;
1183
1184 if (diff >= DIFF_TRICKY) {
1185 if (solve_removesplits(state, ss) > 0) continue;
1186 if (state->impossible) break;
1187 }
1188
1189 break;
1190 }
1191
1192 solver_state_free(ss);
1193 return state->impossible ? -1 : check_complete(state, CC_MUST_FILL);
1194}
1195
1196static char *solve_game(const game_state *state, const game_state *currstate,
1197 const char *aux, const char **error)
1198{
1199 game_state *solved = dup_game(currstate);
1200 char *move = NULL;
1201
1202 if (solve_specific(solved, DIFF_ANY, false) > 0) goto solved;
1203 free_game(solved);
1204
1205 solved = dup_game(state);
1206 if (solve_specific(solved, DIFF_ANY, false) > 0) goto solved;
1207 free_game(solved);
1208
1209 *error = "Unable to solve puzzle.";
1210 return NULL;
1211
1212solved:
1213 move = game_state_diff(currstate, solved, true);
1214 free_game(solved);
1215 return move;
1216}
1217
1218/* --- Game generation --- */
1219
1220/* A correctly completed Hitori board is essentially a latin square
1221 * (no duplicated numbers in any row or column) with black squares
1222 * added such that no black square touches another, and the white
1223 * squares make a contiguous region.
1224 *
1225 * So we can generate it by:
1226 * constructing a latin square
1227 * adding black squares at random (minding the constraints)
1228 * altering the numbers under the new black squares such that
1229 the solver gets a headstart working out where they are.
1230 */
1231
1232static bool new_game_is_good(const game_params *params,
1233 game_state *state, game_state *tosolve)
1234{
1235 int sret, sret_easy = 0;
1236
1237 memcpy(tosolve->nums, state->nums, state->n * sizeof(int));
1238 memset(tosolve->flags, 0, state->n * sizeof(unsigned int));
1239 tosolve->completed = false;
1240 tosolve->impossible = false;
1241
1242 /*
1243 * We try and solve it twice, once at our requested difficulty level
1244 * (ensuring it's soluble at all) and once at the level below (if
1245 * it exists), which we hope to fail: if you can also solve it at
1246 * the level below then it's too easy and we have to try again.
1247 *
1248 * With this puzzle in particular there's an extra finesse, which is
1249 * that we check that the generated puzzle isn't too easy _with
1250 * an extra solver step first_, which is the 'sneaky' mode of deductions
1251 * (asserting that any number which fulfils the latin-square rules
1252 * on its row/column must be white). This is an artefact of the
1253 * generation process and not implicit in the rules, so we don't want
1254 * people to be able to use it to make the puzzle easier.
1255 */
1256
1257 assert(params->diff < DIFF_MAX);
1258 sret = solve_specific(tosolve, params->diff, false);
1259 if (params->diff > DIFF_EASY) {
1260 memset(tosolve->flags, 0, state->n * sizeof(unsigned int));
1261 tosolve->completed = false;
1262 tosolve->impossible = false;
1263
1264 /* this is the only time the 'sneaky' flag is set. */
1265 sret_easy = solve_specific(tosolve, params->diff-1, true);
1266 }
1267
1268 if (sret <= 0 || sret_easy > 0) {
1269 debug(("Generated puzzle %s at chosen difficulty %s\n",
1270 sret <= 0 ? "insoluble" : "too easy",
1271 singles_diffnames[params->diff]));
1272 return false;
1273 }
1274 return true;
1275}
1276
1277#define MAXTRIES 20
1278
1279static int best_black_col(game_state *state, random_state *rs, int *scratch,
1280 int i, int *rownums, int *colnums)
1281{
1282 int w = state->w, x = i%w, y = i/w, j, o = state->o;
1283
1284 /* Randomise the list of numbers to try. */
1285 for (i = 0; i < o; i++) scratch[i] = i;
1286 shuffle(scratch, o, sizeof(int), rs);
1287
1288 /* Try each number in turn, first giving preference to removing
1289 * latin-square characteristics (i.e. those numbers which only
1290 * occur once in a row/column). The '&&' here, although intuitively
1291 * wrong, results in a smaller number of 'sneaky' deductions on
1292 * solvable boards. */
1293 for (i = 0; i < o; i++) {
1294 j = scratch[i] + 1;
1295 if (rownums[y*o + j-1] == 1 && colnums[x*o + j-1] == 1)
1296 goto found;
1297 }
1298
1299 /* Then try each number in turn returning the first one that's
1300 * not actually unique in its row/column (see comment below) */
1301 for (i = 0; i < o; i++) {
1302 j = scratch[i] + 1;
1303 if (rownums[y*o + j-1] != 0 || colnums[x*o + j-1] != 0)
1304 goto found;
1305 }
1306 assert(!"unable to place number under black cell.");
1307 return 0;
1308
1309found:
1310 /* Update column and row counts assuming this number will be placed. */
1311 rownums[y*o + j-1] += 1;
1312 colnums[x*o + j-1] += 1;
1313 return j;
1314}
1315
1316static char *new_game_desc(const game_params *params_orig, random_state *rs,
1317 char **aux, bool interactive)
1318{
1319 game_params *params = dup_params(params_orig);
1320 game_state *state = blank_game(params->w, params->h);
1321 game_state *tosolve = blank_game(params->w, params->h);
1322 int i, j, *scratch, *rownums, *colnums, x, y, ntries;
1323 int w = state->w, h = state->h, o = state->o;
1324 char *ret;
1325 digit *latin;
1326 struct solver_state *ss = solver_state_new(state);
1327
1328 /* Downgrade difficulty to Easy for puzzles so tiny that they aren't
1329 * possible to generate at Tricky. These are 2x2, 2x3 and 3x3, i.e.
1330 * any puzzle that doesn't have one dimension at least 4. */
1331 if ((w < 4 || h < 4) && params->diff > DIFF_EASY)
1332 params->diff = DIFF_EASY;
1333
1334 scratch = snewn(state->n, int);
1335 rownums = snewn(h*o, int);
1336 colnums = snewn(w*o, int);
1337
1338generate:
1339 ss->n_ops = 0;
1340 debug(("Starting game generation, size %dx%d\n", w, h));
1341
1342 memset(state->flags, 0, state->n*sizeof(unsigned int));
1343
1344 /* First, generate the latin rectangle.
1345 * The order of this, o, is max(w,h). */
1346 latin = latin_generate_rect(w, h, rs);
1347 for (i = 0; i < state->n; i++)
1348 state->nums[i] = (int)latin[i];
1349 sfree(latin);
1350 debug_state("State after latin square", state);
1351
1352 /* Add black squares at random, using bits of solver as we go (to lay
1353 * white squares), until we can lay no more blacks. */
1354 for (i = 0; i < state->n; i++)
1355 scratch[i] = i;
1356 shuffle(scratch, state->n, sizeof(int), rs);
1357 for (j = 0; j < state->n; j++) {
1358 i = scratch[j];
1359 if ((state->flags[i] & F_CIRCLE) || (state->flags[i] & F_BLACK)) {
1360 debug(("generator skipping (%d,%d): %s\n", i%w, i/w,
1361 (state->flags[i] & F_CIRCLE) ? "CIRCLE" : "BLACK"));
1362 continue; /* solver knows this must be one or the other already. */
1363 }
1364
1365 /* Add a random black cell... */
1366 solver_op_add(ss, i%w, i/w, BLACK, "Generator: adding random black cell");
1367 solver_ops_do(state, ss);
1368
1369 /* ... and do as well as we know how to lay down whites that are now forced. */
1370 solve_allblackbutone(state, ss);
1371 solver_ops_do(state, ss);
1372
1373 solve_removesplits(state, ss);
1374 solver_ops_do(state, ss);
1375
1376 if (state->impossible) {
1377 debug(("generator made impossible, restarting...\n"));
1378 goto generate;
1379 }
1380 }
1381 debug_state("State after adding blacks", state);
1382
1383 /* Now we know which squares are white and which are black, we lay numbers
1384 * under black squares at random, except that the number must appear in
1385 * white cells at least once more in the same column or row as that [black]
1386 * square. That's necessary to avoid multiple solutions, where blackening
1387 * squares in the finished puzzle becomes optional. We use two arrays:
1388 *
1389 * rownums[ROW * o + NUM-1] is the no. of white cells containing NUM in y=ROW
1390 * colnums[COL * o + NUM-1] is the no. of white cells containing NUM in x=COL
1391 */
1392
1393 memset(rownums, 0, h*o * sizeof(int));
1394 memset(colnums, 0, w*o * sizeof(int));
1395 for (i = 0; i < state->n; i++) {
1396 if (state->flags[i] & F_BLACK) continue;
1397 j = state->nums[i];
1398 x = i%w; y = i/w;
1399 rownums[y * o + j-1] += 1;
1400 colnums[x * o + j-1] += 1;
1401 }
1402
1403 ntries = 0;
1404randomise:
1405 for (i = 0; i < state->n; i++) {
1406 if (!(state->flags[i] & F_BLACK)) continue;
1407 state->nums[i] = best_black_col(state, rs, scratch, i, rownums, colnums);
1408 }
1409 debug_state("State after adding numbers", state);
1410
1411 /* DIFF_ANY just returns whatever we first generated, for testing purposes. */
1412 if (params->diff != DIFF_ANY &&
1413 !new_game_is_good(params, state, tosolve)) {
1414 ntries++;
1415 if (ntries > MAXTRIES) {
1416 debug(("Ran out of randomisation attempts, re-generating.\n"));
1417 goto generate;
1418 }
1419 debug(("Re-randomising numbers under black squares.\n"));
1420 goto randomise;
1421 }
1422
1423 ret = generate_desc(state, false);
1424
1425 free_game(tosolve);
1426 free_game(state);
1427 free_params(params);
1428 solver_state_free(ss);
1429 sfree(scratch);
1430 sfree(rownums);
1431 sfree(colnums);
1432
1433 return ret;
1434}
1435
1436static const char *validate_desc(const game_params *params, const char *desc)
1437{
1438 const char *ret = NULL;
1439
1440 unpick_desc(params, desc, NULL, &ret);
1441 return ret;
1442}
1443
1444static game_state *new_game(midend *me, const game_params *params,
1445 const char *desc)
1446{
1447 game_state *state = NULL;
1448
1449 unpick_desc(params, desc, &state, NULL);
1450 if (!state) assert(!"new_game failed to unpick");
1451 return state;
1452}
1453
1454/* --- Game UI and move routines --- */
1455
1456struct game_ui {
1457 int cx, cy;
1458 bool cshow, show_black_nums;
1459};
1460
1461static game_ui *new_ui(const game_state *state)
1462{
1463 game_ui *ui = snew(game_ui);
1464
1465 ui->cx = ui->cy = 0;
1466 ui->cshow = getenv_bool("PUZZLES_SHOW_CURSOR", false);
1467 ui->show_black_nums = false;
1468
1469 return ui;
1470}
1471
1472static config_item *get_prefs(game_ui *ui)
1473{
1474 config_item *ret;
1475
1476 ret = snewn(N_PREF_ITEMS+1, config_item);
1477
1478 ret[PREF_SHOW_BLACK_NUMS].name = "Show numbers on black squares";
1479 ret[PREF_SHOW_BLACK_NUMS].kw = "show-black-nums";
1480 ret[PREF_SHOW_BLACK_NUMS].type = C_BOOLEAN;
1481 ret[PREF_SHOW_BLACK_NUMS].u.boolean.bval = ui->show_black_nums;
1482
1483 ret[N_PREF_ITEMS].name = NULL;
1484 ret[N_PREF_ITEMS].type = C_END;
1485
1486 return ret;
1487}
1488
1489static void set_prefs(game_ui *ui, const config_item *cfg)
1490{
1491 ui->show_black_nums = cfg[PREF_SHOW_BLACK_NUMS].u.boolean.bval;
1492}
1493
1494static void free_ui(game_ui *ui)
1495{
1496 sfree(ui);
1497}
1498
1499static void game_changed_state(game_ui *ui, const game_state *oldstate,
1500 const game_state *newstate)
1501{
1502 if (!oldstate->completed && newstate->completed)
1503 ui->cshow = false;
1504}
1505
1506static const char *current_key_label(const game_ui *ui,
1507 const game_state *state, int button)
1508{
1509 if (IS_CURSOR_SELECT(button) && ui->cshow) {
1510 unsigned int f = state->flags[ui->cy * state->w + ui->cx];
1511 if (f & F_BLACK) return "Restore";
1512 if (f & F_CIRCLE) return "Remove";
1513 return button == CURSOR_SELECT ? "Black" : "Circle";
1514 }
1515 return "";
1516}
1517
1518#define DS_BLACK 0x1
1519#define DS_CIRCLE 0x2
1520#define DS_CURSOR 0x4
1521#define DS_BLACK_NUM 0x8
1522#define DS_ERROR 0x10
1523#define DS_FLASH 0x20
1524#define DS_IMPOSSIBLE 0x40
1525
1526struct game_drawstate {
1527 int tilesize;
1528 bool started, solved;
1529 int w, h, n;
1530
1531 unsigned int *flags;
1532};
1533
1534static char *interpret_move(const game_state *state, game_ui *ui,
1535 const game_drawstate *ds,
1536 int mx, int my, int button)
1537{
1538 char buf[80], c;
1539 int i, x = FROMCOORD(mx), y = FROMCOORD(my);
1540 enum { NONE, TOGGLE_BLACK, TOGGLE_CIRCLE, UI } action = NONE;
1541
1542 if (IS_CURSOR_MOVE(button))
1543 return move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, true,
1544 &ui->cshow);
1545 else if (IS_CURSOR_SELECT(button)) {
1546 x = ui->cx; y = ui->cy;
1547 if (!ui->cshow) {
1548 action = UI;
1549 ui->cshow = true;
1550 }
1551 if (button == CURSOR_SELECT) {
1552 action = TOGGLE_BLACK;
1553 } else if (button == CURSOR_SELECT2) {
1554 action = TOGGLE_CIRCLE;
1555 }
1556 } else if (IS_MOUSE_DOWN(button)) {
1557 if (ui->cshow) {
1558 ui->cshow = false;
1559 action = UI;
1560 }
1561 if (!INGRID(state, x, y)) {
1562 ui->show_black_nums = !ui->show_black_nums;
1563 action = UI;
1564 } else if (button == LEFT_BUTTON) {
1565 action = TOGGLE_BLACK;
1566 } else if (button == RIGHT_BUTTON) {
1567 action = TOGGLE_CIRCLE;
1568 }
1569 }
1570 if (action == UI) return MOVE_UI_UPDATE;
1571
1572 if (action == TOGGLE_BLACK || action == TOGGLE_CIRCLE) {
1573 i = y * state->w + x;
1574 if (state->flags[i] & (F_BLACK | F_CIRCLE))
1575 c = 'E';
1576 else
1577 c = (action == TOGGLE_BLACK) ? 'B' : 'C';
1578 sprintf(buf, "%c%d,%d", (int)c, x, y);
1579 return dupstr(buf);
1580 }
1581
1582 return NULL;
1583}
1584
1585static game_state *execute_move(const game_state *state, const char *move)
1586{
1587 game_state *ret = dup_game(state);
1588 int x, y, i, n;
1589
1590 debug(("move: %s\n", move));
1591
1592 while (*move) {
1593 char c = *move;
1594 if (c == 'B' || c == 'C' || c == 'E') {
1595 move++;
1596 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
1597 !INGRID(state, x, y))
1598 goto badmove;
1599
1600 i = y*ret->w + x;
1601 ret->flags[i] &= ~(F_CIRCLE | F_BLACK); /* empty first, always. */
1602 if (c == 'B')
1603 ret->flags[i] |= F_BLACK;
1604 else if (c == 'C')
1605 ret->flags[i] |= F_CIRCLE;
1606 move += n;
1607 } else if (c == 'S') {
1608 move++;
1609 ret->used_solve = true;
1610 } else
1611 goto badmove;
1612
1613 if (*move == ';')
1614 move++;
1615 else if (*move)
1616 goto badmove;
1617 }
1618 if (check_complete(ret, CC_MARK_ERRORS)) ret->completed = true;
1619 return ret;
1620
1621badmove:
1622 free_game(ret);
1623 return NULL;
1624}
1625
1626/* ----------------------------------------------------------------------
1627 * Drawing routines.
1628 */
1629
1630static void game_compute_size(const game_params *params, int tilesize,
1631 const game_ui *ui, int *x, int *y)
1632{
1633 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1634 struct { int tilesize; } ads, *ds = &ads;
1635 ads.tilesize = tilesize;
1636
1637 *x = TILE_SIZE * params->w + 2 * BORDER;
1638 *y = TILE_SIZE * params->h + 2 * BORDER;
1639}
1640
1641static void game_set_size(drawing *dr, game_drawstate *ds,
1642 const game_params *params, int tilesize)
1643{
1644 ds->tilesize = tilesize;
1645}
1646
1647static float *game_colours(frontend *fe, int *ncolours)
1648{
1649 float *ret = snewn(3 * NCOLOURS, float);
1650 int i;
1651
1652 game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_LOWLIGHT);
1653 for (i = 0; i < 3; i++) {
1654 ret[COL_BLACK * 3 + i] = 0.0F;
1655 ret[COL_BLACKNUM * 3 + i] = 0.4F;
1656 ret[COL_WHITE * 3 + i] = 1.0F;
1657 ret[COL_GRID * 3 + i] = ret[COL_LOWLIGHT * 3 + i];
1658 ret[COL_UNUSED1 * 3 + i] = 0.0F; /* To placate an assertion. */
1659 }
1660 ret[COL_CURSOR * 3 + 0] = 0.2F;
1661 ret[COL_CURSOR * 3 + 1] = 0.8F;
1662 ret[COL_CURSOR * 3 + 2] = 0.0F;
1663
1664 ret[COL_ERROR * 3 + 0] = 1.0F;
1665 ret[COL_ERROR * 3 + 1] = 0.0F;
1666 ret[COL_ERROR * 3 + 2] = 0.0F;
1667
1668 *ncolours = NCOLOURS;
1669 return ret;
1670}
1671
1672static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
1673{
1674 struct game_drawstate *ds = snew(struct game_drawstate);
1675
1676 ds->tilesize = 0;
1677 ds->started = false;
1678 ds->solved = false;
1679 ds->w = state->w;
1680 ds->h = state->h;
1681 ds->n = state->n;
1682
1683 ds->flags = snewn(state->n, unsigned int);
1684
1685 memset(ds->flags, 0, state->n*sizeof(unsigned int));
1686
1687 return ds;
1688}
1689
1690static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1691{
1692 sfree(ds->flags);
1693 sfree(ds);
1694}
1695
1696static void tile_redraw(drawing *dr, game_drawstate *ds, int x, int y,
1697 int num, unsigned int f)
1698{
1699 int tcol, bg, cx, cy, tsz;
1700 bool dnum;
1701 char buf[32];
1702
1703 if (f & DS_BLACK) {
1704 bg = (f & DS_ERROR) ? COL_ERROR : COL_BLACK;
1705 tcol = COL_BLACKNUM;
1706 dnum = (f & DS_BLACK_NUM);
1707 } else {
1708 bg = (f & DS_FLASH) ? COL_LOWLIGHT : COL_BACKGROUND;
1709 tcol = (f & DS_ERROR) ? COL_ERROR : COL_BLACK;
1710 dnum = true;
1711 }
1712
1713 cx = x + TILE_SIZE/2; cy = y + TILE_SIZE/2;
1714
1715 draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE, bg);
1716 draw_rect_outline(dr, x, y, TILE_SIZE, TILE_SIZE,
1717 (f & DS_IMPOSSIBLE) ? COL_ERROR : COL_GRID);
1718
1719 if (f & DS_CIRCLE) {
1720 draw_circle(dr, cx, cy, CRAD, tcol, tcol);
1721 draw_circle(dr, cx, cy, CRAD-1, bg, tcol);
1722 }
1723
1724 if (dnum) {
1725 sprintf(buf, "%d", num);
1726 if (strlen(buf) == 1)
1727 tsz = TEXTSZ;
1728 else
1729 tsz = (CRAD*2 - 1) / strlen(buf);
1730 draw_text(dr, cx, cy, FONT_VARIABLE, tsz,
1731 ALIGN_VCENTRE | ALIGN_HCENTRE, tcol, buf);
1732 }
1733
1734 if (f & DS_CURSOR)
1735 draw_rect_corners(dr, cx, cy, TEXTSZ/2, COL_CURSOR);
1736
1737 draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
1738}
1739
1740static void game_redraw(drawing *dr, game_drawstate *ds,
1741 const game_state *oldstate, const game_state *state,
1742 int dir, const game_ui *ui,
1743 float animtime, float flashtime)
1744{
1745 int x, y, i, flash;
1746 unsigned int f;
1747
1748 flash = (int)(flashtime * 5 / FLASH_TIME) % 2;
1749
1750 if (!ds->started) {
1751 int wsz = TILE_SIZE * state->w + 2 * BORDER;
1752 int hsz = TILE_SIZE * state->h + 2 * BORDER;
1753 draw_rect_outline(dr, COORD(0)-1, COORD(0)-1,
1754 TILE_SIZE * state->w + 2, TILE_SIZE * state->h + 2,
1755 COL_GRID);
1756 draw_update(dr, 0, 0, wsz, hsz);
1757 }
1758 for (x = 0; x < state->w; x++) {
1759 for (y = 0; y < state->h; y++) {
1760 i = y*state->w + x;
1761 f = 0;
1762
1763 if (flash) f |= DS_FLASH;
1764 if (state->impossible) f |= DS_IMPOSSIBLE;
1765
1766 if (ui->cshow && x == ui->cx && y == ui->cy)
1767 f |= DS_CURSOR;
1768 if (state->flags[i] & F_BLACK) {
1769 f |= DS_BLACK;
1770 if (ui->show_black_nums) f |= DS_BLACK_NUM;
1771 }
1772 if (state->flags[i] & F_CIRCLE)
1773 f |= DS_CIRCLE;
1774 if (state->flags[i] & F_ERROR)
1775 f |= DS_ERROR;
1776
1777 if (!ds->started || ds->flags[i] != f) {
1778 tile_redraw(dr, ds, COORD(x), COORD(y),
1779 state->nums[i], f);
1780 ds->flags[i] = f;
1781 }
1782 }
1783 }
1784 ds->started = true;
1785}
1786
1787static float game_anim_length(const game_state *oldstate,
1788 const game_state *newstate, int dir, game_ui *ui)
1789{
1790 return 0.0F;
1791}
1792
1793static float game_flash_length(const game_state *oldstate,
1794 const game_state *newstate, int dir, game_ui *ui)
1795{
1796 if (!oldstate->completed &&
1797 newstate->completed && !newstate->used_solve)
1798 return FLASH_TIME;
1799 return 0.0F;
1800}
1801
1802static void game_get_cursor_location(const game_ui *ui,
1803 const game_drawstate *ds,
1804 const game_state *state,
1805 const game_params *params,
1806 int *x, int *y, int *w, int *h)
1807{
1808 if(ui->cshow) {
1809 *x = COORD(ui->cx);
1810 *y = COORD(ui->cy);
1811 *w = *h = TILE_SIZE;
1812 }
1813}
1814
1815static int game_status(const game_state *state)
1816{
1817 return state->completed ? +1 : 0;
1818}
1819
1820static void game_print_size(const game_params *params, const game_ui *ui,
1821 float *x, float *y)
1822{
1823 int pw, ph;
1824
1825 /* 8mm squares by default. */
1826 game_compute_size(params, 800, ui, &pw, &ph);
1827 *x = pw / 100.0F;
1828 *y = ph / 100.0F;
1829}
1830
1831static void game_print(drawing *dr, const game_state *state, const game_ui *ui,
1832 int tilesize)
1833{
1834 int ink = print_mono_colour(dr, 0);
1835 int paper = print_mono_colour(dr, 1);
1836 int x, y, ox, oy, i;
1837 char buf[32];
1838
1839 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1840 game_drawstate ads, *ds = &ads;
1841 game_set_size(dr, ds, NULL, tilesize);
1842
1843 print_line_width(dr, 2 * TILE_SIZE / 40);
1844
1845 for (x = 0; x < state->w; x++) {
1846 for (y = 0; y < state->h; y++) {
1847 ox = COORD(x); oy = COORD(y);
1848 i = y*state->w+x;
1849
1850 if (state->flags[i] & F_BLACK) {
1851 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink);
1852 } else {
1853 draw_rect_outline(dr, ox, oy, TILE_SIZE, TILE_SIZE, ink);
1854
1855 if (state->flags[i] & DS_CIRCLE)
1856 draw_circle(dr, ox+TILE_SIZE/2, oy+TILE_SIZE/2, CRAD,
1857 paper, ink);
1858
1859 sprintf(buf, "%d", state->nums[i]);
1860 draw_text(dr, ox+TILE_SIZE/2, oy+TILE_SIZE/2, FONT_VARIABLE,
1861 TEXTSZ/strlen(buf), ALIGN_VCENTRE | ALIGN_HCENTRE,
1862 ink, buf);
1863 }
1864 }
1865 }
1866}
1867
1868#ifdef COMBINED
1869#define thegame singles
1870#endif
1871
1872const struct game thegame = {
1873 "Singles", "games.singles", "singles",
1874 default_params,
1875 game_fetch_preset, NULL,
1876 decode_params,
1877 encode_params,
1878 free_params,
1879 dup_params,
1880 true, game_configure, custom_params,
1881 validate_params,
1882 new_game_desc,
1883 validate_desc,
1884 new_game,
1885 dup_game,
1886 free_game,
1887 true, solve_game,
1888 true, game_can_format_as_text_now, game_text_format,
1889 get_prefs, set_prefs,
1890 new_ui,
1891 free_ui,
1892 NULL, /* encode_ui */
1893 NULL, /* decode_ui */
1894 NULL, /* game_request_keys */
1895 game_changed_state,
1896 current_key_label,
1897 interpret_move,
1898 execute_move,
1899 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
1900 game_colours,
1901 game_new_drawstate,
1902 game_free_drawstate,
1903 game_redraw,
1904 game_anim_length,
1905 game_flash_length,
1906 game_get_cursor_location,
1907 game_status,
1908 true, false, game_print_size, game_print,
1909 false, /* wants_statusbar */
1910 false, NULL, /* timing_state */
1911 REQUIRE_RBUTTON, /* flags */
1912};
1913
1914#ifdef STANDALONE_SOLVER
1915
1916#include <time.h>
1917#include <stdarg.h>
1918
1919static void start_soak(game_params *p, random_state *rs)
1920{
1921 time_t tt_start, tt_now, tt_last;
1922 char *desc, *aux;
1923 game_state *s;
1924 int i, n = 0, ndiff[DIFF_MAX], diff, sret, nblack = 0, nsneaky = 0;
1925
1926 tt_start = tt_now = time(NULL);
1927
1928 printf("Soak-testing a %dx%d grid.\n", p->w, p->h);
1929 p->diff = DIFF_ANY;
1930
1931 memset(ndiff, 0, DIFF_MAX * sizeof(int));
1932
1933 while (1) {
1934 n++;
1935 desc = new_game_desc(p, rs, &aux, false);
1936 s = new_game(NULL, p, desc);
1937 nsneaky += solve_sneaky(s, NULL);
1938
1939 for (diff = 0; diff < DIFF_MAX; diff++) {
1940 memset(s->flags, 0, s->n * sizeof(unsigned int));
1941 s->completed = false;
1942 s->impossible = false;
1943 sret = solve_specific(s, diff, false);
1944 if (sret > 0) {
1945 ndiff[diff]++;
1946 break;
1947 } else if (sret < 0)
1948 fprintf(stderr, "Impossible! %s\n", desc);
1949 }
1950 for (i = 0; i < s->n; i++) {
1951 if (s->flags[i] & F_BLACK) nblack++;
1952 }
1953 free_game(s);
1954 sfree(desc);
1955
1956 tt_last = time(NULL);
1957 if (tt_last > tt_now) {
1958 tt_now = tt_last;
1959 printf("%d total, %3.1f/s, bl/sn %3.1f%%/%3.1f%%: ",
1960 n, (double)n / ((double)tt_now - tt_start),
1961 ((double)nblack * 100.0) / (double)(n * p->w * p->h),
1962 ((double)nsneaky * 100.0) / (double)(n * p->w * p->h));
1963 for (diff = 0; diff < DIFF_MAX; diff++) {
1964 if (diff > 0) printf(", ");
1965 printf("%d (%3.1f%%) %s",
1966 ndiff[diff], (double)ndiff[diff] * 100.0 / (double)n,
1967 singles_diffnames[diff]);
1968 }
1969 printf("\n");
1970 }
1971 }
1972}
1973
1974int main(int argc, char **argv)
1975{
1976 char *id = NULL, *desc, *desc_gen = NULL, *tgame, *aux;
1977 const char *err;
1978 game_state *s = NULL;
1979 game_params *p = NULL;
1980 int soln, ret = 1;
1981 bool soak = false;
1982 time_t seed = time(NULL);
1983 random_state *rs = NULL;
1984
1985 setvbuf(stdout, NULL, _IONBF, 0);
1986
1987 while (--argc > 0) {
1988 char *p = *++argv;
1989 if (!strcmp(p, "-v")) {
1990 verbose = true;
1991 } else if (!strcmp(p, "--soak")) {
1992 soak = true;
1993 } else if (!strcmp(p, "--seed")) {
1994 if (argc == 0) {
1995 fprintf(stderr, "%s: --seed needs an argument", argv[0]);
1996 goto done;
1997 }
1998 seed = (time_t)atoi(*++argv);
1999 argc--;
2000 } else if (*p == '-') {
2001 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2002 return 1;
2003 } else {
2004 id = p;
2005 }
2006 }
2007
2008 rs = random_new((void*)&seed, sizeof(time_t));
2009
2010 if (!id) {
2011 fprintf(stderr, "usage: %s [-v] [--soak] <params> | <game_id>\n", argv[0]);
2012 goto done;
2013 }
2014 desc = strchr(id, ':');
2015 if (desc) *desc++ = '\0';
2016
2017 p = default_params();
2018 decode_params(p, id);
2019 err = validate_params(p, true);
2020 if (err) {
2021 fprintf(stderr, "%s: %s", argv[0], err);
2022 goto done;
2023 }
2024
2025 if (soak) {
2026 if (desc) {
2027 fprintf(stderr, "%s: --soak only needs params, not game desc.\n", argv[0]);
2028 goto done;
2029 }
2030 start_soak(p, rs);
2031 } else {
2032 if (!desc) desc = desc_gen = new_game_desc(p, rs, &aux, false);
2033
2034 err = validate_desc(p, desc);
2035 if (err) {
2036 fprintf(stderr, "%s: %s\n", argv[0], err);
2037 free_params(p);
2038 goto done;
2039 }
2040 s = new_game(NULL, p, desc);
2041
2042 if (verbose) {
2043 tgame = game_text_format(s);
2044 fputs(tgame, stdout);
2045 sfree(tgame);
2046 }
2047
2048 soln = solve_specific(s, DIFF_ANY, false);
2049 tgame = game_text_format(s);
2050 fputs(tgame, stdout);
2051 sfree(tgame);
2052 printf("Game was %s.\n\n",
2053 soln < 0 ? "impossible" : soln > 0 ? "solved" : "not solved");
2054 }
2055 ret = 0;
2056
2057done:
2058 if (desc_gen) sfree(desc_gen);
2059 if (p) free_params(p);
2060 if (s) free_game(s);
2061 if (rs) random_free(rs);
2062
2063 return ret;
2064}
2065
2066#endif
2067
2068
2069/* vim: set shiftwidth=4 tabstop=8: */