2 * signpost.c: implementation of the janko game 'arrow path'
4 * Remaining troublesome games:
18 #define PREFERRED_TILE_SIZE 48
19 #define TILE_SIZE (ds->tilesize)
20 #define BLITTER_SIZE TILE_SIZE
21 #define BORDER (TILE_SIZE / 2)
23 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
24 #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
26 #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
28 #define FLASH_SPIN 0.7F
30 #define NBACKGROUNDS 16
33 COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
,
34 COL_GRID
, COL_CURSOR
, COL_ERROR
, COL_DRAG_ORIGIN
,
35 COL_ARROW
, COL_ARROW_BG_DIM
,
36 COL_NUMBER
, COL_NUMBER_SET
, COL_NUMBER_SET_MID
,
37 COL_B0
, /* background colours */
38 COL_M0
= COL_B0
+ 1*NBACKGROUNDS
, /* mid arrow colours */
39 COL_D0
= COL_B0
+ 2*NBACKGROUNDS
, /* dim arrow colours */
40 COL_X0
= COL_B0
+ 3*NBACKGROUNDS
, /* dim arrow colours */
41 NCOLOURS
= COL_B0
+ 4*NBACKGROUNDS
46 int force_corner_start
;
49 enum { DIR_N
= 0, DIR_NE
, DIR_E
, DIR_SE
, DIR_S
, DIR_SW
, DIR_W
, DIR_NW
, DIR_MAX
};
50 static const char *dirstrings
[8] = { "N ", "NE", "E ", "SE", "S ", "SW", "W ", "NW" };
52 static const int dxs
[DIR_MAX
] = { 0, 1, 1, 1, 0, -1, -1, -1 };
53 static const int dys
[DIR_MAX
] = { -1, -1, 0, 1, 1, 1, 0, -1 };
55 #define DIR_OPPOSITE(d) ((d+4)%8)
59 int completed
, used_solve
, impossible
;
60 int *dirs
; /* direction enums, size n */
61 int *nums
; /* numbers, size n */
62 unsigned int *flags
; /* flags, size n */
63 int *next
, *prev
; /* links to other cell indexes, size n (-1 absent) */
64 int *dsf
; /* connects regions with a dsf. */
65 int *numsi
; /* for each number, which index is it in? (-1 absent) */
68 #define FLAG_IMMUTABLE 1
71 /* --- Generally useful functions --- */
73 #define ISREALNUM(state, num) ((num) > 0 && (num) <= (state)->n)
75 static int whichdir(int fromx
, int fromy
, int tox
, int toy
)
82 if (dx
&& dy
&& abs(dx
) != abs(dy
)) return -1;
84 if (dx
) dx
= dx
/ abs(dx
); /* limit to (-1, 0, 1) */
85 if (dy
) dy
= dy
/ abs(dy
); /* ditto */
87 for (i
= 0; i
< DIR_MAX
; i
++) {
88 if (dx
== dxs
[i
] && dy
== dys
[i
]) return i
;
93 static int whichdiri(game_state
*state
, int fromi
, int toi
)
96 return whichdir(fromi
%w
, fromi
/w
, toi
%w
, toi
/w
);
99 static int ispointing(game_state
*state
, int fromx
, int fromy
, int tox
, int toy
)
101 int w
= state
->w
, dir
= state
->dirs
[fromy
*w
+fromx
];
103 /* (by convention) squares do not point to themselves. */
104 if (fromx
== tox
&& fromy
== toy
) return 0;
106 /* the final number points to nothing. */
107 if (state
->nums
[fromy
*w
+ fromx
] == state
->n
) return 0;
110 if (!INGRID(state
, fromx
, fromy
)) return 0;
111 if (fromx
== tox
&& fromy
== toy
) return 1;
112 fromx
+= dxs
[dir
]; fromy
+= dys
[dir
];
114 return 0; /* not reached */
117 static int ispointingi(game_state
*state
, int fromi
, int toi
)
120 return ispointing(state
, fromi
%w
, fromi
/w
, toi
%w
, toi
/w
);
123 /* Taking the number 'num', work out the gap between it and the next
124 * available number up or down (depending on d). Return 1 if the region
125 * at (x,y) will fit in that gap, or 0 otherwise. */
126 static int move_couldfit(game_state
*state
, int num
, int d
, int x
, int y
)
128 int n
, gap
, i
= y
*state
->w
+x
, sz
;
131 /* The 'gap' is the number of missing numbers in the grid between
132 * our number and the next one in the sequence (up or down), or
133 * the end of the sequence (if we happen not to have 1/n present) */
134 for (n
= num
+ d
, gap
= 0;
135 ISREALNUM(state
, n
) && state
->numsi
[n
] == -1;
136 n
+= d
, gap
++) ; /* empty loop */
139 /* no gap, so the only allowable move is that that directly
140 * links the two numbers. */
142 return (n
== num
+d
) ?
0 : 1;
144 if (state
->prev
[i
] == -1 && state
->next
[i
] == -1)
145 return 1; /* single unconnected square, always OK */
147 sz
= dsf_size(state
->dsf
, i
);
148 return (sz
> gap
) ?
0 : 1;
151 static int isvalidmove(game_state
*state
, int clever
,
152 int fromx
, int fromy
, int tox
, int toy
)
154 int w
= state
->w
, from
= fromy
*w
+fromx
, to
= toy
*w
+tox
;
157 if (!INGRID(state
, fromx
, fromy
) || !INGRID(state
, tox
, toy
))
160 /* can only move where we point */
161 if (!ispointing(state
, fromx
, fromy
, tox
, toy
))
164 nfrom
= state
->nums
[from
]; nto
= state
->nums
[to
];
166 /* can't move _from_ the final number, or _to_ the 1. */
167 if (nfrom
== state
->n
|| nto
== 1)
170 /* can't create a new connection between cells in the same region
171 * as that would create a loop. */
172 if (dsf_canonify(state
->dsf
, from
) == dsf_canonify(state
->dsf
, to
))
175 /* if both cells are actual numbers, can't drag if we're not
176 * one digit apart. */
177 if (ISREALNUM(state
, nfrom
) && ISREALNUM(state
, nto
)) {
180 } else if (clever
&& ISREALNUM(state
, nfrom
)) {
181 if (!move_couldfit(state
, nfrom
, +1, tox
, toy
))
183 } else if (clever
&& ISREALNUM(state
, nto
)) {
184 if (!move_couldfit(state
, nto
, -1, fromx
, fromy
))
191 static void makelink(game_state
*state
, int from
, int to
)
193 if (state
->next
[from
] != -1)
194 state
->prev
[state
->next
[from
]] = -1;
195 state
->next
[from
] = to
;
197 if (state
->prev
[to
] != -1)
198 state
->next
[state
->prev
[to
]] = -1;
199 state
->prev
[to
] = from
;
202 static int game_can_format_as_text_now(game_params
*params
)
204 if (params
->w
* params
->h
>= 100) return 0;
208 static char *game_text_format(game_state
*state
)
210 int len
= state
->h
* 2 * (4*state
->w
+ 1) + state
->h
+ 2;
211 int x
, y
, i
, num
, n
, set
;
214 p
= ret
= snewn(len
, char);
216 for (y
= 0; y
< state
->h
; y
++) {
217 for (x
= 0; x
< state
->h
; x
++) {
219 *p
++ = dirstrings
[state
->dirs
[i
]][0];
220 *p
++ = dirstrings
[state
->dirs
[i
]][1];
221 *p
++ = (state
->flags
[i
] & FLAG_IMMUTABLE
) ?
'I' : ' ';
225 for (x
= 0; x
< state
->h
; x
++) {
227 num
= state
->nums
[i
];
233 n
= num
% (state
->n
+1);
234 set
= num
/ (state
->n
+1);
236 assert(n
<= 99); /* two digits only! */
241 *p
++ = (n
>= 10) ?
('0' + (n
/10)) : ' ';
257 static void debug_state(const char *desc
, game_state
*state
)
261 if (state
->n
>= 100) {
262 debug(("[ no game_text_format for this size ]"));
265 dbg
= game_text_format(state
);
266 debug(("%s\n%s", desc
, dbg
));
272 static void strip_nums(game_state
*state
) {
274 for (i
= 0; i
< state
->n
; i
++) {
275 if (!(state
->flags
[i
] & FLAG_IMMUTABLE
))
278 memset(state
->next
, -1, state
->n
*sizeof(int));
279 memset(state
->prev
, -1, state
->n
*sizeof(int));
280 memset(state
->numsi
, -1, (state
->n
+1)*sizeof(int));
281 dsf_init(state
->dsf
, state
->n
);
284 static int check_nums(game_state
*orig
, game_state
*copy
, int only_immutable
)
287 assert(copy
->n
== orig
->n
);
288 for (i
= 0; i
< copy
->n
; i
++) {
289 if (only_immutable
&& !copy
->flags
[i
] & FLAG_IMMUTABLE
) continue;
290 assert(copy
->nums
[i
] >= 0);
291 assert(copy
->nums
[i
] <= copy
->n
);
292 if (copy
->nums
[i
] != orig
->nums
[i
]) {
293 debug(("check_nums: (%d,%d) copy=%d, orig=%d.",
294 i
%orig
->w
, i
/orig
->w
, copy
->nums
[i
], orig
->nums
[i
]));
301 /* --- Game parameter/presets functions --- */
303 static game_params
*default_params(void)
305 game_params
*ret
= snew(game_params
);
307 ret
->force_corner_start
= 1;
312 static const struct game_params signpost_presets
[] = {
321 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
326 if (i
< 0 || i
>= lenof(signpost_presets
))
329 ret
= default_params();
330 *ret
= signpost_presets
[i
];
333 sprintf(buf
, "%dx%d%s", ret
->w
, ret
->h
,
334 ret
->force_corner_start ?
"" : ", free ends");
340 static void free_params(game_params
*params
)
345 static game_params
*dup_params(game_params
*params
)
347 game_params
*ret
= snew(game_params
);
348 *ret
= *params
; /* structure copy */
352 static void decode_params(game_params
*ret
, char const *string
)
354 ret
->w
= ret
->h
= atoi(string
);
355 while (*string
&& isdigit((unsigned char)*string
)) string
++;
356 if (*string
== 'x') {
358 ret
->h
= atoi(string
);
359 while (*string
&& isdigit((unsigned char)*string
)) string
++;
361 ret
->force_corner_start
= 0;
362 if (*string
== 'c') {
364 ret
->force_corner_start
= 1;
369 static char *encode_params(game_params
*params
, int full
)
374 sprintf(data
, "%dx%d%s", params
->w
, params
->h
,
375 params
->force_corner_start ?
"c" : "");
377 sprintf(data
, "%dx%d", params
->w
, params
->h
);
382 static config_item
*game_configure(game_params
*params
)
387 ret
= snewn(4, config_item
);
389 ret
[0].name
= "Width";
390 ret
[0].type
= C_STRING
;
391 sprintf(buf
, "%d", params
->w
);
392 ret
[0].sval
= dupstr(buf
);
395 ret
[1].name
= "Height";
396 ret
[1].type
= C_STRING
;
397 sprintf(buf
, "%d", params
->h
);
398 ret
[1].sval
= dupstr(buf
);
401 ret
[2].name
= "Start and end in corners";
402 ret
[2].type
= C_BOOLEAN
;
404 ret
[2].ival
= params
->force_corner_start
;
414 static game_params
*custom_params(config_item
*cfg
)
416 game_params
*ret
= snew(game_params
);
418 ret
->w
= atoi(cfg
[0].sval
);
419 ret
->h
= atoi(cfg
[1].sval
);
420 ret
->force_corner_start
= cfg
[2].ival
;
425 static char *validate_params(game_params
*params
, int full
)
427 if (params
->w
< 2 || params
->h
< 2)
428 return "Width and height must both be at least two";
433 /* --- Game description string generation and unpicking --- */
435 static void blank_game_into(game_state
*state
)
437 memset(state
->dirs
, 0, state
->n
*sizeof(int));
438 memset(state
->nums
, 0, state
->n
*sizeof(int));
439 memset(state
->flags
, 0, state
->n
*sizeof(unsigned int));
440 memset(state
->next
, -1, state
->n
*sizeof(int));
441 memset(state
->prev
, -1, state
->n
*sizeof(int));
442 memset(state
->numsi
, -1, (state
->n
+1)*sizeof(int));
445 static game_state
*blank_game(int w
, int h
)
447 game_state
*state
= snew(game_state
);
449 memset(state
, 0, sizeof(game_state
));
454 state
->dirs
= snewn(state
->n
, int);
455 state
->nums
= snewn(state
->n
, int);
456 state
->flags
= snewn(state
->n
, unsigned int);
457 state
->next
= snewn(state
->n
, int);
458 state
->prev
= snewn(state
->n
, int);
459 state
->dsf
= snew_dsf(state
->n
);
460 state
->numsi
= snewn(state
->n
+1, int);
462 blank_game_into(state
);
467 static void dup_game_to(game_state
*to
, game_state
*from
)
469 to
->completed
= from
->completed
;
470 to
->used_solve
= from
->used_solve
;
471 to
->impossible
= from
->impossible
;
473 memcpy(to
->dirs
, from
->dirs
, to
->n
*sizeof(int));
474 memcpy(to
->flags
, from
->flags
, to
->n
*sizeof(unsigned int));
475 memcpy(to
->nums
, from
->nums
, to
->n
*sizeof(int));
477 memcpy(to
->next
, from
->next
, to
->n
*sizeof(int));
478 memcpy(to
->prev
, from
->prev
, to
->n
*sizeof(int));
480 memcpy(to
->dsf
, from
->dsf
, to
->n
*sizeof(int));
481 memcpy(to
->numsi
, from
->numsi
, (to
->n
+1)*sizeof(int));
484 static game_state
*dup_game(game_state
*state
)
486 game_state
*ret
= blank_game(state
->w
, state
->h
);
487 dup_game_to(ret
, state
);
491 static void free_game(game_state
*state
)
503 static void unpick_desc(game_params
*params
, char *desc
,
504 game_state
**sout
, char **mout
)
506 game_state
*state
= blank_game(params
->w
, params
->h
);
512 msg
= "Game description longer than expected";
518 num
= (num
*10) + (int)(c
-'0');
519 if (num
> state
->n
) {
520 msg
= "Number too large";
523 } else if ((c
-'a') >= 0 && (c
-'a') < DIR_MAX
) {
524 state
->nums
[i
] = num
;
525 state
->flags
[i
] = num ? FLAG_IMMUTABLE
: 0;
528 state
->dirs
[i
] = c
- 'a';
531 msg
= "Game description shorter than expected";
534 msg
= "Game description contains unexpected characters";
540 msg
= "Game description shorter than expected";
545 if (msg
) { /* sth went wrong. */
546 if (mout
) *mout
= msg
;
549 if (mout
) *mout
= NULL
;
550 if (sout
) *sout
= state
;
551 else free_game(state
);
555 static char *generate_desc(game_state
*state
, int issolve
)
560 ret
= NULL
; retlen
= 0;
562 ret
= sresize(ret
, 2, char);
563 ret
[0] = 'S'; ret
[1] = '\0';
566 for (i
= 0; i
< state
->n
; i
++) {
568 k
= sprintf(buf
, "%d%c", state
->nums
[i
], (int)(state
->dirs
[i
]+'a'));
570 k
= sprintf(buf
, "%c", (int)(state
->dirs
[i
]+'a'));
571 ret
= sresize(ret
, retlen
+ k
+ 1, char);
572 strcpy(ret
+ retlen
, buf
);
578 /* --- Game generation --- */
580 /* Fills in preallocated arrays ai (indices) and ad (directions)
581 * showing all non-numbered cells adjacent to index i, returns length */
582 /* This function has been somewhat optimised... */
583 static int cell_adj(game_state
*state
, int i
, int *ai
, int *ad
)
585 int n
= 0, a
, x
, y
, sx
, sy
, dx
, dy
, newi
;
586 int w
= state
->w
, h
= state
->h
;
588 sx
= i
% w
; sy
= i
/ w
;
590 for (a
= 0; a
< DIR_MAX
; a
++) {
592 dx
= dxs
[a
]; dy
= dys
[a
];
595 if (x
< 0 || y
< 0 || x
>= w
|| y
>= h
) break;
598 if (state
->nums
[newi
] == 0) {
608 static int new_game_fill(game_state
*state
, random_state
*rs
,
609 int headi
, int taili
)
611 int nfilled
, an
, ret
= 0, j
;
614 aidx
= snewn(state
->n
, int);
615 adir
= snewn(state
->n
, int);
617 debug(("new_game_fill: headi=%d, taili=%d.", headi
, taili
));
619 memset(state
->nums
, 0, state
->n
*sizeof(int));
621 state
->nums
[headi
] = 1;
622 state
->nums
[taili
] = state
->n
;
624 state
->dirs
[taili
] = 0;
627 while (nfilled
< state
->n
) {
628 /* Try and expand _from_ headi; keep going if there's only one
630 an
= cell_adj(state
, headi
, aidx
, adir
);
632 if (an
== 0) goto done
;
633 j
= random_upto(rs
, an
);
634 state
->dirs
[headi
] = adir
[j
];
635 state
->nums
[aidx
[j
]] = state
->nums
[headi
] + 1;
638 an
= cell_adj(state
, headi
, aidx
, adir
);
641 /* Try and expand _to_ taili; keep going if there's only one
643 an
= cell_adj(state
, taili
, aidx
, adir
);
645 if (an
== 0) goto done
;
646 j
= random_upto(rs
, an
);
647 state
->dirs
[aidx
[j
]] = DIR_OPPOSITE(adir
[j
]);
648 state
->nums
[aidx
[j
]] = state
->nums
[taili
] - 1;
651 an
= cell_adj(state
, taili
, aidx
, adir
);
654 /* If we get here we have headi and taili set but unconnected
655 * by direction: we need to set headi's direction so as to point
657 state
->dirs
[headi
] = whichdiri(state
, headi
, taili
);
659 /* it could happen that our last two weren't in line; if that's the
660 * case, we have to start again. */
661 if (state
->dirs
[headi
] != -1) ret
= 1;
669 /* Better generator: with the 'generate, sprinkle numbers, solve,
670 * repeat' algorithm we're _never_ generating anything greater than
671 * 6x6, and spending all of our time in new_game_fill (and very little
674 * So, new generator steps:
675 * generate the grid, at random (same as now). Numbers 1 and N get
676 immutable flag immediately.
677 * squirrel that away for the solved state.
679 * (solve:) Try and solve it.
680 * If we solved it, we're done:
681 * generate the description from current immutable numbers,
682 * free stuff that needs freeing,
683 * return description + solved state.
684 * If we didn't solve it:
685 * count #tiles in state we've made deductions about.
687 * randomise a scratch array.
688 * for each index in scratch (in turn):
689 * if the cell isn't empty, continue (through scratch array)
690 * set number + immutable in state.
691 * try and solve state.
692 * if we've solved it, we're done.
693 * otherwise, count #tiles. If it's more than we had before:
694 * good, break from this loop and re-randomise.
695 * otherwise (number didn't help):
696 * remove number and try next in scratch array.
697 * if we've got to the end of the scratch array, no luck:
698 free everything we need to, and go back to regenerate the grid.
701 static int solve_state(game_state
*state
);
703 static void debug_desc(const char *what
, game_state
*state
)
707 char *desc
= generate_desc(state
, 0);
708 debug(("%s game state: %dx%d:%s", what
, state
->w
, state
->h
, desc
));
714 /* Expects a fully-numbered game_state on input, and makes sure
715 * FLAG_IMMUTABLE is only set on those numbers we need to solve
716 * (as for a real new-game); returns 1 if it managed
717 * this (such that it could solve it), or 0 if not. */
718 static int new_game_strip(game_state
*state
, random_state
*rs
)
720 int *scratch
, i
, j
, ret
= 1;
721 game_state
*copy
= dup_game(state
);
723 debug(("new_game_strip."));
726 debug_desc("Stripped", copy
);
728 if (solve_state(copy
) > 0) {
729 debug(("new_game_strip: soluble immediately after strip."));
734 scratch
= snewn(state
->n
, int);
735 for (i
= 0; i
< state
->n
; i
++) scratch
[i
] = i
;
736 shuffle(scratch
, state
->n
, sizeof(int), rs
);
738 /* This is scungy. It might just be quick enough.
739 * It goes through, adding set numbers in empty squares
740 * until either we run out of empty squares (in the one
741 * we're half-solving) or else we solve it properly.
742 * NB that we run the entire solver each time, which
743 * strips the grid beforehand; we will save time if we
745 for (i
= 0; i
< state
->n
; i
++) {
747 if (copy
->nums
[j
] > 0 && copy
->nums
[j
] <= state
->n
)
748 continue; /* already solved to a real number here. */
749 assert(state
->nums
[j
] <= state
->n
);
750 debug(("new_game_strip: testing add IMMUTABLE number %d at square (%d,%d).",
751 state
->nums
[j
], j
%state
->w
, j
/state
->w
));
752 copy
->nums
[j
] = state
->nums
[j
];
753 copy
->flags
[j
] |= FLAG_IMMUTABLE
;
754 state
->flags
[j
] |= FLAG_IMMUTABLE
;
755 debug_state("Copy of state: ", copy
);
756 if (solve_state(copy
) > 0) goto solved
;
757 assert(check_nums(state
, copy
, 1));
763 debug(("new_game_strip: now solved."));
764 /* Since we added basically at random, try now to remove numbers
765 * and see if we can still solve it; if we can (still), really
766 * remove the number. Make sure we don't remove the anchor numbers
768 for (i
= 0; i
< state
->n
; i
++) {
770 if ((state
->flags
[j
] & FLAG_IMMUTABLE
) &&
771 (state
->nums
[j
] != 1 && state
->nums
[j
] != state
->n
)) {
772 debug(("new_game_strip: testing remove IMMUTABLE number %d at square (%d,%d).",
773 state
->nums
[j
], j
%state
->w
, j
/state
->w
));
774 state
->flags
[j
] &= ~FLAG_IMMUTABLE
;
775 dup_game_to(copy
, state
);
777 if (solve_state(copy
) > 0) {
778 assert(check_nums(state
, copy
, 0));
779 debug(("new_game_strip: OK, removing number"));
781 assert(state
->nums
[j
] <= state
->n
);
782 debug(("new_game_strip: cannot solve, putting IMMUTABLE back."));
783 copy
->nums
[j
] = state
->nums
[j
];
784 state
->flags
[j
] |= FLAG_IMMUTABLE
;
790 debug(("new_game_strip: %ssuccessful.", ret ?
"" : "not "));
796 static char *new_game_desc(game_params
*params
, random_state
*rs
,
797 char **aux
, int interactive
)
799 game_state
*state
= blank_game(params
->w
, params
->h
);
804 blank_game_into(state
);
806 /* keep trying until we fill successfully. */
808 if (params
->force_corner_start
) {
813 headi
= random_upto(rs
, state
->n
);
814 taili
= random_upto(rs
, state
->n
);
815 } while (headi
== taili
);
817 } while (!new_game_fill(state
, rs
, headi
, taili
));
819 debug_state("Filled game:", state
);
821 assert(state
->nums
[headi
] <= state
->n
);
822 assert(state
->nums
[taili
] <= state
->n
);
824 state
->flags
[headi
] |= FLAG_IMMUTABLE
;
825 state
->flags
[taili
] |= FLAG_IMMUTABLE
;
827 /* This will have filled in directions and _all_ numbers.
828 * Store the game definition for this, as the solved-state. */
829 if (!new_game_strip(state
, rs
)) {
834 game_state
*tosolve
= dup_game(state
);
835 assert(solve_state(tosolve
) > 0);
838 ret
= generate_desc(state
, 0);
843 static char *validate_desc(game_params
*params
, char *desc
)
847 unpick_desc(params
, desc
, NULL
, &ret
);
851 /* --- Linked-list and numbers array --- */
853 /* Assuming numbers are always up-to-date, there are only four possibilities
854 * for regions changing:
856 * 1) two differently-coloured regions being combined (the resulting colouring
857 * should be based on the larger of the two regions)
858 * 2) a numbered region having a single number added to the start (the
859 * region's colour will remain, and the numbers will shift by 1)
860 * 3) a numbered region having a single number added to the end (the
861 * region's colour and numbering remains as-is)
862 * 4) two unnumbered squares being joined (will pick the smallest unused set
863 * of colours to use for the new region).
865 * There should never be any complications with regions containing 3 colours
866 * being combined, since two of those colours should have been merged on a
870 /* New algorithm for working out numbering:
872 * At start, only remove numbers from cells with neither prev nor next.
873 * Search for all cells with !prev && next (head of chain); for each one:
874 * Search the group for a 'real' number: if we find one the num. for
875 the head of the chain is trivial.
876 * Otherwise, if we _don't_ have a number already:
877 * If head->next has a number, that number is the one we should use
878 * Otherwise pick the smallest unused colour set.
879 * and if we _do_ have a number already:
880 * Work out the size of this group (the dsf must already have been set up)
881 * Start enumerating through the group counting squares that have the
883 * If we reach a square with a different colour, work out which set is
884 bigger (ncol1 vs ncol2 == sz-ncol1), and use that colour
885 * If we reached a square with no colour (or the end of the group, which
886 would be weird under the circumstances) just keep the existing colour.
889 #define COLOUR(a) ((a) / (state->n+1))
890 #define START(c) ((c) * (state->n+1))
892 static int lowest_start(game_state
*state
, int *scratch
)
896 /* Fill in 'scratch' array with the currently-used colours... */
897 memset(scratch
, 0, state
->n
* sizeof(int));
898 for (i
= 0; i
< state
->n
; i
++) {
899 if (state
->nums
[i
] != 0)
900 scratch
[COLOUR(state
->nums
[i
])] = 1;
902 /* ... and return the first one that was unused. */
903 for (c
= 1; c
< state
->n
; c
++) { /* NB start at 1 */
907 assert(!"shouldn't get here");
908 return -1; /* suyb */
911 static int used_colour(game_state
*state
, int i
, int start
)
914 for (j
= 0; j
< i
; j
++) {
915 if (state
->nums
[j
] == start
)
921 static int head_number(game_state
*state
, int i
, int *scratch
)
923 int off
= 0, start
= -1, ss
, j
= i
, c
, n
, sz
;
924 const char *why
= NULL
;
926 assert(state
->prev
[i
] == -1 && state
->next
[i
] != -1);
928 /* Search through this chain looking for real numbers, checking that
929 * they match up (if there are more than one). */
931 if (state
->flags
[j
] & FLAG_IMMUTABLE
) {
932 ss
= state
->nums
[j
] - off
;
935 why
= "contains cell with immutable number";
936 } else if (start
!= ss
) {
937 debug(("head_number: chain with non-sequential numbers."));
938 state
->impossible
= 1;
943 assert(j
!= i
); /* we have created a loop, obviously wrong */
945 if (start
!= -1) goto found
;
947 if (state
->nums
[i
] == 0) {
948 if (state
->nums
[state
->next
[i
]] != 0) {
949 /* make sure we start at a 0 offset. */
950 start
= START(COLOUR(state
->nums
[state
->next
[i
]]));
951 why
= "adding blank cell to head of numbered region";
953 start
= lowest_start(state
, scratch
);
954 why
= "lowest available colour group";
957 c
= COLOUR(state
->nums
[i
]);
959 sz
= dsf_size(state
->dsf
, i
);
961 while (state
->next
[j
] != -1) {
963 if (state
->nums
[j
] == 0) {
965 why
= "adding blank cell to end of numbered region";
968 if (COLOUR(state
->nums
[j
]) == c
)
971 int start_alternate
= START(COLOUR(state
->nums
[j
]));
972 if (n
< (sz
- n
) && !used_colour(state
, i
, start_alternate
)) {
973 start
= start_alternate
;
974 why
= "joining two coloured regions, swapping to larger colour";
977 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";
988 if (used_colour(state
, i
, start
)) {
989 start
= lowest_start(state
, scratch
);
990 why
= "split region in two, lowest available colour group";
995 assert(start
!= -1 && why
!= NULL
);
996 debug(("Chain at (%d,%d) numbered at %d: %s.",
997 i
%state
->w
, i
/state
->w
, start
, why
));
1002 static void debug_numbers(game_state
*state
)
1006 for (i
= 0; i
< state
->n
; i
++) {
1007 debug(("(%d,%d) --> (%d,%d) --> (%d,%d)",
1008 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]%w
,
1009 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]/w
,
1011 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]%w
,
1012 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]/w
));
1018 static void connect_numbers(game_state
*state
)
1022 dsf_init(state
->dsf
, state
->n
);
1023 for (i
= 0; i
< state
->n
; i
++) {
1024 if (state
->next
[i
] != -1) {
1025 assert(state
->prev
[state
->next
[i
]] == i
);
1026 di
= dsf_canonify(state
->dsf
, i
);
1027 dni
= dsf_canonify(state
->dsf
, state
->next
[i
]);
1029 debug(("connect_numbers: chain forms a loop."));
1030 state
->impossible
= 1;
1032 dsf_merge(state
->dsf
, di
, dni
);
1037 static void update_numbers(game_state
*state
)
1040 int *scratch
= snewn(state
->n
, int);
1042 for (i
= 0; i
< state
->n
; i
++) {
1043 assert(state
->nums
[i
] >= 0);
1044 state
->numsi
[i
] = -1;
1047 for (i
= 0; i
< state
->n
; i
++) {
1048 if (state
->flags
[i
] & FLAG_IMMUTABLE
) {
1049 assert(state
->nums
[i
] >= 0);
1050 assert(state
->nums
[i
] <= state
->n
);
1051 state
->numsi
[state
->nums
[i
]] = i
;
1053 else if (state
->prev
[i
] == -1 && state
->next
[i
] == -1)
1056 connect_numbers(state
);
1058 for (i
= 0; i
< state
->n
; i
++) {
1059 /* Look for a cell that is the start of a chain
1060 * (has a next but no prev). */
1061 if (state
->prev
[i
] != -1 || state
->next
[i
] == -1) continue;
1063 nnum
= head_number(state
, i
, scratch
);
1066 if (nnum
> 0 && nnum
<= state
->n
)
1067 state
->numsi
[nnum
] = j
;
1068 state
->nums
[j
] = nnum
++;
1070 assert(j
!= i
); /* loop?! */
1073 /*debug_numbers(state);*/
1077 static int check_completion(game_state
*state
, int mark_errors
)
1079 int n
, j
, k
, error
= 0, complete
;
1081 /* NB This only marks errors that are possible to perpetrate with
1082 * the current UI in interpret_move. Things like forming loops in
1083 * linked sections and having numbers not add up should be forbidden
1084 * by the code elsewhere, so we don't bother marking those (because
1085 * it would add lots of tricky drawing code for very little gain). */
1087 for (j
= 0; j
< state
->n
; j
++)
1088 state
->flags
[j
] &= ~FLAG_ERROR
;
1091 /* Search for repeated numbers. */
1092 for (j
= 0; j
< state
->n
; j
++) {
1093 if (state
->nums
[j
] > 0 && state
->nums
[j
] <= state
->n
) {
1094 for (k
= j
+1; k
< state
->n
; k
++) {
1095 if (state
->nums
[k
] == state
->nums
[j
]) {
1097 state
->flags
[j
] |= FLAG_ERROR
;
1098 state
->flags
[k
] |= FLAG_ERROR
;
1106 /* Search and mark numbers n not pointing to n+1; if any numbers
1107 * are missing we know we've not completed. */
1109 for (n
= 1; n
< state
->n
; n
++) {
1110 if (state
->numsi
[n
] == -1 || state
->numsi
[n
+1] == -1)
1112 else if (!ispointingi(state
, state
->numsi
[n
], state
->numsi
[n
+1])) {
1114 state
->flags
[state
->numsi
[n
]] |= FLAG_ERROR
;
1115 state
->flags
[state
->numsi
[n
+1]] |= FLAG_ERROR
;
1119 /* make sure the link is explicitly made here; for instance, this
1120 * is nice if the user drags from 2 out (making 3) and a 4 is also
1121 * visible; this ensures that the link from 3 to 4 is also made. */
1123 makelink(state
, state
->numsi
[n
], state
->numsi
[n
+1]);
1127 if (error
) return 0;
1130 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
1132 game_state
*state
= NULL
;
1134 unpick_desc(params
, desc
, &state
, NULL
);
1135 if (!state
) assert(!"new_game failed to unpick");
1137 update_numbers(state
);
1138 check_completion(state
, 1); /* update any auto-links */
1143 /* --- Solver --- */
1145 /* If a tile has a single tile it can link _to_, or there's only a single
1146 * location that can link to a given tile, fill that link in. */
1147 static int solve_single(game_state
*state
, game_state
*copy
, int *from
)
1149 int i
, j
, sx
, sy
, x
, y
, d
, poss
, w
=state
->w
, nlinks
= 0;
1151 /* The from array is a list of 'which square can link _to_ us';
1152 * we start off with from as '-1' (meaning 'not found'); if we find
1153 * something that can link to us it is set to that index, and then if
1154 * we find another we set it to -2. */
1156 memset(from
, -1, state
->n
*sizeof(int));
1158 /* poss is 'can I link to anything' with the same meanings. */
1160 for (i
= 0; i
< state
->n
; i
++) {
1161 if (state
->next
[i
] != -1) continue;
1162 if (state
->nums
[i
] == state
->n
) continue; /* no next from last no. */
1166 sx
= x
= i
%w
; sy
= y
= i
/w
;
1168 x
+= dxs
[d
]; y
+= dys
[d
];
1169 if (!INGRID(state
, x
, y
)) break;
1170 if (!isvalidmove(state
, 1, sx
, sy
, x
, y
)) continue;
1172 /* can't link to somewhere with a back-link we would have to
1173 * break (the solver just doesn't work like this). */
1175 if (state
->prev
[j
] != -1) continue;
1177 if (state
->nums
[i
] > 0 && state
->nums
[j
] > 0 &&
1178 state
->nums
[i
] <= state
->n
&& state
->nums
[j
] <= state
->n
&&
1179 state
->nums
[j
] == state
->nums
[i
]+1) {
1180 debug(("Solver: forcing link through existing consecutive numbers."));
1186 /* if there's been a valid move already, we have to move on;
1187 * we can't make any deductions here. */
1188 poss
= (poss
== -1) ? j
: -2;
1190 /* Modify the from array as described above (which is enumerating
1191 * what points to 'j' in a similar way). */
1192 from
[j
] = (from
[j
] == -1) ? i
: -2;
1195 /*debug(("Solver: (%d,%d) has multiple possible next squares.", sx, sy));*/
1197 } else if (poss
== -1) {
1198 debug(("Solver: nowhere possible for (%d,%d) to link to.", sx
, sy
));
1199 copy
->impossible
= 1;
1202 debug(("Solver: linking (%d,%d) to only possible next (%d,%d).",
1203 sx
, sy
, poss
%w
, poss
/w
));
1204 makelink(copy
, i
, poss
);
1209 for (i
= 0; i
< state
->n
; i
++) {
1210 if (state
->prev
[i
] != -1) continue;
1211 if (state
->nums
[i
] == 1) continue; /* no prev from 1st no. */
1214 if (from
[i
] == -1) {
1215 debug(("Solver: nowhere possible to link to (%d,%d)", x
, y
));
1216 copy
->impossible
= 1;
1218 } else if (from
[i
] == -2) {
1219 /*debug(("Solver: (%d,%d) has multiple possible prev squares.", x, y));*/
1222 debug(("Solver: linking only possible prev (%d,%d) to (%d,%d).",
1223 from
[i
]%w
, from
[i
]/w
, x
, y
));
1224 makelink(copy
, from
[i
], i
);
1232 /* Returns 1 if we managed to solve it, 0 otherwise. */
1233 static int solve_state(game_state
*state
)
1235 game_state
*copy
= dup_game(state
);
1236 int *scratch
= snewn(state
->n
, int), ret
;
1238 debug_state("Before solver: ", state
);
1241 update_numbers(state
);
1243 if (solve_single(state
, copy
, scratch
)) {
1244 dup_game_to(state
, copy
);
1245 if (state
->impossible
) break; else continue;
1252 update_numbers(state
);
1253 ret
= state
->impossible ?
-1 : check_completion(state
, 0);
1254 debug(("Solver finished: %s",
1255 ret
< 0 ?
"impossible" : ret
> 0 ?
"solved" : "not solved"));
1256 debug_state("After solver: ", state
);
1260 static char *solve_game(game_state
*state
, game_state
*currstate
,
1261 char *aux
, char **error
)
1263 game_state
*tosolve
;
1267 tosolve
= dup_game(currstate
);
1268 result
= solve_state(tosolve
);
1270 ret
= generate_desc(tosolve
, 1);
1272 if (ret
) return ret
;
1274 tosolve
= dup_game(state
);
1275 result
= solve_state(tosolve
);
1277 *error
= "Puzzle is impossible.";
1278 else if (result
== 0)
1279 *error
= "Unable to solve puzzle.";
1281 ret
= generate_desc(tosolve
, 1);
1288 /* --- UI and move routines. --- */
1294 int dragging
, drag_is_from
;
1295 int sx
, sy
; /* grid coords of start cell */
1296 int dx
, dy
; /* pixel coords of drag posn */
1299 static game_ui
*new_ui(game_state
*state
)
1301 game_ui
*ui
= snew(game_ui
);
1303 /* NB: if this is ever changed to as to require more than a structure
1304 * copy to clone, there's code that needs fixing in game_redraw too. */
1306 ui
->cx
= ui
->cy
= ui
->cshow
= 0;
1309 ui
->sx
= ui
->sy
= ui
->dx
= ui
->dy
= 0;
1314 static void free_ui(game_ui
*ui
)
1319 static char *encode_ui(game_ui
*ui
)
1324 static void decode_ui(game_ui
*ui
, char *encoding
)
1328 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
1329 game_state
*newstate
)
1331 if (!oldstate
->completed
&& newstate
->completed
)
1332 ui
->cshow
= ui
->dragging
= 0;
1335 struct game_drawstate
{
1336 int tilesize
, started
, solved
;
1340 double angle_offset
;
1342 int dragging
, dx
, dy
;
1346 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
1347 int mx
, int my
, int button
)
1349 int x
= FROMCOORD(mx
), y
= FROMCOORD(my
), w
= state
->w
;
1352 if (IS_CURSOR_MOVE(button
)) {
1353 move_cursor(button
, &ui
->cx
, &ui
->cy
, state
->w
, state
->h
, 0);
1356 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1357 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1360 } else if (IS_CURSOR_SELECT(button
)) {
1363 else if (ui
->dragging
) {
1364 ui
->dragging
= FALSE
;
1365 if (ui
->sx
== ui
->cx
&& ui
->sy
== ui
->cy
) return "";
1366 if (ui
->drag_is_from
) {
1367 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
)) return "";
1368 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
);
1370 if (!isvalidmove(state
, 0, ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
)) return "";
1371 sprintf(buf
, "L%d,%d-%d,%d", ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
);
1375 ui
->dragging
= TRUE
;
1378 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1379 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1380 ui
->drag_is_from
= (button
== CURSOR_SELECT
) ?
1 : 0;
1384 if (IS_MOUSE_DOWN(button
)) {
1386 ui
->cshow
= ui
->dragging
= 0;
1388 assert(!ui
->dragging
);
1389 if (!INGRID(state
, x
, y
)) return NULL
;
1391 if (button
== LEFT_BUTTON
) {
1392 /* disallow dragging from the final number. */
1393 if (state
->nums
[y
*w
+x
] == state
->n
) return NULL
;
1394 } else if (button
== RIGHT_BUTTON
) {
1395 /* disallow dragging to the first number. */
1396 if (state
->nums
[y
*w
+x
] == 1) return NULL
;
1399 ui
->dragging
= TRUE
;
1400 ui
->drag_is_from
= (button
== LEFT_BUTTON
) ?
1 : 0;
1407 } else if (IS_MOUSE_DRAG(button
) && ui
->dragging
) {
1411 } else if (IS_MOUSE_RELEASE(button
) && ui
->dragging
) {
1412 ui
->dragging
= FALSE
;
1413 if (ui
->sx
== x
&& ui
->sy
== y
) return ""; /* single click */
1415 if (!INGRID(state
, x
, y
)) {
1416 int si
= ui
->sy
*w
+ui
->sx
;
1417 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1419 sprintf(buf
, "%c%d,%d",
1420 ui
->drag_is_from ?
'C' : 'X', ui
->sx
, ui
->sy
);
1424 if (ui
->drag_is_from
) {
1425 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, x
, y
)) return "";
1426 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, x
, y
);
1428 if (!isvalidmove(state
, 0, x
, y
, ui
->sx
, ui
->sy
)) return "";
1429 sprintf(buf
, "L%d,%d-%d,%d", x
, y
, ui
->sx
, ui
->sy
);
1432 } /* else if (button == 'H' || button == 'h')
1433 return dupstr("H"); */
1434 else if ((button
== 'x' || button
== 'X') && ui
->cshow
) {
1435 int si
= ui
->cy
*w
+ ui
->cx
;
1436 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1438 sprintf(buf
, "%c%d,%d",
1439 (button
== 'x') ?
'C' : 'X', ui
->cx
, ui
->cy
);
1446 static void unlink_cell(game_state
*state
, int si
)
1448 debug(("Unlinking (%d,%d).", si
%state
->w
, si
/state
->w
));
1449 if (state
->prev
[si
] != -1) {
1450 debug((" ... removing prev link from (%d,%d).",
1451 state
->prev
[si
]%state
->w
, state
->prev
[si
]/state
->w
));
1452 state
->next
[state
->prev
[si
]] = -1;
1453 state
->prev
[si
] = -1;
1455 if (state
->next
[si
] != -1) {
1456 debug((" ... removing next link to (%d,%d).",
1457 state
->next
[si
]%state
->w
, state
->next
[si
]/state
->w
));
1458 state
->prev
[state
->next
[si
]] = -1;
1459 state
->next
[si
] = -1;
1463 static game_state
*execute_move(game_state
*state
, char *move
)
1465 game_state
*ret
= NULL
;
1466 int sx
, sy
, ex
, ey
, si
, ei
, w
= state
->w
;
1469 debug(("move: %s", move
));
1471 if (move
[0] == 'S') {
1477 p
.w
= state
->w
; p
.h
= state
->h
;
1478 valid
= validate_desc(&p
, move
+1);
1480 debug(("execute_move: move not valid: %s", valid
));
1483 ret
= dup_game(state
);
1484 tmp
= new_game(NULL
, &p
, move
+1);
1485 for (i
= 0; i
< state
->n
; i
++) {
1486 ret
->prev
[i
] = tmp
->prev
[i
];
1487 ret
->next
[i
] = tmp
->next
[i
];
1490 ret
->used_solve
= 1;
1491 } else if (sscanf(move
, "L%d,%d-%d,%d", &sx
, &sy
, &ex
, &ey
) == 4) {
1492 if (!isvalidmove(state
, 0, sx
, sy
, ex
, ey
)) return NULL
;
1494 ret
= dup_game(state
);
1496 si
= sy
*w
+sx
; ei
= ey
*w
+ex
;
1497 makelink(ret
, si
, ei
);
1498 } else if (sscanf(move
, "%c%d,%d", &c
, &sx
, &sy
) == 3) {
1499 if (c
!= 'C' && c
!= 'X') return NULL
;
1500 if (!INGRID(state
, sx
, sy
)) return NULL
;
1502 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1505 ret
= dup_game(state
);
1508 /* Unlink the single cell we dragged from the board. */
1509 unlink_cell(ret
, si
);
1511 int i
, set
, sset
= state
->nums
[si
] / (state
->n
+1);
1512 for (i
= 0; i
< state
->n
; i
++) {
1513 /* Unlink all cells in the same set as the one we dragged
1514 * from the board. */
1516 if (state
->nums
[i
] == 0) continue;
1517 set
= state
->nums
[i
] / (state
->n
+1);
1518 if (set
!= sset
) continue;
1520 unlink_cell(ret
, i
);
1523 } else if (strcmp(move
, "H") == 0) {
1524 ret
= dup_game(state
);
1528 update_numbers(ret
);
1529 if (check_completion(ret
, 1)) ret
->completed
= 1;
1535 /* ----------------------------------------------------------------------
1539 static void game_compute_size(game_params
*params
, int tilesize
,
1542 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1543 struct { int tilesize
, order
; } ads
, *ds
= &ads
;
1544 ads
.tilesize
= tilesize
;
1546 *x
= TILE_SIZE
* params
->w
+ 2 * BORDER
;
1547 *y
= TILE_SIZE
* params
->h
+ 2 * BORDER
;
1550 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
1551 game_params
*params
, int tilesize
)
1553 ds
->tilesize
= tilesize
;
1554 assert(TILE_SIZE
> 0);
1557 ds
->dragb
= blitter_new(dr
, BLITTER_SIZE
, BLITTER_SIZE
);
1560 /* Colours chosen from the webby palette to work as a background to black text,
1561 * W then some plausible approximation to pastelly ROYGBIV; we then interpolate
1562 * between consecutive pairs to give another 8 (and then the drawing routine
1563 * will reuse backgrounds). */
1564 static const unsigned long bgcols
[8] = {
1565 0xffffff, /* white */
1566 0xffa07a, /* lightsalmon */
1567 0x98fb98, /* green */
1568 0x7fffd4, /* aquamarine */
1569 0x9370db, /* medium purple */
1570 0xffa500, /* orange */
1571 0x87cefa, /* lightskyblue */
1572 0xffff00, /* yellow */
1575 static float *game_colours(frontend
*fe
, int *ncolours
)
1577 float *ret
= snewn(3 * NCOLOURS
, float);
1580 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
1582 for (i
= 0; i
< 3; i
++) {
1583 ret
[COL_NUMBER
* 3 + i
] = 0.0F
;
1584 ret
[COL_ARROW
* 3 + i
] = 0.0F
;
1585 ret
[COL_CURSOR
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 2.0F
;
1586 ret
[COL_GRID
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 1.3F
;
1588 ret
[COL_NUMBER_SET
* 3 + 0] = 0.0F
;
1589 ret
[COL_NUMBER_SET
* 3 + 1] = 0.0F
;
1590 ret
[COL_NUMBER_SET
* 3 + 2] = 0.9F
;
1592 ret
[COL_ERROR
* 3 + 0] = 1.0F
;
1593 ret
[COL_ERROR
* 3 + 1] = 0.0F
;
1594 ret
[COL_ERROR
* 3 + 2] = 0.0F
;
1596 ret
[COL_DRAG_ORIGIN
* 3 + 0] = 0.2F
;
1597 ret
[COL_DRAG_ORIGIN
* 3 + 1] = 1.0F
;
1598 ret
[COL_DRAG_ORIGIN
* 3 + 2] = 0.2F
;
1600 for (c
= 0; c
< 8; c
++) {
1601 ret
[(COL_B0
+ c
) * 3 + 0] = (float)((bgcols
[c
] & 0xff0000) >> 16) / 256.0F
;
1602 ret
[(COL_B0
+ c
) * 3 + 1] = (float)((bgcols
[c
] & 0xff00) >> 8) / 256.0F
;
1603 ret
[(COL_B0
+ c
) * 3 + 2] = (float)((bgcols
[c
] & 0xff)) / 256.0F
;
1605 for (c
= 0; c
< 8; c
++) {
1606 for (i
= 0; i
< 3; i
++) {
1607 ret
[(COL_B0
+ 8 + c
) * 3 + i
] =
1608 (ret
[(COL_B0
+ c
) * 3 + i
] + ret
[(COL_B0
+ c
+ 1) * 3 + i
]) / 2.0F
;
1612 #define average(r,a,b,w) do { \
1613 for (i = 0; i < 3; i++) \
1614 ret[(r)*3+i] = ret[(a)*3+i] + w * (ret[(b)*3+i] - ret[(a)*3+i]); \
1616 average(COL_ARROW_BG_DIM
, COL_BACKGROUND
, COL_ARROW
, 0.1F
);
1617 average(COL_NUMBER_SET_MID
, COL_B0
, COL_NUMBER_SET
, 0.3F
);
1618 for (c
= 0; c
< NBACKGROUNDS
; c
++) {
1619 /* I assume here that COL_ARROW and COL_NUMBER are the same.
1620 * Otherwise I'd need two sets of COL_M*. */
1621 average(COL_M0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.3F
);
1622 average(COL_D0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.1F
);
1623 average(COL_X0
+ c
, COL_BACKGROUND
, COL_B0
+ c
, 0.5F
);
1626 *ncolours
= NCOLOURS
;
1630 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1632 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1635 ds
->tilesize
= ds
->started
= ds
->solved
= 0;
1640 ds
->nums
= snewn(state
->n
, int);
1641 ds
->dirp
= snewn(state
->n
, int);
1642 ds
->f
= snewn(state
->n
, unsigned int);
1643 for (i
= 0; i
< state
->n
; i
++) {
1649 ds
->angle_offset
= 0.0F
;
1651 ds
->dragging
= ds
->dx
= ds
->dy
= 0;
1657 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1662 if (ds
->dragb
) blitter_free(dr
, ds
->dragb
);
1667 /* cx, cy are top-left corner. sz is the 'radius' of the arrow.
1668 * ang is in radians, clockwise from 0 == straight up. */
1669 static void draw_arrow(drawing
*dr
, int cx
, int cy
, int sz
, double ang
,
1670 int cfill
, int cout
)
1673 int xdx
, ydx
, xdy
, ydy
, xdx3
, xdy3
;
1674 double s
= sin(ang
), c
= cos(ang
);
1676 xdx3
= (int)(sz
* (c
/3 + 1) + 0.5) - sz
;
1677 xdy3
= (int)(sz
* (s
/3 + 1) + 0.5) - sz
;
1678 xdx
= (int)(sz
* (c
+ 1) + 0.5) - sz
;
1679 xdy
= (int)(sz
* (s
+ 1) + 0.5) - sz
;
1684 coords
[2*0 + 0] = cx
- ydx
;
1685 coords
[2*0 + 1] = cy
- ydy
;
1686 coords
[2*1 + 0] = cx
+ xdx
;
1687 coords
[2*1 + 1] = cy
+ xdy
;
1688 coords
[2*2 + 0] = cx
+ xdx3
;
1689 coords
[2*2 + 1] = cy
+ xdy3
;
1690 coords
[2*3 + 0] = cx
+ xdx3
+ ydx
;
1691 coords
[2*3 + 1] = cy
+ xdy3
+ ydy
;
1692 coords
[2*4 + 0] = cx
- xdx3
+ ydx
;
1693 coords
[2*4 + 1] = cy
- xdy3
+ ydy
;
1694 coords
[2*5 + 0] = cx
- xdx3
;
1695 coords
[2*5 + 1] = cy
- xdy3
;
1696 coords
[2*6 + 0] = cx
- xdx
;
1697 coords
[2*6 + 1] = cy
- xdy
;
1699 draw_polygon(dr
, coords
, 7, cfill
, cout
);
1702 static void draw_arrow_dir(drawing
*dr
, int cx
, int cy
, int sz
, int dir
,
1703 int cfill
, int cout
, double angle_offset
)
1705 double ang
= 2.0 * PI
* (double)dir
/ 8.0 + angle_offset
;
1706 draw_arrow(dr
, cx
, cy
, sz
, ang
, cfill
, cout
);
1709 /* cx, cy are centre coordinates.. */
1710 static void draw_star(drawing
*dr
, int cx
, int cy
, int rad
, int npoints
,
1711 int cfill
, int cout
, double angle_offset
)
1716 assert(npoints
> 0);
1718 coords
= snewn(npoints
* 2 * 2, int);
1720 for (n
= 0; n
< npoints
* 2; n
++) {
1721 a
= 2.0 * PI
* ((double)n
/ ((double)npoints
* 2.0)) + angle_offset
;
1722 r
= (n
% 2) ?
(double)rad
/2.0 : (double)rad
;
1724 /* We're rotating the point at (0, -r) by a degrees */
1725 coords
[2*n
+0] = cx
+ (int)( r
* sin(a
));
1726 coords
[2*n
+1] = cy
+ (int)(-r
* cos(a
));
1728 draw_polygon(dr
, coords
, npoints
*2, cfill
, cout
);
1732 static int num2col(game_drawstate
*ds
, int num
)
1734 int set
= num
/ (ds
->n
+1);
1736 if (num
<= 0) return COL_BACKGROUND
;
1737 return COL_B0
+ (set
% 16);
1740 #define ARROW_HALFSZ (7 * TILE_SIZE / 32)
1742 #define F_CUR 0x001 /* Cursor on this tile. */
1743 #define F_DRAG_SRC 0x002 /* Tile is source of a drag. */
1744 #define F_ERROR 0x004 /* Tile marked in error. */
1745 #define F_IMMUTABLE 0x008 /* Tile (number) is immutable. */
1746 #define F_ARROW_POINT 0x010 /* Tile points to other tile */
1747 #define F_ARROW_INPOINT 0x020 /* Other tile points in here. */
1748 #define F_DIM 0x040 /* Tile is dim */
1750 static void tile_redraw(drawing
*dr
, game_drawstate
*ds
, int tx
, int ty
,
1751 int dir
, int dirp
, int num
, unsigned int f
,
1752 double angle_offset
, int print_ink
)
1754 int cb
= TILE_SIZE
/ 16, textsz
;
1756 int arrowcol
, sarrowcol
, setcol
, textcol
;
1757 int n
= num
% (ds
->n
+1), set
= num
/ (ds
->n
+1);
1760 /* Calculate colours. */
1762 if (print_ink
>= 0) {
1764 * We're printing, so just do everything in black.
1766 arrowcol
= textcol
= print_ink
;
1767 setcol
= sarrowcol
= -1; /* placate optimiser */
1770 setcol
= num2col(ds
, num
);
1772 #define dim(fg,bg) ( \
1773 (bg)==COL_BACKGROUND ? COL_ARROW_BG_DIM : \
1774 (bg) + COL_D0 - COL_B0 \
1777 #define mid(fg,bg) ( \
1778 (fg)==COL_NUMBER_SET ? COL_NUMBER_SET_MID : \
1779 (bg) + COL_M0 - COL_B0 \
1782 #define dimbg(bg) ( \
1783 (bg)==COL_BACKGROUND ? COL_BACKGROUND : \
1784 (bg) + COL_X0 - COL_B0 \
1787 if (f
& F_DRAG_SRC
) arrowcol
= COL_DRAG_ORIGIN
;
1788 else if (f
& F_DIM
) arrowcol
= dim(COL_ARROW
, setcol
);
1789 else if (f
& F_ARROW_POINT
) arrowcol
= mid(COL_ARROW
, setcol
);
1790 else arrowcol
= COL_ARROW
;
1792 if (f
& (F_ERROR
)) textcol
= COL_ERROR
;
1794 if (f
& F_IMMUTABLE
) textcol
= COL_NUMBER_SET
;
1795 else textcol
= COL_NUMBER
;
1797 if (f
& F_DIM
) textcol
= dim(textcol
, setcol
);
1798 else if (((f
& F_ARROW_POINT
) || num
==ds
->n
) &&
1799 ((f
& F_ARROW_INPOINT
) || num
==1))
1800 textcol
= mid(textcol
, setcol
);
1803 if (f
& F_DIM
) sarrowcol
= dim(COL_ARROW
, setcol
);
1804 else sarrowcol
= COL_ARROW
;
1807 /* Clear tile background */
1809 if (print_ink
< 0) {
1810 draw_rect(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
,
1811 (f
& F_DIM
) ?
dimbg(setcol
) : setcol
);
1814 /* Draw large (outwards-pointing) arrow. */
1816 asz
= ARROW_HALFSZ
; /* 'radius' of arrow/star. */
1817 acx
= tx
+TILE_SIZE
/2+asz
; /* centre x */
1818 acy
= ty
+TILE_SIZE
/2+asz
; /* centre y */
1820 if (num
== ds
->n
&& (f
& F_IMMUTABLE
))
1821 draw_star(dr
, acx
, acy
, asz
, 5, arrowcol
, arrowcol
, angle_offset
);
1823 draw_arrow_dir(dr
, acx
, acy
, asz
, dir
, arrowcol
, arrowcol
, angle_offset
);
1824 if (print_ink
< 0 && (f
& F_CUR
))
1825 draw_rect_corners(dr
, acx
, acy
, asz
+1, COL_CURSOR
);
1827 /* Draw dot iff this tile requires a predecessor and doesn't have one. */
1829 if (print_ink
< 0) {
1830 acx
= tx
+TILE_SIZE
/2-asz
;
1831 acy
= ty
+TILE_SIZE
/2+asz
;
1833 if (!(f
& F_ARROW_INPOINT
) && num
!= 1) {
1834 draw_circle(dr
, acx
, acy
, asz
/ 4, sarrowcol
, sarrowcol
);
1838 /* Draw text (number or set). */
1843 sprintf(buf
, "%d", n
);
1846 sprintf(buf
, "%c", (int)(set
+'a'-1));
1848 sprintf(buf
, "%c+%d", (int)(set
+'a'-1), n
);
1850 textsz
= min(2*asz
, (TILE_SIZE
- 2 * cb
) / (int)strlen(buf
));
1851 draw_text(dr
, tx
+ cb
, ty
+ TILE_SIZE
/4, FONT_VARIABLE
, textsz
,
1852 ALIGN_VCENTRE
| ALIGN_HLEFT
, textcol
, buf
);
1855 if (print_ink
< 0) {
1856 draw_rect_outline(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
, COL_GRID
);
1857 draw_update(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
);
1861 static void draw_drag_indicator(drawing
*dr
, game_drawstate
*ds
,
1862 game_state
*state
, game_ui
*ui
, int validdrag
)
1864 int dir
, w
= ds
->w
, acol
= COL_ARROW
;
1865 int fx
= FROMCOORD(ui
->dx
), fy
= FROMCOORD(ui
->dy
);
1869 /* If we could move here, lock the arrow to the appropriate direction. */
1870 dir
= ui
->drag_is_from ? state
->dirs
[ui
->sy
*w
+ui
->sx
] : state
->dirs
[fy
*w
+fx
];
1872 ang
= (2.0 * PI
* dir
) / 8.0; /* similar to calculation in draw_arrow_dir. */
1874 /* Draw an arrow pointing away from/towards the origin cell. */
1875 int ox
= COORD(ui
->sx
) + TILE_SIZE
/2, oy
= COORD(ui
->sy
) + TILE_SIZE
/2;
1876 double tana
, offset
;
1877 double xdiff
= fabs(ox
- ui
->dx
), ydiff
= fabs(oy
- ui
->dy
);
1880 ang
= (oy
> ui
->dy
) ?
0.0F
: PI
;
1881 } else if (ydiff
== 0) {
1882 ang
= (ox
> ui
->dx
) ?
3.0F
*PI
/2.0F
: PI
/2.0F
;
1884 if (ui
->dx
> ox
&& ui
->dy
< oy
) {
1885 tana
= xdiff
/ ydiff
;
1887 } else if (ui
->dx
> ox
&& ui
->dy
> oy
) {
1888 tana
= ydiff
/ xdiff
;
1890 } else if (ui
->dx
< ox
&& ui
->dy
> oy
) {
1891 tana
= xdiff
/ ydiff
;
1894 tana
= ydiff
/ xdiff
;
1895 offset
= 3.0F
* PI
/ 2.0F
;
1897 ang
= atan(tana
) + offset
;
1900 if (!ui
->drag_is_from
) ang
+= PI
; /* point to origin, not away from. */
1903 draw_arrow(dr
, ui
->dx
, ui
->dy
, ARROW_HALFSZ
, ang
, acol
, acol
);
1906 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
1907 game_state
*state
, int dir
, game_ui
*ui
,
1908 float animtime
, float flashtime
)
1910 int x
, y
, i
, w
= ds
->w
, dirp
, force
= 0;
1912 double angle_offset
= 0.0;
1913 game_state
*postdrop
= NULL
;
1915 if (flashtime
> 0.0F
)
1916 angle_offset
= 2.0 * PI
* (flashtime
/ FLASH_SPIN
);
1917 if (angle_offset
!= ds
->angle_offset
) {
1918 ds
->angle_offset
= angle_offset
;
1924 blitter_load(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
1925 draw_update(dr
, ds
->dx
, ds
->dy
, BLITTER_SIZE
, BLITTER_SIZE
);
1926 ds
->dragging
= FALSE
;
1929 /* If an in-progress drag would make a valid move if finished, we
1930 * reflect that move in the board display. We let interpret_move do
1931 * most of the heavy lifting for us: we have to copy the game_ui so
1932 * as not to stomp on the real UI's drag state. */
1934 game_ui uicopy
= *ui
;
1935 char *movestr
= interpret_move(state
, &uicopy
, ds
, ui
->dx
, ui
->dy
, LEFT_RELEASE
);
1937 if (movestr
!= NULL
&& strcmp(movestr
, "") != 0) {
1938 postdrop
= execute_move(state
, movestr
);
1946 int aw
= TILE_SIZE
* state
->w
;
1947 int ah
= TILE_SIZE
* state
->h
;
1948 draw_rect(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
, COL_BACKGROUND
);
1949 draw_rect_outline(dr
, BORDER
- 1, BORDER
- 1, aw
+ 2, ah
+ 2, COL_GRID
);
1950 draw_update(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
);
1952 for (x
= 0; x
< state
->w
; x
++) {
1953 for (y
= 0; y
< state
->h
; y
++) {
1958 if (ui
->cshow
&& x
== ui
->cx
&& y
== ui
->cy
)
1962 if (x
== ui
->sx
&& y
== ui
->sy
)
1964 else if (ui
->drag_is_from
) {
1965 if (!ispointing(state
, ui
->sx
, ui
->sy
, x
, y
))
1968 if (!ispointing(state
, x
, y
, ui
->sx
, ui
->sy
))
1973 if (state
->impossible
||
1974 state
->nums
[i
] < 0 || state
->flags
[i
] & FLAG_ERROR
)
1976 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
1979 if (state
->next
[i
] != -1)
1982 if (state
->prev
[i
] != -1) {
1983 /* Currently the direction here is from our square _back_
1984 * to its previous. We could change this to give the opposite
1985 * sense to the direction. */
1986 f
|= F_ARROW_INPOINT
;
1987 dirp
= whichdir(x
, y
, state
->prev
[i
]%w
, state
->prev
[i
]/w
);
1990 if (state
->nums
[i
] != ds
->nums
[i
] ||
1991 f
!= ds
->f
[i
] || dirp
!= ds
->dirp
[i
] ||
1992 force
|| !ds
->started
) {
1994 BORDER
+ x
* TILE_SIZE
,
1995 BORDER
+ y
* TILE_SIZE
,
1996 state
->dirs
[i
], dirp
, state
->nums
[i
], f
,
1998 ds
->nums
[i
] = state
->nums
[i
];
2005 ds
->dragging
= TRUE
;
2006 ds
->dx
= ui
->dx
- BLITTER_SIZE
/2;
2007 ds
->dy
= ui
->dy
- BLITTER_SIZE
/2;
2008 blitter_save(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
2010 draw_drag_indicator(dr
, ds
, state
, ui
, postdrop ?
1 : 0);
2012 if (postdrop
) free_game(postdrop
);
2013 if (!ds
->started
) ds
->started
= TRUE
;
2016 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
2017 int dir
, game_ui
*ui
)
2022 static float game_flash_length(game_state
*oldstate
, game_state
*newstate
,
2023 int dir
, game_ui
*ui
)
2025 if (!oldstate
->completed
&&
2026 newstate
->completed
&& !newstate
->used_solve
)
2032 static int game_timing_state(game_state
*state
, game_ui
*ui
)
2037 static void game_print_size(game_params
*params
, float *x
, float *y
)
2041 game_compute_size(params
, 1300, &pw
, &ph
);
2046 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
2048 int ink
= print_mono_colour(dr
, 0);
2051 /* Fake up just enough of a drawstate */
2052 game_drawstate ads
, *ds
= &ads
;
2053 ds
->tilesize
= tilesize
;
2059 print_line_width(dr
, TILE_SIZE
/ 40);
2060 for (x
= 1; x
< state
->w
; x
++)
2061 draw_line(dr
, COORD(x
), COORD(0), COORD(x
), COORD(state
->h
), ink
);
2062 for (y
= 1; y
< state
->h
; y
++)
2063 draw_line(dr
, COORD(0), COORD(y
), COORD(state
->w
), COORD(y
), ink
);
2064 print_line_width(dr
, 2*TILE_SIZE
/ 40);
2065 draw_rect_outline(dr
, COORD(0), COORD(0), TILE_SIZE
*state
->w
,
2066 TILE_SIZE
*state
->h
, ink
);
2069 * Arrows and numbers.
2071 print_line_width(dr
, 0);
2072 for (y
= 0; y
< state
->h
; y
++)
2073 for (x
= 0; x
< state
->w
; x
++)
2074 tile_redraw(dr
, ds
, COORD(x
), COORD(y
), state
->dirs
[y
*state
->w
+x
],
2075 0, state
->nums
[y
*state
->w
+x
], 0, 0.0, ink
);
2079 #define thegame signpost
2082 const struct game thegame
= {
2083 "Signpost", "games.signpost", "signpost",
2090 TRUE
, game_configure
, custom_params
,
2098 TRUE
, game_can_format_as_text_now
, game_text_format
,
2106 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
2109 game_free_drawstate
,
2113 TRUE
, FALSE
, game_print_size
, game_print
,
2114 FALSE
, /* wants_statusbar */
2115 FALSE
, game_timing_state
,
2116 REQUIRE_RBUTTON
| REQUIRE_NUMPAD
, /* flags */
2119 #ifdef STANDALONE_SOLVER
2124 const char *quis
= NULL
;
2127 void usage(FILE *out
) {
2128 fprintf(out
, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis
);
2131 static void cycle_seed(char **seedstr
, random_state
*rs
)
2137 newseed
[0] = '1' + (char)random_upto(rs
, 9);
2138 for (j
= 1; j
< 15; j
++)
2139 newseed
[j
] = '0' + (char)random_upto(rs
, 10);
2141 *seedstr
= dupstr(newseed
);
2144 static void start_soak(game_params
*p
, char *seedstr
)
2146 time_t tt_start
, tt_now
, tt_last
;
2149 long n
= 0, nnums
= 0, i
;
2152 tt_start
= tt_now
= time(NULL
);
2153 printf("Soak-generating a %dx%d grid.\n", p
->w
, p
->h
);
2156 rs
= random_new(seedstr
, strlen(seedstr
));
2157 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2159 state
= thegame
.new_game(NULL
, p
, desc
);
2160 for (i
= 0; i
< state
->n
; i
++) {
2161 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
2164 thegame
.free_game(state
);
2167 cycle_seed(&seedstr
, rs
);
2171 tt_last
= time(NULL
);
2172 if (tt_last
> tt_now
) {
2174 printf("%ld total, %3.1f/s, %3.1f nums/grid (%3.1f%%).\n",
2176 (double)n
/ ((double)tt_now
- tt_start
),
2177 (double)nnums
/ (double)n
,
2178 ((double)nnums
* 100.0) / ((double)n
* (double)p
->w
* (double)p
->h
) );
2183 static void process_desc(char *id
)
2185 char *desc
, *err
, *solvestr
;
2189 printf("%s\n ", id
);
2191 desc
= strchr(id
, ':');
2193 fprintf(stderr
, "%s: expecting game description.", quis
);
2199 p
= thegame
.default_params();
2200 thegame
.decode_params(p
, id
);
2201 err
= thegame
.validate_params(p
, 1);
2203 fprintf(stderr
, "%s: %s", quis
, err
);
2204 thegame
.free_params(p
);
2208 err
= thegame
.validate_desc(p
, desc
);
2210 fprintf(stderr
, "%s: %s\nDescription: %s\n", quis
, err
, desc
);
2211 thegame
.free_params(p
);
2215 s
= thegame
.new_game(NULL
, p
, desc
);
2217 solvestr
= thegame
.solve(s
, s
, NULL
, &err
);
2219 fprintf(stderr
, "%s\n", err
);
2221 printf("Puzzle is soluble.\n");
2223 thegame
.free_game(s
);
2224 thegame
.free_params(p
);
2227 int main(int argc
, const char *argv
[])
2229 char *id
= NULL
, *desc
, *err
, *aux
= NULL
;
2230 int soak
= 0, verbose
= 0, stdin_desc
= 0, n
= 1, i
;
2231 char *seedstr
= NULL
, newseed
[16];
2233 setvbuf(stdout
, NULL
, _IONBF
, 0);
2236 while (--argc
> 0) {
2237 char *p
= (char*)(*++argv
);
2238 if (!strcmp(p
, "-v") || !strcmp(p
, "--verbose"))
2240 else if (!strcmp(p
, "--stdin"))
2242 else if (!strcmp(p
, "-e") || !strcmp(p
, "--seed")) {
2243 seedstr
= dupstr(*++argv
);
2245 } else if (!strcmp(p
, "-n") || !strcmp(p
, "--number")) {
2248 } else if (!strcmp(p
, "-s") || !strcmp(p
, "--soak")) {
2250 } else if (*p
== '-') {
2251 fprintf(stderr
, "%s: unrecognised option `%s'\n", argv
[0], p
);
2259 sprintf(newseed
, "%lu", time(NULL
));
2260 seedstr
= dupstr(newseed
);
2262 if (id
|| !stdin_desc
) {
2263 if (id
&& strchr(id
, ':')) {
2264 /* Parameters and description passed on cmd-line:
2265 * try and solve it. */
2268 /* No description passed on cmd-line: decode parameters
2269 * (with optional seed too) */
2271 game_params
*p
= thegame
.default_params();
2274 char *cmdseed
= strchr(id
, '#');
2278 seedstr
= dupstr(cmdseed
);
2281 thegame
.decode_params(p
, id
);
2284 err
= thegame
.validate_params(p
, 1);
2286 fprintf(stderr
, "%s: %s", quis
, err
);
2287 thegame
.free_params(p
);
2291 /* We have a set of valid parameters; either soak with it
2292 * or generate a single game description and print to stdout. */
2294 start_soak(p
, seedstr
);
2296 char *pstring
= thegame
.encode_params(p
, 0);
2298 for (i
= 0; i
< n
; i
++) {
2299 random_state
*rs
= random_new(seedstr
, strlen(seedstr
));
2301 if (verbose
) printf("%s#%s\n", pstring
, seedstr
);
2302 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2303 printf("%s:%s\n", pstring
, desc
);
2306 cycle_seed(&seedstr
, rs
);
2313 thegame
.free_params(p
);
2320 while (fgets(buf
, sizeof(buf
), stdin
)) {
2321 buf
[strcspn(buf
, "\r\n")] = '\0';
2333 /* vim: set shiftwidth=4 tabstop=8: */