Chris Boyle reports an off-by-two error ('a Qui-Gon Jinx' :-) in
[sgt/puzzles] / signpost.c
CommitLineData
4cbcbfca 1/*
2 * signpost.c: implementation of the janko game 'arrow path'
4cbcbfca 3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <assert.h>
9#include <ctype.h>
10#include <math.h>
11
12#include "puzzles.h"
13
14#define PREFERRED_TILE_SIZE 48
15#define TILE_SIZE (ds->tilesize)
16#define BLITTER_SIZE TILE_SIZE
17#define BORDER (TILE_SIZE / 2)
18
19#define COORD(x) ( (x) * TILE_SIZE + BORDER )
20#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
21
22#define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
23
24#define FLASH_SPIN 0.7F
25
26#define NBACKGROUNDS 16
27
28enum {
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
38};
39
40struct game_params {
41 int w, h;
42 int force_corner_start;
43};
44
45enum { DIR_N = 0, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW, DIR_MAX };
46static const char *dirstrings[8] = { "N ", "NE", "E ", "SE", "S ", "SW", "W ", "NW" };
47
48static const int dxs[DIR_MAX] = { 0, 1, 1, 1, 0, -1, -1, -1 };
49static const int dys[DIR_MAX] = { -1, -1, 0, 1, 1, 1, 0, -1 };
50
51#define DIR_OPPOSITE(d) ((d+4)%8)
52
53struct game_state {
54 int w, h, n;
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) */
62};
63
64#define FLAG_IMMUTABLE 1
65#define FLAG_ERROR 2
66
67/* --- Generally useful functions --- */
68
69#define ISREALNUM(state, num) ((num) > 0 && (num) <= (state)->n)
70
71static int whichdir(int fromx, int fromy, int tox, int toy)
72{
73 int i, dx, dy;
74
75 dx = tox - fromx;
76 dy = toy - fromy;
77
78 if (dx && dy && abs(dx) != abs(dy)) return -1;
79
80 if (dx) dx = dx / abs(dx); /* limit to (-1, 0, 1) */
81 if (dy) dy = dy / abs(dy); /* ditto */
82
83 for (i = 0; i < DIR_MAX; i++) {
84 if (dx == dxs[i] && dy == dys[i]) return i;
85 }
86 return -1;
87}
88
89static int whichdiri(game_state *state, int fromi, int toi)
90{
91 int w = state->w;
92 return whichdir(fromi%w, fromi/w, toi%w, toi/w);
93}
94
95static int ispointing(game_state *state, int fromx, int fromy, int tox, int toy)
96{
97 int w = state->w, dir = state->dirs[fromy*w+fromx];
98
99 /* (by convention) squares do not point to themselves. */
100 if (fromx == tox && fromy == toy) return 0;
101
102 /* the final number points to nothing. */
103 if (state->nums[fromy*w + fromx] == state->n) return 0;
104
105 while (1) {
106 if (!INGRID(state, fromx, fromy)) return 0;
107 if (fromx == tox && fromy == toy) return 1;
108 fromx += dxs[dir]; fromy += dys[dir];
109 }
110 return 0; /* not reached */
111}
112
113static int ispointingi(game_state *state, int fromi, int toi)
114{
115 int w = state->w;
116 return ispointing(state, fromi%w, fromi/w, toi%w, toi/w);
117}
118
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. */
122static int move_couldfit(game_state *state, int num, int d, int x, int y)
123{
124 int n, gap, i = y*state->w+x, sz;
125
126 assert(d != 0);
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 */
133
134 if (gap == 0) {
135 /* no gap, so the only allowable move is that that directly
136 * links the two numbers. */
137 n = state->nums[i];
138 return (n == num+d) ? 0 : 1;
139 }
140 if (state->prev[i] == -1 && state->next[i] == -1)
141 return 1; /* single unconnected square, always OK */
142
143 sz = dsf_size(state->dsf, i);
144 return (sz > gap) ? 0 : 1;
145}
146
147static int isvalidmove(game_state *state, int clever,
148 int fromx, int fromy, int tox, int toy)
149{
150 int w = state->w, from = fromy*w+fromx, to = toy*w+tox;
151 int nfrom, nto;
152
153 if (!INGRID(state, fromx, fromy) || !INGRID(state, tox, toy))
154 return 0;
155
156 /* can only move where we point */
157 if (!ispointing(state, fromx, fromy, tox, toy))
158 return 0;
159
160 nfrom = state->nums[from]; nto = state->nums[to];
161
51990f54 162 /* can't move _from_ the preset final number, or _to_ the preset 1. */
163 if (((nfrom == state->n) && (state->flags[from] & FLAG_IMMUTABLE)) ||
164 ((nto == 1) && (state->flags[to] & FLAG_IMMUTABLE)))
4cbcbfca 165 return 0;
166
167 /* can't create a new connection between cells in the same region
168 * as that would create a loop. */
169 if (dsf_canonify(state->dsf, from) == dsf_canonify(state->dsf, to))
170 return 0;
171
172 /* if both cells are actual numbers, can't drag if we're not
173 * one digit apart. */
174 if (ISREALNUM(state, nfrom) && ISREALNUM(state, nto)) {
175 if (nfrom != nto-1)
176 return 0;
177 } else if (clever && ISREALNUM(state, nfrom)) {
178 if (!move_couldfit(state, nfrom, +1, tox, toy))
179 return 0;
180 } else if (clever && ISREALNUM(state, nto)) {
181 if (!move_couldfit(state, nto, -1, fromx, fromy))
182 return 0;
183 }
184
185 return 1;
186}
187
188static void makelink(game_state *state, int from, int to)
189{
190 if (state->next[from] != -1)
191 state->prev[state->next[from]] = -1;
192 state->next[from] = to;
193
194 if (state->prev[to] != -1)
195 state->next[state->prev[to]] = -1;
196 state->prev[to] = from;
197}
198
199static int game_can_format_as_text_now(game_params *params)
200{
201 if (params->w * params->h >= 100) return 0;
202 return 1;
203}
204
205static char *game_text_format(game_state *state)
206{
207 int len = state->h * 2 * (4*state->w + 1) + state->h + 2;
208 int x, y, i, num, n, set;
209 char *ret, *p;
210
211 p = ret = snewn(len, char);
212
213 for (y = 0; y < state->h; y++) {
214 for (x = 0; x < state->h; x++) {
215 i = y*state->w+x;
216 *p++ = dirstrings[state->dirs[i]][0];
217 *p++ = dirstrings[state->dirs[i]][1];
218 *p++ = (state->flags[i] & FLAG_IMMUTABLE) ? 'I' : ' ';
219 *p++ = ' ';
220 }
221 *p++ = '\n';
222 for (x = 0; x < state->h; x++) {
223 i = y*state->w+x;
224 num = state->nums[i];
225 if (num == 0) {
226 *p++ = ' ';
227 *p++ = ' ';
228 *p++ = ' ';
229 } else {
230 n = num % (state->n+1);
231 set = num / (state->n+1);
232
233 assert(n <= 99); /* two digits only! */
234
235 if (set != 0)
236 *p++ = set+'a'-1;
237
238 *p++ = (n >= 10) ? ('0' + (n/10)) : ' ';
239 *p++ = '0' + (n%10);
240
241 if (set == 0)
242 *p++ = ' ';
243 }
244 *p++ = ' ';
245 }
246 *p++ = '\n';
247 *p++ = '\n';
248 }
249 *p++ = '\0';
250
251 return ret;
252}
253
254static void debug_state(const char *desc, game_state *state)
255{
256#ifdef DEBUGGING
257 char *dbg;
258 if (state->n >= 100) {
259 debug(("[ no game_text_format for this size ]"));
260 return;
261 }
262 dbg = game_text_format(state);
263 debug(("%s\n%s", desc, dbg));
264 sfree(dbg);
265#endif
266}
267
268
269static void strip_nums(game_state *state) {
270 int i;
271 for (i = 0; i < state->n; i++) {
272 if (!(state->flags[i] & FLAG_IMMUTABLE))
273 state->nums[i] = 0;
274 }
275 memset(state->next, -1, state->n*sizeof(int));
276 memset(state->prev, -1, state->n*sizeof(int));
277 memset(state->numsi, -1, (state->n+1)*sizeof(int));
278 dsf_init(state->dsf, state->n);
279}
280
281static int check_nums(game_state *orig, game_state *copy, int only_immutable)
282{
283 int i, ret = 1;
284 assert(copy->n == orig->n);
285 for (i = 0; i < copy->n; i++) {
286 if (only_immutable && !copy->flags[i] & FLAG_IMMUTABLE) continue;
287 assert(copy->nums[i] >= 0);
288 assert(copy->nums[i] <= copy->n);
289 if (copy->nums[i] != orig->nums[i]) {
290 debug(("check_nums: (%d,%d) copy=%d, orig=%d.",
291 i%orig->w, i/orig->w, copy->nums[i], orig->nums[i]));
292 ret = 0;
293 }
294 }
295 return ret;
296}
297
298/* --- Game parameter/presets functions --- */
299
300static game_params *default_params(void)
301{
302 game_params *ret = snew(game_params);
303 ret->w = ret->h = 4;
304 ret->force_corner_start = 1;
305
306 return ret;
307}
308
309static const struct game_params signpost_presets[] = {
310 { 4, 4, 1 },
311 { 4, 4, 0 },
312 { 5, 5, 1 },
313 { 5, 5, 0 },
314 { 6, 6, 1 },
315 { 7, 7, 1 }
316};
317
318static int game_fetch_preset(int i, char **name, game_params **params)
319{
320 game_params *ret;
321 char buf[80];
322
323 if (i < 0 || i >= lenof(signpost_presets))
324 return FALSE;
325
326 ret = default_params();
327 *ret = signpost_presets[i];
328 *params = ret;
329
330 sprintf(buf, "%dx%d%s", ret->w, ret->h,
331 ret->force_corner_start ? "" : ", free ends");
332 *name = dupstr(buf);
333
334 return TRUE;
335}
336
337static void free_params(game_params *params)
338{
339 sfree(params);
340}
341
342static game_params *dup_params(game_params *params)
343{
344 game_params *ret = snew(game_params);
345 *ret = *params; /* structure copy */
346 return ret;
347}
348
349static void decode_params(game_params *ret, char const *string)
350{
351 ret->w = ret->h = atoi(string);
352 while (*string && isdigit((unsigned char)*string)) string++;
353 if (*string == 'x') {
354 string++;
355 ret->h = atoi(string);
356 while (*string && isdigit((unsigned char)*string)) string++;
357 }
358 ret->force_corner_start = 0;
359 if (*string == 'c') {
360 string++;
361 ret->force_corner_start = 1;
362 }
363
364}
365
366static char *encode_params(game_params *params, int full)
367{
368 char data[256];
369
370 if (full)
371 sprintf(data, "%dx%d%s", params->w, params->h,
372 params->force_corner_start ? "c" : "");
373 else
374 sprintf(data, "%dx%d", params->w, params->h);
375
376 return dupstr(data);
377}
378
379static config_item *game_configure(game_params *params)
380{
381 config_item *ret;
382 char buf[80];
383
384 ret = snewn(4, config_item);
385
386 ret[0].name = "Width";
387 ret[0].type = C_STRING;
388 sprintf(buf, "%d", params->w);
389 ret[0].sval = dupstr(buf);
390 ret[0].ival = 0;
391
392 ret[1].name = "Height";
393 ret[1].type = C_STRING;
394 sprintf(buf, "%d", params->h);
395 ret[1].sval = dupstr(buf);
396 ret[1].ival = 0;
397
398 ret[2].name = "Start and end in corners";
399 ret[2].type = C_BOOLEAN;
400 ret[2].sval = NULL;
401 ret[2].ival = params->force_corner_start;
402
403 ret[3].name = NULL;
404 ret[3].type = C_END;
405 ret[3].sval = NULL;
406 ret[3].ival = 0;
407
408 return ret;
409}
410
411static game_params *custom_params(config_item *cfg)
412{
413 game_params *ret = snew(game_params);
414
415 ret->w = atoi(cfg[0].sval);
416 ret->h = atoi(cfg[1].sval);
417 ret->force_corner_start = cfg[2].ival;
418
419 return ret;
420}
421
422static char *validate_params(game_params *params, int full)
423{
424 if (params->w < 2 || params->h < 2)
425 return "Width and height must both be at least two";
426
427 return NULL;
428}
429
430/* --- Game description string generation and unpicking --- */
431
432static void blank_game_into(game_state *state)
433{
434 memset(state->dirs, 0, state->n*sizeof(int));
435 memset(state->nums, 0, state->n*sizeof(int));
436 memset(state->flags, 0, state->n*sizeof(unsigned int));
437 memset(state->next, -1, state->n*sizeof(int));
438 memset(state->prev, -1, state->n*sizeof(int));
439 memset(state->numsi, -1, (state->n+1)*sizeof(int));
440}
441
442static game_state *blank_game(int w, int h)
443{
444 game_state *state = snew(game_state);
445
446 memset(state, 0, sizeof(game_state));
447 state->w = w;
448 state->h = h;
449 state->n = w*h;
450
451 state->dirs = snewn(state->n, int);
452 state->nums = snewn(state->n, int);
453 state->flags = snewn(state->n, unsigned int);
454 state->next = snewn(state->n, int);
455 state->prev = snewn(state->n, int);
456 state->dsf = snew_dsf(state->n);
457 state->numsi = snewn(state->n+1, int);
458
459 blank_game_into(state);
460
461 return state;
462}
463
464static void dup_game_to(game_state *to, game_state *from)
465{
466 to->completed = from->completed;
467 to->used_solve = from->used_solve;
468 to->impossible = from->impossible;
469
470 memcpy(to->dirs, from->dirs, to->n*sizeof(int));
471 memcpy(to->flags, from->flags, to->n*sizeof(unsigned int));
472 memcpy(to->nums, from->nums, to->n*sizeof(int));
473
474 memcpy(to->next, from->next, to->n*sizeof(int));
475 memcpy(to->prev, from->prev, to->n*sizeof(int));
476
477 memcpy(to->dsf, from->dsf, to->n*sizeof(int));
478 memcpy(to->numsi, from->numsi, (to->n+1)*sizeof(int));
479}
480
481static game_state *dup_game(game_state *state)
482{
483 game_state *ret = blank_game(state->w, state->h);
484 dup_game_to(ret, state);
485 return ret;
486}
487
488static void free_game(game_state *state)
489{
490 sfree(state->dirs);
491 sfree(state->nums);
492 sfree(state->flags);
493 sfree(state->next);
494 sfree(state->prev);
495 sfree(state->dsf);
496 sfree(state->numsi);
497 sfree(state);
498}
499
500static void unpick_desc(game_params *params, char *desc,
501 game_state **sout, char **mout)
502{
503 game_state *state = blank_game(params->w, params->h);
504 char *msg = NULL, c;
505 int num = 0, i = 0;
506
507 while (*desc) {
508 if (i >= state->n) {
509 msg = "Game description longer than expected";
510 goto done;
511 }
512
513 c = *desc;
514 if (isdigit(c)) {
515 num = (num*10) + (int)(c-'0');
516 if (num > state->n) {
517 msg = "Number too large";
518 goto done;
519 }
520 } else if ((c-'a') >= 0 && (c-'a') < DIR_MAX) {
521 state->nums[i] = num;
522 state->flags[i] = num ? FLAG_IMMUTABLE : 0;
523 num = 0;
524
525 state->dirs[i] = c - 'a';
526 i++;
527 } else if (!*desc) {
528 msg = "Game description shorter than expected";
529 goto done;
530 } else {
531 msg = "Game description contains unexpected characters";
532 goto done;
533 }
534 desc++;
535 }
536 if (i < state->n) {
537 msg = "Game description shorter than expected";
538 goto done;
539 }
540
541done:
542 if (msg) { /* sth went wrong. */
543 if (mout) *mout = msg;
544 free_game(state);
545 } else {
546 if (mout) *mout = NULL;
547 if (sout) *sout = state;
548 else free_game(state);
549 }
550}
551
552static char *generate_desc(game_state *state, int issolve)
553{
554 char *ret, buf[80];
555 int retlen, i, k;
556
557 ret = NULL; retlen = 0;
558 if (issolve) {
559 ret = sresize(ret, 2, char);
560 ret[0] = 'S'; ret[1] = '\0';
561 retlen += 1;
562 }
563 for (i = 0; i < state->n; i++) {
564 if (state->nums[i])
565 k = sprintf(buf, "%d%c", state->nums[i], (int)(state->dirs[i]+'a'));
566 else
567 k = sprintf(buf, "%c", (int)(state->dirs[i]+'a'));
568 ret = sresize(ret, retlen + k + 1, char);
569 strcpy(ret + retlen, buf);
570 retlen += k;
571 }
572 return ret;
573}
574
575/* --- Game generation --- */
576
577/* Fills in preallocated arrays ai (indices) and ad (directions)
578 * showing all non-numbered cells adjacent to index i, returns length */
579/* This function has been somewhat optimised... */
580static int cell_adj(game_state *state, int i, int *ai, int *ad)
581{
582 int n = 0, a, x, y, sx, sy, dx, dy, newi;
583 int w = state->w, h = state->h;
584
585 sx = i % w; sy = i / w;
586
587 for (a = 0; a < DIR_MAX; a++) {
588 x = sx; y = sy;
589 dx = dxs[a]; dy = dys[a];
590 while (1) {
591 x += dx; y += dy;
592 if (x < 0 || y < 0 || x >= w || y >= h) break;
593
594 newi = y*w + x;
595 if (state->nums[newi] == 0) {
596 ai[n] = newi;
597 ad[n] = a;
598 n++;
599 }
600 }
601 }
602 return n;
603}
604
605static int new_game_fill(game_state *state, random_state *rs,
606 int headi, int taili)
607{
608 int nfilled, an, ret = 0, j;
609 int *aidx, *adir;
610
611 aidx = snewn(state->n, int);
612 adir = snewn(state->n, int);
613
614 debug(("new_game_fill: headi=%d, taili=%d.", headi, taili));
615
616 memset(state->nums, 0, state->n*sizeof(int));
617
618 state->nums[headi] = 1;
619 state->nums[taili] = state->n;
620
621 state->dirs[taili] = 0;
622 nfilled = 2;
623
624 while (nfilled < state->n) {
625 /* Try and expand _from_ headi; keep going if there's only one
626 * place to go to. */
627 an = cell_adj(state, headi, aidx, adir);
628 do {
629 if (an == 0) goto done;
630 j = random_upto(rs, an);
631 state->dirs[headi] = adir[j];
632 state->nums[aidx[j]] = state->nums[headi] + 1;
633 nfilled++;
634 headi = aidx[j];
635 an = cell_adj(state, headi, aidx, adir);
636 } while (an == 1);
637
638 /* Try and expand _to_ taili; keep going if there's only one
639 * place to go to. */
640 an = cell_adj(state, taili, aidx, adir);
641 do {
642 if (an == 0) goto done;
643 j = random_upto(rs, an);
644 state->dirs[aidx[j]] = DIR_OPPOSITE(adir[j]);
645 state->nums[aidx[j]] = state->nums[taili] - 1;
646 nfilled++;
647 taili = aidx[j];
648 an = cell_adj(state, taili, aidx, adir);
649 } while (an == 1);
650 }
651 /* If we get here we have headi and taili set but unconnected
652 * by direction: we need to set headi's direction so as to point
653 * at taili. */
654 state->dirs[headi] = whichdiri(state, headi, taili);
655
656 /* it could happen that our last two weren't in line; if that's the
657 * case, we have to start again. */
658 if (state->dirs[headi] != -1) ret = 1;
659
660done:
661 sfree(aidx);
662 sfree(adir);
663 return ret;
664}
665
666/* Better generator: with the 'generate, sprinkle numbers, solve,
667 * repeat' algorithm we're _never_ generating anything greater than
668 * 6x6, and spending all of our time in new_game_fill (and very little
669 * in solve_state).
670 *
671 * So, new generator steps:
672 * generate the grid, at random (same as now). Numbers 1 and N get
673 immutable flag immediately.
674 * squirrel that away for the solved state.
675 *
676 * (solve:) Try and solve it.
677 * If we solved it, we're done:
678 * generate the description from current immutable numbers,
679 * free stuff that needs freeing,
680 * return description + solved state.
681 * If we didn't solve it:
682 * count #tiles in state we've made deductions about.
683 * while (1):
684 * randomise a scratch array.
685 * for each index in scratch (in turn):
686 * if the cell isn't empty, continue (through scratch array)
687 * set number + immutable in state.
688 * try and solve state.
689 * if we've solved it, we're done.
690 * otherwise, count #tiles. If it's more than we had before:
691 * good, break from this loop and re-randomise.
692 * otherwise (number didn't help):
693 * remove number and try next in scratch array.
694 * if we've got to the end of the scratch array, no luck:
695 free everything we need to, and go back to regenerate the grid.
696 */
697
698static int solve_state(game_state *state);
699
700static void debug_desc(const char *what, game_state *state)
701{
702#if DEBUGGING
703 {
704 char *desc = generate_desc(state, 0);
705 debug(("%s game state: %dx%d:%s", what, state->w, state->h, desc));
706 sfree(desc);
707 }
708#endif
709}
710
711/* Expects a fully-numbered game_state on input, and makes sure
712 * FLAG_IMMUTABLE is only set on those numbers we need to solve
713 * (as for a real new-game); returns 1 if it managed
714 * this (such that it could solve it), or 0 if not. */
715static int new_game_strip(game_state *state, random_state *rs)
716{
717 int *scratch, i, j, ret = 1;
718 game_state *copy = dup_game(state);
719
720 debug(("new_game_strip."));
721
722 strip_nums(copy);
723 debug_desc("Stripped", copy);
724
725 if (solve_state(copy) > 0) {
726 debug(("new_game_strip: soluble immediately after strip."));
727 free_game(copy);
728 return 1;
729 }
730
731 scratch = snewn(state->n, int);
732 for (i = 0; i < state->n; i++) scratch[i] = i;
733 shuffle(scratch, state->n, sizeof(int), rs);
734
735 /* This is scungy. It might just be quick enough.
736 * It goes through, adding set numbers in empty squares
737 * until either we run out of empty squares (in the one
738 * we're half-solving) or else we solve it properly.
739 * NB that we run the entire solver each time, which
740 * strips the grid beforehand; we will save time if we
741 * avoid that. */
742 for (i = 0; i < state->n; i++) {
743 j = scratch[i];
744 if (copy->nums[j] > 0 && copy->nums[j] <= state->n)
745 continue; /* already solved to a real number here. */
746 assert(state->nums[j] <= state->n);
747 debug(("new_game_strip: testing add IMMUTABLE number %d at square (%d,%d).",
748 state->nums[j], j%state->w, j/state->w));
749 copy->nums[j] = state->nums[j];
750 copy->flags[j] |= FLAG_IMMUTABLE;
751 state->flags[j] |= FLAG_IMMUTABLE;
752 debug_state("Copy of state: ", copy);
753 if (solve_state(copy) > 0) goto solved;
754 assert(check_nums(state, copy, 1));
755 }
756 ret = 0;
757 goto done;
758
759solved:
760 debug(("new_game_strip: now solved."));
761 /* Since we added basically at random, try now to remove numbers
762 * and see if we can still solve it; if we can (still), really
763 * remove the number. Make sure we don't remove the anchor numbers
764 * 1 and N. */
765 for (i = 0; i < state->n; i++) {
766 j = scratch[i];
767 if ((state->flags[j] & FLAG_IMMUTABLE) &&
768 (state->nums[j] != 1 && state->nums[j] != state->n)) {
769 debug(("new_game_strip: testing remove IMMUTABLE number %d at square (%d,%d).",
770 state->nums[j], j%state->w, j/state->w));
771 state->flags[j] &= ~FLAG_IMMUTABLE;
772 dup_game_to(copy, state);
773 strip_nums(copy);
774 if (solve_state(copy) > 0) {
775 assert(check_nums(state, copy, 0));
776 debug(("new_game_strip: OK, removing number"));
777 } else {
778 assert(state->nums[j] <= state->n);
779 debug(("new_game_strip: cannot solve, putting IMMUTABLE back."));
780 copy->nums[j] = state->nums[j];
781 state->flags[j] |= FLAG_IMMUTABLE;
782 }
783 }
784 }
785
786done:
787 debug(("new_game_strip: %ssuccessful.", ret ? "" : "not "));
788 sfree(scratch);
789 free_game(copy);
790 return ret;
791}
792
793static char *new_game_desc(game_params *params, random_state *rs,
794 char **aux, int interactive)
795{
796 game_state *state = blank_game(params->w, params->h);
797 char *ret;
798 int headi, taili;
799
800generate:
801 blank_game_into(state);
802
803 /* keep trying until we fill successfully. */
804 do {
805 if (params->force_corner_start) {
806 headi = 0;
807 taili = state->n-1;
808 } else {
809 do {
810 headi = random_upto(rs, state->n);
811 taili = random_upto(rs, state->n);
812 } while (headi == taili);
813 }
814 } while (!new_game_fill(state, rs, headi, taili));
815
816 debug_state("Filled game:", state);
817
818 assert(state->nums[headi] <= state->n);
819 assert(state->nums[taili] <= state->n);
820
821 state->flags[headi] |= FLAG_IMMUTABLE;
822 state->flags[taili] |= FLAG_IMMUTABLE;
823
824 /* This will have filled in directions and _all_ numbers.
825 * Store the game definition for this, as the solved-state. */
826 if (!new_game_strip(state, rs)) {
827 goto generate;
828 }
829 strip_nums(state);
830 {
831 game_state *tosolve = dup_game(state);
832 assert(solve_state(tosolve) > 0);
833 free_game(tosolve);
834 }
835 ret = generate_desc(state, 0);
836 free_game(state);
837 return ret;
838}
839
840static char *validate_desc(game_params *params, char *desc)
841{
842 char *ret = NULL;
843
844 unpick_desc(params, desc, NULL, &ret);
845 return ret;
846}
847
848/* --- Linked-list and numbers array --- */
849
850/* Assuming numbers are always up-to-date, there are only four possibilities
51990f54 851 * for regions changing after a single valid move:
4cbcbfca 852 *
853 * 1) two differently-coloured regions being combined (the resulting colouring
854 * should be based on the larger of the two regions)
855 * 2) a numbered region having a single number added to the start (the
856 * region's colour will remain, and the numbers will shift by 1)
857 * 3) a numbered region having a single number added to the end (the
858 * region's colour and numbering remains as-is)
859 * 4) two unnumbered squares being joined (will pick the smallest unused set
860 * of colours to use for the new region).
861 *
862 * There should never be any complications with regions containing 3 colours
863 * being combined, since two of those colours should have been merged on a
864 * previous move.
4cbcbfca 865 *
51990f54 866 * Most of the complications are in ensuring we don't accidentally set two
867 * regions with the same colour (e.g. if a region was split). If this happens
868 * we always try and give the largest original portion the original colour.
4cbcbfca 869 */
870
871#define COLOUR(a) ((a) / (state->n+1))
872#define START(c) ((c) * (state->n+1))
873
51990f54 874struct head_meta {
875 int i; /* position */
876 int sz; /* size of region */
877 int start; /* region start number preferred, or 0 if !preference */
878 int preference; /* 0 if we have no preference (and should just pick one) */
879 const char *why;
880};
4cbcbfca 881
51990f54 882static void head_number(game_state *state, int i, struct head_meta *head)
4cbcbfca 883{
51990f54 884 int off = 0, ss, j = i, c, n, sz;
4cbcbfca 885
51990f54 886 /* Insist we really were passed the head of a chain. */
4cbcbfca 887 assert(state->prev[i] == -1 && state->next[i] != -1);
888
51990f54 889 head->i = i;
890 head->sz = dsf_size(state->dsf, i);
891 head->why = NULL;
892
4cbcbfca 893 /* Search through this chain looking for real numbers, checking that
894 * they match up (if there are more than one). */
51990f54 895 head->preference = 0;
4cbcbfca 896 while (j != -1) {
897 if (state->flags[j] & FLAG_IMMUTABLE) {
898 ss = state->nums[j] - off;
51990f54 899 if (!head->preference) {
900 head->start = ss;
901 head->preference = 1;
902 head->why = "contains cell with immutable number";
903 } else if (head->start != ss) {
33c2bb47 904 debug(("head_number: chain with non-sequential numbers!"));
4cbcbfca 905 state->impossible = 1;
906 }
907 }
908 off++;
909 j = state->next[j];
910 assert(j != i); /* we have created a loop, obviously wrong */
911 }
51990f54 912 if (head->preference) goto done;
913
914 if (state->nums[i] == 0 && state->nums[state->next[i]] > state->n) {
915 /* (probably) empty cell onto the head of a coloured region:
916 * make sure we start at a 0 offset. */
917 head->start = START(COLOUR(state->nums[state->next[i]]));
918 head->preference = 1;
919 head->why = "adding blank cell to head of numbered region";
920 } else if (state->nums[i] <= state->n) {
921 /* if we're 0 we're probably just blank -- but even if we're a
922 * (real) numbered region, we don't have an immutable number
923 * in it (any more) otherwise it'd have been caught above, so
924 * reassign the colour. */
925 head->start = 0;
926 head->preference = 0;
927 head->why = "lowest available colour group";
4cbcbfca 928 } else {
929 c = COLOUR(state->nums[i]);
930 n = 1;
931 sz = dsf_size(state->dsf, i);
932 j = i;
933 while (state->next[j] != -1) {
934 j = state->next[j];
51990f54 935 if (state->nums[j] == 0 && state->next[j] == -1) {
936 head->start = START(c);
937 head->preference = 1;
938 head->why = "adding blank cell to end of numbered region";
939 goto done;
4cbcbfca 940 }
941 if (COLOUR(state->nums[j]) == c)
942 n++;
943 else {
944 int start_alternate = START(COLOUR(state->nums[j]));
51990f54 945 if (n < (sz - n)) {
946 head->start = start_alternate;
947 head->preference = 1;
948 head->why = "joining two coloured regions, swapping to larger colour";
4cbcbfca 949 } else {
51990f54 950 head->start = START(c);
951 head->preference = 1;
952 head->why = "joining two coloured regions, taking largest";
4cbcbfca 953 }
51990f54 954 goto done;
4cbcbfca 955 }
956 }
957 /* If we got here then we may have split a region into
958 * two; make sure we don't assign a colour we've already used. */
51990f54 959 if (c == 0) {
960 /* not convinced this shouldn't be an assertion failure here. */
961 head->start = 0;
962 head->preference = 0;
963 } else {
964 head->start = START(c);
965 head->preference = 1;
4cbcbfca 966 }
51990f54 967 head->why = "got to end of coloured region";
4cbcbfca 968 }
969
33c2bb47 970done:
51990f54 971 assert(head->why != NULL);
972 if (head->preference)
973 debug(("Chain at (%d,%d) numbered for preference at %d (colour %d): %s.",
974 head->i%state->w, head->i/state->w,
975 head->start, COLOUR(head->start), head->why));
976 else
977 debug(("Chain at (%d,%d) using next available colour: %s.",
978 head->i%state->w, head->i/state->w,
979 head->why));
4cbcbfca 980}
981
982#if 0
983static void debug_numbers(game_state *state)
984{
985 int i, w=state->w;
986
987 for (i = 0; i < state->n; i++) {
988 debug(("(%d,%d) --> (%d,%d) --> (%d,%d)",
989 state->prev[i]==-1 ? -1 : state->prev[i]%w,
990 state->prev[i]==-1 ? -1 : state->prev[i]/w,
991 i%w, i/w,
992 state->next[i]==-1 ? -1 : state->next[i]%w,
993 state->next[i]==-1 ? -1 : state->next[i]/w));
994 }
995 w = w+1;
996}
997#endif
998
999static void connect_numbers(game_state *state)
1000{
1001 int i, di, dni;
1002
1003 dsf_init(state->dsf, state->n);
1004 for (i = 0; i < state->n; i++) {
1005 if (state->next[i] != -1) {
1006 assert(state->prev[state->next[i]] == i);
1007 di = dsf_canonify(state->dsf, i);
1008 dni = dsf_canonify(state->dsf, state->next[i]);
1009 if (di == dni) {
1010 debug(("connect_numbers: chain forms a loop."));
1011 state->impossible = 1;
1012 }
1013 dsf_merge(state->dsf, di, dni);
1014 }
1015 }
1016}
1017
51990f54 1018static int compare_heads(const void *a, const void *b)
1019{
1020 struct head_meta *ha = (struct head_meta *)a;
1021 struct head_meta *hb = (struct head_meta *)b;
1022
1023 /* Heads with preferred colours first... */
1024 if (ha->preference && !hb->preference) return -1;
1025 if (hb->preference && !ha->preference) return 1;
1026
1027 /* ...then heads with low colours first... */
1028 if (ha->start < hb->start) return -1;
1029 if (ha->start > hb->start) return 1;
1030
1031 /* ... then large regions first... */
1032 if (ha->sz > hb->sz) return -1;
1033 if (ha->sz < hb->sz) return 1;
1034
1035 /* ... then position. */
1036 if (ha->i > hb->i) return -1;
1037 if (ha->i < hb->i) return 1;
1038
1039 return 0;
1040}
1041
1042static int lowest_start(game_state *state, struct head_meta *heads, int nheads)
1043{
1044 int n, c;
1045
1046 /* NB start at 1: colour 0 is real numbers */
1047 for (c = 1; c < state->n; c++) {
1048 for (n = 0; n < nheads; n++) {
1049 if (COLOUR(heads[n].start) == c)
1050 goto used;
1051 }
1052 return c;
1053used:
1054 ;
1055 }
1056 assert(!"No available colours!");
1057 return 0;
1058}
1059
4cbcbfca 1060static void update_numbers(game_state *state)
1061{
51990f54 1062 int i, j, n, nnum, nheads;
1063 struct head_meta *heads = snewn(state->n, struct head_meta);
4cbcbfca 1064
51990f54 1065 for (n = 0; n < state->n; n++)
1066 state->numsi[n] = -1;
4cbcbfca 1067
1068 for (i = 0; i < state->n; i++) {
1069 if (state->flags[i] & FLAG_IMMUTABLE) {
51990f54 1070 assert(state->nums[i] > 0);
4cbcbfca 1071 assert(state->nums[i] <= state->n);
1072 state->numsi[state->nums[i]] = i;
1073 }
1074 else if (state->prev[i] == -1 && state->next[i] == -1)
1075 state->nums[i] = 0;
1076 }
1077 connect_numbers(state);
1078
51990f54 1079 /* Construct an array of the heads of all current regions, together
1080 * with their preferred colours. */
1081 nheads = 0;
4cbcbfca 1082 for (i = 0; i < state->n; i++) {
1083 /* Look for a cell that is the start of a chain
1084 * (has a next but no prev). */
1085 if (state->prev[i] != -1 || state->next[i] == -1) continue;
1086
51990f54 1087 head_number(state, i, &heads[nheads++]);
1088 }
1089
1090 /* Sort that array:
1091 * - heads with preferred colours first, then
1092 * - heads with low colours first, then
1093 * - large regions first
1094 */
1095 qsort(heads, nheads, sizeof(struct head_meta), compare_heads);
1096
1097 /* Remove duplicate-coloured regions. */
1098 for (n = nheads-1; n >= 0; n--) { /* order is important! */
1099 if ((n != 0) && (heads[n].start == heads[n-1].start)) {
1100 /* We have a duplicate-coloured region: since we're
1101 * sorted in size order and this is not the first
1102 * of its colour it's not the largest: recolour it. */
1103 heads[n].start = START(lowest_start(state, heads, nheads));
1104 heads[n].preference = -1; /* '-1' means 'was duplicate' */
1105 }
1106 else if (!heads[n].preference) {
1107 assert(heads[n].start == 0);
1108 heads[n].start = START(lowest_start(state, heads, nheads));
1109 }
1110 }
1111
1112 debug(("Region colouring after duplicate removal:"));
1113
1114 for (n = 0; n < nheads; n++) {
1115 debug((" Chain at (%d,%d) sz %d numbered at %d (colour %d): %s%s",
1116 heads[n].i % state->w, heads[n].i / state->w, heads[n].sz,
1117 heads[n].start, COLOUR(heads[n].start), heads[n].why,
1118 heads[n].preference == 0 ? " (next available)" :
1119 heads[n].preference < 0 ? " (duplicate, next available)" : ""));
1120
1121 nnum = heads[n].start;
1122 j = heads[n].i;
4cbcbfca 1123 while (j != -1) {
51990f54 1124 if (!(state->flags[j] & FLAG_IMMUTABLE)) {
1125 if (nnum > 0 && nnum <= state->n)
1126 state->numsi[nnum] = j;
1127 state->nums[j] = nnum;
1128 }
1129 nnum++;
4cbcbfca 1130 j = state->next[j];
51990f54 1131 assert(j != heads[n].i); /* loop?! */
4cbcbfca 1132 }
1133 }
1134 /*debug_numbers(state);*/
51990f54 1135 sfree(heads);
4cbcbfca 1136}
1137
1138static int check_completion(game_state *state, int mark_errors)
1139{
1140 int n, j, k, error = 0, complete;
1141
1142 /* NB This only marks errors that are possible to perpetrate with
1143 * the current UI in interpret_move. Things like forming loops in
1144 * linked sections and having numbers not add up should be forbidden
1145 * by the code elsewhere, so we don't bother marking those (because
1146 * it would add lots of tricky drawing code for very little gain). */
1147 if (mark_errors) {
1148 for (j = 0; j < state->n; j++)
1149 state->flags[j] &= ~FLAG_ERROR;
1150 }
1151
1152 /* Search for repeated numbers. */
1153 for (j = 0; j < state->n; j++) {
1154 if (state->nums[j] > 0 && state->nums[j] <= state->n) {
1155 for (k = j+1; k < state->n; k++) {
1156 if (state->nums[k] == state->nums[j]) {
1157 if (mark_errors) {
1158 state->flags[j] |= FLAG_ERROR;
1159 state->flags[k] |= FLAG_ERROR;
1160 }
1161 error = 1;
1162 }
1163 }
1164 }
1165 }
1166
1167 /* Search and mark numbers n not pointing to n+1; if any numbers
1168 * are missing we know we've not completed. */
1169 complete = 1;
1170 for (n = 1; n < state->n; n++) {
1171 if (state->numsi[n] == -1 || state->numsi[n+1] == -1)
1172 complete = 0;
1173 else if (!ispointingi(state, state->numsi[n], state->numsi[n+1])) {
1174 if (mark_errors) {
1175 state->flags[state->numsi[n]] |= FLAG_ERROR;
1176 state->flags[state->numsi[n+1]] |= FLAG_ERROR;
1177 }
1178 error = 1;
1179 } else {
1180 /* make sure the link is explicitly made here; for instance, this
1181 * is nice if the user drags from 2 out (making 3) and a 4 is also
1182 * visible; this ensures that the link from 3 to 4 is also made. */
1183 if (mark_errors)
1184 makelink(state, state->numsi[n], state->numsi[n+1]);
1185 }
1186 }
1187
33c2bb47 1188 /* Search and mark numbers less than 0, or 0 with links. */
1189 for (n = 1; n < state->n; n++) {
1190 if ((state->nums[n] < 0) ||
1191 (state->nums[n] == 0 &&
1192 (state->next[n] != -1 || state->prev[n] != -1))) {
1193 error = 1;
1194 if (mark_errors)
1195 state->flags[n] |= FLAG_ERROR;
1196 }
1197 }
1198
4cbcbfca 1199 if (error) return 0;
1200 return complete;
1201}
1202static game_state *new_game(midend *me, game_params *params, char *desc)
1203{
1204 game_state *state = NULL;
1205
1206 unpick_desc(params, desc, &state, NULL);
1207 if (!state) assert(!"new_game failed to unpick");
1208
1209 update_numbers(state);
1210 check_completion(state, 1); /* update any auto-links */
1211
1212 return state;
1213}
1214
1215/* --- Solver --- */
1216
1217/* If a tile has a single tile it can link _to_, or there's only a single
1218 * location that can link to a given tile, fill that link in. */
1219static int solve_single(game_state *state, game_state *copy, int *from)
1220{
1221 int i, j, sx, sy, x, y, d, poss, w=state->w, nlinks = 0;
1222
1223 /* The from array is a list of 'which square can link _to_ us';
1224 * we start off with from as '-1' (meaning 'not found'); if we find
1225 * something that can link to us it is set to that index, and then if
1226 * we find another we set it to -2. */
1227
1228 memset(from, -1, state->n*sizeof(int));
1229
1230 /* poss is 'can I link to anything' with the same meanings. */
1231
1232 for (i = 0; i < state->n; i++) {
1233 if (state->next[i] != -1) continue;
1234 if (state->nums[i] == state->n) continue; /* no next from last no. */
1235
1236 d = state->dirs[i];
1237 poss = -1;
1238 sx = x = i%w; sy = y = i/w;
1239 while (1) {
1240 x += dxs[d]; y += dys[d];
1241 if (!INGRID(state, x, y)) break;
1242 if (!isvalidmove(state, 1, sx, sy, x, y)) continue;
1243
1244 /* can't link to somewhere with a back-link we would have to
1245 * break (the solver just doesn't work like this). */
1246 j = y*w+x;
1247 if (state->prev[j] != -1) continue;
1248
1249 if (state->nums[i] > 0 && state->nums[j] > 0 &&
1250 state->nums[i] <= state->n && state->nums[j] <= state->n &&
1251 state->nums[j] == state->nums[i]+1) {
1252 debug(("Solver: forcing link through existing consecutive numbers."));
1253 poss = j;
1254 from[j] = i;
1255 break;
1256 }
1257
1258 /* if there's been a valid move already, we have to move on;
1259 * we can't make any deductions here. */
1260 poss = (poss == -1) ? j : -2;
1261
1262 /* Modify the from array as described above (which is enumerating
1263 * what points to 'j' in a similar way). */
1264 from[j] = (from[j] == -1) ? i : -2;
1265 }
1266 if (poss == -2) {
1267 /*debug(("Solver: (%d,%d) has multiple possible next squares.", sx, sy));*/
1268 ;
1269 } else if (poss == -1) {
1270 debug(("Solver: nowhere possible for (%d,%d) to link to.", sx, sy));
1271 copy->impossible = 1;
1272 return -1;
1273 } else {
1274 debug(("Solver: linking (%d,%d) to only possible next (%d,%d).",
1275 sx, sy, poss%w, poss/w));
1276 makelink(copy, i, poss);
1277 nlinks++;
1278 }
1279 }
1280
1281 for (i = 0; i < state->n; i++) {
1282 if (state->prev[i] != -1) continue;
1283 if (state->nums[i] == 1) continue; /* no prev from 1st no. */
1284
1285 x = i%w; y = i/w;
1286 if (from[i] == -1) {
1287 debug(("Solver: nowhere possible to link to (%d,%d)", x, y));
1288 copy->impossible = 1;
1289 return -1;
1290 } else if (from[i] == -2) {
1291 /*debug(("Solver: (%d,%d) has multiple possible prev squares.", x, y));*/
1292 ;
1293 } else {
1294 debug(("Solver: linking only possible prev (%d,%d) to (%d,%d).",
1295 from[i]%w, from[i]/w, x, y));
1296 makelink(copy, from[i], i);
1297 nlinks++;
1298 }
1299 }
1300
1301 return nlinks;
1302}
1303
1304/* Returns 1 if we managed to solve it, 0 otherwise. */
1305static int solve_state(game_state *state)
1306{
1307 game_state *copy = dup_game(state);
1308 int *scratch = snewn(state->n, int), ret;
1309
1310 debug_state("Before solver: ", state);
1311
1312 while (1) {
1313 update_numbers(state);
1314
1315 if (solve_single(state, copy, scratch)) {
1316 dup_game_to(state, copy);
1317 if (state->impossible) break; else continue;
1318 }
1319 break;
1320 }
1321 free_game(copy);
1322 sfree(scratch);
1323
1324 update_numbers(state);
1325 ret = state->impossible ? -1 : check_completion(state, 0);
1326 debug(("Solver finished: %s",
1327 ret < 0 ? "impossible" : ret > 0 ? "solved" : "not solved"));
1328 debug_state("After solver: ", state);
1329 return ret;
1330}
1331
1332static char *solve_game(game_state *state, game_state *currstate,
1333 char *aux, char **error)
1334{
1335 game_state *tosolve;
1336 char *ret = NULL;
1337 int result;
1338
1339 tosolve = dup_game(currstate);
1340 result = solve_state(tosolve);
1341 if (result > 0)
1342 ret = generate_desc(tosolve, 1);
1343 free_game(tosolve);
1344 if (ret) return ret;
1345
1346 tosolve = dup_game(state);
1347 result = solve_state(tosolve);
1348 if (result < 0)
1349 *error = "Puzzle is impossible.";
1350 else if (result == 0)
1351 *error = "Unable to solve puzzle.";
1352 else
1353 ret = generate_desc(tosolve, 1);
1354
1355 free_game(tosolve);
1356
1357 return ret;
1358}
1359
1360/* --- UI and move routines. --- */
1361
1362
1363struct game_ui {
1364 int cx, cy, cshow;
1365
1366 int dragging, drag_is_from;
1367 int sx, sy; /* grid coords of start cell */
1368 int dx, dy; /* pixel coords of drag posn */
1369};
1370
1371static game_ui *new_ui(game_state *state)
1372{
1373 game_ui *ui = snew(game_ui);
1374
1375 /* NB: if this is ever changed to as to require more than a structure
1376 * copy to clone, there's code that needs fixing in game_redraw too. */
1377
1378 ui->cx = ui->cy = ui->cshow = 0;
1379
1380 ui->dragging = 0;
1381 ui->sx = ui->sy = ui->dx = ui->dy = 0;
1382
1383 return ui;
1384}
1385
1386static void free_ui(game_ui *ui)
1387{
1388 sfree(ui);
1389}
1390
1391static char *encode_ui(game_ui *ui)
1392{
1393 return NULL;
1394}
1395
1396static void decode_ui(game_ui *ui, char *encoding)
1397{
1398}
1399
1400static void game_changed_state(game_ui *ui, game_state *oldstate,
1401 game_state *newstate)
1402{
1403 if (!oldstate->completed && newstate->completed)
1404 ui->cshow = ui->dragging = 0;
1405}
1406
1407struct game_drawstate {
1408 int tilesize, started, solved;
1409 int w, h, n;
1410 int *nums, *dirp;
1411 unsigned int *f;
1412 double angle_offset;
1413
1414 int dragging, dx, dy;
1415 blitter *dragb;
1416};
1417
1418static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
1419 int mx, int my, int button)
1420{
1421 int x = FROMCOORD(mx), y = FROMCOORD(my), w = state->w;
1422 char buf[80];
1423
1424 if (IS_CURSOR_MOVE(button)) {
1425 move_cursor(button, &ui->cx, &ui->cy, state->w, state->h, 0);
1426 ui->cshow = 1;
1427 if (ui->dragging) {
1428 ui->dx = COORD(ui->cx) + TILE_SIZE/2;
1429 ui->dy = COORD(ui->cy) + TILE_SIZE/2;
1430 }
1431 return "";
1432 } else if (IS_CURSOR_SELECT(button)) {
1433 if (!ui->cshow)
1434 ui->cshow = 1;
1435 else if (ui->dragging) {
1436 ui->dragging = FALSE;
1437 if (ui->sx == ui->cx && ui->sy == ui->cy) return "";
1438 if (ui->drag_is_from) {
1439 if (!isvalidmove(state, 0, ui->sx, ui->sy, ui->cx, ui->cy)) return "";
1440 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, ui->cx, ui->cy);
1441 } else {
1442 if (!isvalidmove(state, 0, ui->cx, ui->cy, ui->sx, ui->sy)) return "";
1443 sprintf(buf, "L%d,%d-%d,%d", ui->cx, ui->cy, ui->sx, ui->sy);
1444 }
1445 return dupstr(buf);
1446 } else {
1447 ui->dragging = TRUE;
1448 ui->sx = ui->cx;
1449 ui->sy = ui->cy;
1450 ui->dx = COORD(ui->cx) + TILE_SIZE/2;
1451 ui->dy = COORD(ui->cy) + TILE_SIZE/2;
1452 ui->drag_is_from = (button == CURSOR_SELECT) ? 1 : 0;
1453 }
1454 return "";
1455 }
1456 if (IS_MOUSE_DOWN(button)) {
1457 if (ui->cshow) {
1458 ui->cshow = ui->dragging = 0;
1459 }
1460 assert(!ui->dragging);
1461 if (!INGRID(state, x, y)) return NULL;
1462
1463 if (button == LEFT_BUTTON) {
1464 /* disallow dragging from the final number. */
51990f54 1465 if ((state->nums[y*w+x] == state->n) &&
1466 (state->flags[y*w+x] & FLAG_IMMUTABLE))
1467 return NULL;
4cbcbfca 1468 } else if (button == RIGHT_BUTTON) {
1469 /* disallow dragging to the first number. */
51990f54 1470 if ((state->nums[y*w+x] == 1) &&
1471 (state->flags[y*w+x] & FLAG_IMMUTABLE))
1472 return NULL;
4cbcbfca 1473 }
1474
1475 ui->dragging = TRUE;
1476 ui->drag_is_from = (button == LEFT_BUTTON) ? 1 : 0;
1477 ui->sx = x;
1478 ui->sy = y;
1479 ui->dx = mx;
1480 ui->dy = my;
1481 ui->cshow = 0;
1482 return "";
1483 } else if (IS_MOUSE_DRAG(button) && ui->dragging) {
1484 ui->dx = mx;
1485 ui->dy = my;
1486 return "";
1487 } else if (IS_MOUSE_RELEASE(button) && ui->dragging) {
1488 ui->dragging = FALSE;
1489 if (ui->sx == x && ui->sy == y) return ""; /* single click */
1490
1491 if (!INGRID(state, x, y)) {
1492 int si = ui->sy*w+ui->sx;
1493 if (state->prev[si] == -1 && state->next[si] == -1)
1494 return "";
1495 sprintf(buf, "%c%d,%d",
1496 ui->drag_is_from ? 'C' : 'X', ui->sx, ui->sy);
1497 return dupstr(buf);
1498 }
1499
1500 if (ui->drag_is_from) {
1501 if (!isvalidmove(state, 0, ui->sx, ui->sy, x, y)) return "";
1502 sprintf(buf, "L%d,%d-%d,%d", ui->sx, ui->sy, x, y);
1503 } else {
1504 if (!isvalidmove(state, 0, x, y, ui->sx, ui->sy)) return "";
1505 sprintf(buf, "L%d,%d-%d,%d", x, y, ui->sx, ui->sy);
1506 }
1507 return dupstr(buf);
1508 } /* else if (button == 'H' || button == 'h')
1509 return dupstr("H"); */
1510 else if ((button == 'x' || button == 'X') && ui->cshow) {
1511 int si = ui->cy*w + ui->cx;
1512 if (state->prev[si] == -1 && state->next[si] == -1)
1513 return "";
1514 sprintf(buf, "%c%d,%d",
1515 (button == 'x') ? 'C' : 'X', ui->cx, ui->cy);
1516 return dupstr(buf);
1517 }
1518
1519 return NULL;
1520}
1521
1522static void unlink_cell(game_state *state, int si)
1523{
1524 debug(("Unlinking (%d,%d).", si%state->w, si/state->w));
1525 if (state->prev[si] != -1) {
1526 debug((" ... removing prev link from (%d,%d).",
1527 state->prev[si]%state->w, state->prev[si]/state->w));
1528 state->next[state->prev[si]] = -1;
1529 state->prev[si] = -1;
1530 }
1531 if (state->next[si] != -1) {
1532 debug((" ... removing next link to (%d,%d).",
1533 state->next[si]%state->w, state->next[si]/state->w));
1534 state->prev[state->next[si]] = -1;
1535 state->next[si] = -1;
1536 }
1537}
1538
1539static game_state *execute_move(game_state *state, char *move)
1540{
1541 game_state *ret = NULL;
1542 int sx, sy, ex, ey, si, ei, w = state->w;
1543 char c;
1544
1545 debug(("move: %s", move));
1546
1547 if (move[0] == 'S') {
1548 game_params p;
1549 game_state *tmp;
1550 char *valid;
1551 int i;
1552
1553 p.w = state->w; p.h = state->h;
1554 valid = validate_desc(&p, move+1);
1555 if (valid) {
1556 debug(("execute_move: move not valid: %s", valid));
1557 return NULL;
1558 }
1559 ret = dup_game(state);
1560 tmp = new_game(NULL, &p, move+1);
1561 for (i = 0; i < state->n; i++) {
1562 ret->prev[i] = tmp->prev[i];
1563 ret->next[i] = tmp->next[i];
1564 }
1565 free_game(tmp);
1566 ret->used_solve = 1;
1567 } else if (sscanf(move, "L%d,%d-%d,%d", &sx, &sy, &ex, &ey) == 4) {
1568 if (!isvalidmove(state, 0, sx, sy, ex, ey)) return NULL;
1569
1570 ret = dup_game(state);
1571
1572 si = sy*w+sx; ei = ey*w+ex;
1573 makelink(ret, si, ei);
1574 } else if (sscanf(move, "%c%d,%d", &c, &sx, &sy) == 3) {
1575 if (c != 'C' && c != 'X') return NULL;
1576 if (!INGRID(state, sx, sy)) return NULL;
1577 si = sy*w+sx;
1578 if (state->prev[si] == -1 && state->next[si] == -1)
1579 return NULL;
1580
1581 ret = dup_game(state);
1582
1583 if (c == 'C') {
1584 /* Unlink the single cell we dragged from the board. */
1585 unlink_cell(ret, si);
1586 } else {
1587 int i, set, sset = state->nums[si] / (state->n+1);
1588 for (i = 0; i < state->n; i++) {
1589 /* Unlink all cells in the same set as the one we dragged
1590 * from the board. */
1591
1592 if (state->nums[i] == 0) continue;
1593 set = state->nums[i] / (state->n+1);
1594 if (set != sset) continue;
1595
1596 unlink_cell(ret, i);
1597 }
1598 }
1599 } else if (strcmp(move, "H") == 0) {
1600 ret = dup_game(state);
1601 solve_state(ret);
1602 }
1603 if (ret) {
1604 update_numbers(ret);
1605 if (check_completion(ret, 1)) ret->completed = 1;
1606 }
1607
1608 return ret;
1609}
1610
1611/* ----------------------------------------------------------------------
1612 * Drawing routines.
1613 */
1614
1615static void game_compute_size(game_params *params, int tilesize,
1616 int *x, int *y)
1617{
1618 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1619 struct { int tilesize, order; } ads, *ds = &ads;
1620 ads.tilesize = tilesize;
1621
1622 *x = TILE_SIZE * params->w + 2 * BORDER;
1623 *y = TILE_SIZE * params->h + 2 * BORDER;
1624}
1625
1626static void game_set_size(drawing *dr, game_drawstate *ds,
1627 game_params *params, int tilesize)
1628{
1629 ds->tilesize = tilesize;
1630 assert(TILE_SIZE > 0);
1631
1632 assert(!ds->dragb);
1633 ds->dragb = blitter_new(dr, BLITTER_SIZE, BLITTER_SIZE);
1634}
1635
1636/* Colours chosen from the webby palette to work as a background to black text,
1637 * W then some plausible approximation to pastelly ROYGBIV; we then interpolate
1638 * between consecutive pairs to give another 8 (and then the drawing routine
1639 * will reuse backgrounds). */
1640static const unsigned long bgcols[8] = {
1641 0xffffff, /* white */
1642 0xffa07a, /* lightsalmon */
1643 0x98fb98, /* green */
1644 0x7fffd4, /* aquamarine */
1645 0x9370db, /* medium purple */
1646 0xffa500, /* orange */
1647 0x87cefa, /* lightskyblue */
1648 0xffff00, /* yellow */
1649};
1650
1651static float *game_colours(frontend *fe, int *ncolours)
1652{
1653 float *ret = snewn(3 * NCOLOURS, float);
1654 int c, i;
1655
1656 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
1657
1658 for (i = 0; i < 3; i++) {
1659 ret[COL_NUMBER * 3 + i] = 0.0F;
1660 ret[COL_ARROW * 3 + i] = 0.0F;
1661 ret[COL_CURSOR * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 2.0F;
1662 ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] / 1.3F;
1663 }
1664 ret[COL_NUMBER_SET * 3 + 0] = 0.0F;
1665 ret[COL_NUMBER_SET * 3 + 1] = 0.0F;
1666 ret[COL_NUMBER_SET * 3 + 2] = 0.9F;
1667
1668 ret[COL_ERROR * 3 + 0] = 1.0F;
1669 ret[COL_ERROR * 3 + 1] = 0.0F;
1670 ret[COL_ERROR * 3 + 2] = 0.0F;
1671
1672 ret[COL_DRAG_ORIGIN * 3 + 0] = 0.2F;
1673 ret[COL_DRAG_ORIGIN * 3 + 1] = 1.0F;
1674 ret[COL_DRAG_ORIGIN * 3 + 2] = 0.2F;
1675
1676 for (c = 0; c < 8; c++) {
1677 ret[(COL_B0 + c) * 3 + 0] = (float)((bgcols[c] & 0xff0000) >> 16) / 256.0F;
1678 ret[(COL_B0 + c) * 3 + 1] = (float)((bgcols[c] & 0xff00) >> 8) / 256.0F;
1679 ret[(COL_B0 + c) * 3 + 2] = (float)((bgcols[c] & 0xff)) / 256.0F;
1680 }
1681 for (c = 0; c < 8; c++) {
1682 for (i = 0; i < 3; i++) {
1683 ret[(COL_B0 + 8 + c) * 3 + i] =
1684 (ret[(COL_B0 + c) * 3 + i] + ret[(COL_B0 + c + 1) * 3 + i]) / 2.0F;
1685 }
1686 }
1687
1688#define average(r,a,b,w) do { \
1689 for (i = 0; i < 3; i++) \
1690 ret[(r)*3+i] = ret[(a)*3+i] + w * (ret[(b)*3+i] - ret[(a)*3+i]); \
1691} while (0)
1692 average(COL_ARROW_BG_DIM, COL_BACKGROUND, COL_ARROW, 0.1F);
1693 average(COL_NUMBER_SET_MID, COL_B0, COL_NUMBER_SET, 0.3F);
1694 for (c = 0; c < NBACKGROUNDS; c++) {
1695 /* I assume here that COL_ARROW and COL_NUMBER are the same.
1696 * Otherwise I'd need two sets of COL_M*. */
1697 average(COL_M0 + c, COL_B0 + c, COL_NUMBER, 0.3F);
1698 average(COL_D0 + c, COL_B0 + c, COL_NUMBER, 0.1F);
1699 average(COL_X0 + c, COL_BACKGROUND, COL_B0 + c, 0.5F);
1700 }
1701
1702 *ncolours = NCOLOURS;
1703 return ret;
1704}
1705
1706static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
1707{
1708 struct game_drawstate *ds = snew(struct game_drawstate);
1709 int i;
1710
1711 ds->tilesize = ds->started = ds->solved = 0;
1712 ds->w = state->w;
1713 ds->h = state->h;
1714 ds->n = state->n;
1715
1716 ds->nums = snewn(state->n, int);
1717 ds->dirp = snewn(state->n, int);
1718 ds->f = snewn(state->n, unsigned int);
1719 for (i = 0; i < state->n; i++) {
1720 ds->nums[i] = 0;
1721 ds->dirp[i] = -1;
1722 ds->f[i] = 0;
1723 }
1724
1725 ds->angle_offset = 0.0F;
1726
1727 ds->dragging = ds->dx = ds->dy = 0;
1728 ds->dragb = NULL;
1729
1730 return ds;
1731}
1732
1733static void game_free_drawstate(drawing *dr, game_drawstate *ds)
1734{
1735 sfree(ds->nums);
1736 sfree(ds->dirp);
1737 sfree(ds->f);
1738 if (ds->dragb) blitter_free(dr, ds->dragb);
1739
1740 sfree(ds);
1741}
1742
1743/* cx, cy are top-left corner. sz is the 'radius' of the arrow.
1744 * ang is in radians, clockwise from 0 == straight up. */
1745static void draw_arrow(drawing *dr, int cx, int cy, int sz, double ang,
1746 int cfill, int cout)
1747{
1748 int coords[14];
1749 int xdx, ydx, xdy, ydy, xdx3, xdy3;
1750 double s = sin(ang), c = cos(ang);
1751
1752 xdx3 = (int)(sz * (c/3 + 1) + 0.5) - sz;
1753 xdy3 = (int)(sz * (s/3 + 1) + 0.5) - sz;
1754 xdx = (int)(sz * (c + 1) + 0.5) - sz;
1755 xdy = (int)(sz * (s + 1) + 0.5) - sz;
1756 ydx = -xdy;
1757 ydy = xdx;
1758
1759
1760 coords[2*0 + 0] = cx - ydx;
1761 coords[2*0 + 1] = cy - ydy;
1762 coords[2*1 + 0] = cx + xdx;
1763 coords[2*1 + 1] = cy + xdy;
1764 coords[2*2 + 0] = cx + xdx3;
1765 coords[2*2 + 1] = cy + xdy3;
1766 coords[2*3 + 0] = cx + xdx3 + ydx;
1767 coords[2*3 + 1] = cy + xdy3 + ydy;
1768 coords[2*4 + 0] = cx - xdx3 + ydx;
1769 coords[2*4 + 1] = cy - xdy3 + ydy;
1770 coords[2*5 + 0] = cx - xdx3;
1771 coords[2*5 + 1] = cy - xdy3;
1772 coords[2*6 + 0] = cx - xdx;
1773 coords[2*6 + 1] = cy - xdy;
1774
1775 draw_polygon(dr, coords, 7, cfill, cout);
1776}
1777
1778static void draw_arrow_dir(drawing *dr, int cx, int cy, int sz, int dir,
1779 int cfill, int cout, double angle_offset)
1780{
1781 double ang = 2.0 * PI * (double)dir / 8.0 + angle_offset;
1782 draw_arrow(dr, cx, cy, sz, ang, cfill, cout);
1783}
1784
1785/* cx, cy are centre coordinates.. */
1786static void draw_star(drawing *dr, int cx, int cy, int rad, int npoints,
1787 int cfill, int cout, double angle_offset)
1788{
1789 int *coords, n;
1790 double a, r;
1791
1792 assert(npoints > 0);
1793
1794 coords = snewn(npoints * 2 * 2, int);
1795
1796 for (n = 0; n < npoints * 2; n++) {
1797 a = 2.0 * PI * ((double)n / ((double)npoints * 2.0)) + angle_offset;
1798 r = (n % 2) ? (double)rad/2.0 : (double)rad;
1799
1800 /* We're rotating the point at (0, -r) by a degrees */
1801 coords[2*n+0] = cx + (int)( r * sin(a));
1802 coords[2*n+1] = cy + (int)(-r * cos(a));
1803 }
1804 draw_polygon(dr, coords, npoints*2, cfill, cout);
1805 sfree(coords);
1806}
1807
1808static int num2col(game_drawstate *ds, int num)
1809{
1810 int set = num / (ds->n+1);
1811
33c2bb47 1812 if (num <= 0) return COL_B0;
4cbcbfca 1813 return COL_B0 + (set % 16);
1814}
1815
1816#define ARROW_HALFSZ (7 * TILE_SIZE / 32)
1817
1818#define F_CUR 0x001 /* Cursor on this tile. */
1819#define F_DRAG_SRC 0x002 /* Tile is source of a drag. */
1820#define F_ERROR 0x004 /* Tile marked in error. */
1821#define F_IMMUTABLE 0x008 /* Tile (number) is immutable. */
1822#define F_ARROW_POINT 0x010 /* Tile points to other tile */
1823#define F_ARROW_INPOINT 0x020 /* Other tile points in here. */
1824#define F_DIM 0x040 /* Tile is dim */
1825
1826static void tile_redraw(drawing *dr, game_drawstate *ds, int tx, int ty,
1827 int dir, int dirp, int num, unsigned int f,
1828 double angle_offset, int print_ink)
1829{
1830 int cb = TILE_SIZE / 16, textsz;
1831 char buf[20];
1832 int arrowcol, sarrowcol, setcol, textcol;
33c2bb47 1833 int acx, acy, asz, empty = 0;
1834
1835 if (num == 0 && !(f & F_ARROW_POINT) && !(f & F_ARROW_INPOINT)) {
1836 empty = 1;
1837 /*
1838 * We don't display text in empty cells: typically these are
1839 * signified by num=0. However, in some cases a cell could
1840 * have had the number 0 assigned to it if the user made an
1841 * error (e.g. tried to connect a chain of length 5 to the
1842 * immutable number 4) so we _do_ display the 0 if the cell
1843 * has a link in or a link out.
1844 */
1845 }
4cbcbfca 1846
1847 /* Calculate colours. */
1848
1849 if (print_ink >= 0) {
1850 /*
1851 * We're printing, so just do everything in black.
1852 */
1853 arrowcol = textcol = print_ink;
1854 setcol = sarrowcol = -1; /* placate optimiser */
1855 } else {
1856
33c2bb47 1857 setcol = empty ? COL_BACKGROUND : num2col(ds, num);
4cbcbfca 1858
1859#define dim(fg,bg) ( \
1860 (bg)==COL_BACKGROUND ? COL_ARROW_BG_DIM : \
1861 (bg) + COL_D0 - COL_B0 \
1862 )
1863
1864#define mid(fg,bg) ( \
1865 (fg)==COL_NUMBER_SET ? COL_NUMBER_SET_MID : \
1866 (bg) + COL_M0 - COL_B0 \
1867 )
1868
1869#define dimbg(bg) ( \
1870 (bg)==COL_BACKGROUND ? COL_BACKGROUND : \
1871 (bg) + COL_X0 - COL_B0 \
1872 )
1873
1874 if (f & F_DRAG_SRC) arrowcol = COL_DRAG_ORIGIN;
1875 else if (f & F_DIM) arrowcol = dim(COL_ARROW, setcol);
1876 else if (f & F_ARROW_POINT) arrowcol = mid(COL_ARROW, setcol);
1877 else arrowcol = COL_ARROW;
1878
51990f54 1879 if ((f & F_ERROR) && !(f & F_IMMUTABLE)) textcol = COL_ERROR;
4cbcbfca 1880 else {
1881 if (f & F_IMMUTABLE) textcol = COL_NUMBER_SET;
1882 else textcol = COL_NUMBER;
1883
1884 if (f & F_DIM) textcol = dim(textcol, setcol);
1885 else if (((f & F_ARROW_POINT) || num==ds->n) &&
1886 ((f & F_ARROW_INPOINT) || num==1))
1887 textcol = mid(textcol, setcol);
1888 }
1889
1890 if (f & F_DIM) sarrowcol = dim(COL_ARROW, setcol);
1891 else sarrowcol = COL_ARROW;
1892 }
1893
1894 /* Clear tile background */
1895
1896 if (print_ink < 0) {
1897 draw_rect(dr, tx, ty, TILE_SIZE, TILE_SIZE,
1898 (f & F_DIM) ? dimbg(setcol) : setcol);
1899 }
1900
1901 /* Draw large (outwards-pointing) arrow. */
1902
1903 asz = ARROW_HALFSZ; /* 'radius' of arrow/star. */
1904 acx = tx+TILE_SIZE/2+asz; /* centre x */
1905 acy = ty+TILE_SIZE/2+asz; /* centre y */
1906
1907 if (num == ds->n && (f & F_IMMUTABLE))
1908 draw_star(dr, acx, acy, asz, 5, arrowcol, arrowcol, angle_offset);
1909 else
1910 draw_arrow_dir(dr, acx, acy, asz, dir, arrowcol, arrowcol, angle_offset);
1911 if (print_ink < 0 && (f & F_CUR))
1912 draw_rect_corners(dr, acx, acy, asz+1, COL_CURSOR);
1913
1914 /* Draw dot iff this tile requires a predecessor and doesn't have one. */
1915
1916 if (print_ink < 0) {
1917 acx = tx+TILE_SIZE/2-asz;
1918 acy = ty+TILE_SIZE/2+asz;
1919
1920 if (!(f & F_ARROW_INPOINT) && num != 1) {
1921 draw_circle(dr, acx, acy, asz / 4, sarrowcol, sarrowcol);
1922 }
1923 }
1924
1925 /* Draw text (number or set). */
1926
33c2bb47 1927 if (!empty) {
1928 int set = (num <= 0) ? 0 : num / (ds->n+1);
1929
1930 if (set == 0 || num <= 0) {
1931 sprintf(buf, "%d", num);
4cbcbfca 1932 } else {
33c2bb47 1933 int n = num % (ds->n+1);
1934
4cbcbfca 1935 if (n == 0)
1936 sprintf(buf, "%c", (int)(set+'a'-1));
1937 else
1938 sprintf(buf, "%c+%d", (int)(set+'a'-1), n);
1939 }
1940 textsz = min(2*asz, (TILE_SIZE - 2 * cb) / (int)strlen(buf));
1941 draw_text(dr, tx + cb, ty + TILE_SIZE/4, FONT_VARIABLE, textsz,
1942 ALIGN_VCENTRE | ALIGN_HLEFT, textcol, buf);
1943 }
1944
1945 if (print_ink < 0) {
1946 draw_rect_outline(dr, tx, ty, TILE_SIZE, TILE_SIZE, COL_GRID);
1947 draw_update(dr, tx, ty, TILE_SIZE, TILE_SIZE);
1948 }
1949}
1950
1951static void draw_drag_indicator(drawing *dr, game_drawstate *ds,
1952 game_state *state, game_ui *ui, int validdrag)
1953{
1954 int dir, w = ds->w, acol = COL_ARROW;
1955 int fx = FROMCOORD(ui->dx), fy = FROMCOORD(ui->dy);
1956 double ang;
1957
1958 if (validdrag) {
1959 /* If we could move here, lock the arrow to the appropriate direction. */
1960 dir = ui->drag_is_from ? state->dirs[ui->sy*w+ui->sx] : state->dirs[fy*w+fx];
1961
1962 ang = (2.0 * PI * dir) / 8.0; /* similar to calculation in draw_arrow_dir. */
1963 } else {
1964 /* Draw an arrow pointing away from/towards the origin cell. */
1965 int ox = COORD(ui->sx) + TILE_SIZE/2, oy = COORD(ui->sy) + TILE_SIZE/2;
1966 double tana, offset;
1967 double xdiff = fabs(ox - ui->dx), ydiff = fabs(oy - ui->dy);
1968
1969 if (xdiff == 0) {
1970 ang = (oy > ui->dy) ? 0.0F : PI;
1971 } else if (ydiff == 0) {
1972 ang = (ox > ui->dx) ? 3.0F*PI/2.0F : PI/2.0F;
1973 } else {
1974 if (ui->dx > ox && ui->dy < oy) {
1975 tana = xdiff / ydiff;
1976 offset = 0.0F;
1977 } else if (ui->dx > ox && ui->dy > oy) {
1978 tana = ydiff / xdiff;
1979 offset = PI/2.0F;
1980 } else if (ui->dx < ox && ui->dy > oy) {
1981 tana = xdiff / ydiff;
1982 offset = PI;
1983 } else {
1984 tana = ydiff / xdiff;
1985 offset = 3.0F * PI / 2.0F;
1986 }
1987 ang = atan(tana) + offset;
1988 }
1989
1990 if (!ui->drag_is_from) ang += PI; /* point to origin, not away from. */
1991
1992 }
1993 draw_arrow(dr, ui->dx, ui->dy, ARROW_HALFSZ, ang, acol, acol);
1994}
1995
1996static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
1997 game_state *state, int dir, game_ui *ui,
1998 float animtime, float flashtime)
1999{
2000 int x, y, i, w = ds->w, dirp, force = 0;
2001 unsigned int f;
2002 double angle_offset = 0.0;
2003 game_state *postdrop = NULL;
2004
2005 if (flashtime > 0.0F)
2006 angle_offset = 2.0 * PI * (flashtime / FLASH_SPIN);
2007 if (angle_offset != ds->angle_offset) {
2008 ds->angle_offset = angle_offset;
2009 force = 1;
2010 }
2011
2012 if (ds->dragging) {
2013 assert(ds->dragb);
2014 blitter_load(dr, ds->dragb, ds->dx, ds->dy);
2015 draw_update(dr, ds->dx, ds->dy, BLITTER_SIZE, BLITTER_SIZE);
2016 ds->dragging = FALSE;
2017 }
2018
2019 /* If an in-progress drag would make a valid move if finished, we
2020 * reflect that move in the board display. We let interpret_move do
2021 * most of the heavy lifting for us: we have to copy the game_ui so
2022 * as not to stomp on the real UI's drag state. */
2023 if (ui->dragging) {
2024 game_ui uicopy = *ui;
2025 char *movestr = interpret_move(state, &uicopy, ds, ui->dx, ui->dy, LEFT_RELEASE);
2026
2027 if (movestr != NULL && strcmp(movestr, "") != 0) {
2028 postdrop = execute_move(state, movestr);
2029 sfree(movestr);
2030
2031 state = postdrop;
2032 }
2033 }
2034
2035 if (!ds->started) {
2036 int aw = TILE_SIZE * state->w;
2037 int ah = TILE_SIZE * state->h;
2038 draw_rect(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER, COL_BACKGROUND);
2039 draw_rect_outline(dr, BORDER - 1, BORDER - 1, aw + 2, ah + 2, COL_GRID);
2040 draw_update(dr, 0, 0, aw + 2 * BORDER, ah + 2 * BORDER);
2041 }
2042 for (x = 0; x < state->w; x++) {
2043 for (y = 0; y < state->h; y++) {
2044 i = y*w + x;
2045 f = 0;
2046 dirp = -1;
2047
2048 if (ui->cshow && x == ui->cx && y == ui->cy)
2049 f |= F_CUR;
2050
2051 if (ui->dragging) {
2052 if (x == ui->sx && y == ui->sy)
2053 f |= F_DRAG_SRC;
2054 else if (ui->drag_is_from) {
2055 if (!ispointing(state, ui->sx, ui->sy, x, y))
2056 f |= F_DIM;
2057 } else {
2058 if (!ispointing(state, x, y, ui->sx, ui->sy))
2059 f |= F_DIM;
2060 }
2061 }
2062
2063 if (state->impossible ||
2064 state->nums[i] < 0 || state->flags[i] & FLAG_ERROR)
2065 f |= F_ERROR;
2066 if (state->flags[i] & FLAG_IMMUTABLE)
2067 f |= F_IMMUTABLE;
2068
2069 if (state->next[i] != -1)
2070 f |= F_ARROW_POINT;
2071
2072 if (state->prev[i] != -1) {
2073 /* Currently the direction here is from our square _back_
2074 * to its previous. We could change this to give the opposite
2075 * sense to the direction. */
2076 f |= F_ARROW_INPOINT;
2077 dirp = whichdir(x, y, state->prev[i]%w, state->prev[i]/w);
2078 }
2079
2080 if (state->nums[i] != ds->nums[i] ||
2081 f != ds->f[i] || dirp != ds->dirp[i] ||
2082 force || !ds->started) {
2083 tile_redraw(dr, ds,
2084 BORDER + x * TILE_SIZE,
2085 BORDER + y * TILE_SIZE,
2086 state->dirs[i], dirp, state->nums[i], f,
2087 angle_offset, -1);
2088 ds->nums[i] = state->nums[i];
2089 ds->f[i] = f;
2090 ds->dirp[i] = dirp;
2091 }
2092 }
2093 }
2094 if (ui->dragging) {
2095 ds->dragging = TRUE;
2096 ds->dx = ui->dx - BLITTER_SIZE/2;
2097 ds->dy = ui->dy - BLITTER_SIZE/2;
2098 blitter_save(dr, ds->dragb, ds->dx, ds->dy);
2099
2100 draw_drag_indicator(dr, ds, state, ui, postdrop ? 1 : 0);
2101 }
2102 if (postdrop) free_game(postdrop);
2103 if (!ds->started) ds->started = TRUE;
2104}
2105
2106static float game_anim_length(game_state *oldstate, game_state *newstate,
2107 int dir, game_ui *ui)
2108{
2109 return 0.0F;
2110}
2111
2112static float game_flash_length(game_state *oldstate, game_state *newstate,
2113 int dir, game_ui *ui)
2114{
2115 if (!oldstate->completed &&
2116 newstate->completed && !newstate->used_solve)
2117 return FLASH_SPIN;
2118 else
2119 return 0.0F;
2120}
2121
2122static int game_timing_state(game_state *state, game_ui *ui)
2123{
2124 return TRUE;
2125}
2126
2127static void game_print_size(game_params *params, float *x, float *y)
2128{
2129 int pw, ph;
2130
2131 game_compute_size(params, 1300, &pw, &ph);
2132 *x = pw / 100.0F;
2133 *y = ph / 100.0F;
2134}
2135
2136static void game_print(drawing *dr, game_state *state, int tilesize)
2137{
2138 int ink = print_mono_colour(dr, 0);
2139 int x, y;
2140
2141 /* Fake up just enough of a drawstate */
2142 game_drawstate ads, *ds = &ads;
2143 ds->tilesize = tilesize;
2144 ds->n = state->n;
2145
2146 /*
2147 * Border and grid.
2148 */
2149 print_line_width(dr, TILE_SIZE / 40);
2150 for (x = 1; x < state->w; x++)
2151 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(state->h), ink);
2152 for (y = 1; y < state->h; y++)
2153 draw_line(dr, COORD(0), COORD(y), COORD(state->w), COORD(y), ink);
2154 print_line_width(dr, 2*TILE_SIZE / 40);
2155 draw_rect_outline(dr, COORD(0), COORD(0), TILE_SIZE*state->w,
2156 TILE_SIZE*state->h, ink);
2157
2158 /*
2159 * Arrows and numbers.
2160 */
2161 print_line_width(dr, 0);
2162 for (y = 0; y < state->h; y++)
2163 for (x = 0; x < state->w; x++)
2164 tile_redraw(dr, ds, COORD(x), COORD(y), state->dirs[y*state->w+x],
2165 0, state->nums[y*state->w+x], 0, 0.0, ink);
2166}
2167
2168#ifdef COMBINED
2169#define thegame signpost
2170#endif
2171
2172const struct game thegame = {
2173 "Signpost", "games.signpost", "signpost",
2174 default_params,
2175 game_fetch_preset,
2176 decode_params,
2177 encode_params,
2178 free_params,
2179 dup_params,
2180 TRUE, game_configure, custom_params,
2181 validate_params,
2182 new_game_desc,
2183 validate_desc,
2184 new_game,
2185 dup_game,
2186 free_game,
2187 TRUE, solve_game,
2188 TRUE, game_can_format_as_text_now, game_text_format,
2189 new_ui,
2190 free_ui,
2191 encode_ui,
2192 decode_ui,
2193 game_changed_state,
2194 interpret_move,
2195 execute_move,
2196 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
2197 game_colours,
2198 game_new_drawstate,
2199 game_free_drawstate,
2200 game_redraw,
2201 game_anim_length,
2202 game_flash_length,
2203 TRUE, FALSE, game_print_size, game_print,
2204 FALSE, /* wants_statusbar */
2205 FALSE, game_timing_state,
2206 REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */
2207};
2208
2209#ifdef STANDALONE_SOLVER
2210
2211#include <time.h>
2212#include <stdarg.h>
2213
2214const char *quis = NULL;
2215int verbose = 0;
2216
2217void usage(FILE *out) {
2218 fprintf(out, "usage: %s [--stdin] [--soak] [--seed SEED] <params>|<game id>\n", quis);
2219}
2220
2221static void cycle_seed(char **seedstr, random_state *rs)
2222{
2223 char newseed[16];
2224 int j;
2225
2226 newseed[15] = '\0';
2227 newseed[0] = '1' + (char)random_upto(rs, 9);
2228 for (j = 1; j < 15; j++)
2229 newseed[j] = '0' + (char)random_upto(rs, 10);
2230 sfree(*seedstr);
2231 *seedstr = dupstr(newseed);
2232}
2233
2234static void start_soak(game_params *p, char *seedstr)
2235{
2236 time_t tt_start, tt_now, tt_last;
2237 char *desc, *aux;
2238 random_state *rs;
2239 long n = 0, nnums = 0, i;
2240 game_state *state;
2241
2242 tt_start = tt_now = time(NULL);
2243 printf("Soak-generating a %dx%d grid.\n", p->w, p->h);
2244
2245 while (1) {
2246 rs = random_new(seedstr, strlen(seedstr));
2247 desc = thegame.new_desc(p, rs, &aux, 0);
2248
2249 state = thegame.new_game(NULL, p, desc);
2250 for (i = 0; i < state->n; i++) {
2251 if (state->flags[i] & FLAG_IMMUTABLE)
2252 nnums++;
2253 }
2254 thegame.free_game(state);
2255
2256 sfree(desc);
2257 cycle_seed(&seedstr, rs);
2258 random_free(rs);
2259
2260 n++;
2261 tt_last = time(NULL);
2262 if (tt_last > tt_now) {
2263 tt_now = tt_last;
2264 printf("%ld total, %3.1f/s, %3.1f nums/grid (%3.1f%%).\n",
2265 n,
2266 (double)n / ((double)tt_now - tt_start),
2267 (double)nnums / (double)n,
2268 ((double)nnums * 100.0) / ((double)n * (double)p->w * (double)p->h) );
2269 }
2270 }
2271}
2272
2273static void process_desc(char *id)
2274{
2275 char *desc, *err, *solvestr;
2276 game_params *p;
2277 game_state *s;
2278
2279 printf("%s\n ", id);
2280
2281 desc = strchr(id, ':');
2282 if (!desc) {
2283 fprintf(stderr, "%s: expecting game description.", quis);
2284 exit(1);
2285 }
2286
2287 *desc++ = '\0';
2288
2289 p = thegame.default_params();
2290 thegame.decode_params(p, id);
2291 err = thegame.validate_params(p, 1);
2292 if (err) {
2293 fprintf(stderr, "%s: %s", quis, err);
2294 thegame.free_params(p);
2295 return;
2296 }
2297
2298 err = thegame.validate_desc(p, desc);
2299 if (err) {
2300 fprintf(stderr, "%s: %s\nDescription: %s\n", quis, err, desc);
2301 thegame.free_params(p);
2302 return;
2303 }
2304
2305 s = thegame.new_game(NULL, p, desc);
2306
2307 solvestr = thegame.solve(s, s, NULL, &err);
2308 if (!solvestr)
2309 fprintf(stderr, "%s\n", err);
2310 else
2311 printf("Puzzle is soluble.\n");
2312
2313 thegame.free_game(s);
2314 thegame.free_params(p);
2315}
2316
2317int main(int argc, const char *argv[])
2318{
2319 char *id = NULL, *desc, *err, *aux = NULL;
2320 int soak = 0, verbose = 0, stdin_desc = 0, n = 1, i;
2321 char *seedstr = NULL, newseed[16];
2322
2323 setvbuf(stdout, NULL, _IONBF, 0);
2324
2325 quis = argv[0];
2326 while (--argc > 0) {
2327 char *p = (char*)(*++argv);
2328 if (!strcmp(p, "-v") || !strcmp(p, "--verbose"))
2329 verbose = 1;
2330 else if (!strcmp(p, "--stdin"))
2331 stdin_desc = 1;
2332 else if (!strcmp(p, "-e") || !strcmp(p, "--seed")) {
2333 seedstr = dupstr(*++argv);
2334 argc--;
2335 } else if (!strcmp(p, "-n") || !strcmp(p, "--number")) {
2336 n = atoi(*++argv);
2337 argc--;
2338 } else if (!strcmp(p, "-s") || !strcmp(p, "--soak")) {
2339 soak = 1;
2340 } else if (*p == '-') {
2341 fprintf(stderr, "%s: unrecognised option `%s'\n", argv[0], p);
2342 usage(stderr);
2343 exit(1);
2344 } else {
2345 id = p;
2346 }
2347 }
2348
2349 sprintf(newseed, "%lu", time(NULL));
2350 seedstr = dupstr(newseed);
2351
2352 if (id || !stdin_desc) {
2353 if (id && strchr(id, ':')) {
2354 /* Parameters and description passed on cmd-line:
2355 * try and solve it. */
2356 process_desc(id);
2357 } else {
2358 /* No description passed on cmd-line: decode parameters
2359 * (with optional seed too) */
2360
2361 game_params *p = thegame.default_params();
2362
2363 if (id) {
2364 char *cmdseed = strchr(id, '#');
2365 if (cmdseed) {
2366 *cmdseed++ = '\0';
2367 sfree(seedstr);
2368 seedstr = dupstr(cmdseed);
2369 }
2370
2371 thegame.decode_params(p, id);
2372 }
2373
2374 err = thegame.validate_params(p, 1);
2375 if (err) {
2376 fprintf(stderr, "%s: %s", quis, err);
2377 thegame.free_params(p);
2378 exit(1);
2379 }
2380
2381 /* We have a set of valid parameters; either soak with it
2382 * or generate a single game description and print to stdout. */
2383 if (soak)
2384 start_soak(p, seedstr);
2385 else {
2386 char *pstring = thegame.encode_params(p, 0);
2387
2388 for (i = 0; i < n; i++) {
2389 random_state *rs = random_new(seedstr, strlen(seedstr));
2390
2391 if (verbose) printf("%s#%s\n", pstring, seedstr);
2392 desc = thegame.new_desc(p, rs, &aux, 0);
2393 printf("%s:%s\n", pstring, desc);
2394 sfree(desc);
2395
2396 cycle_seed(&seedstr, rs);
2397
2398 random_free(rs);
2399 }
2400
2401 sfree(pstring);
2402 }
2403 thegame.free_params(p);
2404 }
2405 }
2406
2407 if (stdin_desc) {
2408 char buf[4096];
2409
2410 while (fgets(buf, sizeof(buf), stdin)) {
2411 buf[strcspn(buf, "\r\n")] = '\0';
2412 process_desc(buf);
2413 }
2414 }
2415 sfree(seedstr);
2416
2417 return 0;
2418}
2419
2420#endif
2421
2422
2423/* vim: set shiftwidth=4 tabstop=8: */