Tweak a conditional expression in pearl.c to work around a display bug
[sgt-puzzles/ydirson.git] / pearl.c
blob21cca29a80a4ae85d9926c4f1f397a1b4f22124e
1 /*
2 * pearl.c: Nikoli's `Masyu' puzzle.
3 */
5 /*
6 * TODO:
8 * - Keyboard-control cursor. (Would probably have to address both
9 * square centres, for laying multiple edges at a time in a
10 * drag-like style, and grid edges for marking particular line
11 * segments as no-go.)
13 * - Generation is still pretty slow, due to difficulty coming up in
14 * the first place with a loop that makes a soluble puzzle even
15 * with all possible clues filled in.
16 * + A possible alternative strategy to further tuning of the
17 * existing loop generator would be to throw the entire
18 * mechanism out and instead write a different generator from
19 * scratch which evolves the solution along with the puzzle:
20 * place a few clues, nail down a bit of the loop, place another
21 * clue, nail down some more, etc. However, I don't have a
22 * detailed plan for any such mechanism, so it may be a pipe
23 * dream.
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <ctype.h>
31 #include <math.h>
33 #include "puzzles.h"
34 #include "grid.h"
35 #include "loopgen.h"
37 #define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
39 #define NOCLUE 0
40 #define CORNER 1
41 #define STRAIGHT 2
43 #define R 1
44 #define U 2
45 #define L 4
46 #define D 8
48 #define DX(d) ( ((d)==R) - ((d)==L) )
49 #define DY(d) ( ((d)==D) - ((d)==U) )
51 #define F(d) (((d << 2) | (d >> 2)) & 0xF)
52 #define C(d) (((d << 3) | (d >> 1)) & 0xF)
53 #define A(d) (((d << 1) | (d >> 3)) & 0xF)
55 #define LR (L | R)
56 #define RL (R | L)
57 #define UD (U | D)
58 #define DU (D | U)
59 #define LU (L | U)
60 #define UL (U | L)
61 #define LD (L | D)
62 #define DL (D | L)
63 #define RU (R | U)
64 #define UR (U | R)
65 #define RD (R | D)
66 #define DR (D | R)
67 #define BLANK 0
68 #define UNKNOWN 15
70 #define bLR (1 << LR)
71 #define bRL (1 << RL)
72 #define bUD (1 << UD)
73 #define bDU (1 << DU)
74 #define bLU (1 << LU)
75 #define bUL (1 << UL)
76 #define bLD (1 << LD)
77 #define bDL (1 << DL)
78 #define bRU (1 << RU)
79 #define bUR (1 << UR)
80 #define bRD (1 << RD)
81 #define bDR (1 << DR)
82 #define bBLANK (1 << BLANK)
84 enum {
85 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
86 COL_BLACK, COL_WHITE,
87 COL_ERROR, COL_GRID, COL_FLASH,
88 COL_DRAGON, COL_DRAGOFF,
89 NCOLOURS
92 /* Macro ickery copied from slant.c */
93 #define DIFFLIST(A) \
94 A(EASY,Easy,e) \
95 A(TRICKY,Tricky,t)
96 #define ENUM(upper,title,lower) DIFF_ ## upper,
97 #define TITLE(upper,title,lower) #title,
98 #define ENCODE(upper,title,lower) #lower
99 #define CONFIG(upper,title,lower) ":" #title
100 enum { DIFFLIST(ENUM) DIFFCOUNT };
101 static char const *const pearl_diffnames[] = { DIFFLIST(TITLE) "(count)" };
102 static char const pearl_diffchars[] = DIFFLIST(ENCODE);
103 #define DIFFCONFIG DIFFLIST(CONFIG)
105 struct game_params {
106 int w, h;
107 int difficulty;
108 int nosolve; /* XXX remove me! */
111 struct shared_state {
112 int w, h, sz;
113 char *clues; /* size w*h */
114 int refcnt;
117 #define INGRID(state, gx, gy) ((gx) >= 0 && (gx) < (state)->shared->w && \
118 (gy) >= 0 && (gy) < (state)->shared->h)
119 struct game_state {
120 struct shared_state *shared;
121 char *lines; /* size w*h: lines placed */
122 char *errors; /* size w*h: errors detected */
123 char *marks; /* size w*h: 'no line here' marks placed. */
124 int completed, used_solve;
125 int loop_length; /* filled in by check_completion when complete. */
128 #define DEFAULT_PRESET 3
130 static const struct game_params pearl_presets[] = {
131 {6, 6, DIFF_EASY},
132 {6, 6, DIFF_TRICKY},
133 {8, 8, DIFF_EASY},
134 {8, 8, DIFF_TRICKY},
135 {10, 10, DIFF_EASY},
136 {10, 10, DIFF_TRICKY},
137 {12, 8, DIFF_EASY},
138 {12, 8, DIFF_TRICKY},
141 static game_params *default_params(void)
143 game_params *ret = snew(game_params);
145 *ret = pearl_presets[DEFAULT_PRESET];
146 ret->nosolve = FALSE;
148 return ret;
151 static int game_fetch_preset(int i, char **name, game_params **params)
153 game_params *ret;
154 char buf[64];
156 if (i < 0 || i >= lenof(pearl_presets)) return FALSE;
158 ret = default_params();
159 *ret = pearl_presets[i]; /* struct copy */
160 *params = ret;
162 sprintf(buf, "%dx%d %s",
163 pearl_presets[i].w, pearl_presets[i].h,
164 pearl_diffnames[pearl_presets[i].difficulty]);
165 *name = dupstr(buf);
167 return TRUE;
170 static void free_params(game_params *params)
172 sfree(params);
175 static game_params *dup_params(game_params *params)
177 game_params *ret = snew(game_params);
178 *ret = *params; /* structure copy */
179 return ret;
182 static void decode_params(game_params *ret, char const *string)
184 ret->w = ret->h = atoi(string);
185 while (*string && isdigit((unsigned char) *string)) ++string;
186 if (*string == 'x') {
187 string++;
188 ret->h = atoi(string);
189 while (*string && isdigit((unsigned char)*string)) string++;
192 ret->difficulty = DIFF_EASY;
193 if (*string == 'd') {
194 int i;
195 string++;
196 for (i = 0; i < DIFFCOUNT; i++)
197 if (*string == pearl_diffchars[i])
198 ret->difficulty = i;
199 if (*string) string++;
202 ret->nosolve = FALSE;
203 if (*string == 'n') {
204 ret->nosolve = TRUE;
205 string++;
209 static char *encode_params(game_params *params, int full)
211 char buf[256];
212 sprintf(buf, "%dx%d", params->w, params->h);
213 if (full)
214 sprintf(buf + strlen(buf), "d%c%s",
215 pearl_diffchars[params->difficulty],
216 params->nosolve ? "n" : "");
217 return dupstr(buf);
220 static config_item *game_configure(game_params *params)
222 config_item *ret;
223 char buf[64];
225 ret = snewn(5, config_item);
227 ret[0].name = "Width";
228 ret[0].type = C_STRING;
229 sprintf(buf, "%d", params->w);
230 ret[0].sval = dupstr(buf);
231 ret[0].ival = 0;
233 ret[1].name = "Height";
234 ret[1].type = C_STRING;
235 sprintf(buf, "%d", params->h);
236 ret[1].sval = dupstr(buf);
237 ret[1].ival = 0;
239 ret[2].name = "Difficulty";
240 ret[2].type = C_CHOICES;
241 ret[2].sval = DIFFCONFIG;
242 ret[2].ival = params->difficulty;
244 ret[3].name = "Allow unsoluble";
245 ret[3].type = C_BOOLEAN;
246 ret[3].sval = NULL;
247 ret[3].ival = params->nosolve;
249 ret[4].name = NULL;
250 ret[4].type = C_END;
251 ret[4].sval = NULL;
252 ret[4].ival = 0;
254 return ret;
257 static game_params *custom_params(config_item *cfg)
259 game_params *ret = snew(game_params);
261 ret->w = atoi(cfg[0].sval);
262 ret->h = atoi(cfg[1].sval);
263 ret->difficulty = cfg[2].ival;
264 ret->nosolve = cfg[3].ival;
266 return ret;
269 static char *validate_params(game_params *params, int full)
271 if (params->w < 5) return "Width must be at least five";
272 if (params->h < 5) return "Height must be at least five";
273 if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT)
274 return "Unknown difficulty level";
276 return NULL;
279 /* ----------------------------------------------------------------------
280 * Solver.
283 int pearl_solve(int w, int h, char *clues, char *result,
284 int difficulty, int partial)
286 int W = 2*w+1, H = 2*h+1;
287 short *workspace;
288 int *dsf, *dsfsize;
289 int x, y, b, d;
290 int ret = -1;
293 * workspace[(2*y+1)*W+(2*x+1)] indicates the possible nature
294 * of the square (x,y), as a logical OR of bitfields.
296 * workspace[(2*y)*W+(2*x+1)], for x odd and y even, indicates
297 * whether the horizontal edge between (x,y) and (x+1,y) is
298 * connected (1), disconnected (2) or unknown (3).
300 * workspace[(2*y+1)*W+(2*x)], indicates the same about the
301 * vertical edge between (x,y) and (x,y+1).
303 * Initially, every square is considered capable of being in
304 * any of the seven possible states (two straights, four
305 * corners and empty), except those corresponding to clue
306 * squares which are more restricted.
308 * Initially, all edges are unknown, except the ones around the
309 * grid border which are known to be disconnected.
311 workspace = snewn(W*H, short);
312 for (x = 0; x < W*H; x++)
313 workspace[x] = 0;
314 /* Square states */
315 for (y = 0; y < h; y++)
316 for (x = 0; x < w; x++)
317 switch (clues[y*w+x]) {
318 case CORNER:
319 workspace[(2*y+1)*W+(2*x+1)] = bLU|bLD|bRU|bRD;
320 break;
321 case STRAIGHT:
322 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD;
323 break;
324 default:
325 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD|bLU|bLD|bRU|bRD|bBLANK;
326 break;
328 /* Horizontal edges */
329 for (y = 0; y <= h; y++)
330 for (x = 0; x < w; x++)
331 workspace[(2*y)*W+(2*x+1)] = (y==0 || y==h ? 2 : 3);
332 /* Vertical edges */
333 for (y = 0; y < h; y++)
334 for (x = 0; x <= w; x++)
335 workspace[(2*y+1)*W+(2*x)] = (x==0 || x==w ? 2 : 3);
338 * We maintain a dsf of connected squares, together with a
339 * count of the size of each equivalence class.
341 dsf = snewn(w*h, int);
342 dsfsize = snewn(w*h, int);
345 * Now repeatedly try to find something we can do.
347 while (1) {
348 int done_something = FALSE;
350 #ifdef SOLVER_DIAGNOSTICS
351 for (y = 0; y < H; y++) {
352 for (x = 0; x < W; x++)
353 printf("%*x", (x&1) ? 5 : 2, workspace[y*W+x]);
354 printf("\n");
356 #endif
359 * Go through the square state words, and discard any
360 * square state which is inconsistent with known facts
361 * about the edges around the square.
363 for (y = 0; y < h; y++)
364 for (x = 0; x < w; x++) {
365 for (b = 0; b < 0xD; b++)
366 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) {
368 * If any edge of this square is known to
369 * be connected when state b would require
370 * it disconnected, or vice versa, discard
371 * the state.
373 for (d = 1; d <= 8; d += d) {
374 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
375 if (workspace[ey*W+ex] ==
376 ((b & d) ? 2 : 1)) {
377 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<b);
378 #ifdef SOLVER_DIAGNOSTICS
379 printf("edge (%d,%d)-(%d,%d) rules out state"
380 " %d for square (%d,%d)\n",
381 ex/2, ey/2, (ex+1)/2, (ey+1)/2,
382 b, x, y);
383 #endif
384 done_something = TRUE;
385 break;
391 * Consistency check: each square must have at
392 * least one state left!
394 if (!workspace[(2*y+1)*W+(2*x+1)]) {
395 #ifdef SOLVER_DIAGNOSTICS
396 printf("edge check at (%d,%d): inconsistency\n", x, y);
397 #endif
398 ret = 0;
399 goto cleanup;
404 * Now go through the states array again, and nail down any
405 * unknown edge if one of its neighbouring squares makes it
406 * known.
408 for (y = 0; y < h; y++)
409 for (x = 0; x < w; x++) {
410 int edgeor = 0, edgeand = 15;
412 for (b = 0; b < 0xD; b++)
413 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) {
414 edgeor |= b;
415 edgeand &= b;
419 * Now any bit clear in edgeor marks a disconnected
420 * edge, and any bit set in edgeand marks a
421 * connected edge.
424 /* First check consistency: neither bit is both! */
425 if (edgeand & ~edgeor) {
426 #ifdef SOLVER_DIAGNOSTICS
427 printf("square check at (%d,%d): inconsistency\n", x, y);
428 #endif
429 ret = 0;
430 goto cleanup;
433 for (d = 1; d <= 8; d += d) {
434 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
436 if (!(edgeor & d) && workspace[ey*W+ex] == 3) {
437 workspace[ey*W+ex] = 2;
438 done_something = TRUE;
439 #ifdef SOLVER_DIAGNOSTICS
440 printf("possible states of square (%d,%d) force edge"
441 " (%d,%d)-(%d,%d) to be disconnected\n",
442 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2);
443 #endif
444 } else if ((edgeand & d) && workspace[ey*W+ex] == 3) {
445 workspace[ey*W+ex] = 1;
446 done_something = TRUE;
447 #ifdef SOLVER_DIAGNOSTICS
448 printf("possible states of square (%d,%d) force edge"
449 " (%d,%d)-(%d,%d) to be connected\n",
450 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2);
451 #endif
456 if (done_something)
457 continue;
460 * Now for longer-range clue-based deductions (using the
461 * rules that a corner clue must connect to two straight
462 * squares, and a straight clue must connect to at least
463 * one corner square).
465 for (y = 0; y < h; y++)
466 for (x = 0; x < w; x++)
467 switch (clues[y*w+x]) {
468 case CORNER:
469 for (d = 1; d <= 8; d += d) {
470 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
471 int fx = ex + DX(d), fy = ey + DY(d);
472 int type = d | F(d);
474 if (workspace[ey*W+ex] == 1) {
476 * If a corner clue is connected on any
477 * edge, then we can immediately nail
478 * down the square beyond that edge as
479 * being a straight in the appropriate
480 * direction.
482 if (workspace[fy*W+fx] != (1<<type)) {
483 workspace[fy*W+fx] = (1<<type);
484 done_something = TRUE;
485 #ifdef SOLVER_DIAGNOSTICS
486 printf("corner clue at (%d,%d) forces square "
487 "(%d,%d) into state %d\n", x, y,
488 fx/2, fy/2, type);
489 #endif
492 } else if (workspace[ey*W+ex] == 3) {
494 * Conversely, if a corner clue is
495 * separated by an unknown edge from a
496 * square which _cannot_ be a straight
497 * in the appropriate direction, we can
498 * mark that edge as disconnected.
500 if (!(workspace[fy*W+fx] & (1<<type))) {
501 workspace[ey*W+ex] = 2;
502 done_something = TRUE;
503 #ifdef SOLVER_DIAGNOSTICS
504 printf("corner clue at (%d,%d), plus square "
505 "(%d,%d) not being state %d, "
506 "disconnects edge (%d,%d)-(%d,%d)\n",
507 x, y, fx/2, fy/2, type,
508 ex/2, ey/2, (ex+1)/2, (ey+1)/2);
509 #endif
515 break;
516 case STRAIGHT:
518 * If a straight clue is between two squares
519 * neither of which is capable of being a
520 * corner connected to it, then the straight
521 * clue cannot point in that direction.
523 for (d = 1; d <= 2; d += d) {
524 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d);
525 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d);
526 int type = d | F(d);
528 if (!(workspace[(2*y+1)*W+(2*x+1)] & (1<<type)))
529 continue;
531 if (!(workspace[fy*W+fx] & ((1<<(F(d)|A(d))) |
532 (1<<(F(d)|C(d))))) &&
533 !(workspace[gy*W+gx] & ((1<<( d |A(d))) |
534 (1<<( d |C(d)))))) {
535 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<type);
536 done_something = TRUE;
537 #ifdef SOLVER_DIAGNOSTICS
538 printf("straight clue at (%d,%d) cannot corner at "
539 "(%d,%d) or (%d,%d) so is not state %d\n",
540 x, y, fx/2, fy/2, gx/2, gy/2, type);
541 #endif
547 * If a straight clue with known direction is
548 * connected on one side to a known straight,
549 * then on the other side it must be a corner.
551 for (d = 1; d <= 8; d += d) {
552 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d);
553 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d);
554 int type = d | F(d);
556 if (workspace[(2*y+1)*W+(2*x+1)] != (1<<type))
557 continue;
559 if (!(workspace[fy*W+fx] &~ (bLR|bUD)) &&
560 (workspace[gy*W+gx] &~ (bLU|bLD|bRU|bRD))) {
561 workspace[gy*W+gx] &= (bLU|bLD|bRU|bRD);
562 done_something = TRUE;
563 #ifdef SOLVER_DIAGNOSTICS
564 printf("straight clue at (%d,%d) connecting to "
565 "straight at (%d,%d) makes (%d,%d) a "
566 "corner\n", x, y, fx/2, fy/2, gx/2, gy/2);
567 #endif
571 break;
574 if (done_something)
575 continue;
578 * Now detect shortcut loops.
582 int nonblanks, loopclass;
584 dsf_init(dsf, w*h);
585 for (x = 0; x < w*h; x++)
586 dsfsize[x] = 1;
589 * First go through the edge entries and update the dsf
590 * of which squares are connected to which others. We
591 * also track the number of squares in each equivalence
592 * class, and count the overall number of
593 * known-non-blank squares.
595 * In the process of doing this, we must notice if a
596 * loop has already been formed. If it has, we blank
597 * out any square which isn't part of that loop
598 * (failing a consistency check if any such square does
599 * not have BLANK as one of its remaining options) and
600 * exit the deduction loop with success.
602 nonblanks = 0;
603 loopclass = -1;
604 for (y = 1; y < H-1; y++)
605 for (x = 1; x < W-1; x++)
606 if ((y ^ x) & 1) {
608 * (x,y) are the workspace coordinates of
609 * an edge field. Compute the normal-space
610 * coordinates of the squares it connects.
612 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax;
613 int bx = x/2, by = y/2, bc = by*w+bx;
616 * If the edge is connected, do the dsf
617 * thing.
619 if (workspace[y*W+x] == 1) {
620 int ae, be;
622 ae = dsf_canonify(dsf, ac);
623 be = dsf_canonify(dsf, bc);
625 if (ae == be) {
627 * We have a loop!
629 if (loopclass != -1) {
631 * In fact, we have two
632 * separate loops, which is
633 * doom.
635 #ifdef SOLVER_DIAGNOSTICS
636 printf("two loops found in grid!\n");
637 #endif
638 ret = 0;
639 goto cleanup;
641 loopclass = ae;
642 } else {
644 * Merge the two equivalence
645 * classes.
647 int size = dsfsize[ae] + dsfsize[be];
648 dsf_merge(dsf, ac, bc);
649 ae = dsf_canonify(dsf, ac);
650 dsfsize[ae] = size;
653 } else if ((y & x) & 1) {
655 * (x,y) are the workspace coordinates of a
656 * square field. If the square is
657 * definitely not blank, count it.
659 if (!(workspace[y*W+x] & bBLANK))
660 nonblanks++;
664 * If we discovered an existing loop above, we must now
665 * blank every square not part of it, and exit the main
666 * deduction loop.
668 if (loopclass != -1) {
669 #ifdef SOLVER_DIAGNOSTICS
670 printf("loop found in grid!\n");
671 #endif
672 for (y = 0; y < h; y++)
673 for (x = 0; x < w; x++)
674 if (dsf_canonify(dsf, y*w+x) != loopclass) {
675 if (workspace[(y*2+1)*W+(x*2+1)] & bBLANK) {
676 workspace[(y*2+1)*W+(x*2+1)] = bBLANK;
677 } else {
679 * This square is not part of the
680 * loop, but is known non-blank. We
681 * have goofed.
683 #ifdef SOLVER_DIAGNOSTICS
684 printf("non-blank square (%d,%d) found outside"
685 " loop!\n", x, y);
686 #endif
687 ret = 0;
688 goto cleanup;
692 * And we're done.
694 ret = 1;
695 break;
698 /* Further deductions are considered 'tricky'. */
699 if (difficulty == DIFF_EASY) goto done_deductions;
702 * Now go through the workspace again and mark any edge
703 * which would cause a shortcut loop (i.e. would
704 * connect together two squares in the same equivalence
705 * class, and that equivalence class does not contain
706 * _all_ the known-non-blank squares currently in the
707 * grid) as disconnected. Also, mark any _square state_
708 * which would cause a shortcut loop as disconnected.
710 for (y = 1; y < H-1; y++)
711 for (x = 1; x < W-1; x++)
712 if ((y ^ x) & 1) {
714 * (x,y) are the workspace coordinates of
715 * an edge field. Compute the normal-space
716 * coordinates of the squares it connects.
718 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax;
719 int bx = x/2, by = y/2, bc = by*w+bx;
722 * If the edge is currently unknown, and
723 * sits between two squares in the same
724 * equivalence class, and the size of that
725 * class is less than nonblanks, then
726 * connecting this edge would be a shortcut
727 * loop and so we must not do so.
729 if (workspace[y*W+x] == 3) {
730 int ae, be;
732 ae = dsf_canonify(dsf, ac);
733 be = dsf_canonify(dsf, bc);
735 if (ae == be) {
737 * We have a loop. Is it a shortcut?
739 if (dsfsize[ae] < nonblanks) {
741 * Yes! Mark this edge disconnected.
743 workspace[y*W+x] = 2;
744 done_something = TRUE;
745 #ifdef SOLVER_DIAGNOSTICS
746 printf("edge (%d,%d)-(%d,%d) would create"
747 " a shortcut loop, hence must be"
748 " disconnected\n", x/2, y/2,
749 (x+1)/2, (y+1)/2);
750 #endif
754 } else if ((y & x) & 1) {
756 * (x,y) are the workspace coordinates of a
757 * square field. Go through its possible
758 * (non-blank) states and see if any gives
759 * rise to a shortcut loop.
761 * This is slightly fiddly, because we have
762 * to check whether this square is already
763 * part of the same equivalence class as
764 * the things it's joining.
766 int ae = dsf_canonify(dsf, (y/2)*w+(x/2));
768 for (b = 2; b < 0xD; b++)
769 if (workspace[y*W+x] & (1<<b)) {
771 * Find the equivalence classes of
772 * the two squares this one would
773 * connect if it were in this
774 * state.
776 int e = -1;
778 for (d = 1; d <= 8; d += d) if (b & d) {
779 int xx = x/2 + DX(d), yy = y/2 + DY(d);
780 int ee = dsf_canonify(dsf, yy*w+xx);
782 if (e == -1)
783 ee = e;
784 else if (e != ee)
785 e = -2;
788 if (e >= 0) {
790 * This square state would form
791 * a loop on equivalence class
792 * e. Measure the size of that
793 * loop, and see if it's a
794 * shortcut.
796 int loopsize = dsfsize[e];
797 if (e != ae)
798 loopsize++;/* add the square itself */
799 if (loopsize < nonblanks) {
801 * It is! Mark this square
802 * state invalid.
804 workspace[y*W+x] &= ~(1<<b);
805 done_something = TRUE;
806 #ifdef SOLVER_DIAGNOSTICS
807 printf("square (%d,%d) would create a "
808 "shortcut loop in state %d, "
809 "hence cannot be\n",
810 x/2, y/2, b);
811 #endif
818 done_deductions:
820 if (done_something)
821 continue;
824 * If we reach here, there is nothing left we can do.
825 * Return 2 for ambiguous puzzle.
827 ret = 2;
828 break;
831 cleanup:
834 * If ret = 1 then we've successfully achieved a solution. This
835 * means that we expect every square to be nailed down to
836 * exactly one possibility. If this is the case, or if the caller
837 * asked for a partial solution anyway, transcribe those
838 * possibilities into the result array.
840 if (ret == 1 || partial) {
841 for (y = 0; y < h; y++) {
842 for (x = 0; x < w; x++) {
843 for (b = 0; b < 0xD; b++)
844 if (workspace[(2*y+1)*W+(2*x+1)] == (1<<b)) {
845 result[y*w+x] = b;
846 break;
848 if (ret == 1) assert(b < 0xD); /* we should have had a break by now */
853 sfree(dsfsize);
854 sfree(dsf);
855 sfree(workspace);
856 assert(ret >= 0);
857 return ret;
860 /* ----------------------------------------------------------------------
861 * Loop generator.
865 * We use the loop generator code from loopy, hard-coding to a square
866 * grid of the appropriate size. Knowing the grid layout and the tile
867 * size we can shrink that to our small grid and then make our line
868 * layout from the face colour info.
870 * We provide a bias function to the loop generator which tries to
871 * bias in favour of loops with more scope for Pearl black clues. This
872 * seems to improve the success rate of the puzzle generator, in that
873 * such loops have a better chance of being soluble with all valid
874 * clues put in.
877 struct pearl_loopgen_bias_ctx {
879 * Our bias function counts the number of 'black clue' corners
880 * (i.e. corners adjacent to two straights) in both the
881 * BLACK/nonBLACK and WHITE/nonWHITE boundaries. In order to do
882 * this, we must:
884 * - track the edges that are part of each of those loops
885 * - track the types of vertex in each loop (corner, straight,
886 * none)
887 * - track the current black-clue status of each vertex in each
888 * loop.
890 * Each of these chunks of data is updated incrementally from the
891 * previous one, to avoid slowdown due to the bias function
892 * rescanning the whole grid every time it's called.
894 * So we need a lot of separate arrays, plus a tdq for each one,
895 * and we must repeat it all twice for the BLACK and WHITE
896 * boundaries.
898 struct pearl_loopgen_bias_ctx_boundary {
899 int colour; /* FACE_WHITE or FACE_BLACK */
901 char *edges; /* is each edge part of the loop? */
902 tdq *edges_todo;
904 char *vertextypes; /* bits 0-3 == outgoing edge bitmap;
905 * bit 4 set iff corner clue.
906 * Hence, 0 means non-vertex;
907 * nonzero but bit 4 zero = straight. */
908 int *neighbour[2]; /* indices of neighbour vertices in loop */
909 tdq *vertextypes_todo;
911 char *blackclues; /* is each vertex a black clue site? */
912 tdq *blackclues_todo;
913 } boundaries[2]; /* boundaries[0]=WHITE, [1]=BLACK */
915 char *faces; /* remember last-seen colour of each face */
916 tdq *faces_todo;
918 int score;
920 grid *g;
922 int pearl_loopgen_bias(void *vctx, char *board, int face)
924 struct pearl_loopgen_bias_ctx *ctx = (struct pearl_loopgen_bias_ctx *)vctx;
925 grid *g = ctx->g;
926 int oldface, newface;
927 int i, j, k;
929 tdq_add(ctx->faces_todo, face);
930 while ((j = tdq_remove(ctx->faces_todo)) >= 0) {
931 oldface = ctx->faces[j];
932 ctx->faces[j] = newface = board[j];
933 for (i = 0; i < 2; i++) {
934 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i];
935 int c = b->colour;
938 * If the face has changed either from or to colour c, we need
939 * to reprocess the edges for this boundary.
941 if (oldface == c || newface == c) {
942 grid_face *f = &g->faces[face];
943 for (k = 0; k < f->order; k++)
944 tdq_add(b->edges_todo, f->edges[k] - g->edges);
949 for (i = 0; i < 2; i++) {
950 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i];
951 int c = b->colour;
954 * Go through the to-do list of edges. For each edge, decide
955 * anew whether it's part of this boundary or not. Any edge
956 * that changes state has to have both its endpoints put on
957 * the vertextypes_todo list.
959 while ((j = tdq_remove(b->edges_todo)) >= 0) {
960 grid_edge *e = &g->edges[j];
961 int fc1 = e->face1 ? board[e->face1 - g->faces] : FACE_BLACK;
962 int fc2 = e->face2 ? board[e->face2 - g->faces] : FACE_BLACK;
963 int oldedge = b->edges[j];
964 int newedge = (fc1==c) ^ (fc2==c);
965 if (oldedge != newedge) {
966 b->edges[j] = newedge;
967 tdq_add(b->vertextypes_todo, e->dot1 - g->dots);
968 tdq_add(b->vertextypes_todo, e->dot2 - g->dots);
973 * Go through the to-do list of vertices whose types need
974 * refreshing. For each one, decide whether it's a corner, a
975 * straight, or a vertex not in the loop, and in the former
976 * two cases also work out the indices of its neighbour
977 * vertices along the loop. Any vertex that changes state must
978 * be put back on the to-do list for deciding if it's a black
979 * clue site, and so must its two new neighbours _and_ its two
980 * old neighbours.
982 while ((j = tdq_remove(b->vertextypes_todo)) >= 0) {
983 grid_dot *d = &g->dots[j];
984 int neighbours[2], type = 0, n = 0;
986 for (k = 0; k < d->order; k++) {
987 grid_edge *e = d->edges[k];
988 grid_dot *d2 = (e->dot1 == d ? e->dot2 : e->dot1);
989 /* dir == 0,1,2,3 for an edge going L,U,R,D */
990 int dir = (d->y == d2->y) + 2*(d->x+d->y > d2->x+d2->y);
991 int ei = e - g->edges;
992 if (b->edges[ei]) {
993 type |= 1 << dir;
994 neighbours[n] = d2 - g->dots;
995 n++;
1000 * Decide if it's a corner, and set the corner flag if so.
1002 if (type != 0 && type != 0x5 && type != 0xA)
1003 type |= 0x10;
1005 if (type != b->vertextypes[j]) {
1007 * Recompute old neighbours, if any.
1009 if (b->vertextypes[j]) {
1010 tdq_add(b->blackclues_todo, b->neighbour[0][j]);
1011 tdq_add(b->blackclues_todo, b->neighbour[1][j]);
1014 * Recompute this vertex.
1016 tdq_add(b->blackclues_todo, j);
1017 b->vertextypes[j] = type;
1019 * Recompute new neighbours, if any.
1021 if (b->vertextypes[j]) {
1022 b->neighbour[0][j] = neighbours[0];
1023 b->neighbour[1][j] = neighbours[1];
1024 tdq_add(b->blackclues_todo, b->neighbour[0][j]);
1025 tdq_add(b->blackclues_todo, b->neighbour[1][j]);
1031 * Go through the list of vertices which we must check to see
1032 * if they're black clue sites. Each one is a black clue site
1033 * iff it is a corner and its loop neighbours are non-corners.
1034 * Adjust the running total of black clues we've counted.
1036 while ((j = tdq_remove(b->blackclues_todo)) >= 0) {
1037 ctx->score -= b->blackclues[j];
1038 b->blackclues[j] = ((b->vertextypes[j] & 0x10) &&
1039 !((b->vertextypes[b->neighbour[0][j]] |
1040 b->vertextypes[b->neighbour[1][j]])
1041 & 0x10));
1042 ctx->score += b->blackclues[j];
1046 return ctx->score;
1049 void pearl_loopgen(int w, int h, char *lines, random_state *rs)
1051 grid *g = grid_new(GRID_SQUARE, w-1, h-1, NULL);
1052 char *board = snewn(g->num_faces, char);
1053 int i, s = g->tilesize;
1054 struct pearl_loopgen_bias_ctx biasctx;
1056 memset(lines, 0, w*h);
1059 * Initialise the context for the bias function. Initially we fill
1060 * all the to-do lists, so that the first call will scan
1061 * everything; thereafter the lists stay empty so we make
1062 * incremental changes.
1064 biasctx.g = g;
1065 biasctx.faces = snewn(g->num_faces, char);
1066 biasctx.faces_todo = tdq_new(g->num_faces);
1067 tdq_fill(biasctx.faces_todo);
1068 biasctx.score = 0;
1069 memset(biasctx.faces, FACE_GREY, g->num_faces);
1070 for (i = 0; i < 2; i++) {
1071 biasctx.boundaries[i].edges = snewn(g->num_edges, char);
1072 memset(biasctx.boundaries[i].edges, 0, g->num_edges);
1073 biasctx.boundaries[i].edges_todo = tdq_new(g->num_edges);
1074 tdq_fill(biasctx.boundaries[i].edges_todo);
1075 biasctx.boundaries[i].vertextypes = snewn(g->num_dots, char);
1076 memset(biasctx.boundaries[i].vertextypes, 0, g->num_dots);
1077 biasctx.boundaries[i].neighbour[0] = snewn(g->num_dots, int);
1078 biasctx.boundaries[i].neighbour[1] = snewn(g->num_dots, int);
1079 biasctx.boundaries[i].vertextypes_todo = tdq_new(g->num_dots);
1080 tdq_fill(biasctx.boundaries[i].vertextypes_todo);
1081 biasctx.boundaries[i].blackclues = snewn(g->num_dots, char);
1082 memset(biasctx.boundaries[i].blackclues, 0, g->num_dots);
1083 biasctx.boundaries[i].blackclues_todo = tdq_new(g->num_dots);
1084 tdq_fill(biasctx.boundaries[i].blackclues_todo);
1086 biasctx.boundaries[0].colour = FACE_WHITE;
1087 biasctx.boundaries[1].colour = FACE_BLACK;
1088 generate_loop(g, board, rs, pearl_loopgen_bias, &biasctx);
1089 sfree(biasctx.faces);
1090 tdq_free(biasctx.faces_todo);
1091 for (i = 0; i < 2; i++) {
1092 sfree(biasctx.boundaries[i].edges);
1093 tdq_free(biasctx.boundaries[i].edges_todo);
1094 sfree(biasctx.boundaries[i].vertextypes);
1095 sfree(biasctx.boundaries[i].neighbour[0]);
1096 sfree(biasctx.boundaries[i].neighbour[1]);
1097 tdq_free(biasctx.boundaries[i].vertextypes_todo);
1098 sfree(biasctx.boundaries[i].blackclues);
1099 tdq_free(biasctx.boundaries[i].blackclues_todo);
1102 for (i = 0; i < g->num_edges; i++) {
1103 grid_edge *e = g->edges + i;
1104 enum face_colour c1 = FACE_COLOUR(e->face1);
1105 enum face_colour c2 = FACE_COLOUR(e->face2);
1106 assert(c1 != FACE_GREY);
1107 assert(c2 != FACE_GREY);
1108 if (c1 != c2) {
1109 /* This grid edge is on the loop: lay line along it */
1110 int x1 = e->dot1->x/s, y1 = e->dot1->y/s;
1111 int x2 = e->dot2->x/s, y2 = e->dot2->y/s;
1113 /* (x1,y1) and (x2,y2) are now in our grid coords (0-w,0-h). */
1114 if (x1 == x2) {
1115 if (y1 > y2) SWAP(y1,y2);
1117 assert(y1+1 == y2);
1118 lines[y1*w+x1] |= D;
1119 lines[y2*w+x1] |= U;
1120 } else if (y1 == y2) {
1121 if (x1 > x2) SWAP(x1,x2);
1123 assert(x1+1 == x2);
1124 lines[y1*w+x1] |= R;
1125 lines[y1*w+x2] |= L;
1126 } else
1127 assert(!"grid with diagonal coords?!");
1131 grid_free(g);
1132 sfree(board);
1134 #if defined LOOPGEN_DIAGNOSTICS && !defined GENERATION_DIAGNOSTICS
1135 printf("as returned:\n");
1136 for (y = 0; y < h; y++) {
1137 for (x = 0; x < w; x++) {
1138 int type = lines[y*w+x];
1139 char s[5], *p = s;
1140 if (type & L) *p++ = 'L';
1141 if (type & R) *p++ = 'R';
1142 if (type & U) *p++ = 'U';
1143 if (type & D) *p++ = 'D';
1144 *p = '\0';
1145 printf("%3s", s);
1147 printf("\n");
1149 printf("\n");
1150 #endif
1153 static int new_clues(game_params *params, random_state *rs,
1154 char *clues, char *grid)
1156 int w = params->w, h = params->h;
1157 int ngen = 0, x, y, d, ret, i;
1159 while (1) {
1160 ngen++;
1161 pearl_loopgen(w, h, grid, rs);
1163 #ifdef GENERATION_DIAGNOSTICS
1164 printf("grid array:\n");
1165 for (y = 0; y < h; y++) {
1166 for (x = 0; x < w; x++) {
1167 int type = grid[y*w+x];
1168 char s[5], *p = s;
1169 if (type & L) *p++ = 'L';
1170 if (type & R) *p++ = 'R';
1171 if (type & U) *p++ = 'U';
1172 if (type & D) *p++ = 'D';
1173 *p = '\0';
1174 printf("%2s ", s);
1176 printf("\n");
1178 printf("\n");
1179 #endif
1182 * Set up the maximal clue array.
1184 for (y = 0; y < h; y++)
1185 for (x = 0; x < w; x++) {
1186 int type = grid[y*w+x];
1188 clues[y*w+x] = NOCLUE;
1190 if ((bLR|bUD) & (1 << type)) {
1192 * This is a straight; see if it's a viable
1193 * candidate for a straight clue. It qualifies if
1194 * at least one of the squares it connects to is a
1195 * corner.
1197 for (d = 1; d <= 8; d += d) if (type & d) {
1198 int xx = x + DX(d), yy = y + DY(d);
1199 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1200 if ((bLU|bLD|bRU|bRD) & (1 << grid[yy*w+xx]))
1201 break;
1203 if (d <= 8) /* we found one */
1204 clues[y*w+x] = STRAIGHT;
1205 } else if ((bLU|bLD|bRU|bRD) & (1 << type)) {
1207 * This is a corner; see if it's a viable candidate
1208 * for a corner clue. It qualifies if all the
1209 * squares it connects to are straights.
1211 for (d = 1; d <= 8; d += d) if (type & d) {
1212 int xx = x + DX(d), yy = y + DY(d);
1213 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1214 if (!((bLR|bUD) & (1 << grid[yy*w+xx])))
1215 break;
1217 if (d > 8) /* we didn't find a counterexample */
1218 clues[y*w+x] = CORNER;
1222 #ifdef GENERATION_DIAGNOSTICS
1223 printf("clue array:\n");
1224 for (y = 0; y < h; y++) {
1225 for (x = 0; x < w; x++) {
1226 printf("%c", " *O"[(unsigned char)clues[y*w+x]]);
1228 printf("\n");
1230 printf("\n");
1231 #endif
1233 if (!params->nosolve) {
1234 int *cluespace, *straights, *corners;
1235 int nstraights, ncorners, nstraightpos, ncornerpos;
1238 * See if we can solve the puzzle just like this.
1240 ret = pearl_solve(w, h, clues, grid, params->difficulty, FALSE);
1241 assert(ret > 0); /* shouldn't be inconsistent! */
1242 if (ret != 1)
1243 continue; /* go round and try again */
1246 * Check this puzzle isn't too easy.
1248 if (params->difficulty > DIFF_EASY) {
1249 ret = pearl_solve(w, h, clues, grid, params->difficulty-1, FALSE);
1250 assert(ret > 0);
1251 if (ret == 1)
1252 continue; /* too easy: try again */
1256 * Now shuffle the grid points and gradually remove the
1257 * clues to find a minimal set which still leaves the
1258 * puzzle soluble.
1260 * We preferentially attempt to remove whichever type of
1261 * clue is currently most numerous, to combat a general
1262 * tendency of plain random generation to bias in favour
1263 * of many white clues and few black.
1265 * 'nstraights' and 'ncorners' count the number of clues
1266 * of each type currently remaining in the grid;
1267 * 'nstraightpos' and 'ncornerpos' count the clues of each
1268 * type we have left to try to remove. (Clues which we
1269 * have tried and failed to remove are counted by the
1270 * former but not the latter.)
1272 cluespace = snewn(w*h, int);
1273 straights = cluespace;
1274 nstraightpos = 0;
1275 for (i = 0; i < w*h; i++)
1276 if (clues[i] == STRAIGHT)
1277 straights[nstraightpos++] = i;
1278 corners = straights + nstraightpos;
1279 ncornerpos = 0;
1280 for (i = 0; i < w*h; i++)
1281 if (clues[i] == STRAIGHT)
1282 corners[ncornerpos++] = i;
1283 nstraights = nstraightpos;
1284 ncorners = ncornerpos;
1286 shuffle(straights, nstraightpos, sizeof(*straights), rs);
1287 shuffle(corners, ncornerpos, sizeof(*corners), rs);
1288 while (nstraightpos > 0 || ncornerpos > 0) {
1289 int cluepos;
1290 int clue;
1293 * Decide which clue to try to remove next. If both
1294 * types are available, we choose whichever kind is
1295 * currently overrepresented; otherwise we take
1296 * whatever we can get.
1298 if (nstraightpos > 0 && ncornerpos > 0) {
1299 if (nstraights >= ncorners)
1300 cluepos = straights[--nstraightpos];
1301 else
1302 cluepos = straights[--ncornerpos];
1303 } else {
1304 if (nstraightpos > 0)
1305 cluepos = straights[--nstraightpos];
1306 else
1307 cluepos = straights[--ncornerpos];
1310 y = cluepos / w;
1311 x = cluepos % w;
1313 clue = clues[y*w+x];
1314 clues[y*w+x] = 0; /* try removing this clue */
1316 ret = pearl_solve(w, h, clues, grid, params->difficulty, FALSE);
1317 assert(ret > 0);
1318 if (ret != 1)
1319 clues[y*w+x] = clue; /* oops, put it back again */
1321 sfree(cluespace);
1324 #ifdef FINISHED_PUZZLE
1325 printf("clue array:\n");
1326 for (y = 0; y < h; y++) {
1327 for (x = 0; x < w; x++) {
1328 printf("%c", " *O"[(unsigned char)clues[y*w+x]]);
1330 printf("\n");
1332 printf("\n");
1333 #endif
1335 break; /* got it */
1338 debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h));
1340 return ngen;
1343 static char *new_game_desc(game_params *params, random_state *rs,
1344 char **aux, int interactive)
1346 char *grid, *clues;
1347 char *desc;
1348 int w = params->w, h = params->h, i, j;
1350 grid = snewn(w*h, char);
1351 clues = snewn(w*h, char);
1353 new_clues(params, rs, clues, grid);
1355 desc = snewn(w * h + 1, char);
1356 for (i = j = 0; i < w*h; i++) {
1357 if (clues[i] == NOCLUE && j > 0 &&
1358 desc[j-1] >= 'a' && desc[j-1] < 'z')
1359 desc[j-1]++;
1360 else if (clues[i] == NOCLUE)
1361 desc[j++] = 'a';
1362 else if (clues[i] == CORNER)
1363 desc[j++] = 'B';
1364 else if (clues[i] == STRAIGHT)
1365 desc[j++] = 'W';
1367 desc[j] = '\0';
1369 *aux = snewn(w*h+1, char);
1370 for (i = 0; i < w*h; i++)
1371 (*aux)[i] = (grid[i] < 10) ? (grid[i] + '0') : (grid[i] + 'A' - 10);
1372 (*aux)[w*h] = '\0';
1374 sfree(grid);
1375 sfree(clues);
1377 return desc;
1380 static char *validate_desc(game_params *params, char *desc)
1382 int i, sizesofar;
1383 const int totalsize = params->w * params->h;
1385 sizesofar = 0;
1386 for (i = 0; desc[i]; i++) {
1387 if (desc[i] >= 'a' && desc[i] <= 'z')
1388 sizesofar += desc[i] - 'a' + 1;
1389 else if (desc[i] == 'B' || desc[i] == 'W')
1390 sizesofar++;
1391 else
1392 return "unrecognised character in string";
1395 if (sizesofar > totalsize)
1396 return "string too long";
1397 else if (sizesofar < totalsize)
1398 return "string too short";
1400 return NULL;
1403 static game_state *new_game(midend *me, game_params *params, char *desc)
1405 game_state *state = snew(game_state);
1406 int i, j, sz = params->w*params->h;
1408 state->completed = state->used_solve = FALSE;
1409 state->shared = snew(struct shared_state);
1411 state->shared->w = params->w;
1412 state->shared->h = params->h;
1413 state->shared->sz = sz;
1414 state->shared->refcnt = 1;
1415 state->shared->clues = snewn(sz, char);
1416 for (i = j = 0; desc[i]; i++) {
1417 assert(j < sz);
1418 if (desc[i] >= 'a' && desc[i] <= 'z') {
1419 int n = desc[i] - 'a' + 1;
1420 assert(j + n <= sz);
1421 while (n-- > 0)
1422 state->shared->clues[j++] = NOCLUE;
1423 } else if (desc[i] == 'B') {
1424 state->shared->clues[j++] = CORNER;
1425 } else if (desc[i] == 'W') {
1426 state->shared->clues[j++] = STRAIGHT;
1430 state->lines = snewn(sz, char);
1431 state->errors = snewn(sz, char);
1432 state->marks = snewn(sz, char);
1433 for (i = 0; i < sz; i++)
1434 state->lines[i] = state->errors[i] = state->marks[i] = BLANK;
1436 return state;
1439 static game_state *dup_game(game_state *state)
1441 game_state *ret = snew(game_state);
1442 int sz = state->shared->sz, i;
1444 ret->shared = state->shared;
1445 ret->completed = state->completed;
1446 ret->used_solve = state->used_solve;
1447 ++ret->shared->refcnt;
1449 ret->lines = snewn(sz, char);
1450 ret->errors = snewn(sz, char);
1451 ret->marks = snewn(sz, char);
1452 for (i = 0; i < sz; i++) {
1453 ret->lines[i] = state->lines[i];
1454 ret->errors[i] = state->errors[i];
1455 ret->marks[i] = state->marks[i];
1458 return ret;
1461 static void free_game(game_state *state)
1463 assert(state);
1464 if (--state->shared->refcnt == 0) {
1465 sfree(state->shared->clues);
1466 sfree(state->shared);
1468 sfree(state->lines);
1469 sfree(state->errors);
1470 sfree(state->marks);
1471 sfree(state);
1474 static char nbits[16] = { 0, 1, 1, 2,
1475 1, 2, 2, 3,
1476 1, 2, 2, 3,
1477 2, 3, 3, 4 };
1478 #define NBITS(l) ( ((l) < 0 || (l) > 15) ? 4 : nbits[l] )
1480 #define ERROR_CLUE 16
1482 static void dsf_update_completion(game_state *state, int *loopclass,
1483 int ax, int ay, char dir,
1484 int *dsf, int *dsfsize)
1486 int w = state->shared->w /*, h = state->shared->h */;
1487 int ac = ay*w+ax, ae, bx, by, bc, be;
1489 if (!(state->lines[ac] & dir)) return; /* no link */
1490 bx = ax + DX(dir); by = ay + DY(dir);
1492 assert(INGRID(state, bx, by)); /* should not have a link off grid */
1494 bc = by*w+bx;
1495 #if 0
1496 assert(state->lines[bc] & F(dir)); /* should have reciprocal link */
1497 #endif
1498 /* TODO put above assertion back in once we stop generating partially
1499 * soluble puzzles. */
1500 if (!(state->lines[bc] & F(dir))) return;
1502 ae = dsf_canonify(dsf, ac);
1503 be = dsf_canonify(dsf, bc);
1505 if (ae == be) { /* detected a loop! */
1506 if (*loopclass != -1) /* this is the second loop, doom. */
1507 return;
1508 *loopclass = ae;
1509 } else {
1510 int size = dsfsize[ae] + dsfsize[be];
1511 dsf_merge(dsf, ac, bc);
1512 ae = dsf_canonify(dsf, ac);
1513 dsfsize[ae] = size;
1515 return;
1518 static void check_completion(game_state *state, int mark)
1520 int w = state->shared->w, h = state->shared->h, x, y, i, d;
1521 int had_error = FALSE /*, is_complete = FALSE */, loopclass;
1522 int *dsf, *dsfsize;
1524 if (mark) {
1525 for (i = 0; i < w*h; i++) {
1526 state->errors[i] = 0;
1530 #define ERROR(x,y,e) do { had_error = TRUE; if (mark) state->errors[(y)*w+(x)] |= (e); } while(0)
1533 * First of all: we should have one single closed loop, passing through all clues.
1535 dsf = snewn(w*h, int);
1536 dsfsize = snewn(w*h, int);
1537 dsf_init(dsf, w*h);
1538 for (i = 0; i < w*h; i++) dsfsize[i] = 1;
1539 loopclass = -1;
1541 for (x = 0; x < w; x++) {
1542 for (y = 0; y < h; y++) {
1543 dsf_update_completion(state, &loopclass, x, y, R, dsf, dsfsize);
1544 dsf_update_completion(state, &loopclass, x, y, D, dsf, dsfsize);
1547 if (loopclass != -1) {
1548 /* We have a loop. Check all squares with lines on. */
1549 for (x = 0; x < w; x++) {
1550 for (y = 0; y < h; y++) {
1551 if (state->lines[y*w+x] == BLANK) {
1552 if (state->shared->clues[y*w+x] != NOCLUE) {
1553 /* the loop doesn't include this clue square! */
1554 ERROR(x, y, ERROR_CLUE);
1556 } else {
1557 if (dsf_canonify(dsf, y*w+x) != loopclass) {
1558 /* these lines are not on the loop: mark them as error. */
1559 ERROR(x, y, state->lines[y*w+x]);
1567 * Second: check no clues are contradicted.
1570 for (x = 0; x < w; x++) {
1571 for (y = 0; y < h; y++) {
1572 int type = state->lines[y*w+x];
1574 * Check that no square has more than two line segments.
1576 if (NBITS(type) > 2) {
1577 ERROR(x,y,type);
1580 * Check that no clues are contradicted. This code is similar to
1581 * the code that sets up the maximal clue array for any given
1582 * loop.
1584 if (state->shared->clues[y*w+x] == CORNER) {
1585 /* Supposed to be a corner: will find a contradiction if
1586 * it actually contains a straight line, or if it touches any
1587 * corners. */
1588 if ((bLR|bUD) & (1 << type)) {
1589 ERROR(x,y,ERROR_CLUE); /* actually straight */
1591 for (d = 1; d <= 8; d += d) if (type & d) {
1592 int xx = x + DX(d), yy = y + DY(d);
1593 if (!INGRID(state, xx, yy)) {
1594 ERROR(x,y,d); /* leads off grid */
1595 } else {
1596 if ((bLU|bLD|bRU|bRD) & (1 << state->lines[yy*w+xx])) {
1597 ERROR(x,y,ERROR_CLUE); /* touches corner */
1601 } else if (state->shared->clues[y*w+x] == STRAIGHT) {
1602 /* Supposed to be straight: will find a contradiction if
1603 * it actually contains a corner, or if it only touches
1604 * straight lines. */
1605 if ((bLU|bLD|bRU|bRD) & (1 << type)) {
1606 ERROR(x,y,ERROR_CLUE); /* actually a corner */
1608 i = 0;
1609 for (d = 1; d <= 8; d += d) if (type & d) {
1610 int xx = x + DX(d), yy = y + DY(d);
1611 if (!INGRID(state, xx, yy)) {
1612 ERROR(x,y,d); /* leads off grid */
1613 } else {
1614 if ((bLR|bUD) & (1 << state->lines[yy*w+xx]))
1615 i++; /* a straight */
1618 if (i >= 2 && NBITS(type) >= 2) {
1619 ERROR(x,y,ERROR_CLUE); /* everything touched is straight */
1624 if (!had_error && loopclass != -1) {
1625 state->completed = TRUE;
1626 state->loop_length = dsfsize[loopclass];
1627 } else {
1628 state->completed = FALSE;
1631 sfree(dsf);
1632 sfree(dsfsize);
1634 return;
1637 /* completion check:
1639 * - no clues must be contradicted (highlight clue itself in error if so)
1640 * - if there is a closed loop it must include every line segment laid
1641 * - if there's a smaller closed loop then highlight whole loop as error
1642 * - no square must have more than 3 lines radiating from centre point
1643 * (highlight all lines in that square as error if so)
1646 static char *solve_for_diff(game_state *state, char *old_lines, char *new_lines)
1648 int w = state->shared->w, h = state->shared->h, i;
1649 char *move = snewn(w*h*40, char), *p = move;
1651 *p++ = 'S';
1652 for (i = 0; i < w*h; i++) {
1653 if (old_lines[i] != new_lines[i]) {
1654 p += sprintf(p, ";R%d,%d,%d", new_lines[i], i%w, i/w);
1657 *p++ = '\0';
1658 move = sresize(move, p - move, char);
1660 return move;
1663 static char *solve_game(game_state *state, game_state *currstate,
1664 char *aux, char **error)
1666 game_state *solved = dup_game(state);
1667 int i, ret, sz = state->shared->sz;
1668 char *move;
1670 if (aux) {
1671 for (i = 0; i < sz; i++) {
1672 if (aux[i] >= '0' && aux[i] <= '9')
1673 solved->lines[i] = aux[i] - '0';
1674 else if (aux[i] >= 'A' && aux[i] <= 'F')
1675 solved->lines[i] = aux[i] - 'A' + 10;
1676 else {
1677 *error = "invalid char in aux";
1678 move = NULL;
1679 goto done;
1682 ret = 1;
1683 } else {
1684 /* Try to solve with present (half-solved) state first: if there's no
1685 * solution from there go back to original state. */
1686 ret = pearl_solve(currstate->shared->w, currstate->shared->h,
1687 currstate->shared->clues, solved->lines,
1688 DIFFCOUNT, FALSE);
1689 if (ret < 1)
1690 ret = pearl_solve(state->shared->w, state->shared->h,
1691 state->shared->clues, solved->lines,
1692 DIFFCOUNT, FALSE);
1696 if (ret < 1) {
1697 *error = "Unable to find solution";
1698 move = NULL;
1699 } else {
1700 move = solve_for_diff(solved, currstate->lines, solved->lines);
1703 done:
1704 free_game(solved);
1705 return move;
1708 static int game_can_format_as_text_now(game_params *params)
1710 return FALSE;
1713 static char *game_text_format(game_state *state)
1715 return NULL;
1718 struct game_ui {
1719 int *dragcoords; /* list of (y*w+x) coords in drag so far */
1720 int ndragcoords; /* number of entries in dragcoords.
1721 * 0 = click but no drag yet. -1 = no drag at all */
1722 int clickx, clicky; /* pixel position of initial click */
1725 static game_ui *new_ui(game_state *state)
1727 game_ui *ui = snew(game_ui);
1728 int sz = state->shared->sz;
1730 ui->ndragcoords = -1;
1731 ui->dragcoords = snewn(sz, int);
1733 return ui;
1736 static void free_ui(game_ui *ui)
1738 sfree(ui->dragcoords);
1739 sfree(ui);
1742 static char *encode_ui(game_ui *ui)
1744 return NULL;
1747 static void decode_ui(game_ui *ui, char *encoding)
1751 static void game_changed_state(game_ui *ui, game_state *oldstate,
1752 game_state *newstate)
1756 #define PREFERRED_TILE_SIZE 31
1757 #define HALFSZ (ds->halfsz)
1758 #define TILE_SIZE (ds->halfsz*2 + 1)
1760 #define BORDER ((get_gui_style() == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2))
1762 #define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
1764 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
1765 #define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) )
1767 #define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */
1768 #define DS_DSHIFT 8 /* R/U/L/D shift, for drag-in-progress flags */
1769 #define DS_MSHIFT 12 /* shift for no-line mark */
1771 #define DS_ERROR_CLUE (1 << 20)
1772 #define DS_FLASH (1 << 21)
1774 enum { GUI_MASYU, GUI_LOOPY };
1776 static int get_gui_style(void)
1778 static int gui_style = -1;
1780 if (gui_style == -1) {
1781 char *env = getenv("PEARL_GUI_LOOPY");
1782 if (env && (env[0] == 'y' || env[0] == 'Y'))
1783 gui_style = GUI_LOOPY;
1784 else
1785 gui_style = GUI_MASYU;
1787 return gui_style;
1790 struct game_drawstate {
1791 int halfsz;
1792 int started;
1794 int w, h, sz;
1795 unsigned int *lflags; /* size w*h */
1797 char *draglines; /* size w*h; lines flipped by current drag */
1800 static void update_ui_drag(game_state *state, game_ui *ui, int gx, int gy)
1802 int /* sz = state->shared->sz, */ w = state->shared->w;
1803 int i, ox, oy, pos;
1804 int lastpos;
1806 if (!INGRID(state, gx, gy))
1807 return; /* square is outside grid */
1809 if (ui->ndragcoords < 0)
1810 return; /* drag not in progress anyway */
1812 pos = gy * w + gx;
1814 lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0];
1815 if (pos == lastpos)
1816 return; /* same square as last visited one */
1818 /* Drag confirmed, if it wasn't already. */
1819 if (ui->ndragcoords == 0)
1820 ui->ndragcoords = 1;
1823 * Dragging the mouse into a square that's already been visited by
1824 * the drag path so far has the effect of truncating the path back
1825 * to that square, so a player can back out part of an uncommitted
1826 * drag without having to let go of the mouse.
1828 for (i = 0; i < ui->ndragcoords; i++)
1829 if (pos == ui->dragcoords[i]) {
1830 ui->ndragcoords = i+1;
1831 return;
1835 * Otherwise, dragging the mouse into a square that's a rook-move
1836 * away from the last one on the path extends the path.
1838 oy = ui->dragcoords[ui->ndragcoords-1] / w;
1839 ox = ui->dragcoords[ui->ndragcoords-1] % w;
1840 if (ox == gx || oy == gy) {
1841 int dx = (gx < ox ? -1 : gx > ox ? +1 : 0);
1842 int dy = (gy < oy ? -1 : gy > oy ? +1 : 0);
1843 int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L);
1844 while (ox != gx || oy != gy) {
1846 * If the drag attempts to cross a 'no line here' mark,
1847 * stop there. We physically don't allow the user to drag
1848 * over those marks.
1850 if (state->marks[oy*w+ox] & dir)
1851 break;
1852 ox += dx;
1853 oy += dy;
1854 ui->dragcoords[ui->ndragcoords++] = oy * w + ox;
1859 * Failing that, we do nothing at all: if the user has dragged
1860 * diagonally across the board, they'll just have to return the
1861 * mouse to the last known position and do whatever they meant to
1862 * do again, more slowly and clearly.
1867 * Routine shared between interpret_move and game_redraw to work out
1868 * the intended effect of a drag path on the grid.
1870 * Call it in a loop, like this:
1872 * int clearing = TRUE;
1873 * for (i = 0; i < ui->ndragcoords - 1; i++) {
1874 * int sx, sy, dx, dy, dir, oldstate, newstate;
1875 * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
1876 * &dir, &oldstate, &newstate);
1878 * [do whatever is needed to handle the fact that the drag
1879 * wants the edge from sx,sy to dx,dy (heading in direction
1880 * 'dir' at the sx,sy end) to be changed from state oldstate
1881 * to state newstate, each of which equals either 0 or dir]
1884 static void interpret_ui_drag(game_state *state, game_ui *ui, int *clearing,
1885 int i, int *sx, int *sy, int *dx, int *dy,
1886 int *dir, int *oldstate, int *newstate)
1888 int w = state->shared->w;
1889 int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1];
1890 *sy = sp/w;
1891 *sx = sp%w;
1892 *dy = dp/w;
1893 *dx = dp%w;
1894 *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L);
1895 *oldstate = state->lines[sp] & *dir;
1896 if (*oldstate) {
1898 * The edge we've dragged over was previously
1899 * present. Set it to absent, unless we've already
1900 * stopped doing that.
1902 *newstate = *clearing ? 0 : *dir;
1903 } else {
1905 * The edge we've dragged over was previously
1906 * absent. Set it to present, and cancel the
1907 * 'clearing' flag so that all subsequent edges in
1908 * the drag are set rather than cleared.
1910 *newstate = *dir;
1911 *clearing = FALSE;
1915 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
1916 int x, int y, int button)
1918 int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */;
1919 int gx = FROMCOORD(x), gy = FROMCOORD(y), i;
1920 char tmpbuf[80];
1922 if (IS_MOUSE_DOWN(button)) {
1923 if (!INGRID(state, gx, gy)) {
1924 ui->ndragcoords = -1;
1925 return NULL;
1928 ui->clickx = x; ui->clicky = y;
1929 ui->dragcoords[0] = gy * w + gx;
1930 ui->ndragcoords = 0; /* will be 1 once drag is confirmed */
1932 return "";
1935 if (button == LEFT_DRAG && ui->ndragcoords >= 0) {
1936 update_ui_drag(state, ui, gx, gy);
1937 return "";
1940 if (IS_MOUSE_RELEASE(button)) {
1941 if (ui->ndragcoords > 0) {
1942 /* End of a drag: process the cached line data. */
1943 int buflen = 0, bufsize = 256, tmplen;
1944 char *buf = NULL;
1945 const char *sep = "";
1946 int clearing = TRUE;
1948 for (i = 0; i < ui->ndragcoords - 1; i++) {
1949 int sx, sy, dx, dy, dir, oldstate, newstate;
1950 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
1951 &dir, &oldstate, &newstate);
1953 if (oldstate != newstate) {
1954 if (!buf) buf = snewn(bufsize, char);
1955 tmplen = sprintf(tmpbuf, "%sF%d,%d,%d;F%d,%d,%d", sep,
1956 dir, sx, sy, F(dir), dx, dy);
1957 if (buflen + tmplen >= bufsize) {
1958 bufsize = (buflen + tmplen) * 5 / 4 + 256;
1959 buf = sresize(buf, bufsize, char);
1961 strcpy(buf + buflen, tmpbuf);
1962 buflen += tmplen;
1963 sep = ";";
1967 ui->ndragcoords = -1;
1969 return buf ? buf : "";
1970 } else if (ui->ndragcoords == 0) {
1971 /* Click (or tiny drag). Work out which edge we were
1972 * closest to. */
1973 int cx, cy;
1974 int gx2, gy2, l1, l2, ismark = (button == RIGHT_RELEASE);
1975 char movec = ismark ? 'M' : 'F';
1977 ui->ndragcoords = -1;
1980 * We process clicks based on the mouse-down location,
1981 * because that's more natural for a user to carefully
1982 * control than the mouse-up.
1984 x = ui->clickx;
1985 y = ui->clicky;
1987 gx = FROMCOORD(x);
1988 gy = FROMCOORD(y);
1989 cx = COORD(gx) + TILE_SIZE/2;
1990 cy = COORD(gy) + TILE_SIZE/2;
1992 if (!INGRID(state, gx, gy)) return "";
1994 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
1995 /* TODO closer to centre of grid: process as a cell click not an edge click. */
1997 return "";
1998 } else {
1999 if (abs(x-cx) < abs(y-cy)) {
2000 /* Closest to top/bottom edge. */
2001 l1 = (y < cy) ? U : D;
2002 } else {
2003 /* Closest to left/right edge. */
2004 l1 = (x < cx) ? L : R;
2006 gx2 = gx + DX(l1); gy2 = gy + DY(l1);
2007 l2 = F(l1);
2009 if (!INGRID(state, gx, gy) || !INGRID(state, gx2, gy2)) return "";
2011 /* disallow laying a mark over a line, or vice versa. */
2012 if (ismark) {
2013 if ((state->lines[gy*w+gx] & l1) || (state->lines[gy2*w+gx2] & l2))
2014 return "";
2015 } else {
2016 if ((state->marks[gy*w+gx] & l1) || (state->marks[gy2*w+gx2] & l2))
2017 return "";
2020 sprintf(tmpbuf, "%c%d,%d,%d;%c%d,%d,%d",
2021 movec, l1, gx, gy, movec, l2, gx2, gy2);
2022 return dupstr(tmpbuf);
2027 if (button == 'H' || button == 'h')
2028 return dupstr("H");
2030 /* TODO cursor */
2032 return NULL;
2035 static game_state *execute_move(game_state *state, char *move)
2037 int w = state->shared->w, h = state->shared->h;
2038 char c;
2039 int x, y, l, n;
2040 game_state *ret = dup_game(state);
2042 debug(("move: %s\n", move));
2044 while (*move) {
2045 c = *move;
2046 if (c == 'S') {
2047 ret->used_solve = TRUE;
2048 move++;
2049 } else if (c == 'L' || c == 'N' || c == 'R' || c == 'F' || c == 'M') {
2050 /* 'line' or 'noline' or 'replace' or 'flip' or 'mark' */
2051 move++;
2052 if (sscanf(move, "%d,%d,%d%n", &l, &x, &y, &n) != 3)
2053 goto badmove;
2054 if (!INGRID(state, x, y)) goto badmove;
2055 if (l < 0 || l > 15) goto badmove;
2057 if (c == 'L')
2058 ret->lines[y*w + x] |= (char)l;
2059 else if (c == 'N')
2060 ret->lines[y*w + x] &= ~((char)l);
2061 else if (c == 'R') {
2062 ret->lines[y*w + x] = (char)l;
2063 ret->marks[y*w + x] &= ~((char)l); /* erase marks too */
2064 } else if (c == 'F')
2065 ret->lines[y*w + x] ^= (char)l;
2066 else if (c == 'M')
2067 ret->marks[y*w + x] ^= (char)l;
2070 * If we ended up trying to lay a line _over_ a mark,
2071 * that's a failed move: interpret_move() should have
2072 * ensured we never received a move string like that in
2073 * the first place.
2075 if ((ret->lines[y*w + x] & (char)l) &&
2076 (ret->marks[y*w + x] & (char)l))
2077 goto badmove;
2079 move += n;
2080 } else if (strcmp(move, "H") == 0) {
2081 pearl_solve(ret->shared->w, ret->shared->h,
2082 ret->shared->clues, ret->lines, DIFFCOUNT, TRUE);
2083 for (n = 0; n < w*h; n++)
2084 ret->marks[n] &= ~ret->lines[n]; /* erase marks too */
2085 move++;
2086 } else {
2087 goto badmove;
2089 if (*move == ';')
2090 move++;
2091 else if (*move)
2092 goto badmove;
2095 check_completion(ret, TRUE);
2097 return ret;
2099 badmove:
2100 free_game(ret);
2101 return NULL;
2104 /* ----------------------------------------------------------------------
2105 * Drawing routines.
2108 #define FLASH_TIME 0.5F
2110 static void game_compute_size(game_params *params, int tilesize,
2111 int *x, int *y)
2113 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2114 struct { int halfsz; } ads, *ds = &ads;
2115 ads.halfsz = (tilesize-1)/2;
2117 *x = (params->w) * TILE_SIZE + 2 * BORDER;
2118 *y = (params->h) * TILE_SIZE + 2 * BORDER;
2121 static void game_set_size(drawing *dr, game_drawstate *ds,
2122 game_params *params, int tilesize)
2124 ds->halfsz = (tilesize-1)/2;
2127 static float *game_colours(frontend *fe, int *ncolours)
2129 float *ret = snewn(3 * NCOLOURS, float);
2130 int i;
2132 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
2134 for (i = 0; i < 3; i++) {
2135 ret[COL_BLACK * 3 + i] = 0.0F;
2136 ret[COL_WHITE * 3 + i] = 1.0F;
2137 ret[COL_GRID * 3 + i] = 0.4F;
2140 ret[COL_ERROR * 3 + 0] = 1.0F;
2141 ret[COL_ERROR * 3 + 1] = 0.0F;
2142 ret[COL_ERROR * 3 + 2] = 0.0F;
2144 ret[COL_DRAGON * 3 + 0] = 0.0F;
2145 ret[COL_DRAGON * 3 + 1] = 0.0F;
2146 ret[COL_DRAGON * 3 + 2] = 1.0F;
2148 ret[COL_DRAGOFF * 3 + 0] = 0.8F;
2149 ret[COL_DRAGOFF * 3 + 1] = 0.8F;
2150 ret[COL_DRAGOFF * 3 + 2] = 1.0F;
2152 ret[COL_FLASH * 3 + 0] = 1.0F;
2153 ret[COL_FLASH * 3 + 1] = 1.0F;
2154 ret[COL_FLASH * 3 + 2] = 1.0F;
2156 *ncolours = NCOLOURS;
2158 return ret;
2161 static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
2163 struct game_drawstate *ds = snew(struct game_drawstate);
2164 int i;
2166 ds->halfsz = 0;
2167 ds->started = FALSE;
2169 ds->w = state->shared->w;
2170 ds->h = state->shared->h;
2171 ds->sz = state->shared->sz;
2172 ds->lflags = snewn(ds->sz, unsigned int);
2173 for (i = 0; i < ds->sz; i++)
2174 ds->lflags[i] = 0;
2176 ds->draglines = snewn(ds->sz, char);
2178 return ds;
2181 static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2183 sfree(ds->draglines);
2184 sfree(ds->lflags);
2185 sfree(ds);
2188 static void draw_lines_specific(drawing *dr, game_drawstate *ds,
2189 int x, int y, unsigned int lflags,
2190 unsigned int shift, int c)
2192 int ox = COORD(x), oy = COORD(y);
2193 int t2 = HALFSZ, t16 = HALFSZ/4;
2194 int cx = ox + t2, cy = oy + t2;
2195 int d;
2197 /* Draw each of the four directions, where laid (or error, or drag, etc.) */
2198 for (d = 1; d < 16; d *= 2) {
2199 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2200 int xnudge = abs(t16 * DX(C(d))), ynudge = abs(t16 * DY(C(d)));
2202 if ((lflags >> shift) & d) {
2203 int lx = cx + ((xoff < 0) ? xoff : 0) - xnudge;
2204 int ly = cy + ((yoff < 0) ? yoff : 0) - ynudge;
2206 if (c == COL_DRAGOFF && !(lflags & d))
2207 continue;
2208 if (c == COL_DRAGON && (lflags & d))
2209 continue;
2211 draw_rect(dr, lx, ly,
2212 abs(xoff)+2*xnudge+1,
2213 abs(yoff)+2*ynudge+1, c);
2214 /* end cap */
2215 draw_rect(dr, cx - t16, cy - t16, 2*t16+1, 2*t16+1, c);
2220 static void draw_square(drawing *dr, game_drawstate *ds, game_ui *ui,
2221 int x, int y, unsigned int lflags, char clue)
2223 int ox = COORD(x), oy = COORD(y);
2224 int t2 = HALFSZ, t16 = HALFSZ/4;
2225 int cx = ox + t2, cy = oy + t2;
2226 int d;
2228 assert(dr);
2230 /* Clip to the grid square. */
2231 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2233 /* Clear the square. */
2234 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
2236 if (get_gui_style() == GUI_LOOPY) {
2237 /* Draw small dot, underneath any lines. */
2238 draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID);
2239 } else {
2240 /* Draw outline of grid square */
2241 draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID);
2242 draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID);
2245 /* Draw grid: either thin gridlines, or no-line marks.
2246 * We draw these first because the thick laid lines should be on top. */
2247 for (d = 1; d < 16; d *= 2) {
2248 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2250 if ((x == 0 && d == L) ||
2251 (y == 0 && d == U) ||
2252 (x == ds->w-1 && d == R) ||
2253 (y == ds->h-1 && d == D))
2254 continue; /* no gridlines out to the border. */
2256 if ((lflags >> DS_MSHIFT) & d) {
2257 /* either a no-line mark ... */
2258 int mx = cx + xoff, my = cy + yoff, msz = t16;
2260 draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK);
2261 draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK);
2262 } else {
2263 if (get_gui_style() == GUI_LOOPY) {
2264 /* draw grid lines connecting centre of cells */
2265 draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID);
2270 /* Draw each of the four directions, where laid (or error, or drag, etc.)
2271 * Order is important here, specifically for the eventual colours of the
2272 * exposed end caps. */
2273 draw_lines_specific(dr, ds, x, y, lflags, 0,
2274 (lflags & DS_FLASH ? COL_FLASH : COL_BLACK));
2275 draw_lines_specific(dr, ds, x, y, lflags, DS_ESHIFT, COL_ERROR);
2276 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGOFF);
2277 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGON);
2279 /* Draw a clue, if present */
2280 if (clue != NOCLUE) {
2281 int c = (lflags & DS_FLASH) ? COL_FLASH :
2282 (clue == STRAIGHT) ? COL_WHITE : COL_BLACK;
2284 if (lflags & DS_ERROR_CLUE) /* draw a bigger 'error' clue circle. */
2285 draw_circle(dr, cx, cy, TILE_SIZE*3/8, COL_ERROR, COL_ERROR);
2287 draw_circle(dr, cx, cy, TILE_SIZE/4, c, COL_BLACK);
2290 unclip(dr);
2291 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2294 static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
2295 game_state *state, int dir, game_ui *ui,
2296 float animtime, float flashtime)
2298 int w = state->shared->w, h = state->shared->h, sz = state->shared->sz;
2299 int x, y, force = 0, flashing = 0;
2301 if (!ds->started) {
2303 * The initial contents of the window are not guaranteed and
2304 * can vary with front ends. To be on the safe side, all games
2305 * should start by drawing a big background-colour rectangle
2306 * covering the whole window.
2308 draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER,
2309 COL_BACKGROUND);
2311 if (get_gui_style() == GUI_MASYU) {
2313 * Smaller black rectangle which is the main grid.
2315 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2316 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
2317 h*TILE_SIZE + 2*BORDER_WIDTH + 1,
2318 COL_GRID);
2321 draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER);
2323 ds->started = TRUE;
2324 force = 1;
2327 if (flashtime > 0 &&
2328 (flashtime <= FLASH_TIME/3 ||
2329 flashtime >= FLASH_TIME*2/3))
2330 flashing = DS_FLASH;
2332 memset(ds->draglines, 0, sz);
2333 if (ui->ndragcoords > 0) {
2334 int i, clearing = TRUE;
2335 for (i = 0; i < ui->ndragcoords - 1; i++) {
2336 int sx, sy, dx, dy, dir, oldstate, newstate;
2337 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2338 &dir, &oldstate, &newstate);
2339 ds->draglines[sy*w+sx] ^= (oldstate ^ newstate);
2340 ds->draglines[dy*w+dx] ^= (F(oldstate) ^ F(newstate));
2344 for (x = 0; x < w; x++) {
2345 for (y = 0; y < h; y++) {
2346 unsigned int f = (unsigned int)state->lines[y*w+x];
2347 unsigned int eline = (unsigned int)(state->errors[y*w+x] & (R|U|L|D));
2349 f |= eline << DS_ESHIFT;
2350 f |= ((unsigned int)ds->draglines[y*w+x]) << DS_DSHIFT;
2351 f |= ((unsigned int)state->marks[y*w+x]) << DS_MSHIFT;
2353 if (state->errors[y*w+x] & ERROR_CLUE)
2354 f |= DS_ERROR_CLUE;
2356 f |= flashing;
2358 if (f != ds->lflags[y*w+x] || force) {
2359 ds->lflags[y*w+x] = f;
2360 draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]);
2366 static float game_anim_length(game_state *oldstate, game_state *newstate,
2367 int dir, game_ui *ui)
2369 return 0.0F;
2372 static float game_flash_length(game_state *oldstate, game_state *newstate,
2373 int dir, game_ui *ui)
2375 if (!oldstate->completed &&
2376 newstate->completed && !newstate->used_solve)
2377 return FLASH_TIME;
2378 else
2379 return 0.0F;
2382 static int game_status(game_state *state)
2384 return state->completed ? +1 : 0;
2387 static int game_timing_state(game_state *state, game_ui *ui)
2389 return TRUE;
2392 static void game_print_size(game_params *params, float *x, float *y)
2394 int pw, ph;
2397 * I'll use 6mm squares by default.
2399 game_compute_size(params, 600, &pw, &ph);
2400 *x = pw / 100.0F;
2401 *y = ph / 100.0F;
2404 static void game_print(drawing *dr, game_state *state, int tilesize)
2406 int w = state->shared->w, h = state->shared->h, x, y;
2407 int black = print_mono_colour(dr, 0);
2408 int white = print_mono_colour(dr, 1);
2410 /* No GUI_LOOPY here: only use the familiar masyu style. */
2412 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2413 game_drawstate *ds = game_new_drawstate(dr, state);
2414 game_set_size(dr, ds, NULL, tilesize);
2416 /* Draw grid outlines (black). */
2417 for (x = 0; x <= w; x++)
2418 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
2419 for (y = 0; y <= h; y++)
2420 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
2422 for (x = 0; x < w; x++) {
2423 for (y = 0; y < h; y++) {
2424 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ;
2425 int clue = state->shared->clues[y*w+x];
2427 draw_lines_specific(dr, ds, x, y, state->lines[y*w+x], 0, black);
2429 if (clue != NOCLUE) {
2430 int c = (clue == CORNER) ? black : white;
2431 draw_circle(dr, cx, cy, TILE_SIZE/4, c, black);
2436 game_free_drawstate(dr, ds);
2439 #ifdef COMBINED
2440 #define thegame pearl
2441 #endif
2443 const struct game thegame = {
2444 "Pearl", "games.pearl", "pearl",
2445 default_params,
2446 game_fetch_preset,
2447 decode_params,
2448 encode_params,
2449 free_params,
2450 dup_params,
2451 TRUE, game_configure, custom_params,
2452 validate_params,
2453 new_game_desc,
2454 validate_desc,
2455 new_game,
2456 dup_game,
2457 free_game,
2458 TRUE, solve_game,
2459 FALSE, game_can_format_as_text_now, game_text_format,
2460 new_ui,
2461 free_ui,
2462 encode_ui,
2463 decode_ui,
2464 game_changed_state,
2465 interpret_move,
2466 execute_move,
2467 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2468 game_colours,
2469 game_new_drawstate,
2470 game_free_drawstate,
2471 game_redraw,
2472 game_anim_length,
2473 game_flash_length,
2474 game_status,
2475 TRUE, FALSE, game_print_size, game_print,
2476 FALSE, /* wants_statusbar */
2477 FALSE, game_timing_state,
2478 0, /* flags */
2481 #ifdef STANDALONE_SOLVER
2483 #include <time.h>
2484 #include <stdarg.h>
2486 const char *quis = NULL;
2488 static void usage(FILE *out) {
2489 fprintf(out, "usage: %s <params>\n", quis);
2492 static void pnum(int n, int ntot, const char *desc)
2494 printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc);
2497 static void start_soak(game_params *p, random_state *rs, int nsecs)
2499 time_t tt_start, tt_now, tt_last;
2500 int n = 0, nsolved = 0, nimpossible = 0, ret;
2501 char *grid, *clues;
2503 tt_start = tt_last = time(NULL);
2505 /* Currently this generates puzzles of any difficulty (trying to solve it
2506 * on the maximum difficulty level and not checking it's not too easy). */
2507 printf("Soak-testing a %dx%d grid (any difficulty)", p->w, p->h);
2508 if (nsecs > 0) printf(" for %d seconds", nsecs);
2509 printf(".\n");
2511 p->nosolve = TRUE;
2513 grid = snewn(p->w*p->h, char);
2514 clues = snewn(p->w*p->h, char);
2516 while (1) {
2517 n += new_clues(p, rs, clues, grid); /* should be 1, with nosolve */
2519 ret = pearl_solve(p->w, p->h, clues, grid, DIFF_TRICKY, FALSE);
2520 if (ret <= 0) nimpossible++;
2521 if (ret == 1) nsolved++;
2523 tt_now = time(NULL);
2524 if (tt_now > tt_last) {
2525 tt_last = tt_now;
2527 printf("%d total, %3.1f/s, ",
2528 n, (double)n / ((double)tt_now - tt_start));
2529 pnum(nsolved, n, "solved"); printf(", ");
2530 printf("%3.1f/s", (double)nsolved / ((double)tt_now - tt_start));
2531 if (nimpossible > 0)
2532 pnum(nimpossible, n, "impossible");
2533 printf("\n");
2535 if (nsecs > 0 && (tt_now - tt_start) > nsecs) {
2536 printf("\n");
2537 break;
2541 sfree(grid);
2542 sfree(clues);
2545 int main(int argc, const char *argv[])
2547 game_params *p = NULL;
2548 random_state *rs = NULL;
2549 time_t seed = time(NULL);
2550 char *id = NULL, *err;
2552 setvbuf(stdout, NULL, _IONBF, 0);
2554 quis = argv[0];
2556 while (--argc > 0) {
2557 char *p = (char*)(*++argv);
2558 if (!strcmp(p, "-e") || !strcmp(p, "--seed")) {
2559 seed = atoi(*++argv);
2560 argc--;
2561 } else if (*p == '-') {
2562 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2563 usage(stderr);
2564 exit(1);
2565 } else {
2566 id = p;
2570 rs = random_new((void*)&seed, sizeof(time_t));
2571 p = default_params();
2573 if (id) {
2574 if (strchr(id, ':')) {
2575 fprintf(stderr, "soak takes params only.\n");
2576 goto done;
2579 decode_params(p, id);
2580 err = validate_params(p, 1);
2581 if (err) {
2582 fprintf(stderr, "%s: %s", argv[0], err);
2583 goto done;
2586 start_soak(p, rs, 0); /* run forever */
2587 } else {
2588 int i;
2590 for (i = 5; i <= 12; i++) {
2591 p->w = p->h = i;
2592 start_soak(p, rs, 5);
2596 done:
2597 free_params(p);
2598 random_free(rs);
2600 return 0;
2603 #endif
2605 /* vim: set shiftwidth=4 tabstop=8: */