2 * galaxies.c: implementation of 'Tentai Show' from Nikoli,
3 * also sometimes called 'Spiral Galaxies'.
7 * Grid is stored as size (2n-1), holding edges as well as spaces
8 * (and thus vertices too, at edge intersections).
10 * Any dot will thus be positioned at one of our grid points,
11 * which saves any faffing with half-of-a-square stuff.
13 * Edges have on/off state; obviously the actual edges of the
14 * board are fixed to on, and everything else starts as off.
18 * Think about how to display remote groups of tiles?
24 * Nikoli's example [web site has wrong highlighting]
25 * (at http://www.nikoli.co.jp/en/puzzles/astronomical_show/):
28 * The 'spiral galaxies puzzles are NP-complete' paper
29 * (at http://www.stetson.edu/~efriedma/papers/spiral.pdf):
30 * 7x7:chpgdqqqoezdddki
32 * Puzzle competition pdf examples
33 * (at http://www.puzzleratings.org/Yurekli2006puz.pdf):
34 * 6x6:EDbaMucCohbrecEi
35 * 10x10:beFbufEEzowDlxldibMHezBQzCdcFzjlci
36 * 13x13:dCemIHFFkJajjgDfdbdBzdzEgjccoPOcztHjBczLDjczqktJjmpreivvNcggFi
52 int solver_show_working
;
53 #define solvep(x) do { if (solver_show_working) { printf x; } } while(0)
56 #ifdef STANDALONE_PICTURE_GENERATOR
58 * Dirty hack to enable the generator to construct a game ID which
59 * solves to a specified black-and-white bitmap. We define a global
60 * variable here which gives the desired colour of each square, and
61 * we arrange that the grid generator never merges squares of
64 * The bitmap as stored here is a simple int array (at these sizes
65 * it isn't worth doing fiddly bit-packing). picture[y*w+x] is 1
66 * iff the pixel at (x,y) is intended to be black.
68 * (It might be nice to be able to specify some pixels as
69 * don't-care, to give the generator more leeway. But that might be
90 A(UNREASONABLE,Unreasonable,u)
92 #define ENUM(upper,title,lower) DIFF_ ## upper,
93 #define TITLE(upper,title,lower) #title,
94 #define ENCODE(upper,title,lower) #lower
95 #define CONFIG(upper,title,lower) ":" #title
97 DIFF_IMPOSSIBLE
, DIFF_AMBIGUOUS
, DIFF_UNFINISHED
, DIFF_MAX
};
98 static char const *const galaxies_diffnames
[] = {
99 DIFFLIST(TITLE
) "Impossible", "Ambiguous", "Unfinished" };
100 static char const galaxies_diffchars
[] = DIFFLIST(ENCODE
);
101 #define DIFFCONFIG DIFFLIST(CONFIG)
104 /* X and Y is the area of the board as seen by
105 * the user, not the (2n+1) area the game uses. */
109 enum { s_tile
, s_edge
, s_vertex
};
111 #define F_DOT 1 /* there's a dot here */
112 #define F_EDGE_SET 2 /* the edge is set */
113 #define F_TILE_ASSOC 4 /* this tile is associated with a dot. */
114 #define F_DOT_BLACK 8 /* (ui only) dot is black. */
115 #define F_MARK 16 /* scratch flag */
116 #define F_REACHABLE 32
118 #define F_MULTIPLE 128
119 #define F_DOT_HOLD 256
122 typedef struct space
{
123 int x
, y
; /* its position */
126 int dotx
, doty
; /* if flags & F_TILE_ASSOC */
127 int nassoc
; /* if flags & F_DOT */
130 #define INGRID(s,x,y) ((x) >= 0 && (y) >= 0 && \
131 (x) < (state)->sx && (y) < (state)->sy)
132 #define INUI(s,x,y) ((x) > 0 && (y) > 0 && \
133 (x) < ((state)->sx-1) && (y) < ((state)->sy-1))
135 #define GRID(s,g,x,y) ((s)->g[((y)*(s)->sx)+(x)])
136 #define SPACE(s,x,y) GRID(s,grid,x,y)
139 int w
, h
; /* size from params */
140 int sx
, sy
; /* allocated size, (2x-1)*(2y-1) */
142 int completed
, used_solve
;
146 midend
*me
; /* to call supersede_game_desc */
147 int cdiff
; /* difficulty of current puzzle (for status bar),
151 /* ----------------------------------------------------------
152 * Game parameters and presets
155 /* make up some sensible default sizes */
157 #define DEFAULT_PRESET 0
159 static const game_params galaxies_presets
[] = {
160 { 7, 7, DIFF_NORMAL
},
161 { 7, 7, DIFF_UNREASONABLE
},
162 { 10, 10, DIFF_NORMAL
},
163 { 15, 15, DIFF_NORMAL
},
166 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
171 if (i
< 0 || i
>= lenof(galaxies_presets
))
174 ret
= snew(game_params
);
175 *ret
= galaxies_presets
[i
]; /* structure copy */
177 sprintf(buf
, "%dx%d %s", ret
->w
, ret
->h
,
178 galaxies_diffnames
[ret
->diff
]);
180 if (name
) *name
= dupstr(buf
);
185 static game_params
*default_params(void)
188 game_fetch_preset(DEFAULT_PRESET
, NULL
, &ret
);
192 static void free_params(game_params
*params
)
197 static game_params
*dup_params(game_params
*params
)
199 game_params
*ret
= snew(game_params
);
200 *ret
= *params
; /* structure copy */
204 static void decode_params(game_params
*params
, char const *string
)
206 params
->h
= params
->w
= atoi(string
);
207 params
->diff
= DIFF_NORMAL
;
208 while (*string
&& isdigit((unsigned char)*string
)) string
++;
209 if (*string
== 'x') {
211 params
->h
= atoi(string
);
212 while (*string
&& isdigit((unsigned char)*string
)) string
++;
214 if (*string
== 'd') {
217 for (i
= 0; i
<= DIFF_UNREASONABLE
; i
++)
218 if (*string
== galaxies_diffchars
[i
])
220 if (*string
) string
++;
224 static char *encode_params(game_params
*params
, int full
)
227 sprintf(str
, "%dx%d", params
->w
, params
->h
);
229 sprintf(str
+ strlen(str
), "d%c", galaxies_diffchars
[params
->diff
]);
233 static config_item
*game_configure(game_params
*params
)
238 ret
= snewn(4, config_item
);
240 ret
[0].name
= "Width";
241 ret
[0].type
= C_STRING
;
242 sprintf(buf
, "%d", params
->w
);
243 ret
[0].sval
= dupstr(buf
);
246 ret
[1].name
= "Height";
247 ret
[1].type
= C_STRING
;
248 sprintf(buf
, "%d", params
->h
);
249 ret
[1].sval
= dupstr(buf
);
252 ret
[2].name
= "Difficulty";
253 ret
[2].type
= C_CHOICES
;
254 ret
[2].sval
= DIFFCONFIG
;
255 ret
[2].ival
= params
->diff
;
265 static game_params
*custom_params(config_item
*cfg
)
267 game_params
*ret
= snew(game_params
);
269 ret
->w
= atoi(cfg
[0].sval
);
270 ret
->h
= atoi(cfg
[1].sval
);
271 ret
->diff
= cfg
[2].ival
;
276 static char *validate_params(game_params
*params
, int full
)
278 if (params
->w
< 3 || params
->h
< 3)
279 return "Width and height must both be at least 3";
281 * This shouldn't be able to happen at all, since decode_params
282 * and custom_params will never generate anything that isn't
285 assert(params
->diff
<= DIFF_UNREASONABLE
);
290 /* ----------------------------------------------------------
291 * Game utility functions.
294 static void add_dot(space
*space
) {
295 assert(!(space
->flags
& F_DOT
));
296 space
->flags
|= F_DOT
;
300 static void remove_dot(space
*space
) {
301 assert(space
->flags
& F_DOT
);
302 space
->flags
&= ~F_DOT
;
305 static void remove_assoc(game_state
*state
, space
*tile
) {
306 if (tile
->flags
& F_TILE_ASSOC
) {
307 SPACE(state
, tile
->dotx
, tile
->doty
).nassoc
--;
308 tile
->flags
&= ~F_TILE_ASSOC
;
314 static void add_assoc(game_state
*state
, space
*tile
, space
*dot
) {
315 remove_assoc(state
, tile
);
317 #ifdef STANDALONE_PICTURE_GENERATOR
319 assert(!picture
[(tile
->y
/2) * state
->w
+ (tile
->x
/2)] ==
320 !(dot
->flags
& F_DOT_BLACK
));
322 tile
->flags
|= F_TILE_ASSOC
;
326 /*debug(("add_assoc sp %d %d --> dot %d,%d, new nassoc %d.\n",
327 tile->x, tile->y, dot->x, dot->y, dot->nassoc));*/
330 static struct space
*sp2dot(game_state
*state
, int x
, int y
)
332 struct space
*sp
= &SPACE(state
, x
, y
);
333 if (!(sp
->flags
& F_TILE_ASSOC
)) return NULL
;
334 return &SPACE(state
, sp
->dotx
, sp
->doty
);
337 #define IS_VERTICAL_EDGE(x) ((x % 2) == 0)
339 static int game_can_format_as_text_now(game_params
*params
)
344 static char *game_text_format(game_state
*state
)
346 int maxlen
= (state
->sx
+1)*state
->sy
, x
, y
;
350 ret
= snewn(maxlen
+1, char);
353 for (y
= 0; y
< state
->sy
; y
++) {
354 for (x
= 0; x
< state
->sx
; x
++) {
355 sp
= &SPACE(state
, x
, y
);
356 if (sp
->flags
& F_DOT
)
359 else if (sp
->flags
& (F_REACHABLE
|F_MULTIPLE
|F_MARK
))
360 *p
++ = (sp
->flags
& F_MULTIPLE
) ? 'M' :
361 (sp
->flags
& F_REACHABLE
) ? 'R' : 'X';
366 if (sp
->flags
& F_TILE_ASSOC
) {
367 space
*dot
= sp2dot(state
, sp
->x
, sp
->y
);
368 if (dot
&& dot
->flags
& F_DOT
)
369 *p
++ = (dot
->flags
& F_DOT_BLACK
) ? 'B' : 'W';
371 *p
++ = '?'; /* association with not-a-dot. */
381 if (sp
->flags
& F_EDGE_SET
)
382 *p
++ = (IS_VERTICAL_EDGE(x
)) ? '|' : '-';
388 assert(!"shouldn't get here!");
395 assert(p
- ret
== maxlen
);
401 static void dbg_state(game_state
*state
)
404 char *temp
= game_text_format(state
);
405 debug(("%s\n", temp
));
410 /* Space-enumeration callbacks should all return 1 for 'progress made',
411 * -1 for 'impossible', and 0 otherwise. */
412 typedef int (*space_cb
)(game_state
*state
, space
*sp
, void *ctx
);
414 #define IMPOSSIBLE_QUITS 1
416 static int foreach_sub(game_state
*state
, space_cb cb
, unsigned int f
,
417 void *ctx
, int startx
, int starty
)
419 int x
, y
, progress
= 0, impossible
= 0, ret
;
422 for (y
= starty
; y
< state
->sy
; y
+= 2) {
423 sp
= &SPACE(state
, startx
, y
);
424 for (x
= startx
; x
< state
->sx
; x
+= 2) {
425 ret
= cb(state
, sp
, ctx
);
427 if (f
& IMPOSSIBLE_QUITS
) return -1;
429 } else if (ret
== 1) {
435 return impossible
? -1 : progress
;
438 static int foreach_tile(game_state
*state
, space_cb cb
, unsigned int f
,
441 return foreach_sub(state
, cb
, f
, ctx
, 1, 1);
444 static int foreach_edge(game_state
*state
, space_cb cb
, unsigned int f
,
449 ret1
= foreach_sub(state
, cb
, f
, ctx
, 0, 1);
450 ret2
= foreach_sub(state
, cb
, f
, ctx
, 1, 0);
452 if (ret1
== -1 || ret2
== -1) return -1;
453 return (ret1
|| ret2
) ? 1 : 0;
457 static int foreach_vertex(game_state
*state
, space_cb cb
, unsigned int f
,
460 return foreach_sub(state
, cb
, f
, ctx
, 0, 0);
465 static int is_same_assoc(game_state
*state
,
466 int x1
, int y1
, int x2
, int y2
)
468 struct space
*s1
, *s2
;
470 if (!INGRID(state
, x1
, y1
) || !INGRID(state
, x2
, y2
))
473 s1
= &SPACE(state
, x1
, y1
);
474 s2
= &SPACE(state
, x2
, y2
);
475 assert(s1
->type
== s_tile
&& s2
->type
== s_tile
);
476 if ((s1
->flags
& F_TILE_ASSOC
) && (s2
->flags
& F_TILE_ASSOC
) &&
477 s1
->dotx
== s2
->dotx
&& s1
->doty
== s2
->doty
)
479 return 0; /* 0 if not same or not both associated. */
484 static int edges_into_vertex(game_state
*state
,
487 int dx
, dy
, nx
, ny
, count
= 0;
489 assert(SPACE(state
, x
, y
).type
== s_vertex
);
490 for (dx
= -1; dx
<= 1; dx
++) {
491 for (dy
= -1; dy
<= 1; dy
++) {
492 if (dx
!= 0 && dy
!= 0) continue;
493 if (dx
== 0 && dy
== 0) continue;
495 nx
= x
+dx
; ny
= y
+dy
;
496 if (!INGRID(state
, nx
, ny
)) continue;
497 assert(SPACE(state
, nx
, ny
).type
== s_edge
);
498 if (SPACE(state
, nx
, ny
).flags
& F_EDGE_SET
)
506 static struct space
*space_opposite_dot(struct game_state
*state
,
507 struct space
*sp
, struct space
*dot
)
516 if (!INGRID(state
, tx
, ty
)) return NULL
;
518 sp2
= &SPACE(state
, tx
, ty
);
519 assert(sp2
->type
== sp
->type
);
523 static struct space
*tile_opposite(struct game_state
*state
, struct space
*sp
)
527 assert(sp
->flags
& F_TILE_ASSOC
);
528 dot
= &SPACE(state
, sp
->dotx
, sp
->doty
);
529 return space_opposite_dot(state
, sp
, dot
);
532 static int dotfortile(game_state
*state
, space
*tile
, space
*dot
)
534 space
*tile_opp
= space_opposite_dot(state
, tile
, dot
);
536 if (!tile_opp
) return 0; /* opposite would be off grid */
537 if (tile_opp
->flags
& F_TILE_ASSOC
&&
538 (tile_opp
->dotx
!= dot
->x
|| tile_opp
->doty
!= dot
->y
))
539 return 0; /* opposite already associated with diff. dot */
543 static void adjacencies(struct game_state
*state
, struct space
*sp
,
544 struct space
**a1s
, struct space
**a2s
)
546 int dxs
[4] = {-1, 1, 0, 0}, dys
[4] = {0, 0, -1, 1};
549 /* this function needs optimising. */
551 for (n
= 0; n
< 4; n
++) {
555 if (INGRID(state
, x
, y
)) {
556 a1s
[n
] = &SPACE(state
, x
, y
);
558 x
+= dxs
[n
]; y
+= dys
[n
];
560 if (INGRID(state
, x
, y
))
561 a2s
[n
] = &SPACE(state
, x
, y
);
565 a1s
[n
] = a2s
[n
] = NULL
;
570 static int outline_tile_fordot(game_state
*state
, space
*tile
, int mark
)
572 struct space
*tadj
[4], *eadj
[4];
573 int i
, didsth
= 0, edge
, same
;
575 assert(tile
->type
== s_tile
);
576 adjacencies(state
, tile
, eadj
, tadj
);
577 for (i
= 0; i
< 4; i
++) {
578 if (!eadj
[i
]) continue;
580 edge
= (eadj
[i
]->flags
& F_EDGE_SET
) ? 1 : 0;
582 if (!(tile
->flags
& F_TILE_ASSOC
))
583 same
= (tadj
[i
]->flags
& F_TILE_ASSOC
) ? 0 : 1;
585 same
= ((tadj
[i
]->flags
& F_TILE_ASSOC
) &&
586 tile
->dotx
== tadj
[i
]->dotx
&&
587 tile
->doty
== tadj
[i
]->doty
) ? 1 : 0;
591 if (!edge
&& !same
) {
592 if (mark
) eadj
[i
]->flags
|= F_EDGE_SET
;
594 } else if (edge
&& same
) {
595 if (mark
) eadj
[i
]->flags
&= ~F_EDGE_SET
;
602 static void tiles_from_edge(struct game_state
*state
,
603 struct space
*sp
, struct space
**ts
)
607 if (IS_VERTICAL_EDGE(sp
->x
)) {
608 xs
[0] = sp
->x
-1; ys
[0] = sp
->y
;
609 xs
[1] = sp
->x
+1; ys
[1] = sp
->y
;
611 xs
[0] = sp
->x
; ys
[0] = sp
->y
-1;
612 xs
[1] = sp
->x
; ys
[1] = sp
->y
+1;
614 ts
[0] = INGRID(state
, xs
[0], ys
[0]) ? &SPACE(state
, xs
[0], ys
[0]) : NULL
;
615 ts
[1] = INGRID(state
, xs
[1], ys
[1]) ? &SPACE(state
, xs
[1], ys
[1]) : NULL
;
618 /* Returns a move string for use by 'solve', including the initial
619 * 'S' if issolve is true. */
620 static char *diff_game(game_state
*src
, game_state
*dest
, int issolve
)
622 int movelen
= 0, movesize
= 256, x
, y
, len
;
623 char *move
= snewn(movesize
, char), buf
[80], *sep
= "";
624 char achar
= issolve
? 'a' : 'A';
627 assert(src
->sx
== dest
->sx
&& src
->sy
== dest
->sy
);
630 move
[movelen
++] = 'S';
633 move
[movelen
] = '\0';
634 for (x
= 0; x
< src
->sx
; x
++) {
635 for (y
= 0; y
< src
->sy
; y
++) {
636 sps
= &SPACE(src
, x
, y
);
637 spd
= &SPACE(dest
, x
, y
);
639 assert(sps
->type
== spd
->type
);
642 if (sps
->type
== s_tile
) {
643 if ((sps
->flags
& F_TILE_ASSOC
) &&
644 (spd
->flags
& F_TILE_ASSOC
)) {
645 if (sps
->dotx
!= spd
->dotx
||
646 sps
->doty
!= spd
->doty
)
647 /* Both associated; change association, if different */
648 len
= sprintf(buf
, "%s%c%d,%d,%d,%d", sep
,
649 (int)achar
, x
, y
, spd
->dotx
, spd
->doty
);
650 } else if (sps
->flags
& F_TILE_ASSOC
)
651 /* Only src associated; remove. */
652 len
= sprintf(buf
, "%sU%d,%d", sep
, x
, y
);
653 else if (spd
->flags
& F_TILE_ASSOC
)
654 /* Only dest associated; add. */
655 len
= sprintf(buf
, "%s%c%d,%d,%d,%d", sep
,
656 (int)achar
, x
, y
, spd
->dotx
, spd
->doty
);
657 } else if (sps
->type
== s_edge
) {
658 if ((sps
->flags
& F_EDGE_SET
) != (spd
->flags
& F_EDGE_SET
))
659 /* edge flags are different; flip them. */
660 len
= sprintf(buf
, "%sE%d,%d", sep
, x
, y
);
663 if (movelen
+ len
>= movesize
) {
664 movesize
= movelen
+ len
+ 256;
665 move
= sresize(move
, movesize
, char);
667 strcpy(move
+ movelen
, buf
);
673 debug(("diff_game src then dest:\n"));
676 debug(("diff string %s\n", move
));
680 /* Returns 1 if a dot here would not be too close to any other dots
681 * (and would avoid other game furniture). */
682 static int dot_is_possible(game_state
*state
, space
*sp
, int allow_assoc
)
684 int bx
= 0, by
= 0, dx
, dy
;
686 #ifdef STANDALONE_PICTURE_GENERATOR
694 if (IS_VERTICAL_EDGE(sp
->x
)) {
704 for (dx
= -bx
; dx
<= bx
; dx
++) {
705 for (dy
= -by
; dy
<= by
; dy
++) {
706 if (!INGRID(state
, sp
->x
+dx
, sp
->y
+dy
)) continue;
708 adj
= &SPACE(state
, sp
->x
+dx
, sp
->y
+dy
);
710 #ifdef STANDALONE_PICTURE_GENERATOR
712 * Check that all the squares we're looking at have the
716 if (adj
->type
== s_tile
) {
717 int c
= picture
[(adj
->y
/ 2) * state
->w
+ (adj
->x
/ 2)];
721 return 0; /* colour mismatch */
726 if (!allow_assoc
&& (adj
->flags
& F_TILE_ASSOC
))
729 if (dx
!= 0 || dy
!= 0) {
730 /* Other than our own square, no dots nearby. */
731 if (adj
->flags
& (F_DOT
))
735 /* We don't want edges within our rectangle
736 * (but don't care about edges on the edge) */
737 if (abs(dx
) < bx
&& abs(dy
) < by
&&
738 adj
->flags
& F_EDGE_SET
)
745 /* ----------------------------------------------------------
746 * Game generation, structure creation, and descriptions.
749 static game_state
*blank_game(int w
, int h
)
751 game_state
*state
= snew(game_state
);
759 state
->grid
= snewn(state
->sx
* state
->sy
, struct space
);
760 state
->completed
= state
->used_solve
= 0;
762 for (x
= 0; x
< state
->sx
; x
++) {
763 for (y
= 0; y
< state
->sy
; y
++) {
764 struct space
*sp
= &SPACE(state
, x
, y
);
765 memset(sp
, 0, sizeof(struct space
));
768 if ((x
% 2) == 0 && (y
% 2) == 0)
770 else if ((x
% 2) == 0 || (y
% 2) == 0) {
772 if (x
== 0 || y
== 0 || x
== state
->sx
-1 || y
== state
->sy
-1)
773 sp
->flags
|= F_EDGE_SET
;
782 state
->me
= NULL
; /* filled in by new_game. */
788 static void game_update_dots(game_state
*state
)
790 int i
, n
, sz
= state
->sx
* state
->sy
;
792 if (state
->dots
) sfree(state
->dots
);
795 for (i
= 0; i
< sz
; i
++) {
796 if (state
->grid
[i
].flags
& F_DOT
) state
->ndots
++;
798 state
->dots
= snewn(state
->ndots
, space
*);
800 for (i
= 0; i
< sz
; i
++) {
801 if (state
->grid
[i
].flags
& F_DOT
)
802 state
->dots
[n
++] = &state
->grid
[i
];
806 static void clear_game(game_state
*state
, int cleardots
)
810 /* don't erase edge flags around outline! */
811 for (x
= 1; x
< state
->sx
-1; x
++) {
812 for (y
= 1; y
< state
->sy
-1; y
++) {
814 SPACE(state
, x
, y
).flags
= 0;
816 SPACE(state
, x
, y
).flags
&= (F_DOT
|F_DOT_BLACK
);
819 if (cleardots
) game_update_dots(state
);
822 static game_state
*dup_game(game_state
*state
)
824 game_state
*ret
= blank_game(state
->w
, state
->h
);
826 ret
->completed
= state
->completed
;
827 ret
->used_solve
= state
->used_solve
;
829 memcpy(ret
->grid
, state
->grid
,
830 ret
->sx
*ret
->sy
*sizeof(struct space
));
832 game_update_dots(ret
);
835 ret
->cdiff
= state
->cdiff
;
840 static void free_game(game_state
*state
)
842 if (state
->dots
) sfree(state
->dots
);
847 /* Game description is a sequence of letters representing the number
848 * of spaces (a = 0, y = 24) before the next dot; a-y for a white dot,
849 * and A-Y for a black dot. 'z' is 25 spaces (and no dot).
851 * I know it's a bitch to generate by hand, so we provide
855 static char *encode_game(game_state
*state
)
861 area
= (state
->sx
-2) * (state
->sy
-2);
863 desc
= snewn(area
, char);
866 for (y
= 1; y
< state
->sy
-1; y
++) {
867 for (x
= 1; x
< state
->sx
-1; x
++) {
868 f
= SPACE(state
, x
, y
).flags
;
870 /* a/A is 0 spaces between, b/B is 1 space, ...
871 * y/Y is 24 spaces, za/zA is 25 spaces, ...
872 * It's easier to count from 0 because we then
873 * don't have to special-case the top left-hand corner
874 * (which could be a dot with 0 spaces before it). */
882 *p
++ = ((f
& F_DOT_BLACK
) ? 'A' : 'a') + run
;
887 assert(p
- desc
< area
);
889 desc
= sresize(desc
, p
- desc
, char);
896 space
*olddot
, *newdot
;
899 enum { MD_CHECK
, MD_MOVE
};
901 static int movedot_cb(game_state
*state
, space
*tile
, void *vctx
)
903 struct movedot
*md
= (struct movedot
*)vctx
;
904 space
*newopp
= NULL
;
906 assert(tile
->type
== s_tile
);
907 assert(md
->olddot
&& md
->newdot
);
909 if (!(tile
->flags
& F_TILE_ASSOC
)) return 0;
910 if (tile
->dotx
!= md
->olddot
->x
|| tile
->doty
!= md
->olddot
->y
)
913 newopp
= space_opposite_dot(state
, tile
, md
->newdot
);
917 /* If the tile is associated with the old dot, check its
918 * opposite wrt the _new_ dot is empty or same assoc. */
919 if (!newopp
) return -1; /* no new opposite */
920 if (newopp
->flags
& F_TILE_ASSOC
) {
921 if (newopp
->dotx
!= md
->olddot
->x
||
922 newopp
->doty
!= md
->olddot
->y
)
923 return -1; /* associated, but wrong dot. */
925 #ifdef STANDALONE_PICTURE_GENERATOR
928 * Reject if either tile and the dot don't match in colour.
930 if (!(picture
[(tile
->y
/2) * state
->w
+ (tile
->x
/2)]) ^
931 !(md
->newdot
->flags
& F_DOT_BLACK
))
933 if (!(picture
[(newopp
->y
/2) * state
->w
+ (newopp
->x
/2)]) ^
934 !(md
->newdot
->flags
& F_DOT_BLACK
))
941 /* Move dot associations: anything that was associated
942 * with the old dot, and its opposite wrt the new dot,
943 * become associated with the new dot. */
945 debug(("Associating %d,%d and %d,%d with new dot %d,%d.\n",
946 tile
->x
, tile
->y
, newopp
->x
, newopp
->y
,
947 md
->newdot
->x
, md
->newdot
->y
));
948 add_assoc(state
, tile
, md
->newdot
);
949 add_assoc(state
, newopp
, md
->newdot
);
950 return 1; /* we did something! */
955 /* For the given dot, first see if we could expand it into all the given
956 * extra spaces (by checking for empty spaces on the far side), and then
957 * see if we can move the dot to shift the CoG to include the new spaces.
959 static int dot_expand_or_move(game_state
*state
, space
*dot
,
960 space
**toadd
, int nadd
)
963 int i
, ret
, nnew
, cx
, cy
;
966 debug(("dot_expand_or_move: %d tiles for dot %d,%d\n",
967 nadd
, dot
->x
, dot
->y
));
968 for (i
= 0; i
< nadd
; i
++)
969 debug(("dot_expand_or_move: dot %d,%d\n",
970 toadd
[i
]->x
, toadd
[i
]->y
));
971 assert(dot
->flags
& F_DOT
);
973 #ifdef STANDALONE_PICTURE_GENERATOR
976 * Reject the expansion totally if any of the new tiles are
979 for (i
= 0; i
< nadd
; i
++) {
980 if (!(picture
[(toadd
[i
]->y
/2) * state
->w
+ (toadd
[i
]->x
/2)]) ^
981 !(dot
->flags
& F_DOT_BLACK
))
987 /* First off, could we just expand the current dot's tile to cover
988 * the space(s) passed in and their opposites? */
989 for (i
= 0; i
< nadd
; i
++) {
990 tileopp
= space_opposite_dot(state
, toadd
[i
], dot
);
991 if (!tileopp
) goto noexpand
;
992 if (tileopp
->flags
& F_TILE_ASSOC
) goto noexpand
;
993 #ifdef STANDALONE_PICTURE_GENERATOR
996 * The opposite tiles have to be the right colour as well.
998 if (!(picture
[(tileopp
->y
/2) * state
->w
+ (tileopp
->x
/2)]) ^
999 !(dot
->flags
& F_DOT_BLACK
))
1004 /* OK, all spaces have valid empty opposites: associate spaces and
1005 * opposites with our dot. */
1006 for (i
= 0; i
< nadd
; i
++) {
1007 tileopp
= space_opposite_dot(state
, toadd
[i
], dot
);
1008 add_assoc(state
, toadd
[i
], dot
);
1009 add_assoc(state
, tileopp
, dot
);
1010 debug(("Added associations %d,%d and %d,%d --> %d,%d\n",
1011 toadd
[i
]->x
, toadd
[i
]->y
,
1012 tileopp
->x
, tileopp
->y
,
1019 /* Otherwise, try to move dot so as to encompass given spaces: */
1020 /* first, calculate the 'centre of gravity' of the new dot. */
1021 nnew
= dot
->nassoc
+ nadd
; /* number of tiles assoc. with new dot. */
1022 cx
= dot
->x
* dot
->nassoc
;
1023 cy
= dot
->y
* dot
->nassoc
;
1024 for (i
= 0; i
< nadd
; i
++) {
1028 /* If the CoG isn't a whole number, it's not possible. */
1029 if ((cx
% nnew
) != 0 || (cy
% nnew
) != 0) {
1030 debug(("Unable to move dot %d,%d, CoG not whole number.\n",
1034 cx
/= nnew
; cy
/= nnew
;
1036 /* Check whether all spaces in the old tile would have a good
1037 * opposite wrt the new dot. */
1039 md
.newdot
= &SPACE(state
, cx
, cy
);
1041 ret
= foreach_tile(state
, movedot_cb
, IMPOSSIBLE_QUITS
, &md
);
1043 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1047 /* Also check whether all spaces we're adding would have a good
1048 * opposite wrt the new dot. */
1049 for (i
= 0; i
< nadd
; i
++) {
1050 tileopp
= space_opposite_dot(state
, toadd
[i
], md
.newdot
);
1051 if (tileopp
&& (tileopp
->flags
& F_TILE_ASSOC
) &&
1052 (tileopp
->dotx
!= dot
->x
|| tileopp
->doty
!= dot
->y
)) {
1056 debug(("Unable to move dot %d,%d, new dot not symmetrical.\n",
1060 #ifdef STANDALONE_PICTURE_GENERATOR
1062 if (!(picture
[(tileopp
->y
/2) * state
->w
+ (tileopp
->x
/2)]) ^
1063 !(dot
->flags
& F_DOT_BLACK
))
1069 /* If we've got here, we're ok. First, associate all of 'toadd'
1070 * with the _old_ dot (so they'll get fixed up, with their opposites,
1071 * in the next step). */
1072 for (i
= 0; i
< nadd
; i
++) {
1073 debug(("Associating to-add %d,%d with old dot %d,%d.\n",
1074 toadd
[i
]->x
, toadd
[i
]->y
, dot
->x
, dot
->y
));
1075 add_assoc(state
, toadd
[i
], dot
);
1078 /* Finally, move the dot and fix up all the old associations. */
1079 debug(("Moving dot at %d,%d to %d,%d\n",
1080 dot
->x
, dot
->y
, md
.newdot
->x
, md
.newdot
->y
));
1082 #ifdef STANDALONE_PICTURE_GENERATOR
1083 int f
= dot
->flags
& F_DOT_BLACK
;
1087 #ifdef STANDALONE_PICTURE_GENERATOR
1088 md
.newdot
->flags
|= f
;
1093 ret
= foreach_tile(state
, movedot_cb
, 0, &md
);
1100 /* Hard-code to a max. of 2x2 squares, for speed (less malloc) */
1102 #define MAX_OUTSIDE 8
1104 #define MAX_TILE_PERC 20
1106 static int generate_try_block(game_state
*state
, random_state
*rs
,
1107 int x1
, int y1
, int x2
, int y2
)
1109 int x
, y
, nadd
= 0, nout
= 0, i
, maxsz
;
1110 space
*sp
, *toadd
[MAX_TOADD
], *outside
[MAX_OUTSIDE
], *dot
;
1112 if (!INGRID(state
, x1
, y1
) || !INGRID(state
, x2
, y2
)) return 0;
1114 /* We limit the maximum size of tiles to be ~2*sqrt(area); so,
1115 * a 5x5 grid shouldn't have anything >10 tiles, a 20x20 grid
1116 * nothing >40 tiles. */
1117 maxsz
= (int)sqrt((double)(state
->w
* state
->h
)) * 2;
1118 debug(("generate_try_block, maxsz %d\n", maxsz
));
1120 /* Make a static list of the spaces; if any space is already
1121 * associated then quit immediately. */
1122 for (x
= x1
; x
<= x2
; x
+= 2) {
1123 for (y
= y1
; y
<= y2
; y
+= 2) {
1124 assert(nadd
< MAX_TOADD
);
1125 sp
= &SPACE(state
, x
, y
);
1126 assert(sp
->type
== s_tile
);
1127 if (sp
->flags
& F_TILE_ASSOC
) return 0;
1132 /* Make a list of the spaces outside of our block, and shuffle it. */
1133 #define OUTSIDE(x, y) do { \
1134 if (INGRID(state, (x), (y))) { \
1135 assert(nout < MAX_OUTSIDE); \
1136 outside[nout++] = &SPACE(state, (x), (y)); \
1139 for (x
= x1
; x
<= x2
; x
+= 2) {
1143 for (y
= y1
; y
<= y2
; y
+= 2) {
1147 shuffle(outside
, nout
, sizeof(space
*), rs
);
1149 for (i
= 0; i
< nout
; i
++) {
1150 if (!(outside
[i
]->flags
& F_TILE_ASSOC
)) continue;
1151 dot
= &SPACE(state
, outside
[i
]->dotx
, outside
[i
]->doty
);
1152 if (dot
->nassoc
>= maxsz
) {
1153 debug(("Not adding to dot %d,%d, large enough (%d) already.\n",
1154 dot
->x
, dot
->y
, dot
->nassoc
));
1157 if (dot_expand_or_move(state
, dot
, toadd
, nadd
)) return 1;
1162 #ifdef STANDALONE_SOLVER
1164 #define MAXTRIES maxtries
1169 static int solver_obvious_dot(game_state
*state
,space
*dot
);
1173 static void generate_pass(game_state
*state
, random_state
*rs
, int *scratch
,
1174 int perc
, unsigned int flags
)
1176 int sz
= state
->sx
*state
->sy
, nspc
, i
, ret
;
1178 shuffle(scratch
, sz
, sizeof(int), rs
);
1180 /* This bug took me a, er, little while to track down. On PalmOS,
1181 * which has 16-bit signed ints, puzzles over about 9x9 started
1182 * failing to generate because the nspc calculation would start
1183 * to overflow, causing the dots not to be filled in properly. */
1184 nspc
= (int)(((long)perc
* (long)sz
) / 100L);
1185 debug(("generate_pass: %d%% (%d of %dx%d) squares, flags 0x%x\n",
1186 perc
, nspc
, state
->sx
, state
->sy
, flags
));
1188 for (i
= 0; i
< nspc
; i
++) {
1189 space
*sp
= &state
->grid
[scratch
[i
]];
1190 int x1
= sp
->x
, y1
= sp
->y
, x2
= sp
->x
, y2
= sp
->y
;
1192 if (sp
->type
== s_edge
) {
1193 if (IS_VERTICAL_EDGE(sp
->x
)) {
1199 if (sp
->type
!= s_vertex
) {
1200 /* heuristic; expanding from vertices tends to generate lots of
1201 * too-big regions of tiles. */
1202 if (generate_try_block(state
, rs
, x1
, y1
, x2
, y2
))
1203 continue; /* we expanded successfully. */
1206 if (!(flags
& GP_DOTS
)) continue;
1208 if ((sp
->type
== s_edge
) && (i
% 2)) {
1209 debug(("Omitting edge %d,%d as half-of.\n", sp
->x
, sp
->y
));
1213 /* If we've got here we might want to put a dot down. Check
1214 * if we can, and add one if so. */
1215 if (dot_is_possible(state
, sp
, 0)) {
1217 #ifdef STANDALONE_PICTURE_GENERATOR
1219 if (picture
[(sp
->y
/2) * state
->w
+ (sp
->x
/2)])
1220 sp
->flags
|= F_DOT_BLACK
;
1223 ret
= solver_obvious_dot(state
, sp
);
1225 debug(("Added dot (and obvious associations) at %d,%d\n",
1233 static int check_complete(game_state
*state
, int *dsf
, int *colours
);
1234 static int solver_state(game_state
*state
, int maxdiff
);
1236 static char *new_game_desc(game_params
*params
, random_state
*rs
,
1237 char **aux
, int interactive
)
1239 game_state
*state
= blank_game(params
->w
, params
->h
), *copy
;
1241 int *scratch
, sz
= state
->sx
*state
->sy
, i
;
1242 int diff
, ntries
= 0, cc
;
1244 /* Random list of squares to try and process, one-by-one. */
1245 scratch
= snewn(sz
, int);
1246 for (i
= 0; i
< sz
; i
++) scratch
[i
] = i
;
1249 clear_game(state
, 1);
1252 /* generate_pass(state, rs, scratch, 10, GP_DOTS); */
1253 /* generate_pass(state, rs, scratch, 100, 0); */
1254 generate_pass(state
, rs
, scratch
, 100, GP_DOTS
);
1256 game_update_dots(state
);
1260 char *tmp
= encode_game(state
);
1261 debug(("new_game_desc state %dx%d:%s\n", params
->w
, params
->h
, tmp
));
1266 for (i
= 0; i
< state
->sx
*state
->sy
; i
++)
1267 if (state
->grid
[i
].type
== s_tile
)
1268 outline_tile_fordot(state
, &state
->grid
[i
], TRUE
);
1269 cc
= check_complete(state
, NULL
, NULL
);
1272 copy
= dup_game(state
);
1273 clear_game(copy
, 0);
1275 diff
= solver_state(copy
, params
->diff
);
1278 assert(diff
!= DIFF_IMPOSSIBLE
);
1279 if (diff
!= params
->diff
) {
1281 * We'll grudgingly accept a too-easy puzzle, but we must
1282 * _not_ permit a too-hard one (one which the solver
1283 * couldn't handle at all).
1285 if (diff
> params
->diff
||
1286 ntries
< MAXTRIES
) goto generate
;
1289 #ifdef STANDALONE_PICTURE_GENERATOR
1291 * Postprocessing pass to prevent excessive numbers of adjacent
1292 * singletons. Iterate over all edges in random shuffled order;
1293 * for each edge that separates two regions, investigate
1294 * whether removing that edge and merging the regions would
1295 * still yield a valid and soluble puzzle. (The two regions
1296 * must also be the same colour, of course.) If so, do it.
1298 * This postprocessing pass is slow (due to repeated solver
1299 * invocations), and seems to be unnecessary during normal
1300 * unconstrained game generation. However, when generating a
1301 * game under colour constraints, excessive singletons seem to
1302 * turn up more often, so it's worth doing this.
1309 nposns
= params
->w
* (params
->h
+1) + params
->h
* (params
->w
+1);
1310 posns
= snewn(nposns
, int);
1311 for (i
= j
= 0; i
< state
->sx
*state
->sy
; i
++)
1312 if (state
->grid
[i
].type
== s_edge
)
1314 assert(j
== nposns
);
1316 shuffle(posns
, nposns
, sizeof(*posns
), rs
);
1318 for (i
= 0; i
< nposns
; i
++) {
1319 int x
, y
, x0
, y0
, x1
, y1
, cx
, cy
, cn
, cx0
, cy0
, cx1
, cy1
, tx
, ty
;
1320 space
*s0
, *s1
, *ts
, *d0
, *d1
, *dn
;
1323 /* Coordinates of edge space */
1324 x
= posns
[i
] % state
->sx
;
1325 y
= posns
[i
] / state
->sx
;
1327 /* Coordinates of square spaces on either side of edge */
1328 x0
= ((x
+1) & ~1) - 1; /* round down to next odd number */
1329 y0
= ((y
+1) & ~1) - 1;
1330 x1
= 2*x
-x0
; /* and reflect about x to get x1 */
1333 if (!INGRID(state
, x0
, y0
) || !INGRID(state
, x1
, y1
))
1334 continue; /* outermost edge of grid */
1335 s0
= &SPACE(state
, x0
, y0
);
1336 s1
= &SPACE(state
, x1
, y1
);
1337 assert(s0
->type
== s_tile
&& s1
->type
== s_tile
);
1339 if (s0
->dotx
== s1
->dotx
&& s0
->doty
== s1
->doty
)
1340 continue; /* tiles _already_ owned by same dot */
1342 d0
= &SPACE(state
, s0
->dotx
, s0
->doty
);
1343 d1
= &SPACE(state
, s1
->dotx
, s1
->doty
);
1345 if ((d0
->flags
^ d1
->flags
) & F_DOT_BLACK
)
1346 continue; /* different colours: cannot merge */
1349 * Work out where the centre of gravity of the new
1352 cx
= d0
->nassoc
* d0
->x
+ d1
->nassoc
* d1
->x
;
1353 cy
= d0
->nassoc
* d0
->y
+ d1
->nassoc
* d1
->y
;
1354 cn
= d0
->nassoc
+ d1
->nassoc
;
1355 if (cx
% cn
|| cy
% cn
)
1356 continue; /* CoG not at integer coordinates */
1359 assert(INUI(state
, cx
, cy
));
1362 * Ensure that the CoG would actually be _in_ the new
1363 * region, by verifying that all its surrounding tiles
1364 * belong to one or other of our two dots.
1366 cx0
= ((cx
+1) & ~1) - 1; /* round down to next odd number */
1367 cy0
= ((cy
+1) & ~1) - 1;
1368 cx1
= 2*cx
-cx0
; /* and reflect about cx to get cx1 */
1371 for (ty
= cy0
; ty
<= cy1
; ty
+= 2)
1372 for (tx
= cx0
; tx
<= cx1
; tx
+= 2) {
1373 ts
= &SPACE(state
, tx
, ty
);
1374 assert(ts
->type
== s_tile
);
1375 if ((ts
->dotx
!= d0
->x
|| ts
->doty
!= d0
->y
) &&
1376 (ts
->dotx
!= d1
->x
|| ts
->doty
!= d1
->y
))
1383 * Verify that for every tile in either source region,
1384 * that tile's image in the new CoG is also in one of
1385 * the two source regions.
1387 for (ty
= 1; ty
< state
->sy
; ty
+= 2) {
1388 for (tx
= 1; tx
< state
->sx
; tx
+= 2) {
1391 ts
= &SPACE(state
, tx
, ty
);
1392 assert(ts
->type
== s_tile
);
1393 if ((ts
->dotx
!= d0
->x
|| ts
->doty
!= d0
->y
) &&
1394 (ts
->dotx
!= d1
->x
|| ts
->doty
!= d1
->y
))
1395 continue; /* not part of these tiles anyway */
1398 if (!INGRID(state
, tx1
, ty1
)) {
1402 ts
= &SPACE(state
, cx
+cx
-tx
, cy
+cy
-ty
);
1403 if ((ts
->dotx
!= d0
->x
|| ts
->doty
!= d0
->y
) &&
1404 (ts
->dotx
!= d1
->x
|| ts
->doty
!= d1
->y
)) {
1416 * Now we're clear to attempt the merge. We take a copy
1417 * of the game state first, so we can revert it easily
1418 * if the resulting puzzle turns out to have become
1421 copy2
= dup_game(state
);
1425 dn
= &SPACE(state
, cx
, cy
);
1427 dn
->flags
|= (d0
->flags
& F_DOT_BLACK
);
1428 for (ty
= 1; ty
< state
->sy
; ty
+= 2) {
1429 for (tx
= 1; tx
< state
->sx
; tx
+= 2) {
1430 ts
= &SPACE(state
, tx
, ty
);
1431 assert(ts
->type
== s_tile
);
1432 if ((ts
->dotx
!= d0
->x
|| ts
->doty
!= d0
->y
) &&
1433 (ts
->dotx
!= d1
->x
|| ts
->doty
!= d1
->y
))
1434 continue; /* not part of these tiles anyway */
1435 add_assoc(state
, ts
, dn
);
1439 copy
= dup_game(state
);
1440 clear_game(copy
, 0);
1442 newdiff
= solver_state(copy
, params
->diff
);
1444 if (diff
== newdiff
) {
1445 /* Still just as soluble. Let the merge stand. */
1448 /* Became insoluble. Revert. */
1457 desc
= encode_game(state
);
1458 #ifndef STANDALONE_SOLVER
1459 debug(("new_game_desc generated: \n"));
1469 static int solver_obvious(game_state
*state
);
1471 static int dots_too_close(game_state
*state
)
1473 /* Quick-and-dirty check, using half the solver:
1474 * solver_obvious will only fail if the dots are
1475 * too close together, so dot-proximity associations
1477 game_state
*tmp
= dup_game(state
);
1478 int ret
= solver_obvious(tmp
);
1480 return (ret
== -1) ? 1 : 0;
1483 static game_state
*load_game(game_params
*params
, char *desc
,
1486 game_state
*state
= blank_game(params
->w
, params
->h
);
1498 if (n
>= 'a' && n
<= 'y') {
1501 } else if (n
>= 'A' && n
<= 'Y') {
1505 why
= "Invalid characters in game description"; goto fail
;
1507 /* if we got here we incremented i and have a dot to add. */
1508 y
= (i
/ (state
->sx
-2)) + 1;
1509 x
= (i
% (state
->sx
-2)) + 1;
1510 if (!INUI(state
, x
, y
)) {
1511 why
= "Too much data to fit in grid"; goto fail
;
1513 add_dot(&SPACE(state
, x
, y
));
1514 SPACE(state
, x
, y
).flags
|= df
;
1517 game_update_dots(state
);
1519 if (dots_too_close(state
)) {
1520 why
= "Dots too close together"; goto fail
;
1527 if (why_r
) *why_r
= why
;
1531 static char *validate_desc(game_params
*params
, char *desc
)
1534 game_state
*dummy
= load_game(params
, desc
, &why
);
1543 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
1545 game_state
*state
= load_game(params
, desc
, NULL
);
1547 assert("Unable to load ?validated game.");
1556 /* ----------------------------------------------------------
1557 * Solver and all its little wizards.
1560 int solver_recurse_depth
;
1562 typedef struct solver_ctx
{
1564 int sz
; /* state->sx * state->sy */
1565 space
**scratch
; /* size sz */
1569 static solver_ctx
*new_solver(game_state
*state
)
1571 solver_ctx
*sctx
= snew(solver_ctx
);
1572 sctx
->state
= state
;
1573 sctx
->sz
= state
->sx
*state
->sy
;
1574 sctx
->scratch
= snewn(sctx
->sz
, space
*);
1578 static void free_solver(solver_ctx
*sctx
)
1580 sfree(sctx
->scratch
);
1584 /* Solver ideas so far:
1586 * For any empty space, work out how many dots it could associate
1588 * it needs line-of-sight
1589 * it needs an empty space on the far side
1590 * any adjacent lines need corresponding line possibilities.
1593 /* The solver_ctx should keep a list of dot positions, for quicker looping.
1595 * Solver techniques, in order of difficulty:
1596 * obvious adjacency to dots
1597 * transferring tiles to opposite side
1598 * transferring lines to opposite side
1599 * one possible dot for a given tile based on opposite availability
1600 * tile with 3 definite edges next to an associated tile must associate
1603 * one possible dot for a given tile based on line-of-sight
1606 static int solver_add_assoc(game_state
*state
, space
*tile
, int dx
, int dy
,
1609 space
*dot
, *tile_opp
;
1611 dot
= &SPACE(state
, dx
, dy
);
1612 tile_opp
= space_opposite_dot(state
, tile
, dot
);
1614 assert(tile
->type
== s_tile
);
1615 if (tile
->flags
& F_TILE_ASSOC
) {
1616 if ((tile
->dotx
!= dx
) || (tile
->doty
!= dy
)) {
1617 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1618 "already --> %d,%d.\n",
1619 solver_recurse_depth
*4, "",
1620 tile
->x
, tile
->y
, dx
, dy
, why
,
1621 tile
->dotx
, tile
->doty
));
1624 return 0; /* no-op */
1627 solvep(("%*s%d,%d --> %d,%d impossible, no opposite tile.\n",
1628 solver_recurse_depth
*4, "", tile
->x
, tile
->y
, dx
, dy
));
1631 if (tile_opp
->flags
& F_TILE_ASSOC
&&
1632 (tile_opp
->dotx
!= dx
|| tile_opp
->doty
!= dy
)) {
1633 solvep(("%*sSet %d,%d --> %d,%d (%s) impossible; "
1634 "opposite already --> %d,%d.\n",
1635 solver_recurse_depth
*4, "",
1636 tile
->x
, tile
->y
, dx
, dy
, why
,
1637 tile_opp
->dotx
, tile_opp
->doty
));
1641 add_assoc(state
, tile
, dot
);
1642 add_assoc(state
, tile_opp
, dot
);
1643 solvep(("%*sSetting %d,%d --> %d,%d (%s).\n",
1644 solver_recurse_depth
*4, "",
1645 tile
->x
, tile
->y
,dx
, dy
, why
));
1646 solvep(("%*sSetting %d,%d --> %d,%d (%s, opposite).\n",
1647 solver_recurse_depth
*4, "",
1648 tile_opp
->x
, tile_opp
->y
, dx
, dy
, why
));
1652 static int solver_obvious_dot(game_state
*state
, space
*dot
)
1654 int dx
, dy
, ret
, didsth
= 0;
1657 debug(("%*ssolver_obvious_dot for %d,%d.\n",
1658 solver_recurse_depth
*4, "", dot
->x
, dot
->y
));
1660 assert(dot
->flags
& F_DOT
);
1661 for (dx
= -1; dx
<= 1; dx
++) {
1662 for (dy
= -1; dy
<= 1; dy
++) {
1663 if (!INGRID(state
, dot
->x
+dx
, dot
->y
+dy
)) continue;
1665 tile
= &SPACE(state
, dot
->x
+dx
, dot
->y
+dy
);
1666 if (tile
->type
== s_tile
) {
1667 ret
= solver_add_assoc(state
, tile
, dot
->x
, dot
->y
,
1669 if (ret
< 0) return -1;
1670 if (ret
> 0) didsth
= 1;
1677 static int solver_obvious(game_state
*state
)
1679 int i
, didsth
= 0, ret
;
1681 debug(("%*ssolver_obvious.\n", solver_recurse_depth
*4, ""));
1683 for (i
= 0; i
< state
->ndots
; i
++) {
1684 ret
= solver_obvious_dot(state
, state
->dots
[i
]);
1685 if (ret
< 0) return -1;
1686 if (ret
> 0) didsth
= 1;
1691 static int solver_lines_opposite_cb(game_state
*state
, space
*edge
, void *ctx
)
1693 int didsth
= 0, n
, dx
, dy
;
1694 space
*tiles
[2], *tile_opp
, *edge_opp
;
1696 assert(edge
->type
== s_edge
);
1698 tiles_from_edge(state
, edge
, tiles
);
1700 /* if tiles[0] && tiles[1] && they're both associated
1701 * and they're both associated with different dots,
1702 * ensure the line is set. */
1703 if (!(edge
->flags
& F_EDGE_SET
) &&
1704 tiles
[0] && tiles
[1] &&
1705 (tiles
[0]->flags
& F_TILE_ASSOC
) &&
1706 (tiles
[1]->flags
& F_TILE_ASSOC
) &&
1707 (tiles
[0]->dotx
!= tiles
[1]->dotx
||
1708 tiles
[0]->doty
!= tiles
[1]->doty
)) {
1709 /* No edge, but the two adjacent tiles are both
1710 * associated with different dots; add the edge. */
1711 solvep(("%*sSetting edge %d,%d - tiles different dots.\n",
1712 solver_recurse_depth
*4, "", edge
->x
, edge
->y
));
1713 edge
->flags
|= F_EDGE_SET
;
1717 if (!(edge
->flags
& F_EDGE_SET
)) return didsth
;
1718 for (n
= 0; n
< 2; n
++) {
1719 if (!tiles
[n
]) continue;
1720 assert(tiles
[n
]->type
== s_tile
);
1721 if (!(tiles
[n
]->flags
& F_TILE_ASSOC
)) continue;
1723 tile_opp
= tile_opposite(state
, tiles
[n
]);
1725 solvep(("%*simpossible: edge %d,%d has assoc. tile %d,%d"
1726 " with no opposite.\n",
1727 solver_recurse_depth
*4, "",
1728 edge
->x
, edge
->y
, tiles
[n
]->x
, tiles
[n
]->y
));
1729 /* edge of tile has no opposite edge (off grid?);
1730 * this is impossible. */
1734 dx
= tiles
[n
]->x
- edge
->x
;
1735 dy
= tiles
[n
]->y
- edge
->y
;
1736 assert(INGRID(state
, tile_opp
->x
+dx
, tile_opp
->y
+dy
));
1737 edge_opp
= &SPACE(state
, tile_opp
->x
+dx
, tile_opp
->y
+dy
);
1738 if (!(edge_opp
->flags
& F_EDGE_SET
)) {
1739 solvep(("%*sSetting edge %d,%d as opposite %d,%d\n",
1740 solver_recurse_depth
*4, "",
1741 tile_opp
->x
-dx
, tile_opp
->y
-dy
, edge
->x
, edge
->y
));
1742 edge_opp
->flags
|= F_EDGE_SET
;
1749 static int solver_spaces_oneposs_cb(game_state
*state
, space
*tile
, void *ctx
)
1752 struct space
*edgeadj
[4], *tileadj
[4];
1755 assert(tile
->type
== s_tile
);
1756 if (tile
->flags
& F_TILE_ASSOC
) return 0;
1758 adjacencies(state
, tile
, edgeadj
, tileadj
);
1760 /* Empty tile. If each edge is either set, or associated with
1761 * the same dot, we must also associate with dot. */
1762 eset
= 0; dotx
= -1; doty
= -1;
1763 for (n
= 0; n
< 4; n
++) {
1765 assert(edgeadj
[n
]->type
== s_edge
);
1766 if (edgeadj
[n
]->flags
& F_EDGE_SET
) {
1770 assert(tileadj
[n
]->type
== s_tile
);
1772 /* If an adjacent tile is empty we can't make any deductions.*/
1773 if (!(tileadj
[n
]->flags
& F_TILE_ASSOC
))
1776 /* If an adjacent tile is assoc. with a different dot
1777 * we can't make any deductions. */
1778 if (dotx
!= -1 && doty
!= -1 &&
1779 (tileadj
[n
]->dotx
!= dotx
||
1780 tileadj
[n
]->doty
!= doty
))
1783 dotx
= tileadj
[n
]->dotx
;
1784 doty
= tileadj
[n
]->doty
;
1788 solvep(("%*simpossible: empty tile %d,%d has 4 edges\n",
1789 solver_recurse_depth
*4, "",
1793 assert(dotx
!= -1 && doty
!= -1);
1795 ret
= solver_add_assoc(state
, tile
, dotx
, doty
, "rest are edges");
1796 if (ret
== -1) return -1;
1797 assert(ret
!= 0); /* really should have done something. */
1802 /* Improved algorithm for tracking line-of-sight from dots, and not spaces.
1804 * The solver_ctx already stores a list of dots: the algorithm proceeds by
1805 * expanding outwards from each dot in turn, expanding first to the boundary
1806 * of its currently-connected tile and then to all empty tiles that could see
1807 * it. Empty tiles will be flagged with a 'can see dot <x,y>' sticker.
1809 * Expansion will happen by (symmetrically opposite) pairs of squares; if
1810 * a square hasn't an opposite number there's no point trying to expand through
1811 * it. Empty tiles will therefore also be tagged in pairs.
1813 * If an empty tile already has a 'can see dot <x,y>' tag from a previous dot,
1814 * it (and its partner) gets untagged (or, rather, a 'can see two dots' tag)
1815 * because we're looking for single-dot possibilities.
1817 * Once we've gone through all the dots, any which still have a 'can see dot'
1818 * tag get associated with that dot (because it must have been the only one);
1819 * any without any tag (i.e. that could see _no_ dots) cause an impossibility
1822 * The expansion will happen each time with a stored list of (space *) pairs,
1823 * rather than a mark-and-sweep idea; that's horrifically inefficient.
1825 * expansion algorithm:
1827 * * allocate list of (space *) the size of s->sx*s->sy.
1828 * * allocate second grid for (flags, dotx, doty) size of sx*sy.
1830 * clear second grid (flags = 0, all dotx and doty = 0)
1831 * flags: F_REACHABLE, F_MULTIPLE
1834 * * for each dot, start with one pair of tiles that are associated with it --
1835 * * vertex --> (dx+1, dy+1), (dx-1, dy-1)
1836 * * edge --> (adj1, adj2)
1837 * * tile --> (tile, tile) ???
1838 * * mark that pair of tiles with F_MARK, clear all other F_MARKs.
1839 * * add two tiles to start of list.
1841 * set start = 0, end = next = 2
1843 * from (start to end-1, step 2) {
1844 * * we have two tiles (t1, t2), opposites wrt our dot.
1845 * * for each (at1) sensible adjacent tile to t1 (i.e. not past an edge):
1846 * * work out at2 as the opposite to at1
1847 * * assert at1 and at2 have the same F_MARK values.
1848 * * if at1 & F_MARK ignore it (we've been there on a previous sweep)
1849 * * if either are associated with a different dot
1850 * * mark both with F_MARK (so we ignore them later)
1851 * * otherwise (assoc. with our dot, or empty):
1852 * * mark both with F_MARK
1853 * * add their space * values to the end of the list, set next += 2.
1857 * * we didn't add any new squares; exit the loop.
1859 * * set start = next+1, end = next. go round again
1861 * We've finished expanding from the dot. Now, for each square we have
1862 * in our list (--> each square with F_MARK):
1863 * * if the tile is empty:
1864 * * if F_REACHABLE was already set
1867 * * set F_REACHABLE, set dotx and doty to our dot.
1869 * Then, continue the whole thing for each dot in turn.
1871 * Once we've done for each dot, go through the entire grid looking for
1872 * empty tiles: for each empty tile:
1873 * if F_REACHABLE and not F_MULTIPLE, set that dot (and its double)
1874 * if !F_REACHABLE, return as impossible.
1878 /* Returns 1 if this tile is either already associated with this dot,
1880 static int solver_expand_checkdot(space
*tile
, space
*dot
)
1882 if (!(tile
->flags
& F_TILE_ASSOC
)) return 1;
1883 if (tile
->dotx
== dot
->x
&& tile
->doty
== dot
->y
) return 1;
1887 static void solver_expand_fromdot(game_state
*state
, space
*dot
, solver_ctx
*sctx
)
1889 int i
, j
, x
, y
, start
, end
, next
;
1892 /* Clear the grid of the (space) flags we'll use. */
1894 /* This is well optimised; analysis showed that:
1895 for (i = 0; i < sctx->sz; i++) state->grid[i].flags &= ~F_MARK;
1896 took up ~85% of the total function time! */
1897 for (y
= 1; y
< state
->sy
; y
+= 2) {
1898 sp
= &SPACE(state
, 1, y
);
1899 for (x
= 1; x
< state
->sx
; x
+= 2, sp
+= 2)
1900 sp
->flags
&= ~F_MARK
;
1903 /* Seed the list of marked squares with two that must be associated
1904 * with our dot (possibly the same space) */
1905 if (dot
->type
== s_tile
) {
1906 sctx
->scratch
[0] = sctx
->scratch
[1] = dot
;
1907 } else if (dot
->type
== s_edge
) {
1908 tiles_from_edge(state
, dot
, sctx
->scratch
);
1909 } else if (dot
->type
== s_vertex
) {
1910 /* pick two of the opposite ones arbitrarily. */
1911 sctx
->scratch
[0] = &SPACE(state
, dot
->x
-1, dot
->y
-1);
1912 sctx
->scratch
[1] = &SPACE(state
, dot
->x
+1, dot
->y
+1);
1914 assert(sctx
->scratch
[0]->flags
& F_TILE_ASSOC
);
1915 assert(sctx
->scratch
[1]->flags
& F_TILE_ASSOC
);
1917 sctx
->scratch
[0]->flags
|= F_MARK
;
1918 sctx
->scratch
[1]->flags
|= F_MARK
;
1920 debug(("%*sexpand from dot %d,%d seeded with %d,%d and %d,%d.\n",
1921 solver_recurse_depth
*4, "", dot
->x
, dot
->y
,
1922 sctx
->scratch
[0]->x
, sctx
->scratch
[0]->y
,
1923 sctx
->scratch
[1]->x
, sctx
->scratch
[1]->y
));
1925 start
= 0; end
= 2; next
= 2;
1928 debug(("%*sexpand: start %d, end %d, next %d\n",
1929 solver_recurse_depth
*4, "", start
, end
, next
));
1930 for (i
= start
; i
< end
; i
+= 2) {
1931 space
*t1
= sctx
->scratch
[i
]/*, *t2 = sctx->scratch[i+1]*/;
1932 space
*edges
[4], *tileadj
[4], *tileadj2
;
1934 adjacencies(state
, t1
, edges
, tileadj
);
1936 for (j
= 0; j
< 4; j
++) {
1938 if (edges
[j
]->flags
& F_EDGE_SET
) continue;
1941 if (tileadj
[j
]->flags
& F_MARK
) continue; /* seen before. */
1943 /* We have a tile adjacent to t1; find its opposite. */
1944 tileadj2
= space_opposite_dot(state
, tileadj
[j
], dot
);
1946 debug(("%*sMarking %d,%d, no opposite.\n",
1947 solver_recurse_depth
*4, "",
1948 tileadj
[j
]->x
, tileadj
[j
]->y
));
1949 tileadj
[j
]->flags
|= F_MARK
;
1950 continue; /* no opposite, so mark for next time. */
1952 /* If the tile had an opposite we should have either seen both of
1953 * these, or neither of these, before. */
1954 assert(!(tileadj2
->flags
& F_MARK
));
1956 if (solver_expand_checkdot(tileadj
[j
], dot
) &&
1957 solver_expand_checkdot(tileadj2
, dot
)) {
1958 /* Both tiles could associate with this dot; add them to
1960 debug(("%*sAdding %d,%d and %d,%d to possibles list.\n",
1961 solver_recurse_depth
*4, "",
1962 tileadj
[j
]->x
, tileadj
[j
]->y
, tileadj2
->x
, tileadj2
->y
));
1963 sctx
->scratch
[next
++] = tileadj
[j
];
1964 sctx
->scratch
[next
++] = tileadj2
;
1966 /* Either way, we've seen these tiles already so mark them. */
1967 debug(("%*sMarking %d,%d and %d,%d.\n",
1968 solver_recurse_depth
*4, "",
1969 tileadj
[j
]->x
, tileadj
[j
]->y
, tileadj2
->x
, tileadj2
->y
));
1970 tileadj
[j
]->flags
|= F_MARK
;
1971 tileadj2
->flags
|= F_MARK
;
1975 /* We added more squares; go back and try again. */
1976 start
= end
; end
= next
; goto expand
;
1979 /* We've expanded as far as we can go. Now we update the main flags
1980 * on all tiles we've expanded into -- if they were empty, we have
1981 * found possible associations for this dot. */
1982 for (i
= 0; i
< end
; i
++) {
1983 if (sctx
->scratch
[i
]->flags
& F_TILE_ASSOC
) continue;
1984 if (sctx
->scratch
[i
]->flags
& F_REACHABLE
) {
1985 /* This is (at least) the second dot this tile could
1986 * associate with. */
1987 debug(("%*sempty tile %d,%d could assoc. other dot %d,%d\n",
1988 solver_recurse_depth
*4, "",
1989 sctx
->scratch
[i
]->x
, sctx
->scratch
[i
]->y
, dot
->x
, dot
->y
));
1990 sctx
->scratch
[i
]->flags
|= F_MULTIPLE
;
1992 /* This is the first (possibly only) dot. */
1993 debug(("%*sempty tile %d,%d could assoc. 1st dot %d,%d\n",
1994 solver_recurse_depth
*4, "",
1995 sctx
->scratch
[i
]->x
, sctx
->scratch
[i
]->y
, dot
->x
, dot
->y
));
1996 sctx
->scratch
[i
]->flags
|= F_REACHABLE
;
1997 sctx
->scratch
[i
]->dotx
= dot
->x
;
1998 sctx
->scratch
[i
]->doty
= dot
->y
;
2004 static int solver_expand_postcb(game_state
*state
, space
*tile
, void *ctx
)
2006 assert(tile
->type
== s_tile
);
2008 if (tile
->flags
& F_TILE_ASSOC
) return 0;
2010 if (!(tile
->flags
& F_REACHABLE
)) {
2011 solvep(("%*simpossible: space (%d,%d) can reach no dots.\n",
2012 solver_recurse_depth
*4, "", tile
->x
, tile
->y
));
2015 if (tile
->flags
& F_MULTIPLE
) return 0;
2017 return solver_add_assoc(state
, tile
, tile
->dotx
, tile
->doty
,
2018 "single possible dot after expansion");
2021 static int solver_expand_dots(game_state
*state
, solver_ctx
*sctx
)
2025 for (i
= 0; i
< sctx
->sz
; i
++)
2026 state
->grid
[i
].flags
&= ~(F_REACHABLE
|F_MULTIPLE
);
2028 for (i
= 0; i
< state
->ndots
; i
++)
2029 solver_expand_fromdot(state
, state
->dots
[i
], sctx
);
2031 return foreach_tile(state
, solver_expand_postcb
, IMPOSSIBLE_QUITS
, sctx
);
2034 struct recurse_ctx
{
2039 static int solver_recurse_cb(game_state
*state
, space
*tile
, void *ctx
)
2041 struct recurse_ctx
*rctx
= (struct recurse_ctx
*)ctx
;
2044 assert(tile
->type
== s_tile
);
2045 if (tile
->flags
& F_TILE_ASSOC
) return 0;
2047 /* We're unassociated: count up all the dots we could associate with. */
2048 for (i
= 0; i
< state
->ndots
; i
++) {
2049 if (dotfortile(state
, tile
, state
->dots
[i
]))
2052 if (n
> rctx
->bestn
) {
2059 static int solver_state(game_state
*state
, int maxdiff
);
2061 #define MAXRECURSE 5
2063 static int solver_recurse(game_state
*state
, int maxdiff
)
2065 int diff
= DIFF_IMPOSSIBLE
, ret
, n
, gsz
= state
->sx
* state
->sy
;
2066 space
*ingrid
, *outgrid
= NULL
, *bestopp
;
2067 struct recurse_ctx rctx
;
2069 if (solver_recurse_depth
>= MAXRECURSE
) {
2070 solvep(("Limiting recursion to %d, returning.", MAXRECURSE
));
2071 return DIFF_UNFINISHED
;
2074 /* Work out the cell to recurse on; go through all unassociated tiles
2075 * and find which one has the most possible dots it could associate
2080 foreach_tile(state
, solver_recurse_cb
, 0, &rctx
);
2081 if (rctx
.bestn
== 0) return DIFF_IMPOSSIBLE
; /* or assert? */
2084 solvep(("%*sRecursing around %d,%d, with %d possible dots.\n",
2085 solver_recurse_depth
*4, "",
2086 rctx
.best
->x
, rctx
.best
->y
, rctx
.bestn
));
2088 #ifdef STANDALONE_SOLVER
2089 solver_recurse_depth
++;
2092 ingrid
= snewn(gsz
, struct space
);
2093 memcpy(ingrid
, state
->grid
, gsz
* sizeof(struct space
));
2095 for (n
= 0; n
< state
->ndots
; n
++) {
2096 memcpy(state
->grid
, ingrid
, gsz
* sizeof(struct space
));
2098 if (!dotfortile(state
, rctx
.best
, state
->dots
[n
])) continue;
2100 /* set cell (temporarily) pointing to that dot. */
2101 solver_add_assoc(state
, rctx
.best
,
2102 state
->dots
[n
]->x
, state
->dots
[n
]->y
,
2103 "Attempting for recursion");
2105 ret
= solver_state(state
, maxdiff
);
2107 if (diff
== DIFF_IMPOSSIBLE
&& ret
!= DIFF_IMPOSSIBLE
) {
2108 /* we found our first solved grid; copy it away. */
2110 outgrid
= snewn(gsz
, struct space
);
2111 memcpy(outgrid
, state
->grid
, gsz
* sizeof(struct space
));
2113 /* reset cell back to unassociated. */
2114 bestopp
= tile_opposite(state
, rctx
.best
);
2115 assert(bestopp
&& bestopp
->flags
& F_TILE_ASSOC
);
2117 remove_assoc(state
, rctx
.best
);
2118 remove_assoc(state
, bestopp
);
2120 if (ret
== DIFF_AMBIGUOUS
|| ret
== DIFF_UNFINISHED
)
2122 else if (ret
== DIFF_IMPOSSIBLE
)
2125 /* precisely one solution */
2126 if (diff
== DIFF_IMPOSSIBLE
)
2127 diff
= DIFF_UNREASONABLE
;
2129 diff
= DIFF_AMBIGUOUS
;
2131 /* if we've found >1 solution, or ran out of recursion,
2132 * give up immediately. */
2133 if (diff
== DIFF_AMBIGUOUS
|| diff
== DIFF_UNFINISHED
)
2137 #ifdef STANDALONE_SOLVER
2138 solver_recurse_depth
--;
2142 /* we found (at least one) soln; copy it back to state */
2143 memcpy(state
->grid
, outgrid
, gsz
* sizeof(struct space
));
2150 static int solver_state(game_state
*state
, int maxdiff
)
2152 solver_ctx
*sctx
= new_solver(state
);
2153 int ret
, diff
= DIFF_NORMAL
;
2155 #ifdef STANDALONE_PICTURE_GENERATOR
2156 /* hack, hack: set picture to NULL during solving so that add_assoc
2157 * won't complain when we attempt recursive guessing and guess wrong */
2158 int *savepic
= picture
;
2162 ret
= solver_obvious(state
);
2164 diff
= DIFF_IMPOSSIBLE
;
2168 #define CHECKRET(d) do { \
2169 if (ret < 0) { diff = DIFF_IMPOSSIBLE; goto got_result; } \
2170 if (ret > 0) { diff = max(diff, (d)); goto cont; } \
2175 ret
= foreach_edge(state
, solver_lines_opposite_cb
,
2176 IMPOSSIBLE_QUITS
, sctx
);
2177 CHECKRET(DIFF_NORMAL
);
2179 ret
= foreach_tile(state
, solver_spaces_oneposs_cb
,
2180 IMPOSSIBLE_QUITS
, sctx
);
2181 CHECKRET(DIFF_NORMAL
);
2183 ret
= solver_expand_dots(state
, sctx
);
2184 CHECKRET(DIFF_NORMAL
);
2186 if (maxdiff
<= DIFF_NORMAL
)
2191 /* if we reach here, we've made no deductions, so we terminate. */
2195 if (check_complete(state
, NULL
, NULL
)) goto got_result
;
2197 diff
= (maxdiff
>= DIFF_UNREASONABLE
) ?
2198 solver_recurse(state
, maxdiff
) : DIFF_UNFINISHED
;
2202 #ifndef STANDALONE_SOLVER
2203 debug(("solver_state ends, diff %s:\n", galaxies_diffnames
[diff
]));
2207 #ifdef STANDALONE_PICTURE_GENERATOR
2215 static char *solve_game(game_state
*state
, game_state
*currstate
,
2216 char *aux
, char **error
)
2218 game_state
*tosolve
;
2223 tosolve
= dup_game(currstate
);
2224 diff
= solver_state(tosolve
, DIFF_UNREASONABLE
);
2225 if (diff
!= DIFF_UNFINISHED
&& diff
!= DIFF_IMPOSSIBLE
) {
2226 debug(("solve_game solved with current state.\n"));
2231 tosolve
= dup_game(state
);
2232 diff
= solver_state(tosolve
, DIFF_UNREASONABLE
);
2233 if (diff
!= DIFF_UNFINISHED
&& diff
!= DIFF_IMPOSSIBLE
) {
2234 debug(("solve_game solved with original state.\n"));
2243 * Clear tile associations: the solution will only include the
2246 for (i
= 0; i
< tosolve
->sx
*tosolve
->sy
; i
++)
2247 tosolve
->grid
[i
].flags
&= ~F_TILE_ASSOC
;
2248 ret
= diff_game(currstate
, tosolve
, 1);
2254 /* ----------------------------------------------------------
2260 int dx
, dy
; /* pixel coords of drag pos. */
2261 int dotx
, doty
; /* grid coords of dot we're dragging from. */
2262 int srcx
, srcy
; /* grid coords of drag start */
2263 int cur_x
, cur_y
, cur_visible
;
2266 static game_ui
*new_ui(game_state
*state
)
2268 game_ui
*ui
= snew(game_ui
);
2269 ui
->dragging
= FALSE
;
2270 ui
->cur_x
= ui
->cur_y
= 1;
2271 ui
->cur_visible
= 0;
2275 static void free_ui(game_ui
*ui
)
2280 static char *encode_ui(game_ui
*ui
)
2285 static void decode_ui(game_ui
*ui
, char *encoding
)
2289 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
2290 game_state
*newstate
)
2294 #define FLASH_TIME 0.15F
2296 #define PREFERRED_TILE_SIZE 32
2297 #define TILE_SIZE (ds->tilesize)
2298 #define DOT_SIZE (TILE_SIZE / 4)
2299 #define EDGE_THICKNESS (max(TILE_SIZE / 16, 2))
2300 #define BORDER TILE_SIZE
2302 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
2303 #define SCOORD(x) ( ((x) * TILE_SIZE)/2 + BORDER )
2304 #define FROMCOORD(x) ( ((x) - BORDER) / TILE_SIZE )
2306 #define DRAW_WIDTH (BORDER * 2 + ds->w * TILE_SIZE)
2307 #define DRAW_HEIGHT (BORDER * 2 + ds->h * TILE_SIZE)
2309 #define CURSOR_SIZE DOT_SIZE
2311 struct game_drawstate
{
2315 unsigned long *grid
;
2319 int dragging
, dragx
, dragy
;
2321 int *colour_scratch
;
2323 int cx
, cy
, cur_visible
;
2327 #define CORNER_TOLERANCE 0.15F
2328 #define CENTRE_TOLERANCE 0.15F
2331 * Round FP coordinates to the centre of the nearest edge.
2334 static void coord_round_to_edge(float x
, float y
, int *xr
, int *yr
)
2336 float xs
, ys
, xv
, yv
, dx
, dy
;
2339 * Find the nearest square-centre.
2341 xs
= (float)floor(x
) + 0.5F
;
2342 ys
= (float)floor(y
) + 0.5F
;
2345 * Find the nearest grid vertex.
2347 xv
= (float)floor(x
+ 0.5F
);
2348 yv
= (float)floor(y
+ 0.5F
);
2351 * Determine whether the horizontal or vertical edge from that
2352 * vertex alongside that square is closer to us, by comparing
2353 * distances from the square cente.
2355 dx
= (float)fabs(x
- xs
);
2356 dy
= (float)fabs(y
- ys
);
2358 /* Vertical edge: x-coord of corner,
2359 * y-coord of square centre. */
2361 *yr
= 1 + 2 * (int)floor(ys
);
2363 /* Horizontal edge: x-coord of square centre,
2364 * y-coord of corner. */
2365 *xr
= 1 + 2 * (int)floor(xs
);
2372 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
2373 int x
, int y
, int button
)
2379 px
= 2*FROMCOORD((float)x
) + 0.5;
2380 py
= 2*FROMCOORD((float)y
) + 0.5;
2384 if (button
== 'C' || button
== 'c') return dupstr("C");
2386 if (button
== 'S' || button
== 's') {
2388 game_state
*tmp
= dup_game(state
);
2389 state
->cdiff
= solver_state(tmp
, DIFF_UNREASONABLE
-1);
2390 ret
= diff_game(state
, tmp
, 0);
2395 if (button
== LEFT_BUTTON
|| button
== RIGHT_BUTTON
) {
2396 if (!INUI(state
, px
, py
)) return NULL
;
2397 sp
= &SPACE(state
, px
, py
);
2398 if (!dot_is_possible(state
, sp
, 1)) return NULL
;
2399 sprintf(buf
, "%c%d,%d",
2400 (char)((button
== LEFT_BUTTON
) ? 'D' : 'd'), px
, py
);
2407 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
2408 int x
, int y
, int button
)
2410 /* UI operations (play mode):
2412 * Toggle edge (set/unset) (left-click on edge)
2413 * Associate space with dot (left-drag from dot)
2414 * Unassociate space (left-drag from space off grid)
2415 * Autofill lines around shape? (right-click?)
2417 * (edit mode; will clear all lines/associations)
2419 * Add or remove dot (left-click)
2422 const char *sep
= "";
2424 struct space
*sp
, *dot
;
2428 if (button
== 'H' || button
== 'h') {
2430 game_state
*tmp
= dup_game(state
);
2431 solver_obvious(tmp
);
2432 ret
= diff_game(state
, tmp
, 0);
2437 if (button
== LEFT_BUTTON
) {
2438 ui
->cur_visible
= 0;
2439 coord_round_to_edge(FROMCOORD((float)x
), FROMCOORD((float)y
),
2442 if (!INUI(state
, px
, py
)) return NULL
;
2444 sp
= &SPACE(state
, px
, py
);
2445 assert(sp
->type
== s_edge
);
2447 sprintf(buf
, "E%d,%d", px
, py
);
2450 } else if (button
== RIGHT_BUTTON
) {
2453 ui
->cur_visible
= 0;
2455 px
= (int)(2*FROMCOORD((float)x
) + 0.5);
2456 py
= (int)(2*FROMCOORD((float)y
) + 0.5);
2461 * If there's a dot anywhere nearby, we pick up an arrow
2462 * pointing at that dot.
2464 for (py1
= py
-1; py1
<= py
+1; py1
++)
2465 for (px1
= px
-1; px1
<= px
+1; px1
++) {
2466 if (px1
>= 0 && px1
< state
->sx
&&
2467 py1
>= 0 && py1
< state
->sy
&&
2468 x
>= SCOORD(px1
-1) && x
< SCOORD(px1
+1) &&
2469 y
>= SCOORD(py1
-1) && y
< SCOORD(py1
+1) &&
2470 SPACE(state
, px1
, py1
).flags
& F_DOT
) {
2472 * Found a dot. Begin a drag from it.
2474 dot
= &SPACE(state
, px1
, py1
);
2477 goto done
; /* multi-level break */
2482 * Otherwise, find the nearest _square_, and pick up the
2483 * same arrow as it's got on it, if any.
2486 px
= 2*FROMCOORD(x
+TILE_SIZE
) - 1;
2487 py
= 2*FROMCOORD(y
+TILE_SIZE
) - 1;
2488 if (px
>= 0 && px
< state
->sx
&& py
>= 0 && py
< state
->sy
) {
2489 sp
= &SPACE(state
, px
, py
);
2490 if (sp
->flags
& F_TILE_ASSOC
) {
2491 dot
= &SPACE(state
, sp
->dotx
, sp
->doty
);
2500 * Now, if we've managed to find a dot, begin a drag.
2503 ui
->dragging
= TRUE
;
2510 } else if (button
== RIGHT_DRAG
&& ui
->dragging
) {
2511 /* just move the drag coords. */
2515 } else if (button
== RIGHT_RELEASE
&& ui
->dragging
) {
2516 ui
->dragging
= FALSE
;
2519 * Drags are always targeted at a single square.
2521 px
= 2*FROMCOORD(x
+TILE_SIZE
) - 1;
2522 py
= 2*FROMCOORD(y
+TILE_SIZE
) - 1;
2525 * Dragging an arrow on to the same square it started from
2526 * is a null move; just update the ui and finish.
2528 if (px
== ui
->srcx
&& py
== ui
->srcy
)
2532 * Otherwise, we remove the arrow from its starting
2533 * square if we didn't start from a dot...
2535 if ((ui
->srcx
!= ui
->dotx
|| ui
->srcy
!= ui
->doty
) &&
2536 SPACE(state
, ui
->srcx
, ui
->srcy
).flags
& F_TILE_ASSOC
) {
2537 sprintf(buf
+ strlen(buf
), "%sU%d,%d", sep
, ui
->srcx
, ui
->srcy
);
2542 * ... and if the square we're moving it _to_ is valid, we
2543 * add one there instead.
2545 if (INUI(state
, px
, py
)) {
2546 sp
= &SPACE(state
, px
, py
);
2548 if (!(sp
->flags
& F_DOT
) && !(sp
->flags
& F_TILE_ASSOC
))
2549 sprintf(buf
+ strlen(buf
), "%sA%d,%d,%d,%d",
2550 sep
, px
, py
, ui
->dotx
, ui
->doty
);
2557 } else if (IS_CURSOR_MOVE(button
)) {
2558 move_cursor(button
, &ui
->cur_x
, &ui
->cur_y
, state
->sx
-1, state
->sy
-1, 0);
2559 if (ui
->cur_x
< 1) ui
->cur_x
= 1;
2560 if (ui
->cur_y
< 1) ui
->cur_y
= 1;
2561 ui
->cur_visible
= 1;
2563 ui
->dx
= SCOORD(ui
->cur_x
);
2564 ui
->dy
= SCOORD(ui
->cur_y
);
2567 } else if (IS_CURSOR_SELECT(button
)) {
2568 if (!ui
->cur_visible
) {
2569 ui
->cur_visible
= 1;
2572 sp
= &SPACE(state
, ui
->cur_x
, ui
->cur_y
);
2574 ui
->dragging
= FALSE
;
2576 if ((ui
->srcx
!= ui
->dotx
|| ui
->srcy
!= ui
->doty
) &&
2577 SPACE(state
, ui
->srcx
, ui
->srcy
).flags
& F_TILE_ASSOC
) {
2578 sprintf(buf
, "%sU%d,%d", sep
, ui
->srcx
, ui
->srcy
);
2581 if (sp
->type
== s_tile
&& !(sp
->flags
& F_DOT
) && !(sp
->flags
& F_TILE_ASSOC
)) {
2582 sprintf(buf
+ strlen(buf
), "%sA%d,%d,%d,%d",
2583 sep
, ui
->cur_x
, ui
->cur_y
, ui
->dotx
, ui
->doty
);
2586 } else if (sp
->flags
& F_DOT
) {
2587 ui
->dragging
= TRUE
;
2588 ui
->dx
= SCOORD(ui
->cur_x
);
2589 ui
->dy
= SCOORD(ui
->cur_y
);
2590 ui
->dotx
= ui
->srcx
= ui
->cur_x
;
2591 ui
->doty
= ui
->srcy
= ui
->cur_y
;
2593 } else if (sp
->flags
& F_TILE_ASSOC
) {
2594 assert(sp
->type
== s_tile
);
2595 ui
->dragging
= TRUE
;
2596 ui
->dx
= SCOORD(ui
->cur_x
);
2597 ui
->dy
= SCOORD(ui
->cur_y
);
2598 ui
->dotx
= sp
->dotx
;
2599 ui
->doty
= sp
->doty
;
2600 ui
->srcx
= ui
->cur_x
;
2601 ui
->srcy
= ui
->cur_y
;
2603 } else if (sp
->type
== s_edge
) {
2604 sprintf(buf
, "E%d,%d", ui
->cur_x
, ui
->cur_y
);
2613 static int check_complete(game_state
*state
, int *dsf
, int *colours
)
2615 int w
= state
->w
, h
= state
->h
;
2620 int minx
, miny
, maxx
, maxy
;
2626 dsf
= snew_dsf(w
*h
);
2634 * During actual game play, completion checking is done on the
2635 * basis of the edges rather than the square associations. So
2636 * first we must go through the grid figuring out the connected
2637 * components into which the edges divide it.
2639 for (y
= 0; y
< h
; y
++)
2640 for (x
= 0; x
< w
; x
++) {
2641 if (y
+1 < h
&& !(SPACE(state
, 2*x
+1, 2*y
+2).flags
& F_EDGE_SET
))
2642 dsf_merge(dsf
, y
*w
+x
, (y
+1)*w
+x
);
2643 if (x
+1 < w
&& !(SPACE(state
, 2*x
+2, 2*y
+1).flags
& F_EDGE_SET
))
2644 dsf_merge(dsf
, y
*w
+x
, y
*w
+(x
+1));
2648 * That gives us our connected components. Now, for each
2649 * component, decide whether it's _valid_. A valid component is
2652 * - is 180-degree rotationally symmetric
2653 * - has a dot at its centre of symmetry
2654 * - has no other dots anywhere within it (including on its
2656 * - contains no internal edges (i.e. edges separating two
2657 * squares which are both part of the component).
2661 * First, go through the grid finding the bounding box of each
2664 sqdata
= snewn(w
*h
, struct sqdata
);
2665 for (i
= 0; i
< w
*h
; i
++) {
2666 sqdata
[i
].minx
= w
+1;
2667 sqdata
[i
].miny
= h
+1;
2668 sqdata
[i
].maxx
= sqdata
[i
].maxy
= -1;
2669 sqdata
[i
].valid
= FALSE
;
2671 for (y
= 0; y
< h
; y
++)
2672 for (x
= 0; x
< w
; x
++) {
2673 i
= dsf_canonify(dsf
, y
*w
+x
);
2674 if (sqdata
[i
].minx
> x
)
2676 if (sqdata
[i
].maxx
< x
)
2678 if (sqdata
[i
].miny
> y
)
2680 if (sqdata
[i
].maxy
< y
)
2682 sqdata
[i
].valid
= TRUE
;
2686 * Now we're in a position to loop over each actual component
2687 * and figure out where its centre of symmetry has to be if
2690 for (i
= 0; i
< w
*h
; i
++)
2691 if (sqdata
[i
].valid
) {
2693 cx
= sqdata
[i
].cx
= sqdata
[i
].minx
+ sqdata
[i
].maxx
+ 1;
2694 cy
= sqdata
[i
].cy
= sqdata
[i
].miny
+ sqdata
[i
].maxy
+ 1;
2695 if (!(SPACE(state
, sqdata
[i
].cx
, sqdata
[i
].cy
).flags
& F_DOT
))
2696 sqdata
[i
].valid
= FALSE
; /* no dot at centre of symmetry */
2697 if (dsf_canonify(dsf
, (cy
-1)/2*w
+(cx
-1)/2) != i
||
2698 dsf_canonify(dsf
, (cy
)/2*w
+(cx
-1)/2) != i
||
2699 dsf_canonify(dsf
, (cy
-1)/2*w
+(cx
)/2) != i
||
2700 dsf_canonify(dsf
, (cy
)/2*w
+(cx
)/2) != i
)
2701 sqdata
[i
].valid
= FALSE
; /* dot at cx,cy isn't ours */
2702 if (SPACE(state
, sqdata
[i
].cx
, sqdata
[i
].cy
).flags
& F_DOT_BLACK
)
2703 sqdata
[i
].colour
= 2;
2705 sqdata
[i
].colour
= 1;
2709 * Now we loop over the whole grid again, this time finding
2710 * extraneous dots (any dot which wholly or partially overlaps
2711 * a square and is not at the centre of symmetry of that
2712 * square's component disqualifies the component from validity)
2713 * and extraneous edges (any edge separating two squares
2714 * belonging to the same component also disqualifies that
2717 for (y
= 1; y
< state
->sy
-1; y
++)
2718 for (x
= 1; x
< state
->sx
-1; x
++) {
2719 space
*sp
= &SPACE(state
, x
, y
);
2721 if (sp
->flags
& F_DOT
) {
2723 * There's a dot here. Use it to disqualify any
2724 * component which deserves it.
2727 for (cy
= (y
-1) >> 1; cy
<= y
>> 1; cy
++)
2728 for (cx
= (x
-1) >> 1; cx
<= x
>> 1; cx
++) {
2729 i
= dsf_canonify(dsf
, cy
*w
+cx
);
2730 if (x
!= sqdata
[i
].cx
|| y
!= sqdata
[i
].cy
)
2731 sqdata
[i
].valid
= FALSE
;
2735 if (sp
->flags
& F_EDGE_SET
) {
2737 * There's an edge here. Use it to disqualify a
2738 * component if necessary.
2740 int cx1
= (x
-1) >> 1, cx2
= x
>> 1;
2741 int cy1
= (y
-1) >> 1, cy2
= y
>> 1;
2742 assert((cx1
==cx2
) ^ (cy1
==cy2
));
2743 i
= dsf_canonify(dsf
, cy1
*w
+cx1
);
2744 if (i
== dsf_canonify(dsf
, cy2
*w
+cx2
))
2745 sqdata
[i
].valid
= FALSE
;
2750 * And finally we test rotational symmetry: for each square in
2751 * the grid, find which component it's in, test that that
2752 * component also has a square in the symmetric position, and
2753 * disqualify it if it doesn't.
2755 for (y
= 0; y
< h
; y
++)
2756 for (x
= 0; x
< w
; x
++) {
2759 i
= dsf_canonify(dsf
, y
*w
+x
);
2761 x2
= sqdata
[i
].cx
- 1 - x
;
2762 y2
= sqdata
[i
].cy
- 1 - y
;
2763 if (i
!= dsf_canonify(dsf
, y2
*w
+x2
))
2764 sqdata
[i
].valid
= FALSE
;
2768 * That's it. We now have all the connected components marked
2769 * as valid or not valid. So now we return a `colours' array if
2770 * we were asked for one, and also we return an overall
2771 * true/false value depending on whether _every_ square in the
2772 * grid is part of a valid component.
2775 for (i
= 0; i
< w
*h
; i
++) {
2776 int ci
= dsf_canonify(dsf
, i
);
2777 int thisok
= sqdata
[ci
].valid
;
2779 colours
[i
] = thisok
? sqdata
[ci
].colour
: 0;
2780 ret
= ret
&& thisok
;
2790 static game_state
*execute_move(game_state
*state
, char *move
)
2792 int x
, y
, ax
, ay
, n
, dx
, dy
;
2793 game_state
*ret
= dup_game(state
);
2794 struct space
*sp
, *dot
;
2796 debug(("%s\n", move
));
2800 if (c
== 'E' || c
== 'U' || c
== 'M'
2802 || c
== 'D' || c
== 'd'
2806 if (sscanf(move
, "%d,%d%n", &x
, &y
, &n
) != 2 ||
2810 sp
= &SPACE(ret
, x
, y
);
2812 if (c
== 'D' || c
== 'd') {
2813 unsigned int currf
, newf
, maskf
;
2815 if (!dot_is_possible(state
, sp
, 1)) goto badmove
;
2817 newf
= F_DOT
| (c
== 'd' ? F_DOT_BLACK
: 0);
2818 currf
= GRID(ret
, grid
, x
, y
).flags
;
2819 maskf
= F_DOT
| F_DOT_BLACK
;
2820 /* if we clicked 'white dot':
2821 * white --> empty, empty --> white, black --> white.
2822 * if we clicker 'black dot':
2823 * black --> empty, empty --> black, white --> black.
2825 if (currf
& maskf
) {
2826 sp
->flags
&= ~maskf
;
2827 if ((currf
& maskf
) != newf
)
2831 sp
->nassoc
= 0; /* edit-mode disallows associations. */
2832 game_update_dots(ret
);
2836 if (sp
->type
!= s_edge
) goto badmove
;
2837 sp
->flags
^= F_EDGE_SET
;
2838 } else if (c
== 'U') {
2839 if (sp
->type
!= s_tile
|| !(sp
->flags
& F_TILE_ASSOC
))
2841 remove_assoc(ret
, sp
);
2842 } else if (c
== 'M') {
2843 if (!(sp
->flags
& F_DOT
)) goto badmove
;
2844 sp
->flags
^= F_DOT_HOLD
;
2847 } else if (c
== 'A' || c
== 'a') {
2849 if (sscanf(move
, "%d,%d,%d,%d%n", &x
, &y
, &ax
, &ay
, &n
) != 4 ||
2850 x
< 1 || y
< 1 || x
>= (state
->sx
-1) || y
>= (state
->sy
-1) ||
2851 ax
< 1 || ay
< 1 || ax
>= (state
->sx
-1) || ay
>= (state
->sy
-1))
2854 dot
= &GRID(ret
, grid
, ax
, ay
);
2855 if (!(dot
->flags
& F_DOT
))goto badmove
;
2856 if (dot
->flags
& F_DOT_HOLD
) goto badmove
;
2858 for (dx
= -1; dx
<= 1; dx
++) {
2859 for (dy
= -1; dy
<= 1; dy
++) {
2860 sp
= &GRID(ret
, grid
, x
+dx
, y
+dy
);
2861 if (sp
->type
!= s_tile
) continue;
2862 if (sp
->flags
& F_TILE_ASSOC
) {
2863 space
*dot
= &SPACE(state
, sp
->dotx
, sp
->doty
);
2864 if (dot
->flags
& F_DOT_HOLD
) continue;
2866 add_assoc(state
, sp
, dot
);
2871 } else if (c
== 'C') {
2875 } else if (c
== 'S') {
2877 ret
->used_solve
= 1;
2886 if (check_complete(ret
, NULL
, NULL
))
2895 /* ----------------------------------------------------------------------
2899 /* Lines will be much smaller size than squares; say, 1/8 the size?
2901 * Need a 'top-left corner of location XxY' to take this into account;
2902 * alternaticaly, that could give the middle of that location, and the
2903 * drawing code would just know the expected dimensions.
2905 * We also need something to take a click and work out what it was
2906 * we were interested in. Clicking on vertices is required because
2907 * we may want to drag from them, for example.
2910 static void game_compute_size(game_params
*params
, int sz
,
2913 struct { int tilesize
, w
, h
; } ads
, *ds
= &ads
;
2923 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
2924 game_params
*params
, int sz
)
2928 assert(TILE_SIZE
> 0);
2931 ds
->bl
= blitter_new(dr
, TILE_SIZE
, TILE_SIZE
);
2933 assert(!ds
->cur_bl
);
2934 ds
->cur_bl
= blitter_new(dr
, TILE_SIZE
, TILE_SIZE
);
2937 static float *game_colours(frontend
*fe
, int *ncolours
)
2939 float *ret
= snewn(3 * NCOLOURS
, float);
2943 * We call game_mkhighlight to ensure the background colour
2944 * isn't completely white. We don't actually use the high- and
2945 * lowlight colours it generates.
2947 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_WHITEBG
, COL_BLACKBG
);
2949 for (i
= 0; i
< 3; i
++) {
2951 * Currently, white dots and white-background squares are
2954 ret
[COL_WHITEDOT
* 3 + i
] = 1.0F
;
2955 ret
[COL_WHITEBG
* 3 + i
] = 1.0F
;
2958 * But black-background squares are a dark grey, whereas
2959 * black dots are really black.
2961 ret
[COL_BLACKDOT
* 3 + i
] = 0.0F
;
2962 ret
[COL_BLACKBG
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] * 0.3F
;
2965 * In unfilled squares, we draw a faint gridwork.
2967 ret
[COL_GRID
* 3 + i
] = ret
[COL_BACKGROUND
* 3 + i
] * 0.8F
;
2970 * Edges and arrows are filled in in pure black.
2972 ret
[COL_EDGE
* 3 + i
] = 0.0F
;
2973 ret
[COL_ARROW
* 3 + i
] = 0.0F
;
2977 /* tinge the edit background to bluey */
2978 ret
[COL_BACKGROUND
* 3 + 0] = ret
[COL_BACKGROUND
* 3 + 0] * 0.8F
;
2979 ret
[COL_BACKGROUND
* 3 + 1] = ret
[COL_BACKGROUND
* 3 + 0] * 0.8F
;
2980 ret
[COL_BACKGROUND
* 3 + 2] = max(ret
[COL_BACKGROUND
* 3 + 0] * 1.4F
, 1.0F
);
2983 ret
[COL_CURSOR
* 3 + 0] = max(ret
[COL_BACKGROUND
* 3 + 0] * 1.4F
, 1.0F
);
2984 ret
[COL_CURSOR
* 3 + 1] = ret
[COL_BACKGROUND
* 3 + 0] * 0.8F
;
2985 ret
[COL_CURSOR
* 3 + 2] = ret
[COL_BACKGROUND
* 3 + 0] * 0.8F
;
2987 *ncolours
= NCOLOURS
;
2991 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
2993 struct game_drawstate
*ds
= snew(struct game_drawstate
);
3000 ds
->grid
= snewn(ds
->w
*ds
->h
, unsigned long);
3001 for (i
= 0; i
< ds
->w
*ds
->h
; i
++)
3002 ds
->grid
[i
] = 0xFFFFFFFFUL
;
3003 ds
->dx
= snewn(ds
->w
*ds
->h
, int);
3004 ds
->dy
= snewn(ds
->w
*ds
->h
, int);
3007 ds
->dragging
= FALSE
;
3008 ds
->dragx
= ds
->dragy
= 0;
3010 ds
->colour_scratch
= snewn(ds
->w
* ds
->h
, int);
3013 ds
->cx
= ds
->cy
= 0;
3014 ds
->cur_visible
= 0;
3019 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
3021 if (ds
->cur_bl
) blitter_free(dr
, ds
->cur_bl
);
3022 sfree(ds
->colour_scratch
);
3023 if (ds
->bl
) blitter_free(dr
, ds
->bl
);
3030 #define DRAW_EDGE_L 0x0001
3031 #define DRAW_EDGE_R 0x0002
3032 #define DRAW_EDGE_U 0x0004
3033 #define DRAW_EDGE_D 0x0008
3034 #define DRAW_CORNER_UL 0x0010
3035 #define DRAW_CORNER_UR 0x0020
3036 #define DRAW_CORNER_DL 0x0040
3037 #define DRAW_CORNER_DR 0x0080
3038 #define DRAW_WHITE 0x0100
3039 #define DRAW_BLACK 0x0200
3040 #define DRAW_ARROW 0x0400
3041 #define DRAW_CURSOR 0x0800
3042 #define DOT_SHIFT_C 12
3043 #define DOT_SHIFT_M 2
3044 #define DOT_WHITE 1UL
3045 #define DOT_BLACK 2UL
3048 * Draw an arrow centred on (cx,cy), pointing in the direction
3049 * (ddx,ddy). (I.e. pointing at the point (cx+ddx, cy+ddy).
3051 static void draw_arrow(drawing
*dr
, game_drawstate
*ds
,
3052 int cx
, int cy
, int ddx
, int ddy
, int col
)
3054 float vlen
= (float)sqrt(ddx
*ddx
+ddy
*ddy
);
3055 float xdx
= ddx
/vlen
, xdy
= ddy
/vlen
;
3056 float ydx
= -xdy
, ydy
= xdx
;
3057 int e1x
= cx
+ (int)(xdx
*TILE_SIZE
/3), e1y
= cy
+ (int)(xdy
*TILE_SIZE
/3);
3058 int e2x
= cx
- (int)(xdx
*TILE_SIZE
/3), e2y
= cy
- (int)(xdy
*TILE_SIZE
/3);
3059 int adx
= (int)((ydx
-xdx
)*TILE_SIZE
/8), ady
= (int)((ydy
-xdy
)*TILE_SIZE
/8);
3060 int adx2
= (int)((-ydx
-xdx
)*TILE_SIZE
/8), ady2
= (int)((-ydy
-xdy
)*TILE_SIZE
/8);
3062 draw_line(dr
, e1x
, e1y
, e2x
, e2y
, col
);
3063 draw_line(dr
, e1x
, e1y
, e1x
+adx
, e1y
+ady
, col
);
3064 draw_line(dr
, e1x
, e1y
, e1x
+adx2
, e1y
+ady2
, col
);
3067 static void draw_square(drawing
*dr
, game_drawstate
*ds
, int x
, int y
,
3068 unsigned long flags
, int ddx
, int ddy
)
3070 int lx
= COORD(x
), ly
= COORD(y
);
3074 clip(dr
, lx
, ly
, TILE_SIZE
, TILE_SIZE
);
3077 * Draw the tile background.
3079 draw_rect(dr
, lx
, ly
, TILE_SIZE
, TILE_SIZE
,
3080 (flags
& DRAW_WHITE
? COL_WHITEBG
:
3081 flags
& DRAW_BLACK
? COL_BLACKBG
: COL_BACKGROUND
));
3086 gridcol
= (flags
& DRAW_BLACK
? COL_BLACKDOT
: COL_GRID
);
3087 draw_rect(dr
, lx
, ly
, 1, TILE_SIZE
, gridcol
);
3088 draw_rect(dr
, lx
, ly
, TILE_SIZE
, 1, gridcol
);
3091 * Draw the arrow, if present, or the cursor, if here.
3093 if (flags
& DRAW_ARROW
)
3094 draw_arrow(dr
, ds
, lx
+ TILE_SIZE
/2, ly
+ TILE_SIZE
/2, ddx
, ddy
,
3095 (flags
& DRAW_CURSOR
) ? COL_CURSOR
: COL_ARROW
);
3096 else if (flags
& DRAW_CURSOR
)
3097 draw_rect_outline(dr
,
3098 lx
+ TILE_SIZE
/2 - CURSOR_SIZE
,
3099 ly
+ TILE_SIZE
/2 - CURSOR_SIZE
,
3100 2*CURSOR_SIZE
+1, 2*CURSOR_SIZE
+1,
3106 if (flags
& DRAW_EDGE_L
)
3107 draw_rect(dr
, lx
, ly
, EDGE_THICKNESS
, TILE_SIZE
, COL_EDGE
);
3108 if (flags
& DRAW_EDGE_R
)
3109 draw_rect(dr
, lx
+ TILE_SIZE
- EDGE_THICKNESS
+ 1, ly
,
3110 EDGE_THICKNESS
- 1, TILE_SIZE
, COL_EDGE
);
3111 if (flags
& DRAW_EDGE_U
)
3112 draw_rect(dr
, lx
, ly
, TILE_SIZE
, EDGE_THICKNESS
, COL_EDGE
);
3113 if (flags
& DRAW_EDGE_D
)
3114 draw_rect(dr
, lx
, ly
+ TILE_SIZE
- EDGE_THICKNESS
+ 1,
3115 TILE_SIZE
, EDGE_THICKNESS
- 1, COL_EDGE
);
3116 if (flags
& DRAW_CORNER_UL
)
3117 draw_rect(dr
, lx
, ly
, EDGE_THICKNESS
, EDGE_THICKNESS
, COL_EDGE
);
3118 if (flags
& DRAW_CORNER_UR
)
3119 draw_rect(dr
, lx
+ TILE_SIZE
- EDGE_THICKNESS
+ 1, ly
,
3120 EDGE_THICKNESS
- 1, EDGE_THICKNESS
, COL_EDGE
);
3121 if (flags
& DRAW_CORNER_DL
)
3122 draw_rect(dr
, lx
, ly
+ TILE_SIZE
- EDGE_THICKNESS
+ 1,
3123 EDGE_THICKNESS
, EDGE_THICKNESS
- 1, COL_EDGE
);
3124 if (flags
& DRAW_CORNER_DR
)
3125 draw_rect(dr
, lx
+ TILE_SIZE
- EDGE_THICKNESS
+ 1,
3126 ly
+ TILE_SIZE
- EDGE_THICKNESS
+ 1,
3127 EDGE_THICKNESS
- 1, EDGE_THICKNESS
- 1, COL_EDGE
);
3132 for (dy
= 0; dy
< 3; dy
++)
3133 for (dx
= 0; dx
< 3; dx
++) {
3134 int dotval
= (flags
>> (DOT_SHIFT_C
+ DOT_SHIFT_M
*(dy
*3+dx
)));
3135 dotval
&= (1 << DOT_SHIFT_M
)-1;
3138 draw_circle(dr
, lx
+dx
*TILE_SIZE
/2, ly
+dy
*TILE_SIZE
/2,
3140 (dotval
== 1 ? COL_WHITEDOT
: COL_BLACKDOT
),
3145 draw_update(dr
, lx
, ly
, TILE_SIZE
, TILE_SIZE
);
3148 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
3149 game_state
*state
, int dir
, game_ui
*ui
,
3150 float animtime
, float flashtime
)
3152 int w
= ds
->w
, h
= ds
->h
;
3153 int x
, y
, flashing
= FALSE
;
3155 if (flashtime
> 0) {
3156 int frame
= (int)(flashtime
/ FLASH_TIME
);
3157 flashing
= (frame
% 2 == 0);
3162 blitter_load(dr
, ds
->bl
, ds
->dragx
, ds
->dragy
);
3163 draw_update(dr
, ds
->dragx
, ds
->dragy
, TILE_SIZE
, TILE_SIZE
);
3164 ds
->dragging
= FALSE
;
3166 if (ds
->cur_visible
) {
3168 blitter_load(dr
, ds
->cur_bl
, ds
->cx
, ds
->cy
);
3169 draw_update(dr
, ds
->cx
, ds
->cy
, CURSOR_SIZE
*2+1, CURSOR_SIZE
*2+1);
3170 ds
->cur_visible
= FALSE
;
3174 draw_rect(dr
, 0, 0, DRAW_WIDTH
, DRAW_HEIGHT
, COL_BACKGROUND
);
3175 draw_rect(dr
, BORDER
- EDGE_THICKNESS
+ 1, BORDER
- EDGE_THICKNESS
+ 1,
3176 w
*TILE_SIZE
+ EDGE_THICKNESS
*2 - 1,
3177 h
*TILE_SIZE
+ EDGE_THICKNESS
*2 - 1, COL_EDGE
);
3178 draw_update(dr
, 0, 0, DRAW_WIDTH
, DRAW_HEIGHT
);
3182 check_complete(state
, NULL
, ds
->colour_scratch
);
3184 for (y
= 0; y
< h
; y
++)
3185 for (x
= 0; x
< w
; x
++) {
3186 unsigned long flags
= 0;
3187 int ddx
= 0, ddy
= 0;
3192 * Set up the flags for this square. Firstly, see if we
3195 if (SPACE(state
, x
*2, y
*2+1).flags
& F_EDGE_SET
)
3196 flags
|= DRAW_EDGE_L
;
3197 if (SPACE(state
, x
*2+2, y
*2+1).flags
& F_EDGE_SET
)
3198 flags
|= DRAW_EDGE_R
;
3199 if (SPACE(state
, x
*2+1, y
*2).flags
& F_EDGE_SET
)
3200 flags
|= DRAW_EDGE_U
;
3201 if (SPACE(state
, x
*2+1, y
*2+2).flags
& F_EDGE_SET
)
3202 flags
|= DRAW_EDGE_D
;
3205 * Also, mark corners of neighbouring edges.
3207 if ((x
> 0 && SPACE(state
, x
*2-1, y
*2).flags
& F_EDGE_SET
) ||
3208 (y
> 0 && SPACE(state
, x
*2, y
*2-1).flags
& F_EDGE_SET
))
3209 flags
|= DRAW_CORNER_UL
;
3210 if ((x
+1 < w
&& SPACE(state
, x
*2+3, y
*2).flags
& F_EDGE_SET
) ||
3211 (y
> 0 && SPACE(state
, x
*2+2, y
*2-1).flags
& F_EDGE_SET
))
3212 flags
|= DRAW_CORNER_UR
;
3213 if ((x
> 0 && SPACE(state
, x
*2-1, y
*2+2).flags
& F_EDGE_SET
) ||
3214 (y
+1 < h
&& SPACE(state
, x
*2, y
*2+3).flags
& F_EDGE_SET
))
3215 flags
|= DRAW_CORNER_DL
;
3216 if ((x
+1 < w
&& SPACE(state
, x
*2+3, y
*2+2).flags
& F_EDGE_SET
) ||
3217 (y
+1 < h
&& SPACE(state
, x
*2+2, y
*2+3).flags
& F_EDGE_SET
))
3218 flags
|= DRAW_CORNER_DR
;
3221 * If this square is part of a valid region, paint it
3222 * that region's colour. Exception: if we're flashing,
3223 * everything goes briefly back to background colour.
3225 sp
= &SPACE(state
, x
*2+1, y
*2+1);
3226 if (ds
->colour_scratch
[y
*w
+x
] && !flashing
) {
3227 flags
|= (ds
->colour_scratch
[y
*w
+x
] == 2 ?
3228 DRAW_BLACK
: DRAW_WHITE
);
3232 * If this square is associated with a dot but it isn't
3233 * part of a valid region, draw an arrow in it pointing
3234 * in the direction of that dot.
3236 * Exception: if this is the source point of an active
3237 * drag, we don't draw the arrow.
3239 if ((sp
->flags
& F_TILE_ASSOC
) && !ds
->colour_scratch
[y
*w
+x
]) {
3240 if (ui
->dragging
&& ui
->srcx
== x
*2+1 && ui
->srcy
== y
*2+1) {
3242 } else if (sp
->doty
!= y
*2+1 || sp
->dotx
!= x
*2+1) {
3243 flags
|= DRAW_ARROW
;
3244 ddy
= sp
->doty
- (y
*2+1);
3245 ddx
= sp
->dotx
- (x
*2+1);
3250 * Now go through the nine possible places we could
3253 for (dy
= 0; dy
< 3; dy
++)
3254 for (dx
= 0; dx
< 3; dx
++) {
3255 sp
= &SPACE(state
, x
*2+dx
, y
*2+dy
);
3256 if (sp
->flags
& F_DOT
) {
3257 unsigned long dotval
= (sp
->flags
& F_DOT_BLACK
?
3258 DOT_BLACK
: DOT_WHITE
);
3259 flags
|= dotval
<< (DOT_SHIFT_C
+
3260 DOT_SHIFT_M
*(dy
*3+dx
));
3265 * Now work out if we have to draw a cursor for this square;
3266 * cursors-on-lines are taken care of below.
3268 if (ui
->cur_visible
&&
3269 ui
->cur_x
== x
*2+1 && ui
->cur_y
== y
*2+1 &&
3270 !(SPACE(state
, x
*2+1, y
*2+1).flags
& F_DOT
))
3271 flags
|= DRAW_CURSOR
;
3274 * Now we have everything we're going to need. Draw the
3277 if (ds
->grid
[y
*w
+x
] != flags
||
3278 ds
->dx
[y
*w
+x
] != ddx
||
3279 ds
->dy
[y
*w
+x
] != ddy
) {
3280 draw_square(dr
, ds
, x
, y
, flags
, ddx
, ddy
);
3281 ds
->grid
[y
*w
+x
] = flags
;
3282 ds
->dx
[y
*w
+x
] = ddx
;
3283 ds
->dy
[y
*w
+x
] = ddy
;
3288 * Draw a cursor. This secondary blitter is much less invasive than trying
3289 * to fix up all of the rest of the code with sufficient flags to be able to
3290 * display this sensibly.
3292 if (ui
->cur_visible
) {
3293 space
*sp
= &SPACE(state
, ui
->cur_x
, ui
->cur_y
);
3294 ds
->cur_visible
= TRUE
;
3295 ds
->cx
= SCOORD(ui
->cur_x
) - CURSOR_SIZE
;
3296 ds
->cy
= SCOORD(ui
->cur_y
) - CURSOR_SIZE
;
3297 blitter_save(dr
, ds
->cur_bl
, ds
->cx
, ds
->cy
);
3298 if (sp
->flags
& F_DOT
) {
3299 /* draw a red dot (over the top of whatever would be there already) */
3300 draw_circle(dr
, SCOORD(ui
->cur_x
), SCOORD(ui
->cur_y
), DOT_SIZE
,
3301 COL_CURSOR
, COL_BLACKDOT
);
3302 } else if (sp
->type
!= s_tile
) {
3303 /* draw an edge/vertex square; tile cursors are dealt with above. */
3304 int dx
= (ui
->cur_x
% 2) ? CURSOR_SIZE
: CURSOR_SIZE
/3;
3305 int dy
= (ui
->cur_y
% 2) ? CURSOR_SIZE
: CURSOR_SIZE
/3;
3306 int x1
= SCOORD(ui
->cur_x
)-dx
, y1
= SCOORD(ui
->cur_y
)-dy
;
3307 int xs
= dx
*2+1, ys
= dy
*2+1;
3309 draw_rect(dr
, x1
, y1
, xs
, ys
, COL_CURSOR
);
3311 draw_update(dr
, ds
->cx
, ds
->cy
, CURSOR_SIZE
*2+1, CURSOR_SIZE
*2+1);
3315 ds
->dragging
= TRUE
;
3316 ds
->dragx
= ui
->dx
- TILE_SIZE
/2;
3317 ds
->dragy
= ui
->dy
- TILE_SIZE
/2;
3318 blitter_save(dr
, ds
->bl
, ds
->dragx
, ds
->dragy
);
3319 draw_arrow(dr
, ds
, ui
->dx
, ui
->dy
,
3320 SCOORD(ui
->dotx
) - ui
->dx
,
3321 SCOORD(ui
->doty
) - ui
->dy
, COL_ARROW
);
3326 if (state
->cdiff
!= -1)
3327 sprintf(buf
, "Puzzle is %s.", galaxies_diffnames
[state
->cdiff
]);
3330 status_bar(dr
, buf
);
3335 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
3336 int dir
, game_ui
*ui
)
3341 static float game_flash_length(game_state
*oldstate
, game_state
*newstate
,
3342 int dir
, game_ui
*ui
)
3344 if ((!oldstate
->completed
&& newstate
->completed
) &&
3345 !(newstate
->used_solve
))
3346 return 3 * FLASH_TIME
;
3351 static int game_timing_state(game_state
*state
, game_ui
*ui
)
3357 static void game_print_size(game_params
*params
, float *x
, float *y
)
3362 * 8mm squares by default. (There isn't all that much detail
3363 * that needs to go in each square.)
3365 game_compute_size(params
, 800, &pw
, &ph
);
3370 static void game_print(drawing
*dr
, game_state
*state
, int sz
)
3372 int w
= state
->w
, h
= state
->h
;
3373 int white
, black
, blackish
;
3377 int ncoords
= 0, coordsize
= 0;
3379 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
3380 game_drawstate ads
, *ds
= &ads
;
3383 white
= print_mono_colour(dr
, 1);
3384 black
= print_mono_colour(dr
, 0);
3385 blackish
= print_hatched_colour(dr
, HATCH_X
);
3388 * Get the completion information.
3390 dsf
= snewn(w
* h
, int);
3391 colours
= snewn(w
* h
, int);
3392 check_complete(state
, dsf
, colours
);
3397 print_line_width(dr
, TILE_SIZE
/ 64);
3398 for (x
= 1; x
< w
; x
++)
3399 draw_line(dr
, COORD(x
), COORD(0), COORD(x
), COORD(h
), black
);
3400 for (y
= 1; y
< h
; y
++)
3401 draw_line(dr
, COORD(0), COORD(y
), COORD(w
), COORD(y
), black
);
3404 * Shade the completed regions. Just in case any particular
3405 * printing platform deals badly with adjacent
3406 * similarly-hatched regions, we'll fill each one as a single
3409 for (i
= 0; i
< w
*h
; i
++) {
3410 j
= dsf_canonify(dsf
, i
);
3411 if (colours
[j
] != 0) {
3415 * This is the first square we've run into belonging to
3416 * this polyomino, which means an edge of the polyomino
3417 * is certain to be to our left. (After we finish
3418 * tracing round it, we'll set the colours[] entry to
3419 * zero to prevent accidentally doing it again.)
3429 * We are currently sitting on square (x,y), which
3430 * we know to be in our polyomino, and we also know
3431 * that (x+dx,y+dy) is not. The way I visualise
3432 * this is that we're standing to the right of a
3433 * boundary line, stretching our left arm out to
3434 * point to the exterior square on the far side.
3438 * First, check if we've gone round the entire
3442 (x
== i
%w
&& y
== i
/w
&& dx
== -1 && dy
== 0))
3446 * Add to our coordinate list the coordinate
3447 * backwards and to the left of where we are.
3449 if (ncoords
+ 2 > coordsize
) {
3450 coordsize
= (ncoords
* 3 / 2) + 64;
3451 coords
= sresize(coords
, coordsize
, int);
3453 coords
[ncoords
++] = COORD((2*x
+1 + dx
+ dy
) / 2);
3454 coords
[ncoords
++] = COORD((2*y
+1 + dy
- dx
) / 2);
3457 * Follow the edge round. If the square directly in
3458 * front of us is not part of the polyomino, we
3459 * turn right; if it is and so is the square in
3460 * front of (x+dx,y+dy), we turn left; otherwise we
3463 if (x
-dy
< 0 || x
-dy
>= w
|| y
+dx
< 0 || y
+dx
>= h
||
3464 dsf_canonify(dsf
, (y
+dx
)*w
+(x
-dy
)) != j
) {
3469 } else if (x
+dx
-dy
>= 0 && x
+dx
-dy
< w
&&
3470 y
+dy
+dx
>= 0 && y
+dy
+dx
< h
&&
3471 dsf_canonify(dsf
, (y
+dy
+dx
)*w
+(x
+dx
-dy
)) == j
) {
3488 * Now we have our polygon complete, so fill it.
3490 draw_polygon(dr
, coords
, ncoords
/2,
3491 colours
[j
] == 2 ? blackish
: -1, black
);
3494 * And mark this polyomino as done.
3503 for (y
= 0; y
<= h
; y
++)
3504 for (x
= 0; x
<= w
; x
++) {
3505 if (x
< w
&& SPACE(state
, x
*2+1, y
*2).flags
& F_EDGE_SET
)
3506 draw_rect(dr
, COORD(x
)-EDGE_THICKNESS
, COORD(y
)-EDGE_THICKNESS
,
3507 EDGE_THICKNESS
* 2 + TILE_SIZE
, EDGE_THICKNESS
* 2,
3509 if (y
< h
&& SPACE(state
, x
*2, y
*2+1).flags
& F_EDGE_SET
)
3510 draw_rect(dr
, COORD(x
)-EDGE_THICKNESS
, COORD(y
)-EDGE_THICKNESS
,
3511 EDGE_THICKNESS
* 2, EDGE_THICKNESS
* 2 + TILE_SIZE
,
3518 for (y
= 0; y
<= 2*h
; y
++)
3519 for (x
= 0; x
<= 2*w
; x
++)
3520 if (SPACE(state
, x
, y
).flags
& F_DOT
) {
3521 draw_circle(dr
, (int)COORD(x
/2.0), (int)COORD(y
/2.0), DOT_SIZE
,
3522 (SPACE(state
, x
, y
).flags
& F_DOT_BLACK
?
3523 black
: white
), black
);
3533 #define thegame galaxies
3536 const struct game thegame
= {
3537 "Galaxies", "games.galaxies", "galaxies",
3544 TRUE
, game_configure
, custom_params
,
3556 TRUE
, game_can_format_as_text_now
, game_text_format
,
3564 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
3567 game_free_drawstate
,
3572 FALSE
, FALSE
, NULL
, NULL
,
3573 TRUE
, /* wants_statusbar */
3575 TRUE
, FALSE
, game_print_size
, game_print
,
3576 FALSE
, /* wants_statusbar */
3578 FALSE
, game_timing_state
,
3579 REQUIRE_RBUTTON
, /* flags */
3582 #ifdef STANDALONE_SOLVER
3588 static void usage_exit(const char *msg
)
3591 fprintf(stderr
, "%s: %s\n", quis
, msg
);
3592 fprintf(stderr
, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis
);
3596 static void dump_state(game_state
*state
)
3598 char *temp
= game_text_format(state
);
3599 printf("%s\n", temp
);
3603 static int gen(game_params
*p
, random_state
*rs
, int debug
)
3610 solver_show_working
= debug
;
3612 printf("Generating a %dx%d %s puzzle.\n",
3613 p
->w
, p
->h
, galaxies_diffnames
[p
->diff
]);
3615 desc
= new_game_desc(p
, rs
, NULL
, 0);
3616 state
= new_game(NULL
, p
, desc
);
3619 diff
= solver_state(state
, DIFF_UNREASONABLE
);
3620 printf("Generated %s game %dx%d:%s\n",
3621 galaxies_diffnames
[diff
], p
->w
, p
->h
, desc
);
3630 static void soak(game_params
*p
, random_state
*rs
)
3632 time_t tt_start
, tt_now
, tt_last
;
3635 int diff
, n
= 0, i
, diffs
[DIFF_MAX
], ndots
= 0, nspaces
= 0;
3638 solver_show_working
= 0;
3640 tt_start
= tt_now
= time(NULL
);
3641 for (i
= 0; i
< DIFF_MAX
; i
++) diffs
[i
] = 0;
3644 printf("Soak-generating a %dx%d grid, max. diff %s.\n",
3645 p
->w
, p
->h
, galaxies_diffnames
[p
->diff
]);
3647 for (i
= 0; i
< DIFF_MAX
; i
++)
3648 printf("%s%s", (i
== 0) ? "" : ", ", galaxies_diffnames
[i
]);
3652 desc
= new_game_desc(p
, rs
, NULL
, 0);
3653 st
= new_game(NULL
, p
, desc
);
3654 diff
= solver_state(st
, p
->diff
);
3655 nspaces
+= st
->w
*st
->h
;
3656 for (i
= 0; i
< st
->sx
*st
->sy
; i
++)
3657 if (st
->grid
[i
].flags
& F_DOT
) ndots
++;
3663 tt_last
= time(NULL
);
3664 if (tt_last
> tt_now
) {
3666 printf("%d total, %3.1f/s, [",
3667 n
, (double)n
/ ((double)tt_now
- tt_start
));
3668 for (i
= 0; i
< DIFF_MAX
; i
++)
3669 printf("%s%.1f%%", (i
== 0) ? "" : ", ",
3670 100.0 * ((double)diffs
[i
] / (double)n
));
3671 printf("], %.1f%% dots\n",
3672 100.0 * ((double)ndots
/ (double)nspaces
));
3677 int main(int argc
, char **argv
)
3680 char *id
= NULL
, *desc
, *err
;
3682 int diff
, do_soak
= 0, verbose
= 0;
3684 time_t seed
= time(NULL
);
3687 while (--argc
> 0) {
3689 if (!strcmp(p
, "-v")) {
3691 } else if (!strcmp(p
, "--seed")) {
3692 if (argc
== 0) usage_exit("--seed needs an argument");
3693 seed
= (time_t)atoi(*++argv
);
3695 } else if (!strcmp(p
, "--soak")) {
3697 } else if (*p
== '-') {
3698 usage_exit("unrecognised option");
3706 p
= default_params();
3707 rs
= random_new((void*)&seed
, sizeof(time_t));
3710 if (!id
) usage_exit("need one argument for --soak");
3711 decode_params(p
, *argv
);
3718 p
->w
= random_upto(rs
, 15) + 3;
3719 p
->h
= random_upto(rs
, 15) + 3;
3720 p
->diff
= random_upto(rs
, DIFF_UNREASONABLE
);
3721 diff
= gen(p
, rs
, 0);
3726 desc
= strchr(id
, ':');
3728 decode_params(p
, id
);
3729 gen(p
, rs
, verbose
);
3732 solver_show_working
= 1;
3735 decode_params(p
, id
);
3736 err
= validate_desc(p
, desc
);
3738 fprintf(stderr
, "%s: %s\n", argv
[0], err
);
3741 s
= new_game(NULL
, p
, desc
);
3742 diff
= solver_state(s
, DIFF_UNREASONABLE
);
3744 printf("Puzzle is %s.\n", galaxies_diffnames
[diff
]);
3755 #ifdef STANDALONE_PICTURE_GENERATOR
3758 * Main program for the standalone picture generator. To use it,
3759 * simply provide it with an XBM-format bitmap file (note XBM, not
3760 * XPM) on standard input, and it will output a game ID in return.
3763 * $ ./galaxiespicture < badly-drawn-cat.xbm
3764 * 11x11:eloMBLzFeEzLNMWifhaWYdDbixCymBbBMLoDdewGg
3766 * If you want a puzzle with a non-standard difficulty level, pass
3767 * a partial parameters string as a command-line argument (e.g.
3768 * `./galaxiespicture du < foo.xbm', where `du' is the same suffix
3769 * which if it appeared in a random-seed game ID would set the
3770 * difficulty level to Unreasonable). However, be aware that if the
3771 * generator fails to produce an adequately difficult puzzle too
3772 * many times then it will give up and return an easier one (just
3773 * as it does during normal GUI play). To be sure you really have
3774 * the difficulty you asked for, use galaxiessolver to
3777 * (Perhaps I ought to include an option to make this standalone
3778 * generator carry on looping until it really does get the right
3779 * difficulty. Hmmm.)
3784 int main(int argc
, char **argv
)
3787 char *params
, *desc
;
3789 time_t seed
= time(NULL
);
3794 par
= default_params();
3796 decode_params(par
, argv
[1]); /* get difficulty */
3797 par
->w
= par
->h
= -1;
3800 * Now read an XBM file from standard input. This is simple and
3801 * hacky and will do very little error detection, so don't feed
3806 while (fgets(buf
, sizeof(buf
), stdin
)) {
3807 buf
[strcspn(buf
, "\r\n")] = '\0';
3808 if (!strncmp(buf
, "#define", 7)) {
3810 * Lines starting `#define' give the width and height.
3812 char *num
= buf
+ strlen(buf
);
3815 while (num
> buf
&& isdigit((unsigned char)num
[-1]))
3818 while (symend
> buf
&& isspace((unsigned char)symend
[-1]))
3821 if (symend
-5 >= buf
&& !strncmp(symend
-5, "width", 5))
3823 else if (symend
-6 >= buf
&& !strncmp(symend
-6, "height", 6))
3827 * Otherwise, break the string up into words and take
3828 * any word of the form `0x' plus hex digits to be a
3831 char *p
, *wordstart
;
3834 if (par
->w
< 0 || par
->h
< 0) {
3835 printf("failed to read width and height\n");
3838 picture
= snewn(par
->w
* par
->h
, int);
3839 for (i
= 0; i
< par
->w
* par
->h
; i
++)
3845 while (*p
&& (*p
== ',' || isspace((unsigned char)*p
)))
3848 while (*p
&& !(*p
== ',' || *p
== '}' ||
3849 isspace((unsigned char)*p
)))
3854 if (wordstart
[0] == '0' &&
3855 (wordstart
[1] == 'x' || wordstart
[1] == 'X') &&
3856 !wordstart
[2 + strspn(wordstart
+2,
3857 "0123456789abcdefABCDEF")]) {
3858 unsigned long byte
= strtoul(wordstart
+2, NULL
, 16);
3859 for (i
= 0; i
< 8; i
++) {
3860 int bit
= (byte
>> i
) & 1;
3861 if (y
< par
->h
&& x
< par
->w
)
3862 picture
[y
* par
->w
+ x
] = bit
;
3875 for (i
= 0; i
< par
->w
* par
->h
; i
++)
3876 if (picture
[i
] < 0) {
3877 fprintf(stderr
, "failed to read enough bitmap data\n");
3881 rs
= random_new((void*)&seed
, sizeof(time_t));
3883 desc
= new_game_desc(par
, rs
, NULL
, FALSE
);
3884 params
= encode_params(par
, FALSE
);
3885 printf("%s:%s\n", params
, desc
);
3897 /* vim: set shiftwidth=4 tabstop=8: */