Add James Harvey's excellent new puzzle, `Galaxies'.
[sgt/puzzles] / galaxies.c
1 /*
2 * galaxies.c: implementation of 'Tentai Show' from Nikoli,
3 * also sometimes called 'Spiral Galaxies'.
4 *
5 * Notes:
6 *
7 * Grid is stored as size (2n-1), holding edges as well as spaces
8 * (and thus vertices too, at edge intersections).
9 *
10 * Any dot will thus be positioned at one of our grid points,
11 * which saves any faffing with half-of-a-square stuff.
12 *
13 * Edges have on/off state; obviously the actual edges of the
14 * board are fixed to on, and everything else starts as off.
15 *
16 * TTD:
17 * Cleverer solver
18 * Think about how to display remote groups of tiles?
19 *
20 * Bugs:
21 *
22 * Notable puzzle IDs:
23 *
24 * Nikoli's example [web site has wrong highlighting]
25 * (at http://www.nikoli.co.jp/en/puzzles/astronomical_show/):
26 * 5x5:eBbbMlaBbOEnf
27 *
28 * The 'spiral galaxies puzzles are NP-complete' paper
29 * (at http://www.stetson.edu/~efriedma/papers/spiral.pdf):
30 * 7x7:chpgdqqqoezdddki
31 *
32 * Puzzle competition pdf examples
33 * (at http://www.puzzleratings.org/Yurekli2006puz.pdf):
34 * 6x6:EDbaMucCohbrecEi
35 * 10x10:beFbufEEzowDlxldibMHezBQzCdcFzjlci
36 * 13x13:dCemIHFFkJajjgDfdbdBzdzEgjccoPOcztHjBczLDjczqktJjmpreivvNcggFi
37 *
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <assert.h>
44 #include <ctype.h>
45 #include <math.h>
46
47 #include "puzzles.h"
48
49 #ifdef DEBUGGING
50 #define solvep debug
51 #else
52 int solver_show_working;
53 #define solvep(x) do { if (solver_show_working) { printf x; } } while(0)
54 #endif
55
56 enum {
57 COL_BACKGROUND,
58 COL_WHITEBG,
59 COL_BLACKBG,
60 COL_WHITEDOT,
61 COL_BLACKDOT,
62 COL_GRID,
63 COL_EDGE,
64 COL_ARROW,
65 NCOLOURS
66 };
67
68 #define DIFFLIST(A) \
69 A(EASY,Easy,e) \
70 A(HARD,Hard,h) \
71 A(RECURSIVE,Recursive,r)
72
73 #define ENUM(upper,title,lower) DIFF_ ## upper,
74 #define TITLE(upper,title,lower) #title,
75 #define ENCODE(upper,title,lower) #lower
76 #define CONFIG(upper,title,lower) ":" #title
77 enum { DIFFLIST(ENUM)
78 DIFF_IMPOSSIBLE, DIFF_AMBIGUOUS, DIFF_UNFINISHED, DIFF_MAX };
79 static char const *const galaxies_diffnames[] = {
80 DIFFLIST(TITLE) "Impossible", "Ambiguous", "Unfinished" };
81 static char const galaxies_diffchars[] = DIFFLIST(ENCODE);
82 #define DIFFCONFIG DIFFLIST(CONFIG)
83
84 struct game_params {
85 /* X and Y is the area of the board as seen by
86 * the user, not the (2n+1) area the game uses. */
87 int w, h, diff;
88 };
89
90 enum { s_tile, s_edge, s_vertex };
91
92 #define F_DOT 1 /* there's a dot here */
93 #define F_EDGE_SET 2 /* the edge is set */
94 #define F_TILE_ASSOC 4 /* this tile is associated with a dot. */
95 #define F_DOT_BLACK 8 /* (ui only) dot is black. */
96 #define F_MARK 16 /* scratch flag */
97 #define F_REACHABLE 32
98 #define F_SCRATCH 64
99 #define F_MULTIPLE 128
100 #define F_DOT_HOLD 256
101 #define F_GOOD 512
102
103 typedef struct space {
104 int x, y; /* its position */
105 int type;
106 unsigned int flags;
107 int dotx, doty; /* if flags & F_TILE_ASSOC */
108 int nassoc; /* if flags & F_DOT */
109 } space;
110
111 #define INGRID(s,x,y) ((x) >= 0 && (y) >= 0 && \
112 (x) < (state)->sx && (y) < (state)->sy)
113 #define INUI(s,x,y) ((x) > 0 && (y) > 0 && \
114 (x) < ((state)->sx-1) && (y) < ((state)->sy-1))
115
116 #define GRID(s,g,x,y) ((s)->g[((y)*(s)->sx)+(x)])
117 #define SPACE(s,x,y) GRID(s,grid,x,y)
118
119 struct game_state {
120 int w, h; /* size from params */
121 int sx, sy; /* allocated size, (2x-1)*(2y-1) */
122 space *grid;
123 int completed, used_solve;
124 int ndots;
125 space **dots;
126
127 midend *me; /* to call supersede_game_desc */
128 int cdiff; /* difficulty of current puzzle (for status bar),
129 or -1 if stale. */
130 };
131
132 /* ----------------------------------------------------------
133 * Game parameters and presets
134 */
135
136 /* make up some sensible default sizes */
137
138 #define DEFAULT_PRESET 1
139
140 static const game_params galaxies_presets[] = {
141 { 7, 7, DIFF_EASY },
142 { 7, 7, DIFF_HARD },
143 { 7, 7, DIFF_RECURSIVE },
144 { 10, 10, DIFF_EASY },
145 { 10, 10, DIFF_HARD },
146 { 15, 15, DIFF_EASY },
147 { 15, 15, DIFF_HARD },
148 };
149
150 static int game_fetch_preset(int i, char **name, game_params **params)
151 {
152 game_params *ret;
153 char buf[80];
154
155 if (i < 0 || i >= lenof(galaxies_presets))
156 return FALSE;
157
158 ret = snew(game_params);
159 *ret = galaxies_presets[i]; /* structure copy */
160
161 sprintf(buf, "%dx%d %s", ret->w, ret->h,
162 galaxies_diffnames[ret->diff]);
163
164 if (name) *name = dupstr(buf);
165 *params = ret;
166 return TRUE;
167 }
168
169 static game_params *default_params(void)
170 {
171 game_params *ret;
172 game_fetch_preset(DEFAULT_PRESET, NULL, &ret);
173 return ret;
174 }
175
176 static void free_params(game_params *params)
177 {
178 sfree(params);
179 }
180
181 static game_params *dup_params(game_params *params)
182 {
183 game_params *ret = snew(game_params);
184 *ret = *params; /* structure copy */
185 return ret;
186 }
187
188 static void decode_params(game_params *params, char const *string)
189 {
190 params->h = params->w = atoi(string);
191 params->diff = DIFF_EASY;
192 while (*string && isdigit((unsigned char)*string)) string++;
193 if (*string == 'x') {
194 string++;
195 params->h = atoi(string);
196 while (*string && isdigit((unsigned char)*string)) string++;
197 }
198 if (*string == 'd') {
199 int i;
200 string++;
201 for (i = 0; i <= DIFF_RECURSIVE; i++)
202 if (*string == galaxies_diffchars[i])
203 params->diff = i;
204 if (*string) string++;
205 }
206 }
207
208 static char *encode_params(game_params *params, int full)
209 {
210 char str[80];
211 sprintf(str, "%dx%d", params->w, params->h);
212 if (full)
213 sprintf(str + strlen(str), "d%c", galaxies_diffchars[params->diff]);
214 return dupstr(str);
215 }
216
217 static config_item *game_configure(game_params *params)
218 {
219 config_item *ret;
220 char buf[80];
221
222 ret = snewn(4, config_item);
223
224 ret[0].name = "Width";
225 ret[0].type = C_STRING;
226 sprintf(buf, "%d", params->w);
227 ret[0].sval = dupstr(buf);
228 ret[0].ival = 0;
229
230 ret[1].name = "Height";
231 ret[1].type = C_STRING;
232 sprintf(buf, "%d", params->h);
233 ret[1].sval = dupstr(buf);
234 ret[1].ival = 0;
235
236 ret[2].name = "Difficulty";
237 ret[2].type = C_CHOICES;
238 ret[2].sval = DIFFCONFIG;
239 ret[2].ival = params->diff;
240
241 ret[3].name = NULL;
242 ret[3].type = C_END;
243 ret[3].sval = NULL;
244 ret[3].ival = 0;
245
246 return ret;
247 }
248
249 static game_params *custom_params(config_item *cfg)
250 {
251 game_params *ret = snew(game_params);
252
253 ret->w = atoi(cfg[0].sval);
254 ret->h = atoi(cfg[1].sval);
255 ret->diff = cfg[2].ival;
256
257 return ret;
258 }
259
260 static char *validate_params(game_params *params, int full)
261 {
262 if (params->w < 3 || params->h < 3)
263 return "Width and height must both be at least 3";
264 /*
265 * This shouldn't be able to happen at all, since decode_params
266 * and custom_params will never generate anything that isn't
267 * within range.
268 */
269 assert(params->diff <= DIFF_RECURSIVE);
270
271 return NULL;
272 }
273
274 /* ----------------------------------------------------------
275 * Game utility functions.
276 */
277
278 static void add_dot(space *space) {
279 assert(!(space->flags & F_DOT));
280 space->flags |= F_DOT;
281 space->nassoc = 0;
282 }
283
284 static void remove_dot(space *space) {
285 assert(space->flags & F_DOT);
286 space->flags &= ~F_DOT;
287 }
288
289 static void remove_assoc(game_state *state, space *tile) {
290 if (tile->flags & F_TILE_ASSOC) {
291 SPACE(state, tile->dotx, tile->doty).nassoc--;
292 tile->flags &= ~F_TILE_ASSOC;
293 tile->dotx = -1;
294 tile->doty = -1;
295 }
296 }
297
298 static void add_assoc(game_state *state, space *tile, space *dot) {
299 remove_assoc(state, tile);
300
301 tile->flags |= F_TILE_ASSOC;
302 tile->dotx = dot->x;
303 tile->doty = dot->y;
304 dot->nassoc++;
305 debug(("add_assoc sp %d %d --> dot %d,%d, new nassoc %d.\n",
306 tile->x, tile->y, dot->x, dot->y, dot->nassoc));
307 }
308
309 static struct space *sp2dot(game_state *state, int x, int y)
310 {
311 struct space *sp = &SPACE(state, x, y);
312 if (!(sp->flags & F_TILE_ASSOC)) return NULL;
313 return &SPACE(state, sp->dotx, sp->doty);
314 }
315
316 #define IS_VERTICAL_EDGE(x) ((x % 2) == 0)
317
318 static char *game_text_format(game_state *state)
319 {
320 int maxlen = (state->sx+1)*state->sy, x, y;
321 char *ret, *p;
322 space *sp;
323
324 ret = snewn(maxlen+1, char);
325 p = ret;
326
327 for (y = 0; y < state->sy; y++) {
328 for (x = 0; x < state->sx; x++) {
329 sp = &SPACE(state, x, y);
330 if (sp->flags & F_DOT)
331 *p++ = 'o';
332 else if (sp->flags & (F_REACHABLE|F_MULTIPLE|F_MARK))
333 *p++ = (sp->flags & F_MULTIPLE) ? 'M' :
334 (sp->flags & F_REACHABLE) ? 'R' : 'X';
335 else {
336 switch (sp->type) {
337 case s_tile:
338 if (sp->flags & F_TILE_ASSOC) {
339 space *dot = sp2dot(state, sp->x, sp->y);
340 if (dot->flags & F_DOT)
341 *p++ = (dot->flags & F_DOT_BLACK) ? 'B' : 'W';
342 else
343 *p++ = '?'; /* association with not-a-dot. */
344 } else
345 *p++ = ' ';
346 break;
347
348 case s_vertex:
349 *p++ = '+';
350 break;
351
352 case s_edge:
353 if (sp->flags & F_EDGE_SET)
354 *p++ = (IS_VERTICAL_EDGE(x)) ? '|' : '-';
355 else
356 *p++ = ' ';
357 break;
358
359 default:
360 assert(!"shouldn't get here!");
361 }
362 }
363 }
364 *p++ = '\n';
365 }
366
367 assert(p - ret == maxlen);
368 *p = '\0';
369
370 return ret;
371 }
372
373 static void dbg_state(game_state *state)
374 {
375 #ifdef DEBUGGING
376 char *temp = game_text_format(state);
377 debug(("%s\n", temp));
378 sfree(temp);
379 #endif
380 }
381
382 /* Space-enumeration callbacks should all return 1 for 'progress made',
383 * -1 for 'impossible', and 0 otherwise. */
384 typedef int (*space_cb)(game_state *state, space *sp, void *ctx);
385
386 #define IMPOSSIBLE_QUITS 1
387
388 static int foreach_sub(game_state *state, space_cb cb, unsigned int f,
389 void *ctx, int startx, int starty)
390 {
391 int x, y, progress = 0, impossible = 0, ret;
392 space *sp;
393
394 for (y = starty; y < state->sy; y += 2) {
395 sp = &SPACE(state, startx, y);
396 for (x = startx; x < state->sx; x += 2) {
397 ret = cb(state, sp, ctx);
398 if (ret == -1) {
399 if (f & IMPOSSIBLE_QUITS) return -1;
400 impossible = -1;
401 } else if (ret == 1) {
402 progress = 1;
403 }
404 sp += 2;
405 }
406 }
407 return impossible ? -1 : progress;
408 }
409
410 static int foreach_tile(game_state *state, space_cb cb, unsigned int f,
411 void *ctx)
412 {
413 return foreach_sub(state, cb, f, ctx, 1, 1);
414 }
415
416 static int foreach_edge(game_state *state, space_cb cb, unsigned int f,
417 void *ctx)
418 {
419 int ret1, ret2;
420
421 ret1 = foreach_sub(state, cb, f, ctx, 0, 1);
422 ret2 = foreach_sub(state, cb, f, ctx, 1, 0);
423
424 if (ret1 == -1 || ret2 == -1) return -1;
425 return (ret1 || ret2) ? 1 : 0;
426 }
427
428 #if 0
429 static int foreach_vertex(game_state *state, space_cb cb, unsigned int f,
430 void *ctx)
431 {
432 return foreach_sub(state, cb, f, ctx, 0, 0);
433 }
434 #endif
435
436 #if 0
437 static int is_same_assoc(game_state *state,
438 int x1, int y1, int x2, int y2)
439 {
440 struct space *s1, *s2;
441
442 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2))
443 return 0;
444
445 s1 = &SPACE(state, x1, y1);
446 s2 = &SPACE(state, x2, y2);
447 assert(s1->type == s_tile && s2->type == s_tile);
448 if ((s1->flags & F_TILE_ASSOC) && (s2->flags & F_TILE_ASSOC) &&
449 s1->dotx == s2->dotx && s1->doty == s2->doty)
450 return 1;
451 return 0; /* 0 if not same or not both associated. */
452 }
453 #endif
454
455 #if 0
456 static int edges_into_vertex(game_state *state,
457 int x, int y)
458 {
459 int dx, dy, nx, ny, count = 0;
460
461 assert(SPACE(state, x, y).type == s_vertex);
462 for (dx = -1; dx <= 1; dx++) {
463 for (dy = -1; dy <= 1; dy++) {
464 if (dx != 0 && dy != 0) continue;
465 if (dx == 0 && dy == 0) continue;
466
467 nx = x+dx; ny = y+dy;
468 if (!INGRID(state, nx, ny)) continue;
469 assert(SPACE(state, nx, ny).type == s_edge);
470 if (SPACE(state, nx, ny).flags & F_EDGE_SET)
471 count++;
472 }
473 }
474 return count;
475 }
476 #endif
477
478 static struct space *space_opposite_dot(struct game_state *state,
479 struct space *sp, struct space *dot)
480 {
481 int dx, dy, tx, ty;
482 space *sp2;
483
484 dx = sp->x - dot->x;
485 dy = sp->y - dot->y;
486 tx = dot->x - dx;
487 ty = dot->y - dy;
488 if (!INGRID(state, tx, ty)) return NULL;
489
490 sp2 = &SPACE(state, tx, ty);
491 assert(sp2->type == sp->type);
492 return sp2;
493 }
494
495 static struct space *tile_opposite(struct game_state *state, struct space *sp)
496 {
497 struct space *dot;
498
499 assert(sp->flags & F_TILE_ASSOC);
500 dot = &SPACE(state, sp->dotx, sp->doty);
501 return space_opposite_dot(state, sp, dot);
502 }
503
504 static int dotfortile(game_state *state, space *tile, space *dot)
505 {
506 space *tile_opp = space_opposite_dot(state, tile, dot);
507
508 if (!tile_opp) return 0; /* opposite would be off grid */
509 if (tile_opp->flags & F_TILE_ASSOC &&
510 (tile_opp->dotx != dot->x || tile_opp->doty != dot->y))
511 return 0; /* opposite already associated with diff. dot */
512 return 1;
513 }
514
515 static void adjacencies(struct game_state *state, struct space *sp,
516 struct space **a1s, struct space **a2s)
517 {
518 int dxs[4] = {-1, 1, 0, 0}, dys[4] = {0, 0, -1, 1};
519 int n, x, y;
520
521 /* this function needs optimising. */
522
523 for (n = 0; n < 4; n++) {
524 x = sp->x+dxs[n];
525 y = sp->y+dys[n];
526
527 if (INGRID(state, x, y)) {
528 a1s[n] = &SPACE(state, x, y);
529
530 x += dxs[n]; y += dys[n];
531
532 if (INGRID(state, x, y))
533 a2s[n] = &SPACE(state, x, y);
534 else
535 a2s[n] = NULL;
536 } else {
537 a1s[n] = a2s[n] = NULL;
538 }
539 }
540 }
541
542 static int outline_tile_fordot(game_state *state, space *tile, int mark)
543 {
544 struct space *tadj[4], *eadj[4];
545 int i, didsth = 0, edge, same;
546
547 assert(tile->type == s_tile);
548 adjacencies(state, tile, eadj, tadj);
549 for (i = 0; i < 4; i++) {
550 if (!eadj[i]) continue;
551
552 edge = (eadj[i]->flags & F_EDGE_SET) ? 1 : 0;
553 if (tadj[i]) {
554 if (!(tile->flags & F_TILE_ASSOC))
555 same = (tadj[i]->flags & F_TILE_ASSOC) ? 0 : 1;
556 else
557 same = ((tadj[i]->flags & F_TILE_ASSOC) &&
558 tile->dotx == tadj[i]->dotx &&
559 tile->doty == tadj[i]->doty) ? 1 : 0;
560 } else
561 same = 0;
562
563 if (!edge && !same) {
564 if (mark) eadj[i]->flags |= F_EDGE_SET;
565 didsth = 1;
566 } else if (edge && same) {
567 if (mark) eadj[i]->flags &= ~F_EDGE_SET;
568 didsth = 1;
569 }
570 }
571 return didsth;
572 }
573
574 static void tiles_from_edge(struct game_state *state,
575 struct space *sp, struct space **ts)
576 {
577 int xs[2], ys[2];
578
579 if (IS_VERTICAL_EDGE(sp->x)) {
580 xs[0] = sp->x-1; ys[0] = sp->y;
581 xs[1] = sp->x+1; ys[1] = sp->y;
582 } else {
583 xs[0] = sp->x; ys[0] = sp->y-1;
584 xs[1] = sp->x; ys[1] = sp->y+1;
585 }
586 ts[0] = INGRID(state, xs[0], ys[0]) ? &SPACE(state, xs[0], ys[0]) : NULL;
587 ts[1] = INGRID(state, xs[1], ys[1]) ? &SPACE(state, xs[1], ys[1]) : NULL;
588 }
589
590 /* Check all tiles are associated with something, and all shapes
591 * are the correct symmetry (i.e. all tiles have a matching tile
592 * the opposite direction from the dot) */
593 static int cccb_assoc(game_state *state, space *tile, void *unused)
594 {
595 assert(tile->type == s_tile);
596
597 if (!(tile->flags & F_TILE_ASSOC)) return -1;
598 return 0;
599 }
600
601 struct dgs_ctx {
602 space *dot;
603 int ndots;
604 };
605
606 static int dgs_cb_check(game_state *state, space *tile, void *vctx)
607 {
608 struct dgs_ctx *ctx = (struct dgs_ctx *)vctx;
609 space *opp;
610
611 if (!(tile->flags & F_TILE_ASSOC)) return 0;
612 if (tile->dotx != ctx->dot->x ||
613 tile->doty != ctx->dot->y) return 0;
614
615 ctx->ndots += 1;
616
617 /* Check this tile has an opposite associated with same dot. */
618 opp = tile_opposite(state, tile);
619 if (!opp || !(opp->flags & F_TILE_ASSOC)) return -1;
620 if (opp->dotx != tile->dotx || opp->doty != tile->doty) return -1;
621
622 /* Check its edges are correct */
623 if (outline_tile_fordot(state, tile, 0) == 1)
624 return -1; /* there was some fixing required, we're wrong. */
625
626 return 0;
627 }
628
629 static int dot_good_shape(game_state *state, space *dot, int mark)
630 {
631 struct dgs_ctx ctx;
632
633 ctx.dot = dot;
634 ctx.ndots = 0;
635
636 if (mark) dot->flags &= ~F_GOOD;
637
638 if (foreach_tile(state, dgs_cb_check, 0, &ctx) == -1)
639 return 0;
640 if (ctx.ndots == 0) return 0; /* no dots assoc. with tile. */
641
642 if (mark) {
643 debug(("marking dot %d,%d good tile.\n", dot->x, dot->y));
644 dot->flags |= F_GOOD;
645 }
646 return 1;
647 }
648
649 static int check_complete(game_state *state, int mark_errors)
650 {
651 int i, complete = 1;
652
653 /* Are all tiles associated? */
654 if (foreach_tile(state, cccb_assoc, 0, NULL) == -1)
655 complete = 0;
656
657 /* Check all dots are associated, and their tiles are well-formed. */
658 for (i = 0; i < state->ndots; i++) {
659 if (!dot_good_shape(state, state->dots[i], mark_errors))
660 complete = 0;
661 }
662
663 /*if (complete == 1) printf("Complete!\n");*/
664 return complete;
665 }
666
667 /* Returns a move string for use by 'solve'; if you don't want the
668 * initial 'S;' use ret[2]. */
669 static char *diff_game(game_state *src, game_state *dest, int issolve)
670 {
671 int movelen = 0, movesize = 256, x, y, len;
672 char *move = snewn(movesize, char), buf[80], *sep = "";
673 char achar = issolve ? 'a' : 'A';
674 space *sps, *spd;
675
676 assert(src->sx == dest->sx && src->sy == dest->sy);
677
678 if (issolve) {
679 move[movelen++] = 'S';
680 sep = ";";
681 }
682 move[movelen] = '\0';
683 for (x = 0; x < src->sx; x++) {
684 for (y = 0; y < src->sy; y++) {
685 sps = &SPACE(src, x, y);
686 spd = &SPACE(dest, x, y);
687
688 assert(sps->type == spd->type);
689
690 len = 0;
691 if (sps->type == s_tile) {
692 if ((sps->flags & F_TILE_ASSOC) &&
693 (spd->flags & F_TILE_ASSOC)) {
694 if (sps->dotx != spd->dotx ||
695 sps->doty != spd->doty)
696 /* Both associated; change association, if different */
697 len = sprintf(buf, "%s%c%d,%d,%d,%d", sep,
698 (int)achar, x, y, spd->dotx, spd->doty);
699 } else if (sps->flags & F_TILE_ASSOC)
700 /* Only src associated; remove. */
701 len = sprintf(buf, "%sU%d,%d", sep, x, y);
702 else if (spd->flags & F_TILE_ASSOC)
703 /* Only dest associated; add. */
704 len = sprintf(buf, "%s%c%d,%d,%d,%d", sep,
705 (int)achar, x, y, spd->dotx, spd->doty);
706 } else if (sps->type == s_edge) {
707 if ((sps->flags & F_EDGE_SET) != (spd->flags & F_EDGE_SET))
708 /* edge flags are different; flip them. */
709 len = sprintf(buf, "%sE%d,%d", sep, x, y);
710 }
711 if (len) {
712 if (movelen + len >= movesize) {
713 movesize = movelen + len + 256;
714 move = sresize(move, movesize, char);
715 }
716 strcpy(move + movelen, buf);
717 movelen += len;
718 sep = ";";
719 }
720 }
721 }
722 debug(("diff_game src then dest:\n"));
723 dbg_state(src);
724 dbg_state(dest);
725 debug(("diff string %s\n", move));
726 return move;
727 }
728
729 /* Returns 1 if a dot here would not be too close to any other dots
730 * (and would avoid other game furniture). */
731 static int dot_is_possible(game_state *state, space *sp, int allow_assoc)
732 {
733 int bx = 0, by = 0, dx, dy;
734 space *adj;
735
736 switch (sp->type) {
737 case s_tile:
738 bx = by = 1; break;
739 case s_edge:
740 if (IS_VERTICAL_EDGE(sp->x)) {
741 bx = 2; by = 1;
742 } else {
743 bx = 1; by = 2;
744 }
745 break;
746 case s_vertex:
747 bx = by = 2; break;
748 }
749
750 for (dx = -bx; dx <= bx; dx++) {
751 for (dy = -by; dy <= by; dy++) {
752 if (!INGRID(state, sp->x+dx, sp->y+dy)) continue;
753
754 adj = &SPACE(state, sp->x+dx, sp->y+dy);
755
756 if (!allow_assoc && (adj->flags & F_TILE_ASSOC))
757 return 0;
758
759 if (dx != 0 || dy != 0) {
760 /* Other than our own square, no dots nearby. */
761 if (adj->flags & (F_DOT))
762 return 0;
763 }
764
765 /* We don't want edges within our rectangle
766 * (but don't care about edges on the edge) */
767 if (abs(dx) < bx && abs(dy) < by &&
768 adj->flags & F_EDGE_SET)
769 return 0;
770 }
771 }
772 return 1;
773 }
774
775 /* ----------------------------------------------------------
776 * Game generation, structure creation, and descriptions.
777 */
778
779 static game_state *blank_game(int w, int h)
780 {
781 game_state *state = snew(game_state);
782 int x, y;
783
784 state->w = w;
785 state->h = h;
786
787 state->sx = (w*2)+1;
788 state->sy = (h*2)+1;
789 state->grid = snewn(state->sx * state->sy, struct space);
790 state->completed = state->used_solve = 0;
791
792 for (x = 0; x < state->sx; x++) {
793 for (y = 0; y < state->sy; y++) {
794 struct space *sp = &SPACE(state, x, y);
795 memset(sp, 0, sizeof(struct space));
796 sp->x = x;
797 sp->y = y;
798 if ((x % 2) == 0 && (y % 2) == 0)
799 sp->type = s_vertex;
800 else if ((x % 2) == 0 || (y % 2) == 0) {
801 sp->type = s_edge;
802 if (x == 0 || y == 0 || x == state->sx-1 || y == state->sy-1)
803 sp->flags |= F_EDGE_SET;
804 } else
805 sp->type = s_tile;
806 }
807 }
808
809 state->ndots = 0;
810 state->dots = NULL;
811
812 state->me = NULL; /* filled in by new_game. */
813 state->cdiff = -1;
814
815 return state;
816 }
817
818 static void game_update_dots(game_state *state)
819 {
820 int i, n, sz = state->sx * state->sy;
821
822 if (state->dots) sfree(state->dots);
823 state->ndots = 0;
824
825 for (i = 0; i < sz; i++) {
826 if (state->grid[i].flags & F_DOT) state->ndots++;
827 }
828 state->dots = snewn(state->ndots, space *);
829 n = 0;
830 for (i = 0; i < sz; i++) {
831 if (state->grid[i].flags & F_DOT)
832 state->dots[n++] = &state->grid[i];
833 }
834 }
835
836 static void clear_game(game_state *state, int cleardots)
837 {
838 int x, y;
839
840 /* don't erase edge flags around outline! */
841 for (x = 1; x < state->sx-1; x++) {
842 for (y = 1; y < state->sy-1; y++) {
843 if (cleardots)
844 SPACE(state, x, y).flags = 0;
845 else
846 SPACE(state, x, y).flags &= (F_DOT|F_DOT_BLACK);
847 }
848 }
849 if (cleardots) game_update_dots(state);
850 }
851
852 static game_state *dup_game(game_state *state)
853 {
854 game_state *ret = blank_game(state->w, state->h);
855
856 ret->completed = state->completed;
857 ret->used_solve = state->used_solve;
858
859 memcpy(ret->grid, state->grid,
860 ret->sx*ret->sy*sizeof(struct space));
861
862 game_update_dots(ret);
863
864 ret->me = state->me;
865 ret->cdiff = state->cdiff;
866
867 return ret;
868 }
869
870 static void free_game(game_state *state)
871 {
872 if (state->dots) sfree(state->dots);
873 sfree(state->grid);
874 sfree(state);
875 }
876
877 /* Game description is a sequence of letters representing the number
878 * of spaces (a = 0, y = 24) before the next dot; a-y for a white dot,
879 * and A-Y for a black dot. 'z' is 25 spaces (and no dot).
880 *
881 * I know it's a bitch to generate by hand, so we provide
882 * an edit mode.
883 */
884
885 static char *encode_game(game_state *state)
886 {
887 char *desc, *p;
888 int run, x, y, area;
889 unsigned int f;
890
891 area = (state->sx-2) * (state->sy-2);
892
893 desc = snewn(area, char);
894 p = desc;
895 run = 0;
896 for (y = 1; y < state->sy-1; y++) {
897 for (x = 1; x < state->sx-1; x++) {
898 f = SPACE(state, x, y).flags;
899
900 /* a/A is 0 spaces between, b/B is 1 space, ...
901 * y/Y is 24 spaces, za/zA is 25 spaces, ...
902 * It's easier to count from 0 because we then
903 * don't have to special-case the top left-hand corner
904 * (which could be a dot with 0 spaces before it). */
905 if (!(f & F_DOT))
906 run++;
907 else {
908 while (run > 24) {
909 *p++ = 'z';
910 run -= 25;
911 }
912 *p++ = ((f & F_DOT_BLACK) ? 'A' : 'a') + run;
913 run = 0;
914 }
915 }
916 }
917 assert(p - desc < area);
918 *p++ = '\0';
919 desc = sresize(desc, p - desc, char);
920
921 return desc;
922 }
923
924 struct movedot {
925 int op;
926 space *olddot, *newdot;
927 };
928
929 enum { MD_CHECK, MD_MOVE };
930
931 static int movedot_cb(game_state *state, space *tile, void *vctx)
932 {
933 struct movedot *md = (struct movedot *)vctx;
934 space *newopp = NULL;
935
936 assert(tile->type == s_tile);
937 assert(md->olddot && md->newdot);
938
939 if (!(tile->flags & F_TILE_ASSOC)) return 0;
940 if (tile->dotx != md->olddot->x || tile->doty != md->olddot->y)
941 return 0;
942
943 newopp = space_opposite_dot(state, tile, md->newdot);
944
945 switch (md->op) {
946 case MD_CHECK:
947 /* If the tile is associated with the old dot, check its
948 * opposite wrt the _new_ dot is empty or same assoc. */
949 if (!newopp) return -1; /* no new opposite */
950 if (newopp->flags & F_TILE_ASSOC) {
951 if (newopp->dotx != md->olddot->x ||
952 newopp->doty != md->olddot->y)
953 return -1; /* associated, but wrong dot. */
954 }
955 break;
956
957 case MD_MOVE:
958 /* Move dot associations: anything that was associated
959 * with the old dot, and its opposite wrt the new dot,
960 * become associated with the new dot. */
961 assert(newopp);
962 debug(("Associating %d,%d and %d,%d with new dot %d,%d.\n",
963 tile->x, tile->y, newopp->x, newopp->y,
964 md->newdot->x, md->newdot->y));
965 add_assoc(state, tile, md->newdot);
966 add_assoc(state, newopp, md->newdot);
967 return 1; /* we did something! */
968 }
969 return 0;
970 }
971
972 /* For the given dot, first see if we could expand it into all the given
973 * extra spaces (by checking for empty spaces on the far side), and then
974 * see if we can move the dot to shift the CoG to include the new spaces.
975 */
976 static int dot_expand_or_move(game_state *state, space *dot,
977 space **toadd, int nadd)
978 {
979 space *tileopp;
980 int i, ret, nnew, cx, cy;
981 struct movedot md;
982
983 debug(("dot_expand_or_move: %d tiles for dot %d,%d\n",
984 nadd, dot->x, dot->y));
985 for (i = 0; i < nadd; i++)
986 debug(("dot_expand_or_move: dot %d,%d\n",
987 toadd[i]->x, toadd[i]->y));
988 assert(dot->flags & F_DOT);
989
990 /* First off, could we just expand the current dot's tile to cover
991 * the space(s) passed in and their opposites? */
992 for (i = 0; i < nadd; i++) {
993 tileopp = space_opposite_dot(state, toadd[i], dot);
994 if (!tileopp) goto noexpand;
995 if (tileopp->flags & F_TILE_ASSOC) goto noexpand;
996 }
997 /* OK, all spaces have valid empty opposites: associate spaces and
998 * opposites with our dot. */
999 for (i = 0; i < nadd; i++) {
1000 tileopp = space_opposite_dot(state, toadd[i], dot);
1001 add_assoc(state, toadd[i], dot);
1002 add_assoc(state, tileopp, dot);
1003 debug(("Added associations %d,%d and %d,%d --> %d,%d\n",
1004 toadd[i]->x, toadd[i]->y,
1005 tileopp->x, tileopp->y,
1006 dot->x, dot->y));
1007 dbg_state(state);
1008 }
1009 return 1;
1010
1011 noexpand:
1012 /* Otherwise, try to move dot so as to encompass given spaces: */
1013 /* first, alculate the 'centre of gravity' of the new dot. */
1014 nnew = dot->nassoc + nadd; /* number of tiles assoc. with new dot. */
1015 cx = dot->x * dot->nassoc;
1016 cy = dot->y * dot->nassoc;
1017 for (i = 0; i < nadd; i++) {
1018 cx += toadd[i]->x;
1019 cy += toadd[i]->y;
1020 }
1021 /* If the CoG isn't a whole number, it's not possible. */
1022 if ((cx % nnew) != 0 || (cy % nnew) != 0) {
1023 debug(("Unable to move dot %d,%d, CoG not whole number.\n",
1024 dot->x, dot->y));
1025 return 0;
1026 }
1027 cx /= nnew; cy /= nnew;
1028
1029 /* Check whether all spaces in the old tile would have a good
1030 * opposite wrt the new dot. */
1031 md.olddot = dot;
1032 md.newdot = &SPACE(state, cx, cy);
1033 md.op = MD_CHECK;
1034 ret = foreach_tile(state, movedot_cb, IMPOSSIBLE_QUITS, &md);
1035 if (ret == -1) {
1036 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1037 dot->x, dot->y));
1038 return 0;
1039 }
1040 /* Also check whether all spaces we're adding would have a good
1041 * opposite wrt the new dot. */
1042 for (i = 0; i < nadd; i++) {
1043 tileopp = space_opposite_dot(state, toadd[i], md.newdot);
1044 if (tileopp && (tileopp->flags & F_TILE_ASSOC) &&
1045 (tileopp->dotx != dot->x || tileopp->doty != dot->y)) {
1046 tileopp = NULL;
1047 }
1048 if (!tileopp) {
1049 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1050 dot->x, dot->y));
1051 return 0;
1052 }
1053 }
1054
1055 /* If we've got here, we're ok. First, associate all of 'toadd'
1056 * with the _old_ dot (so they'll get fixed up, with their opposites,
1057 * in the next step). */
1058 for (i = 0; i < nadd; i++) {
1059 debug(("Associating to-add %d,%d with old dot %d,%d.\n",
1060 toadd[i]->x, toadd[i]->y, dot->x, dot->y));
1061 add_assoc(state, toadd[i], dot);
1062 }
1063
1064 /* Finally, move the dot and fix up all the old associations. */
1065 debug(("Moving dot at %d,%d to %d,%d\n",
1066 dot->x, dot->y, md.newdot->x, md.newdot->y));
1067 remove_dot(dot);
1068 add_dot(md.newdot);
1069
1070 md.op = MD_MOVE;
1071 ret = foreach_tile(state, movedot_cb, 0, &md);
1072 assert(ret == 1);
1073 dbg_state(state);
1074
1075 return 1;
1076 }
1077
1078 /* Hard-code to a max. of 2x2 squares, for speed (less malloc) */
1079 #define MAX_TOADD 4
1080 #define MAX_OUTSIDE 8
1081
1082 #define MAX_TILE_PERC 20
1083
1084 static int generate_try_block(game_state *state, random_state *rs,
1085 int x1, int y1, int x2, int y2)
1086 {
1087 int x, y, nadd = 0, nout = 0, i, maxsz;
1088 space *sp, *toadd[MAX_TOADD], *outside[MAX_OUTSIDE], *dot;
1089
1090 if (!INGRID(state, x1, y1) || !INGRID(state, x2, y2)) return 0;
1091
1092 /* We limit the maximum size of tiles to be ~2*sqrt(area); so,
1093 * a 5x5 grid shouldn't have anything >10 tiles, a 20x20 grid
1094 * nothing >40 tiles. */
1095 maxsz = (int)sqrt((double)(state->w * state->h)) * 2;
1096 debug(("generate_try_block, maxsz %d\n", maxsz));
1097
1098 /* Make a static list of the spaces; if any space is already
1099 * associated then quit immediately. */
1100 for (x = x1; x <= x2; x += 2) {
1101 for (y = y1; y <= y2; y += 2) {
1102 assert(nadd < MAX_TOADD);
1103 sp = &SPACE(state, x, y);
1104 assert(sp->type == s_tile);
1105 if (sp->flags & F_TILE_ASSOC) return 0;
1106 toadd[nadd++] = sp;
1107 }
1108 }
1109
1110 /* Make a list of the spaces outside of our block, and shuffle it. */
1111 #define OUTSIDE(x, y) do { \
1112 if (INGRID(state, (x), (y))) { \
1113 assert(nout < MAX_OUTSIDE); \
1114 outside[nout++] = &SPACE(state, (x), (y)); \
1115 } \
1116 } while(0)
1117 for (x = x1; x <= x2; x += 2) {
1118 OUTSIDE(x, y1-2);
1119 OUTSIDE(x, y2+2);
1120 }
1121 for (y = y1; y <= y2; y += 2) {
1122 OUTSIDE(x1-2, y);
1123 OUTSIDE(x2+2, y);
1124 }
1125 shuffle(outside, nout, sizeof(space *), rs);
1126
1127 for (i = 0; i < nout; i++) {
1128 if (!(outside[i]->flags & F_TILE_ASSOC)) continue;
1129 dot = &SPACE(state, outside[i]->dotx, outside[i]->doty);
1130 if (dot->nassoc >= maxsz) {
1131 debug(("Not adding to dot %d,%d, large enough (%d) already.\n",
1132 dot->x, dot->y, dot->nassoc));
1133 continue;
1134 }
1135 if (dot_expand_or_move(state, dot, toadd, nadd)) return 1;
1136 }
1137 return 0;
1138 }
1139
1140 #ifdef STANDALONE_SOLVER
1141 int maxtries;
1142 #define MAXTRIES maxtries
1143 #else
1144 #define MAXTRIES 50
1145 #endif
1146
1147 static int solver_obvious_dot(game_state *state,space *dot);
1148
1149 #define GP_DOTS 1
1150
1151 static void generate_pass(game_state *state, random_state *rs, int *scratch,
1152 int perc, unsigned int flags)
1153 {
1154 int sz = state->sx*state->sy, nspc, i, ret;
1155
1156 shuffle(scratch, sz, sizeof(int), rs);
1157
1158 /* This bug took me a, er, little while to track down. On PalmOS,
1159 * which has 16-bit signed ints, puzzles over about 9x9 started
1160 * failing to generate because the nspc calculation would start
1161 * to overflow, causing the dots not to be filled in properly. */
1162 nspc = (int)(((long)perc * (long)sz) / 100L);
1163 debug(("generate_pass: %d%% (%d of %dx%d) squares, flags 0x%x\n",
1164 perc, nspc, state->sx, state->sy, flags));
1165
1166 for (i = 0; i < nspc; i++) {
1167 space *sp = &state->grid[scratch[i]];
1168 int x1 = sp->x, y1 = sp->y, x2 = sp->x, y2 = sp->y;
1169
1170 if (sp->type == s_edge) {
1171 if (IS_VERTICAL_EDGE(sp->x)) {
1172 x1--; x2++;
1173 } else {
1174 y1--; y2++;
1175 }
1176 }
1177 if (sp->type != s_vertex) {
1178 /* heuristic; expanding from vertices tends to generate lots of
1179 * too-big regions of tiles. */
1180 if (generate_try_block(state, rs, x1, y1, x2, y2))
1181 continue; /* we expanded successfully. */
1182 }
1183
1184 if (!(flags & GP_DOTS)) continue;
1185
1186 if ((sp->type == s_edge) && (i % 2)) {
1187 debug(("Omitting edge %d,%d as half-of.\n", sp->x, sp->y));
1188 continue;
1189 }
1190
1191 /* If we've got here we might want to put a dot down. Check
1192 * if we can, and add one if so. */
1193 if (dot_is_possible(state, sp, 0)) {
1194 add_dot(sp);
1195 ret = solver_obvious_dot(state, sp);
1196 assert(ret != -1);
1197 debug(("Added dot (and obvious associations) at %d,%d\n",
1198 sp->x, sp->y));
1199 dbg_state(state);
1200 }
1201 }
1202 dbg_state(state);
1203 }
1204
1205 static int solver_state(game_state *state, int maxdiff);
1206
1207 static char *new_game_desc(game_params *params, random_state *rs,
1208 char **aux, int interactive)
1209 {
1210 game_state *state = blank_game(params->w, params->h), *copy;
1211 char *desc;
1212 int *scratch, sz = state->sx*state->sy, i;
1213 int diff, ntries = 0;
1214
1215 /* Random list of squares to try and process, one-by-one. */
1216 scratch = snewn(sz, int);
1217 for (i = 0; i < sz; i++) scratch[i] = i;
1218
1219 generate:
1220 clear_game(state, 1);
1221 ntries++;
1222
1223 //generate_pass(state, rs, scratch, 10, GP_DOTS);
1224 //generate_pass(state, rs, scratch, 100, 0);
1225 generate_pass(state, rs, scratch, 100, GP_DOTS);
1226
1227 game_update_dots(state);
1228
1229 #ifdef DEBUGGING
1230 {
1231 char *tmp = encode_game(state);
1232 debug(("new_game_desc state %dx%d:%s\n", params->w, params->h, tmp));
1233 sfree(tmp);
1234 }
1235 #endif
1236
1237 copy = dup_game(state);
1238 clear_game(copy, 0);
1239 dbg_state(copy);
1240 diff = solver_state(copy, params->diff);
1241 free_game(copy);
1242
1243 assert(diff != DIFF_IMPOSSIBLE);
1244 if (diff != params->diff) {
1245 if (ntries < MAXTRIES) goto generate;
1246 }
1247
1248 desc = encode_game(state);
1249 #ifndef STANDALONE_SOLVER
1250 debug(("new_game_desc generated: \n"));
1251 dbg_state(state);
1252 #endif
1253
1254 free_game(state);
1255 sfree(scratch);
1256
1257 return desc;
1258 }
1259
1260 static int solver_obvious(game_state *state);
1261
1262 static int dots_too_close(game_state *state)
1263 {
1264 /* Quick-and-dirty check, using half the solver:
1265 * solver_obvious will only fail if the dots are
1266 * too close together, so dot-proximity associations
1267 * overlap. */
1268 game_state *tmp = dup_game(state);
1269 int ret = solver_obvious(tmp);
1270 free_game(tmp);
1271 return (ret == -1) ? 1 : 0;
1272 }
1273
1274 static game_state *load_game(game_params *params, char *desc,
1275 char **why_r)
1276 {
1277 game_state *state = blank_game(params->w, params->h);
1278 char *why = NULL;
1279 int i, x, y, n;
1280 unsigned int df;
1281
1282 i = 0;
1283 while (*desc) {
1284 n = *desc++;
1285 if (n == 'z') {
1286 i += 25;
1287 continue;
1288 }
1289 if (n >= 'a' && n <= 'y') {
1290 i += n - 'a';
1291 df = 0;
1292 } else if (n >= 'A' && n <= 'Y') {
1293 i += n - 'A';
1294 df = F_DOT_BLACK;
1295 } else {
1296 why = "Invalid characters in game description"; goto fail;
1297 }
1298 /* if we got here we incremented i and have a dot to add. */
1299 y = (i / (state->sx-2)) + 1;
1300 x = (i % (state->sx-2)) + 1;
1301 if (!INUI(state, x, y)) {
1302 why = "Too much data to fit in grid"; goto fail;
1303 }
1304 add_dot(&SPACE(state, x, y));
1305 SPACE(state, x, y).flags |= df;
1306 i++;
1307 }
1308 game_update_dots(state);
1309
1310 if (dots_too_close(state)) {
1311 why = "Dots too close together"; goto fail;
1312 }
1313
1314 return state;
1315
1316 fail:
1317 free_game(state);
1318 if (why_r) *why_r = why;
1319 return NULL;
1320 }
1321
1322 static char *validate_desc(game_params *params, char *desc)
1323 {
1324 char *why = NULL;
1325 game_state *dummy = load_game(params, desc, &why);
1326 if (dummy) {
1327 free_game(dummy);
1328 assert(!why);
1329 } else
1330 assert(why);
1331 return why;
1332 }
1333
1334 static game_state *new_game(midend *me, game_params *params, char *desc)
1335 {
1336 game_state *state = load_game(params, desc, NULL);
1337 if (!state) {
1338 assert("Unable to load ?validated game.");
1339 return NULL;
1340 }
1341 #ifdef EDITOR
1342 state->me = me;
1343 #endif
1344 return state;
1345 }
1346
1347 /* ----------------------------------------------------------
1348 * Solver and all its little wizards.
1349 */
1350
1351 int solver_recurse_depth;
1352
1353 typedef struct solver_ctx {
1354 game_state *state;
1355 int sz; /* state->sx * state->sy */
1356 space **scratch; /* size sz */
1357
1358 } solver_ctx;
1359
1360 static solver_ctx *new_solver(game_state *state)
1361 {
1362 solver_ctx *sctx = snew(solver_ctx);
1363 sctx->state = state;
1364 sctx->sz = state->sx*state->sy;
1365 sctx->scratch = snewn(sctx->sz, space *);
1366 return sctx;
1367 }
1368
1369 static void free_solver(solver_ctx *sctx)
1370 {
1371 sfree(sctx->scratch);
1372 sfree(sctx);
1373 }
1374
1375 /* Solver ideas so far:
1376 *
1377 * For any empty space, work out how many dots it could associate
1378 * with:
1379 * it needs line-of-sight
1380 * it needs an empty space on the far side
1381 * any adjacent lines need corresponding line possibilities.
1382 */
1383
1384 /* The solver_ctx should keep a list of dot positions, for quicker looping.
1385 *
1386 * Solver techniques, in order of difficulty:
1387 * obvious adjacency to dots
1388 * transferring tiles to opposite side
1389 * transferring lines to opposite side
1390 * one possible dot for a given tile based on opposite availability
1391 * tile with 3 definite edges next to an associated tile must associate
1392 with same dot.
1393 *
1394 * one possible dot for a given tile based on line-of-sight
1395 */
1396
1397 static int solver_add_assoc(game_state *state, space *tile, int dx, int dy,
1398 const char *why)
1399 {
1400 space *dot, *tile_opp;
1401
1402 dot = &SPACE(state, dx, dy);
1403 tile_opp = space_opposite_dot(state, tile, dot);
1404
1405 assert(tile->type == s_tile);
1406 if (tile->flags & F_TILE_ASSOC) {
1407 if ((tile->dotx != dx) || (tile->doty != dy)) {
1408 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1409 "already --> %d,%d.\n",
1410 solver_recurse_depth*4, "",
1411 tile->x, tile->y, dx, dy, why,
1412 tile->dotx, tile->doty));
1413 return -1;
1414 }
1415 return 0; /* no-op */
1416 }
1417 if (!tile_opp) {
1418 solvep(("%*s%d,%d --> %d,%d impossible, no opposite tile.\n",
1419 solver_recurse_depth*4, "", tile->x, tile->y, dx, dy));
1420 return -1;
1421 }
1422 if (tile_opp->flags & F_TILE_ASSOC &&
1423 (tile_opp->dotx != dx || tile_opp->doty != dy)) {
1424 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1425 "opposite already --> %d,%d.\n",
1426 solver_recurse_depth*4, "",
1427 tile->x, tile->y, dx, dy, why,
1428 tile_opp->dotx, tile_opp->doty));
1429 return -1;
1430 }
1431
1432 add_assoc(state, tile, dot);
1433 add_assoc(state, tile_opp, dot);
1434 solvep(("%*sSetting %d,%d --> %d,%d (%s).\n",
1435 solver_recurse_depth*4, "",
1436 tile->x, tile->y,dx, dy, why));
1437 solvep(("%*sSetting %d,%d --> %d,%d (%s, opposite).\n",
1438 solver_recurse_depth*4, "",
1439 tile_opp->x, tile_opp->y, dx, dy, why));
1440 return 1;
1441 }
1442
1443 static int solver_obvious_dot(game_state *state, space *dot)
1444 {
1445 int dx, dy, ret, didsth = 0;
1446 space *tile;
1447
1448 debug(("%*ssolver_obvious_dot for %d,%d.\n",
1449 solver_recurse_depth*4, "", dot->x, dot->y));
1450
1451 assert(dot->flags & F_DOT);
1452 for (dx = -1; dx <= 1; dx++) {
1453 for (dy = -1; dy <= 1; dy++) {
1454 if (!INGRID(state, dot->x+dx, dot->y+dy)) continue;
1455
1456 tile = &SPACE(state, dot->x+dx, dot->y+dy);
1457 if (tile->type == s_tile) {
1458 ret = solver_add_assoc(state, tile, dot->x, dot->y,
1459 "next to dot");
1460 if (ret < 0) return -1;
1461 if (ret > 0) didsth = 1;
1462 }
1463 }
1464 }
1465 return didsth;
1466 }
1467
1468 static int solver_obvious(game_state *state)
1469 {
1470 int i, didsth = 0, ret;
1471
1472 debug(("%*ssolver_obvious.\n", solver_recurse_depth*4, ""));
1473
1474 for (i = 0; i < state->ndots; i++) {
1475 ret = solver_obvious_dot(state, state->dots[i]);
1476 if (ret < 0) return -1;
1477 if (ret > 0) didsth = 1;
1478 }
1479 return didsth;
1480 }
1481
1482 static int solver_lines_opposite_cb(game_state *state, space *edge, void *ctx)
1483 {
1484 int didsth = 0, n, dx, dy;
1485 space *tiles[2], *tile_opp, *edge_opp;
1486
1487 assert(edge->type == s_edge);
1488
1489 tiles_from_edge(state, edge, tiles);
1490
1491 /* if tiles[0] && tiles[1] && they're both associated
1492 * and they're both associated with different dots,
1493 * ensure the line is set. */
1494 if (!(edge->flags & F_EDGE_SET) &&
1495 tiles[0] && tiles[1] &&
1496 (tiles[0]->flags & F_TILE_ASSOC) &&
1497 (tiles[1]->flags & F_TILE_ASSOC) &&
1498 (tiles[0]->dotx != tiles[1]->dotx ||
1499 tiles[0]->doty != tiles[1]->doty)) {
1500 /* No edge, but the two adjacent tiles are both
1501 * associated with different dots; add the edge. */
1502 solvep(("%*sSetting edge %d,%d - tiles different dots.\n",
1503 solver_recurse_depth*4, "", edge->x, edge->y));
1504 edge->flags |= F_EDGE_SET;
1505 didsth = 1;
1506 }
1507
1508 if (!(edge->flags & F_EDGE_SET)) return didsth;
1509 for (n = 0; n < 2; n++) {
1510 if (!tiles[n]) continue;
1511 assert(tiles[n]->type == s_tile);
1512 if (!(tiles[n]->flags & F_TILE_ASSOC)) continue;
1513
1514 tile_opp = tile_opposite(state, tiles[n]);
1515 if (!tile_opp) {
1516 solvep(("%*simpossible: edge %d,%d has assoc. tile %d,%d"
1517 " with no opposite.\n",
1518 solver_recurse_depth*4, "",
1519 edge->x, edge->y, tiles[n]->x, tiles[n]->y));
1520 /* edge of tile has no opposite edge (off grid?);
1521 * this is impossible. */
1522 return -1;
1523 }
1524
1525 dx = tiles[n]->x - edge->x;
1526 dy = tiles[n]->y - edge->y;
1527 assert(INGRID(state, tile_opp->x+dx, tile_opp->y+dy));
1528 edge_opp = &SPACE(state, tile_opp->x+dx, tile_opp->y+dy);
1529 if (!(edge_opp->flags & F_EDGE_SET)) {
1530 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n",
1531 solver_recurse_depth*4, "",
1532 tile_opp->x-dx, tile_opp->y-dy, edge->x, edge->y));
1533 edge_opp->flags |= F_EDGE_SET;
1534 didsth = 1;
1535 }
1536 }
1537 return didsth;
1538 }
1539
1540 static int solver_spaces_oneposs_cb(game_state *state, space *tile, void *ctx)
1541 {
1542 int n, eset, ret;
1543 struct space *edgeadj[4], *tileadj[4];
1544 int dotx, doty;
1545
1546 assert(tile->type == s_tile);
1547 if (tile->flags & F_TILE_ASSOC) return 0;
1548
1549 adjacencies(state, tile, edgeadj, tileadj);
1550
1551 /* Empty tile. If each edge is either set, or associated with
1552 * the same dot, we must also associate with dot. */
1553 eset = 0; dotx = -1; doty = -1;
1554 for (n = 0; n < 4; n++) {
1555 assert(edgeadj[n]);
1556 assert(edgeadj[n]->type == s_edge);
1557 if (edgeadj[n]->flags & F_EDGE_SET) {
1558 eset++;
1559 } else {
1560 assert(tileadj[n]);
1561 assert(tileadj[n]->type == s_tile);
1562
1563 /* If an adjacent tile is empty we can't make any deductions.*/
1564 if (!(tileadj[n]->flags & F_TILE_ASSOC))
1565 return 0;
1566
1567 /* If an adjacent tile is assoc. with a different dot
1568 * we can't make any deductions. */
1569 if (dotx != -1 && doty != -1 &&
1570 (tileadj[n]->dotx != dotx ||
1571 tileadj[n]->doty != doty))
1572 return 0;
1573
1574 dotx = tileadj[n]->dotx;
1575 doty = tileadj[n]->doty;
1576 }
1577 }
1578 if (eset == 4) {
1579 solvep(("%*simpossible: empty tile %d,%d has 4 edges\n",
1580 solver_recurse_depth*4, "",
1581 tile->x, tile->y));
1582 return -1;
1583 }
1584 assert(dotx != -1 && doty != -1);
1585
1586 ret = solver_add_assoc(state, tile, dotx, doty, "rest are edges");
1587 if (ret == -1) return -1;
1588 assert(ret != 0); /* really should have done something. */
1589
1590 return 1;
1591 }
1592
1593 /* Improved algorithm for tracking line-of-sight from dots, and not spaces.
1594 *
1595 * The solver_ctx already stores a list of dots: the algorithm proceeds by
1596 * expanding outwards from each dot in turn, expanding first to the boundary
1597 * of its currently-connected tile and then to all empty tiles that could see
1598 * it. Empty tiles will be flagged with a 'can see dot <x,y>' sticker.
1599 *
1600 * Expansion will happen by (symmetrically opposite) pairs of squares; if
1601 * a square hasn't an opposite number there's no point trying to expand through
1602 * it. Empty tiles will therefore also be tagged in pairs.
1603 *
1604 * If an empty tile already has a 'can see dot <x,y>' tag from a previous dot,
1605 * it (and its partner) gets untagged (or, rather, a 'can see two dots' tag)
1606 * because we're looking for single-dot possibilities.
1607 *
1608 * Once we've gone through all the dots, any which still have a 'can see dot'
1609 * tag get associated with that dot (because it must have been the only one);
1610 * any without any tag (i.e. that could see _no_ dots) cause an impossibility
1611 * marked.
1612 *
1613 * The expansion will happen each time with a stored list of (space *) pairs,
1614 * rather than a mark-and-sweep idea; that's horrifically inefficient.
1615 *
1616 * expansion algorithm:
1617 *
1618 * * allocate list of (space *) the size of s->sx*s->sy.
1619 * * allocate second grid for (flags, dotx, doty) size of sx*sy.
1620 *
1621 * clear second grid (flags = 0, all dotx and doty = 0)
1622 * flags: F_REACHABLE, F_MULTIPLE
1623 *
1624 *
1625 * * for each dot, start with one pair of tiles that are associated with it --
1626 * * vertex --> (dx+1, dy+1), (dx-1, dy-1)
1627 * * edge --> (adj1, adj2)
1628 * * tile --> (tile, tile) ???
1629 * * mark that pair of tiles with F_MARK, clear all other F_MARKs.
1630 * * add two tiles to start of list.
1631 *
1632 * set start = 0, end = next = 2
1633 *
1634 * from (start to end-1, step 2) {
1635 * * we have two tiles (t1, t2), opposites wrt our dot.
1636 * * for each (at1) sensible adjacent tile to t1 (i.e. not past an edge):
1637 * * work out at2 as the opposite to at1
1638 * * assert at1 and at2 have the same F_MARK values.
1639 * * if at1 & F_MARK ignore it (we've been there on a previous sweep)
1640 * * if either are associated with a different dot
1641 * * mark both with F_MARK (so we ignore them later)
1642 * * otherwise (assoc. with our dot, or empty):
1643 * * mark both with F_MARK
1644 * * add their space * values to the end of the list, set next += 2.
1645 * }
1646 *
1647 * if (end == next)
1648 * * we didn't add any new squares; exit the loop.
1649 * else
1650 * * set start = next+1, end = next. go round again
1651 *
1652 * We've finished expanding from the dot. Now, for each square we have
1653 * in our list (--> each square with F_MARK):
1654 * * if the tile is empty:
1655 * * if F_REACHABLE was already set
1656 * * set F_MULTIPLE
1657 * * otherwise
1658 * * set F_REACHABLE, set dotx and doty to our dot.
1659 *
1660 * Then, continue the whole thing for each dot in turn.
1661 *
1662 * Once we've done for each dot, go through the entire grid looking for
1663 * empty tiles: for each empty tile:
1664 * if F_REACHABLE and not F_MULTIPLE, set that dot (and its double)
1665 * if !F_REACHABLE, return as impossible.
1666 *
1667 */
1668
1669 /* Returns 1 if this tile is either already associated with this dot,
1670 * or blank. */
1671 static int solver_expand_checkdot(space *tile, space *dot)
1672 {
1673 if (!(tile->flags & F_TILE_ASSOC)) return 1;
1674 if (tile->dotx == dot->x && tile->doty == dot->y) return 1;
1675 return 0;
1676 }
1677
1678 static void solver_expand_fromdot(game_state *state, space *dot, solver_ctx *sctx)
1679 {
1680 int i, j, x, y, start, end, next;
1681 space *sp;
1682
1683 /* Clear the grid of the (space) flags we'll use. */
1684
1685 /* This is well optimised; analysis showed that:
1686 for (i = 0; i < sctx->sz; i++) state->grid[i].flags &= ~F_MARK;
1687 took up ~85% of the total function time! */
1688 for (y = 1; y < state->sy; y += 2) {
1689 sp = &SPACE(state, 1, y);
1690 for (x = 1; x < state->sx; x += 2, sp += 2)
1691 sp->flags &= ~F_MARK;
1692 }
1693
1694 /* Seed the list of marked squares with two that must be associated
1695 * with our dot (possibly the same space) */
1696 if (dot->type == s_tile) {
1697 sctx->scratch[0] = sctx->scratch[1] = dot;
1698 } else if (dot->type == s_edge) {
1699 tiles_from_edge(state, dot, sctx->scratch);
1700 } else if (dot->type == s_vertex) {
1701 /* pick two of the opposite ones arbitrarily. */
1702 sctx->scratch[0] = &SPACE(state, dot->x-1, dot->y-1);
1703 sctx->scratch[1] = &SPACE(state, dot->x+1, dot->y+1);
1704 }
1705 assert(sctx->scratch[0]->flags & F_TILE_ASSOC);
1706 assert(sctx->scratch[1]->flags & F_TILE_ASSOC);
1707
1708 sctx->scratch[0]->flags |= F_MARK;
1709 sctx->scratch[1]->flags |= F_MARK;
1710
1711 debug(("%*sexpand from dot %d,%d seeded with %d,%d and %d,%d.\n",
1712 solver_recurse_depth*4, "", dot->x, dot->y,
1713 sctx->scratch[0]->x, sctx->scratch[0]->y,
1714 sctx->scratch[1]->x, sctx->scratch[1]->y));
1715
1716 start = 0; end = 2; next = 2;
1717
1718 expand:
1719 debug(("%*sexpand: start %d, end %d, next %d\n",
1720 solver_recurse_depth*4, "", start, end, next));
1721 for (i = start; i < end; i += 2) {
1722 space *t1 = sctx->scratch[i]/*, *t2 = sctx->scratch[i+1]*/;
1723 space *edges[4], *tileadj[4], *tileadj2;
1724
1725 adjacencies(state, t1, edges, tileadj);
1726
1727 for (j = 0; j < 4; j++) {
1728 assert(edges[j]);
1729 if (edges[j]->flags & F_EDGE_SET) continue;
1730 assert(tileadj[j]);
1731
1732 if (tileadj[j]->flags & F_MARK) continue; /* seen before. */
1733
1734 /* We have a tile adjacent to t1; find its opposite. */
1735 tileadj2 = space_opposite_dot(state, tileadj[j], dot);
1736 if (!tileadj2) {
1737 debug(("%*sMarking %d,%d, no opposite.\n",
1738 solver_recurse_depth*4, "",
1739 tileadj[j]->x, tileadj[j]->y));
1740 tileadj[j]->flags |= F_MARK;
1741 continue; /* no opposite, so mark for next time. */
1742 }
1743 /* If the tile had an opposite we should have either seen both of
1744 * these, or neither of these, before. */
1745 assert(!(tileadj2->flags & F_MARK));
1746
1747 if (solver_expand_checkdot(tileadj[j], dot) &&
1748 solver_expand_checkdot(tileadj2, dot)) {
1749 /* Both tiles could associate with this dot; add them to
1750 * our list. */
1751 debug(("%*sAdding %d,%d and %d,%d to possibles list.\n",
1752 solver_recurse_depth*4, "",
1753 tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y));
1754 sctx->scratch[next++] = tileadj[j];
1755 sctx->scratch[next++] = tileadj2;
1756 }
1757 /* Either way, we've seen these tiles already so mark them. */
1758 debug(("%*sMarking %d,%d and %d,%d.\n",
1759 solver_recurse_depth*4, "",
1760 tileadj[j]->x, tileadj[j]->y, tileadj2->x, tileadj2->y));
1761 tileadj[j]->flags |= F_MARK;
1762 tileadj2->flags |= F_MARK;
1763 }
1764 }
1765 if (next > end) {
1766 /* We added more squares; go back and try again. */
1767 start = end; end = next; goto expand;
1768 }
1769
1770 /* We've expanded as far as we can go. Now we update the main flags
1771 * on all tiles we've expanded into -- if they were empty, we have
1772 * found possible associations for this dot. */
1773 for (i = 0; i < end; i++) {
1774 if (sctx->scratch[i]->flags & F_TILE_ASSOC) continue;
1775 if (sctx->scratch[i]->flags & F_REACHABLE) {
1776 /* This is (at least) the second dot this tile could
1777 * associate with. */
1778 debug(("%*sempty tile %d,%d could assoc. other dot %d,%d\n",
1779 solver_recurse_depth*4, "",
1780 sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y));
1781 sctx->scratch[i]->flags |= F_MULTIPLE;
1782 } else {
1783 /* This is the first (possibly only) dot. */
1784 debug(("%*sempty tile %d,%d could assoc. 1st dot %d,%d\n",
1785 solver_recurse_depth*4, "",
1786 sctx->scratch[i]->x, sctx->scratch[i]->y, dot->x, dot->y));
1787 sctx->scratch[i]->flags |= F_REACHABLE;
1788 sctx->scratch[i]->dotx = dot->x;
1789 sctx->scratch[i]->doty = dot->y;
1790 }
1791 }
1792 dbg_state(state);
1793 }
1794
1795 static int solver_expand_postcb(game_state *state, space *tile, void *ctx)
1796 {
1797 assert(tile->type == s_tile);
1798
1799 if (tile->flags & F_TILE_ASSOC) return 0;
1800
1801 if (!(tile->flags & F_REACHABLE)) {
1802 solvep(("%*simpossible: space (%d,%d) can reach no dots.\n",
1803 solver_recurse_depth*4, "", tile->x, tile->y));
1804 return -1;
1805 }
1806 if (tile->flags & F_MULTIPLE) return 0;
1807
1808 return solver_add_assoc(state, tile, tile->dotx, tile->doty,
1809 "single possible dot after expansion");
1810 }
1811
1812 static int solver_expand_dots(game_state *state, solver_ctx *sctx)
1813 {
1814 int i;
1815
1816 for (i = 0; i < sctx->sz; i++)
1817 state->grid[i].flags &= ~(F_REACHABLE|F_MULTIPLE);
1818
1819 for (i = 0; i < state->ndots; i++)
1820 solver_expand_fromdot(state, state->dots[i], sctx);
1821
1822 return foreach_tile(state, solver_expand_postcb, IMPOSSIBLE_QUITS, sctx);
1823 }
1824
1825 struct recurse_ctx {
1826 space *best;
1827 int bestn;
1828 };
1829
1830 static int solver_recurse_cb(game_state *state, space *tile, void *ctx)
1831 {
1832 struct recurse_ctx *rctx = (struct recurse_ctx *)ctx;
1833 int i, n = 0;
1834
1835 assert(tile->type == s_tile);
1836 if (tile->flags & F_TILE_ASSOC) return 0;
1837
1838 /* We're unassociated: count up all the dots we could associate with. */
1839 for (i = 0; i < state->ndots; i++) {
1840 if (dotfortile(state, tile, state->dots[i]))
1841 n++;
1842 }
1843 if (n > rctx->bestn) {
1844 rctx->bestn = n;
1845 rctx->best = tile;
1846 }
1847 return 0;
1848 }
1849
1850 static int solver_state(game_state *state, int maxdiff);
1851
1852 #define MAXRECURSE 5
1853
1854 static int solver_recurse(game_state *state, int maxdiff)
1855 {
1856 int diff = DIFF_IMPOSSIBLE, ret, n, gsz = state->sx * state->sy;
1857 space *ingrid, *outgrid = NULL, *bestopp;
1858 struct recurse_ctx rctx;
1859
1860 if (solver_recurse_depth >= MAXRECURSE) {
1861 solvep(("Limiting recursion to %d, returning.", MAXRECURSE));
1862 return DIFF_UNFINISHED;
1863 }
1864
1865 /* Work out the cell to recurse on; go through all unassociated tiles
1866 * and find which one has the most possible dots it could associate
1867 * with. */
1868 rctx.best = NULL;
1869 rctx.bestn = 0;
1870
1871 foreach_tile(state, solver_recurse_cb, 0, &rctx);
1872 if (rctx.bestn == 0) return DIFF_IMPOSSIBLE; /* or assert? */
1873 assert(rctx.best);
1874
1875 solvep(("%*sRecursing around %d,%d, with %d possible dots.\n",
1876 solver_recurse_depth*4, "",
1877 rctx.best->x, rctx.best->y, rctx.bestn));
1878
1879 #ifdef STANDALONE_SOLVER
1880 solver_recurse_depth++;
1881 #endif
1882
1883 ingrid = snewn(gsz, struct space);
1884 memcpy(ingrid, state->grid, gsz * sizeof(struct space));
1885
1886 for (n = 0; n < state->ndots; n++) {
1887 memcpy(state->grid, ingrid, gsz * sizeof(struct space));
1888
1889 if (!dotfortile(state, rctx.best, state->dots[n])) continue;
1890
1891 /* set cell (temporarily) pointing to that dot. */
1892 solver_add_assoc(state, rctx.best,
1893 state->dots[n]->x, state->dots[n]->y,
1894 "Attempting for recursion");
1895
1896 ret = solver_state(state, maxdiff);
1897
1898 if (diff == DIFF_IMPOSSIBLE && ret != DIFF_IMPOSSIBLE) {
1899 /* we found our first solved grid; copy it away. */
1900 assert(!outgrid);
1901 outgrid = snewn(gsz, struct space);
1902 memcpy(outgrid, state->grid, gsz * sizeof(struct space));
1903 }
1904 /* reset cell back to unassociated. */
1905 bestopp = tile_opposite(state, rctx.best);
1906 assert(bestopp && bestopp->flags & F_TILE_ASSOC);
1907
1908 remove_assoc(state, rctx.best);
1909 remove_assoc(state, bestopp);
1910
1911 if (ret == DIFF_AMBIGUOUS || ret == DIFF_UNFINISHED)
1912 diff = ret;
1913 else if (ret == DIFF_IMPOSSIBLE)
1914 /* no change */;
1915 else {
1916 /* precisely one solution */
1917 if (diff == DIFF_IMPOSSIBLE)
1918 diff = DIFF_RECURSIVE;
1919 else
1920 diff = DIFF_AMBIGUOUS;
1921 }
1922 /* if we've found >1 solution, or ran out of recursion,
1923 * give up immediately. */
1924 if (diff == DIFF_AMBIGUOUS || diff == DIFF_UNFINISHED)
1925 break;
1926 }
1927
1928 #ifdef STANDALONE_SOLVER
1929 solver_recurse_depth--;
1930 #endif
1931
1932 if (outgrid) {
1933 /* we found (at least one) soln; copy it back to state */
1934 memcpy(state->grid, outgrid, gsz * sizeof(struct space));
1935 sfree(outgrid);
1936 }
1937 sfree(ingrid);
1938 return diff;
1939 }
1940
1941 static int solver_state(game_state *state, int maxdiff)
1942 {
1943 solver_ctx *sctx = new_solver(state);
1944 int ret, diff = DIFF_EASY;
1945
1946 ret = solver_obvious(state);
1947 if (ret < 0) {
1948 diff = DIFF_IMPOSSIBLE;
1949 goto got_result;
1950 }
1951
1952 #define CHECKRET(d) do { \
1953 if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } \
1954 if (ret > 0) { diff = max(diff, (d)); goto cont; } \
1955 } while(0)
1956
1957 while (1) {
1958 cont:
1959 ret = foreach_edge(state, solver_lines_opposite_cb,
1960 IMPOSSIBLE_QUITS, sctx);
1961 CHECKRET(DIFF_EASY);
1962
1963 ret = foreach_tile(state, solver_spaces_oneposs_cb,
1964 IMPOSSIBLE_QUITS, sctx);
1965 CHECKRET(DIFF_EASY);
1966
1967 /* more easy stuff? */
1968
1969 if (maxdiff <= DIFF_EASY)
1970 break;
1971
1972 ret = solver_expand_dots(state, sctx);
1973 CHECKRET(DIFF_HARD);
1974
1975 if (maxdiff <= DIFF_HARD)
1976 break;
1977
1978 /* harder still? */
1979
1980 /* if we reach here, we've made no deductions, so we terminate. */
1981 break;
1982 }
1983
1984 if (check_complete(state, 0)) goto got_result;
1985
1986 diff = (maxdiff >= DIFF_RECURSIVE) ?
1987 solver_recurse(state, maxdiff) : DIFF_UNFINISHED;
1988
1989 got_result:
1990 free_solver(sctx);
1991 #ifndef STANDALONE_SOLVER
1992 debug(("solver_state ends:\n"));
1993 dbg_state(state);
1994 #endif
1995
1996 return diff;
1997 }
1998
1999 #ifndef EDITOR
2000 static char *solve_game(game_state *state, game_state *currstate,
2001 char *aux, char **error)
2002 {
2003 game_state *tosolve;
2004 char *ret;
2005 int i;
2006 int diff;
2007
2008 tosolve = dup_game(currstate);
2009 diff = solver_state(tosolve, DIFF_RECURSIVE);
2010 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2011 debug(("solve_game solved with current state.\n"));
2012 goto solved;
2013 }
2014 free_game(tosolve);
2015
2016 tosolve = dup_game(state);
2017 diff = solver_state(tosolve, DIFF_RECURSIVE);
2018 if (diff != DIFF_UNFINISHED && diff != DIFF_IMPOSSIBLE) {
2019 debug(("solve_game solved with original state.\n"));
2020 goto solved;
2021 }
2022 free_game(tosolve);
2023
2024 return NULL;
2025
2026 solved:
2027 /*
2028 * Clear tile associations: the solution will only include the
2029 * edges.
2030 */
2031 for (i = 0; i < tosolve->sx*tosolve->sy; i++)
2032 tosolve->grid[i].flags &= ~F_TILE_ASSOC;
2033 ret = diff_game(currstate, tosolve, 1);
2034 free_game(tosolve);
2035 return ret;
2036 }
2037 #endif
2038
2039 /* ----------------------------------------------------------
2040 * User interface.
2041 */
2042
2043 struct game_ui {
2044 int dragging;
2045 int dx, dy; /* pixel coords of drag pos. */
2046 int dotx, doty; /* grid coords of dot we're dragging from. */
2047 int srcx, srcy; /* grid coords of drag start */
2048 };
2049
2050 static game_ui *new_ui(game_state *state)
2051 {
2052 game_ui *ui = snew(game_ui);
2053 ui->dragging = FALSE;
2054 return ui;
2055 }
2056
2057 static void free_ui(game_ui *ui)
2058 {
2059 sfree(ui);
2060 }
2061
2062 static char *encode_ui(game_ui *ui)
2063 {
2064 return NULL;
2065 }
2066
2067 static void decode_ui(game_ui *ui, char *encoding)
2068 {
2069 }
2070
2071 static void game_changed_state(game_ui *ui, game_state *oldstate,
2072 game_state *newstate)
2073 {
2074 }
2075
2076 #define FLASH_TIME 0.15F
2077
2078 #define PREFERRED_TILE_SIZE 32
2079 #define TILE_SIZE (ds->tilesize)
2080 #define DOT_SIZE (TILE_SIZE / 4)
2081 #define EDGE_THICKNESS (TILE_SIZE / 16)
2082 #define BORDER TILE_SIZE
2083
2084 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
2085 #define SCOORD(x) ( ((x) * TILE_SIZE)/2 + BORDER )
2086 #define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE )
2087
2088 #define DRAW_WIDTH (BORDER * 2 + ds->w * TILE_SIZE)
2089 #define DRAW_HEIGHT (BORDER * 2 + ds->h * TILE_SIZE)
2090
2091 struct game_drawstate {
2092 int started;
2093 int w, h;
2094 int tilesize;
2095 unsigned long *grid;
2096 int *dx, *dy;
2097 blitter *bl;
2098
2099 int dragging, dragx, dragy;
2100
2101 int *colour_scratch;
2102 };
2103
2104 #define CORNER_TOLERANCE 0.15F
2105 #define CENTRE_TOLERANCE 0.15F
2106
2107 /*
2108 * Round FP coordinates to the centre of the nearest edge.
2109 */
2110 #ifndef EDITOR
2111 static void coord_round_to_edge(float x, float y, int *xr, int *yr)
2112 {
2113 float xs, ys, xv, yv, dx, dy;
2114
2115 /*
2116 * Find the nearest square-centre.
2117 */
2118 xs = (float)floor(x) + 0.5F;
2119 ys = (float)floor(y) + 0.5F;
2120
2121 /*
2122 * Find the nearest grid vertex.
2123 */
2124 xv = (float)floor(x + 0.5F);
2125 yv = (float)floor(y + 0.5F);
2126
2127 /*
2128 * Determine whether the horizontal or vertical edge from that
2129 * vertex alongside that square is closer to us, by comparing
2130 * distances from the square cente.
2131 */
2132 dx = (float)fabs(x - xs);
2133 dy = (float)fabs(y - ys);
2134 if (dx > dy) {
2135 /* Vertical edge: x-coord of corner,
2136 * y-coord of square centre. */
2137 *xr = 2 * (int)xv;
2138 *yr = 1 + 2 * (int)floor(ys);
2139 } else {
2140 /* Horizontal edge: x-coord of square centre,
2141 * y-coord of corner. */
2142 *xr = 1 + 2 * (int)floor(xs);
2143 *yr = 2 * (int)yv;
2144 }
2145 }
2146 #endif
2147
2148 #ifdef EDITOR
2149 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
2150 int x, int y, int button)
2151 {
2152 char buf[80];
2153 int px, py;
2154 struct space *sp;
2155
2156 px = 2*FROMCOORD((float)x) + 0.5;
2157 py = 2*FROMCOORD((float)y) + 0.5;
2158
2159 state->cdiff = -1;
2160
2161 if (button == 'C' || button == 'c') return dupstr("C");
2162
2163 if (button == 'S' || button == 's') {
2164 char *ret;
2165 game_state *tmp = dup_game(state);
2166 state->cdiff = solver_state(tmp, DIFF_RECURSIVE-1);
2167 ret = diff_game(state, tmp, 0);
2168 free_game(tmp);
2169 return ret;
2170 }
2171
2172 if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
2173 if (!INUI(state, px, py)) return NULL;
2174 sp = &SPACE(state, px, py);
2175 if (!dot_is_possible(state, sp, 1)) return NULL;
2176 sprintf(buf, "%c%d,%d",
2177 (char)((button == LEFT_BUTTON) ? 'D' : 'd'), px, py);
2178 return dupstr(buf);
2179 }
2180
2181 return NULL;
2182 }
2183 #else
2184 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
2185 int x, int y, int button)
2186 {
2187 /* UI operations (play mode):
2188 *
2189 * Toggle edge (set/unset) (left-click on edge)
2190 * Associate space with dot (left-drag from dot)
2191 * Unassociate space (left-drag from space off grid)
2192 * Autofill lines around shape? (right-click?)
2193 *
2194 * (edit mode; will clear all lines/associations)
2195 *
2196 * Add or remove dot (left-click)
2197 */
2198 char buf[80];
2199 const char *sep;
2200 int px, py;
2201 struct space *sp, *dot;
2202
2203 if (button == 'H' || button == 'h' ||
2204 button == 'S' || button == 's') {
2205 char *ret;
2206 game_state *tmp = dup_game(state);
2207 if (button == 'H' || button == 'h')
2208 solver_obvious(tmp);
2209 else
2210 solver_state(tmp, DIFF_RECURSIVE-1);
2211 ret = diff_game(state, tmp, 0);
2212 free_game(tmp);
2213 return ret;
2214 }
2215
2216 if (button == LEFT_BUTTON) {
2217 coord_round_to_edge(FROMCOORD((float)x), FROMCOORD((float)y),
2218 &px, &py);
2219
2220 if (!INUI(state, px, py)) return NULL;
2221
2222 sp = &SPACE(state, px, py);
2223 assert(sp->type == s_edge);
2224 {
2225 sprintf(buf, "E%d,%d", px, py);
2226 return dupstr(buf);
2227 }
2228 } else if (button == RIGHT_BUTTON) {
2229 int px1, py1;
2230
2231 px = 2*FROMCOORD((float)x) + 0.5;
2232 py = 2*FROMCOORD((float)y) + 0.5;
2233
2234 dot = NULL;
2235
2236 /*
2237 * If there's a dot anywhere nearby, we pick up an arrow
2238 * pointing at that dot.
2239 */
2240 for (py1 = py-1; py1 <= py+1; py1++)
2241 for (px1 = px-1; px1 <= px+1; px1++) {
2242 if (px1 >= 0 && px1 < state->sx &&
2243 py1 >= 0 && py1 < state->sx &&
2244 x >= SCOORD(px1-1) && x < SCOORD(px1+1) &&
2245 y >= SCOORD(py1-1) && y < SCOORD(py1+1) &&
2246 SPACE(state, px1, py1).flags & F_DOT) {
2247 /*
2248 * Found a dot. Begin a drag from it.
2249 */
2250 dot = &SPACE(state, px1, py1);
2251 ui->srcx = px;
2252 ui->srcy = py;
2253 goto done; /* multi-level break */
2254 }
2255 }
2256
2257 /*
2258 * Otherwise, find the nearest _square_, and pick up the
2259 * same arrow as it's got on it, if any.
2260 */
2261 if (!dot) {
2262 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2263 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2264 if (px >= 0 && px < state->sx && py >= 0 && py < state->sx) {
2265 sp = &SPACE(state, px, py);
2266 if (sp->flags & F_TILE_ASSOC) {
2267 dot = &SPACE(state, sp->dotx, sp->doty);
2268 ui->srcx = px;
2269 ui->srcy = py;
2270 }
2271 }
2272 }
2273
2274 done:
2275 /*
2276 * Now, if we've managed to find a dot, begin a drag.
2277 */
2278 if (dot) {
2279 ui->dragging = TRUE;
2280 ui->dx = x;
2281 ui->dy = y;
2282 ui->dotx = dot->x;
2283 ui->doty = dot->y;
2284 return "";
2285 }
2286 } else if (button == RIGHT_DRAG && ui->dragging) {
2287 /* just move the drag coords. */
2288 ui->dx = x;
2289 ui->dy = y;
2290 return "";
2291 } else if (button == RIGHT_RELEASE && ui->dragging) {
2292 ui->dragging = FALSE;
2293
2294 /*
2295 * Drags are always targeted at a single square.
2296 */
2297 px = 2*FROMCOORD(x+TILE_SIZE) - 1;
2298 py = 2*FROMCOORD(y+TILE_SIZE) - 1;
2299
2300 /*
2301 * Dragging an arrow on to the same square it started from
2302 * is a null move; just update the ui and finish.
2303 */
2304 if (px == ui->srcx && py == ui->srcy)
2305 return "";
2306
2307 sep = "";
2308 buf[0] = '\0';
2309
2310 /*
2311 * Otherwise, we remove the arrow from its starting
2312 * square if we didn't start from a dot...
2313 */
2314 if ((ui->srcx != ui->dotx || ui->srcy != ui->doty) &&
2315 SPACE(state, ui->srcx, ui->srcy).flags & F_TILE_ASSOC) {
2316 sprintf(buf + strlen(buf), "%sU%d,%d", sep, ui->srcx, ui->srcy);
2317 sep = ";";
2318 }
2319
2320 /*
2321 * ... and if the square we're moving it _to_ is valid, we
2322 * add one there instead.
2323 */
2324 if (INUI(state, px, py)) {
2325 sp = &SPACE(state, px, py);
2326
2327 if (!(sp->flags & F_DOT) && !(sp->flags & F_TILE_ASSOC))
2328 sprintf(buf + strlen(buf), "%sA%d,%d,%d,%d",
2329 sep, px, py, ui->dotx, ui->doty);
2330 }
2331
2332 if (buf[0])
2333 return dupstr(buf);
2334 else
2335 return "";
2336 }
2337
2338 return NULL;
2339 }
2340 #endif
2341
2342 static int check_complete_in_play(game_state *state, int *dsf, int *colours)
2343 {
2344 int w = state->w, h = state->h;
2345 int x, y, i, ret;
2346
2347 int free_dsf;
2348 struct sqdata {
2349 int minx, miny, maxx, maxy;
2350 int cx, cy;
2351 int valid, colour;
2352 } *sqdata;
2353
2354 if (!dsf) {
2355 dsf = snew_dsf(w*h);
2356 free_dsf = TRUE;
2357 } else {
2358 dsf_init(dsf, w*h);
2359 free_dsf = FALSE;
2360 }
2361
2362 /*
2363 * During actual game play, completion checking is done on the
2364 * basis of the edges rather than the square associations. So
2365 * first we must go through the grid figuring out the connected
2366 * components into which the edges divide it.
2367 */
2368 for (y = 0; y < h; y++)
2369 for (x = 0; x < w; x++) {
2370 if (y+1 < h && !(SPACE(state, 2*x+1, 2*y+2).flags & F_EDGE_SET))
2371 dsf_merge(dsf, y*w+x, (y+1)*w+x);
2372 if (x+1 < w && !(SPACE(state, 2*x+2, 2*y+1).flags & F_EDGE_SET))
2373 dsf_merge(dsf, y*w+x, y*w+(x+1));
2374 }
2375
2376 /*
2377 * That gives us our connected components. Now, for each
2378 * component, decide whether it's _valid_. A valid component is
2379 * one which:
2380 *
2381 * - is 180-degree rotationally symmetric
2382 * - has a dot at its centre of symmetry
2383 * - has no other dots anywhere within it (including on its
2384 * boundary)
2385 * - contains no internal edges (i.e. edges separating two
2386 * squares which are both part of the component).
2387 */
2388
2389 /*
2390 * First, go through the grid finding the bounding box of each
2391 * component.
2392 */
2393 sqdata = snewn(w*h, struct sqdata);
2394 for (i = 0; i < w*h; i++) {
2395 sqdata[i].minx = w+1;
2396 sqdata[i].miny = h+1;
2397 sqdata[i].maxx = sqdata[i].maxy = -1;
2398 sqdata[i].valid = FALSE;
2399 }
2400 for (y = 0; y < h; y++)
2401 for (x = 0; x < w; x++) {
2402 i = dsf_canonify(dsf, y*w+x);
2403 if (sqdata[i].minx > x)
2404 sqdata[i].minx = x;
2405 if (sqdata[i].maxx < x)
2406 sqdata[i].maxx = x;
2407 if (sqdata[i].miny > y)
2408 sqdata[i].miny = y;
2409 if (sqdata[i].maxy < y)
2410 sqdata[i].maxy = y;
2411 sqdata[i].valid = TRUE;
2412 }
2413
2414 /*
2415 * Now we're in a position to loop over each actual component
2416 * and figure out where its centre of symmetry has to be if
2417 * it's anywhere.
2418 */
2419 for (i = 0; i < w*h; i++)
2420 if (sqdata[i].valid) {
2421 sqdata[i].cx = sqdata[i].minx + sqdata[i].maxx + 1;
2422 sqdata[i].cy = sqdata[i].miny + sqdata[i].maxy + 1;
2423 if (!(SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT))
2424 sqdata[i].valid = FALSE; /* no dot at centre of symmetry */
2425 if (SPACE(state, sqdata[i].cx, sqdata[i].cy).flags & F_DOT_BLACK)
2426 sqdata[i].colour = 2;
2427 else
2428 sqdata[i].colour = 1;
2429 }
2430
2431 /*
2432 * Now we loop over the whole grid again, this time finding
2433 * extraneous dots (any dot which wholly or partially overlaps
2434 * a square and is not at the centre of symmetry of that
2435 * square's component disqualifies the component from validity)
2436 * and extraneous edges (any edge separating two squares
2437 * belonging to the same component also disqualifies that
2438 * component).
2439 */
2440 for (y = 1; y < state->sy-1; y++)
2441 for (x = 1; x < state->sx-1; x++) {
2442 space *sp = &SPACE(state, x, y);
2443
2444 if (sp->flags & F_DOT) {
2445 /*
2446 * There's a dot here. Use it to disqualify any
2447 * component which deserves it.
2448 */
2449 int cx, cy;
2450 for (cy = (y-1) >> 1; cy <= y >> 1; cy++)
2451 for (cx = (x-1) >> 1; cx <= x >> 1; cx++) {
2452 i = dsf_canonify(dsf, cy*w+cx);
2453 if (x != sqdata[i].cx || y != sqdata[i].cy)
2454 sqdata[i].valid = FALSE;
2455 }
2456 }
2457
2458 if (sp->flags & F_EDGE_SET) {
2459 /*
2460 * There's an edge here. Use it to disqualify a
2461 * component if necessary.
2462 */
2463 int cx1 = (x-1) >> 1, cx2 = x >> 1;
2464 int cy1 = (y-1) >> 1, cy2 = y >> 1;
2465 assert((cx1==cx2) ^ (cy1==cy2));
2466 i = dsf_canonify(dsf, cy1*w+cx1);
2467 if (i == dsf_canonify(dsf, cy2*w+cx2))
2468 sqdata[i].valid = FALSE;
2469 }
2470 }
2471
2472 /*
2473 * And finally we test rotational symmetry: for each square in
2474 * the grid, find which component it's in, test that that
2475 * component also has a square in the symmetric position, and
2476 * disqualify it if it doesn't.
2477 */
2478 for (y = 0; y < h; y++)
2479 for (x = 0; x < w; x++) {
2480 int x2, y2;
2481
2482 i = dsf_canonify(dsf, y*w+x);
2483
2484 x2 = sqdata[i].cx - 1 - x;
2485 y2 = sqdata[i].cy - 1 - y;
2486 if (i != dsf_canonify(dsf, y2*w+x2))
2487 sqdata[i].valid = FALSE;
2488 }
2489
2490 /*
2491 * That's it. We now have all the connected components marked
2492 * as valid or not valid. So now we return a `colours' array if
2493 * we were asked for one, and also we return an overall
2494 * true/false value depending on whether _every_ square in the
2495 * grid is part of a valid component.
2496 */
2497 ret = TRUE;
2498 for (i = 0; i < w*h; i++) {
2499 int ci = dsf_canonify(dsf, i);
2500 int thisok = sqdata[ci].valid;
2501 if (colours)
2502 colours[i] = thisok ? sqdata[ci].colour : 0;
2503 ret = ret && thisok;
2504 }
2505
2506 sfree(sqdata);
2507 if (free_dsf)
2508 sfree(dsf);
2509
2510 return ret;
2511 }
2512
2513 static game_state *execute_move(game_state *state, char *move)
2514 {
2515 int x, y, ax, ay, n, dx, dy;
2516 game_state *ret = dup_game(state);
2517 struct space *sp, *dot;
2518
2519 debug(("%s\n", move));
2520
2521 while (*move) {
2522 char c = *move;
2523 if (c == 'E' || c == 'U' || c == 'M'
2524 #ifdef EDITOR
2525 || c == 'D' || c == 'd'
2526 #endif
2527 ) {
2528 move++;
2529 if (sscanf(move, "%d,%d%n", &x, &y, &n) != 2 ||
2530 !INUI(state, x, y))
2531 goto badmove;
2532
2533 sp = &SPACE(ret, x, y);
2534 #ifdef EDITOR
2535 if (c == 'D' || c == 'd') {
2536 unsigned int currf, newf, maskf;
2537
2538 if (!dot_is_possible(state, sp, 1)) goto badmove;
2539
2540 newf = F_DOT | (c == 'd' ? F_DOT_BLACK : 0);
2541 currf = GRID(ret, grid, x, y).flags;
2542 maskf = F_DOT | F_DOT_BLACK;
2543 /* if we clicked 'white dot':
2544 * white --> empty, empty --> white, black --> white.
2545 * if we clicker 'black dot':
2546 * black --> empty, empty --> black, white --> black.
2547 */
2548 if (currf & maskf) {
2549 sp->flags &= ~maskf;
2550 if ((currf & maskf) != newf)
2551 sp->flags |= newf;
2552 } else
2553 sp->flags |= newf;
2554 sp->nassoc = 0; /* edit-mode disallows associations. */
2555 game_update_dots(ret);
2556 } else
2557 #endif
2558 if (c == 'E') {
2559 if (sp->type != s_edge) goto badmove;
2560 sp->flags ^= F_EDGE_SET;
2561 } else if (c == 'U') {
2562 if (sp->type != s_tile || !(sp->flags & F_TILE_ASSOC))
2563 goto badmove;
2564 remove_assoc(ret, sp);
2565 } else if (c == 'M') {
2566 if (!(sp->flags & F_DOT)) goto badmove;
2567 sp->flags ^= F_DOT_HOLD;
2568 }
2569 move += n;
2570 } else if (c == 'A' || c == 'a') {
2571 move++;
2572 if (sscanf(move, "%d,%d,%d,%d%n", &x, &y, &ax, &ay, &n) != 4 ||
2573 x < 1 || y < 1 || x >= (state->sx-1) || y >= (state->sy-1) ||
2574 ax < 1 || ay < 1 || ax >= (state->sx-1) || ay >= (state->sy-1))
2575 goto badmove;
2576
2577 dot = &GRID(ret, grid, ax, ay);
2578 if (!(dot->flags & F_DOT))goto badmove;
2579 if (dot->flags & F_DOT_HOLD) goto badmove;
2580
2581 for (dx = -1; dx <= 1; dx++) {
2582 for (dy = -1; dy <= 1; dy++) {
2583 sp = &GRID(ret, grid, x+dx, y+dy);
2584 if (sp->type != s_tile) continue;
2585 if (sp->flags & F_TILE_ASSOC) {
2586 space *dot = &SPACE(state, sp->dotx, sp->doty);
2587 if (dot->flags & F_DOT_HOLD) continue;
2588 }
2589 add_assoc(state, sp, dot);
2590 }
2591 }
2592 move += n;
2593 #ifdef EDITOR
2594 } else if (c == 'C') {
2595 move++;
2596 clear_game(ret, 1);
2597 #endif
2598 } else if (c == 'S') {
2599 move++;
2600 } else
2601 goto badmove;
2602
2603 if (*move == ';')
2604 move++;
2605 else if (*move)
2606 goto badmove;
2607 }
2608 if (check_complete_in_play(ret, NULL, NULL))
2609 ret->completed = 1;
2610 return ret;
2611
2612 badmove:
2613 free_game(ret);
2614 return NULL;
2615 }
2616
2617 /* ----------------------------------------------------------------------
2618 * Drawing routines.
2619 */
2620
2621 /* Lines will be much smaller size than squares; say, 1/8 the size?
2622 *
2623 * Need a 'top-left corner of location XxY' to take this into account;
2624 * alternaticaly, that could give the middle of that location, and the
2625 * drawing code would just know the expected dimensions.
2626 *
2627 * We also need something to take a click and work out what it was
2628 * we were interested in. Clicking on vertices is required because
2629 * we may want to drag from them, for example.
2630 */
2631
2632 static void game_compute_size(game_params *params, int sz,
2633 int *x, int *y)
2634 {
2635 struct { int tilesize, w, h; } ads, *ds = &ads;
2636
2637 ds->tilesize = sz;
2638 ds->w = params->w;
2639 ds->h = params->h;
2640
2641 *x = DRAW_WIDTH;
2642 *y = DRAW_HEIGHT;
2643 }
2644
2645 static void game_set_size(drawing *dr, game_drawstate *ds,
2646 game_params *params, int sz)
2647 {
2648 ds->tilesize = sz;
2649
2650 assert(TILE_SIZE > 0);
2651
2652 assert(!ds->bl);
2653 ds->bl = blitter_new(dr, TILE_SIZE, TILE_SIZE);
2654 }
2655
2656 static float *game_colours(frontend *fe, int *ncolours)
2657 {
2658 float *ret = snewn(3 * NCOLOURS, float);
2659 int i;
2660
2661 /*
2662 * We call game_mkhighlight to ensure the background colour
2663 * isn't completely white. We don't actually use the high- and
2664 * lowlight colours it generates.
2665 */
2666 game_mkhighlight(fe, ret, COL_BACKGROUND, COL_WHITEBG, COL_BLACKBG);
2667
2668 for (i = 0; i < 3; i++) {
2669 /*
2670 * Currently, white dots and white-background squares are
2671 * both pure white.
2672 */
2673 ret[COL_WHITEDOT * 3 + i] = 1.0F;
2674 ret[COL_WHITEBG * 3 + i] = 1.0F;
2675
2676 /*
2677 * But black-background squares are a dark grey, whereas
2678 * black dots are really black.
2679 */
2680 ret[COL_BLACKDOT * 3 + i] = 0.0F;
2681 ret[COL_BLACKBG * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.3F;
2682
2683 /*
2684 * In unfilled squares, we draw a faint gridwork.
2685 */
2686 ret[COL_GRID * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.8F;
2687
2688 /*
2689 * Edges and arrows are filled in in pure black.
2690 */
2691 ret[COL_EDGE * 3 + i] = 0.0F;
2692 ret[COL_ARROW * 3 + i] = 0.0F;
2693 }
2694
2695 #ifdef EDITOR
2696 /* tinge the edit background to bluey */
2697 ret[COL_BACKGROUND * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
2698 ret[COL_BACKGROUND * 3 + 1] = ret[COL_BACKGROUND * 3 + 0] * 0.8F;
2699 ret[COL_BACKGROUND * 3 + 2] = ret[COL_BACKGROUND * 3 + 0] * 1.4F;
2700 if (ret[COL_BACKGROUND * 3 + 2] > 1.0F) ret[COL_BACKGROUND * 3 + 2] = 1.0F;
2701 #endif
2702
2703 *ncolours = NCOLOURS;
2704 return ret;
2705 }
2706
2707 static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
2708 {
2709 struct game_drawstate *ds = snew(struct game_drawstate);
2710 int i;
2711
2712 ds->started = 0;
2713 ds->w = state->w;
2714 ds->h = state->h;
2715
2716 ds->grid = snewn(ds->w*ds->h, unsigned long);
2717 for (i = 0; i < ds->w*ds->h; i++)
2718 ds->grid[i] = 0xFFFFFFFFUL;
2719 ds->dx = snewn(ds->w*ds->h, int);
2720 ds->dy = snewn(ds->w*ds->h, int);
2721
2722 ds->bl = NULL;
2723 ds->dragging = FALSE;
2724 ds->dragx = ds->dragy = 0;
2725
2726 ds->colour_scratch = snewn(ds->w * ds->h, int);
2727
2728 return ds;
2729 }
2730
2731 static void game_free_drawstate(drawing *dr, game_drawstate *ds)
2732 {
2733 sfree(ds->colour_scratch);
2734 if (ds->bl) blitter_free(dr, ds->bl);
2735 sfree(ds->dx);
2736 sfree(ds->dy);
2737 sfree(ds->grid);
2738 sfree(ds);
2739 }
2740
2741 #define DRAW_EDGE_L 0x0001
2742 #define DRAW_EDGE_R 0x0002
2743 #define DRAW_EDGE_U 0x0004
2744 #define DRAW_EDGE_D 0x0008
2745 #define DRAW_CORNER_UL 0x0010
2746 #define DRAW_CORNER_UR 0x0020
2747 #define DRAW_CORNER_DL 0x0040
2748 #define DRAW_CORNER_DR 0x0080
2749 #define DRAW_WHITE 0x0100
2750 #define DRAW_BLACK 0x0200
2751 #define DRAW_ARROW 0x0400
2752 #define DOT_SHIFT_C 11
2753 #define DOT_SHIFT_M 2
2754 #define DOT_WHITE 1UL
2755 #define DOT_BLACK 2UL
2756
2757 /*
2758 * Draw an arrow centred on (cx,cy), pointing in the direction
2759 * (ddx,ddy). (I.e. pointing at the point (cx+ddx, cy+ddy).
2760 */
2761 static void draw_arrow(drawing *dr, game_drawstate *ds,
2762 int cx, int cy, int ddx, int ddy)
2763 {
2764 float vlen = sqrt(ddx*ddx+ddy*ddy);
2765 float xdx = ddx/vlen, xdy = ddy/vlen;
2766 float ydx = -xdy, ydy = xdx;
2767 int e1x = cx + xdx*TILE_SIZE/3, e1y = cy + xdy*TILE_SIZE/3;
2768 int e2x = cx - xdx*TILE_SIZE/3, e2y = cy - xdy*TILE_SIZE/3;
2769 int adx = (ydx-xdx)*TILE_SIZE/8, ady = (ydy-xdy)*TILE_SIZE/8;
2770 int adx2 = (-ydx-xdx)*TILE_SIZE/8, ady2 = (-ydy-xdy)*TILE_SIZE/8;
2771
2772 draw_line(dr, e1x, e1y, e2x, e2y, COL_ARROW);
2773 draw_line(dr, e1x, e1y, e1x+adx, e1y+ady, COL_ARROW);
2774 draw_line(dr, e1x, e1y, e1x+adx2, e1y+ady2, COL_ARROW);
2775 }
2776
2777 static void draw_square(drawing *dr, game_drawstate *ds, int x, int y,
2778 unsigned long flags, int ddx, int ddy)
2779 {
2780 int lx = COORD(x), ly = COORD(y);
2781 int dx, dy;
2782 int gridcol;
2783
2784 clip(dr, lx, ly, TILE_SIZE, TILE_SIZE);
2785
2786 /*
2787 * Draw the tile background.
2788 */
2789 draw_rect(dr, lx, ly, TILE_SIZE, TILE_SIZE,
2790 (flags & DRAW_WHITE ? COL_WHITEBG :
2791 flags & DRAW_BLACK ? COL_BLACKBG : COL_BACKGROUND));
2792
2793 /*
2794 * Draw the grid.
2795 */
2796 gridcol = (flags & DRAW_BLACK ? COL_BLACKDOT : COL_GRID);
2797 draw_rect(dr, lx, ly, 1, TILE_SIZE, gridcol);
2798 draw_rect(dr, lx, ly, TILE_SIZE, 1, gridcol);
2799
2800 /*
2801 * Draw the arrow.
2802 */
2803 if (flags & DRAW_ARROW)
2804 draw_arrow(dr, ds, lx + TILE_SIZE/2, ly + TILE_SIZE/2, ddx, ddy);
2805
2806 /*
2807 * Draw the edges.
2808 */
2809 if (flags & DRAW_EDGE_L)
2810 draw_rect(dr, lx, ly, EDGE_THICKNESS, TILE_SIZE, COL_EDGE);
2811 if (flags & DRAW_EDGE_R)
2812 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly,
2813 EDGE_THICKNESS - 1, TILE_SIZE, COL_EDGE);
2814 if (flags & DRAW_EDGE_U)
2815 draw_rect(dr, lx, ly, TILE_SIZE, EDGE_THICKNESS, COL_EDGE);
2816 if (flags & DRAW_EDGE_D)
2817 draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1,
2818 TILE_SIZE, EDGE_THICKNESS - 1, COL_EDGE);
2819 if (flags & DRAW_CORNER_UL)
2820 draw_rect(dr, lx, ly, EDGE_THICKNESS, EDGE_THICKNESS, COL_EDGE);
2821 if (flags & DRAW_CORNER_UR)
2822 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1, ly,
2823 EDGE_THICKNESS - 1, EDGE_THICKNESS, COL_EDGE);
2824 if (flags & DRAW_CORNER_DL)
2825 draw_rect(dr, lx, ly + TILE_SIZE - EDGE_THICKNESS + 1,
2826 EDGE_THICKNESS, EDGE_THICKNESS - 1, COL_EDGE);
2827 if (flags & DRAW_CORNER_DR)
2828 draw_rect(dr, lx + TILE_SIZE - EDGE_THICKNESS + 1,
2829 ly + TILE_SIZE - EDGE_THICKNESS + 1,
2830 EDGE_THICKNESS - 1, EDGE_THICKNESS - 1, COL_EDGE);
2831
2832 /*
2833 * Draw the dots.
2834 */
2835 for (dy = 0; dy < 3; dy++)
2836 for (dx = 0; dx < 3; dx++) {
2837 int dotval = (flags >> (DOT_SHIFT_C + DOT_SHIFT_M*(dy*3+dx)));
2838 dotval &= (1 << DOT_SHIFT_M)-1;
2839
2840 if (dotval)
2841 draw_circle(dr, lx+dx*TILE_SIZE/2, ly+dy*TILE_SIZE/2,
2842 DOT_SIZE,
2843 (dotval == 1 ? COL_WHITEDOT : COL_BLACKDOT),
2844 COL_BLACKDOT);
2845 }
2846
2847 unclip(dr);
2848 draw_update(dr, lx, ly, TILE_SIZE, TILE_SIZE);
2849 }
2850
2851 static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
2852 game_state *state, int dir, game_ui *ui,
2853 float animtime, float flashtime)
2854 {
2855 int w = ds->w, h = ds->h;
2856 int x, y, flashing = FALSE;
2857
2858 if (flashtime > 0) {
2859 int frame = (int)(flashtime / FLASH_TIME);
2860 flashing = (frame % 2 == 0);
2861 }
2862
2863 if (ds->dragging) {
2864 assert(ds->bl);
2865 blitter_load(dr, ds->bl, ds->dragx, ds->dragy);
2866 draw_update(dr, ds->dragx, ds->dragy, TILE_SIZE, TILE_SIZE);
2867 ds->dragging = FALSE;
2868 }
2869
2870 if (!ds->started) {
2871 draw_rect(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT, COL_BACKGROUND);
2872 draw_rect(dr, BORDER - EDGE_THICKNESS + 1, BORDER - EDGE_THICKNESS + 1,
2873 w*TILE_SIZE + EDGE_THICKNESS*2 - 1,
2874 h*TILE_SIZE + EDGE_THICKNESS*2 - 1, COL_EDGE);
2875 draw_update(dr, 0, 0, DRAW_WIDTH, DRAW_HEIGHT);
2876 ds->started = TRUE;
2877 }
2878
2879 check_complete_in_play(state, NULL, ds->colour_scratch);
2880
2881 for (y = 0; y < h; y++)
2882 for (x = 0; x < w; x++) {
2883 unsigned long flags = 0;
2884 int ddx = 0, ddy = 0;
2885 space *sp;
2886 int dx, dy;
2887
2888 /*
2889 * Set up the flags for this square. Firstly, see if we
2890 * have edges.
2891 */
2892 if (SPACE(state, x*2, y*2+1).flags & F_EDGE_SET)
2893 flags |= DRAW_EDGE_L;
2894 if (SPACE(state, x*2+2, y*2+1).flags & F_EDGE_SET)
2895 flags |= DRAW_EDGE_R;
2896 if (SPACE(state, x*2+1, y*2).flags & F_EDGE_SET)
2897 flags |= DRAW_EDGE_U;
2898 if (SPACE(state, x*2+1, y*2+2).flags & F_EDGE_SET)
2899 flags |= DRAW_EDGE_D;
2900
2901 /*
2902 * Also, mark corners of neighbouring edges.
2903 */
2904 if ((x > 0 && SPACE(state, x*2-1, y*2).flags & F_EDGE_SET) ||
2905 (y > 0 && SPACE(state, x*2, y*2-1).flags & F_EDGE_SET))
2906 flags |= DRAW_CORNER_UL;
2907 if ((x+1 < w && SPACE(state, x*2+3, y*2).flags & F_EDGE_SET) ||
2908 (y > 0 && SPACE(state, x*2+2, y*2-1).flags & F_EDGE_SET))
2909 flags |= DRAW_CORNER_UR;
2910 if ((x > 0 && SPACE(state, x*2-1, y*2+2).flags & F_EDGE_SET) ||
2911 (y+1 < h && SPACE(state, x*2, y*2+3).flags & F_EDGE_SET))
2912 flags |= DRAW_CORNER_DL;
2913 if ((x+1 < w && SPACE(state, x*2+3, y*2+2).flags & F_EDGE_SET) ||
2914 (y+1 < h && SPACE(state, x*2+2, y*2+3).flags & F_EDGE_SET))
2915 flags |= DRAW_CORNER_DR;
2916
2917 /*
2918 * If this square is part of a valid region, paint it
2919 * that region's colour. Exception: if we're flashing,
2920 * everything goes briefly back to background colour.
2921 */
2922 sp = &SPACE(state, x*2+1, y*2+1);
2923 if (ds->colour_scratch[y*w+x] && !flashing) {
2924 flags |= (ds->colour_scratch[y*w+x] == 2 ?
2925 DRAW_BLACK : DRAW_WHITE);
2926 }
2927
2928 /*
2929 * If this square is associated with a dot but it isn't
2930 * part of a valid region, draw an arrow in it pointing
2931 * in the direction of that dot.
2932 *
2933 * Exception: if this is the source point of an active
2934 * drag, we don't draw the arrow.
2935 */
2936 if ((sp->flags & F_TILE_ASSOC) && !ds->colour_scratch[y*w+x]) {
2937 if (ui->dragging && ui->srcx == x*2+1 && ui->srcy == y*2+1) {
2938 /* don't do it */
2939 } else if (sp->doty != y*2+1 || sp->dotx != x*2+1) {
2940 flags |= DRAW_ARROW;
2941 ddy = sp->doty - (y*2+1);
2942 ddx = sp->dotx - (x*2+1);
2943 }
2944 }
2945
2946 /*
2947 * Now go through the nine possible places we could
2948 * have dots.
2949 */
2950 for (dy = 0; dy < 3; dy++)
2951 for (dx = 0; dx < 3; dx++) {
2952 sp = &SPACE(state, x*2+dx, y*2+dy);
2953 if (sp->flags & F_DOT) {
2954 unsigned long dotval = (sp->flags & F_DOT_BLACK ?
2955 DOT_BLACK : DOT_WHITE);
2956 flags |= dotval << (DOT_SHIFT_C +
2957 DOT_SHIFT_M*(dy*3+dx));
2958 }
2959 }
2960
2961 /*
2962 * Now we have everything we're going to need. Draw the
2963 * square.
2964 */
2965 if (ds->grid[y*w+x] != flags ||
2966 ds->dx[y*w+x] != ddx ||
2967 ds->dy[y*w+x] != ddy) {
2968 draw_square(dr, ds, x, y, flags, ddx, ddy);
2969 ds->grid[y*w+x] = flags;
2970 ds->dx[y*w+x] = ddx;
2971 ds->dy[y*w+x] = ddy;
2972 }
2973 }
2974
2975 if (ui->dragging) {
2976 ds->dragging = TRUE;
2977 ds->dragx = ui->dx - TILE_SIZE/2;
2978 ds->dragy = ui->dy - TILE_SIZE/2;
2979 blitter_save(dr, ds->bl, ds->dragx, ds->dragy);
2980 draw_arrow(dr, ds, ui->dx, ui->dy,
2981 SCOORD(ui->dotx) - ui->dx,
2982 SCOORD(ui->doty) - ui->dy);
2983 }
2984 #ifdef EDITOR
2985 {
2986 char buf[256];
2987 if (state->cdiff != -1)
2988 sprintf(buf, "Puzzle is %s.", galaxies_diffnames[state->cdiff]);
2989 else
2990 buf[0] = '\0';
2991 status_bar(dr, buf);
2992 }
2993 #endif
2994 }
2995
2996 static float game_anim_length(game_state *oldstate, game_state *newstate,
2997 int dir, game_ui *ui)
2998 {
2999 return 0.0F;
3000 }
3001
3002 static float game_flash_length(game_state *oldstate, game_state *newstate,
3003 int dir, game_ui *ui)
3004 {
3005 if ((!oldstate->completed && newstate->completed) &&
3006 !(newstate->used_solve))
3007 return 3 * FLASH_TIME;
3008 else
3009 return 0.0F;
3010 }
3011
3012 static int game_timing_state(game_state *state, game_ui *ui)
3013 {
3014 return TRUE;
3015 }
3016
3017 #ifndef EDITOR
3018 static void game_print_size(game_params *params, float *x, float *y)
3019 {
3020 int pw, ph;
3021
3022 /*
3023 * 8mm squares by default. (There isn't all that much detail
3024 * that needs to go in each square.)
3025 */
3026 game_compute_size(params, 800, &pw, &ph);
3027 *x = pw / 100.0F;
3028 *y = ph / 100.0F;
3029 }
3030
3031 static void game_print(drawing *dr, game_state *state, int sz)
3032 {
3033 int w = state->w, h = state->h;
3034 int white, black, blackish;
3035 int x, y, i, j;
3036 int *colours, *dsf;
3037 int *coords = NULL;
3038 int ncoords = 0, coordsize = 0;
3039
3040 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3041 game_drawstate ads, *ds = &ads;
3042 ds->tilesize = sz;
3043
3044 white = print_grey_colour(dr, HATCH_CLEAR, 1.0F);
3045 black = print_grey_colour(dr, HATCH_SOLID, 0.0F);
3046 blackish = print_grey_colour(dr, HATCH_X, 0.5F);
3047
3048 /*
3049 * Get the completion information.
3050 */
3051 dsf = snewn(w * h, int);
3052 colours = snewn(w * h, int);
3053 check_complete_in_play(state, dsf, colours);
3054
3055 /*
3056 * Draw the grid.
3057 */
3058 print_line_width(dr, TILE_SIZE / 64);
3059 for (x = 1; x < w; x++)
3060 draw_line(dr, COORD(x), COORD(0), COORD(x), COORD(h), black);
3061 for (y = 1; y < h; y++)
3062 draw_line(dr, COORD(0), COORD(y), COORD(w), COORD(y), black);
3063
3064 /*
3065 * Shade the completed regions. Just in case any particular
3066 * printing platform deals badly with adjacent
3067 * similarly-hatched regions, we'll fill each one as a single
3068 * polygon.
3069 */
3070 for (i = 0; i < w*h; i++) {
3071 j = dsf_canonify(dsf, i);
3072 if (colours[j] != 0) {
3073 int dx, dy, t;
3074
3075 /*
3076 * This is the first square we've run into belonging to
3077 * this polyomino, which means an edge of the polyomino
3078 * is certain to be to our left. (After we finish
3079 * tracing round it, we'll set the colours[] entry to
3080 * zero to prevent accidentally doing it again.)
3081 */
3082
3083 x = i % w;
3084 y = i / w;
3085 dx = -1;
3086 dy = 0;
3087 ncoords = 0;
3088 while (1) {
3089 /*
3090 * We are currently sitting on square (x,y), which
3091 * we know to be in our polyomino, and we also know
3092 * that (x+dx,y+dy) is not. The way I visualise
3093 * this is that we're standing to the right of a
3094 * boundary line, stretching our left arm out to
3095 * point to the exterior square on the far side.
3096 */
3097
3098 /*
3099 * First, check if we've gone round the entire
3100 * polyomino.
3101 */
3102 if (ncoords > 0 &&
3103 (x == i%w && y == i/w && dx == -1 && dy == 0))
3104 break;
3105
3106 /*
3107 * Add to our coordinate list the coordinate
3108 * backwards and to the left of where we are.
3109 */
3110 if (ncoords + 2 > coordsize) {
3111 coordsize = (ncoords * 3 / 2) + 64;
3112 coords = sresize(coords, coordsize, int);
3113 }
3114 coords[ncoords++] = COORD((2*x+1 + dx + dy) / 2);
3115 coords[ncoords++] = COORD((2*y+1 + dy - dx) / 2);
3116
3117 /*
3118 * Follow the edge round. If the square directly in
3119 * front of us is not part of the polyomino, we
3120 * turn right; if it is and so is the square in
3121 * front of (x+dx,y+dy), we turn left; otherwise we
3122 * go straight on.
3123 */
3124 if (x-dy < 0 || x-dy >= w || y+dx < 0 || y+dx >= h ||
3125 dsf_canonify(dsf, (y+dx)*w+(x-dy)) != j) {
3126 /* Turn right. */
3127 t = dx;
3128 dx = -dy;
3129 dy = t;
3130 } else if (x+dx-dy >= 0 && x+dx-dy < w &&
3131 y+dy+dx >= 0 && y+dy+dx < h &&
3132 dsf_canonify(dsf, (y+dy+dx)*w+(x+dx-dy)) == j) {
3133 /* Turn left. */
3134 x += dx;
3135 y += dy;
3136 t = dx;
3137 dx = dy;
3138 dy = -t;
3139 x -= dx;
3140 y -= dy;
3141 } else {
3142 /* Straight on. */
3143 x -= dy;
3144 y += dx;
3145 }
3146 }
3147
3148 /*
3149 * Now we have our polygon complete, so fill it.
3150 */
3151 draw_polygon(dr, coords, ncoords/2,
3152 colours[j] == 2 ? blackish : -1, black);
3153
3154 /*
3155 * And mark this polyomino as done.
3156 */
3157 colours[j] = 0;
3158 }
3159 }
3160
3161 /*
3162 * Draw the edges.
3163 */
3164 for (y = 0; y <= h; y++)
3165 for (x = 0; x <= w; x++) {
3166 if (x < w && SPACE(state, x*2+1, y*2).flags & F_EDGE_SET)
3167 draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS,
3168 EDGE_THICKNESS * 2 + TILE_SIZE, EDGE_THICKNESS * 2,
3169 black);
3170 if (y < h && SPACE(state, x*2, y*2+1).flags & F_EDGE_SET)
3171 draw_rect(dr, COORD(x)-EDGE_THICKNESS, COORD(y)-EDGE_THICKNESS,
3172 EDGE_THICKNESS * 2, EDGE_THICKNESS * 2 + TILE_SIZE,
3173 black);
3174 }
3175
3176 /*
3177 * Draw the dots.
3178 */
3179 for (y = 0; y <= 2*h; y++)
3180 for (x = 0; x <= 2*w; x++)
3181 if (SPACE(state, x, y).flags & F_DOT) {
3182 draw_circle(dr, COORD(x/2.0), COORD(y/2.0), DOT_SIZE,
3183 (SPACE(state, x, y).flags & F_DOT_BLACK ?
3184 black : white), black);
3185 }
3186
3187 sfree(dsf);
3188 sfree(colours);
3189 sfree(coords);
3190 }
3191 #endif
3192
3193 #ifdef COMBINED
3194 #define thegame galaxies
3195 #endif
3196
3197 const struct game thegame = {
3198 "Galaxies", "games.galaxies", "galaxies",
3199 default_params,
3200 game_fetch_preset,
3201 decode_params,
3202 encode_params,
3203 free_params,
3204 dup_params,
3205 TRUE, game_configure, custom_params,
3206 validate_params,
3207 new_game_desc,
3208 validate_desc,
3209 new_game,
3210 dup_game,
3211 free_game,
3212 #ifdef EDITOR
3213 FALSE, NULL,
3214 #else
3215 TRUE, solve_game,
3216 #endif
3217 TRUE, game_text_format,
3218 new_ui,
3219 free_ui,
3220 encode_ui,
3221 decode_ui,
3222 game_changed_state,
3223 interpret_move,
3224 execute_move,
3225 PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
3226 game_colours,
3227 game_new_drawstate,
3228 game_free_drawstate,
3229 game_redraw,
3230 game_anim_length,
3231 game_flash_length,
3232 #ifdef EDITOR
3233 FALSE, FALSE, NULL, NULL,
3234 TRUE, /* wants_statusbar */
3235 #else
3236 TRUE, TRUE, game_print_size, game_print,
3237 FALSE, /* wants_statusbar */
3238 #endif
3239 FALSE, game_timing_state,
3240 0, /* flags */
3241 };
3242
3243 #ifdef STANDALONE_SOLVER
3244
3245 const char *quis;
3246
3247 #include <time.h>
3248
3249 static void usage_exit(const char *msg)
3250 {
3251 if (msg)
3252 fprintf(stderr, "%s: %s\n", quis, msg);
3253 fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
3254 exit(1);
3255 }
3256
3257 static void dump_state(game_state *state)
3258 {
3259 char *temp = game_text_format(state);
3260 printf("%s\n", temp);
3261 sfree(temp);
3262 }
3263
3264 static int gen(game_params *p, random_state *rs, int debug)
3265 {
3266 char *desc;
3267 int diff;
3268 game_state *state;
3269
3270 #ifndef DEBUGGING
3271 solver_show_working = debug;
3272 #endif
3273 printf("Generating a %dx%d %s puzzle.\n",
3274 p->w, p->h, galaxies_diffnames[p->diff]);
3275
3276 desc = new_game_desc(p, rs, NULL, 0);
3277 state = new_game(NULL, p, desc);
3278 dump_state(state);
3279
3280 diff = solver_state(state, DIFF_RECURSIVE);
3281 printf("Generated %s game %dx%d:%s\n",
3282 galaxies_diffnames[diff], p->w, p->h, desc);
3283 dump_state(state);
3284
3285 free_game(state);
3286 sfree(desc);
3287
3288 return diff;
3289 }
3290
3291 static void soak(game_params *p, random_state *rs)
3292 {
3293 time_t tt_start, tt_now, tt_last;
3294 char *desc;
3295 game_state *st;
3296 int diff, n = 0, i, diffs[DIFF_MAX], ndots = 0, nspaces = 0;
3297
3298 #ifndef DEBUGGING
3299 solver_show_working = 0;
3300 #endif
3301 tt_start = tt_now = time(NULL);
3302 for (i = 0; i < DIFF_MAX; i++) diffs[i] = 0;
3303 maxtries = 1;
3304
3305 printf("Soak-generating a %dx%d grid, max. diff %s.\n",
3306 p->w, p->h, galaxies_diffnames[p->diff]);
3307 printf(" [");
3308 for (i = 0; i < DIFF_MAX; i++)
3309 printf("%s%s", (i == 0) ? "" : ", ", galaxies_diffnames[i]);
3310 printf("]\n");
3311
3312 while (1) {
3313 desc = new_game_desc(p, rs, NULL, 0);
3314 st = new_game(NULL, p, desc);
3315 diff = solver_state(st, p->diff);
3316 nspaces += st->w*st->h;
3317 for (i = 0; i < st->sx*st->sy; i++)
3318 if (st->grid[i].flags & F_DOT) ndots++;
3319 free_game(st);
3320 sfree(desc);
3321
3322 diffs[diff]++;
3323 n++;
3324 tt_last = time(NULL);
3325 if (tt_last > tt_now) {
3326 tt_now = tt_last;
3327 printf("%d total, %3.1f/s, [",
3328 n, (double)n / ((double)tt_now - tt_start));
3329 for (i = 0; i < DIFF_MAX; i++)
3330 printf("%s%.1f%%", (i == 0) ? "" : ", ",
3331 100.0 * ((double)diffs[i] / (double)n));
3332 printf("], %.1f%% dots\n",
3333 100.0 * ((double)ndots / (double)nspaces));
3334 }
3335 }
3336 }
3337
3338 int main(int argc, char **argv)
3339 {
3340 game_params *p;
3341 char *id = NULL, *desc, *err;
3342 game_state *s;
3343 int diff, do_soak = 0, verbose = 0;
3344 random_state *rs;
3345 time_t seed = time(NULL);
3346
3347 quis = argv[0];
3348 while (--argc > 0) {
3349 char *p = *++argv;
3350 if (!strcmp(p, "-v")) {
3351 verbose = 1;
3352 } else if (!strcmp(p, "--seed")) {
3353 if (argc == 0) usage_exit("--seed needs an argument");
3354 seed = (time_t)atoi(*++argv);
3355 argc--;
3356 } else if (!strcmp(p, "--soak")) {
3357 do_soak = 1;
3358 } else if (*p == '-') {
3359 usage_exit("unrecognised option");
3360 } else {
3361 id = p;
3362 }
3363 }
3364
3365 maxtries = 50;
3366
3367 p = default_params();
3368 rs = random_new((void*)&seed, sizeof(time_t));
3369
3370 if (do_soak) {
3371 if (!id) usage_exit("need one argument for --soak");
3372 decode_params(p, *argv);
3373 soak(p, rs);
3374 return 0;
3375 }
3376
3377 if (!id) {
3378 while (1) {
3379 p->w = random_upto(rs, 15) + 3;
3380 p->h = random_upto(rs, 15) + 3;
3381 p->diff = random_upto(rs, DIFF_RECURSIVE);
3382 diff = gen(p, rs, 0);
3383 }
3384 return 0;
3385 }
3386
3387 desc = strchr(id, ':');
3388 if (!desc) {
3389 decode_params(p, id);
3390 gen(p, rs, verbose);
3391 } else {
3392 #ifndef DEBUGGING
3393 solver_show_working = 1;
3394 #endif
3395 *desc++ = '\0';
3396 decode_params(p, id);
3397 err = validate_desc(p, desc);
3398 if (err) {
3399 fprintf(stderr, "%s: %s\n", argv[0], err);
3400 exit(1);
3401 }
3402 s = new_game(NULL, p, desc);
3403 diff = solver_state(s, DIFF_RECURSIVE);
3404 dump_state(s);
3405 printf("Puzzle is %s.\n", galaxies_diffnames[diff]);
3406 free_game(s);
3407 }
3408
3409 free_params(p);
3410
3411 return 0;
3412 }
3413
3414 #endif
3415
3416 /* vim: set shiftwidth=4 tabstop=8: */