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 final number, or _to_ the 1. */
163 if (nfrom
== state
->n
|| nto
== 1)
166 /* can't create a new connection between cells in the same region
167 * as that would create a loop. */
168 if (dsf_canonify(state
->dsf
, from
) == dsf_canonify(state
->dsf
, to
))
171 /* if both cells are actual numbers, can't drag if we're not
172 * one digit apart. */
173 if (ISREALNUM(state
, nfrom
) && ISREALNUM(state
, nto
)) {
176 } else if (clever
&& ISREALNUM(state
, nfrom
)) {
177 if (!move_couldfit(state
, nfrom
, +1, tox
, toy
))
179 } else if (clever
&& ISREALNUM(state
, nto
)) {
180 if (!move_couldfit(state
, nto
, -1, fromx
, fromy
))
187 static void makelink(game_state
*state
, int from
, int to
)
189 if (state
->next
[from
] != -1)
190 state
->prev
[state
->next
[from
]] = -1;
191 state
->next
[from
] = to
;
193 if (state
->prev
[to
] != -1)
194 state
->next
[state
->prev
[to
]] = -1;
195 state
->prev
[to
] = from
;
198 static int game_can_format_as_text_now(game_params
*params
)
200 if (params
->w
* params
->h
>= 100) return 0;
204 static char *game_text_format(game_state
*state
)
206 int len
= state
->h
* 2 * (4*state
->w
+ 1) + state
->h
+ 2;
207 int x
, y
, i
, num
, n
, set
;
210 p
= ret
= snewn(len
, char);
212 for (y
= 0; y
< state
->h
; y
++) {
213 for (x
= 0; x
< state
->h
; x
++) {
215 *p
++ = dirstrings
[state
->dirs
[i
]][0];
216 *p
++ = dirstrings
[state
->dirs
[i
]][1];
217 *p
++ = (state
->flags
[i
] & FLAG_IMMUTABLE
) ?
'I' : ' ';
221 for (x
= 0; x
< state
->h
; x
++) {
223 num
= state
->nums
[i
];
229 n
= num
% (state
->n
+1);
230 set
= num
/ (state
->n
+1);
232 assert(n
<= 99); /* two digits only! */
237 *p
++ = (n
>= 10) ?
('0' + (n
/10)) : ' ';
253 static void debug_state(const char *desc
, game_state
*state
)
257 if (state
->n
>= 100) {
258 debug(("[ no game_text_format for this size ]"));
261 dbg
= game_text_format(state
);
262 debug(("%s\n%s", desc
, dbg
));
268 static void strip_nums(game_state
*state
) {
270 for (i
= 0; i
< state
->n
; i
++) {
271 if (!(state
->flags
[i
] & FLAG_IMMUTABLE
))
274 memset(state
->next
, -1, state
->n
*sizeof(int));
275 memset(state
->prev
, -1, state
->n
*sizeof(int));
276 memset(state
->numsi
, -1, (state
->n
+1)*sizeof(int));
277 dsf_init(state
->dsf
, state
->n
);
280 static int check_nums(game_state
*orig
, game_state
*copy
, int only_immutable
)
283 assert(copy
->n
== orig
->n
);
284 for (i
= 0; i
< copy
->n
; i
++) {
285 if (only_immutable
&& !copy
->flags
[i
] & FLAG_IMMUTABLE
) continue;
286 assert(copy
->nums
[i
] >= 0);
287 assert(copy
->nums
[i
] <= copy
->n
);
288 if (copy
->nums
[i
] != orig
->nums
[i
]) {
289 debug(("check_nums: (%d,%d) copy=%d, orig=%d.",
290 i
%orig
->w
, i
/orig
->w
, copy
->nums
[i
], orig
->nums
[i
]));
297 /* --- Game parameter/presets functions --- */
299 static game_params
*default_params(void)
301 game_params
*ret
= snew(game_params
);
303 ret
->force_corner_start
= 1;
308 static const struct game_params signpost_presets
[] = {
317 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
322 if (i
< 0 || i
>= lenof(signpost_presets
))
325 ret
= default_params();
326 *ret
= signpost_presets
[i
];
329 sprintf(buf
, "%dx%d%s", ret
->w
, ret
->h
,
330 ret
->force_corner_start ?
"" : ", free ends");
336 static void free_params(game_params
*params
)
341 static game_params
*dup_params(game_params
*params
)
343 game_params
*ret
= snew(game_params
);
344 *ret
= *params
; /* structure copy */
348 static void decode_params(game_params
*ret
, char const *string
)
350 ret
->w
= ret
->h
= atoi(string
);
351 while (*string
&& isdigit((unsigned char)*string
)) string
++;
352 if (*string
== 'x') {
354 ret
->h
= atoi(string
);
355 while (*string
&& isdigit((unsigned char)*string
)) string
++;
357 ret
->force_corner_start
= 0;
358 if (*string
== 'c') {
360 ret
->force_corner_start
= 1;
365 static char *encode_params(game_params
*params
, int full
)
370 sprintf(data
, "%dx%d%s", params
->w
, params
->h
,
371 params
->force_corner_start ?
"c" : "");
373 sprintf(data
, "%dx%d", params
->w
, params
->h
);
378 static config_item
*game_configure(game_params
*params
)
383 ret
= snewn(4, config_item
);
385 ret
[0].name
= "Width";
386 ret
[0].type
= C_STRING
;
387 sprintf(buf
, "%d", params
->w
);
388 ret
[0].sval
= dupstr(buf
);
391 ret
[1].name
= "Height";
392 ret
[1].type
= C_STRING
;
393 sprintf(buf
, "%d", params
->h
);
394 ret
[1].sval
= dupstr(buf
);
397 ret
[2].name
= "Start and end in corners";
398 ret
[2].type
= C_BOOLEAN
;
400 ret
[2].ival
= params
->force_corner_start
;
410 static game_params
*custom_params(config_item
*cfg
)
412 game_params
*ret
= snew(game_params
);
414 ret
->w
= atoi(cfg
[0].sval
);
415 ret
->h
= atoi(cfg
[1].sval
);
416 ret
->force_corner_start
= cfg
[2].ival
;
421 static char *validate_params(game_params
*params
, int full
)
423 if (params
->w
< 2 || params
->h
< 2)
424 return "Width and height must both be at least two";
429 /* --- Game description string generation and unpicking --- */
431 static void blank_game_into(game_state
*state
)
433 memset(state
->dirs
, 0, state
->n
*sizeof(int));
434 memset(state
->nums
, 0, state
->n
*sizeof(int));
435 memset(state
->flags
, 0, state
->n
*sizeof(unsigned int));
436 memset(state
->next
, -1, state
->n
*sizeof(int));
437 memset(state
->prev
, -1, state
->n
*sizeof(int));
438 memset(state
->numsi
, -1, (state
->n
+1)*sizeof(int));
441 static game_state
*blank_game(int w
, int h
)
443 game_state
*state
= snew(game_state
);
445 memset(state
, 0, sizeof(game_state
));
450 state
->dirs
= snewn(state
->n
, int);
451 state
->nums
= snewn(state
->n
, int);
452 state
->flags
= snewn(state
->n
, unsigned int);
453 state
->next
= snewn(state
->n
, int);
454 state
->prev
= snewn(state
->n
, int);
455 state
->dsf
= snew_dsf(state
->n
);
456 state
->numsi
= snewn(state
->n
+1, int);
458 blank_game_into(state
);
463 static void dup_game_to(game_state
*to
, game_state
*from
)
465 to
->completed
= from
->completed
;
466 to
->used_solve
= from
->used_solve
;
467 to
->impossible
= from
->impossible
;
469 memcpy(to
->dirs
, from
->dirs
, to
->n
*sizeof(int));
470 memcpy(to
->flags
, from
->flags
, to
->n
*sizeof(unsigned int));
471 memcpy(to
->nums
, from
->nums
, to
->n
*sizeof(int));
473 memcpy(to
->next
, from
->next
, to
->n
*sizeof(int));
474 memcpy(to
->prev
, from
->prev
, to
->n
*sizeof(int));
476 memcpy(to
->dsf
, from
->dsf
, to
->n
*sizeof(int));
477 memcpy(to
->numsi
, from
->numsi
, (to
->n
+1)*sizeof(int));
480 static game_state
*dup_game(game_state
*state
)
482 game_state
*ret
= blank_game(state
->w
, state
->h
);
483 dup_game_to(ret
, state
);
487 static void free_game(game_state
*state
)
499 static void unpick_desc(game_params
*params
, char *desc
,
500 game_state
**sout
, char **mout
)
502 game_state
*state
= blank_game(params
->w
, params
->h
);
508 msg
= "Game description longer than expected";
514 num
= (num
*10) + (int)(c
-'0');
515 if (num
> state
->n
) {
516 msg
= "Number too large";
519 } else if ((c
-'a') >= 0 && (c
-'a') < DIR_MAX
) {
520 state
->nums
[i
] = num
;
521 state
->flags
[i
] = num ? FLAG_IMMUTABLE
: 0;
524 state
->dirs
[i
] = c
- 'a';
527 msg
= "Game description shorter than expected";
530 msg
= "Game description contains unexpected characters";
536 msg
= "Game description shorter than expected";
541 if (msg
) { /* sth went wrong. */
542 if (mout
) *mout
= msg
;
545 if (mout
) *mout
= NULL
;
546 if (sout
) *sout
= state
;
547 else free_game(state
);
551 static char *generate_desc(game_state
*state
, int issolve
)
556 ret
= NULL
; retlen
= 0;
558 ret
= sresize(ret
, 2, char);
559 ret
[0] = 'S'; ret
[1] = '\0';
562 for (i
= 0; i
< state
->n
; i
++) {
564 k
= sprintf(buf
, "%d%c", state
->nums
[i
], (int)(state
->dirs
[i
]+'a'));
566 k
= sprintf(buf
, "%c", (int)(state
->dirs
[i
]+'a'));
567 ret
= sresize(ret
, retlen
+ k
+ 1, char);
568 strcpy(ret
+ retlen
, buf
);
574 /* --- Game generation --- */
576 /* Fills in preallocated arrays ai (indices) and ad (directions)
577 * showing all non-numbered cells adjacent to index i, returns length */
578 /* This function has been somewhat optimised... */
579 static int cell_adj(game_state
*state
, int i
, int *ai
, int *ad
)
581 int n
= 0, a
, x
, y
, sx
, sy
, dx
, dy
, newi
;
582 int w
= state
->w
, h
= state
->h
;
584 sx
= i
% w
; sy
= i
/ w
;
586 for (a
= 0; a
< DIR_MAX
; a
++) {
588 dx
= dxs
[a
]; dy
= dys
[a
];
591 if (x
< 0 || y
< 0 || x
>= w
|| y
>= h
) break;
594 if (state
->nums
[newi
] == 0) {
604 static int new_game_fill(game_state
*state
, random_state
*rs
,
605 int headi
, int taili
)
607 int nfilled
, an
, ret
= 0, j
;
610 aidx
= snewn(state
->n
, int);
611 adir
= snewn(state
->n
, int);
613 debug(("new_game_fill: headi=%d, taili=%d.", headi
, taili
));
615 memset(state
->nums
, 0, state
->n
*sizeof(int));
617 state
->nums
[headi
] = 1;
618 state
->nums
[taili
] = state
->n
;
620 state
->dirs
[taili
] = 0;
623 while (nfilled
< state
->n
) {
624 /* Try and expand _from_ headi; keep going if there's only one
626 an
= cell_adj(state
, headi
, aidx
, adir
);
628 if (an
== 0) goto done
;
629 j
= random_upto(rs
, an
);
630 state
->dirs
[headi
] = adir
[j
];
631 state
->nums
[aidx
[j
]] = state
->nums
[headi
] + 1;
634 an
= cell_adj(state
, headi
, aidx
, adir
);
637 /* Try and expand _to_ taili; keep going if there's only one
639 an
= cell_adj(state
, taili
, aidx
, adir
);
641 if (an
== 0) goto done
;
642 j
= random_upto(rs
, an
);
643 state
->dirs
[aidx
[j
]] = DIR_OPPOSITE(adir
[j
]);
644 state
->nums
[aidx
[j
]] = state
->nums
[taili
] - 1;
647 an
= cell_adj(state
, taili
, aidx
, adir
);
650 /* If we get here we have headi and taili set but unconnected
651 * by direction: we need to set headi's direction so as to point
653 state
->dirs
[headi
] = whichdiri(state
, headi
, taili
);
655 /* it could happen that our last two weren't in line; if that's the
656 * case, we have to start again. */
657 if (state
->dirs
[headi
] != -1) ret
= 1;
665 /* Better generator: with the 'generate, sprinkle numbers, solve,
666 * repeat' algorithm we're _never_ generating anything greater than
667 * 6x6, and spending all of our time in new_game_fill (and very little
670 * So, new generator steps:
671 * generate the grid, at random (same as now). Numbers 1 and N get
672 immutable flag immediately.
673 * squirrel that away for the solved state.
675 * (solve:) Try and solve it.
676 * If we solved it, we're done:
677 * generate the description from current immutable numbers,
678 * free stuff that needs freeing,
679 * return description + solved state.
680 * If we didn't solve it:
681 * count #tiles in state we've made deductions about.
683 * randomise a scratch array.
684 * for each index in scratch (in turn):
685 * if the cell isn't empty, continue (through scratch array)
686 * set number + immutable in state.
687 * try and solve state.
688 * if we've solved it, we're done.
689 * otherwise, count #tiles. If it's more than we had before:
690 * good, break from this loop and re-randomise.
691 * otherwise (number didn't help):
692 * remove number and try next in scratch array.
693 * if we've got to the end of the scratch array, no luck:
694 free everything we need to, and go back to regenerate the grid.
697 static int solve_state(game_state
*state
);
699 static void debug_desc(const char *what
, game_state
*state
)
703 char *desc
= generate_desc(state
, 0);
704 debug(("%s game state: %dx%d:%s", what
, state
->w
, state
->h
, desc
));
710 /* Expects a fully-numbered game_state on input, and makes sure
711 * FLAG_IMMUTABLE is only set on those numbers we need to solve
712 * (as for a real new-game); returns 1 if it managed
713 * this (such that it could solve it), or 0 if not. */
714 static int new_game_strip(game_state
*state
, random_state
*rs
)
716 int *scratch
, i
, j
, ret
= 1;
717 game_state
*copy
= dup_game(state
);
719 debug(("new_game_strip."));
722 debug_desc("Stripped", copy
);
724 if (solve_state(copy
) > 0) {
725 debug(("new_game_strip: soluble immediately after strip."));
730 scratch
= snewn(state
->n
, int);
731 for (i
= 0; i
< state
->n
; i
++) scratch
[i
] = i
;
732 shuffle(scratch
, state
->n
, sizeof(int), rs
);
734 /* This is scungy. It might just be quick enough.
735 * It goes through, adding set numbers in empty squares
736 * until either we run out of empty squares (in the one
737 * we're half-solving) or else we solve it properly.
738 * NB that we run the entire solver each time, which
739 * strips the grid beforehand; we will save time if we
741 for (i
= 0; i
< state
->n
; i
++) {
743 if (copy
->nums
[j
] > 0 && copy
->nums
[j
] <= state
->n
)
744 continue; /* already solved to a real number here. */
745 assert(state
->nums
[j
] <= state
->n
);
746 debug(("new_game_strip: testing add IMMUTABLE number %d at square (%d,%d).",
747 state
->nums
[j
], j
%state
->w
, j
/state
->w
));
748 copy
->nums
[j
] = state
->nums
[j
];
749 copy
->flags
[j
] |= FLAG_IMMUTABLE
;
750 state
->flags
[j
] |= FLAG_IMMUTABLE
;
751 debug_state("Copy of state: ", copy
);
752 if (solve_state(copy
) > 0) goto solved
;
753 assert(check_nums(state
, copy
, 1));
759 debug(("new_game_strip: now solved."));
760 /* Since we added basically at random, try now to remove numbers
761 * and see if we can still solve it; if we can (still), really
762 * remove the number. Make sure we don't remove the anchor numbers
764 for (i
= 0; i
< state
->n
; i
++) {
766 if ((state
->flags
[j
] & FLAG_IMMUTABLE
) &&
767 (state
->nums
[j
] != 1 && state
->nums
[j
] != state
->n
)) {
768 debug(("new_game_strip: testing remove IMMUTABLE number %d at square (%d,%d).",
769 state
->nums
[j
], j
%state
->w
, j
/state
->w
));
770 state
->flags
[j
] &= ~FLAG_IMMUTABLE
;
771 dup_game_to(copy
, state
);
773 if (solve_state(copy
) > 0) {
774 assert(check_nums(state
, copy
, 0));
775 debug(("new_game_strip: OK, removing number"));
777 assert(state
->nums
[j
] <= state
->n
);
778 debug(("new_game_strip: cannot solve, putting IMMUTABLE back."));
779 copy
->nums
[j
] = state
->nums
[j
];
780 state
->flags
[j
] |= FLAG_IMMUTABLE
;
786 debug(("new_game_strip: %ssuccessful.", ret ?
"" : "not "));
792 static char *new_game_desc(game_params
*params
, random_state
*rs
,
793 char **aux
, int interactive
)
795 game_state
*state
= blank_game(params
->w
, params
->h
);
800 blank_game_into(state
);
802 /* keep trying until we fill successfully. */
804 if (params
->force_corner_start
) {
809 headi
= random_upto(rs
, state
->n
);
810 taili
= random_upto(rs
, state
->n
);
811 } while (headi
== taili
);
813 } while (!new_game_fill(state
, rs
, headi
, taili
));
815 debug_state("Filled game:", state
);
817 assert(state
->nums
[headi
] <= state
->n
);
818 assert(state
->nums
[taili
] <= state
->n
);
820 state
->flags
[headi
] |= FLAG_IMMUTABLE
;
821 state
->flags
[taili
] |= FLAG_IMMUTABLE
;
823 /* This will have filled in directions and _all_ numbers.
824 * Store the game definition for this, as the solved-state. */
825 if (!new_game_strip(state
, rs
)) {
830 game_state
*tosolve
= dup_game(state
);
831 assert(solve_state(tosolve
) > 0);
834 ret
= generate_desc(state
, 0);
839 static char *validate_desc(game_params
*params
, char *desc
)
843 unpick_desc(params
, desc
, NULL
, &ret
);
847 /* --- Linked-list and numbers array --- */
849 /* Assuming numbers are always up-to-date, there are only four possibilities
850 * for regions changing:
852 * 1) two differently-coloured regions being combined (the resulting colouring
853 * should be based on the larger of the two regions)
854 * 2) a numbered region having a single number added to the start (the
855 * region's colour will remain, and the numbers will shift by 1)
856 * 3) a numbered region having a single number added to the end (the
857 * region's colour and numbering remains as-is)
858 * 4) two unnumbered squares being joined (will pick the smallest unused set
859 * of colours to use for the new region).
861 * There should never be any complications with regions containing 3 colours
862 * being combined, since two of those colours should have been merged on a
866 /* New algorithm for working out numbering:
868 * At start, only remove numbers from cells with neither prev nor next.
869 * Search for all cells with !prev && next (head of chain); for each one:
870 * Search the group for a 'real' number: if we find one the num. for
871 the head of the chain is trivial.
872 * Otherwise, if we _don't_ have a number already:
873 * If head->next has a number, that number is the one we should use
874 * Otherwise pick the smallest unused colour set.
875 * and if we _do_ have a number already:
876 * Work out the size of this group (the dsf must already have been set up)
877 * Start enumerating through the group counting squares that have the
879 * If we reach a square with a different colour, work out which set is
880 bigger (ncol1 vs ncol2 == sz-ncol1), and use that colour
881 * If we reached a square with no colour (or the end of the group, which
882 would be weird under the circumstances) just keep the existing colour.
885 #define COLOUR(a) ((a) / (state->n+1))
886 #define START(c) ((c) * (state->n+1))
888 static int lowest_start(game_state
*state
, int *scratch
)
892 /* Fill in 'scratch' array with the currently-used colours... */
893 memset(scratch
, 0, state
->n
* sizeof(int));
894 for (i
= 0; i
< state
->n
; i
++) {
895 if (state
->nums
[i
] != 0)
896 scratch
[COLOUR(state
->nums
[i
])] = 1;
898 /* ... and return the first one that was unused. */
899 for (c
= 1; c
< state
->n
; c
++) { /* NB start at 1 */
903 assert(!"shouldn't get here");
904 return -1; /* suyb */
907 static int used_colour(game_state
*state
, int i
, int start
)
910 for (j
= 0; j
< i
; j
++) {
911 if (state
->nums
[j
] == start
)
917 static int head_number(game_state
*state
, int i
, int *scratch
)
919 int off
= 0, found
= 0, start
= 0, ss
, j
= i
, c
, n
, sz
;
920 const char *why
= NULL
;
922 assert(state
->prev
[i
] == -1 && state
->next
[i
] != -1);
924 /* Search through this chain looking for real numbers, checking that
925 * they match up (if there are more than one). */
927 if (state
->flags
[j
] & FLAG_IMMUTABLE
) {
928 ss
= state
->nums
[j
] - off
;
932 why
= "contains cell with immutable number";
933 } else if (start
!= ss
) {
934 debug(("head_number: chain with non-sequential numbers!"));
935 state
->impossible
= 1;
940 assert(j
!= i
); /* we have created a loop, obviously wrong */
942 if (found
) goto done
;
944 if (state
->nums
[i
] == 0) {
945 if (state
->nums
[state
->next
[i
]] != 0) {
946 /* make sure we start at a 0 offset. */
947 start
= START(COLOUR(state
->nums
[state
->next
[i
]]));
948 why
= "adding blank cell to head of numbered region";
950 start
= lowest_start(state
, scratch
);
951 why
= "lowest available colour group";
955 c
= COLOUR(state
->nums
[i
]);
957 sz
= dsf_size(state
->dsf
, i
);
959 while (state
->next
[j
] != -1) {
961 if (state
->nums
[j
] == 0) {
964 why
= "adding blank cell to end of numbered region";
967 if (COLOUR(state
->nums
[j
]) == c
)
970 int start_alternate
= START(COLOUR(state
->nums
[j
]));
971 if (n
< (sz
- n
) && !used_colour(state
, i
, start_alternate
)) {
972 start
= start_alternate
;
973 why
= "joining two coloured regions, swapping to larger colour";
976 why
= "joining two coloured regions, taking largest";
982 /* If we got here then we may have split a region into
983 * two; make sure we don't assign a colour we've already used. */
985 start
= (c
== 0) ?
lowest_start(state
, scratch
) : START(c
);
986 why
= "got to end of coloured region";
989 if (used_colour(state
, i
, start
)) {
990 start
= lowest_start(state
, scratch
);
991 why
= "split region in two, lowest available colour group";
996 assert(found
&& why
!= NULL
);
997 debug(("Chain at (%d,%d) numbered at %d: %s.",
998 i
%state
->w
, i
/state
->w
, start
, why
));
1003 static void debug_numbers(game_state
*state
)
1007 for (i
= 0; i
< state
->n
; i
++) {
1008 debug(("(%d,%d) --> (%d,%d) --> (%d,%d)",
1009 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]%w
,
1010 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]/w
,
1012 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]%w
,
1013 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]/w
));
1019 static void connect_numbers(game_state
*state
)
1023 dsf_init(state
->dsf
, state
->n
);
1024 for (i
= 0; i
< state
->n
; i
++) {
1025 if (state
->next
[i
] != -1) {
1026 assert(state
->prev
[state
->next
[i
]] == i
);
1027 di
= dsf_canonify(state
->dsf
, i
);
1028 dni
= dsf_canonify(state
->dsf
, state
->next
[i
]);
1030 debug(("connect_numbers: chain forms a loop."));
1031 state
->impossible
= 1;
1033 dsf_merge(state
->dsf
, di
, dni
);
1038 static void update_numbers(game_state
*state
)
1041 int *scratch
= snewn(state
->n
, int);
1043 for (i
= 0; i
< state
->n
; i
++)
1044 state
->numsi
[i
] = -1;
1046 for (i
= 0; i
< state
->n
; i
++) {
1047 if (state
->flags
[i
] & FLAG_IMMUTABLE
) {
1048 assert(state
->nums
[i
] >= 0);
1049 assert(state
->nums
[i
] <= state
->n
);
1050 state
->numsi
[state
->nums
[i
]] = i
;
1052 else if (state
->prev
[i
] == -1 && state
->next
[i
] == -1)
1055 connect_numbers(state
);
1057 for (i
= 0; i
< state
->n
; i
++) {
1058 /* Look for a cell that is the start of a chain
1059 * (has a next but no prev). */
1060 if (state
->prev
[i
] != -1 || state
->next
[i
] == -1) continue;
1062 nnum
= head_number(state
, i
, scratch
);
1065 if (nnum
> 0 && nnum
<= state
->n
)
1066 state
->numsi
[nnum
] = j
;
1067 state
->nums
[j
] = nnum
++;
1069 assert(j
!= i
); /* loop?! */
1072 /*debug_numbers(state);*/
1076 static int check_completion(game_state
*state
, int mark_errors
)
1078 int n
, j
, k
, error
= 0, complete
;
1080 /* NB This only marks errors that are possible to perpetrate with
1081 * the current UI in interpret_move. Things like forming loops in
1082 * linked sections and having numbers not add up should be forbidden
1083 * by the code elsewhere, so we don't bother marking those (because
1084 * it would add lots of tricky drawing code for very little gain). */
1086 for (j
= 0; j
< state
->n
; j
++)
1087 state
->flags
[j
] &= ~FLAG_ERROR
;
1090 /* Search for repeated numbers. */
1091 for (j
= 0; j
< state
->n
; j
++) {
1092 if (state
->nums
[j
] > 0 && state
->nums
[j
] <= state
->n
) {
1093 for (k
= j
+1; k
< state
->n
; k
++) {
1094 if (state
->nums
[k
] == state
->nums
[j
]) {
1096 state
->flags
[j
] |= FLAG_ERROR
;
1097 state
->flags
[k
] |= FLAG_ERROR
;
1105 /* Search and mark numbers n not pointing to n+1; if any numbers
1106 * are missing we know we've not completed. */
1108 for (n
= 1; n
< state
->n
; n
++) {
1109 if (state
->numsi
[n
] == -1 || state
->numsi
[n
+1] == -1)
1111 else if (!ispointingi(state
, state
->numsi
[n
], state
->numsi
[n
+1])) {
1113 state
->flags
[state
->numsi
[n
]] |= FLAG_ERROR
;
1114 state
->flags
[state
->numsi
[n
+1]] |= FLAG_ERROR
;
1118 /* make sure the link is explicitly made here; for instance, this
1119 * is nice if the user drags from 2 out (making 3) and a 4 is also
1120 * visible; this ensures that the link from 3 to 4 is also made. */
1122 makelink(state
, state
->numsi
[n
], state
->numsi
[n
+1]);
1126 /* Search and mark numbers less than 0, or 0 with links. */
1127 for (n
= 1; n
< state
->n
; n
++) {
1128 if ((state
->nums
[n
] < 0) ||
1129 (state
->nums
[n
] == 0 &&
1130 (state
->next
[n
] != -1 || state
->prev
[n
] != -1))) {
1133 state
->flags
[n
] |= FLAG_ERROR
;
1137 if (error
) return 0;
1140 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
1142 game_state
*state
= NULL
;
1144 unpick_desc(params
, desc
, &state
, NULL
);
1145 if (!state
) assert(!"new_game failed to unpick");
1147 update_numbers(state
);
1148 check_completion(state
, 1); /* update any auto-links */
1153 /* --- Solver --- */
1155 /* If a tile has a single tile it can link _to_, or there's only a single
1156 * location that can link to a given tile, fill that link in. */
1157 static int solve_single(game_state
*state
, game_state
*copy
, int *from
)
1159 int i
, j
, sx
, sy
, x
, y
, d
, poss
, w
=state
->w
, nlinks
= 0;
1161 /* The from array is a list of 'which square can link _to_ us';
1162 * we start off with from as '-1' (meaning 'not found'); if we find
1163 * something that can link to us it is set to that index, and then if
1164 * we find another we set it to -2. */
1166 memset(from
, -1, state
->n
*sizeof(int));
1168 /* poss is 'can I link to anything' with the same meanings. */
1170 for (i
= 0; i
< state
->n
; i
++) {
1171 if (state
->next
[i
] != -1) continue;
1172 if (state
->nums
[i
] == state
->n
) continue; /* no next from last no. */
1176 sx
= x
= i
%w
; sy
= y
= i
/w
;
1178 x
+= dxs
[d
]; y
+= dys
[d
];
1179 if (!INGRID(state
, x
, y
)) break;
1180 if (!isvalidmove(state
, 1, sx
, sy
, x
, y
)) continue;
1182 /* can't link to somewhere with a back-link we would have to
1183 * break (the solver just doesn't work like this). */
1185 if (state
->prev
[j
] != -1) continue;
1187 if (state
->nums
[i
] > 0 && state
->nums
[j
] > 0 &&
1188 state
->nums
[i
] <= state
->n
&& state
->nums
[j
] <= state
->n
&&
1189 state
->nums
[j
] == state
->nums
[i
]+1) {
1190 debug(("Solver: forcing link through existing consecutive numbers."));
1196 /* if there's been a valid move already, we have to move on;
1197 * we can't make any deductions here. */
1198 poss
= (poss
== -1) ? j
: -2;
1200 /* Modify the from array as described above (which is enumerating
1201 * what points to 'j' in a similar way). */
1202 from
[j
] = (from
[j
] == -1) ? i
: -2;
1205 /*debug(("Solver: (%d,%d) has multiple possible next squares.", sx, sy));*/
1207 } else if (poss
== -1) {
1208 debug(("Solver: nowhere possible for (%d,%d) to link to.", sx
, sy
));
1209 copy
->impossible
= 1;
1212 debug(("Solver: linking (%d,%d) to only possible next (%d,%d).",
1213 sx
, sy
, poss
%w
, poss
/w
));
1214 makelink(copy
, i
, poss
);
1219 for (i
= 0; i
< state
->n
; i
++) {
1220 if (state
->prev
[i
] != -1) continue;
1221 if (state
->nums
[i
] == 1) continue; /* no prev from 1st no. */
1224 if (from
[i
] == -1) {
1225 debug(("Solver: nowhere possible to link to (%d,%d)", x
, y
));
1226 copy
->impossible
= 1;
1228 } else if (from
[i
] == -2) {
1229 /*debug(("Solver: (%d,%d) has multiple possible prev squares.", x, y));*/
1232 debug(("Solver: linking only possible prev (%d,%d) to (%d,%d).",
1233 from
[i
]%w
, from
[i
]/w
, x
, y
));
1234 makelink(copy
, from
[i
], i
);
1242 /* Returns 1 if we managed to solve it, 0 otherwise. */
1243 static int solve_state(game_state
*state
)
1245 game_state
*copy
= dup_game(state
);
1246 int *scratch
= snewn(state
->n
, int), ret
;
1248 debug_state("Before solver: ", state
);
1251 update_numbers(state
);
1253 if (solve_single(state
, copy
, scratch
)) {
1254 dup_game_to(state
, copy
);
1255 if (state
->impossible
) break; else continue;
1262 update_numbers(state
);
1263 ret
= state
->impossible ?
-1 : check_completion(state
, 0);
1264 debug(("Solver finished: %s",
1265 ret
< 0 ?
"impossible" : ret
> 0 ?
"solved" : "not solved"));
1266 debug_state("After solver: ", state
);
1270 static char *solve_game(game_state
*state
, game_state
*currstate
,
1271 char *aux
, char **error
)
1273 game_state
*tosolve
;
1277 tosolve
= dup_game(currstate
);
1278 result
= solve_state(tosolve
);
1280 ret
= generate_desc(tosolve
, 1);
1282 if (ret
) return ret
;
1284 tosolve
= dup_game(state
);
1285 result
= solve_state(tosolve
);
1287 *error
= "Puzzle is impossible.";
1288 else if (result
== 0)
1289 *error
= "Unable to solve puzzle.";
1291 ret
= generate_desc(tosolve
, 1);
1298 /* --- UI and move routines. --- */
1304 int dragging
, drag_is_from
;
1305 int sx
, sy
; /* grid coords of start cell */
1306 int dx
, dy
; /* pixel coords of drag posn */
1309 static game_ui
*new_ui(game_state
*state
)
1311 game_ui
*ui
= snew(game_ui
);
1313 /* NB: if this is ever changed to as to require more than a structure
1314 * copy to clone, there's code that needs fixing in game_redraw too. */
1316 ui
->cx
= ui
->cy
= ui
->cshow
= 0;
1319 ui
->sx
= ui
->sy
= ui
->dx
= ui
->dy
= 0;
1324 static void free_ui(game_ui
*ui
)
1329 static char *encode_ui(game_ui
*ui
)
1334 static void decode_ui(game_ui
*ui
, char *encoding
)
1338 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
1339 game_state
*newstate
)
1341 if (!oldstate
->completed
&& newstate
->completed
)
1342 ui
->cshow
= ui
->dragging
= 0;
1345 struct game_drawstate
{
1346 int tilesize
, started
, solved
;
1350 double angle_offset
;
1352 int dragging
, dx
, dy
;
1356 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
1357 int mx
, int my
, int button
)
1359 int x
= FROMCOORD(mx
), y
= FROMCOORD(my
), w
= state
->w
;
1362 if (IS_CURSOR_MOVE(button
)) {
1363 move_cursor(button
, &ui
->cx
, &ui
->cy
, state
->w
, state
->h
, 0);
1366 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1367 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1370 } else if (IS_CURSOR_SELECT(button
)) {
1373 else if (ui
->dragging
) {
1374 ui
->dragging
= FALSE
;
1375 if (ui
->sx
== ui
->cx
&& ui
->sy
== ui
->cy
) return "";
1376 if (ui
->drag_is_from
) {
1377 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
)) return "";
1378 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
);
1380 if (!isvalidmove(state
, 0, ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
)) return "";
1381 sprintf(buf
, "L%d,%d-%d,%d", ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
);
1385 ui
->dragging
= TRUE
;
1388 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1389 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1390 ui
->drag_is_from
= (button
== CURSOR_SELECT
) ?
1 : 0;
1394 if (IS_MOUSE_DOWN(button
)) {
1396 ui
->cshow
= ui
->dragging
= 0;
1398 assert(!ui
->dragging
);
1399 if (!INGRID(state
, x
, y
)) return NULL
;
1401 if (button
== LEFT_BUTTON
) {
1402 /* disallow dragging from the final number. */
1403 if (state
->nums
[y
*w
+x
] == state
->n
) return NULL
;
1404 } else if (button
== RIGHT_BUTTON
) {
1405 /* disallow dragging to the first number. */
1406 if (state
->nums
[y
*w
+x
] == 1) return NULL
;
1409 ui
->dragging
= TRUE
;
1410 ui
->drag_is_from
= (button
== LEFT_BUTTON
) ?
1 : 0;
1417 } else if (IS_MOUSE_DRAG(button
) && ui
->dragging
) {
1421 } else if (IS_MOUSE_RELEASE(button
) && ui
->dragging
) {
1422 ui
->dragging
= FALSE
;
1423 if (ui
->sx
== x
&& ui
->sy
== y
) return ""; /* single click */
1425 if (!INGRID(state
, x
, y
)) {
1426 int si
= ui
->sy
*w
+ui
->sx
;
1427 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1429 sprintf(buf
, "%c%d,%d",
1430 ui
->drag_is_from ?
'C' : 'X', ui
->sx
, ui
->sy
);
1434 if (ui
->drag_is_from
) {
1435 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, x
, y
)) return "";
1436 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, x
, y
);
1438 if (!isvalidmove(state
, 0, x
, y
, ui
->sx
, ui
->sy
)) return "";
1439 sprintf(buf
, "L%d,%d-%d,%d", x
, y
, ui
->sx
, ui
->sy
);
1442 } /* else if (button == 'H' || button == 'h')
1443 return dupstr("H"); */
1444 else if ((button
== 'x' || button
== 'X') && ui
->cshow
) {
1445 int si
= ui
->cy
*w
+ ui
->cx
;
1446 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1448 sprintf(buf
, "%c%d,%d",
1449 (button
== 'x') ?
'C' : 'X', ui
->cx
, ui
->cy
);
1456 static void unlink_cell(game_state
*state
, int si
)
1458 debug(("Unlinking (%d,%d).", si
%state
->w
, si
/state
->w
));
1459 if (state
->prev
[si
] != -1) {
1460 debug((" ... removing prev link from (%d,%d).",
1461 state
->prev
[si
]%state
->w
, state
->prev
[si
]/state
->w
));
1462 state
->next
[state
->prev
[si
]] = -1;
1463 state
->prev
[si
] = -1;
1465 if (state
->next
[si
] != -1) {
1466 debug((" ... removing next link to (%d,%d).",
1467 state
->next
[si
]%state
->w
, state
->next
[si
]/state
->w
));
1468 state
->prev
[state
->next
[si
]] = -1;
1469 state
->next
[si
] = -1;
1473 static game_state
*execute_move(game_state
*state
, char *move
)
1475 game_state
*ret
= NULL
;
1476 int sx
, sy
, ex
, ey
, si
, ei
, w
= state
->w
;
1479 debug(("move: %s", move
));
1481 if (move
[0] == 'S') {
1487 p
.w
= state
->w
; p
.h
= state
->h
;
1488 valid
= validate_desc(&p
, move
+1);
1490 debug(("execute_move: move not valid: %s", valid
));
1493 ret
= dup_game(state
);
1494 tmp
= new_game(NULL
, &p
, move
+1);
1495 for (i
= 0; i
< state
->n
; i
++) {
1496 ret
->prev
[i
] = tmp
->prev
[i
];
1497 ret
->next
[i
] = tmp
->next
[i
];
1500 ret
->used_solve
= 1;
1501 } else if (sscanf(move
, "L%d,%d-%d,%d", &sx
, &sy
, &ex
, &ey
) == 4) {
1502 if (!isvalidmove(state
, 0, sx
, sy
, ex
, ey
)) return NULL
;
1504 ret
= dup_game(state
);
1506 si
= sy
*w
+sx
; ei
= ey
*w
+ex
;
1507 makelink(ret
, si
, ei
);
1508 } else if (sscanf(move
, "%c%d,%d", &c
, &sx
, &sy
) == 3) {
1509 if (c
!= 'C' && c
!= 'X') return NULL
;
1510 if (!INGRID(state
, sx
, sy
)) return NULL
;
1512 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1515 ret
= dup_game(state
);
1518 /* Unlink the single cell we dragged from the board. */
1519 unlink_cell(ret
, si
);
1521 int i
, set
, sset
= state
->nums
[si
] / (state
->n
+1);
1522 for (i
= 0; i
< state
->n
; i
++) {
1523 /* Unlink all cells in the same set as the one we dragged
1524 * from the board. */
1526 if (state
->nums
[i
] == 0) continue;
1527 set
= state
->nums
[i
] / (state
->n
+1);
1528 if (set
!= sset
) continue;
1530 unlink_cell(ret
, i
);
1533 } else if (strcmp(move
, "H") == 0) {
1534 ret
= dup_game(state
);
1538 update_numbers(ret
);
1539 if (check_completion(ret
, 1)) ret
->completed
= 1;
1545 /* ----------------------------------------------------------------------
1549 static void game_compute_size(game_params
*params
, int tilesize
,
1552 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1553 struct { int tilesize
, order
; } ads
, *ds
= &ads
;
1554 ads
.tilesize
= tilesize
;
1556 *x
= TILE_SIZE
* params
->w
+ 2 * BORDER
;
1557 *y
= TILE_SIZE
* params
->h
+ 2 * BORDER
;
1560 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
1561 game_params
*params
, int tilesize
)
1563 ds
->tilesize
= tilesize
;
1564 assert(TILE_SIZE
> 0);
1567 ds
->dragb
= blitter_new(dr
, BLITTER_SIZE
, BLITTER_SIZE
);
1570 /* Colours chosen from the webby palette to work as a background to black text,
1571 * W then some plausible approximation to pastelly ROYGBIV; we then interpolate
1572 * between consecutive pairs to give another 8 (and then the drawing routine
1573 * will reuse backgrounds). */
1574 static const unsigned long bgcols
[8] = {
1575 0xffffff, /* white */
1576 0xffa07a, /* lightsalmon */
1577 0x98fb98, /* green */
1578 0x7fffd4, /* aquamarine */
1579 0x9370db, /* medium purple */
1580 0xffa500, /* orange */
1581 0x87cefa, /* lightskyblue */
1582 0xffff00, /* yellow */
1585 static float *game_colours(frontend
*fe
, int *ncolours
)
1587 float *ret
= snewn(3 * NCOLOURS
, float);
1590 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
1592 for (i
= 0; i
< 3; i
++) {
1593 ret
[COL_NUMBER
* 3 + i
] = 0.0F
;
1594 ret
[COL_ARROW
* 3 + i
] = 0.0F
;
1595 ret
[COL_CURSOR
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 2.0F
;
1596 ret
[COL_GRID
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 1.3F
;
1598 ret
[COL_NUMBER_SET
* 3 + 0] = 0.0F
;
1599 ret
[COL_NUMBER_SET
* 3 + 1] = 0.0F
;
1600 ret
[COL_NUMBER_SET
* 3 + 2] = 0.9F
;
1602 ret
[COL_ERROR
* 3 + 0] = 1.0F
;
1603 ret
[COL_ERROR
* 3 + 1] = 0.0F
;
1604 ret
[COL_ERROR
* 3 + 2] = 0.0F
;
1606 ret
[COL_DRAG_ORIGIN
* 3 + 0] = 0.2F
;
1607 ret
[COL_DRAG_ORIGIN
* 3 + 1] = 1.0F
;
1608 ret
[COL_DRAG_ORIGIN
* 3 + 2] = 0.2F
;
1610 for (c
= 0; c
< 8; c
++) {
1611 ret
[(COL_B0
+ c
) * 3 + 0] = (float)((bgcols
[c
] & 0xff0000) >> 16) / 256.0F
;
1612 ret
[(COL_B0
+ c
) * 3 + 1] = (float)((bgcols
[c
] & 0xff00) >> 8) / 256.0F
;
1613 ret
[(COL_B0
+ c
) * 3 + 2] = (float)((bgcols
[c
] & 0xff)) / 256.0F
;
1615 for (c
= 0; c
< 8; c
++) {
1616 for (i
= 0; i
< 3; i
++) {
1617 ret
[(COL_B0
+ 8 + c
) * 3 + i
] =
1618 (ret
[(COL_B0
+ c
) * 3 + i
] + ret
[(COL_B0
+ c
+ 1) * 3 + i
]) / 2.0F
;
1622 #define average(r,a,b,w) do { \
1623 for (i = 0; i < 3; i++) \
1624 ret[(r)*3+i] = ret[(a)*3+i] + w * (ret[(b)*3+i] - ret[(a)*3+i]); \
1626 average(COL_ARROW_BG_DIM
, COL_BACKGROUND
, COL_ARROW
, 0.1F
);
1627 average(COL_NUMBER_SET_MID
, COL_B0
, COL_NUMBER_SET
, 0.3F
);
1628 for (c
= 0; c
< NBACKGROUNDS
; c
++) {
1629 /* I assume here that COL_ARROW and COL_NUMBER are the same.
1630 * Otherwise I'd need two sets of COL_M*. */
1631 average(COL_M0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.3F
);
1632 average(COL_D0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.1F
);
1633 average(COL_X0
+ c
, COL_BACKGROUND
, COL_B0
+ c
, 0.5F
);
1636 *ncolours
= NCOLOURS
;
1640 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1642 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1645 ds
->tilesize
= ds
->started
= ds
->solved
= 0;
1650 ds
->nums
= snewn(state
->n
, int);
1651 ds
->dirp
= snewn(state
->n
, int);
1652 ds
->f
= snewn(state
->n
, unsigned int);
1653 for (i
= 0; i
< state
->n
; i
++) {
1659 ds
->angle_offset
= 0.0F
;
1661 ds
->dragging
= ds
->dx
= ds
->dy
= 0;
1667 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1672 if (ds
->dragb
) blitter_free(dr
, ds
->dragb
);
1677 /* cx, cy are top-left corner. sz is the 'radius' of the arrow.
1678 * ang is in radians, clockwise from 0 == straight up. */
1679 static void draw_arrow(drawing
*dr
, int cx
, int cy
, int sz
, double ang
,
1680 int cfill
, int cout
)
1683 int xdx
, ydx
, xdy
, ydy
, xdx3
, xdy3
;
1684 double s
= sin(ang
), c
= cos(ang
);
1686 xdx3
= (int)(sz
* (c
/3 + 1) + 0.5) - sz
;
1687 xdy3
= (int)(sz
* (s
/3 + 1) + 0.5) - sz
;
1688 xdx
= (int)(sz
* (c
+ 1) + 0.5) - sz
;
1689 xdy
= (int)(sz
* (s
+ 1) + 0.5) - sz
;
1694 coords
[2*0 + 0] = cx
- ydx
;
1695 coords
[2*0 + 1] = cy
- ydy
;
1696 coords
[2*1 + 0] = cx
+ xdx
;
1697 coords
[2*1 + 1] = cy
+ xdy
;
1698 coords
[2*2 + 0] = cx
+ xdx3
;
1699 coords
[2*2 + 1] = cy
+ xdy3
;
1700 coords
[2*3 + 0] = cx
+ xdx3
+ ydx
;
1701 coords
[2*3 + 1] = cy
+ xdy3
+ ydy
;
1702 coords
[2*4 + 0] = cx
- xdx3
+ ydx
;
1703 coords
[2*4 + 1] = cy
- xdy3
+ ydy
;
1704 coords
[2*5 + 0] = cx
- xdx3
;
1705 coords
[2*5 + 1] = cy
- xdy3
;
1706 coords
[2*6 + 0] = cx
- xdx
;
1707 coords
[2*6 + 1] = cy
- xdy
;
1709 draw_polygon(dr
, coords
, 7, cfill
, cout
);
1712 static void draw_arrow_dir(drawing
*dr
, int cx
, int cy
, int sz
, int dir
,
1713 int cfill
, int cout
, double angle_offset
)
1715 double ang
= 2.0 * PI
* (double)dir
/ 8.0 + angle_offset
;
1716 draw_arrow(dr
, cx
, cy
, sz
, ang
, cfill
, cout
);
1719 /* cx, cy are centre coordinates.. */
1720 static void draw_star(drawing
*dr
, int cx
, int cy
, int rad
, int npoints
,
1721 int cfill
, int cout
, double angle_offset
)
1726 assert(npoints
> 0);
1728 coords
= snewn(npoints
* 2 * 2, int);
1730 for (n
= 0; n
< npoints
* 2; n
++) {
1731 a
= 2.0 * PI
* ((double)n
/ ((double)npoints
* 2.0)) + angle_offset
;
1732 r
= (n
% 2) ?
(double)rad
/2.0 : (double)rad
;
1734 /* We're rotating the point at (0, -r) by a degrees */
1735 coords
[2*n
+0] = cx
+ (int)( r
* sin(a
));
1736 coords
[2*n
+1] = cy
+ (int)(-r
* cos(a
));
1738 draw_polygon(dr
, coords
, npoints
*2, cfill
, cout
);
1742 static int num2col(game_drawstate
*ds
, int num
)
1744 int set
= num
/ (ds
->n
+1);
1746 if (num
<= 0) return COL_B0
;
1747 return COL_B0
+ (set
% 16);
1750 #define ARROW_HALFSZ (7 * TILE_SIZE / 32)
1752 #define F_CUR 0x001 /* Cursor on this tile. */
1753 #define F_DRAG_SRC 0x002 /* Tile is source of a drag. */
1754 #define F_ERROR 0x004 /* Tile marked in error. */
1755 #define F_IMMUTABLE 0x008 /* Tile (number) is immutable. */
1756 #define F_ARROW_POINT 0x010 /* Tile points to other tile */
1757 #define F_ARROW_INPOINT 0x020 /* Other tile points in here. */
1758 #define F_DIM 0x040 /* Tile is dim */
1760 static void tile_redraw(drawing
*dr
, game_drawstate
*ds
, int tx
, int ty
,
1761 int dir
, int dirp
, int num
, unsigned int f
,
1762 double angle_offset
, int print_ink
)
1764 int cb
= TILE_SIZE
/ 16, textsz
;
1766 int arrowcol
, sarrowcol
, setcol
, textcol
;
1767 int acx
, acy
, asz
, empty
= 0;
1769 if (num
== 0 && !(f
& F_ARROW_POINT
) && !(f
& F_ARROW_INPOINT
)) {
1772 * We don't display text in empty cells: typically these are
1773 * signified by num=0. However, in some cases a cell could
1774 * have had the number 0 assigned to it if the user made an
1775 * error (e.g. tried to connect a chain of length 5 to the
1776 * immutable number 4) so we _do_ display the 0 if the cell
1777 * has a link in or a link out.
1781 /* Calculate colours. */
1783 if (print_ink
>= 0) {
1785 * We're printing, so just do everything in black.
1787 arrowcol
= textcol
= print_ink
;
1788 setcol
= sarrowcol
= -1; /* placate optimiser */
1791 setcol
= empty ? COL_BACKGROUND
: num2col(ds
, num
);
1793 #define dim(fg,bg) ( \
1794 (bg)==COL_BACKGROUND ? COL_ARROW_BG_DIM : \
1795 (bg) + COL_D0 - COL_B0 \
1798 #define mid(fg,bg) ( \
1799 (fg)==COL_NUMBER_SET ? COL_NUMBER_SET_MID : \
1800 (bg) + COL_M0 - COL_B0 \
1803 #define dimbg(bg) ( \
1804 (bg)==COL_BACKGROUND ? COL_BACKGROUND : \
1805 (bg) + COL_X0 - COL_B0 \
1808 if (f
& F_DRAG_SRC
) arrowcol
= COL_DRAG_ORIGIN
;
1809 else if (f
& F_DIM
) arrowcol
= dim(COL_ARROW
, setcol
);
1810 else if (f
& F_ARROW_POINT
) arrowcol
= mid(COL_ARROW
, setcol
);
1811 else arrowcol
= COL_ARROW
;
1813 if (f
& (F_ERROR
)) textcol
= COL_ERROR
;
1815 if (f
& F_IMMUTABLE
) textcol
= COL_NUMBER_SET
;
1816 else textcol
= COL_NUMBER
;
1818 if (f
& F_DIM
) textcol
= dim(textcol
, setcol
);
1819 else if (((f
& F_ARROW_POINT
) || num
==ds
->n
) &&
1820 ((f
& F_ARROW_INPOINT
) || num
==1))
1821 textcol
= mid(textcol
, setcol
);
1824 if (f
& F_DIM
) sarrowcol
= dim(COL_ARROW
, setcol
);
1825 else sarrowcol
= COL_ARROW
;
1828 /* Clear tile background */
1830 if (print_ink
< 0) {
1831 draw_rect(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
,
1832 (f
& F_DIM
) ?
dimbg(setcol
) : setcol
);
1835 /* Draw large (outwards-pointing) arrow. */
1837 asz
= ARROW_HALFSZ
; /* 'radius' of arrow/star. */
1838 acx
= tx
+TILE_SIZE
/2+asz
; /* centre x */
1839 acy
= ty
+TILE_SIZE
/2+asz
; /* centre y */
1841 if (num
== ds
->n
&& (f
& F_IMMUTABLE
))
1842 draw_star(dr
, acx
, acy
, asz
, 5, arrowcol
, arrowcol
, angle_offset
);
1844 draw_arrow_dir(dr
, acx
, acy
, asz
, dir
, arrowcol
, arrowcol
, angle_offset
);
1845 if (print_ink
< 0 && (f
& F_CUR
))
1846 draw_rect_corners(dr
, acx
, acy
, asz
+1, COL_CURSOR
);
1848 /* Draw dot iff this tile requires a predecessor and doesn't have one. */
1850 if (print_ink
< 0) {
1851 acx
= tx
+TILE_SIZE
/2-asz
;
1852 acy
= ty
+TILE_SIZE
/2+asz
;
1854 if (!(f
& F_ARROW_INPOINT
) && num
!= 1) {
1855 draw_circle(dr
, acx
, acy
, asz
/ 4, sarrowcol
, sarrowcol
);
1859 /* Draw text (number or set). */
1862 int set
= (num
<= 0) ?
0 : num
/ (ds
->n
+1);
1864 if (set
== 0 || num
<= 0) {
1865 sprintf(buf
, "%d", num
);
1867 int n
= num
% (ds
->n
+1);
1870 sprintf(buf
, "%c", (int)(set
+'a'-1));
1872 sprintf(buf
, "%c+%d", (int)(set
+'a'-1), n
);
1874 textsz
= min(2*asz
, (TILE_SIZE
- 2 * cb
) / (int)strlen(buf
));
1875 draw_text(dr
, tx
+ cb
, ty
+ TILE_SIZE
/4, FONT_VARIABLE
, textsz
,
1876 ALIGN_VCENTRE
| ALIGN_HLEFT
, textcol
, buf
);
1879 if (print_ink
< 0) {
1880 draw_rect_outline(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
, COL_GRID
);
1881 draw_update(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
);
1885 static void draw_drag_indicator(drawing
*dr
, game_drawstate
*ds
,
1886 game_state
*state
, game_ui
*ui
, int validdrag
)
1888 int dir
, w
= ds
->w
, acol
= COL_ARROW
;
1889 int fx
= FROMCOORD(ui
->dx
), fy
= FROMCOORD(ui
->dy
);
1893 /* If we could move here, lock the arrow to the appropriate direction. */
1894 dir
= ui
->drag_is_from ? state
->dirs
[ui
->sy
*w
+ui
->sx
] : state
->dirs
[fy
*w
+fx
];
1896 ang
= (2.0 * PI
* dir
) / 8.0; /* similar to calculation in draw_arrow_dir. */
1898 /* Draw an arrow pointing away from/towards the origin cell. */
1899 int ox
= COORD(ui
->sx
) + TILE_SIZE
/2, oy
= COORD(ui
->sy
) + TILE_SIZE
/2;
1900 double tana
, offset
;
1901 double xdiff
= fabs(ox
- ui
->dx
), ydiff
= fabs(oy
- ui
->dy
);
1904 ang
= (oy
> ui
->dy
) ?
0.0F
: PI
;
1905 } else if (ydiff
== 0) {
1906 ang
= (ox
> ui
->dx
) ?
3.0F
*PI
/2.0F
: PI
/2.0F
;
1908 if (ui
->dx
> ox
&& ui
->dy
< oy
) {
1909 tana
= xdiff
/ ydiff
;
1911 } else if (ui
->dx
> ox
&& ui
->dy
> oy
) {
1912 tana
= ydiff
/ xdiff
;
1914 } else if (ui
->dx
< ox
&& ui
->dy
> oy
) {
1915 tana
= xdiff
/ ydiff
;
1918 tana
= ydiff
/ xdiff
;
1919 offset
= 3.0F
* PI
/ 2.0F
;
1921 ang
= atan(tana
) + offset
;
1924 if (!ui
->drag_is_from
) ang
+= PI
; /* point to origin, not away from. */
1927 draw_arrow(dr
, ui
->dx
, ui
->dy
, ARROW_HALFSZ
, ang
, acol
, acol
);
1930 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
1931 game_state
*state
, int dir
, game_ui
*ui
,
1932 float animtime
, float flashtime
)
1934 int x
, y
, i
, w
= ds
->w
, dirp
, force
= 0;
1936 double angle_offset
= 0.0;
1937 game_state
*postdrop
= NULL
;
1939 if (flashtime
> 0.0F
)
1940 angle_offset
= 2.0 * PI
* (flashtime
/ FLASH_SPIN
);
1941 if (angle_offset
!= ds
->angle_offset
) {
1942 ds
->angle_offset
= angle_offset
;
1948 blitter_load(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
1949 draw_update(dr
, ds
->dx
, ds
->dy
, BLITTER_SIZE
, BLITTER_SIZE
);
1950 ds
->dragging
= FALSE
;
1953 /* If an in-progress drag would make a valid move if finished, we
1954 * reflect that move in the board display. We let interpret_move do
1955 * most of the heavy lifting for us: we have to copy the game_ui so
1956 * as not to stomp on the real UI's drag state. */
1958 game_ui uicopy
= *ui
;
1959 char *movestr
= interpret_move(state
, &uicopy
, ds
, ui
->dx
, ui
->dy
, LEFT_RELEASE
);
1961 if (movestr
!= NULL
&& strcmp(movestr
, "") != 0) {
1962 postdrop
= execute_move(state
, movestr
);
1970 int aw
= TILE_SIZE
* state
->w
;
1971 int ah
= TILE_SIZE
* state
->h
;
1972 draw_rect(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
, COL_BACKGROUND
);
1973 draw_rect_outline(dr
, BORDER
- 1, BORDER
- 1, aw
+ 2, ah
+ 2, COL_GRID
);
1974 draw_update(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
);
1976 for (x
= 0; x
< state
->w
; x
++) {
1977 for (y
= 0; y
< state
->h
; y
++) {
1982 if (ui
->cshow
&& x
== ui
->cx
&& y
== ui
->cy
)
1986 if (x
== ui
->sx
&& y
== ui
->sy
)
1988 else if (ui
->drag_is_from
) {
1989 if (!ispointing(state
, ui
->sx
, ui
->sy
, x
, y
))
1992 if (!ispointing(state
, x
, y
, ui
->sx
, ui
->sy
))
1997 if (state
->impossible
||
1998 state
->nums
[i
] < 0 || state
->flags
[i
] & FLAG_ERROR
)
2000 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
2003 if (state
->next
[i
] != -1)
2006 if (state
->prev
[i
] != -1) {
2007 /* Currently the direction here is from our square _back_
2008 * to its previous. We could change this to give the opposite
2009 * sense to the direction. */
2010 f
|= F_ARROW_INPOINT
;
2011 dirp
= whichdir(x
, y
, state
->prev
[i
]%w
, state
->prev
[i
]/w
);
2014 if (state
->nums
[i
] != ds
->nums
[i
] ||
2015 f
!= ds
->f
[i
] || dirp
!= ds
->dirp
[i
] ||
2016 force
|| !ds
->started
) {
2018 BORDER
+ x
* TILE_SIZE
,
2019 BORDER
+ y
* TILE_SIZE
,
2020 state
->dirs
[i
], dirp
, state
->nums
[i
], f
,
2022 ds
->nums
[i
] = state
->nums
[i
];
2029 ds
->dragging
= TRUE
;
2030 ds
->dx
= ui
->dx
- BLITTER_SIZE
/2;
2031 ds
->dy
= ui
->dy
- BLITTER_SIZE
/2;
2032 blitter_save(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
2034 draw_drag_indicator(dr
, ds
, state
, ui
, postdrop ?
1 : 0);
2036 if (postdrop
) free_game(postdrop
);
2037 if (!ds
->started
) ds
->started
= TRUE
;
2040 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
2041 int dir
, game_ui
*ui
)
2046 static float game_flash_length(game_state
*oldstate
, game_state
*newstate
,
2047 int dir
, game_ui
*ui
)
2049 if (!oldstate
->completed
&&
2050 newstate
->completed
&& !newstate
->used_solve
)
2056 static int game_timing_state(game_state
*state
, game_ui
*ui
)
2061 static void game_print_size(game_params
*params
, float *x
, float *y
)
2065 game_compute_size(params
, 1300, &pw
, &ph
);
2070 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
2072 int ink
= print_mono_colour(dr
, 0);
2075 /* Fake up just enough of a drawstate */
2076 game_drawstate ads
, *ds
= &ads
;
2077 ds
->tilesize
= tilesize
;
2083 print_line_width(dr
, TILE_SIZE
/ 40);
2084 for (x
= 1; x
< state
->w
; x
++)
2085 draw_line(dr
, COORD(x
), COORD(0), COORD(x
), COORD(state
->h
), ink
);
2086 for (y
= 1; y
< state
->h
; y
++)
2087 draw_line(dr
, COORD(0), COORD(y
), COORD(state
->w
), COORD(y
), ink
);
2088 print_line_width(dr
, 2*TILE_SIZE
/ 40);
2089 draw_rect_outline(dr
, COORD(0), COORD(0), TILE_SIZE
*state
->w
,
2090 TILE_SIZE
*state
->h
, ink
);
2093 * Arrows and numbers.
2095 print_line_width(dr
, 0);
2096 for (y
= 0; y
< state
->h
; y
++)
2097 for (x
= 0; x
< state
->w
; x
++)
2098 tile_redraw(dr
, ds
, COORD(x
), COORD(y
), state
->dirs
[y
*state
->w
+x
],
2099 0, state
->nums
[y
*state
->w
+x
], 0, 0.0, ink
);
2103 #define thegame signpost
2106 const struct game thegame
= {
2107 "Signpost", "games.signpost", "signpost",
2114 TRUE
, game_configure
, custom_params
,
2122 TRUE
, game_can_format_as_text_now
, game_text_format
,
2130 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
2133 game_free_drawstate
,
2137 TRUE
, FALSE
, game_print_size
, game_print
,
2138 FALSE
, /* wants_statusbar */
2139 FALSE
, game_timing_state
,
2140 REQUIRE_RBUTTON
| REQUIRE_NUMPAD
, /* flags */
2143 #ifdef STANDALONE_SOLVER
2148 const char *quis
= NULL
;
2151 void usage(FILE *out
) {
2152 fprintf(out
, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis
);
2155 static void cycle_seed(char **seedstr
, random_state
*rs
)
2161 newseed
[0] = '1' + (char)random_upto(rs
, 9);
2162 for (j
= 1; j
< 15; j
++)
2163 newseed
[j
] = '0' + (char)random_upto(rs
, 10);
2165 *seedstr
= dupstr(newseed
);
2168 static void start_soak(game_params
*p
, char *seedstr
)
2170 time_t tt_start
, tt_now
, tt_last
;
2173 long n
= 0, nnums
= 0, i
;
2176 tt_start
= tt_now
= time(NULL
);
2177 printf("Soak-generating a %dx%d grid.\n", p
->w
, p
->h
);
2180 rs
= random_new(seedstr
, strlen(seedstr
));
2181 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2183 state
= thegame
.new_game(NULL
, p
, desc
);
2184 for (i
= 0; i
< state
->n
; i
++) {
2185 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
2188 thegame
.free_game(state
);
2191 cycle_seed(&seedstr
, rs
);
2195 tt_last
= time(NULL
);
2196 if (tt_last
> tt_now
) {
2198 printf("%ld total, %3.1f/s, %3.1f nums/grid (%3.1f%%).\n",
2200 (double)n
/ ((double)tt_now
- tt_start
),
2201 (double)nnums
/ (double)n
,
2202 ((double)nnums
* 100.0) / ((double)n
* (double)p
->w
* (double)p
->h
) );
2207 static void process_desc(char *id
)
2209 char *desc
, *err
, *solvestr
;
2213 printf("%s\n ", id
);
2215 desc
= strchr(id
, ':');
2217 fprintf(stderr
, "%s: expecting game description.", quis
);
2223 p
= thegame
.default_params();
2224 thegame
.decode_params(p
, id
);
2225 err
= thegame
.validate_params(p
, 1);
2227 fprintf(stderr
, "%s: %s", quis
, err
);
2228 thegame
.free_params(p
);
2232 err
= thegame
.validate_desc(p
, desc
);
2234 fprintf(stderr
, "%s: %s\nDescription: %s\n", quis
, err
, desc
);
2235 thegame
.free_params(p
);
2239 s
= thegame
.new_game(NULL
, p
, desc
);
2241 solvestr
= thegame
.solve(s
, s
, NULL
, &err
);
2243 fprintf(stderr
, "%s\n", err
);
2245 printf("Puzzle is soluble.\n");
2247 thegame
.free_game(s
);
2248 thegame
.free_params(p
);
2251 int main(int argc
, const char *argv
[])
2253 char *id
= NULL
, *desc
, *err
, *aux
= NULL
;
2254 int soak
= 0, verbose
= 0, stdin_desc
= 0, n
= 1, i
;
2255 char *seedstr
= NULL
, newseed
[16];
2257 setvbuf(stdout
, NULL
, _IONBF
, 0);
2260 while (--argc
> 0) {
2261 char *p
= (char*)(*++argv
);
2262 if (!strcmp(p
, "-v") || !strcmp(p
, "--verbose"))
2264 else if (!strcmp(p
, "--stdin"))
2266 else if (!strcmp(p
, "-e") || !strcmp(p
, "--seed")) {
2267 seedstr
= dupstr(*++argv
);
2269 } else if (!strcmp(p
, "-n") || !strcmp(p
, "--number")) {
2272 } else if (!strcmp(p
, "-s") || !strcmp(p
, "--soak")) {
2274 } else if (*p
== '-') {
2275 fprintf(stderr
, "%s: unrecognised option `%s'\n", argv
[0], p
);
2283 sprintf(newseed
, "%lu", time(NULL
));
2284 seedstr
= dupstr(newseed
);
2286 if (id
|| !stdin_desc
) {
2287 if (id
&& strchr(id
, ':')) {
2288 /* Parameters and description passed on cmd-line:
2289 * try and solve it. */
2292 /* No description passed on cmd-line: decode parameters
2293 * (with optional seed too) */
2295 game_params
*p
= thegame
.default_params();
2298 char *cmdseed
= strchr(id
, '#');
2302 seedstr
= dupstr(cmdseed
);
2305 thegame
.decode_params(p
, id
);
2308 err
= thegame
.validate_params(p
, 1);
2310 fprintf(stderr
, "%s: %s", quis
, err
);
2311 thegame
.free_params(p
);
2315 /* We have a set of valid parameters; either soak with it
2316 * or generate a single game description and print to stdout. */
2318 start_soak(p
, seedstr
);
2320 char *pstring
= thegame
.encode_params(p
, 0);
2322 for (i
= 0; i
< n
; i
++) {
2323 random_state
*rs
= random_new(seedstr
, strlen(seedstr
));
2325 if (verbose
) printf("%s#%s\n", pstring
, seedstr
);
2326 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2327 printf("%s:%s\n", pstring
, desc
);
2330 cycle_seed(&seedstr
, rs
);
2337 thegame
.free_params(p
);
2344 while (fgets(buf
, sizeof(buf
), stdin
)) {
2345 buf
[strcspn(buf
, "\r\n")] = '\0';
2357 /* vim: set shiftwidth=4 tabstop=8: */