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, start
= -1, 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
;
931 why
= "contains cell with immutable number";
932 } else if (start
!= ss
) {
933 debug(("head_number: chain with non-sequential numbers."));
934 state
->impossible
= 1;
939 assert(j
!= i
); /* we have created a loop, obviously wrong */
941 if (start
!= -1) goto found
;
943 if (state
->nums
[i
] == 0) {
944 if (state
->nums
[state
->next
[i
]] != 0) {
945 /* make sure we start at a 0 offset. */
946 start
= START(COLOUR(state
->nums
[state
->next
[i
]]));
947 why
= "adding blank cell to head of numbered region";
949 start
= lowest_start(state
, scratch
);
950 why
= "lowest available colour group";
953 c
= COLOUR(state
->nums
[i
]);
955 sz
= dsf_size(state
->dsf
, i
);
957 while (state
->next
[j
] != -1) {
959 if (state
->nums
[j
] == 0) {
961 why
= "adding blank cell to end of numbered region";
964 if (COLOUR(state
->nums
[j
]) == c
)
967 int start_alternate
= START(COLOUR(state
->nums
[j
]));
968 if (n
< (sz
- n
) && !used_colour(state
, i
, start_alternate
)) {
969 start
= start_alternate
;
970 why
= "joining two coloured regions, swapping to larger colour";
973 why
= "joining two coloured regions, taking largest";
978 /* If we got here then we may have split a region into
979 * two; make sure we don't assign a colour we've already used. */
981 start
= (c
== 0) ?
lowest_start(state
, scratch
) : START(c
);
982 why
= "got to end of coloured region";
984 if (used_colour(state
, i
, start
)) {
985 start
= lowest_start(state
, scratch
);
986 why
= "split region in two, lowest available colour group";
991 assert(start
!= -1 && why
!= NULL
);
992 debug(("Chain at (%d,%d) numbered at %d: %s.",
993 i
%state
->w
, i
/state
->w
, start
, why
));
998 static void debug_numbers(game_state
*state
)
1002 for (i
= 0; i
< state
->n
; i
++) {
1003 debug(("(%d,%d) --> (%d,%d) --> (%d,%d)",
1004 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]%w
,
1005 state
->prev
[i
]==-1 ?
-1 : state
->prev
[i
]/w
,
1007 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]%w
,
1008 state
->next
[i
]==-1 ?
-1 : state
->next
[i
]/w
));
1014 static void connect_numbers(game_state
*state
)
1018 dsf_init(state
->dsf
, state
->n
);
1019 for (i
= 0; i
< state
->n
; i
++) {
1020 if (state
->next
[i
] != -1) {
1021 assert(state
->prev
[state
->next
[i
]] == i
);
1022 di
= dsf_canonify(state
->dsf
, i
);
1023 dni
= dsf_canonify(state
->dsf
, state
->next
[i
]);
1025 debug(("connect_numbers: chain forms a loop."));
1026 state
->impossible
= 1;
1028 dsf_merge(state
->dsf
, di
, dni
);
1033 static void update_numbers(game_state
*state
)
1036 int *scratch
= snewn(state
->n
, int);
1038 for (i
= 0; i
< state
->n
; i
++) {
1039 assert(state
->nums
[i
] >= 0);
1040 state
->numsi
[i
] = -1;
1043 for (i
= 0; i
< state
->n
; i
++) {
1044 if (state
->flags
[i
] & FLAG_IMMUTABLE
) {
1045 assert(state
->nums
[i
] >= 0);
1046 assert(state
->nums
[i
] <= state
->n
);
1047 state
->numsi
[state
->nums
[i
]] = i
;
1049 else if (state
->prev
[i
] == -1 && state
->next
[i
] == -1)
1052 connect_numbers(state
);
1054 for (i
= 0; i
< state
->n
; i
++) {
1055 /* Look for a cell that is the start of a chain
1056 * (has a next but no prev). */
1057 if (state
->prev
[i
] != -1 || state
->next
[i
] == -1) continue;
1059 nnum
= head_number(state
, i
, scratch
);
1062 if (nnum
> 0 && nnum
<= state
->n
)
1063 state
->numsi
[nnum
] = j
;
1064 state
->nums
[j
] = nnum
++;
1066 assert(j
!= i
); /* loop?! */
1069 /*debug_numbers(state);*/
1073 static int check_completion(game_state
*state
, int mark_errors
)
1075 int n
, j
, k
, error
= 0, complete
;
1077 /* NB This only marks errors that are possible to perpetrate with
1078 * the current UI in interpret_move. Things like forming loops in
1079 * linked sections and having numbers not add up should be forbidden
1080 * by the code elsewhere, so we don't bother marking those (because
1081 * it would add lots of tricky drawing code for very little gain). */
1083 for (j
= 0; j
< state
->n
; j
++)
1084 state
->flags
[j
] &= ~FLAG_ERROR
;
1087 /* Search for repeated numbers. */
1088 for (j
= 0; j
< state
->n
; j
++) {
1089 if (state
->nums
[j
] > 0 && state
->nums
[j
] <= state
->n
) {
1090 for (k
= j
+1; k
< state
->n
; k
++) {
1091 if (state
->nums
[k
] == state
->nums
[j
]) {
1093 state
->flags
[j
] |= FLAG_ERROR
;
1094 state
->flags
[k
] |= FLAG_ERROR
;
1102 /* Search and mark numbers n not pointing to n+1; if any numbers
1103 * are missing we know we've not completed. */
1105 for (n
= 1; n
< state
->n
; n
++) {
1106 if (state
->numsi
[n
] == -1 || state
->numsi
[n
+1] == -1)
1108 else if (!ispointingi(state
, state
->numsi
[n
], state
->numsi
[n
+1])) {
1110 state
->flags
[state
->numsi
[n
]] |= FLAG_ERROR
;
1111 state
->flags
[state
->numsi
[n
+1]] |= FLAG_ERROR
;
1115 /* make sure the link is explicitly made here; for instance, this
1116 * is nice if the user drags from 2 out (making 3) and a 4 is also
1117 * visible; this ensures that the link from 3 to 4 is also made. */
1119 makelink(state
, state
->numsi
[n
], state
->numsi
[n
+1]);
1123 if (error
) return 0;
1126 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
1128 game_state
*state
= NULL
;
1130 unpick_desc(params
, desc
, &state
, NULL
);
1131 if (!state
) assert(!"new_game failed to unpick");
1133 update_numbers(state
);
1134 check_completion(state
, 1); /* update any auto-links */
1139 /* --- Solver --- */
1141 /* If a tile has a single tile it can link _to_, or there's only a single
1142 * location that can link to a given tile, fill that link in. */
1143 static int solve_single(game_state
*state
, game_state
*copy
, int *from
)
1145 int i
, j
, sx
, sy
, x
, y
, d
, poss
, w
=state
->w
, nlinks
= 0;
1147 /* The from array is a list of 'which square can link _to_ us';
1148 * we start off with from as '-1' (meaning 'not found'); if we find
1149 * something that can link to us it is set to that index, and then if
1150 * we find another we set it to -2. */
1152 memset(from
, -1, state
->n
*sizeof(int));
1154 /* poss is 'can I link to anything' with the same meanings. */
1156 for (i
= 0; i
< state
->n
; i
++) {
1157 if (state
->next
[i
] != -1) continue;
1158 if (state
->nums
[i
] == state
->n
) continue; /* no next from last no. */
1162 sx
= x
= i
%w
; sy
= y
= i
/w
;
1164 x
+= dxs
[d
]; y
+= dys
[d
];
1165 if (!INGRID(state
, x
, y
)) break;
1166 if (!isvalidmove(state
, 1, sx
, sy
, x
, y
)) continue;
1168 /* can't link to somewhere with a back-link we would have to
1169 * break (the solver just doesn't work like this). */
1171 if (state
->prev
[j
] != -1) continue;
1173 if (state
->nums
[i
] > 0 && state
->nums
[j
] > 0 &&
1174 state
->nums
[i
] <= state
->n
&& state
->nums
[j
] <= state
->n
&&
1175 state
->nums
[j
] == state
->nums
[i
]+1) {
1176 debug(("Solver: forcing link through existing consecutive numbers."));
1182 /* if there's been a valid move already, we have to move on;
1183 * we can't make any deductions here. */
1184 poss
= (poss
== -1) ? j
: -2;
1186 /* Modify the from array as described above (which is enumerating
1187 * what points to 'j' in a similar way). */
1188 from
[j
] = (from
[j
] == -1) ? i
: -2;
1191 /*debug(("Solver: (%d,%d) has multiple possible next squares.", sx, sy));*/
1193 } else if (poss
== -1) {
1194 debug(("Solver: nowhere possible for (%d,%d) to link to.", sx
, sy
));
1195 copy
->impossible
= 1;
1198 debug(("Solver: linking (%d,%d) to only possible next (%d,%d).",
1199 sx
, sy
, poss
%w
, poss
/w
));
1200 makelink(copy
, i
, poss
);
1205 for (i
= 0; i
< state
->n
; i
++) {
1206 if (state
->prev
[i
] != -1) continue;
1207 if (state
->nums
[i
] == 1) continue; /* no prev from 1st no. */
1210 if (from
[i
] == -1) {
1211 debug(("Solver: nowhere possible to link to (%d,%d)", x
, y
));
1212 copy
->impossible
= 1;
1214 } else if (from
[i
] == -2) {
1215 /*debug(("Solver: (%d,%d) has multiple possible prev squares.", x, y));*/
1218 debug(("Solver: linking only possible prev (%d,%d) to (%d,%d).",
1219 from
[i
]%w
, from
[i
]/w
, x
, y
));
1220 makelink(copy
, from
[i
], i
);
1228 /* Returns 1 if we managed to solve it, 0 otherwise. */
1229 static int solve_state(game_state
*state
)
1231 game_state
*copy
= dup_game(state
);
1232 int *scratch
= snewn(state
->n
, int), ret
;
1234 debug_state("Before solver: ", state
);
1237 update_numbers(state
);
1239 if (solve_single(state
, copy
, scratch
)) {
1240 dup_game_to(state
, copy
);
1241 if (state
->impossible
) break; else continue;
1248 update_numbers(state
);
1249 ret
= state
->impossible ?
-1 : check_completion(state
, 0);
1250 debug(("Solver finished: %s",
1251 ret
< 0 ?
"impossible" : ret
> 0 ?
"solved" : "not solved"));
1252 debug_state("After solver: ", state
);
1256 static char *solve_game(game_state
*state
, game_state
*currstate
,
1257 char *aux
, char **error
)
1259 game_state
*tosolve
;
1263 tosolve
= dup_game(currstate
);
1264 result
= solve_state(tosolve
);
1266 ret
= generate_desc(tosolve
, 1);
1268 if (ret
) return ret
;
1270 tosolve
= dup_game(state
);
1271 result
= solve_state(tosolve
);
1273 *error
= "Puzzle is impossible.";
1274 else if (result
== 0)
1275 *error
= "Unable to solve puzzle.";
1277 ret
= generate_desc(tosolve
, 1);
1284 /* --- UI and move routines. --- */
1290 int dragging
, drag_is_from
;
1291 int sx
, sy
; /* grid coords of start cell */
1292 int dx
, dy
; /* pixel coords of drag posn */
1295 static game_ui
*new_ui(game_state
*state
)
1297 game_ui
*ui
= snew(game_ui
);
1299 /* NB: if this is ever changed to as to require more than a structure
1300 * copy to clone, there's code that needs fixing in game_redraw too. */
1302 ui
->cx
= ui
->cy
= ui
->cshow
= 0;
1305 ui
->sx
= ui
->sy
= ui
->dx
= ui
->dy
= 0;
1310 static void free_ui(game_ui
*ui
)
1315 static char *encode_ui(game_ui
*ui
)
1320 static void decode_ui(game_ui
*ui
, char *encoding
)
1324 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
1325 game_state
*newstate
)
1327 if (!oldstate
->completed
&& newstate
->completed
)
1328 ui
->cshow
= ui
->dragging
= 0;
1331 struct game_drawstate
{
1332 int tilesize
, started
, solved
;
1336 double angle_offset
;
1338 int dragging
, dx
, dy
;
1342 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
1343 int mx
, int my
, int button
)
1345 int x
= FROMCOORD(mx
), y
= FROMCOORD(my
), w
= state
->w
;
1348 if (IS_CURSOR_MOVE(button
)) {
1349 move_cursor(button
, &ui
->cx
, &ui
->cy
, state
->w
, state
->h
, 0);
1352 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1353 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1356 } else if (IS_CURSOR_SELECT(button
)) {
1359 else if (ui
->dragging
) {
1360 ui
->dragging
= FALSE
;
1361 if (ui
->sx
== ui
->cx
&& ui
->sy
== ui
->cy
) return "";
1362 if (ui
->drag_is_from
) {
1363 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
)) return "";
1364 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, ui
->cx
, ui
->cy
);
1366 if (!isvalidmove(state
, 0, ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
)) return "";
1367 sprintf(buf
, "L%d,%d-%d,%d", ui
->cx
, ui
->cy
, ui
->sx
, ui
->sy
);
1371 ui
->dragging
= TRUE
;
1374 ui
->dx
= COORD(ui
->cx
) + TILE_SIZE
/2;
1375 ui
->dy
= COORD(ui
->cy
) + TILE_SIZE
/2;
1376 ui
->drag_is_from
= (button
== CURSOR_SELECT
) ?
1 : 0;
1380 if (IS_MOUSE_DOWN(button
)) {
1382 ui
->cshow
= ui
->dragging
= 0;
1384 assert(!ui
->dragging
);
1385 if (!INGRID(state
, x
, y
)) return NULL
;
1387 if (button
== LEFT_BUTTON
) {
1388 /* disallow dragging from the final number. */
1389 if (state
->nums
[y
*w
+x
] == state
->n
) return NULL
;
1390 } else if (button
== RIGHT_BUTTON
) {
1391 /* disallow dragging to the first number. */
1392 if (state
->nums
[y
*w
+x
] == 1) return NULL
;
1395 ui
->dragging
= TRUE
;
1396 ui
->drag_is_from
= (button
== LEFT_BUTTON
) ?
1 : 0;
1403 } else if (IS_MOUSE_DRAG(button
) && ui
->dragging
) {
1407 } else if (IS_MOUSE_RELEASE(button
) && ui
->dragging
) {
1408 ui
->dragging
= FALSE
;
1409 if (ui
->sx
== x
&& ui
->sy
== y
) return ""; /* single click */
1411 if (!INGRID(state
, x
, y
)) {
1412 int si
= ui
->sy
*w
+ui
->sx
;
1413 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1415 sprintf(buf
, "%c%d,%d",
1416 ui
->drag_is_from ?
'C' : 'X', ui
->sx
, ui
->sy
);
1420 if (ui
->drag_is_from
) {
1421 if (!isvalidmove(state
, 0, ui
->sx
, ui
->sy
, x
, y
)) return "";
1422 sprintf(buf
, "L%d,%d-%d,%d", ui
->sx
, ui
->sy
, x
, y
);
1424 if (!isvalidmove(state
, 0, x
, y
, ui
->sx
, ui
->sy
)) return "";
1425 sprintf(buf
, "L%d,%d-%d,%d", x
, y
, ui
->sx
, ui
->sy
);
1428 } /* else if (button == 'H' || button == 'h')
1429 return dupstr("H"); */
1430 else if ((button
== 'x' || button
== 'X') && ui
->cshow
) {
1431 int si
= ui
->cy
*w
+ ui
->cx
;
1432 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1434 sprintf(buf
, "%c%d,%d",
1435 (button
== 'x') ?
'C' : 'X', ui
->cx
, ui
->cy
);
1442 static void unlink_cell(game_state
*state
, int si
)
1444 debug(("Unlinking (%d,%d).", si
%state
->w
, si
/state
->w
));
1445 if (state
->prev
[si
] != -1) {
1446 debug((" ... removing prev link from (%d,%d).",
1447 state
->prev
[si
]%state
->w
, state
->prev
[si
]/state
->w
));
1448 state
->next
[state
->prev
[si
]] = -1;
1449 state
->prev
[si
] = -1;
1451 if (state
->next
[si
] != -1) {
1452 debug((" ... removing next link to (%d,%d).",
1453 state
->next
[si
]%state
->w
, state
->next
[si
]/state
->w
));
1454 state
->prev
[state
->next
[si
]] = -1;
1455 state
->next
[si
] = -1;
1459 static game_state
*execute_move(game_state
*state
, char *move
)
1461 game_state
*ret
= NULL
;
1462 int sx
, sy
, ex
, ey
, si
, ei
, w
= state
->w
;
1465 debug(("move: %s", move
));
1467 if (move
[0] == 'S') {
1473 p
.w
= state
->w
; p
.h
= state
->h
;
1474 valid
= validate_desc(&p
, move
+1);
1476 debug(("execute_move: move not valid: %s", valid
));
1479 ret
= dup_game(state
);
1480 tmp
= new_game(NULL
, &p
, move
+1);
1481 for (i
= 0; i
< state
->n
; i
++) {
1482 ret
->prev
[i
] = tmp
->prev
[i
];
1483 ret
->next
[i
] = tmp
->next
[i
];
1486 ret
->used_solve
= 1;
1487 } else if (sscanf(move
, "L%d,%d-%d,%d", &sx
, &sy
, &ex
, &ey
) == 4) {
1488 if (!isvalidmove(state
, 0, sx
, sy
, ex
, ey
)) return NULL
;
1490 ret
= dup_game(state
);
1492 si
= sy
*w
+sx
; ei
= ey
*w
+ex
;
1493 makelink(ret
, si
, ei
);
1494 } else if (sscanf(move
, "%c%d,%d", &c
, &sx
, &sy
) == 3) {
1495 if (c
!= 'C' && c
!= 'X') return NULL
;
1496 if (!INGRID(state
, sx
, sy
)) return NULL
;
1498 if (state
->prev
[si
] == -1 && state
->next
[si
] == -1)
1501 ret
= dup_game(state
);
1504 /* Unlink the single cell we dragged from the board. */
1505 unlink_cell(ret
, si
);
1507 int i
, set
, sset
= state
->nums
[si
] / (state
->n
+1);
1508 for (i
= 0; i
< state
->n
; i
++) {
1509 /* Unlink all cells in the same set as the one we dragged
1510 * from the board. */
1512 if (state
->nums
[i
] == 0) continue;
1513 set
= state
->nums
[i
] / (state
->n
+1);
1514 if (set
!= sset
) continue;
1516 unlink_cell(ret
, i
);
1519 } else if (strcmp(move
, "H") == 0) {
1520 ret
= dup_game(state
);
1524 update_numbers(ret
);
1525 if (check_completion(ret
, 1)) ret
->completed
= 1;
1531 /* ----------------------------------------------------------------------
1535 static void game_compute_size(game_params
*params
, int tilesize
,
1538 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1539 struct { int tilesize
, order
; } ads
, *ds
= &ads
;
1540 ads
.tilesize
= tilesize
;
1542 *x
= TILE_SIZE
* params
->w
+ 2 * BORDER
;
1543 *y
= TILE_SIZE
* params
->h
+ 2 * BORDER
;
1546 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
1547 game_params
*params
, int tilesize
)
1549 ds
->tilesize
= tilesize
;
1550 assert(TILE_SIZE
> 0);
1553 ds
->dragb
= blitter_new(dr
, BLITTER_SIZE
, BLITTER_SIZE
);
1556 /* Colours chosen from the webby palette to work as a background to black text,
1557 * W then some plausible approximation to pastelly ROYGBIV; we then interpolate
1558 * between consecutive pairs to give another 8 (and then the drawing routine
1559 * will reuse backgrounds). */
1560 static const unsigned long bgcols
[8] = {
1561 0xffffff, /* white */
1562 0xffa07a, /* lightsalmon */
1563 0x98fb98, /* green */
1564 0x7fffd4, /* aquamarine */
1565 0x9370db, /* medium purple */
1566 0xffa500, /* orange */
1567 0x87cefa, /* lightskyblue */
1568 0xffff00, /* yellow */
1571 static float *game_colours(frontend
*fe
, int *ncolours
)
1573 float *ret
= snewn(3 * NCOLOURS
, float);
1576 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
1578 for (i
= 0; i
< 3; i
++) {
1579 ret
[COL_NUMBER
* 3 + i
] = 0.0F
;
1580 ret
[COL_ARROW
* 3 + i
] = 0.0F
;
1581 ret
[COL_CURSOR
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 2.0F
;
1582 ret
[COL_GRID
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] / 1.3F
;
1584 ret
[COL_NUMBER_SET
* 3 + 0] = 0.0F
;
1585 ret
[COL_NUMBER_SET
* 3 + 1] = 0.0F
;
1586 ret
[COL_NUMBER_SET
* 3 + 2] = 0.9F
;
1588 ret
[COL_ERROR
* 3 + 0] = 1.0F
;
1589 ret
[COL_ERROR
* 3 + 1] = 0.0F
;
1590 ret
[COL_ERROR
* 3 + 2] = 0.0F
;
1592 ret
[COL_DRAG_ORIGIN
* 3 + 0] = 0.2F
;
1593 ret
[COL_DRAG_ORIGIN
* 3 + 1] = 1.0F
;
1594 ret
[COL_DRAG_ORIGIN
* 3 + 2] = 0.2F
;
1596 for (c
= 0; c
< 8; c
++) {
1597 ret
[(COL_B0
+ c
) * 3 + 0] = (float)((bgcols
[c
] & 0xff0000) >> 16) / 256.0F
;
1598 ret
[(COL_B0
+ c
) * 3 + 1] = (float)((bgcols
[c
] & 0xff00) >> 8) / 256.0F
;
1599 ret
[(COL_B0
+ c
) * 3 + 2] = (float)((bgcols
[c
] & 0xff)) / 256.0F
;
1601 for (c
= 0; c
< 8; c
++) {
1602 for (i
= 0; i
< 3; i
++) {
1603 ret
[(COL_B0
+ 8 + c
) * 3 + i
] =
1604 (ret
[(COL_B0
+ c
) * 3 + i
] + ret
[(COL_B0
+ c
+ 1) * 3 + i
]) / 2.0F
;
1608 #define average(r,a,b,w) do { \
1609 for (i = 0; i < 3; i++) \
1610 ret[(r)*3+i] = ret[(a)*3+i] + w * (ret[(b)*3+i] - ret[(a)*3+i]); \
1612 average(COL_ARROW_BG_DIM
, COL_BACKGROUND
, COL_ARROW
, 0.1F
);
1613 average(COL_NUMBER_SET_MID
, COL_B0
, COL_NUMBER_SET
, 0.3F
);
1614 for (c
= 0; c
< NBACKGROUNDS
; c
++) {
1615 /* I assume here that COL_ARROW and COL_NUMBER are the same.
1616 * Otherwise I'd need two sets of COL_M*. */
1617 average(COL_M0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.3F
);
1618 average(COL_D0
+ c
, COL_B0
+ c
, COL_NUMBER
, 0.1F
);
1619 average(COL_X0
+ c
, COL_BACKGROUND
, COL_B0
+ c
, 0.5F
);
1622 *ncolours
= NCOLOURS
;
1626 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1628 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1631 ds
->tilesize
= ds
->started
= ds
->solved
= 0;
1636 ds
->nums
= snewn(state
->n
, int);
1637 ds
->dirp
= snewn(state
->n
, int);
1638 ds
->f
= snewn(state
->n
, unsigned int);
1639 for (i
= 0; i
< state
->n
; i
++) {
1645 ds
->angle_offset
= 0.0F
;
1647 ds
->dragging
= ds
->dx
= ds
->dy
= 0;
1653 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1658 if (ds
->dragb
) blitter_free(dr
, ds
->dragb
);
1663 /* cx, cy are top-left corner. sz is the 'radius' of the arrow.
1664 * ang is in radians, clockwise from 0 == straight up. */
1665 static void draw_arrow(drawing
*dr
, int cx
, int cy
, int sz
, double ang
,
1666 int cfill
, int cout
)
1669 int xdx
, ydx
, xdy
, ydy
, xdx3
, xdy3
;
1670 double s
= sin(ang
), c
= cos(ang
);
1672 xdx3
= (int)(sz
* (c
/3 + 1) + 0.5) - sz
;
1673 xdy3
= (int)(sz
* (s
/3 + 1) + 0.5) - sz
;
1674 xdx
= (int)(sz
* (c
+ 1) + 0.5) - sz
;
1675 xdy
= (int)(sz
* (s
+ 1) + 0.5) - sz
;
1680 coords
[2*0 + 0] = cx
- ydx
;
1681 coords
[2*0 + 1] = cy
- ydy
;
1682 coords
[2*1 + 0] = cx
+ xdx
;
1683 coords
[2*1 + 1] = cy
+ xdy
;
1684 coords
[2*2 + 0] = cx
+ xdx3
;
1685 coords
[2*2 + 1] = cy
+ xdy3
;
1686 coords
[2*3 + 0] = cx
+ xdx3
+ ydx
;
1687 coords
[2*3 + 1] = cy
+ xdy3
+ ydy
;
1688 coords
[2*4 + 0] = cx
- xdx3
+ ydx
;
1689 coords
[2*4 + 1] = cy
- xdy3
+ ydy
;
1690 coords
[2*5 + 0] = cx
- xdx3
;
1691 coords
[2*5 + 1] = cy
- xdy3
;
1692 coords
[2*6 + 0] = cx
- xdx
;
1693 coords
[2*6 + 1] = cy
- xdy
;
1695 draw_polygon(dr
, coords
, 7, cfill
, cout
);
1698 static void draw_arrow_dir(drawing
*dr
, int cx
, int cy
, int sz
, int dir
,
1699 int cfill
, int cout
, double angle_offset
)
1701 double ang
= 2.0 * PI
* (double)dir
/ 8.0 + angle_offset
;
1702 draw_arrow(dr
, cx
, cy
, sz
, ang
, cfill
, cout
);
1705 /* cx, cy are centre coordinates.. */
1706 static void draw_star(drawing
*dr
, int cx
, int cy
, int rad
, int npoints
,
1707 int cfill
, int cout
, double angle_offset
)
1712 assert(npoints
> 0);
1714 coords
= snewn(npoints
* 2 * 2, int);
1716 for (n
= 0; n
< npoints
* 2; n
++) {
1717 a
= 2.0 * PI
* ((double)n
/ ((double)npoints
* 2.0)) + angle_offset
;
1718 r
= (n
% 2) ?
(double)rad
/2.0 : (double)rad
;
1720 /* We're rotating the point at (0, -r) by a degrees */
1721 coords
[2*n
+0] = cx
+ (int)( r
* sin(a
));
1722 coords
[2*n
+1] = cy
+ (int)(-r
* cos(a
));
1724 draw_polygon(dr
, coords
, npoints
*2, cfill
, cout
);
1728 static int num2col(game_drawstate
*ds
, int num
)
1730 int set
= num
/ (ds
->n
+1);
1732 if (num
<= 0) return COL_BACKGROUND
;
1733 return COL_B0
+ (set
% 16);
1736 #define ARROW_HALFSZ (7 * TILE_SIZE / 32)
1738 #define F_CUR 0x001 /* Cursor on this tile. */
1739 #define F_DRAG_SRC 0x002 /* Tile is source of a drag. */
1740 #define F_ERROR 0x004 /* Tile marked in error. */
1741 #define F_IMMUTABLE 0x008 /* Tile (number) is immutable. */
1742 #define F_ARROW_POINT 0x010 /* Tile points to other tile */
1743 #define F_ARROW_INPOINT 0x020 /* Other tile points in here. */
1744 #define F_DIM 0x040 /* Tile is dim */
1746 static void tile_redraw(drawing
*dr
, game_drawstate
*ds
, int tx
, int ty
,
1747 int dir
, int dirp
, int num
, unsigned int f
,
1748 double angle_offset
, int print_ink
)
1750 int cb
= TILE_SIZE
/ 16, textsz
;
1752 int arrowcol
, sarrowcol
, setcol
, textcol
;
1753 int n
= num
% (ds
->n
+1), set
= num
/ (ds
->n
+1);
1756 /* Calculate colours. */
1758 if (print_ink
>= 0) {
1760 * We're printing, so just do everything in black.
1762 arrowcol
= textcol
= print_ink
;
1763 setcol
= sarrowcol
= -1; /* placate optimiser */
1766 setcol
= num2col(ds
, num
);
1768 #define dim(fg,bg) ( \
1769 (bg)==COL_BACKGROUND ? COL_ARROW_BG_DIM : \
1770 (bg) + COL_D0 - COL_B0 \
1773 #define mid(fg,bg) ( \
1774 (fg)==COL_NUMBER_SET ? COL_NUMBER_SET_MID : \
1775 (bg) + COL_M0 - COL_B0 \
1778 #define dimbg(bg) ( \
1779 (bg)==COL_BACKGROUND ? COL_BACKGROUND : \
1780 (bg) + COL_X0 - COL_B0 \
1783 if (f
& F_DRAG_SRC
) arrowcol
= COL_DRAG_ORIGIN
;
1784 else if (f
& F_DIM
) arrowcol
= dim(COL_ARROW
, setcol
);
1785 else if (f
& F_ARROW_POINT
) arrowcol
= mid(COL_ARROW
, setcol
);
1786 else arrowcol
= COL_ARROW
;
1788 if (f
& (F_ERROR
)) textcol
= COL_ERROR
;
1790 if (f
& F_IMMUTABLE
) textcol
= COL_NUMBER_SET
;
1791 else textcol
= COL_NUMBER
;
1793 if (f
& F_DIM
) textcol
= dim(textcol
, setcol
);
1794 else if (((f
& F_ARROW_POINT
) || num
==ds
->n
) &&
1795 ((f
& F_ARROW_INPOINT
) || num
==1))
1796 textcol
= mid(textcol
, setcol
);
1799 if (f
& F_DIM
) sarrowcol
= dim(COL_ARROW
, setcol
);
1800 else sarrowcol
= COL_ARROW
;
1803 /* Clear tile background */
1805 if (print_ink
< 0) {
1806 draw_rect(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
,
1807 (f
& F_DIM
) ?
dimbg(setcol
) : setcol
);
1810 /* Draw large (outwards-pointing) arrow. */
1812 asz
= ARROW_HALFSZ
; /* 'radius' of arrow/star. */
1813 acx
= tx
+TILE_SIZE
/2+asz
; /* centre x */
1814 acy
= ty
+TILE_SIZE
/2+asz
; /* centre y */
1816 if (num
== ds
->n
&& (f
& F_IMMUTABLE
))
1817 draw_star(dr
, acx
, acy
, asz
, 5, arrowcol
, arrowcol
, angle_offset
);
1819 draw_arrow_dir(dr
, acx
, acy
, asz
, dir
, arrowcol
, arrowcol
, angle_offset
);
1820 if (print_ink
< 0 && (f
& F_CUR
))
1821 draw_rect_corners(dr
, acx
, acy
, asz
+1, COL_CURSOR
);
1823 /* Draw dot iff this tile requires a predecessor and doesn't have one. */
1825 if (print_ink
< 0) {
1826 acx
= tx
+TILE_SIZE
/2-asz
;
1827 acy
= ty
+TILE_SIZE
/2+asz
;
1829 if (!(f
& F_ARROW_INPOINT
) && num
!= 1) {
1830 draw_circle(dr
, acx
, acy
, asz
/ 4, sarrowcol
, sarrowcol
);
1834 /* Draw text (number or set). */
1837 /* assert(num > 0); - actually, no, this obstructs legal play */
1839 sprintf(buf
, "%d", n
);
1842 sprintf(buf
, "%c", (int)(set
+'a'-1));
1844 sprintf(buf
, "%c+%d", (int)(set
+'a'-1), n
);
1846 textsz
= min(2*asz
, (TILE_SIZE
- 2 * cb
) / (int)strlen(buf
));
1847 draw_text(dr
, tx
+ cb
, ty
+ TILE_SIZE
/4, FONT_VARIABLE
, textsz
,
1848 ALIGN_VCENTRE
| ALIGN_HLEFT
, textcol
, buf
);
1851 if (print_ink
< 0) {
1852 draw_rect_outline(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
, COL_GRID
);
1853 draw_update(dr
, tx
, ty
, TILE_SIZE
, TILE_SIZE
);
1857 static void draw_drag_indicator(drawing
*dr
, game_drawstate
*ds
,
1858 game_state
*state
, game_ui
*ui
, int validdrag
)
1860 int dir
, w
= ds
->w
, acol
= COL_ARROW
;
1861 int fx
= FROMCOORD(ui
->dx
), fy
= FROMCOORD(ui
->dy
);
1865 /* If we could move here, lock the arrow to the appropriate direction. */
1866 dir
= ui
->drag_is_from ? state
->dirs
[ui
->sy
*w
+ui
->sx
] : state
->dirs
[fy
*w
+fx
];
1868 ang
= (2.0 * PI
* dir
) / 8.0; /* similar to calculation in draw_arrow_dir. */
1870 /* Draw an arrow pointing away from/towards the origin cell. */
1871 int ox
= COORD(ui
->sx
) + TILE_SIZE
/2, oy
= COORD(ui
->sy
) + TILE_SIZE
/2;
1872 double tana
, offset
;
1873 double xdiff
= fabs(ox
- ui
->dx
), ydiff
= fabs(oy
- ui
->dy
);
1876 ang
= (oy
> ui
->dy
) ?
0.0F
: PI
;
1877 } else if (ydiff
== 0) {
1878 ang
= (ox
> ui
->dx
) ?
3.0F
*PI
/2.0F
: PI
/2.0F
;
1880 if (ui
->dx
> ox
&& ui
->dy
< oy
) {
1881 tana
= xdiff
/ ydiff
;
1883 } else if (ui
->dx
> ox
&& ui
->dy
> oy
) {
1884 tana
= ydiff
/ xdiff
;
1886 } else if (ui
->dx
< ox
&& ui
->dy
> oy
) {
1887 tana
= xdiff
/ ydiff
;
1890 tana
= ydiff
/ xdiff
;
1891 offset
= 3.0F
* PI
/ 2.0F
;
1893 ang
= atan(tana
) + offset
;
1896 if (!ui
->drag_is_from
) ang
+= PI
; /* point to origin, not away from. */
1899 draw_arrow(dr
, ui
->dx
, ui
->dy
, ARROW_HALFSZ
, ang
, acol
, acol
);
1902 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
1903 game_state
*state
, int dir
, game_ui
*ui
,
1904 float animtime
, float flashtime
)
1906 int x
, y
, i
, w
= ds
->w
, dirp
, force
= 0;
1908 double angle_offset
= 0.0;
1909 game_state
*postdrop
= NULL
;
1911 if (flashtime
> 0.0F
)
1912 angle_offset
= 2.0 * PI
* (flashtime
/ FLASH_SPIN
);
1913 if (angle_offset
!= ds
->angle_offset
) {
1914 ds
->angle_offset
= angle_offset
;
1920 blitter_load(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
1921 draw_update(dr
, ds
->dx
, ds
->dy
, BLITTER_SIZE
, BLITTER_SIZE
);
1922 ds
->dragging
= FALSE
;
1925 /* If an in-progress drag would make a valid move if finished, we
1926 * reflect that move in the board display. We let interpret_move do
1927 * most of the heavy lifting for us: we have to copy the game_ui so
1928 * as not to stomp on the real UI's drag state. */
1930 game_ui uicopy
= *ui
;
1931 char *movestr
= interpret_move(state
, &uicopy
, ds
, ui
->dx
, ui
->dy
, LEFT_RELEASE
);
1933 if (movestr
!= NULL
&& strcmp(movestr
, "") != 0) {
1934 postdrop
= execute_move(state
, movestr
);
1942 int aw
= TILE_SIZE
* state
->w
;
1943 int ah
= TILE_SIZE
* state
->h
;
1944 draw_rect(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
, COL_BACKGROUND
);
1945 draw_rect_outline(dr
, BORDER
- 1, BORDER
- 1, aw
+ 2, ah
+ 2, COL_GRID
);
1946 draw_update(dr
, 0, 0, aw
+ 2 * BORDER
, ah
+ 2 * BORDER
);
1948 for (x
= 0; x
< state
->w
; x
++) {
1949 for (y
= 0; y
< state
->h
; y
++) {
1954 if (ui
->cshow
&& x
== ui
->cx
&& y
== ui
->cy
)
1958 if (x
== ui
->sx
&& y
== ui
->sy
)
1960 else if (ui
->drag_is_from
) {
1961 if (!ispointing(state
, ui
->sx
, ui
->sy
, x
, y
))
1964 if (!ispointing(state
, x
, y
, ui
->sx
, ui
->sy
))
1969 if (state
->impossible
||
1970 state
->nums
[i
] < 0 || state
->flags
[i
] & FLAG_ERROR
)
1972 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
1975 if (state
->next
[i
] != -1)
1978 if (state
->prev
[i
] != -1) {
1979 /* Currently the direction here is from our square _back_
1980 * to its previous. We could change this to give the opposite
1981 * sense to the direction. */
1982 f
|= F_ARROW_INPOINT
;
1983 dirp
= whichdir(x
, y
, state
->prev
[i
]%w
, state
->prev
[i
]/w
);
1986 if (state
->nums
[i
] != ds
->nums
[i
] ||
1987 f
!= ds
->f
[i
] || dirp
!= ds
->dirp
[i
] ||
1988 force
|| !ds
->started
) {
1990 BORDER
+ x
* TILE_SIZE
,
1991 BORDER
+ y
* TILE_SIZE
,
1992 state
->dirs
[i
], dirp
, state
->nums
[i
], f
,
1994 ds
->nums
[i
] = state
->nums
[i
];
2001 ds
->dragging
= TRUE
;
2002 ds
->dx
= ui
->dx
- BLITTER_SIZE
/2;
2003 ds
->dy
= ui
->dy
- BLITTER_SIZE
/2;
2004 blitter_save(dr
, ds
->dragb
, ds
->dx
, ds
->dy
);
2006 draw_drag_indicator(dr
, ds
, state
, ui
, postdrop ?
1 : 0);
2008 if (postdrop
) free_game(postdrop
);
2009 if (!ds
->started
) ds
->started
= TRUE
;
2012 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
2013 int dir
, game_ui
*ui
)
2018 static float game_flash_length(game_state
*oldstate
, game_state
*newstate
,
2019 int dir
, game_ui
*ui
)
2021 if (!oldstate
->completed
&&
2022 newstate
->completed
&& !newstate
->used_solve
)
2028 static int game_timing_state(game_state
*state
, game_ui
*ui
)
2033 static void game_print_size(game_params
*params
, float *x
, float *y
)
2037 game_compute_size(params
, 1300, &pw
, &ph
);
2042 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
2044 int ink
= print_mono_colour(dr
, 0);
2047 /* Fake up just enough of a drawstate */
2048 game_drawstate ads
, *ds
= &ads
;
2049 ds
->tilesize
= tilesize
;
2055 print_line_width(dr
, TILE_SIZE
/ 40);
2056 for (x
= 1; x
< state
->w
; x
++)
2057 draw_line(dr
, COORD(x
), COORD(0), COORD(x
), COORD(state
->h
), ink
);
2058 for (y
= 1; y
< state
->h
; y
++)
2059 draw_line(dr
, COORD(0), COORD(y
), COORD(state
->w
), COORD(y
), ink
);
2060 print_line_width(dr
, 2*TILE_SIZE
/ 40);
2061 draw_rect_outline(dr
, COORD(0), COORD(0), TILE_SIZE
*state
->w
,
2062 TILE_SIZE
*state
->h
, ink
);
2065 * Arrows and numbers.
2067 print_line_width(dr
, 0);
2068 for (y
= 0; y
< state
->h
; y
++)
2069 for (x
= 0; x
< state
->w
; x
++)
2070 tile_redraw(dr
, ds
, COORD(x
), COORD(y
), state
->dirs
[y
*state
->w
+x
],
2071 0, state
->nums
[y
*state
->w
+x
], 0, 0.0, ink
);
2075 #define thegame signpost
2078 const struct game thegame
= {
2079 "Signpost", "games.signpost", "signpost",
2086 TRUE
, game_configure
, custom_params
,
2094 TRUE
, game_can_format_as_text_now
, game_text_format
,
2102 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
2105 game_free_drawstate
,
2109 TRUE
, FALSE
, game_print_size
, game_print
,
2110 FALSE
, /* wants_statusbar */
2111 FALSE
, game_timing_state
,
2112 REQUIRE_RBUTTON
| REQUIRE_NUMPAD
, /* flags */
2115 #ifdef STANDALONE_SOLVER
2120 const char *quis
= NULL
;
2123 void usage(FILE *out
) {
2124 fprintf(out
, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis
);
2127 static void cycle_seed(char **seedstr
, random_state
*rs
)
2133 newseed
[0] = '1' + (char)random_upto(rs
, 9);
2134 for (j
= 1; j
< 15; j
++)
2135 newseed
[j
] = '0' + (char)random_upto(rs
, 10);
2137 *seedstr
= dupstr(newseed
);
2140 static void start_soak(game_params
*p
, char *seedstr
)
2142 time_t tt_start
, tt_now
, tt_last
;
2145 long n
= 0, nnums
= 0, i
;
2148 tt_start
= tt_now
= time(NULL
);
2149 printf("Soak-generating a %dx%d grid.\n", p
->w
, p
->h
);
2152 rs
= random_new(seedstr
, strlen(seedstr
));
2153 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2155 state
= thegame
.new_game(NULL
, p
, desc
);
2156 for (i
= 0; i
< state
->n
; i
++) {
2157 if (state
->flags
[i
] & FLAG_IMMUTABLE
)
2160 thegame
.free_game(state
);
2163 cycle_seed(&seedstr
, rs
);
2167 tt_last
= time(NULL
);
2168 if (tt_last
> tt_now
) {
2170 printf("%ld total, %3.1f/s, %3.1f nums/grid (%3.1f%%).\n",
2172 (double)n
/ ((double)tt_now
- tt_start
),
2173 (double)nnums
/ (double)n
,
2174 ((double)nnums
* 100.0) / ((double)n
* (double)p
->w
* (double)p
->h
) );
2179 static void process_desc(char *id
)
2181 char *desc
, *err
, *solvestr
;
2185 printf("%s\n ", id
);
2187 desc
= strchr(id
, ':');
2189 fprintf(stderr
, "%s: expecting game description.", quis
);
2195 p
= thegame
.default_params();
2196 thegame
.decode_params(p
, id
);
2197 err
= thegame
.validate_params(p
, 1);
2199 fprintf(stderr
, "%s: %s", quis
, err
);
2200 thegame
.free_params(p
);
2204 err
= thegame
.validate_desc(p
, desc
);
2206 fprintf(stderr
, "%s: %s\nDescription: %s\n", quis
, err
, desc
);
2207 thegame
.free_params(p
);
2211 s
= thegame
.new_game(NULL
, p
, desc
);
2213 solvestr
= thegame
.solve(s
, s
, NULL
, &err
);
2215 fprintf(stderr
, "%s\n", err
);
2217 printf("Puzzle is soluble.\n");
2219 thegame
.free_game(s
);
2220 thegame
.free_params(p
);
2223 int main(int argc
, const char *argv
[])
2225 char *id
= NULL
, *desc
, *err
, *aux
= NULL
;
2226 int soak
= 0, verbose
= 0, stdin_desc
= 0, n
= 1, i
;
2227 char *seedstr
= NULL
, newseed
[16];
2229 setvbuf(stdout
, NULL
, _IONBF
, 0);
2232 while (--argc
> 0) {
2233 char *p
= (char*)(*++argv
);
2234 if (!strcmp(p
, "-v") || !strcmp(p
, "--verbose"))
2236 else if (!strcmp(p
, "--stdin"))
2238 else if (!strcmp(p
, "-e") || !strcmp(p
, "--seed")) {
2239 seedstr
= dupstr(*++argv
);
2241 } else if (!strcmp(p
, "-n") || !strcmp(p
, "--number")) {
2244 } else if (!strcmp(p
, "-s") || !strcmp(p
, "--soak")) {
2246 } else if (*p
== '-') {
2247 fprintf(stderr
, "%s: unrecognised option `%s'\n", argv
[0], p
);
2255 sprintf(newseed
, "%lu", time(NULL
));
2256 seedstr
= dupstr(newseed
);
2258 if (id
|| !stdin_desc
) {
2259 if (id
&& strchr(id
, ':')) {
2260 /* Parameters and description passed on cmd-line:
2261 * try and solve it. */
2264 /* No description passed on cmd-line: decode parameters
2265 * (with optional seed too) */
2267 game_params
*p
= thegame
.default_params();
2270 char *cmdseed
= strchr(id
, '#');
2274 seedstr
= dupstr(cmdseed
);
2277 thegame
.decode_params(p
, id
);
2280 err
= thegame
.validate_params(p
, 1);
2282 fprintf(stderr
, "%s: %s", quis
, err
);
2283 thegame
.free_params(p
);
2287 /* We have a set of valid parameters; either soak with it
2288 * or generate a single game description and print to stdout. */
2290 start_soak(p
, seedstr
);
2292 char *pstring
= thegame
.encode_params(p
, 0);
2294 for (i
= 0; i
< n
; i
++) {
2295 random_state
*rs
= random_new(seedstr
, strlen(seedstr
));
2297 if (verbose
) printf("%s#%s\n", pstring
, seedstr
);
2298 desc
= thegame
.new_desc(p
, rs
, &aux
, 0);
2299 printf("%s:%s\n", pstring
, desc
);
2302 cycle_seed(&seedstr
, rs
);
2309 thegame
.free_params(p
);
2316 while (fgets(buf
, sizeof(buf
), stdin
)) {
2317 buf
[strcspn(buf
, "\r\n")] = '\0';
2329 /* vim: set shiftwidth=4 tabstop=8: */