2 * signpost.c: implementation of the janko game 'arrow path'
14 #define PREFERRED_TILE_SIZE 48
15 #define TILE_SIZE (ds->tilesize)
16 #define BLITTER_SIZE TILE_SIZE
17 #define BORDER (TILE_SIZE / 2)
19 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
20 #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
22 #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
24 #define FLASH_SPIN 0.7F
26 #define NBACKGROUNDS 16
29 COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
,
30 COL_GRID
, COL_CURSOR
, COL_ERROR
, COL_DRAG_ORIGIN
,
31 COL_ARROW
, COL_ARROW_BG_DIM
,
32 COL_NUMBER
, COL_NUMBER_SET
, COL_NUMBER_SET_MID
,
33 COL_B0
, /* background colours */
34 COL_M0
= COL_B0
+ 1*NBACKGROUNDS
, /* mid arrow colours */
35 COL_D0
= COL_B0
+ 2*NBACKGROUNDS
, /* dim arrow colours */
36 COL_X0
= COL_B0
+ 3*NBACKGROUNDS
, /* dim arrow colours */
37 NCOLOURS
= COL_B0
+ 4*NBACKGROUNDS
42 int force_corner_start
;
45 enum { DIR_N
= 0, DIR_NE
, DIR_E
, DIR_SE
, DIR_S
, DIR_SW
, DIR_W
, DIR_NW
, DIR_MAX
};
46 static const char *dirstrings
[8] = { "N ", "NE", "E ", "SE", "S ", "SW", "W ", "NW" };
48 static const int dxs
[DIR_MAX
] = { 0, 1, 1, 1, 0, -1, -1, -1 };
49 static const int dys
[DIR_MAX
] = { -1, -1, 0, 1, 1, 1, 0, -1 };
51 #define DIR_OPPOSITE(d) ((d+4)%8)
55 int completed
, used_solve
, impossible
;
56 int *dirs
; /* direction enums, size n */
57 int *nums
; /* numbers, size n */
58 unsigned int *flags
; /* flags, size n */
59 int *next
, *prev
; /* links to other cell indexes, size n (-1 absent) */
60 int *dsf
; /* connects regions with a dsf. */
61 int *numsi
; /* for each number, which index is it in? (-1 absent) */
64 #define FLAG_IMMUTABLE 1
67 /* --- Generally useful functions --- */
69 #define ISREALNUM(state, num) ((num) > 0 && (num) <= (state)->n)
71 static int whichdir(int fromx
, int fromy
, int tox
, int toy
)
78 if (dx
&& dy
&& abs(dx
) != abs(dy
)) return -1;
80 if (dx
) dx
= dx
/ abs(dx
); /* limit to (-1, 0, 1) */
81 if (dy
) dy
= dy
/ abs(dy
); /* ditto */
83 for (i
= 0; i
< DIR_MAX
; i
++) {
84 if (dx
== dxs
[i
] && dy
== dys
[i
]) return i
;
89 static int whichdiri(game_state
*state
, int fromi
, int toi
)
92 return whichdir(fromi
%w
, fromi
/w
, toi
%w
, toi
/w
);
95 static int ispointing(game_state
*state
, int fromx
, int fromy
, int tox
, int toy
)
97 int w
= state
->w
, dir
= state
->dirs
[fromy
*w
+fromx
];
99 /* (by convention) squares do not point to themselves. */
100 if (fromx
== tox
&& fromy
== toy
) return 0;
102 /* the final number points to nothing. */
103 if (state
->nums
[fromy
*w
+ fromx
] == state
->n
) return 0;
106 if (!INGRID(state
, fromx
, fromy
)) return 0;
107 if (fromx
== tox
&& fromy
== toy
) return 1;
108 fromx
+= dxs
[dir
]; fromy
+= dys
[dir
];
110 return 0; /* not reached */
113 static int ispointingi(game_state
*state
, int fromi
, int toi
)
116 return ispointing(state
, fromi
%w
, fromi
/w
, toi
%w
, toi
/w
);
119 /* Taking the number 'num', work out the gap between it and the next
120 * available number up or down (depending on d). Return 1 if the region
121 * at (x,y) will fit in that gap, or 0 otherwise. */
122 static int move_couldfit(game_state
*state
, int num
, int d
, int x
, int y
)
124 int n
, gap
, i
= y
*state
->w
+x
, sz
;
127 /* The 'gap' is the number of missing numbers in the grid between
128 * our number and the next one in the sequence (up or down), or
129 * the end of the sequence (if we happen not to have 1/n present) */
130 for (n
= num
+ d
, gap
= 0;
131 ISREALNUM(state
, n
) && state
->numsi
[n
] == -1;
132 n
+= d
, gap
++) ; /* empty loop */
135 /* no gap, so the only allowable move is that that directly
136 * links the two numbers. */
138 return (n
== num
+d
) ?
0 : 1;
140 if (state
->prev
[i
] == -1 && state
->next
[i
] == -1)
141 return 1; /* single unconnected square, always OK */
143 sz
= dsf_size(state
->dsf
, i
);
144 return (sz
> gap
) ?
0 : 1;
147 static int isvalidmove(game_state
*state
, int clever
,
148 int fromx
, int fromy
, int tox
, int toy
)
150 int w
= state
->w
, from
= fromy
*w
+fromx
, to
= toy
*w
+tox
;
153 if (!INGRID(state
, fromx
, fromy
) || !INGRID(state
, tox
, toy
))
156 /* can only move where we point */
157 if (!ispointing(state
, fromx
, fromy
, tox
, toy
))
160 nfrom
= state
->nums
[from
]; nto
= state
->nums
[to
];
162 /* can't move _from_ the preset final number, or _to_ the preset 1. */
163 if (((nfrom
== state
->n
) && (state
->flags
[from
] & FLAG_IMMUTABLE
)) ||
164 ((nto
== 1) && (state
->flags
[to
] & FLAG_IMMUTABLE
)))
167 /* can't create a new connection between cells in the same region
168 * as that would create a loop. */
169 if (dsf_canonify(state
->dsf
, from
) == dsf_canonify(state
->dsf
, to
))
172 /* if both cells are actual numbers, can't drag if we're not
173 * one digit apart. */
174 if (ISREALNUM(state
, nfrom
) && ISREALNUM(state
, nto
)) {
177 } else if (clever
&& ISREALNUM(state
, nfrom
)) {
178 if (!move_couldfit(state
, nfrom
, +1, tox
, toy
))
180 } else if (clever
&& ISREALNUM(state
, nto
)) {
181 if (!move_couldfit(state
, nto
, -1, fromx
, fromy
))
188 static void makelink(game_state
*state
, int from
, int to
)
190 if (state
->next
[from
] != -1)
191 state
->prev
[state
->next
[from
]] = -1;
192 state
->next
[from
] = to
;
194 if (state
->prev
[to
] != -1)
195 state
->next
[state
->prev
[to
]] = -1;
196 state
->prev
[to
] = from
;
199 static int game_can_format_as_text_now(game_params
*params
)
201 if (params
->w
* params
->h
>= 100) return 0;
205 static char *game_text_format(game_state
*state
)
207 int len
= state
->h
* 2 * (4*state
->w
+ 1) + state
->h
+ 2;
208 int x
, y
, i
, num
, n
, set
;
211 p
= ret
= snewn(len
, char);
213 for (y
= 0; y
< state
->h
; y
++) {
214 for (x
= 0; x
< state
->h
; x
++) {
216 *p
++ = dirstrings
[state
->dirs
[i
]][0];
217 *p
++ = dirstrings
[state
->dirs
[i
]][1];
218 *p
++ = (state
->flags
[i
] & FLAG_IMMUTABLE
) ?
'I' : ' ';
222 for (x
= 0; x
< state
->h
; x
++) {
224 num
= state
->nums
[i
];
230 n
= num
% (state
->n
+1);
231 set
= num
/ (state
->n
+1);
233 assert(n
<= 99); /* two digits only! */
238 *p
++ = (n
>= 10) ?
('0' + (n
/10)) : ' ';
254 static void debug_state(const char *desc
, game_state
*state
)
258 if (state
->n
>= 100) {
259 debug(("[ no game_text_format for this size ]"));
262 dbg
= game_text_format(state
);
263 debug(("%s\n%s", desc
, dbg
));
269 static void strip_nums(game_state
*state
) {
271 for (i
= 0; i
< state
->n
; i
++) {
272 if (!(state
->flags
[i
] & FLAG_IMMUTABLE
))
275 memset(state
->next
, -1, state
->n
*sizeof(int));
276 memset(state
->prev
, -1, state
->n
*sizeof(int));
277 memset(state
->numsi
, -1, (state
->n
+1)*sizeof(int));
278 dsf_init(state
->dsf
, state
->n
);
281 static int check_nums(game_state
*orig
, game_state
*copy
, int only_immutable
)
284 assert(copy
->n
== orig
->n
);
285 for (i
= 0; i
< copy
->n
; i
++) {
286 if (only_immutable
&& !copy
->flags
[i
] & FLAG_IMMUTABLE
) continue;
287 assert(copy
->nums
[i
] >= 0);
288 assert(copy
->nums
[i
] <= copy
->n
);
289 if (copy
->nums
[i
] != orig
->nums
[i
]) {
290 debug(("check_nums: (%d,%d) copy=%d, orig=%d.",
291 i
%orig
->w
, i
/orig
->w
, copy
->nums
[i
], orig
->nums
[i
]));
298 /* --- Game parameter/presets functions --- */
300 static game_params
*default_params(void)
302 game_params
*ret
= snew(game_params
);
304 ret
->force_corner_start
= 1;
309 static const struct game_params signpost_presets
[] = {
318 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
323 if (i
< 0 || i
>= lenof(signpost_presets
))
326 ret
= default_params();
327 *ret
= signpost_presets
[i
];
330 sprintf(buf
, "%dx%d%s", ret
->w
, ret
->h
,
331 ret
->force_corner_start ?
"" : ", free ends");
337 static void free_params(game_params
*params
)
342 static game_params
*dup_params(game_params
*params
)
344 game_params
*ret
= snew(game_params
);
345 *ret
= *params
; /* structure copy */
349 static void decode_params(game_params
*ret
, char const *string
)
351 ret
->w
= ret
->h
= atoi(string
);
352 while (*string
&& isdigit((unsigned char)*string
)) string
++;
353 if (*string
== 'x') {
355 ret
->h
= atoi(string
);
356 while (*string
&& isdigit((unsigned char)*string
)) string
++;
358 ret
->force_corner_start
= 0;
359 if (*string
== 'c') {
361 ret
->force_corner_start
= 1;
366 static char *encode_params(game_params
*params
, int full
)
371 sprintf(data
, "%dx%d%s", params
->w
, params
->h
,
372 params
->force_corner_start ?
"c" : "");
374 sprintf(data
, "%dx%d", params
->w
, params
->h
);
379 static config_item
*game_configure(game_params
*params
)
384 ret
= snewn(4, config_item
);
386 ret
[0].name
= "Width";
387 ret
[0].type
= C_STRING
;
388 sprintf(buf
, "%d", params
->w
);
389 ret
[0].sval
= dupstr(buf
);
392 ret
[1].name
= "Height";
393 ret
[1].type
= C_STRING
;
394 sprintf(buf
, "%d", params
->h
);
395 ret
[1].sval
= dupstr(buf
);
398 ret
[2].name
= "Start and end in corners";
399 ret
[2].type
= C_BOOLEAN
;
401 ret
[2].ival
= params
->force_corner_start
;
411 static game_params
*custom_params(config_item
*cfg
)
413 game_params
*ret
= snew(game_params
);
415 ret
->w
= atoi(cfg
[0].sval
);
416 ret
->h
= atoi(cfg
[1].sval
);
417 ret
->force_corner_start
= cfg
[2].ival
;
422 static char *validate_params(game_params
*params
, int full
)
424 if (params
->w
< 2 || params
->h
< 2)
425 return "Width and height must both be at least two";
430 /* --- Game description string generation and unpicking --- */
432 static void blank_game_into(game_state
*state
)
434 memset(state
->dirs
, 0, state
->n
*sizeof(int));
435 memset(state
->nums
, 0, state
->n
*sizeof(int));
436 memset(state
->flags
, 0, state
->n
*sizeof(unsigned int));
437 memset(state
->next
, -1, state
->n
*sizeof(int));
438 memset(state
->prev
, -1, state
->n
*sizeof(int));
439 memset(state
->numsi
, -1, (state
->n
+1)*sizeof(int));
442 static game_state
*blank_game(int w
, int h
)
444 game_state
*state
= snew(game_state
);
446 memset(state
, 0, sizeof(game_state
));
451 state
->dirs
= snewn(state
->n
, int);
452 state
->nums
= snewn(state
->n
, int);
453 state
->flags
= snewn(state
->n
, unsigned int);
454 state
->next
= snewn(state
->n
, int);
455 state
->prev
= snewn(state
->n
, int);
456 state
->dsf
= snew_dsf(state
->n
);
457 state
->numsi
= snewn(state
->n
+1, int);
459 blank_game_into(state
);
464 static void dup_game_to(game_state
*to
, game_state
*from
)
466 to
->completed
= from
->completed
;
467 to
->used_solve
= from
->used_solve
;
468 to
->impossible
= from
->impossible
;
470 memcpy(to
->dirs
, from
->dirs
, to
->n
*sizeof(int));
471 memcpy(to
->flags
, from
->flags
, to
->n
*sizeof(unsigned int));
472 memcpy(to
->nums
, from
->nums
, to
->n
*sizeof(int));
474 memcpy(to
->next
, from
->next
, to
->n
*sizeof(int));
475 memcpy(to
->prev
, from
->prev
, to
->n
*sizeof(int));
477 memcpy(to
->dsf
, from
->dsf
, to
->n
*sizeof(int));
478 memcpy(to
->numsi
, from
->numsi
, (to
->n
+1)*sizeof(int));
481 static game_state
*dup_game(game_state
*state
)
483 game_state
*ret
= blank_game(state
->w
, state
->h
);
484 dup_game_to(ret
, state
);
488 static void free_game(game_state
*state
)
500 static void unpick_desc(game_params
*params
, char *desc
,
501 game_state
**sout
, char **mout
)
503 game_state
*state
= blank_game(params
->w
, params
->h
);
509 msg
= "Game description longer than expected";
515 num
= (num
*10) + (int)(c
-'0');
516 if (num
> state
->n
) {
517 msg
= "Number too large";
520 } else if ((c
-'a') >= 0 && (c
-'a') < DIR_MAX
) {
521 state
->nums
[i
] = num
;
522 state
->flags
[i
] = num ? FLAG_IMMUTABLE
: 0;
525 state
->dirs
[i
] = c
- 'a';
528 msg
= "Game description shorter than expected";
531 msg
= "Game description contains unexpected characters";
537 msg
= "Game description shorter than expected";
542 if (msg
) { /* sth went wrong. */
543 if (mout
) *mout
= msg
;
546 if (mout
) *mout
= NULL
;
547 if (sout
) *sout
= state
;
548 else free_game(state
);
552 static char *generate_desc(game_state
*state
, int issolve
)
557 ret
= NULL
; retlen
= 0;
559 ret
= sresize(ret
, 2, char);
560 ret
[0] = 'S'; ret
[1] = '\0';
563 for (i
= 0; i
< state
->n
; i
++) {
565 k
= sprintf(buf
, "%d%c", state
->nums
[i
], (int)(state
->dirs
[i
]+'a'));
567 k
= sprintf(buf
, "%c", (int)(state
->dirs
[i
]+'a'));
568 ret
= sresize(ret
, retlen
+ k
+ 1, char);
569 strcpy(ret
+ retlen
, buf
);
575 /* --- Game generation --- */
577 /* Fills in preallocated arrays ai (indices) and ad (directions)
578 * showing all non-numbered cells adjacent to index i, returns length */
579 /* This function has been somewhat optimised... */
580 static int cell_adj(game_state
*state
, int i
, int *ai
, int *ad
)
582 int n
= 0, a
, x
, y
, sx
, sy
, dx
, dy
, newi
;
583 int w
= state
->w
, h
= state
->h
;
585 sx
= i
% w
; sy
= i
/ w
;
587 for (a
= 0; a
< DIR_MAX
; a
++) {
589 dx
= dxs
[a
]; dy
= dys
[a
];
592 if (x
< 0 || y
< 0 || x
>= w
|| y
>= h
) break;
595 if (state
->nums
[newi
] == 0) {
605 static int new_game_fill(game_state
*state
, random_state
*rs
,
606 int headi
, int taili
)
608 int nfilled
, an
, ret
= 0, j
;
611 aidx
= snewn(state
->n
, int);
612 adir
= snewn(state
->n
, int);
614 debug(("new_game_fill: headi=%d, taili=%d.", headi
, taili
));
616 memset(state
->nums
, 0, state
->n
*sizeof(int));
618 state
->nums
[headi
] = 1;
619 state
->nums
[taili
] = state
->n
;
621 state
->dirs
[taili
] = 0;
624 while (nfilled
< state
->n
) {
625 /* Try and expand _from_ headi; keep going if there's only one
627 an
= cell_adj(state
, headi
, aidx
, adir
);
629 if (an
== 0) goto done
;
630 j
= random_upto(rs
, an
);
631 state
->dirs
[headi
] = adir
[j
];
632 state
->nums
[aidx
[j
]] = state
->nums
[headi
] + 1;
635 an
= cell_adj(state
, headi
, aidx
, adir
);
638 /* Try and expand _to_ taili; keep going if there's only one
640 an
= cell_adj(state
, taili
, aidx
, adir
);
642 if (an
== 0) goto done
;
643 j
= random_upto(rs
, an
);
644 state
->dirs
[aidx
[j
]] = DIR_OPPOSITE(adir
[j
]);
645 state
->nums
[aidx
[j
]] = state
->nums
[taili
] - 1;
648 an
= cell_adj(state
, taili
, aidx
, adir
);
651 /* If we get here we have headi and taili set but unconnected
652 * by direction: we need to set headi's direction so as to point
654 state
->dirs
[headi
] = whichdiri(state
, headi
, taili
);
656 /* it could happen that our last two weren't in line; if that's the
657 * case, we have to start again. */
658 if (state
->dirs
[headi
] != -1) ret
= 1;
666 /* Better generator: with the 'generate, sprinkle numbers, solve,
667 * repeat' algorithm we're _never_ generating anything greater than
668 * 6x6, and spending all of our time in new_game_fill (and very little
671 * So, new generator steps:
672 * generate the grid, at random (same as now). Numbers 1 and N get
673 immutable flag immediately.
674 * squirrel that away for the solved state.
676 * (solve:) Try and solve it.
677 * If we solved it, we're done:
678 * generate the description from current immutable numbers,
679 * free stuff that needs freeing,
680 * return description + solved state.
681 * If we didn't solve it:
682 * count #tiles in state we've made deductions about.
684 * randomise a scratch array.
685 * for each index in scratch (in turn):
686 * if the cell isn't empty, continue (through scratch array)
687 * set number + immutable in state.
688 * try and solve state.
689 * if we've solved it, we're done.
690 * otherwise, count #tiles. If it's more than we had before:
691 * good, break from this loop and re-randomise.
692 * otherwise (number didn't help):
693 * remove number and try next in scratch array.
694 * if we've got to the end of the scratch array, no luck:
695 free everything we need to, and go back to regenerate the grid.
698 static int solve_state(game_state
*state
);
700 static void debug_desc(const char *what
, game_state
*state
)
704 char *desc
= generate_desc(state
, 0);
705 debug(("%s game state: %dx%d:%s", what
, state
->w
, state
->h
, desc
));
711 /* Expects a fully-numbered game_state on input, and makes sure
712 * FLAG_IMMUTABLE is only set on those numbers we need to solve
713 * (as for a real new-game); returns 1 if it managed
714 * this (such that it could solve it), or 0 if not. */
715 static int new_game_strip(game_state
*state
, random_state
*rs
)
717 int *scratch
, i
, j
, ret
= 1;
718 game_state
*copy
= dup_game(state
);
720 debug(("new_game_strip."));
723 debug_desc("Stripped", copy
);
725 if (solve_state(copy
) > 0) {
726 debug(("new_game_strip: soluble immediately after strip."));
731 scratch
= snewn(state
->n
, int);
732 for (i
= 0; i
< state
->n
; i
++) scratch
[i
] = i
;
733 shuffle(scratch
, state
->n
, sizeof(int), rs
);
735 /* This is scungy. It might just be quick enough.
736 * It goes through, adding set numbers in empty squares
737 * until either we run out of empty squares (in the one
738 * we're half-solving) or else we solve it properly.
739 * NB that we run the entire solver each time, which
740 * strips the grid beforehand; we will save time if we
742 for (i
= 0; i
< state
->n
; i
++) {
744 if (copy
->nums
[j
] > 0 && copy
->nums
[j
] <= state
->n
)
745 continue; /* already solved to a real number here. */
746 assert(state
->nums
[j
] <= state
->n
);
747 debug(("new_game_strip: testing add IMMUTABLE number %d at square (%d,%d).",
748 state
->nums
[j
], j
%state
->w
, j
/state
->w
));
749 copy
->nums
[j
] = state
->nums
[j
];
750 copy
->flags
[j
] |= FLAG_IMMUTABLE
;
751 state
->flags
[j
] |= FLAG_IMMUTABLE
;
752 debug_state("Copy of state: ", copy
);
753 if (solve_state(copy
) > 0) goto solved
;
754 assert(check_nums(state
, copy
, 1));
760 debug(("new_game_strip: now solved."));
761 /* Since we added basically at random, try now to remove numbers
762 * and see if we can still solve it; if we can (still), really
763 * remove the number. Make sure we don't remove the anchor numbers
765 for (i
= 0; i
< state
->n
; i
++) {
767 if ((state
->flags
[j
] & FLAG_IMMUTABLE
) &&
768 (state
->nums
[j
] != 1 && state
->nums
[j
] != state
->n
)) {
769 debug(("new_game_strip: testing remove IMMUTABLE number %d at square (%d,%d).",
770 state
->nums
[j
], j
%state
->w
, j
/state
->w
));
771 state
->flags
[j
] &= ~FLAG_IMMUTABLE
;
772 dup_game_to(copy
, state
);
774 if (solve_state(copy
) > 0) {
775 assert(check_nums(state
, copy
, 0));
776 debug(("new_game_strip: OK, removing number"));
778 assert(state
->nums
[j
] <= state
->n
);
779 debug(("new_game_strip: cannot solve, putting IMMUTABLE back."));
780 copy
->nums
[j
] = state
->nums
[j
];
781 state
->flags
[j
] |= FLAG_IMMUTABLE
;
787 debug(("new_game_strip: %ssuccessful.", ret ?
"" : "not "));
793 static char *new_game_desc(game_params
*params
, random_state
*rs
,
794 char **aux
, int interactive
)
796 game_state
*state
= blank_game(params
->w
, params
->h
);
801 blank_game_into(state
);
803 /* keep trying until we fill successfully. */
805 if (params
->force_corner_start
) {
810 headi
= random_upto(rs
, state
->n
);
811 taili
= random_upto(rs
, state
->n
);
812 } while (headi
== taili
);
814 } while (!new_game_fill(state
, rs
, headi
, taili
));
816 debug_state("Filled game:", state
);
818 assert(state
->nums
[headi
] <= state
->n
);
819 assert(state
->nums
[taili
] <= state
->n
);
821 state
->flags
[headi
] |= FLAG_IMMUTABLE
;
822 state
->flags
[taili
] |= FLAG_IMMUTABLE
;
824 /* This will have filled in directions and _all_ numbers.
825 * Store the game definition for this, as the solved-state. */
826 if (!new_game_strip(state
, rs
)) {
831 game_state
*tosolve
= dup_game(state
);
832 assert(solve_state(tosolve
) > 0);
835 ret
= generate_desc(state
, 0);
840 static char *validate_desc(game_params
*params
, char *desc
)
844 unpick_desc(params
, desc
, NULL
, &ret
);
848 /* --- Linked-list and numbers array --- */
850 /* Assuming numbers are always up-to-date, there are only four possibilities
851 * for regions changing after a single valid move:
853 * 1) two differently-coloured regions being combined (the resulting colouring
854 * should be based on the larger of the two regions)
855 * 2) a numbered region having a single number added to the start (the
856 * region's colour will remain, and the numbers will shift by 1)
857 * 3) a numbered region having a single number added to the end (the
858 * region's colour and numbering remains as-is)
859 * 4) two unnumbered squares being joined (will pick the smallest unused set
860 * of colours to use for the new region).
862 * There should never be any complications with regions containing 3 colours
863 * being combined, since two of those colours should have been merged on a
866 * Most of the complications are in ensuring we don't accidentally set two
867 * regions with the same colour (e.g. if a region was split). If this happens
868 * we always try and give the largest original portion the original colour.
871 #define COLOUR(a) ((a) / (state->n+1))
872 #define START(c) ((c) * (state->n+1))
875 int i
; /* position */
876 int sz
; /* size of region */
877 int start
; /* region start number preferred, or 0 if !preference */
878 int preference
; /* 0 if we have no preference (and should just pick one) */
882 static void head_number(game_state
*state
, int i
, struct head_meta
*head
)
884 int off
= 0, ss
, j
= i
, c
, n
, sz
;
886 /* Insist we really were passed the head of a chain. */
887 assert(state
->prev
[i
] == -1 && state
->next
[i
] != -1);
890 head
->sz
= dsf_size(state
->dsf
, i
);
893 /* Search through this chain looking for real numbers, checking that
894 * they match up (if there are more than one). */
895 head
->preference
= 0;
897 if (state
->flags
[j
] & FLAG_IMMUTABLE
) {
898 ss
= state
->nums
[j
] - off
;
899 if (!head
->preference
) {
901 head
->preference
= 1;
902 head
->why
= "contains cell with immutable number";
903 } else if (head
->start
!= ss
) {
904 debug(("head_number: chain with non-sequential numbers!"));
905 state
->impossible
= 1;
910 assert(j
!= i
); /* we have created a loop, obviously wrong */
912 if (head
->preference
) goto done
;
914 if (state
->nums
[i
] == 0 && state
->nums
[state
->next
[i
]] > state
->n
) {
915 /* (probably) empty cell onto the head of a coloured region:
916 * make sure we start at a 0 offset. */
917 head
->start
= START(COLOUR(state
->nums
[state
->next
[i
]]));
918 head
->preference
= 1;
919 head
->why
= "adding blank cell to head of numbered region";
920 } else if (state
->nums
[i
] <= state
->n
) {
921 /* if we're 0 we're probably just blank -- but even if we're a
922 * (real) numbered region, we don't have an immutable number
923 * in it (any more) otherwise it'd have been caught above, so
924 * reassign the colour. */
926 head
->preference
= 0;
927 head
->why
= "lowest available colour group";
929 c
= COLOUR(state
->nums
[i
]);
931 sz
= dsf_size(state
->dsf
, i
);
933 while (state
->next
[j
] != -1) {
935 if (state
->nums
[j
] == 0 && state
->next
[j
] == -1) {
936 head
->start
= START(c
);
937 head
->preference
= 1;
938 head
->why
= "adding blank cell to end of numbered region";
941 if (COLOUR(state
->nums
[j
]) == c
)
944 int start_alternate
= START(COLOUR(state
->nums
[j
]));
946 head
->start
= start_alternate
;
947 head
->preference
= 1;
948 head
->why
= "joining two coloured regions, swapping to larger colour";
950 head
->start
= START(c
);
951 head
->preference
= 1;
952 head
->why
= "joining two coloured regions, taking largest";
957 /* If we got here then we may have split a region into
958 * two; make sure we don't assign a colour we've already used. */
960 /* not convinced this shouldn't be an assertion failure here. */
962 head
->preference
= 0;
964 head
->start
= START(c
);
965 head
->preference
= 1;
967 head
->why
= "got to end of coloured region";
971 assert(head
->why
!= NULL
);
972 if (head
->preference
)
973 debug(("Chain at (%d,%d) numbered for preference at %d (colour %d): %s.",
974 head
->i
%state
->w
, head
->i
/state
->w
,
975 head
->start
, COLOUR(head
->start
), head
->why
));
977 debug(("Chain at (%d,%d) using next available colour: %s.",
978 head
->i
%state
->w
, head
->i
/state
->w
,
983 static void debug_numbers(game_state
*state
)
987 for (i
= 0; i
< state
->n
; i
++) {
988 debug(("(%d,%d) --> (%d,%d) --> (%d,%d)",
989 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]%w
,
990 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]/w
,
992 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]%w
,
993 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]/w
));
999 static void connect_numbers(game_state
*state
)
1003 dsf_init(state
->dsf
, state
->n
);
1004 for (i
= 0; i
< state
->n
; i
++) {
1005 if (state
->next
[i
] != -1) {
1006 assert(state
->prev
[state
->next
[i
]] == i
);
1007 di
= dsf_canonify(state
->dsf
, i
);
1008 dni
= dsf_canonify(state
->dsf
, state
->next
[i
]);
1010 debug(("connect_numbers: chain forms a loop."));
1011 state
->impossible
= 1;
1013 dsf_merge(state
->dsf
, di
, dni
);
1018 static int compare_heads(const void *a
, const void *b
)
1020 struct head_meta
*ha
= (struct head_meta
*)a
;
1021 struct head_meta
*hb
= (struct head_meta
*)b
;
1023 /* Heads with preferred colours first... */
1024 if (ha
->preference
&& !hb
->preference
) return -1;
1025 if (hb
->preference
&& !ha
->preference
) return 1;
1027 /* ...then heads with low colours first... */
1028 if (ha
->start
< hb
->start
) return -1;
1029 if (ha
->start
> hb
->start
) return 1;
1031 /* ... then large regions first... */
1032 if (ha
->sz
> hb
->sz
) return -1;
1033 if (ha
->sz
< hb
->sz
) return 1;
1035 /* ... then position. */
1036 if (ha
->i
> hb
->i
) return -1;
1037 if (ha
->i
< hb
->i
) return 1;
1042 static int lowest_start(game_state
*state
, struct head_meta
*heads
, int nheads
)
1046 /* NB start at 1: colour 0 is real numbers */
1047 for (c
= 1; c
< state
->n
; c
++) {
1048 for (n
= 0; n
< nheads
; n
++) {
1049 if (COLOUR(heads
[n
].start
) == c
)
1056 assert(!"No available colours!");
1060 static void update_numbers(game_state
*state
)
1062 int i
, j
, n
, nnum
, nheads
;
1063 struct head_meta
*heads
= snewn(state
->n
, struct head_meta
);
1065 for (n
= 0; n
< state
->n
; n
++)
1066 state
->numsi
[n
] = -1;
1068 for (i
= 0; i
< state
->n
; i
++) {
1069 if (state
->flags
[i
] & FLAG_IMMUTABLE
) {
1070 assert(state
->nums
[i
] > 0);
1071 assert(state
->nums
[i
] <= state
->n
);
1072 state
->numsi
[state
->nums
[i
]] = i
;
1074 else if (state
->prev
[i
] == -1 && state
->next
[i
] == -1)
1077 connect_numbers(state
);
1079 /* Construct an array of the heads of all current regions, together
1080 * with their preferred colours. */
1082 for (i
= 0; i
< state
->n
; i
++) {
1083 /* Look for a cell that is the start of a chain
1084 * (has a next but no prev). */
1085 if (state
->prev
[i
] != -1 || state
->next
[i
] == -1) continue;
1087 head_number(state
, i
, &heads
[nheads
++]);
1091 * - heads with preferred colours first, then
1092 * - heads with low colours first, then
1093 * - large regions first
1095 qsort(heads
, nheads
, sizeof(struct head_meta
), compare_heads
);
1097 /* Remove duplicate-coloured regions. */
1098 for (n
= nheads
-1; n
>= 0; n
--) { /* order is important! */
1099 if ((n
!= 0) && (heads
[n
].start
== heads
[n
-1].start
)) {
1100 /* We have a duplicate-coloured region: since we're
1101 * sorted in size order and this is not the first
1102 * of its colour it's not the largest: recolour it. */
1103 heads
[n
].start
= START(lowest_start(state
, heads
, nheads
));
1104 heads
[n
].preference
= -1; /* '-1' means 'was duplicate' */
1106 else if (!heads
[n
].preference
) {
1107 assert(heads
[n
].start
== 0);
1108 heads
[n
].start
= START(lowest_start(state
, heads
, nheads
));
1112 debug(("Region colouring after duplicate removal:"));
1114 for (n
= 0; n
< nheads
; n
++) {
1115 debug((" Chain at (%d,%d) sz %d numbered at %d (colour %d): %s%s",
1116 heads
[n
].i
% state
->w
, heads
[n
].i
/ state
->w
, heads
[n
].sz
,
1117 heads
[n
].start
, COLOUR(heads
[n
].start
), heads
[n
].why
,
1118 heads
[n
].preference
== 0 ?
" (next available)" :
1119 heads
[n
].preference
< 0 ?
" (duplicate, next available)" : ""));
1121 nnum
= heads
[n
].start
;
1124 if (!(state
->flags
[j
] & FLAG_IMMUTABLE
)) {
1125 if (nnum
> 0 && nnum
<= state
->n
)
1126 state
->numsi
[nnum
] = j
;
1127 state
->nums
[j
] = nnum
;
1131 assert(j
!= heads
[n
].i
); /* loop?! */
1134 /*debug_numbers(state);*/
1138 static int check_completion(game_state
*state
, int mark_errors
)
1140 int n
, j
, k
, error
= 0, complete
;
1142 /* NB This only marks errors that are possible to perpetrate with
1143 * the current UI in interpret_move. Things like forming loops in
1144 * linked sections and having numbers not add up should be forbidden
1145 * by the code elsewhere, so we don't bother marking those (because
1146 * it would add lots of tricky drawing code for very little gain). */
1148 for (j
= 0; j
< state
->n
; j
++)
1149 state
->flags
[j
] &= ~FLAG_ERROR
;
1152 /* Search for repeated numbers. */
1153 for (j
= 0; j
< state
->n
; j
++) {
1154 if (state
->nums
[j
] > 0 && state
->nums
[j
] <= state
->n
) {
1155 for (k
= j
+1; k
< state
->n
; k
++) {
1156 if (state
->nums
[k
] == state
->nums
[j
]) {
1158 state
->flags
[j
] |= FLAG_ERROR
;
1159 state
->flags
[k
] |= FLAG_ERROR
;
1167 /* Search and mark numbers n not pointing to n+1; if any numbers
1168 * are missing we know we've not completed. */
1170 for (n
= 1; n
< state
->n
; n
++) {
1171 if (state
->numsi
[n
] == -1 || state
->numsi
[n
+1] == -1)
1173 else if (!ispointingi(state
, state
->numsi
[n
], state
->numsi
[n
+1])) {
1175 state
->flags
[state
->numsi
[n
]] |= FLAG_ERROR
;
1176 state
->flags
[state
->numsi
[n
+1]] |= FLAG_ERROR
;
1180 /* make sure the link is explicitly made here; for instance, this
1181 * is nice if the user drags from 2 out (making 3) and a 4 is also
1182 * visible; this ensures that the link from 3 to 4 is also made. */
1184 makelink(state
, state
->numsi
[n
], state
->numsi
[n
+1]);
1188 /* Search and mark numbers less than 0, or 0 with links. */
1189 for (n
= 1; n
< state
->n
; n
++) {
1190 if ((state
->nums
[n
] < 0) ||
1191 (state
->nums
[n
] == 0 &&
1192 (state
->next
[n
] != -1 || state
->prev
[n
] != -1))) {
1195 state
->flags
[n
] |= FLAG_ERROR
;
1199 if (error
) return 0;
1202 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
1204 game_state
*state
= NULL
;
1206 unpick_desc(params
, desc
, &state
, NULL
);
1207 if (!state
) assert(!"new_game failed to unpick");
1209 update_numbers(state
);
1210 check_completion(state
, 1); /* update any auto-links */
1215 /* --- Solver --- */
1217 /* If a tile has a single tile it can link _to_, or there's only a single
1218 * location that can link to a given tile, fill that link in. */
1219 static int solve_single(game_state
*state
, game_state
*copy
, int *from
)
1221 int i
, j
, sx
, sy
, x
, y
, d
, poss
, w
=state
->w
, nlinks
= 0;
1223 /* The from array is a list of 'which square can link _to_ us';
1224 * we start off with from as '-1' (meaning 'not found'); if we find
1225 * something that can link to us it is set to that index, and then if
1226 * we find another we set it to -2. */
1228 memset(from
, -1, state
->n
*sizeof(int));
1230 /* poss is 'can I link to anything' with the same meanings. */
1232 for (i
= 0; i
< state
->n
; i
++) {
1233 if (state
->next
[i
] != -1) continue;
1234 if (state
->nums
[i
] == state
->n
) continue; /* no next from last no. */
1238 sx
= x
= i
%w
; sy
= y
= i
/w
;
1240 x
+= dxs
[d
]; y
+= dys
[d
];
1241 if (!INGRID(state
, x
, y
)) break;
1242 if (!isvalidmove(state
, 1, sx
, sy
, x
, y
)) continue;
1244 /* can't link to somewhere with a back-link we would have to
1245 * break (the solver just doesn't work like this). */
1247 if (state
->prev
[j
] != -1) continue;
1249 if (state
->nums
[i
] > 0 && state
->nums
[j
] > 0 &&
1250 state
->nums
[i
] <= state
->n
&& state
->nums
[j
] <= state
->n
&&
1251 state
->nums
[j
] == state
->nums
[i
]+1) {
1252 debug(("Solver: forcing link through existing consecutive numbers."));
1258 /* if there's been a valid move already, we have to move on;
1259 * we can't make any deductions here. */
1260 poss
= (poss
== -1) ? j
: -2;
1262 /* Modify the from array as described above (which is enumerating
1263 * what points to 'j' in a similar way). */
1264 from
[j
] = (from
[j
] == -1) ? i
: -2;
1267 /*debug(("Solver: (%d,%d) has multiple possible next squares.", sx, sy));*/
1269 } else if (poss
== -1) {
1270 debug(("Solver: nowhere possible for (%d,%d) to link to.", sx
, sy
));
1271 copy
->impossible
= 1;
1274 debug(("Solver: linking (%d,%d) to only possible next (%d,%d).",
1275 sx
, sy
, poss
%w
, poss
/w
));
1276 makelink(copy
, i
, poss
);
1281 for (i
= 0; i
< state
->n
; i
++) {
1282 if (state
->prev
[i
] != -1) continue;
1283 if (state
->nums
[i
] == 1) continue; /* no prev from 1st no. */
1286 if (from
[i
] == -1) {
1287 debug(("Solver: nowhere possible to link to (%d,%d)", x
, y
));
1288 copy
->impossible
= 1;
1290 } else if (from
[i
] == -2) {
1291 /*debug(("Solver: (%d,%d) has multiple possible prev squares.", x, y));*/
1294 debug(("Solver: linking only possible prev (%d,%d) to (%d,%d).",
1295 from
[i
]%w
, from
[i
]/w
, x
, y
));
1296 makelink(copy
, from
[i
], i
);
1304 /* Returns 1 if we managed to solve it, 0 otherwise. */
1305 static int solve_state(game_state
*state
)
1307 game_state
*copy
= dup_game(state
);
1308 int *scratch
= snewn(state
->n
, int), ret
;
1310 debug_state("Before solver: ", state
);
1313 update_numbers(state
);
1315 if (solve_single(state
, copy
, scratch
)) {
1316 dup_game_to(state
, copy
);
1317 if (state
->impossible
) break; else continue;
1324 update_numbers(state
);
1325 ret
= state
->impossible ?
-1 : check_completion(state
, 0);
1326 debug(("Solver finished: %s",
1327 ret
< 0 ?
"impossible" : ret
> 0 ?
"solved" : "not solved"));
1328 debug_state("After solver: ", state
);
1332 static char *solve_game(game_state
*state
, game_state
*currstate
,
1333 char *aux
, char **error
)
1335 game_state
*tosolve
;
1339 tosolve
= dup_game(currstate
);
1340 result
= solve_state(tosolve
);
1342 ret
= generate_desc(tosolve
, 1);
1344 if (ret
) return ret
;
1346 tosolve
= dup_game(state
);
1347 result
= solve_state(tosolve
);
1349 *error
= "Puzzle is impossible.";
1350 else if (result
== 0)
1351 *error
= "Unable to solve puzzle.";
1353 ret
= generate_desc(tosolve
, 1);
1360 /* --- UI and move routines. --- */
1366 int dragging
, drag_is_from
;
1367 int sx
, sy
; /* grid coords of start cell */
1368 int dx
, dy
; /* pixel coords of drag posn */
1371 static game_ui
*new_ui(game_state
*state
)
1373 game_ui
*ui
= snew(game_ui
);
1375 /* NB: if this is ever changed to as to require more than a structure
1376 * copy to clone, there's code that needs fixing in game_redraw too. */
1378 ui
->cx
= ui
->cy
= ui
->cshow
= 0;
1381 ui
->sx
= ui
->sy
= ui
->dx
= ui
->dy
= 0;
1386 static void free_ui(game_ui
*ui
)
1391 static char *encode_ui(game_ui
*ui
)
1396 static void decode_ui(game_ui
*ui
, char *encoding
)
1400 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
1401 game_state
*newstate
)
1403 if (!oldstate
->completed
&& newstate
->completed
)
1404 ui
->cshow
= ui
->dragging
= 0;
1407 struct game_drawstate
{
1408 int tilesize
, started
, solved
;
1412 double angle_offset
;
1414 int dragging
, dx
, dy
;
1418 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
1419 int mx
, int my
, int button
)
1421 int x
= FROMCOORD(mx
), y
= FROMCOORD(my
), w
= state
->w
;
1424 if (IS_CURSOR_MOVE(button
)) {
1425 move_cursor(button
, &ui
->cx
, &ui
->cy
, state
->w
, state
->h
, 0);
1428 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1429 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1432 } else if (IS_CURSOR_SELECT(button
)) {
1435 else if (ui
->dragging
) {
1436 ui
->dragging
= FALSE
;
1437 if (ui
->sx
== ui
->cx
&& ui
->sy
== ui
->cy
) return "";
1438 if (ui
->drag_is_from
) {
1439 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
)) return "";
1440 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
);
1442 if (!isvalidmove(state
, 0, ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
)) return "";
1443 sprintf(buf
, "L%d,%d-%d,%d", ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
);
1447 ui
->dragging
= TRUE
;
1450 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1451 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1452 ui
->drag_is_from
= (button
== CURSOR_SELECT
) ?
1 : 0;
1456 if (IS_MOUSE_DOWN(button
)) {
1458 ui
->cshow
= ui
->dragging
= 0;
1460 assert(!ui
->dragging
);
1461 if (!INGRID(state
, x
, y
)) return NULL
;
1463 if (button
== LEFT_BUTTON
) {
1464 /* disallow dragging from the final number. */
1465 if ((state
->nums
[y
*w
+x
] == state
->n
) &&
1466 (state
->flags
[y
*w
+x
] & FLAG_IMMUTABLE
))
1468 } else if (button
== RIGHT_BUTTON
) {
1469 /* disallow dragging to the first number. */
1470 if ((state
->nums
[y
*w
+x
] == 1) &&
1471 (state
->flags
[y
*w
+x
] & FLAG_IMMUTABLE
))
1475 ui
->dragging
= TRUE
;
1476 ui
->drag_is_from
= (button
== LEFT_BUTTON
) ?
1 : 0;
1483 } else if (IS_MOUSE_DRAG(button
) && ui
->dragging
) {
1487 } else if (IS_MOUSE_RELEASE(button
) && ui
->dragging
) {
1488 ui
->dragging
= FALSE
;
1489 if (ui
->sx
== x
&& ui
->sy
== y
) return ""; /* single click */
1491 if (!INGRID(state
, x
, y
)) {
1492 int si
= ui
->sy
*w
+ui
->sx
;
1493 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1495 sprintf(buf
, "%c%d,%d",
1496 ui
->drag_is_from ?
'C' : 'X', ui
->sx
, ui
->sy
);
1500 if (ui
->drag_is_from
) {
1501 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, x
, y
)) return "";
1502 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, x
, y
);
1504 if (!isvalidmove(state
, 0, x
, y
, ui
->sx
, ui
->sy
)) return "";
1505 sprintf(buf
, "L%d,%d-%d,%d", x
, y
, ui
->sx
, ui
->sy
);
1508 } /* else if (button == 'H' || button == 'h')
1509 return dupstr("H"); */
1510 else if ((button
== 'x' || button
== 'X') && ui
->cshow
) {
1511 int si
= ui
->cy
*w
+ ui
->cx
;
1512 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1514 sprintf(buf
, "%c%d,%d",
1515 (button
== 'x') ?
'C' : 'X', ui
->cx
, ui
->cy
);
1522 static void unlink_cell(game_state
*state
, int si
)
1524 debug(("Unlinking (%d,%d).", si
%state
->w
, si
/state
->w
));
1525 if (state
->prev
[si
] != -1) {
1526 debug((" ... removing prev link from (%d,%d).",
1527 state
->prev
[si
]%state
->w
, state
->prev
[si
]/state
->w
));
1528 state
->next
[state
->prev
[si
]] = -1;
1529 state
->prev
[si
] = -1;
1531 if (state
->next
[si
] != -1) {
1532 debug((" ... removing next link to (%d,%d).",
1533 state
->next
[si
]%state
->w
, state
->next
[si
]/state
->w
));
1534 state
->prev
[state
->next
[si
]] = -1;
1535 state
->next
[si
] = -1;
1539 static game_state
*execute_move(game_state
*state
, char *move
)
1541 game_state
*ret
= NULL
;
1542 int sx
, sy
, ex
, ey
, si
, ei
, w
= state
->w
;
1545 debug(("move: %s", move
));
1547 if (move
[0] == 'S') {
1553 p
.w
= state
->w
; p
.h
= state
->h
;
1554 valid
= validate_desc(&p
, move
+1);
1556 debug(("execute_move: move not valid: %s", valid
));
1559 ret
= dup_game(state
);
1560 tmp
= new_game(NULL
, &p
, move
+1);
1561 for (i
= 0; i
< state
->n
; i
++) {
1562 ret
->prev
[i
] = tmp
->prev
[i
];
1563 ret
->next
[i
] = tmp
->next
[i
];
1566 ret
->used_solve
= 1;
1567 } else if (sscanf(move
, "L%d,%d-%d,%d", &sx
, &sy
, &ex
, &ey
) == 4) {
1568 if (!isvalidmove(state
, 0, sx
, sy
, ex
, ey
)) return NULL
;
1570 ret
= dup_game(state
);
1572 si
= sy
*w
+sx
; ei
= ey
*w
+ex
;
1573 makelink(ret
, si
, ei
);
1574 } else if (sscanf(move
, "%c%d,%d", &c
, &sx
, &sy
) == 3) {
1575 if (c
!= 'C' && c
!= 'X') return NULL
;
1576 if (!INGRID(state
, sx
, sy
)) return NULL
;
1578 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1581 ret
= dup_game(state
);
1584 /* Unlink the single cell we dragged from the board. */
1585 unlink_cell(ret
, si
);
1587 int i
, set
, sset
= state
->nums
[si
] / (state
->n
+1);
1588 for (i
= 0; i
< state
->n
; i
++) {
1589 /* Unlink all cells in the same set as the one we dragged
1590 * from the board. */
1592 if (state
->nums
[i
] == 0) continue;
1593 set
= state
->nums
[i
] / (state
->n
+1);
1594 if (set
!= sset
) continue;
1596 unlink_cell(ret
, i
);
1599 } else if (strcmp(move
, "H") == 0) {
1600 ret
= dup_game(state
);
1604 update_numbers(ret
);
1605 if (check_completion(ret
, 1)) ret
->completed
= 1;
1611 /* ----------------------------------------------------------------------
1615 static void game_compute_size(game_params
*params
, int tilesize
,
1618 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1619 struct { int tilesize
, order
; } ads
, *ds
= &ads
;
1620 ads
.tilesize
= tilesize
;
1622 *x
= TILE_SIZE
* params
->w
+ 2 * BORDER
;
1623 *y
= TILE_SIZE
* params
->h
+ 2 * BORDER
;
1626 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
1627 game_params
*params
, int tilesize
)
1629 ds
->tilesize
= tilesize
;
1630 assert(TILE_SIZE
> 0);
1633 ds
->dragb
= blitter_new(dr
, BLITTER_SIZE
, BLITTER_SIZE
);
1636 /* Colours chosen from the webby palette to work as a background to black text,
1637 * W then some plausible approximation to pastelly ROYGBIV; we then interpolate
1638 * between consecutive pairs to give another 8 (and then the drawing routine
1639 * will reuse backgrounds). */
1640 static const unsigned long bgcols
[8] = {
1641 0xffffff, /* white */
1642 0xffa07a, /* lightsalmon */
1643 0x98fb98, /* green */
1644 0x7fffd4, /* aquamarine */
1645 0x9370db, /* medium purple */
1646 0xffa500, /* orange */
1647 0x87cefa, /* lightskyblue */
1648 0xffff00, /* yellow */
1651 static float *game_colours(frontend
*fe
, int *ncolours
)
1653 float *ret
= snewn(3 * NCOLOURS
, float);
1656 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
1658 for (i
= 0; i
< 3; i
++) {
1659 ret
[COL_NUMBER
* 3 + i
] = 0.0F
;
1660 ret
[COL_ARROW
* 3 + i
] = 0.0F
;
1661 ret
[COL_CURSOR
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 2.0F
;
1662 ret
[COL_GRID
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 1.3F
;
1664 ret
[COL_NUMBER_SET
* 3 + 0] = 0.0F
;
1665 ret
[COL_NUMBER_SET
* 3 + 1] = 0.0F
;
1666 ret
[COL_NUMBER_SET
* 3 + 2] = 0.9F
;
1668 ret
[COL_ERROR
* 3 + 0] = 1.0F
;
1669 ret
[COL_ERROR
* 3 + 1] = 0.0F
;
1670 ret
[COL_ERROR
* 3 + 2] = 0.0F
;
1672 ret
[COL_DRAG_ORIGIN
* 3 + 0] = 0.2F
;
1673 ret
[COL_DRAG_ORIGIN
* 3 + 1] = 1.0F
;
1674 ret
[COL_DRAG_ORIGIN
* 3 + 2] = 0.2F
;
1676 for (c
= 0; c
< 8; c
++) {
1677 ret
[(COL_B0
+ c
) * 3 + 0] = (float)((bgcols
[c
] & 0xff0000) >> 16) / 256.0F
;
1678 ret
[(COL_B0
+ c
) * 3 + 1] = (float)((bgcols
[c
] & 0xff00) >> 8) / 256.0F
;
1679 ret
[(COL_B0
+ c
) * 3 + 2] = (float)((bgcols
[c
] & 0xff)) / 256.0F
;
1681 for (c
= 0; c
< 8; c
++) {
1682 for (i
= 0; i
< 3; i
++) {
1683 ret
[(COL_B0
+ 8 + c
) * 3 + i
] =
1684 (ret
[(COL_B0
+ c
) * 3 + i
] + ret
[(COL_B0
+ c
+ 1) * 3 + i
]) / 2.0F
;
1688 #define average(r,a,b,w) do { \
1689 for (i = 0; i < 3; i++) \
1690 ret[(r)*3+i] = ret[(a)*3+i] + w * (ret[(b)*3+i] - ret[(a)*3+i]); \
1692 average(COL_ARROW_BG_DIM
, COL_BACKGROUND
, COL_ARROW
, 0.1F
);
1693 average(COL_NUMBER_SET_MID
, COL_B0
, COL_NUMBER_SET
, 0.3F
);
1694 for (c
= 0; c
< NBACKGROUNDS
; c
++) {
1695 /* I assume here that COL_ARROW and COL_NUMBER are the same.
1696 * Otherwise I'd need two sets of COL_M*. */
1697 average(COL_M0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.3F
);
1698 average(COL_D0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.1F
);
1699 average(COL_X0
+ c
, COL_BACKGROUND
, COL_B0
+ c
, 0.5F
);
1702 *ncolours
= NCOLOURS
;
1706 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1708 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1711 ds
->tilesize
= ds
->started
= ds
->solved
= 0;
1716 ds
->nums
= snewn(state
->n
, int);
1717 ds
->dirp
= snewn(state
->n
, int);
1718 ds
->f
= snewn(state
->n
, unsigned int);
1719 for (i
= 0; i
< state
->n
; i
++) {
1725 ds
->angle_offset
= 0.0F
;
1727 ds
->dragging
= ds
->dx
= ds
->dy
= 0;
1733 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1738 if (ds
->dragb
) blitter_free(dr
, ds
->dragb
);
1743 /* cx, cy are top-left corner. sz is the 'radius' of the arrow.
1744 * ang is in radians, clockwise from 0 == straight up. */
1745 static void draw_arrow(drawing
*dr
, int cx
, int cy
, int sz
, double ang
,
1746 int cfill
, int cout
)
1749 int xdx
, ydx
, xdy
, ydy
, xdx3
, xdy3
;
1750 double s
= sin(ang
), c
= cos(ang
);
1752 xdx3
= (int)(sz
* (c
/3 + 1) + 0.5) - sz
;
1753 xdy3
= (int)(sz
* (s
/3 + 1) + 0.5) - sz
;
1754 xdx
= (int)(sz
* (c
+ 1) + 0.5) - sz
;
1755 xdy
= (int)(sz
* (s
+ 1) + 0.5) - sz
;
1760 coords
[2*0 + 0] = cx
- ydx
;
1761 coords
[2*0 + 1] = cy
- ydy
;
1762 coords
[2*1 + 0] = cx
+ xdx
;
1763 coords
[2*1 + 1] = cy
+ xdy
;
1764 coords
[2*2 + 0] = cx
+ xdx3
;
1765 coords
[2*2 + 1] = cy
+ xdy3
;
1766 coords
[2*3 + 0] = cx
+ xdx3
+ ydx
;
1767 coords
[2*3 + 1] = cy
+ xdy3
+ ydy
;
1768 coords
[2*4 + 0] = cx
- xdx3
+ ydx
;
1769 coords
[2*4 + 1] = cy
- xdy3
+ ydy
;
1770 coords
[2*5 + 0] = cx
- xdx3
;
1771 coords
[2*5 + 1] = cy
- xdy3
;
1772 coords
[2*6 + 0] = cx
- xdx
;
1773 coords
[2*6 + 1] = cy
- xdy
;
1775 draw_polygon(dr
, coords
, 7, cfill
, cout
);
1778 static void draw_arrow_dir(drawing
*dr
, int cx
, int cy
, int sz
, int dir
,
1779 int cfill
, int cout
, double angle_offset
)
1781 double ang
= 2.0 * PI
* (double)dir
/ 8.0 + angle_offset
;
1782 draw_arrow(dr
, cx
, cy
, sz
, ang
, cfill
, cout
);
1785 /* cx, cy are centre coordinates.. */
1786 static void draw_star(drawing
*dr
, int cx
, int cy
, int rad
, int npoints
,
1787 int cfill
, int cout
, double angle_offset
)
1792 assert(npoints
> 0);
1794 coords
= snewn(npoints
* 2 * 2, int);
1796 for (n
= 0; n
< npoints
* 2; n
++) {
1797 a
= 2.0 * PI
* ((double)n
/ ((double)npoints
* 2.0)) + angle_offset
;
1798 r
= (n
% 2) ?
(double)rad
/2.0 : (double)rad
;
1800 /* We're rotating the point at (0, -r) by a degrees */
1801 coords
[2*n
+0] = cx
+ (int)( r
* sin(a
));
1802 coords
[2*n
+1] = cy
+ (int)(-r
* cos(a
));
1804 draw_polygon(dr
, coords
, npoints
*2, cfill
, cout
);
1808 static int num2col(game_drawstate
*ds
, int num
)
1810 int set
= num
/ (ds
->n
+1);
1812 if (num
<= 0) return COL_B0
;
1813 return COL_B0
+ (set
% 16);
1816 #define ARROW_HALFSZ (7 * TILE_SIZE / 32)
1818 #define F_CUR 0x001 /* Cursor on this tile. */
1819 #define F_DRAG_SRC 0x002 /* Tile is source of a drag. */
1820 #define F_ERROR 0x004 /* Tile marked in error. */
1821 #define F_IMMUTABLE 0x008 /* Tile (number) is immutable. */
1822 #define F_ARROW_POINT 0x010 /* Tile points to other tile */
1823 #define F_ARROW_INPOINT 0x020 /* Other tile points in here. */
1824 #define F_DIM 0x040 /* Tile is dim */
1826 static void tile_redraw(drawing
*dr
, game_drawstate
*ds
, int tx
, int ty
,
1827 int dir
, int dirp
, int num
, unsigned int f
,
1828 double angle_offset
, int print_ink
)
1830 int cb
= TILE_SIZE
/ 16, textsz
;
1832 int arrowcol
, sarrowcol
, setcol
, textcol
;
1833 int acx
, acy
, asz
, empty
= 0;
1835 if (num
== 0 && !(f
& F_ARROW_POINT
) && !(f
& F_ARROW_INPOINT
)) {
1838 * We don't display text in empty cells: typically these are
1839 * signified by num=0. However, in some cases a cell could
1840 * have had the number 0 assigned to it if the user made an
1841 * error (e.g. tried to connect a chain of length 5 to the
1842 * immutable number 4) so we _do_ display the 0 if the cell
1843 * has a link in or a link out.
1847 /* Calculate colours. */
1849 if (print_ink
>= 0) {
1851 * We're printing, so just do everything in black.
1853 arrowcol
= textcol
= print_ink
;
1854 setcol
= sarrowcol
= -1; /* placate optimiser */
1857 setcol
= empty ? COL_BACKGROUND
: num2col(ds
, num
);
1859 #define dim(fg,bg) ( \
1860 (bg)==COL_BACKGROUND ? COL_ARROW_BG_DIM : \
1861 (bg) + COL_D0 - COL_B0 \
1864 #define mid(fg,bg) ( \
1865 (fg)==COL_NUMBER_SET ? COL_NUMBER_SET_MID : \
1866 (bg) + COL_M0 - COL_B0 \
1869 #define dimbg(bg) ( \
1870 (bg)==COL_BACKGROUND ? COL_BACKGROUND : \
1871 (bg) + COL_X0 - COL_B0 \
1874 if (f
& F_DRAG_SRC
) arrowcol
= COL_DRAG_ORIGIN
;
1875 else if (f
& F_DIM
) arrowcol
= dim(COL_ARROW
, setcol
);
1876 else if (f
& F_ARROW_POINT
) arrowcol
= mid(COL_ARROW
, setcol
);
1877 else arrowcol
= COL_ARROW
;
1879 if ((f
& F_ERROR
) && !(f
& F_IMMUTABLE
)) textcol
= COL_ERROR
;
1881 if (f
& F_IMMUTABLE
) textcol
= COL_NUMBER_SET
;
1882 else textcol
= COL_NUMBER
;
1884 if (f
& F_DIM
) textcol
= dim(textcol
, setcol
);
1885 else if (((f
& F_ARROW_POINT
) || num
==ds
->n
) &&
1886 ((f
& F_ARROW_INPOINT
) || num
==1))
1887 textcol
= mid(textcol
, setcol
);
1890 if (f
& F_DIM
) sarrowcol
= dim(COL_ARROW
, setcol
);
1891 else sarrowcol
= COL_ARROW
;
1894 /* Clear tile background */
1896 if (print_ink
< 0) {
1897 draw_rect(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
,
1898 (f
& F_DIM
) ?
dimbg(setcol
) : setcol
);
1901 /* Draw large (outwards-pointing) arrow. */
1903 asz
= ARROW_HALFSZ
; /* 'radius' of arrow/star. */
1904 acx
= tx
+TILE_SIZE
/2+asz
; /* centre x */
1905 acy
= ty
+TILE_SIZE
/2+asz
; /* centre y */
1907 if (num
== ds
->n
&& (f
& F_IMMUTABLE
))
1908 draw_star(dr
, acx
, acy
, asz
, 5, arrowcol
, arrowcol
, angle_offset
);
1910 draw_arrow_dir(dr
, acx
, acy
, asz
, dir
, arrowcol
, arrowcol
, angle_offset
);
1911 if (print_ink
< 0 && (f
& F_CUR
))
1912 draw_rect_corners(dr
, acx
, acy
, asz
+1, COL_CURSOR
);
1914 /* Draw dot iff this tile requires a predecessor and doesn't have one. */
1916 if (print_ink
< 0) {
1917 acx
= tx
+TILE_SIZE
/2-asz
;
1918 acy
= ty
+TILE_SIZE
/2+asz
;
1920 if (!(f
& F_ARROW_INPOINT
) && num
!= 1) {
1921 draw_circle(dr
, acx
, acy
, asz
/ 4, sarrowcol
, sarrowcol
);
1925 /* Draw text (number or set). */
1928 int set
= (num
<= 0) ?
0 : num
/ (ds
->n
+1);
1930 if (set
== 0 || num
<= 0) {
1931 sprintf(buf
, "%d", num
);
1933 int n
= num
% (ds
->n
+1);
1936 sprintf(buf
, "%c", (int)(set
+'a'-1));
1938 sprintf(buf
, "%c+%d", (int)(set
+'a'-1), n
);
1940 textsz
= min(2*asz
, (TILE_SIZE
- 2 * cb
) / (int)strlen(buf
));
1941 draw_text(dr
, tx
+ cb
, ty
+ TILE_SIZE
/4, FONT_VARIABLE
, textsz
,
1942 ALIGN_VCENTRE
| ALIGN_HLEFT
, textcol
, buf
);
1945 if (print_ink
< 0) {
1946 draw_rect_outline(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
, COL_GRID
);
1947 draw_update(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
);
1951 static void draw_drag_indicator(drawing
*dr
, game_drawstate
*ds
,
1952 game_state
*state
, game_ui
*ui
, int validdrag
)
1954 int dir
, w
= ds
->w
, acol
= COL_ARROW
;
1955 int fx
= FROMCOORD(ui
->dx
), fy
= FROMCOORD(ui
->dy
);
1959 /* If we could move here, lock the arrow to the appropriate direction. */
1960 dir
= ui
->drag_is_from ? state
->dirs
[ui
->sy
*w
+ui
->sx
] : state
->dirs
[fy
*w
+fx
];
1962 ang
= (2.0 * PI
* dir
) / 8.0; /* similar to calculation in draw_arrow_dir. */
1964 /* Draw an arrow pointing away from/towards the origin cell. */
1965 int ox
= COORD(ui
->sx
) + TILE_SIZE
/2, oy
= COORD(ui
->sy
) + TILE_SIZE
/2;
1966 double tana
, offset
;
1967 double xdiff
= fabs(ox
- ui
->dx
), ydiff
= fabs(oy
- ui
->dy
);
1970 ang
= (oy
> ui
->dy
) ?
0.0F
: PI
;
1971 } else if (ydiff
== 0) {
1972 ang
= (ox
> ui
->dx
) ?
3.0F
*PI
/2.0F
: PI
/2.0F
;
1974 if (ui
->dx
> ox
&& ui
->dy
< oy
) {
1975 tana
= xdiff
/ ydiff
;
1977 } else if (ui
->dx
> ox
&& ui
->dy
> oy
) {
1978 tana
= ydiff
/ xdiff
;
1980 } else if (ui
->dx
< ox
&& ui
->dy
> oy
) {
1981 tana
= xdiff
/ ydiff
;
1984 tana
= ydiff
/ xdiff
;
1985 offset
= 3.0F
* PI
/ 2.0F
;
1987 ang
= atan(tana
) + offset
;
1990 if (!ui
->drag_is_from
) ang
+= PI
; /* point to origin, not away from. */
1993 draw_arrow(dr
, ui
->dx
, ui
->dy
, ARROW_HALFSZ
, ang
, acol
, acol
);
1996 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
1997 game_state
*state
, int dir
, game_ui
*ui
,
1998 float animtime
, float flashtime
)
2000 int x
, y
, i
, w
= ds
->w
, dirp
, force
= 0;
2002 double angle_offset
= 0.0;
2003 game_state
*postdrop
= NULL
;
2005 if (flashtime
> 0.0F
)
2006 angle_offset
= 2.0 * PI
* (flashtime
/ FLASH_SPIN
);
2007 if (angle_offset
!= ds
->angle_offset
) {
2008 ds
->angle_offset
= angle_offset
;
2014 blitter_load(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
2015 draw_update(dr
, ds
->dx
, ds
->dy
, BLITTER_SIZE
, BLITTER_SIZE
);
2016 ds
->dragging
= FALSE
;
2019 /* If an in-progress drag would make a valid move if finished, we
2020 * reflect that move in the board display. We let interpret_move do
2021 * most of the heavy lifting for us: we have to copy the game_ui so
2022 * as not to stomp on the real UI's drag state. */
2024 game_ui uicopy
= *ui
;
2025 char *movestr
= interpret_move(state
, &uicopy
, ds
, ui
->dx
, ui
->dy
, LEFT_RELEASE
);
2027 if (movestr
!= NULL
&& strcmp(movestr
, "") != 0) {
2028 postdrop
= execute_move(state
, movestr
);
2036 int aw
= TILE_SIZE
* state
->w
;
2037 int ah
= TILE_SIZE
* state
->h
;
2038 draw_rect(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
, COL_BACKGROUND
);
2039 draw_rect_outline(dr
, BORDER
- 1, BORDER
- 1, aw
+ 2, ah
+ 2, COL_GRID
);
2040 draw_update(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
);
2042 for (x
= 0; x
< state
->w
; x
++) {
2043 for (y
= 0; y
< state
->h
; y
++) {
2048 if (ui
->cshow
&& x
== ui
->cx
&& y
== ui
->cy
)
2052 if (x
== ui
->sx
&& y
== ui
->sy
)
2054 else if (ui
->drag_is_from
) {
2055 if (!ispointing(state
, ui
->sx
, ui
->sy
, x
, y
))
2058 if (!ispointing(state
, x
, y
, ui
->sx
, ui
->sy
))
2063 if (state
->impossible
||
2064 state
->nums
[i
] < 0 || state
->flags
[i
] & FLAG_ERROR
)
2066 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
2069 if (state
->next
[i
] != -1)
2072 if (state
->prev
[i
] != -1) {
2073 /* Currently the direction here is from our square _back_
2074 * to its previous. We could change this to give the opposite
2075 * sense to the direction. */
2076 f
|= F_ARROW_INPOINT
;
2077 dirp
= whichdir(x
, y
, state
->prev
[i
]%w
, state
->prev
[i
]/w
);
2080 if (state
->nums
[i
] != ds
->nums
[i
] ||
2081 f
!= ds
->f
[i
] || dirp
!= ds
->dirp
[i
] ||
2082 force
|| !ds
->started
) {
2084 BORDER
+ x
* TILE_SIZE
,
2085 BORDER
+ y
* TILE_SIZE
,
2086 state
->dirs
[i
], dirp
, state
->nums
[i
], f
,
2088 ds
->nums
[i
] = state
->nums
[i
];
2095 ds
->dragging
= TRUE
;
2096 ds
->dx
= ui
->dx
- BLITTER_SIZE
/2;
2097 ds
->dy
= ui
->dy
- BLITTER_SIZE
/2;
2098 blitter_save(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
2100 draw_drag_indicator(dr
, ds
, state
, ui
, postdrop ?
1 : 0);
2102 if (postdrop
) free_game(postdrop
);
2103 if (!ds
->started
) ds
->started
= TRUE
;
2106 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
2107 int dir
, game_ui
*ui
)
2112 static float game_flash_length(game_state
*oldstate
, game_state
*newstate
,
2113 int dir
, game_ui
*ui
)
2115 if (!oldstate
->completed
&&
2116 newstate
->completed
&& !newstate
->used_solve
)
2122 static int game_timing_state(game_state
*state
, game_ui
*ui
)
2127 static void game_print_size(game_params
*params
, float *x
, float *y
)
2131 game_compute_size(params
, 1300, &pw
, &ph
);
2136 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
2138 int ink
= print_mono_colour(dr
, 0);
2141 /* Fake up just enough of a drawstate */
2142 game_drawstate ads
, *ds
= &ads
;
2143 ds
->tilesize
= tilesize
;
2149 print_line_width(dr
, TILE_SIZE
/ 40);
2150 for (x
= 1; x
< state
->w
; x
++)
2151 draw_line(dr
, COORD(x
), COORD(0), COORD(x
), COORD(state
->h
), ink
);
2152 for (y
= 1; y
< state
->h
; y
++)
2153 draw_line(dr
, COORD(0), COORD(y
), COORD(state
->w
), COORD(y
), ink
);
2154 print_line_width(dr
, 2*TILE_SIZE
/ 40);
2155 draw_rect_outline(dr
, COORD(0), COORD(0), TILE_SIZE
*state
->w
,
2156 TILE_SIZE
*state
->h
, ink
);
2159 * Arrows and numbers.
2161 print_line_width(dr
, 0);
2162 for (y
= 0; y
< state
->h
; y
++)
2163 for (x
= 0; x
< state
->w
; x
++)
2164 tile_redraw(dr
, ds
, COORD(x
), COORD(y
), state
->dirs
[y
*state
->w
+x
],
2165 0, state
->nums
[y
*state
->w
+x
], 0, 0.0, ink
);
2169 #define thegame signpost
2172 const struct game thegame
= {
2173 "Signpost", "games.signpost", "signpost",
2180 TRUE
, game_configure
, custom_params
,
2188 TRUE
, game_can_format_as_text_now
, game_text_format
,
2196 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
2199 game_free_drawstate
,
2203 TRUE
, FALSE
, game_print_size
, game_print
,
2204 FALSE
, /* wants_statusbar */
2205 FALSE
, game_timing_state
,
2206 REQUIRE_RBUTTON
| REQUIRE_NUMPAD
, /* flags */
2209 #ifdef STANDALONE_SOLVER
2214 const char *quis
= NULL
;
2217 void usage(FILE *out
) {
2218 fprintf(out
, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis
);
2221 static void cycle_seed(char **seedstr
, random_state
*rs
)
2227 newseed
[0] = '1' + (char)random_upto(rs
, 9);
2228 for (j
= 1; j
< 15; j
++)
2229 newseed
[j
] = '0' + (char)random_upto(rs
, 10);
2231 *seedstr
= dupstr(newseed
);
2234 static void start_soak(game_params
*p
, char *seedstr
)
2236 time_t tt_start
, tt_now
, tt_last
;
2239 long n
= 0, nnums
= 0, i
;
2242 tt_start
= tt_now
= time(NULL
);
2243 printf("Soak-generating a %dx%d grid.\n", p
->w
, p
->h
);
2246 rs
= random_new(seedstr
, strlen(seedstr
));
2247 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2249 state
= thegame
.new_game(NULL
, p
, desc
);
2250 for (i
= 0; i
< state
->n
; i
++) {
2251 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
2254 thegame
.free_game(state
);
2257 cycle_seed(&seedstr
, rs
);
2261 tt_last
= time(NULL
);
2262 if (tt_last
> tt_now
) {
2264 printf("%ld total, %3.1f/s, %3.1f nums/grid (%3.1f%%).\n",
2266 (double)n
/ ((double)tt_now
- tt_start
),
2267 (double)nnums
/ (double)n
,
2268 ((double)nnums
* 100.0) / ((double)n
* (double)p
->w
* (double)p
->h
) );
2273 static void process_desc(char *id
)
2275 char *desc
, *err
, *solvestr
;
2279 printf("%s\n ", id
);
2281 desc
= strchr(id
, ':');
2283 fprintf(stderr
, "%s: expecting game description.", quis
);
2289 p
= thegame
.default_params();
2290 thegame
.decode_params(p
, id
);
2291 err
= thegame
.validate_params(p
, 1);
2293 fprintf(stderr
, "%s: %s", quis
, err
);
2294 thegame
.free_params(p
);
2298 err
= thegame
.validate_desc(p
, desc
);
2300 fprintf(stderr
, "%s: %s\nDescription: %s\n", quis
, err
, desc
);
2301 thegame
.free_params(p
);
2305 s
= thegame
.new_game(NULL
, p
, desc
);
2307 solvestr
= thegame
.solve(s
, s
, NULL
, &err
);
2309 fprintf(stderr
, "%s\n", err
);
2311 printf("Puzzle is soluble.\n");
2313 thegame
.free_game(s
);
2314 thegame
.free_params(p
);
2317 int main(int argc
, const char *argv
[])
2319 char *id
= NULL
, *desc
, *err
, *aux
= NULL
;
2320 int soak
= 0, verbose
= 0, stdin_desc
= 0, n
= 1, i
;
2321 char *seedstr
= NULL
, newseed
[16];
2323 setvbuf(stdout
, NULL
, _IONBF
, 0);
2326 while (--argc
> 0) {
2327 char *p
= (char*)(*++argv
);
2328 if (!strcmp(p
, "-v") || !strcmp(p
, "--verbose"))
2330 else if (!strcmp(p
, "--stdin"))
2332 else if (!strcmp(p
, "-e") || !strcmp(p
, "--seed")) {
2333 seedstr
= dupstr(*++argv
);
2335 } else if (!strcmp(p
, "-n") || !strcmp(p
, "--number")) {
2338 } else if (!strcmp(p
, "-s") || !strcmp(p
, "--soak")) {
2340 } else if (*p
== '-') {
2341 fprintf(stderr
, "%s: unrecognised option `%s'\n", argv
[0], p
);
2349 sprintf(newseed
, "%lu", time(NULL
));
2350 seedstr
= dupstr(newseed
);
2352 if (id
|| !stdin_desc
) {
2353 if (id
&& strchr(id
, ':')) {
2354 /* Parameters and description passed on cmd-line:
2355 * try and solve it. */
2358 /* No description passed on cmd-line: decode parameters
2359 * (with optional seed too) */
2361 game_params
*p
= thegame
.default_params();
2364 char *cmdseed
= strchr(id
, '#');
2368 seedstr
= dupstr(cmdseed
);
2371 thegame
.decode_params(p
, id
);
2374 err
= thegame
.validate_params(p
, 1);
2376 fprintf(stderr
, "%s: %s", quis
, err
);
2377 thegame
.free_params(p
);
2381 /* We have a set of valid parameters; either soak with it
2382 * or generate a single game description and print to stdout. */
2384 start_soak(p
, seedstr
);
2386 char *pstring
= thegame
.encode_params(p
, 0);
2388 for (i
= 0; i
< n
; i
++) {
2389 random_state
*rs
= random_new(seedstr
, strlen(seedstr
));
2391 if (verbose
) printf("%s#%s\n", pstring
, seedstr
);
2392 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2393 printf("%s:%s\n", pstring
, desc
);
2396 cycle_seed(&seedstr
, rs
);
2403 thegame
.free_params(p
);
2410 while (fgets(buf
, sizeof(buf
), stdin
)) {
2411 buf
[strcspn(buf
, "\r\n")] = '\0';
2423 /* vim: set shiftwidth=4 tabstop=8: */