2 * pearl.c: Nikoli's `Masyu' puzzle.
8 * - The current keyboard cursor mechanism works well on ordinary PC
9 * keyboards, but for platforms with only arrow keys and a select
10 * button or two, we may at some point need a simpler one which can
11 * handle 'x' markings without needing shift keys. For instance, a
12 * cursor with twice the grid resolution, so that it can range
13 * across face centres, edge centres and vertices; 'clicks' on face
14 * centres begin a drag as currently, clicks on edges toggle
15 * markings, and clicks on vertices are ignored (but it would be
16 * too confusing not to let the cursor rest on them). But I'm
17 * pretty sure that would be less pleasant to play on a full
18 * keyboard, so probably a #ifdef would be the thing.
20 * - Generation is still pretty slow, due to difficulty coming up in
21 * the first place with a loop that makes a soluble puzzle even
22 * with all possible clues filled in.
23 * + A possible alternative strategy to further tuning of the
24 * existing loop generator would be to throw the entire
25 * mechanism out and instead write a different generator from
26 * scratch which evolves the solution along with the puzzle:
27 * place a few clues, nail down a bit of the loop, place another
28 * clue, nail down some more, etc. However, I don't have a
29 * detailed plan for any such mechanism, so it may be a pipe
44 #define SWAP(i,j) do { int swaptmp = (i); (i) = (j); (j) = swaptmp; } while (0)
55 #define DX(d) ( ((d)==R) - ((d)==L) )
56 #define DY(d) ( ((d)==D) - ((d)==U) )
58 #define F(d) (((d << 2) | (d >> 2)) & 0xF)
59 #define C(d) (((d << 3) | (d >> 1)) & 0xF)
60 #define A(d) (((d << 1) | (d >> 3)) & 0xF)
89 #define bBLANK (1 << BLANK)
92 COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
93 COL_CURSOR_BACKGROUND = COL_LOWLIGHT,
95 COL_ERROR, COL_GRID, COL_FLASH,
96 COL_DRAGON, COL_DRAGOFF,
100 /* Macro ickery copied from slant.c */
101 #define DIFFLIST(A) \
104 #define ENUM(upper,title,lower) DIFF_ ## upper,
105 #define TITLE(upper,title,lower) #title,
106 #define ENCODE(upper,title,lower) #lower
107 #define CONFIG(upper,title,lower) ":" #title
108 enum { DIFFLIST(ENUM) DIFFCOUNT };
109 static char const *const pearl_diffnames[] = { DIFFLIST(TITLE) "(count)" };
110 static char const pearl_diffchars[] = DIFFLIST(ENCODE);
111 #define DIFFCONFIG DIFFLIST(CONFIG)
116 int nosolve; /* XXX remove me! */
119 struct shared_state {
121 char *clues; /* size w*h */
125 #define INGRID(state, gx, gy) ((gx) >= 0 && (gx) < (state)->shared->w && \
126 (gy) >= 0 && (gy) < (state)->shared->h)
128 struct shared_state *shared;
129 char *lines; /* size w*h: lines placed */
130 char *errors; /* size w*h: errors detected */
131 char *marks; /* size w*h: 'no line here' marks placed. */
132 int completed, used_solve;
135 #define DEFAULT_PRESET 3
137 static const struct game_params pearl_presets[] = {
143 {10, 10, DIFF_TRICKY},
145 {12, 8, DIFF_TRICKY},
148 static game_params *default_params(void)
150 game_params *ret = snew(game_params);
152 *ret = pearl_presets[DEFAULT_PRESET];
153 ret->nosolve = FALSE;
158 static int game_fetch_preset(int i, char **name, game_params **params)
163 if (i < 0 || i >= lenof(pearl_presets)) return FALSE;
165 ret = default_params();
166 *ret = pearl_presets[i]; /* struct copy */
169 sprintf(buf, "%dx%d %s",
170 pearl_presets[i].w, pearl_presets[i].h,
171 pearl_diffnames[pearl_presets[i].difficulty]);
177 static void free_params(game_params *params)
182 static game_params *dup_params(const game_params *params)
184 game_params *ret = snew(game_params);
185 *ret = *params; /* structure copy */
189 static void decode_params(game_params *ret, char const *string)
191 ret->w = ret->h = atoi(string);
192 while (*string && isdigit((unsigned char) *string)) ++string;
193 if (*string == 'x') {
195 ret->h = atoi(string);
196 while (*string && isdigit((unsigned char)*string)) string++;
199 ret->difficulty = DIFF_EASY;
200 if (*string == 'd') {
203 for (i = 0; i < DIFFCOUNT; i++)
204 if (*string == pearl_diffchars[i])
206 if (*string) string++;
209 ret->nosolve = FALSE;
210 if (*string == 'n') {
216 static char *encode_params(const game_params *params, int full)
219 sprintf(buf, "%dx%d", params->w, params->h);
221 sprintf(buf + strlen(buf), "d%c%s",
222 pearl_diffchars[params->difficulty],
223 params->nosolve ? "n" : "");
227 static config_item *game_configure(const game_params *params)
232 ret = snewn(5, config_item);
234 ret[0].name = "Width";
235 ret[0].type = C_STRING;
236 sprintf(buf, "%d", params->w);
237 ret[0].u.string.sval = dupstr(buf);
239 ret[1].name = "Height";
240 ret[1].type = C_STRING;
241 sprintf(buf, "%d", params->h);
242 ret[1].u.string.sval = dupstr(buf);
244 ret[2].name = "Difficulty";
245 ret[2].type = C_CHOICES;
246 ret[2].u.choices.choicenames = DIFFCONFIG;
247 ret[2].u.choices.selected = params->difficulty;
249 ret[3].name = "Allow unsoluble";
250 ret[3].type = C_BOOLEAN;
251 ret[3].u.boolean.bval = params->nosolve;
259 static game_params *custom_params(const config_item *cfg)
261 game_params *ret = snew(game_params);
263 ret->w = atoi(cfg[0].u.string.sval);
264 ret->h = atoi(cfg[1].u.string.sval);
265 ret->difficulty = cfg[2].u.choices.selected;
266 ret->nosolve = cfg[3].u.boolean.bval;
271 static const char *validate_params(const game_params *params, int full)
273 if (params->w < 5) return "Width must be at least five";
274 if (params->h < 5) return "Height must be at least five";
275 if (params->difficulty < 0 || params->difficulty >= DIFFCOUNT)
276 return "Unknown difficulty level";
281 /* ----------------------------------------------------------------------
285 int pearl_solve(int w, int h, char *clues, char *result,
286 int difficulty, int partial)
288 int W = 2*w+1, H = 2*h+1;
295 * workspace[(2*y+1)*W+(2*x+1)] indicates the possible nature
296 * of the square (x,y), as a logical OR of bitfields.
298 * workspace[(2*y)*W+(2*x+1)], for x odd and y even, indicates
299 * whether the horizontal edge between (x,y) and (x+1,y) is
300 * connected (1), disconnected (2) or unknown (3).
302 * workspace[(2*y+1)*W+(2*x)], indicates the same about the
303 * vertical edge between (x,y) and (x,y+1).
305 * Initially, every square is considered capable of being in
306 * any of the seven possible states (two straights, four
307 * corners and empty), except those corresponding to clue
308 * squares which are more restricted.
310 * Initially, all edges are unknown, except the ones around the
311 * grid border which are known to be disconnected.
313 workspace = snewn(W*H, short);
314 for (x = 0; x < W*H; x++)
317 for (y = 0; y < h; y++)
318 for (x = 0; x < w; x++)
319 switch (clues[y*w+x]) {
321 workspace[(2*y+1)*W+(2*x+1)] = bLU|bLD|bRU|bRD;
324 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD;
327 workspace[(2*y+1)*W+(2*x+1)] = bLR|bUD|bLU|bLD|bRU|bRD|bBLANK;
330 /* Horizontal edges */
331 for (y = 0; y <= h; y++)
332 for (x = 0; x < w; x++)
333 workspace[(2*y)*W+(2*x+1)] = (y==0 || y==h ? 2 : 3);
335 for (y = 0; y < h; y++)
336 for (x = 0; x <= w; x++)
337 workspace[(2*y+1)*W+(2*x)] = (x==0 || x==w ? 2 : 3);
340 * We maintain a dsf of connected squares, together with a
341 * count of the size of each equivalence class.
343 dsf = snewn(w*h, int);
344 dsfsize = snewn(w*h, int);
347 * Now repeatedly try to find something we can do.
350 int done_something = FALSE;
352 #ifdef SOLVER_DIAGNOSTICS
353 for (y = 0; y < H; y++) {
354 for (x = 0; x < W; x++)
355 printf("%*x", (x&1) ? 5 : 2, workspace[y*W+x]);
361 * Go through the square state words, and discard any
362 * square state which is inconsistent with known facts
363 * about the edges around the square.
365 for (y = 0; y < h; y++)
366 for (x = 0; x < w; x++) {
367 for (b = 0; b < 0xD; b++)
368 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) {
370 * If any edge of this square is known to
371 * be connected when state b would require
372 * it disconnected, or vice versa, discard
375 for (d = 1; d <= 8; d += d) {
376 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
377 if (workspace[ey*W+ex] ==
379 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<b);
380 #ifdef SOLVER_DIAGNOSTICS
381 printf("edge (%d,%d)-(%d,%d) rules out state"
382 " %d for square (%d,%d)\n",
383 ex/2, ey/2, (ex+1)/2, (ey+1)/2,
386 done_something = TRUE;
393 * Consistency check: each square must have at
394 * least one state left!
396 if (!workspace[(2*y+1)*W+(2*x+1)]) {
397 #ifdef SOLVER_DIAGNOSTICS
398 printf("edge check at (%d,%d): inconsistency\n", x, y);
406 * Now go through the states array again, and nail down any
407 * unknown edge if one of its neighbouring squares makes it
410 for (y = 0; y < h; y++)
411 for (x = 0; x < w; x++) {
412 int edgeor = 0, edgeand = 15;
414 for (b = 0; b < 0xD; b++)
415 if (workspace[(2*y+1)*W+(2*x+1)] & (1<<b)) {
421 * Now any bit clear in edgeor marks a disconnected
422 * edge, and any bit set in edgeand marks a
426 /* First check consistency: neither bit is both! */
427 if (edgeand & ~edgeor) {
428 #ifdef SOLVER_DIAGNOSTICS
429 printf("square check at (%d,%d): inconsistency\n", x, y);
435 for (d = 1; d <= 8; d += d) {
436 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
438 if (!(edgeor & d) && workspace[ey*W+ex] == 3) {
439 workspace[ey*W+ex] = 2;
440 done_something = TRUE;
441 #ifdef SOLVER_DIAGNOSTICS
442 printf("possible states of square (%d,%d) force edge"
443 " (%d,%d)-(%d,%d) to be disconnected\n",
444 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2);
446 } else if ((edgeand & d) && workspace[ey*W+ex] == 3) {
447 workspace[ey*W+ex] = 1;
448 done_something = TRUE;
449 #ifdef SOLVER_DIAGNOSTICS
450 printf("possible states of square (%d,%d) force edge"
451 " (%d,%d)-(%d,%d) to be connected\n",
452 x, y, ex/2, ey/2, (ex+1)/2, (ey+1)/2);
462 * Now for longer-range clue-based deductions (using the
463 * rules that a corner clue must connect to two straight
464 * squares, and a straight clue must connect to at least
465 * one corner square).
467 for (y = 0; y < h; y++)
468 for (x = 0; x < w; x++)
469 switch (clues[y*w+x]) {
471 for (d = 1; d <= 8; d += d) {
472 int ex = 2*x+1 + DX(d), ey = 2*y+1 + DY(d);
473 int fx = ex + DX(d), fy = ey + DY(d);
476 if (workspace[ey*W+ex] == 1) {
478 * If a corner clue is connected on any
479 * edge, then we can immediately nail
480 * down the square beyond that edge as
481 * being a straight in the appropriate
484 if (workspace[fy*W+fx] != (1<<type)) {
485 workspace[fy*W+fx] = (1<<type);
486 done_something = TRUE;
487 #ifdef SOLVER_DIAGNOSTICS
488 printf("corner clue at (%d,%d) forces square "
489 "(%d,%d) into state %d\n", x, y,
494 } else if (workspace[ey*W+ex] == 3) {
496 * Conversely, if a corner clue is
497 * separated by an unknown edge from a
498 * square which _cannot_ be a straight
499 * in the appropriate direction, we can
500 * mark that edge as disconnected.
502 if (!(workspace[fy*W+fx] & (1<<type))) {
503 workspace[ey*W+ex] = 2;
504 done_something = TRUE;
505 #ifdef SOLVER_DIAGNOSTICS
506 printf("corner clue at (%d,%d), plus square "
507 "(%d,%d) not being state %d, "
508 "disconnects edge (%d,%d)-(%d,%d)\n",
509 x, y, fx/2, fy/2, type,
510 ex/2, ey/2, (ex+1)/2, (ey+1)/2);
520 * If a straight clue is between two squares
521 * neither of which is capable of being a
522 * corner connected to it, then the straight
523 * clue cannot point in that direction.
525 for (d = 1; d <= 2; d += d) {
526 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d);
527 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d);
530 if (!(workspace[(2*y+1)*W+(2*x+1)] & (1<<type)))
533 if (!(workspace[fy*W+fx] & ((1<<(F(d)|A(d))) |
534 (1<<(F(d)|C(d))))) &&
535 !(workspace[gy*W+gx] & ((1<<( d |A(d))) |
537 workspace[(2*y+1)*W+(2*x+1)] &= ~(1<<type);
538 done_something = TRUE;
539 #ifdef SOLVER_DIAGNOSTICS
540 printf("straight clue at (%d,%d) cannot corner at "
541 "(%d,%d) or (%d,%d) so is not state %d\n",
542 x, y, fx/2, fy/2, gx/2, gy/2, type);
549 * If a straight clue with known direction is
550 * connected on one side to a known straight,
551 * then on the other side it must be a corner.
553 for (d = 1; d <= 8; d += d) {
554 int fx = 2*x+1 + 2*DX(d), fy = 2*y+1 + 2*DY(d);
555 int gx = 2*x+1 - 2*DX(d), gy = 2*y+1 - 2*DY(d);
558 if (workspace[(2*y+1)*W+(2*x+1)] != (1<<type))
561 if (!(workspace[fy*W+fx] &~ (bLR|bUD)) &&
562 (workspace[gy*W+gx] &~ (bLU|bLD|bRU|bRD))) {
563 workspace[gy*W+gx] &= (bLU|bLD|bRU|bRD);
564 done_something = TRUE;
565 #ifdef SOLVER_DIAGNOSTICS
566 printf("straight clue at (%d,%d) connecting to "
567 "straight at (%d,%d) makes (%d,%d) a "
568 "corner\n", x, y, fx/2, fy/2, gx/2, gy/2);
580 * Now detect shortcut loops.
584 int nonblanks, loopclass;
587 for (x = 0; x < w*h; x++)
591 * First go through the edge entries and update the dsf
592 * of which squares are connected to which others. We
593 * also track the number of squares in each equivalence
594 * class, and count the overall number of
595 * known-non-blank squares.
597 * In the process of doing this, we must notice if a
598 * loop has already been formed. If it has, we blank
599 * out any square which isn't part of that loop
600 * (failing a consistency check if any such square does
601 * not have BLANK as one of its remaining options) and
602 * exit the deduction loop with success.
606 for (y = 1; y < H-1; y++)
607 for (x = 1; x < W-1; x++)
610 * (x,y) are the workspace coordinates of
611 * an edge field. Compute the normal-space
612 * coordinates of the squares it connects.
614 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax;
615 int bx = x/2, by = y/2, bc = by*w+bx;
618 * If the edge is connected, do the dsf
621 if (workspace[y*W+x] == 1) {
624 ae = dsf_canonify(dsf, ac);
625 be = dsf_canonify(dsf, bc);
631 if (loopclass != -1) {
633 * In fact, we have two
634 * separate loops, which is
637 #ifdef SOLVER_DIAGNOSTICS
638 printf("two loops found in grid!\n");
646 * Merge the two equivalence
649 int size = dsfsize[ae] + dsfsize[be];
650 dsf_merge(dsf, ac, bc);
651 ae = dsf_canonify(dsf, ac);
655 } else if ((y & x) & 1) {
657 * (x,y) are the workspace coordinates of a
658 * square field. If the square is
659 * definitely not blank, count it.
661 if (!(workspace[y*W+x] & bBLANK))
666 * If we discovered an existing loop above, we must now
667 * blank every square not part of it, and exit the main
670 if (loopclass != -1) {
671 #ifdef SOLVER_DIAGNOSTICS
672 printf("loop found in grid!\n");
674 for (y = 0; y < h; y++)
675 for (x = 0; x < w; x++)
676 if (dsf_canonify(dsf, y*w+x) != loopclass) {
677 if (workspace[(y*2+1)*W+(x*2+1)] & bBLANK) {
678 workspace[(y*2+1)*W+(x*2+1)] = bBLANK;
681 * This square is not part of the
682 * loop, but is known non-blank. We
685 #ifdef SOLVER_DIAGNOSTICS
686 printf("non-blank square (%d,%d) found outside"
700 /* Further deductions are considered 'tricky'. */
701 if (difficulty == DIFF_EASY) goto done_deductions;
704 * Now go through the workspace again and mark any edge
705 * which would cause a shortcut loop (i.e. would
706 * connect together two squares in the same equivalence
707 * class, and that equivalence class does not contain
708 * _all_ the known-non-blank squares currently in the
709 * grid) as disconnected. Also, mark any _square state_
710 * which would cause a shortcut loop as disconnected.
712 for (y = 1; y < H-1; y++)
713 for (x = 1; x < W-1; x++)
716 * (x,y) are the workspace coordinates of
717 * an edge field. Compute the normal-space
718 * coordinates of the squares it connects.
720 int ax = (x-1)/2, ay = (y-1)/2, ac = ay*w+ax;
721 int bx = x/2, by = y/2, bc = by*w+bx;
724 * If the edge is currently unknown, and
725 * sits between two squares in the same
726 * equivalence class, and the size of that
727 * class is less than nonblanks, then
728 * connecting this edge would be a shortcut
729 * loop and so we must not do so.
731 if (workspace[y*W+x] == 3) {
734 ae = dsf_canonify(dsf, ac);
735 be = dsf_canonify(dsf, bc);
739 * We have a loop. Is it a shortcut?
741 if (dsfsize[ae] < nonblanks) {
743 * Yes! Mark this edge disconnected.
745 workspace[y*W+x] = 2;
746 done_something = TRUE;
747 #ifdef SOLVER_DIAGNOSTICS
748 printf("edge (%d,%d)-(%d,%d) would create"
749 " a shortcut loop, hence must be"
750 " disconnected\n", x/2, y/2,
756 } else if ((y & x) & 1) {
758 * (x,y) are the workspace coordinates of a
759 * square field. Go through its possible
760 * (non-blank) states and see if any gives
761 * rise to a shortcut loop.
763 * This is slightly fiddly, because we have
764 * to check whether this square is already
765 * part of the same equivalence class as
766 * the things it's joining.
768 int ae = dsf_canonify(dsf, (y/2)*w+(x/2));
770 for (b = 2; b < 0xD; b++)
771 if (workspace[y*W+x] & (1<<b)) {
773 * Find the equivalence classes of
774 * the two squares this one would
775 * connect if it were in this
780 for (d = 1; d <= 8; d += d) if (b & d) {
781 int xx = x/2 + DX(d), yy = y/2 + DY(d);
782 int ee = dsf_canonify(dsf, yy*w+xx);
792 * This square state would form
793 * a loop on equivalence class
794 * e. Measure the size of that
795 * loop, and see if it's a
798 int loopsize = dsfsize[e];
800 loopsize++;/* add the square itself */
801 if (loopsize < nonblanks) {
803 * It is! Mark this square
806 workspace[y*W+x] &= ~(1<<b);
807 done_something = TRUE;
808 #ifdef SOLVER_DIAGNOSTICS
809 printf("square (%d,%d) would create a "
810 "shortcut loop in state %d, "
826 * If we reach here, there is nothing left we can do.
827 * Return 2 for ambiguous puzzle.
836 * If ret = 1 then we've successfully achieved a solution. This
837 * means that we expect every square to be nailed down to
838 * exactly one possibility. If this is the case, or if the caller
839 * asked for a partial solution anyway, transcribe those
840 * possibilities into the result array.
842 if (ret == 1 || partial) {
843 for (y = 0; y < h; y++) {
844 for (x = 0; x < w; x++) {
845 for (b = 0; b < 0xD; b++)
846 if (workspace[(2*y+1)*W+(2*x+1)] == (1<<b)) {
850 if (ret == 1) assert(b < 0xD); /* we should have had a break by now */
862 /* ----------------------------------------------------------------------
867 * We use the loop generator code from loopy, hard-coding to a square
868 * grid of the appropriate size. Knowing the grid layout and the tile
869 * size we can shrink that to our small grid and then make our line
870 * layout from the face colour info.
872 * We provide a bias function to the loop generator which tries to
873 * bias in favour of loops with more scope for Pearl black clues. This
874 * seems to improve the success rate of the puzzle generator, in that
875 * such loops have a better chance of being soluble with all valid
879 struct pearl_loopgen_bias_ctx {
881 * Our bias function counts the number of 'black clue' corners
882 * (i.e. corners adjacent to two straights) in both the
883 * BLACK/nonBLACK and WHITE/nonWHITE boundaries. In order to do
886 * - track the edges that are part of each of those loops
887 * - track the types of vertex in each loop (corner, straight,
889 * - track the current black-clue status of each vertex in each
892 * Each of these chunks of data is updated incrementally from the
893 * previous one, to avoid slowdown due to the bias function
894 * rescanning the whole grid every time it's called.
896 * So we need a lot of separate arrays, plus a tdq for each one,
897 * and we must repeat it all twice for the BLACK and WHITE
900 struct pearl_loopgen_bias_ctx_boundary {
901 int colour; /* FACE_WHITE or FACE_BLACK */
903 char *edges; /* is each edge part of the loop? */
906 char *vertextypes; /* bits 0-3 == outgoing edge bitmap;
907 * bit 4 set iff corner clue.
908 * Hence, 0 means non-vertex;
909 * nonzero but bit 4 zero = straight. */
910 int *neighbour[2]; /* indices of neighbour vertices in loop */
911 tdq *vertextypes_todo;
913 char *blackclues; /* is each vertex a black clue site? */
914 tdq *blackclues_todo;
915 } boundaries[2]; /* boundaries[0]=WHITE, [1]=BLACK */
917 char *faces; /* remember last-seen colour of each face */
924 int pearl_loopgen_bias(void *vctx, char *board, int face)
926 struct pearl_loopgen_bias_ctx *ctx = (struct pearl_loopgen_bias_ctx *)vctx;
928 int oldface, newface;
931 tdq_add(ctx->faces_todo, face);
932 while ((j = tdq_remove(ctx->faces_todo)) >= 0) {
933 oldface = ctx->faces[j];
934 ctx->faces[j] = newface = board[j];
935 for (i = 0; i < 2; i++) {
936 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i];
940 * If the face has changed either from or to colour c, we need
941 * to reprocess the edges for this boundary.
943 if (oldface == c || newface == c) {
944 grid_face *f = &g->faces[face];
945 for (k = 0; k < f->order; k++)
946 tdq_add(b->edges_todo, f->edges[k] - g->edges);
951 for (i = 0; i < 2; i++) {
952 struct pearl_loopgen_bias_ctx_boundary *b = &ctx->boundaries[i];
956 * Go through the to-do list of edges. For each edge, decide
957 * anew whether it's part of this boundary or not. Any edge
958 * that changes state has to have both its endpoints put on
959 * the vertextypes_todo list.
961 while ((j = tdq_remove(b->edges_todo)) >= 0) {
962 grid_edge *e = &g->edges[j];
963 int fc1 = e->face1 ? board[e->face1 - g->faces] : FACE_BLACK;
964 int fc2 = e->face2 ? board[e->face2 - g->faces] : FACE_BLACK;
965 int oldedge = b->edges[j];
966 int newedge = (fc1==c) ^ (fc2==c);
967 if (oldedge != newedge) {
968 b->edges[j] = newedge;
969 tdq_add(b->vertextypes_todo, e->dot1 - g->dots);
970 tdq_add(b->vertextypes_todo, e->dot2 - g->dots);
975 * Go through the to-do list of vertices whose types need
976 * refreshing. For each one, decide whether it's a corner, a
977 * straight, or a vertex not in the loop, and in the former
978 * two cases also work out the indices of its neighbour
979 * vertices along the loop. Any vertex that changes state must
980 * be put back on the to-do list for deciding if it's a black
981 * clue site, and so must its two new neighbours _and_ its two
984 while ((j = tdq_remove(b->vertextypes_todo)) >= 0) {
985 grid_dot *d = &g->dots[j];
986 int neighbours[2], type = 0, n = 0;
988 for (k = 0; k < d->order; k++) {
989 grid_edge *e = d->edges[k];
990 grid_dot *d2 = (e->dot1 == d ? e->dot2 : e->dot1);
991 /* dir == 0,1,2,3 for an edge going L,U,R,D */
992 int dir = (d->y == d2->y) + 2*(d->x+d->y > d2->x+d2->y);
993 int ei = e - g->edges;
996 neighbours[n] = d2 - g->dots;
1002 * Decide if it's a corner, and set the corner flag if so.
1004 if (type != 0 && type != 0x5 && type != 0xA)
1007 if (type != b->vertextypes[j]) {
1009 * Recompute old neighbours, if any.
1011 if (b->vertextypes[j]) {
1012 tdq_add(b->blackclues_todo, b->neighbour[0][j]);
1013 tdq_add(b->blackclues_todo, b->neighbour[1][j]);
1016 * Recompute this vertex.
1018 tdq_add(b->blackclues_todo, j);
1019 b->vertextypes[j] = type;
1021 * Recompute new neighbours, if any.
1023 if (b->vertextypes[j]) {
1024 b->neighbour[0][j] = neighbours[0];
1025 b->neighbour[1][j] = neighbours[1];
1026 tdq_add(b->blackclues_todo, b->neighbour[0][j]);
1027 tdq_add(b->blackclues_todo, b->neighbour[1][j]);
1033 * Go through the list of vertices which we must check to see
1034 * if they're black clue sites. Each one is a black clue site
1035 * iff it is a corner and its loop neighbours are non-corners.
1036 * Adjust the running total of black clues we've counted.
1038 while ((j = tdq_remove(b->blackclues_todo)) >= 0) {
1039 ctx->score -= b->blackclues[j];
1040 b->blackclues[j] = ((b->vertextypes[j] & 0x10) &&
1041 !((b->vertextypes[b->neighbour[0][j]] |
1042 b->vertextypes[b->neighbour[1][j]])
1044 ctx->score += b->blackclues[j];
1051 void pearl_loopgen(int w, int h, char *lines, random_state *rs)
1053 grid *g = grid_new(GRID_SQUARE, w-1, h-1, NULL);
1054 char *board = snewn(g->num_faces, char);
1055 int i, s = g->tilesize;
1056 struct pearl_loopgen_bias_ctx biasctx;
1058 memset(lines, 0, w*h);
1061 * Initialise the context for the bias function. Initially we fill
1062 * all the to-do lists, so that the first call will scan
1063 * everything; thereafter the lists stay empty so we make
1064 * incremental changes.
1067 biasctx.faces = snewn(g->num_faces, char);
1068 biasctx.faces_todo = tdq_new(g->num_faces);
1069 tdq_fill(biasctx.faces_todo);
1071 memset(biasctx.faces, FACE_GREY, g->num_faces);
1072 for (i = 0; i < 2; i++) {
1073 biasctx.boundaries[i].edges = snewn(g->num_edges, char);
1074 memset(biasctx.boundaries[i].edges, 0, g->num_edges);
1075 biasctx.boundaries[i].edges_todo = tdq_new(g->num_edges);
1076 tdq_fill(biasctx.boundaries[i].edges_todo);
1077 biasctx.boundaries[i].vertextypes = snewn(g->num_dots, char);
1078 memset(biasctx.boundaries[i].vertextypes, 0, g->num_dots);
1079 biasctx.boundaries[i].neighbour[0] = snewn(g->num_dots, int);
1080 biasctx.boundaries[i].neighbour[1] = snewn(g->num_dots, int);
1081 biasctx.boundaries[i].vertextypes_todo = tdq_new(g->num_dots);
1082 tdq_fill(biasctx.boundaries[i].vertextypes_todo);
1083 biasctx.boundaries[i].blackclues = snewn(g->num_dots, char);
1084 memset(biasctx.boundaries[i].blackclues, 0, g->num_dots);
1085 biasctx.boundaries[i].blackclues_todo = tdq_new(g->num_dots);
1086 tdq_fill(biasctx.boundaries[i].blackclues_todo);
1088 biasctx.boundaries[0].colour = FACE_WHITE;
1089 biasctx.boundaries[1].colour = FACE_BLACK;
1090 generate_loop(g, board, rs, pearl_loopgen_bias, &biasctx);
1091 sfree(biasctx.faces);
1092 tdq_free(biasctx.faces_todo);
1093 for (i = 0; i < 2; i++) {
1094 sfree(biasctx.boundaries[i].edges);
1095 tdq_free(biasctx.boundaries[i].edges_todo);
1096 sfree(biasctx.boundaries[i].vertextypes);
1097 sfree(biasctx.boundaries[i].neighbour[0]);
1098 sfree(biasctx.boundaries[i].neighbour[1]);
1099 tdq_free(biasctx.boundaries[i].vertextypes_todo);
1100 sfree(biasctx.boundaries[i].blackclues);
1101 tdq_free(biasctx.boundaries[i].blackclues_todo);
1104 for (i = 0; i < g->num_edges; i++) {
1105 grid_edge *e = g->edges + i;
1106 enum face_colour c1 = FACE_COLOUR(e->face1);
1107 enum face_colour c2 = FACE_COLOUR(e->face2);
1108 assert(c1 != FACE_GREY);
1109 assert(c2 != FACE_GREY);
1111 /* This grid edge is on the loop: lay line along it */
1112 int x1 = e->dot1->x/s, y1 = e->dot1->y/s;
1113 int x2 = e->dot2->x/s, y2 = e->dot2->y/s;
1115 /* (x1,y1) and (x2,y2) are now in our grid coords (0-w,0-h). */
1117 if (y1 > y2) SWAP(y1,y2);
1120 lines[y1*w+x1] |= D;
1121 lines[y2*w+x1] |= U;
1122 } else if (y1 == y2) {
1123 if (x1 > x2) SWAP(x1,x2);
1126 lines[y1*w+x1] |= R;
1127 lines[y1*w+x2] |= L;
1129 assert(!"grid with diagonal coords?!");
1136 #if defined LOOPGEN_DIAGNOSTICS && !defined GENERATION_DIAGNOSTICS
1137 printf("as returned:\n");
1138 for (y = 0; y < h; y++) {
1139 for (x = 0; x < w; x++) {
1140 int type = lines[y*w+x];
1142 if (type & L) *p++ = 'L';
1143 if (type & R) *p++ = 'R';
1144 if (type & U) *p++ = 'U';
1145 if (type & D) *p++ = 'D';
1155 static int new_clues(const game_params *params, random_state *rs,
1156 char *clues, char *grid)
1158 int w = params->w, h = params->h, diff = params->difficulty;
1159 int ngen = 0, x, y, d, ret, i;
1163 * Difficulty exception: 5x5 Tricky is not generable (the
1164 * generator will spin forever trying) and so we fudge it to Easy.
1166 if (w == 5 && h == 5 && diff > DIFF_EASY)
1171 pearl_loopgen(w, h, grid, rs);
1173 #ifdef GENERATION_DIAGNOSTICS
1174 printf("grid array:\n");
1175 for (y = 0; y < h; y++) {
1176 for (x = 0; x < w; x++) {
1177 int type = grid[y*w+x];
1179 if (type & L) *p++ = 'L';
1180 if (type & R) *p++ = 'R';
1181 if (type & U) *p++ = 'U';
1182 if (type & D) *p++ = 'D';
1192 * Set up the maximal clue array.
1194 for (y = 0; y < h; y++)
1195 for (x = 0; x < w; x++) {
1196 int type = grid[y*w+x];
1198 clues[y*w+x] = NOCLUE;
1200 if ((bLR|bUD) & (1 << type)) {
1202 * This is a straight; see if it's a viable
1203 * candidate for a straight clue. It qualifies if
1204 * at least one of the squares it connects to is a
1207 for (d = 1; d <= 8; d += d) if (type & d) {
1208 int xx = x + DX(d), yy = y + DY(d);
1209 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1210 if ((bLU|bLD|bRU|bRD) & (1 << grid[yy*w+xx]))
1213 if (d <= 8) /* we found one */
1214 clues[y*w+x] = STRAIGHT;
1215 } else if ((bLU|bLD|bRU|bRD) & (1 << type)) {
1217 * This is a corner; see if it's a viable candidate
1218 * for a corner clue. It qualifies if all the
1219 * squares it connects to are straights.
1221 for (d = 1; d <= 8; d += d) if (type & d) {
1222 int xx = x + DX(d), yy = y + DY(d);
1223 assert(xx >= 0 && xx < w && yy >= 0 && yy < h);
1224 if (!((bLR|bUD) & (1 << grid[yy*w+xx])))
1227 if (d > 8) /* we didn't find a counterexample */
1228 clues[y*w+x] = CORNER;
1232 #ifdef GENERATION_DIAGNOSTICS
1233 printf("clue array:\n");
1234 for (y = 0; y < h; y++) {
1235 for (x = 0; x < w; x++) {
1236 printf("%c", " *O"[(unsigned char)clues[y*w+x]]);
1243 if (!params->nosolve) {
1244 int *cluespace, *straights, *corners;
1245 int nstraights, ncorners, nstraightpos, ncornerpos;
1248 * See if we can solve the puzzle just like this.
1250 ret = pearl_solve(w, h, clues, grid, diff, FALSE);
1251 assert(ret > 0); /* shouldn't be inconsistent! */
1253 continue; /* go round and try again */
1256 * Check this puzzle isn't too easy.
1258 if (diff > DIFF_EASY) {
1259 ret = pearl_solve(w, h, clues, grid, diff-1, FALSE);
1262 continue; /* too easy: try again */
1266 * Now shuffle the grid points and gradually remove the
1267 * clues to find a minimal set which still leaves the
1270 * We preferentially attempt to remove whichever type of
1271 * clue is currently most numerous, to combat a general
1272 * tendency of plain random generation to bias in favour
1273 * of many white clues and few black.
1275 * 'nstraights' and 'ncorners' count the number of clues
1276 * of each type currently remaining in the grid;
1277 * 'nstraightpos' and 'ncornerpos' count the clues of each
1278 * type we have left to try to remove. (Clues which we
1279 * have tried and failed to remove are counted by the
1280 * former but not the latter.)
1282 cluespace = snewn(w*h, int);
1283 straights = cluespace;
1285 for (i = 0; i < w*h; i++)
1286 if (clues[i] == STRAIGHT)
1287 straights[nstraightpos++] = i;
1288 corners = straights + nstraightpos;
1290 for (i = 0; i < w*h; i++)
1291 if (clues[i] == STRAIGHT)
1292 corners[ncornerpos++] = i;
1293 nstraights = nstraightpos;
1294 ncorners = ncornerpos;
1296 shuffle(straights, nstraightpos, sizeof(*straights), rs);
1297 shuffle(corners, ncornerpos, sizeof(*corners), rs);
1298 while (nstraightpos > 0 || ncornerpos > 0) {
1303 * Decide which clue to try to remove next. If both
1304 * types are available, we choose whichever kind is
1305 * currently overrepresented; otherwise we take
1306 * whatever we can get.
1308 if (nstraightpos > 0 && ncornerpos > 0) {
1309 if (nstraights >= ncorners)
1310 cluepos = straights[--nstraightpos];
1312 cluepos = straights[--ncornerpos];
1314 if (nstraightpos > 0)
1315 cluepos = straights[--nstraightpos];
1317 cluepos = straights[--ncornerpos];
1323 clue = clues[y*w+x];
1324 clues[y*w+x] = 0; /* try removing this clue */
1326 ret = pearl_solve(w, h, clues, grid, diff, FALSE);
1329 clues[y*w+x] = clue; /* oops, put it back again */
1334 #ifdef FINISHED_PUZZLE
1335 printf("clue array:\n");
1336 for (y = 0; y < h; y++) {
1337 for (x = 0; x < w; x++) {
1338 printf("%c", " *O"[(unsigned char)clues[y*w+x]]);
1348 debug(("%d %dx%d loops before finished puzzle.\n", ngen, w, h));
1353 static char *new_game_desc(const game_params *params, random_state *rs,
1354 char **aux, int interactive)
1358 int w = params->w, h = params->h, i, j;
1360 grid = snewn(w*h, char);
1361 clues = snewn(w*h, char);
1363 new_clues(params, rs, clues, grid);
1365 desc = snewn(w * h + 1, char);
1366 for (i = j = 0; i < w*h; i++) {
1367 if (clues[i] == NOCLUE && j > 0 &&
1368 desc[j-1] >= 'a' && desc[j-1] < 'z')
1370 else if (clues[i] == NOCLUE)
1372 else if (clues[i] == CORNER)
1374 else if (clues[i] == STRAIGHT)
1379 *aux = snewn(w*h+1, char);
1380 for (i = 0; i < w*h; i++)
1381 (*aux)[i] = (grid[i] < 10) ? (grid[i] + '0') : (grid[i] + 'A' - 10);
1390 static const char *validate_desc(const game_params *params, const char *desc)
1393 const int totalsize = params->w * params->h;
1396 for (i = 0; desc[i]; i++) {
1397 if (desc[i] >= 'a' && desc[i] <= 'z')
1398 sizesofar += desc[i] - 'a' + 1;
1399 else if (desc[i] == 'B' || desc[i] == 'W')
1402 return "unrecognised character in string";
1405 if (sizesofar > totalsize)
1406 return "string too long";
1407 else if (sizesofar < totalsize)
1408 return "string too short";
1413 static game_state *new_game(midend *me, const game_params *params,
1416 game_state *state = snew(game_state);
1417 int i, j, sz = params->w*params->h;
1419 state->completed = state->used_solve = FALSE;
1420 state->shared = snew(struct shared_state);
1422 state->shared->w = params->w;
1423 state->shared->h = params->h;
1424 state->shared->sz = sz;
1425 state->shared->refcnt = 1;
1426 state->shared->clues = snewn(sz, char);
1427 for (i = j = 0; desc[i]; i++) {
1429 if (desc[i] >= 'a' && desc[i] <= 'z') {
1430 int n = desc[i] - 'a' + 1;
1431 assert(j + n <= sz);
1433 state->shared->clues[j++] = NOCLUE;
1434 } else if (desc[i] == 'B') {
1435 state->shared->clues[j++] = CORNER;
1436 } else if (desc[i] == 'W') {
1437 state->shared->clues[j++] = STRAIGHT;
1441 state->lines = snewn(sz, char);
1442 state->errors = snewn(sz, char);
1443 state->marks = snewn(sz, char);
1444 for (i = 0; i < sz; i++)
1445 state->lines[i] = state->errors[i] = state->marks[i] = BLANK;
1450 static game_state *dup_game(const game_state *state)
1452 game_state *ret = snew(game_state);
1453 int sz = state->shared->sz, i;
1455 ret->shared = state->shared;
1456 ret->completed = state->completed;
1457 ret->used_solve = state->used_solve;
1458 ++ret->shared->refcnt;
1460 ret->lines = snewn(sz, char);
1461 ret->errors = snewn(sz, char);
1462 ret->marks = snewn(sz, char);
1463 for (i = 0; i < sz; i++) {
1464 ret->lines[i] = state->lines[i];
1465 ret->errors[i] = state->errors[i];
1466 ret->marks[i] = state->marks[i];
1472 static void free_game(game_state *state)
1475 if (--state->shared->refcnt == 0) {
1476 sfree(state->shared->clues);
1477 sfree(state->shared);
1479 sfree(state->lines);
1480 sfree(state->errors);
1481 sfree(state->marks);
1485 static char nbits[16] = { 0, 1, 1, 2,
1489 #define NBITS(l) ( ((l) < 0 || (l) > 15) ? 4 : nbits[l] )
1491 #define ERROR_CLUE 16
1493 static void dsf_update_completion(game_state *state, int ax, int ay, char dir,
1496 int w = state->shared->w /*, h = state->shared->h */;
1497 int ac = ay*w+ax, bx, by, bc;
1499 if (!(state->lines[ac] & dir)) return; /* no link */
1500 bx = ax + DX(dir); by = ay + DY(dir);
1502 assert(INGRID(state, bx, by)); /* should not have a link off grid */
1505 assert(state->lines[bc] & F(dir)); /* should have reciprocal link */
1506 if (!(state->lines[bc] & F(dir))) return;
1508 dsf_merge(dsf, ac, bc);
1511 static void check_completion(game_state *state, int mark)
1513 int w = state->shared->w, h = state->shared->h, x, y, i, d;
1514 int had_error = FALSE;
1515 int *dsf, *component_state;
1516 int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
1517 enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
1520 for (i = 0; i < w*h; i++) {
1521 state->errors[i] = 0;
1525 #define ERROR(x,y,e) do { had_error = TRUE; if (mark) state->errors[(y)*w+(x)] |= (e); } while(0)
1528 * Analyse the solution into loops, paths and stranger things.
1529 * Basic strategy here is all the same as in Loopy - see the big
1530 * comment in loopy.c's check_completion() - and for exactly the
1531 * same reasons, since Loopy and Pearl have basically the same
1532 * form of expected solution.
1534 dsf = snew_dsf(w*h);
1536 /* Build the dsf. */
1537 for (x = 0; x < w; x++) {
1538 for (y = 0; y < h; y++) {
1539 dsf_update_completion(state, x, y, R, dsf);
1540 dsf_update_completion(state, x, y, D, dsf);
1544 /* Initialise a state variable for each connected component. */
1545 component_state = snewn(w*h, int);
1546 for (i = 0; i < w*h; i++) {
1547 if (dsf_canonify(dsf, i) == i)
1548 component_state[i] = COMP_LOOP;
1550 component_state[i] = COMP_NONE;
1554 * Classify components, and mark errors where a square has more
1555 * than two line segments.
1557 for (x = 0; x < w; x++) {
1558 for (y = 0; y < h; y++) {
1559 int type = state->lines[y*w+x];
1560 int degree = NBITS(type);
1561 int comp = dsf_canonify(dsf, y*w+x);
1564 component_state[comp] = COMP_SILLY;
1565 } else if (degree == 0) {
1566 component_state[comp] = COMP_EMPTY;
1567 } else if (degree == 1) {
1568 if (component_state[comp] != COMP_SILLY)
1569 component_state[comp] = COMP_PATH;
1574 /* Count the components, and find the largest sensible one. */
1575 nsilly = nloop = npath = 0;
1577 largest_comp = largest_size = -1;
1578 for (i = 0; i < w*h; i++) {
1579 if (component_state[i] == COMP_SILLY) {
1581 } else if (component_state[i] == COMP_PATH) {
1582 total_pathsize += dsf_size(dsf, i);
1584 } else if (component_state[i] == COMP_LOOP) {
1589 if ((this_size = dsf_size(dsf, i)) > largest_size) {
1591 largest_size = this_size;
1595 if (largest_size < total_pathsize) {
1596 largest_comp = -1; /* means the paths */
1597 largest_size = total_pathsize;
1600 if (nloop > 0 && nloop + npath > 1) {
1602 * If there are at least two sensible components including at
1603 * least one loop, highlight every sensible component that is
1604 * not the largest one.
1606 for (i = 0; i < w*h; i++) {
1607 int comp = dsf_canonify(dsf, i);
1608 if ((component_state[comp] == COMP_PATH &&
1609 -1 != largest_comp) ||
1610 (component_state[comp] == COMP_LOOP &&
1611 comp != largest_comp))
1612 ERROR(i%w, i/w, state->lines[i]);
1616 /* Now we've finished with the dsf and component states. The only
1617 * thing we'll need to remember later on is whether all edges were
1618 * part of a single loop, for which our counter variables
1619 * nsilly,nloop,npath are enough. */
1620 sfree(component_state);
1624 * Check that no clues are contradicted. This code is similar to
1625 * the code that sets up the maximal clue array for any given
1628 for (x = 0; x < w; x++) {
1629 for (y = 0; y < h; y++) {
1630 int type = state->lines[y*w+x];
1631 if (state->shared->clues[y*w+x] == CORNER) {
1632 /* Supposed to be a corner: will find a contradiction if
1633 * it actually contains a straight line, or if it touches any
1635 if ((bLR|bUD) & (1 << type)) {
1636 ERROR(x,y,ERROR_CLUE); /* actually straight */
1638 for (d = 1; d <= 8; d += d) if (type & d) {
1639 int xx = x + DX(d), yy = y + DY(d);
1640 if (!INGRID(state, xx, yy)) {
1641 ERROR(x,y,d); /* leads off grid */
1643 if ((bLU|bLD|bRU|bRD) & (1 << state->lines[yy*w+xx])) {
1644 ERROR(x,y,ERROR_CLUE); /* touches corner */
1648 } else if (state->shared->clues[y*w+x] == STRAIGHT) {
1649 /* Supposed to be straight: will find a contradiction if
1650 * it actually contains a corner, or if it only touches
1651 * straight lines. */
1652 if ((bLU|bLD|bRU|bRD) & (1 << type)) {
1653 ERROR(x,y,ERROR_CLUE); /* actually a corner */
1656 for (d = 1; d <= 8; d += d) if (type & d) {
1657 int xx = x + DX(d), yy = y + DY(d);
1658 if (!INGRID(state, xx, yy)) {
1659 ERROR(x,y,d); /* leads off grid */
1661 if ((bLR|bUD) & (1 << state->lines[yy*w+xx]))
1662 i++; /* a straight */
1665 if (i >= 2 && NBITS(type) >= 2) {
1666 ERROR(x,y,ERROR_CLUE); /* everything touched is straight */
1672 if (nloop == 1 && nsilly == 0 && npath == 0) {
1674 * If there's exactly one loop (so that the puzzle is at least
1675 * potentially complete), we need to ensure it hasn't left any
1676 * clue out completely.
1678 for (x = 0; x < w; x++) {
1679 for (y = 0; y < h; y++) {
1680 if (state->lines[y*w+x] == BLANK) {
1681 if (state->shared->clues[y*w+x] != NOCLUE) {
1682 /* the loop doesn't include this clue square! */
1683 ERROR(x, y, ERROR_CLUE);
1690 * But if not, then we're done!
1693 state->completed = TRUE;
1697 /* completion check:
1699 * - no clues must be contradicted (highlight clue itself in error if so)
1700 * - if there is a closed loop it must include every line segment laid
1701 * - if there's a smaller closed loop then highlight whole loop as error
1702 * - no square must have more than 2 lines radiating from centre point
1703 * (highlight all lines in that square as error if so)
1706 static char *solve_for_diff(game_state *state, char *old_lines, char *new_lines)
1708 int w = state->shared->w, h = state->shared->h, i;
1709 char *move = snewn(w*h*40, char), *p = move;
1712 for (i = 0; i < w*h; i++) {
1713 if (old_lines[i] != new_lines[i]) {
1714 p += sprintf(p, ";R%d,%d,%d", new_lines[i], i%w, i/w);
1718 move = sresize(move, p - move, char);
1723 static char *solve_game(const game_state *state, const game_state *currstate,
1724 const char *aux, const char **error)
1726 game_state *solved = dup_game(state);
1727 int i, ret, sz = state->shared->sz;
1731 for (i = 0; i < sz; i++) {
1732 if (aux[i] >= '0' && aux[i] <= '9')
1733 solved->lines[i] = aux[i] - '0';
1734 else if (aux[i] >= 'A' && aux[i] <= 'F')
1735 solved->lines[i] = aux[i] - 'A' + 10;
1737 *error = "invalid char in aux";
1744 /* Try to solve with present (half-solved) state first: if there's no
1745 * solution from there go back to original state. */
1746 ret = pearl_solve(currstate->shared->w, currstate->shared->h,
1747 currstate->shared->clues, solved->lines,
1750 ret = pearl_solve(state->shared->w, state->shared->h,
1751 state->shared->clues, solved->lines,
1757 *error = "Unable to find solution";
1760 move = solve_for_diff(solved, currstate->lines, solved->lines);
1768 static int game_can_format_as_text_now(const game_params *params)
1773 static char *game_text_format(const game_state *state)
1775 int w = state->shared->w, h = state->shared->h, cw = 4, ch = 2;
1776 int gw = cw*(w-1) + 2, gh = ch*(h-1) + 1, len = gw * gh, r, c, j;
1777 char *board = snewn(len + 1, char);
1780 memset(board, ' ', len);
1782 for (r = 0; r < h; ++r) {
1783 for (c = 0; c < w; ++c) {
1784 int i = r*w + c, cell = r*ch*gw + c*cw;
1785 board[cell] = "+BW"[(unsigned char)state->shared->clues[i]];
1786 if (c < w - 1 && (state->lines[i] & R || state->lines[i+1] & L))
1787 memset(board + cell + 1, '-', cw - 1);
1788 if (r < h - 1 && (state->lines[i] & D || state->lines[i+w] & U))
1789 for (j = 1; j < ch; ++j) board[cell + j*gw] = '|';
1790 if (c < w - 1 && (state->marks[i] & R || state->marks[i+1] & L))
1791 board[cell + cw/2] = 'x';
1792 if (r < h - 1 && (state->marks[i] & D || state->marks[i+w] & U))
1793 board[cell + (ch/2 * gw)] = 'x';
1796 for (j = 0; j < (r == h - 1 ? 1 : ch); ++j)
1797 board[r*ch*gw + (gw - 1) + j*gw] = '\n';
1805 int *dragcoords; /* list of (y*w+x) coords in drag so far */
1806 int ndragcoords; /* number of entries in dragcoords.
1807 * 0 = click but no drag yet. -1 = no drag at all */
1808 int clickx, clicky; /* pixel position of initial click */
1810 int curx, cury; /* grid position of keyboard cursor */
1811 int cursor_active; /* TRUE iff cursor is shown */
1814 static game_ui *new_ui(const game_state *state)
1816 game_ui *ui = snew(game_ui);
1817 int sz = state->shared->sz;
1819 ui->ndragcoords = -1;
1820 ui->dragcoords = snewn(sz, int);
1821 ui->cursor_active = FALSE;
1822 ui->curx = ui->cury = 0;
1827 static void free_ui(game_ui *ui)
1829 sfree(ui->dragcoords);
1833 static char *encode_ui(const game_ui *ui)
1838 static void decode_ui(game_ui *ui, const char *encoding)
1842 static void game_changed_state(game_ui *ui, const game_state *oldstate,
1843 const game_state *newstate)
1847 #define PREFERRED_TILE_SIZE 31
1848 #define HALFSZ (ds->halfsz)
1849 #define TILE_SIZE (ds->halfsz*2 + 1)
1851 #define BORDER ((get_gui_style() == GUI_LOOPY) ? (TILE_SIZE/8) : (TILE_SIZE/2))
1853 #define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
1855 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
1856 #define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 )
1857 #define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) )
1859 #define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */
1860 #define DS_DSHIFT 8 /* R/U/L/D shift, for drag-in-progress flags */
1861 #define DS_MSHIFT 12 /* shift for no-line mark */
1863 #define DS_ERROR_CLUE (1 << 20)
1864 #define DS_FLASH (1 << 21)
1865 #define DS_CURSOR (1 << 22)
1867 enum { GUI_MASYU, GUI_LOOPY };
1869 static int get_gui_style(void)
1871 static int gui_style = -1;
1873 if (gui_style == -1) {
1874 char *env = getenv("PEARL_GUI_LOOPY");
1875 if (env && (env[0] == 'y' || env[0] == 'Y'))
1876 gui_style = GUI_LOOPY;
1878 gui_style = GUI_MASYU;
1883 struct game_drawstate {
1888 unsigned int *lflags; /* size w*h */
1890 char *draglines; /* size w*h; lines flipped by current drag */
1893 static void update_ui_drag(const game_state *state, game_ui *ui,
1896 int /* sz = state->shared->sz, */ w = state->shared->w;
1900 if (!INGRID(state, gx, gy))
1901 return; /* square is outside grid */
1903 if (ui->ndragcoords < 0)
1904 return; /* drag not in progress anyway */
1908 lastpos = ui->dragcoords[ui->ndragcoords > 0 ? ui->ndragcoords-1 : 0];
1910 return; /* same square as last visited one */
1912 /* Drag confirmed, if it wasn't already. */
1913 if (ui->ndragcoords == 0)
1914 ui->ndragcoords = 1;
1917 * Dragging the mouse into a square that's already been visited by
1918 * the drag path so far has the effect of truncating the path back
1919 * to that square, so a player can back out part of an uncommitted
1920 * drag without having to let go of the mouse.
1922 for (i = 0; i < ui->ndragcoords; i++)
1923 if (pos == ui->dragcoords[i]) {
1924 ui->ndragcoords = i+1;
1929 * Otherwise, dragging the mouse into a square that's a rook-move
1930 * away from the last one on the path extends the path.
1932 oy = ui->dragcoords[ui->ndragcoords-1] / w;
1933 ox = ui->dragcoords[ui->ndragcoords-1] % w;
1934 if (ox == gx || oy == gy) {
1935 int dx = (gx < ox ? -1 : gx > ox ? +1 : 0);
1936 int dy = (gy < oy ? -1 : gy > oy ? +1 : 0);
1937 int dir = (dy>0 ? D : dy<0 ? U : dx>0 ? R : L);
1938 while (ox != gx || oy != gy) {
1940 * If the drag attempts to cross a 'no line here' mark,
1941 * stop there. We physically don't allow the user to drag
1944 if (state->marks[oy*w+ox] & dir)
1948 ui->dragcoords[ui->ndragcoords++] = oy * w + ox;
1953 * Failing that, we do nothing at all: if the user has dragged
1954 * diagonally across the board, they'll just have to return the
1955 * mouse to the last known position and do whatever they meant to
1956 * do again, more slowly and clearly.
1961 * Routine shared between interpret_move and game_redraw to work out
1962 * the intended effect of a drag path on the grid.
1964 * Call it in a loop, like this:
1966 * int clearing = TRUE;
1967 * for (i = 0; i < ui->ndragcoords - 1; i++) {
1968 * int sx, sy, dx, dy, dir, oldstate, newstate;
1969 * interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
1970 * &dir, &oldstate, &newstate);
1972 * [do whatever is needed to handle the fact that the drag
1973 * wants the edge from sx,sy to dx,dy (heading in direction
1974 * 'dir' at the sx,sy end) to be changed from state oldstate
1975 * to state newstate, each of which equals either 0 or dir]
1978 static void interpret_ui_drag(const game_state *state, const game_ui *ui,
1979 int *clearing, int i, int *sx, int *sy,
1980 int *dx, int *dy, int *dir,
1981 int *oldstate, int *newstate)
1983 int w = state->shared->w;
1984 int sp = ui->dragcoords[i], dp = ui->dragcoords[i+1];
1989 *dir = (*dy>*sy ? D : *dy<*sy ? U : *dx>*sx ? R : L);
1990 *oldstate = state->lines[sp] & *dir;
1993 * The edge we've dragged over was previously
1994 * present. Set it to absent, unless we've already
1995 * stopped doing that.
1997 *newstate = *clearing ? 0 : *dir;
2000 * The edge we've dragged over was previously
2001 * absent. Set it to present, and cancel the
2002 * 'clearing' flag so that all subsequent edges in
2003 * the drag are set rather than cleared.
2010 static char *mark_in_direction(const game_state *state, int x, int y, int dir,
2011 int primary, char *buf)
2013 int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */;
2014 int x2 = x + DX(dir);
2015 int y2 = y + DY(dir);
2018 char ch = primary ? 'F' : 'M', *other;
2020 if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return UI_UPDATE;
2022 /* disallow laying a mark over a line, or vice versa. */
2023 other = primary ? state->marks : state->lines;
2024 if (other[y*w+x] & dir || other[y2*w+x2] & dir2) return UI_UPDATE;
2026 sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2);
2030 #define KEY_DIRECTION(btn) (\
2031 (btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\
2032 (btn) == CURSOR_LEFT ? L : R)
2034 static char *interpret_move(const game_state *state, game_ui *ui,
2035 const game_drawstate *ds,
2036 int x, int y, int button)
2038 int w = state->shared->w, h = state->shared->h /*, sz = state->shared->sz */;
2039 int gx = FROMCOORD(x), gy = FROMCOORD(y), i;
2040 int release = FALSE;
2043 int shift = button & MOD_SHFT, control = button & MOD_CTRL;
2044 button &= ~MOD_MASK;
2046 if (IS_MOUSE_DOWN(button)) {
2047 ui->cursor_active = FALSE;
2049 if (!INGRID(state, gx, gy)) {
2050 ui->ndragcoords = -1;
2054 ui->clickx = x; ui->clicky = y;
2055 ui->dragcoords[0] = gy * w + gx;
2056 ui->ndragcoords = 0; /* will be 1 once drag is confirmed */
2061 if (button == LEFT_DRAG && ui->ndragcoords >= 0) {
2062 update_ui_drag(state, ui, gx, gy);
2066 if (IS_MOUSE_RELEASE(button)) release = TRUE;
2068 if (IS_CURSOR_MOVE(button)) {
2069 if (!ui->cursor_active) {
2070 ui->cursor_active = TRUE;
2071 } else if (control | shift) {
2073 if (ui->ndragcoords > 0) return NULL;
2074 ui->ndragcoords = -1;
2075 move = mark_in_direction(state, ui->curx, ui->cury,
2076 KEY_DIRECTION(button), control, tmpbuf);
2077 if (control && !shift && *move)
2078 move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE);
2081 move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE);
2082 if (ui->ndragcoords >= 0)
2083 update_ui_drag(state, ui, ui->curx, ui->cury);
2088 if (IS_CURSOR_SELECT(button)) {
2089 if (!ui->cursor_active) {
2090 ui->cursor_active = TRUE;
2092 } else if (button == CURSOR_SELECT) {
2093 if (ui->ndragcoords == -1) {
2094 ui->ndragcoords = 0;
2095 ui->dragcoords[0] = ui->cury * w + ui->curx;
2096 ui->clickx = CENTERED_COORD(ui->curx);
2097 ui->clicky = CENTERED_COORD(ui->cury);
2099 } else release = TRUE;
2100 } else if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) {
2101 ui->ndragcoords = -1;
2106 if (button == 27 || button == '\b') {
2107 ui->ndragcoords = -1;
2112 if (ui->ndragcoords > 0) {
2113 /* End of a drag: process the cached line data. */
2114 int buflen = 0, bufsize = 256, tmplen;
2116 const char *sep = "";
2117 int clearing = TRUE;
2119 for (i = 0; i < ui->ndragcoords - 1; i++) {
2120 int sx, sy, dx, dy, dir, oldstate, newstate;
2121 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2122 &dir, &oldstate, &newstate);
2124 if (oldstate != newstate) {
2125 if (!buf) buf = snewn(bufsize, char);
2126 tmplen = sprintf(tmpbuf, "%sF%d,%d,%d;F%d,%d,%d", sep,
2127 dir, sx, sy, F(dir), dx, dy);
2128 if (buflen + tmplen >= bufsize) {
2129 bufsize = (buflen + tmplen) * 5 / 4 + 256;
2130 buf = sresize(buf, bufsize, char);
2132 strcpy(buf + buflen, tmpbuf);
2138 ui->ndragcoords = -1;
2140 return buf ? buf : UI_UPDATE;
2141 } else if (ui->ndragcoords == 0) {
2142 /* Click (or tiny drag). Work out which edge we were
2146 ui->ndragcoords = -1;
2149 * We process clicks based on the mouse-down location,
2150 * because that's more natural for a user to carefully
2151 * control than the mouse-up.
2158 cx = CENTERED_COORD(gx);
2159 cy = CENTERED_COORD(gy);
2161 if (!INGRID(state, gx, gy)) return UI_UPDATE;
2163 if (max(abs(x-cx),abs(y-cy)) < TILE_SIZE/4) {
2164 /* TODO closer to centre of grid: process as a cell click not an edge click. */
2169 if (abs(x-cx) < abs(y-cy)) {
2170 /* Closest to top/bottom edge. */
2171 direction = (y < cy) ? U : D;
2173 /* Closest to left/right edge. */
2174 direction = (x < cx) ? L : R;
2176 return mark_in_direction(state, gx, gy, direction,
2177 (button == LEFT_RELEASE), tmpbuf);
2182 if (button == 'H' || button == 'h')
2188 static game_state *execute_move(const game_state *state, const char *move)
2190 int w = state->shared->w, h = state->shared->h;
2193 game_state *ret = dup_game(state);
2195 debug(("move: %s\n", move));
2200 ret->used_solve = TRUE;
2202 } else if (c == 'L' || c == 'N' || c == 'R' || c == 'F' || c == 'M') {
2203 /* 'line' or 'noline' or 'replace' or 'flip' or 'mark' */
2205 if (sscanf(move, "%d,%d,%d%n", &l, &x, &y, &n) != 3)
2207 if (!INGRID(state, x, y)) goto badmove;
2208 if (l < 0 || l > 15) goto badmove;
2211 ret->lines[y*w + x] |= (char)l;
2213 ret->lines[y*w + x] &= ~((char)l);
2214 else if (c == 'R') {
2215 ret->lines[y*w + x] = (char)l;
2216 ret->marks[y*w + x] &= ~((char)l); /* erase marks too */
2217 } else if (c == 'F')
2218 ret->lines[y*w + x] ^= (char)l;
2220 ret->marks[y*w + x] ^= (char)l;
2223 * If we ended up trying to lay a line _over_ a mark,
2224 * that's a failed move: interpret_move() should have
2225 * ensured we never received a move string like that in
2228 if ((ret->lines[y*w + x] & (char)l) &&
2229 (ret->marks[y*w + x] & (char)l))
2233 } else if (strcmp(move, "H") == 0) {
2234 pearl_solve(ret->shared->w, ret->shared->h,
2235 ret->shared->clues, ret->lines, DIFFCOUNT, TRUE);
2236 for (n = 0; n < w*h; n++)
2237 ret->marks[n] &= ~ret->lines[n]; /* erase marks too */
2248 check_completion(ret, TRUE);
2257 /* ----------------------------------------------------------------------
2261 #define FLASH_TIME 0.5F
2263 static void game_compute_size(const game_params *params, int tilesize,
2266 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2267 struct { int halfsz; } ads, *ds = &ads;
2268 ads.halfsz = (tilesize-1)/2;
2270 *x = (params->w) * TILE_SIZE + 2 * BORDER;
2271 *y = (params->h) * TILE_SIZE + 2 * BORDER;
2274 static void game_set_size(drawing *dr, game_drawstate *ds,
2275 const game_params *params, int tilesize)
2277 ds->halfsz = (tilesize-1)/2;
2280 static float *game_colours(frontend *fe, int *ncolours)
2282 float *ret = snewn(3 * NCOLOURS, float);
2285 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
2287 for (i = 0; i < 3; i++) {
2288 ret[COL_BLACK * 3 + i] = 0.0F;
2289 ret[COL_WHITE * 3 + i] = 1.0F;
2290 ret[COL_GRID * 3 + i] = 0.4F;
2293 ret[COL_ERROR * 3 + 0] = 1.0F;
2294 ret[COL_ERROR * 3 + 1] = 0.0F;
2295 ret[COL_ERROR * 3 + 2] = 0.0F;
2297 ret[COL_DRAGON * 3 + 0] = 0.0F;
2298 ret[COL_DRAGON * 3 + 1] = 0.0F;
2299 ret[COL_DRAGON * 3 + 2] = 1.0F;
2301 ret[COL_DRAGOFF * 3 + 0] = 0.8F;
2302 ret[COL_DRAGOFF * 3 + 1] = 0.8F;
2303 ret[COL_DRAGOFF * 3 + 2] = 1.0F;
2305 ret[COL_FLASH * 3 + 0] = 1.0F;
2306 ret[COL_FLASH * 3 + 1] = 1.0F;
2307 ret[COL_FLASH * 3 + 2] = 1.0F;
2309 *ncolours = NCOLOURS;
2314 static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
2316 struct game_drawstate *ds = snew(struct game_drawstate);
2320 ds->started = FALSE;
2322 ds->w = state->shared->w;
2323 ds->h = state->shared->h;
2324 ds->sz = state->shared->sz;
2325 ds->lflags = snewn(ds->sz, unsigned int);
2326 for (i = 0; i < ds->sz; i++)
2329 ds->draglines = snewn(ds->sz, char);
2334 static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2336 sfree(ds->draglines);
2341 static void draw_lines_specific(drawing *dr, game_drawstate *ds,
2342 int x, int y, unsigned int lflags,
2343 unsigned int shift, int c)
2345 int ox = COORD(x), oy = COORD(y);
2346 int t2 = HALFSZ, t16 = HALFSZ/4;
2347 int cx = ox + t2, cy = oy + t2;
2350 /* Draw each of the four directions, where laid (or error, or drag, etc.) */
2351 for (d = 1; d < 16; d *= 2) {
2352 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2353 int xnudge = abs(t16 * DX(C(d))), ynudge = abs(t16 * DY(C(d)));
2355 if ((lflags >> shift) & d) {
2356 int lx = cx + ((xoff < 0) ? xoff : 0) - xnudge;
2357 int ly = cy + ((yoff < 0) ? yoff : 0) - ynudge;
2359 if (c == COL_DRAGOFF && !(lflags & d))
2361 if (c == COL_DRAGON && (lflags & d))
2364 draw_rect(dr, lx, ly,
2365 abs(xoff)+2*xnudge+1,
2366 abs(yoff)+2*ynudge+1, c);
2368 draw_rect(dr, cx - t16, cy - t16, 2*t16+1, 2*t16+1, c);
2373 static void draw_square(drawing *dr, game_drawstate *ds, const game_ui *ui,
2374 int x, int y, unsigned int lflags, char clue)
2376 int ox = COORD(x), oy = COORD(y);
2377 int t2 = HALFSZ, t16 = HALFSZ/4;
2378 int cx = ox + t2, cy = oy + t2;
2383 /* Clip to the grid square. */
2384 clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2386 /* Clear the square. */
2387 draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE,
2388 (lflags & DS_CURSOR) ?
2389 COL_CURSOR_BACKGROUND : COL_BACKGROUND);
2392 if (get_gui_style() == GUI_LOOPY) {
2393 /* Draw small dot, underneath any lines. */
2394 draw_circle(dr, cx, cy, t16, COL_GRID, COL_GRID);
2396 /* Draw outline of grid square */
2397 draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID);
2398 draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID);
2401 /* Draw grid: either thin gridlines, or no-line marks.
2402 * We draw these first because the thick laid lines should be on top. */
2403 for (d = 1; d < 16; d *= 2) {
2404 int xoff = t2 * DX(d), yoff = t2 * DY(d);
2406 if ((x == 0 && d == L) ||
2407 (y == 0 && d == U) ||
2408 (x == ds->w-1 && d == R) ||
2409 (y == ds->h-1 && d == D))
2410 continue; /* no gridlines out to the border. */
2412 if ((lflags >> DS_MSHIFT) & d) {
2413 /* either a no-line mark ... */
2414 int mx = cx + xoff, my = cy + yoff, msz = t16;
2416 draw_line(dr, mx-msz, my-msz, mx+msz, my+msz, COL_BLACK);
2417 draw_line(dr, mx-msz, my+msz, mx+msz, my-msz, COL_BLACK);
2419 if (get_gui_style() == GUI_LOOPY) {
2420 /* draw grid lines connecting centre of cells */
2421 draw_line(dr, cx, cy, cx+xoff, cy+yoff, COL_GRID);
2426 /* Draw each of the four directions, where laid (or error, or drag, etc.)
2427 * Order is important here, specifically for the eventual colours of the
2428 * exposed end caps. */
2429 draw_lines_specific(dr, ds, x, y, lflags, 0,
2430 (lflags & DS_FLASH ? COL_FLASH : COL_BLACK));
2431 draw_lines_specific(dr, ds, x, y, lflags, DS_ESHIFT, COL_ERROR);
2432 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGOFF);
2433 draw_lines_specific(dr, ds, x, y, lflags, DS_DSHIFT, COL_DRAGON);
2435 /* Draw a clue, if present */
2436 if (clue != NOCLUE) {
2437 int c = (lflags & DS_FLASH) ? COL_FLASH :
2438 (clue == STRAIGHT) ? COL_WHITE : COL_BLACK;
2440 if (lflags & DS_ERROR_CLUE) /* draw a bigger 'error' clue circle. */
2441 draw_circle(dr, cx, cy, TILE_SIZE*3/8, COL_ERROR, COL_ERROR);
2443 draw_circle(dr, cx, cy, TILE_SIZE/4, c, COL_BLACK);
2447 draw_update(dr, ox, oy, TILE_SIZE, TILE_SIZE);
2450 static void game_redraw(drawing *dr, game_drawstate *ds,
2451 const game_state *oldstate, const game_state *state,
2452 int dir, const game_ui *ui,
2453 float animtime, float flashtime)
2455 int w = state->shared->w, h = state->shared->h, sz = state->shared->sz;
2456 int x, y, force = 0, flashing = 0;
2460 * The initial contents of the window are not guaranteed and
2461 * can vary with front ends. To be on the safe side, all games
2462 * should start by drawing a big background-colour rectangle
2463 * covering the whole window.
2465 draw_rect(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER,
2468 if (get_gui_style() == GUI_MASYU) {
2470 * Smaller black rectangle which is the main grid.
2472 draw_rect(dr, BORDER - BORDER_WIDTH, BORDER - BORDER_WIDTH,
2473 w*TILE_SIZE + 2*BORDER_WIDTH + 1,
2474 h*TILE_SIZE + 2*BORDER_WIDTH + 1,
2478 draw_update(dr, 0, 0, w*TILE_SIZE + 2*BORDER, h*TILE_SIZE + 2*BORDER);
2484 if (flashtime > 0 &&
2485 (flashtime <= FLASH_TIME/3 ||
2486 flashtime >= FLASH_TIME*2/3))
2487 flashing = DS_FLASH;
2489 memset(ds->draglines, 0, sz);
2490 if (ui->ndragcoords > 0) {
2491 int i, clearing = TRUE;
2492 for (i = 0; i < ui->ndragcoords - 1; i++) {
2493 int sx, sy, dx, dy, dir, oldstate, newstate;
2494 interpret_ui_drag(state, ui, &clearing, i, &sx, &sy, &dx, &dy,
2495 &dir, &oldstate, &newstate);
2496 ds->draglines[sy*w+sx] ^= (oldstate ^ newstate);
2497 ds->draglines[dy*w+dx] ^= (F(oldstate) ^ F(newstate));
2501 for (x = 0; x < w; x++) {
2502 for (y = 0; y < h; y++) {
2503 unsigned int f = (unsigned int)state->lines[y*w+x];
2504 unsigned int eline = (unsigned int)(state->errors[y*w+x] & (R|U|L|D));
2506 f |= eline << DS_ESHIFT;
2507 f |= ((unsigned int)ds->draglines[y*w+x]) << DS_DSHIFT;
2508 f |= ((unsigned int)state->marks[y*w+x]) << DS_MSHIFT;
2510 if (state->errors[y*w+x] & ERROR_CLUE)
2515 if (ui->cursor_active && x == ui->curx && y == ui->cury)
2518 if (f != ds->lflags[y*w+x] || force) {
2519 ds->lflags[y*w+x] = f;
2520 draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]);
2526 static float game_anim_length(const game_state *oldstate,
2527 const game_state *newstate, int dir, game_ui *ui)
2532 static float game_flash_length(const game_state *oldstate,
2533 const game_state *newstate, int dir, game_ui *ui)
2535 if (!oldstate->completed && newstate->completed &&
2536 !oldstate->used_solve && !newstate->used_solve)
2542 static int game_status(const game_state *state)
2544 return state->completed ? +1 : 0;
2547 static int game_timing_state(const game_state *state, game_ui *ui)
2552 static void game_print_size(const game_params *params, float *x, float *y)
2557 * I'll use 6mm squares by default.
2559 game_compute_size(params, 600, &pw, &ph);
2564 static void game_print(drawing *dr, const game_state *state, int tilesize)
2566 int w = state->shared->w, h = state->shared->h, x, y;
2567 int black = print_mono_colour(dr, 0);
2568 int white = print_mono_colour(dr, 1);
2570 /* No GUI_LOOPY here: only use the familiar masyu style. */
2572 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2573 game_drawstate *ds = game_new_drawstate(dr, state);
2574 game_set_size(dr, ds, NULL, tilesize);
2576 /* Draw grid outlines (black). */
2577 for (x = 0; x <= w; x++)
2578 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
2579 for (y = 0; y <= h; y++)
2580 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
2582 for (x = 0; x < w; x++) {
2583 for (y = 0; y < h; y++) {
2584 int cx = COORD(x) + HALFSZ, cy = COORD(y) + HALFSZ;
2585 int clue = state->shared->clues[y*w+x];
2587 draw_lines_specific(dr, ds, x, y, state->lines[y*w+x], 0, black);
2589 if (clue != NOCLUE) {
2590 int c = (clue == CORNER) ? black : white;
2591 draw_circle(dr, cx, cy, TILE_SIZE/4, c, black);
2596 game_free_drawstate(dr, ds);
2600 #define thegame pearl
2603 const struct game thegame = {
2604 "Pearl", "games.pearl", "pearl",
2606 game_fetch_preset, NULL,
2611 TRUE, game_configure, custom_params,
2619 TRUE, game_can_format_as_text_now, game_text_format,
2627 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2630 game_free_drawstate,
2635 TRUE, FALSE, game_print_size, game_print,
2636 FALSE, /* wants_statusbar */
2637 FALSE, game_timing_state,
2641 #ifdef STANDALONE_SOLVER
2646 const char *quis = NULL;
2648 static void usage(FILE *out) {
2649 fprintf(out, "usage: %s <params>\n", quis);
2652 static void pnum(int n, int ntot, const char *desc)
2654 printf("%2.1f%% (%d) %s", (double)n*100.0 / (double)ntot, n, desc);
2657 static void start_soak(game_params *p, random_state *rs, int nsecs)
2659 time_t tt_start, tt_now, tt_last;
2660 int n = 0, nsolved = 0, nimpossible = 0, ret;
2663 tt_start = tt_last = time(NULL);
2665 /* Currently this generates puzzles of any difficulty (trying to solve it
2666 * on the maximum difficulty level and not checking it's not too easy). */
2667 printf("Soak-testing a %dx%d grid (any difficulty)", p->w, p->h);
2668 if (nsecs > 0) printf(" for %d seconds", nsecs);
2673 grid = snewn(p->w*p->h, char);
2674 clues = snewn(p->w*p->h, char);
2677 n += new_clues(p, rs, clues, grid); /* should be 1, with nosolve */
2679 ret = pearl_solve(p->w, p->h, clues, grid, DIFF_TRICKY, FALSE);
2680 if (ret <= 0) nimpossible++;
2681 if (ret == 1) nsolved++;
2683 tt_now = time(NULL);
2684 if (tt_now > tt_last) {
2687 printf("%d total, %3.1f/s, ",
2688 n, (double)n / ((double)tt_now - tt_start));
2689 pnum(nsolved, n, "solved"); printf(", ");
2690 printf("%3.1f/s", (double)nsolved / ((double)tt_now - tt_start));
2691 if (nimpossible > 0)
2692 pnum(nimpossible, n, "impossible");
2695 if (nsecs > 0 && (tt_now - tt_start) > nsecs) {
2705 int main(int argc, const char *argv[])
2707 game_params *p = NULL;
2708 random_state *rs = NULL;
2709 time_t seed = time(NULL);
2713 setvbuf(stdout, NULL, _IONBF, 0);
2717 while (--argc > 0) {
2718 char *p = (char*)(*++argv);
2719 if (!strcmp(p, "-e") || !strcmp(p, "--seed")) {
2720 seed = atoi(*++argv);
2722 } else if (*p == '-') {
2723 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2731 rs = random_new((void*)&seed, sizeof(time_t));
2732 p = default_params();
2735 if (strchr(id, ':')) {
2736 fprintf(stderr, "soak takes params only.\n");
2740 decode_params(p, id);
2741 err = validate_params(p, 1);
2743 fprintf(stderr, "%s: %s", argv[0], err);
2747 start_soak(p, rs, 0); /* run forever */
2751 for (i = 5; i <= 12; i++) {
2753 start_soak(p, rs, 5);
2766 /* vim: set shiftwidth=4 tabstop=8: */