2 * magnets.c: implementation of janko.at 'magnets puzzle' game.
4 * http://64.233.179.104/translate_c?hl=en&u=http://www.janko.at/Raetsel/Magnete/Beispiel.htm
6 * Puzzle definition is just the size, and then the list of + (across then
7 * down) and - (across then down) present, then domino edges.
21 * 3x3:201,102,120,111,LRTT*BBLR
23 * 'Zotmeister' examples:
24 * 5x5:.2..1,3..1.,.2..2,2..2.,LRLRTTLRTBBT*BTTBLRBBLRLR
25 * 9x9:3.51...33,.2..23.13,..33.33.2,12...5.3.,**TLRTLR*,*TBLRBTLR,TBLRLRBTT,BLRTLRTBB,LRTB*TBLR,LRBLRBLRT,TTTLRLRTB,BBBTLRTB*,*LRBLRB**
27 * Janko 6x6 with solution:
28 * 6x6:322223,323132,232223,232223,LRTLRTTTBLRBBBTTLRLRBBLRTTLRTTBBLRBB
31 * 8x8:34131323,23131334,43122323,21332243,LRTLRLRT,LRBTTTTB,LRTBBBBT,TTBTLRTB,BBTBTTBT,TTBTBBTB,BBTBLRBT,LRBLRLRB
43 #ifdef STANDALONE_SOLVER
48 COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
,
49 COL_TEXT
, COL_ERROR
, COL_CURSOR
,
50 COL_NEUTRAL
, COL_NEGATIVE
, COL_POSITIVE
, COL_NOT
,
55 enum { EMPTY
= 0, NEUTRAL
= EMPTY
, POSITIVE
= 1, NEGATIVE
= 2 };
57 #if defined DEBUGGING || defined STANDALONE_SOLVER
58 static const char *cellnames
[3] = { "neutral", "positive", "negative" };
59 #define NAME(w) ( ((w) < 0 || (w) > 2) ? "(out of range)" : cellnames[(w)] )
62 #define GRID2CHAR(g) ( ((g) >= 0 && (g) <= 2) ? ".+-"[(g)] : '?' )
63 #define CHAR2GRID(c) ( (c) == '+' ? POSITIVE : (c) == '-' ? NEGATIVE : NEUTRAL )
65 #define INGRID(s,x,y) ((x) >= 0 && (x) < (s)->w && (y) >= 0 && (y) < (s)->h)
67 #define OPPOSITE(x) ( ((x)*2) % 3 ) /* 0 --> 0,
71 #define FLASH_TIME 0.7F
73 /* Macro ickery copied from slant.c */
77 #define ENUM(upper,title,lower) DIFF_ ## upper,
78 #define TITLE(upper,title,lower) #title,
79 #define ENCODE(upper,title,lower) #lower
80 #define CONFIG(upper,title,lower) ":" #title
81 enum { DIFFLIST(ENUM
) DIFFCOUNT
};
82 static char const *const magnets_diffnames
[] = { DIFFLIST(TITLE
) "(count)" };
83 static char const magnets_diffchars
[] = DIFFLIST(ENCODE
);
84 #define DIFFCONFIG DIFFLIST(CONFIG)
87 /* --------------------------------------------------------------- */
88 /* Game parameter functions. */
91 int w
, h
, diff
, stripclues
;
94 #define DEFAULT_PRESET 2
96 static const struct game_params magnets_presets
[] = {
98 {6, 5, DIFF_TRICKY
, 0},
99 {6, 5, DIFF_TRICKY
, 1},
100 {8, 7, DIFF_EASY
, 0},
101 {8, 7, DIFF_TRICKY
, 0},
102 {8, 7, DIFF_TRICKY
, 1},
103 {10, 9, DIFF_TRICKY
, 0},
104 {10, 9, DIFF_TRICKY
, 1}
107 static game_params
*default_params(void)
109 game_params
*ret
= snew(game_params
);
111 *ret
= magnets_presets
[DEFAULT_PRESET
];
116 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
121 if (i
< 0 || i
>= lenof(magnets_presets
)) return FALSE
;
123 ret
= default_params();
124 *ret
= magnets_presets
[i
]; /* struct copy */
127 sprintf(buf
, "%dx%d %s%s",
128 magnets_presets
[i
].w
, magnets_presets
[i
].h
,
129 magnets_diffnames
[magnets_presets
[i
].diff
],
130 magnets_presets
[i
].stripclues
? ", strip clues" : "");
136 static void free_params(game_params
*params
)
141 static game_params
*dup_params(game_params
*params
)
143 game_params
*ret
= snew(game_params
);
144 *ret
= *params
; /* structure copy */
148 static void decode_params(game_params
*ret
, char const *string
)
150 ret
->w
= ret
->h
= atoi(string
);
151 while (*string
&& isdigit((unsigned char) *string
)) ++string
;
152 if (*string
== 'x') {
154 ret
->h
= atoi(string
);
155 while (*string
&& isdigit((unsigned char)*string
)) string
++;
158 ret
->diff
= DIFF_EASY
;
159 if (*string
== 'd') {
162 for (i
= 0; i
< DIFFCOUNT
; i
++)
163 if (*string
== magnets_diffchars
[i
])
165 if (*string
) string
++;
169 if (*string
== 'S') {
175 static char *encode_params(game_params
*params
, int full
)
178 sprintf(buf
, "%dx%d", params
->w
, params
->h
);
180 sprintf(buf
+ strlen(buf
), "d%c%s",
181 magnets_diffchars
[params
->diff
],
182 params
->stripclues
? "S" : "");
186 static config_item
*game_configure(game_params
*params
)
191 ret
= snewn(5, config_item
);
193 ret
[0].name
= "Width";
194 ret
[0].type
= C_STRING
;
195 sprintf(buf
, "%d", params
->w
);
196 ret
[0].sval
= dupstr(buf
);
199 ret
[1].name
= "Height";
200 ret
[1].type
= C_STRING
;
201 sprintf(buf
, "%d", params
->h
);
202 ret
[1].sval
= dupstr(buf
);
205 ret
[2].name
= "Difficulty";
206 ret
[2].type
= C_CHOICES
;
207 ret
[2].sval
= DIFFCONFIG
;
208 ret
[2].ival
= params
->diff
;
210 ret
[3].name
= "Strip clues";
211 ret
[3].type
= C_BOOLEAN
;
213 ret
[3].ival
= params
->stripclues
;
223 static game_params
*custom_params(config_item
*cfg
)
225 game_params
*ret
= snew(game_params
);
227 ret
->w
= atoi(cfg
[0].sval
);
228 ret
->h
= atoi(cfg
[1].sval
);
229 ret
->diff
= cfg
[2].ival
;
230 ret
->stripclues
= cfg
[3].ival
;
235 static char *validate_params(game_params
*params
, int full
)
237 if (params
->w
< 2) return "Width must be at least one";
238 if (params
->h
< 2) return "Height must be at least one";
239 if (params
->diff
< 0 || params
->diff
>= DIFFCOUNT
)
240 return "Unknown difficulty level";
245 /* --------------------------------------------------------------- */
246 /* Game state allocation, deallocation. */
249 int *dominoes
; /* size w*h, dominoes[i] points to other end of domino. */
250 int *rowcount
; /* size 3*h, array of [plus, minus, neutral] counts */
251 int *colcount
; /* size 3*w, ditto */
257 #define GS_NOTPOSITIVE 4
258 #define GS_NOTNEGATIVE 8
259 #define GS_NOTNEUTRAL 16
262 #define GS_NOTMASK (GS_NOTPOSITIVE|GS_NOTNEGATIVE|GS_NOTNEUTRAL)
264 #define NOTFLAG(w) ( (w) == NEUTRAL ? GS_NOTNEUTRAL : \
265 (w) == POSITIVE ? GS_NOTPOSITIVE : \
266 (w) == NEGATIVE ? GS_NOTNEGATIVE : \
269 #define POSSIBLE(f,w) (!(state->flags[(f)] & NOTFLAG(w)))
273 int *grid
; /* size w*h, for cell state (pos/neg) */
274 unsigned int *flags
; /* size w*h */
275 int solved
, completed
, numbered
;
277 struct game_common
*common
; /* domino layout never changes. */
280 static void clear_state(game_state
*ret
)
284 ret
->solved
= ret
->completed
= ret
->numbered
= 0;
286 memset(ret
->common
->rowcount
, 0, ret
->h
*3*sizeof(int));
287 memset(ret
->common
->colcount
, 0, ret
->w
*3*sizeof(int));
289 for (i
= 0; i
< ret
->wh
; i
++) {
290 ret
->grid
[i
] = EMPTY
;
292 ret
->common
->dominoes
[i
] = i
;
296 static game_state
*new_state(int w
, int h
)
298 game_state
*ret
= snew(game_state
);
300 memset(ret
, 0, sizeof(game_state
));
305 ret
->grid
= snewn(ret
->wh
, int);
306 ret
->flags
= snewn(ret
->wh
, unsigned int);
308 ret
->common
= snew(struct game_common
);
309 ret
->common
->refcount
= 1;
311 ret
->common
->dominoes
= snewn(ret
->wh
, int);
312 ret
->common
->rowcount
= snewn(ret
->h
*3, int);
313 ret
->common
->colcount
= snewn(ret
->w
*3, int);
320 static game_state
*dup_game(game_state
*src
)
322 game_state
*dest
= snew(game_state
);
328 dest
->solved
= src
->solved
;
329 dest
->completed
= src
->completed
;
330 dest
->numbered
= src
->numbered
;
332 dest
->common
= src
->common
;
333 dest
->common
->refcount
++;
335 dest
->grid
= snewn(dest
->wh
, int);
336 memcpy(dest
->grid
, src
->grid
, dest
->wh
*sizeof(int));
338 dest
->flags
= snewn(dest
->wh
, unsigned int);
339 memcpy(dest
->flags
, src
->flags
, dest
->wh
*sizeof(unsigned int));
344 static void free_game(game_state
*state
)
346 state
->common
->refcount
--;
347 if (state
->common
->refcount
== 0) {
348 sfree(state
->common
->dominoes
);
349 sfree(state
->common
->rowcount
);
350 sfree(state
->common
->colcount
);
351 sfree(state
->common
);
358 /* --------------------------------------------------------------- */
359 /* Game generation and reading. */
361 /* For a game of size w*h the game description is:
362 * w-sized string of column + numbers (L-R), or '.' for none
364 * h-sized string of row + numbers (T-B), or '.'
366 * w-sized string of column - numbers (L-R), or '.'
368 * h-sized string of row - numbers (T-B), or '.'
370 * w*h-sized string of 'L', 'R', 'U', 'D' for domino associations,
371 * or '*' for a black singleton square.
373 * for a total length of 2w + 2h + wh + 4.
376 static char n2c(int num
) { /* XXX cloned from singles.c */
381 else if (num
< 10+26)
382 return 'a' + num
- 10;
384 return 'A' + num
- 10 - 26;
388 static int c2n(char c
) { /* XXX cloned from singles.c */
389 if (isdigit((unsigned char)c
))
390 return (int)(c
- '0');
391 else if (c
>= 'a' && c
<= 'z')
392 return (int)(c
- 'a' + 10);
393 else if (c
>= 'A' && c
<= 'Z')
394 return (int)(c
- 'A' + 10 + 26);
398 static char *readrow(char *desc
, int n
, int *array
, int off
, const char **prob
)
403 for (i
= 0; i
< n
; i
++) {
405 if (c
== 0) goto badchar
;
410 if (num
< 0) goto badchar
;
412 array
[i
*3+off
] = num
;
415 if (c
!= ',') goto badchar
;
420 "Game description too short" :
421 "Game description contained unexpected characters";
425 static game_state
*new_game_int(game_params
*params
, char *desc
, const char **prob
)
427 game_state
*state
= new_state(params
->w
, params
->h
);
428 int x
, y
, idx
, *count
;
433 /* top row, left-to-right */
434 desc
= readrow(desc
, state
->w
, state
->common
->colcount
, POSITIVE
, prob
);
435 if (*prob
) goto done
;
437 /* left column, top-to-bottom */
438 desc
= readrow(desc
, state
->h
, state
->common
->rowcount
, POSITIVE
, prob
);
439 if (*prob
) goto done
;
441 /* bottom row, left-to-right */
442 desc
= readrow(desc
, state
->w
, state
->common
->colcount
, NEGATIVE
, prob
);
443 if (*prob
) goto done
;
445 /* right column, top-to-bottom */
446 desc
= readrow(desc
, state
->h
, state
->common
->rowcount
, NEGATIVE
, prob
);
447 if (*prob
) goto done
;
449 /* Add neutral counts (== size - pos - neg) to columns and rows.
450 * Any singleton cells will just be treated as permanently neutral. */
451 count
= state
->common
->colcount
;
452 for (x
= 0; x
< state
->w
; x
++) {
453 if (count
[x
*3+POSITIVE
] < 0 || count
[x
*3+NEGATIVE
] < 0)
454 count
[x
*3+NEUTRAL
] = -1;
457 state
->h
- count
[x
*3+POSITIVE
] - count
[x
*3+NEGATIVE
];
458 if (count
[x
*3+NEUTRAL
] < 0) {
459 *prob
= "Column counts inconsistent";
464 count
= state
->common
->rowcount
;
465 for (y
= 0; y
< state
->h
; y
++) {
466 if (count
[y
*3+POSITIVE
] < 0 || count
[y
*3+NEGATIVE
] < 0)
467 count
[y
*3+NEUTRAL
] = -1;
470 state
->w
- count
[y
*3+POSITIVE
] - count
[y
*3+NEGATIVE
];
471 if (count
[y
*3+NEUTRAL
] < 0) {
472 *prob
= "Row counts inconsistent";
479 for (y
= 0; y
< state
->h
; y
++) {
480 for (x
= 0; x
< state
->w
; x
++) {
481 idx
= y
*state
->w
+ x
;
485 if (c
== 'L') /* this square is LHS of a domino */
486 state
->common
->dominoes
[idx
] = idx
+1;
487 else if (c
== 'R') /* ... RHS of a domino */
488 state
->common
->dominoes
[idx
] = idx
-1;
489 else if (c
== 'T') /* ... top of a domino */
490 state
->common
->dominoes
[idx
] = idx
+state
->w
;
491 else if (c
== 'B') /* ... bottom of a domino */
492 state
->common
->dominoes
[idx
] = idx
-state
->w
;
493 else if (c
== '*') /* singleton */
494 state
->common
->dominoes
[idx
] = idx
;
495 else if (c
== ',') /* spacer, ignore */
501 /* Check dominoes as input are sensibly consistent
502 * (i.e. each end points to the other) */
503 for (idx
= 0; idx
< state
->wh
; idx
++) {
504 if (state
->common
->dominoes
[idx
] < 0 ||
505 state
->common
->dominoes
[idx
] > state
->wh
||
506 state
->common
->dominoes
[state
->common
->dominoes
[idx
]] != idx
) {
507 *prob
= "Domino descriptions inconsistent";
510 if (state
->common
->dominoes
[idx
] == idx
) {
511 state
->grid
[idx
] = NEUTRAL
;
512 state
->flags
[idx
] |= GS_SET
;
521 "Game description too short" :
522 "Game description contained unexpected characters";
532 static char *validate_desc(game_params
*params
, char *desc
)
535 game_state
*st
= new_game_int(params
, desc
, &prob
);
536 if (!st
) return (char*)prob
;
541 static game_state
*new_game(midend
*me
, game_params
*params
, char *desc
)
544 game_state
*st
= new_game_int(params
, desc
, &prob
);
549 static char *generate_desc(game_state
*new)
551 int x
, y
, idx
, other
, w
= new->w
, h
= new->h
;
552 char *desc
= snewn(new->wh
+ 2*(w
+ h
) + 5, char), *p
= desc
;
554 for (x
= 0; x
< w
; x
++) *p
++ = n2c(new->common
->colcount
[x
*3+POSITIVE
]);
556 for (y
= 0; y
< h
; y
++) *p
++ = n2c(new->common
->rowcount
[y
*3+POSITIVE
]);
559 for (x
= 0; x
< w
; x
++) *p
++ = n2c(new->common
->colcount
[x
*3+NEGATIVE
]);
561 for (y
= 0; y
< h
; y
++) *p
++ = n2c(new->common
->rowcount
[y
*3+NEGATIVE
]);
564 for (y
= 0; y
< h
; y
++) {
565 for (x
= 0; x
< w
; x
++) {
567 other
= new->common
->dominoes
[idx
];
569 if (other
== idx
) *p
++ = '*';
570 else if (other
== idx
+1) *p
++ = 'L';
571 else if (other
== idx
-1) *p
++ = 'R';
572 else if (other
== idx
+w
) *p
++ = 'T';
573 else if (other
== idx
-w
) *p
++ = 'B';
574 else assert(!"mad domino orientation");
582 static void game_text_hborder(game_state
*state
, char **p_r
)
589 for (x
= 0; x
< state
->w
*2-1; x
++) *p
++ = '-';
596 static int game_can_format_as_text_now(game_params
*params
)
601 static char *game_text_format(game_state
*state
)
606 len
= ((state
->w
*2)+4) * ((state
->h
*2)+4) + 2;
607 p
= ret
= snewn(len
, char);
609 /* top row: '+' then column totals for plus. */
611 for (x
= 0; x
< state
->w
; x
++) {
613 *p
++ = n2c(state
->common
->colcount
[x
*3+POSITIVE
]);
618 game_text_hborder(state
, &p
);
620 for (y
= 0; y
< state
->h
; y
++) {
621 *p
++ = n2c(state
->common
->rowcount
[y
*3+POSITIVE
]);
623 for (x
= 0; x
< state
->w
; x
++) {
625 *p
++ = state
->common
->dominoes
[i
] == i
? '#' :
626 state
->grid
[i
] == POSITIVE
? '+' :
627 state
->grid
[i
] == NEGATIVE
? '-' :
628 state
->flags
[i
] & GS_SET
? '*' : ' ';
629 if (x
< (state
->w
-1))
630 *p
++ = state
->common
->dominoes
[i
] == i
+1 ? ' ' : '|';
633 *p
++ = n2c(state
->common
->rowcount
[y
*3+NEGATIVE
]);
636 if (y
< (state
->h
-1)) {
639 for (x
= 0; x
< state
->w
; x
++) {
641 *p
++ = state
->common
->dominoes
[i
] == i
+state
->w
? ' ' : '-';
642 if (x
< (state
->w
-1))
651 game_text_hborder(state
, &p
);
653 /* bottom row: column totals for minus then '-'. */
655 for (x
= 0; x
< state
->w
; x
++) {
657 *p
++ = n2c(state
->common
->colcount
[x
*3+NEGATIVE
]);
667 static void game_debug(game_state
*state
, const char *desc
)
669 char *fmt
= game_text_format(state
);
670 debug(("%s:\n%s\n", desc
, fmt
));
674 enum { ROW
, COLUMN
};
676 typedef struct rowcol
{
677 int i
, di
, n
, roworcol
, num
;
682 static rowcol
mkrowcol(game_state
*state
, int num
, int roworcol
)
686 rc
.roworcol
= roworcol
;
689 if (roworcol
== ROW
) {
690 rc
.i
= num
* state
->w
;
693 rc
.targets
= &(state
->common
->rowcount
[num
*3]);
695 } else if (roworcol
== COLUMN
) {
699 rc
.targets
= &(state
->common
->colcount
[num
*3]);
702 assert(!"unknown roworcol");
707 static int count_rowcol(game_state
*state
, int num
, int roworcol
, int which
)
710 rowcol rc
= mkrowcol(state
, num
, roworcol
);
712 for (i
= 0; i
< rc
.n
; i
++, rc
.i
+= rc
.di
) {
714 if (state
->grid
[rc
.i
] == EMPTY
&&
715 !(state
->flags
[rc
.i
] & GS_SET
))
717 } else if (state
->grid
[rc
.i
] == which
)
723 static void check_rowcol(game_state
*state
, int num
, int roworcol
, int which
,
724 int *wrong
, int *incomplete
)
726 int count
, target
= mkrowcol(state
, num
, roworcol
).targets
[which
];
728 if (target
== -1) return; /* no number to check against. */
730 count
= count_rowcol(state
, num
, roworcol
, which
);
731 if (count
< target
) *incomplete
= 1;
732 if (count
> target
) *wrong
= 1;
735 static int check_completion(game_state
*state
)
737 int i
, j
, x
, y
, idx
, w
= state
->w
, h
= state
->h
;
738 int which
= POSITIVE
, wrong
= 0, incomplete
= 0;
740 /* Check row and column counts for magnets. */
741 for (which
= POSITIVE
, j
= 0; j
< 2; which
= OPPOSITE(which
), j
++) {
742 for (i
= 0; i
< w
; i
++)
743 check_rowcol(state
, i
, COLUMN
, which
, &wrong
, &incomplete
);
745 for (i
= 0; i
< h
; i
++)
746 check_rowcol(state
, i
, ROW
, which
, &wrong
, &incomplete
);
748 /* Check each domino has been filled, and that we don't have
749 * touching identical terminals. */
750 for (i
= 0; i
< state
->wh
; i
++) state
->flags
[i
] &= ~GS_ERROR
;
751 for (x
= 0; x
< w
; x
++) {
752 for (y
= 0; y
< h
; y
++) {
754 if (state
->common
->dominoes
[idx
] == idx
)
755 continue; /* no domino here */
757 if (!(state
->flags
[idx
] & GS_SET
))
760 which
= state
->grid
[idx
];
761 if (which
!= NEUTRAL
) {
762 #define CHECK(xx,yy) do { \
763 if (INGRID(state,xx,yy) && \
764 (state->grid[(yy)*w+(xx)] == which)) { \
766 state->flags[(yy)*w+(xx)] |= GS_ERROR; \
767 state->flags[y*w+x] |= GS_ERROR; \
778 return wrong
? -1 : incomplete
? 0 : 1;
781 static const int dx
[4] = {-1, 1, 0, 0};
782 static const int dy
[4] = {0, 0, -1, 1};
784 static void solve_clearflags(game_state
*state
)
788 for (i
= 0; i
< state
->wh
; i
++) {
789 state
->flags
[i
] &= ~GS_NOTMASK
;
790 if (state
->common
->dominoes
[i
] != i
)
791 state
->flags
[i
] &= ~GS_SET
;
795 /* Knowing a given cell cannot be a certain colour also tells us
796 * something about the other cell in that domino. */
797 static int solve_unflag(game_state
*state
, int i
, int which
,
798 const char *why
, rowcol
*rc
)
801 #if defined DEBUGGING || defined STANDALONE_SOLVER
805 assert(i
>= 0 && i
< state
->wh
);
806 ii
= state
->common
->dominoes
[i
];
807 if (ii
== i
) return 0;
810 debug(("solve_unflag: (%d,%d) for %s %d", i
%w
, i
/w
, rc
->name
, rc
->num
));
812 if ((state
->flags
[i
] & GS_SET
) && (state
->grid
[i
] == which
)) {
813 debug(("solve_unflag: (%d,%d) already %s, cannot unflag (for %s).",
814 i
%w
, i
/w
, NAME(which
), why
));
817 if ((state
->flags
[ii
] & GS_SET
) && (state
->grid
[ii
] == OPPOSITE(which
))) {
818 debug(("solve_unflag: (%d,%d) opposite already %s, cannot unflag (for %s).",
819 ii
%w
, ii
/w
, NAME(OPPOSITE(which
)), why
));
822 if (POSSIBLE(i
, which
)) {
823 state
->flags
[i
] |= NOTFLAG(which
);
825 debug(("solve_unflag: (%d,%d) CANNOT be %s (%s)",
826 i
%w
, i
/w
, NAME(which
), why
));
828 if (POSSIBLE(ii
, OPPOSITE(which
))) {
829 state
->flags
[ii
] |= NOTFLAG(OPPOSITE(which
));
831 debug(("solve_unflag: (%d,%d) CANNOT be %s (%s, other half)",
832 ii
%w
, ii
/w
, NAME(OPPOSITE(which
)), why
));
834 #ifdef STANDALONE_SOLVER
835 if (verbose
&& ret
) {
836 printf("(%d,%d)", i
%w
, i
/w
);
837 if (rc
) printf(" in %s %d", rc
->name
, rc
->num
);
838 printf(" cannot be %s (%s); opposite (%d,%d) not %s.\n",
839 NAME(which
), why
, ii
%w
, ii
/w
, NAME(OPPOSITE(which
)));
845 static int solve_unflag_surrounds(game_state
*state
, int i
, int which
)
847 int x
= i
%state
->w
, y
= i
/state
->w
, xx
, yy
, j
, ii
;
849 assert(INGRID(state
, x
, y
));
851 for (j
= 0; j
< 4; j
++) {
852 xx
= x
+dx
[j
]; yy
= y
+dy
[j
];
853 if (!INGRID(state
, xx
, yy
)) continue;
856 if (solve_unflag(state
, ii
, which
, "adjacent to set cell", NULL
) < 0)
862 /* Sets a cell to a particular colour, and also perform other
863 * housekeeping around that. */
864 static int solve_set(game_state
*state
, int i
, int which
,
865 const char *why
, rowcol
*rc
)
868 #if defined DEBUGGING || defined STANDALONE_SOLVER
872 ii
= state
->common
->dominoes
[i
];
874 if (state
->flags
[i
] & GS_SET
) {
875 if (state
->grid
[i
] == which
) {
876 return 0; /* was already set and held, do nothing. */
878 debug(("solve_set: (%d,%d) is held and %s, cannot set to %s",
879 i
%w
, i
/w
, NAME(state
->grid
[i
]), NAME(which
)));
883 if ((state
->flags
[ii
] & GS_SET
) && state
->grid
[ii
] != OPPOSITE(which
)) {
884 debug(("solve_set: (%d,%d) opposite is held and %s, cannot set to %s",
885 ii
%w
, ii
/w
, NAME(state
->grid
[ii
]), NAME(OPPOSITE(which
))));
888 if (!POSSIBLE(i
, which
)) {
889 debug(("solve_set: (%d,%d) NOT %s, cannot set.", i
%w
, i
/w
, NAME(which
)));
892 if (!POSSIBLE(ii
, OPPOSITE(which
))) {
893 debug(("solve_set: (%d,%d) NOT %s, cannot set (%d,%d).",
894 ii
%w
, ii
/w
, NAME(OPPOSITE(which
)), i
%w
, i
/w
));
898 #ifdef STANDALONE_SOLVER
900 printf("(%d,%d)", i
%w
, i
/w
);
901 if (rc
) printf(" in %s %d", rc
->name
, rc
->num
);
902 printf(" set to %s (%s), opposite (%d,%d) set to %s.\n",
903 NAME(which
), why
, ii
%w
, ii
/w
, NAME(OPPOSITE(which
)));
907 debug(("solve_set: (%d,%d) for %s %d", i
%w
, i
/w
, rc
->name
, rc
->num
));
908 debug(("solve_set: (%d,%d) setting to %s (%s), surrounds first:",
909 i
%w
, i
/w
, NAME(which
), why
));
911 if (which
!= NEUTRAL
) {
912 if (solve_unflag_surrounds(state
, i
, which
) < 0)
914 if (solve_unflag_surrounds(state
, ii
, OPPOSITE(which
)) < 0)
918 state
->grid
[i
] = which
;
919 state
->grid
[ii
] = OPPOSITE(which
);
921 state
->flags
[i
] |= GS_SET
;
922 state
->flags
[ii
] |= GS_SET
;
924 debug(("solve_set: (%d,%d) set to %s (%s)", i
%w
, i
/w
, NAME(which
), why
));
929 /* counts should be int[4]. */
930 static void solve_counts(game_state
*state
, rowcol rc
, int *counts
, int *unset
)
935 for (i
= 0; i
< 4; i
++) {
937 if (unset
) unset
[i
] = 0;
940 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
941 if (state
->flags
[i
] & GS_SET
) {
942 assert(state
->grid
[i
] < 3);
943 counts
[state
->grid
[i
]]++;
945 for (which
= 0; which
<= 2; which
++) {
946 if (POSSIBLE(i
, which
))
953 static int solve_checkfull(game_state
*state
, rowcol rc
, int *counts
)
955 int starti
= rc
.i
, j
, which
, didsth
= 0, target
;
958 assert(state
->numbered
); /* only useful (should only be called) if numbered. */
960 solve_counts(state
, rc
, counts
, unset
);
962 for (which
= 0; which
<= 2; which
++) {
963 target
= rc
.targets
[which
];
964 if (target
== -1) continue;
966 /*debug(("%s %d for %s: target %d, count %d, unset %d",
967 rc.name, rc.num, NAME(which),
968 target, counts[which], unset[which]));*/
970 if (target
< counts
[which
]) {
971 debug(("%s %d has too many (%d) %s squares (target %d), impossible!",
972 rc
.name
, rc
.num
, counts
[which
], NAME(which
), target
));
975 if (target
== counts
[which
]) {
976 /* We have the correct no. of the colour in this row/column
977 * already; unflag all the rest. */
978 for (rc
.i
= starti
, j
= 0; j
< rc
.n
; rc
.i
+= rc
.di
, j
++) {
979 if (state
->flags
[rc
.i
] & GS_SET
) continue;
980 if (!POSSIBLE(rc
.i
, which
)) continue;
982 if (solve_unflag(state
, rc
.i
, which
, "row/col full", &rc
) < 0)
986 } else if ((target
- counts
[which
]) == unset
[which
]) {
987 /* We need all the remaining unset squares for this colour;
989 for (rc
.i
= starti
, j
= 0; j
< rc
.n
; rc
.i
+= rc
.di
, j
++) {
990 if (state
->flags
[rc
.i
] & GS_SET
) continue;
991 if (!POSSIBLE(rc
.i
, which
)) continue;
993 if (solve_set(state
, rc
.i
, which
, "row/col needs all unset", &rc
) < 0)
1002 static int solve_startflags(game_state
*state
)
1006 for (x
= 0; x
< state
->w
; x
++) {
1007 for (y
= 0; y
< state
->h
; y
++) {
1009 if (state
->common
->dominoes
[i
] == i
) continue;
1010 if (state
->grid
[i
] != NEUTRAL
||
1011 state
->flags
[i
] & GS_SET
) {
1012 if (solve_set(state
, i
, state
->grid
[i
], "initial set-and-hold", NULL
) < 0)
1020 typedef int (*rowcolfn
)(game_state
*state
, rowcol rc
, int *counts
);
1022 static int solve_rowcols(game_state
*state
, rowcolfn fn
)
1024 int x
, y
, didsth
= 0, ret
;
1028 for (x
= 0; x
< state
->w
; x
++) {
1029 rc
= mkrowcol(state
, x
, COLUMN
);
1030 solve_counts(state
, rc
, counts
, NULL
);
1032 ret
= fn(state
, rc
, counts
);
1033 if (ret
< 0) return ret
;
1036 for (y
= 0; y
< state
->h
; y
++) {
1037 rc
= mkrowcol(state
, y
, ROW
);
1038 solve_counts(state
, rc
, counts
, NULL
);
1040 ret
= fn(state
, rc
, counts
);
1041 if (ret
< 0) return ret
;
1047 static int solve_force(game_state
*state
)
1049 int x
, y
, i
, which
, didsth
= 0;
1052 for (i
= 0; i
< state
->wh
; i
++) {
1053 if (state
->flags
[i
] & GS_SET
) continue;
1054 if (state
->common
->dominoes
[i
] == i
) continue;
1056 f
= state
->flags
[i
] & GS_NOTMASK
;
1058 if (f
== (GS_NOTPOSITIVE
|GS_NOTNEGATIVE
))
1060 if (f
== (GS_NOTPOSITIVE
|GS_NOTNEUTRAL
))
1062 if (f
== (GS_NOTNEGATIVE
|GS_NOTNEUTRAL
))
1065 x
= i
%state
->w
; y
= i
/state
->w
;
1066 if (solve_set(state
, i
, which
, "forced by flags", NULL
) < 0)
1074 static int solve_neither(game_state
*state
)
1076 int x
, y
, i
, j
, didsth
= 0;
1078 for (i
= 0; i
< state
->wh
; i
++) {
1079 if (state
->flags
[i
] & GS_SET
) continue;
1080 j
= state
->common
->dominoes
[i
];
1081 if (i
== j
) continue;
1083 if (((state
->flags
[i
] & GS_NOTPOSITIVE
) &&
1084 (state
->flags
[j
] & GS_NOTPOSITIVE
)) ||
1085 ((state
->flags
[i
] & GS_NOTNEGATIVE
) &&
1086 (state
->flags
[j
] & GS_NOTNEGATIVE
))) {
1087 x
= i
%state
->w
; y
= i
/state
->w
;
1088 if (solve_set(state
, i
, NEUTRAL
, "neither tile magnet", NULL
) < 0)
1096 static int solve_advancedfull(game_state
*state
, rowcol rc
, int *counts
)
1098 int i
, j
, nfound
= 0, clearpos
= 0, clearneg
= 0, ret
= 0;
1100 /* For this row/col, look for a domino entirely within the row where
1101 * both ends can only be + or - (but isn't held).
1102 * The +/- counts can thus be decremented by 1 each, and the 'unset'
1105 * Once that's done for all such dominoes (and they're marked), try
1106 * and made usual deductions about rest of the row based on new totals. */
1108 if (rc
.targets
[POSITIVE
] == -1 && rc
.targets
[NEGATIVE
] == -1)
1109 return 0; /* don't have a target for either colour, nothing to do. */
1110 if ((rc
.targets
[POSITIVE
] >= 0 && counts
[POSITIVE
] == rc
.targets
[POSITIVE
]) &&
1111 (rc
.targets
[NEGATIVE
] >= 0 && counts
[NEGATIVE
] == rc
.targets
[NEGATIVE
]))
1112 return 0; /* both colours are full up already, nothing to do. */
1114 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++)
1115 state
->flags
[i
] &= ~GS_MARK
;
1117 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1118 if (state
->flags
[i
] & GS_SET
) continue;
1120 /* We're looking for a domino in our row/col, thus if
1121 * dominoes[i] -> i+di we've found one. */
1122 if (state
->common
->dominoes
[i
] != i
+rc
.di
) continue;
1124 /* We need both squares of this domino to be either + or -
1125 * (i.e. both NOTNEUTRAL only). */
1126 if (((state
->flags
[i
] & GS_NOTMASK
) != GS_NOTNEUTRAL
) ||
1127 ((state
->flags
[i
+rc
.di
] & GS_NOTMASK
) != GS_NOTNEUTRAL
))
1130 debug(("Domino in %s %d at (%d,%d) must be polarised.",
1131 rc
.name
, rc
.num
, i
%state
->w
, i
/state
->w
));
1132 state
->flags
[i
] |= GS_MARK
;
1133 state
->flags
[i
+rc
.di
] |= GS_MARK
;
1136 if (nfound
== 0) return 0;
1138 /* nfound is #dominoes we matched, which will all be marked. */
1139 counts
[POSITIVE
] += nfound
;
1140 counts
[NEGATIVE
] += nfound
;
1142 if (rc
.targets
[POSITIVE
] >= 0 && counts
[POSITIVE
] == rc
.targets
[POSITIVE
]) {
1143 debug(("%s %d has now filled POSITIVE:", rc
.name
, rc
.num
));
1146 if (rc
.targets
[NEGATIVE
] >= 0 && counts
[NEGATIVE
] == rc
.targets
[NEGATIVE
]) {
1147 debug(("%s %d has now filled NEGATIVE:", rc
.name
, rc
.num
));
1151 if (!clearpos
&& !clearneg
) return 0;
1153 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1154 if (state
->flags
[i
] & GS_SET
) continue;
1155 if (state
->flags
[i
] & GS_MARK
) continue;
1157 if (clearpos
&& !(state
->flags
[i
] & GS_NOTPOSITIVE
)) {
1158 if (solve_unflag(state
, i
, POSITIVE
, "row/col full (+ve) [tricky]", &rc
) < 0)
1162 if (clearneg
&& !(state
->flags
[i
] & GS_NOTNEGATIVE
)) {
1163 if (solve_unflag(state
, i
, NEGATIVE
, "row/col full (-ve) [tricky]", &rc
) < 0)
1172 /* If we only have one neutral still to place on a row/column then no
1173 dominoes entirely in that row/column can be neutral. */
1174 static int solve_nonneutral(game_state
*state
, rowcol rc
, int *counts
)
1178 if (rc
.targets
[NEUTRAL
] != counts
[NEUTRAL
]+1)
1181 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1182 if (state
->flags
[i
] & GS_SET
) continue;
1183 if (state
->common
->dominoes
[i
] != i
+rc
.di
) continue;
1185 if (!(state
->flags
[i
] & GS_NOTNEUTRAL
)) {
1186 if (solve_unflag(state
, i
, NEUTRAL
, "single neutral in row/col [tricky]", &rc
) < 0)
1194 /* If we need to fill all unfilled cells with +-, and we need 1 more of
1195 * one than the other, and we have a single odd-numbered region of unfilled
1196 * cells, that odd-numbered region must start and end with the extra number. */
1197 static int solve_oddlength(game_state
*state
, rowcol rc
, int *counts
)
1199 int i
, j
, ret
= 0, extra
, tpos
, tneg
;
1200 int start
= -1, length
= 0, inempty
= 0, startodd
= -1;
1202 /* need zero neutral cells still to find... */
1203 if (rc
.targets
[NEUTRAL
] != counts
[NEUTRAL
])
1206 /* ...and #positive and #negative to differ by one. */
1207 tpos
= rc
.targets
[POSITIVE
] - counts
[POSITIVE
];
1208 tneg
= rc
.targets
[NEGATIVE
] - counts
[NEGATIVE
];
1211 else if (tneg
== tpos
+1)
1215 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1216 if (state
->flags
[i
] & GS_SET
) {
1219 /* we've just finished an odd-length section. */
1220 if (startodd
!= -1) goto twoodd
;
1235 if (inempty
&& (length
% 2)) {
1236 if (startodd
!= -1) goto twoodd
;
1240 ret
= solve_set(state
, startodd
, extra
, "odd-length section start", &rc
);
1245 debug(("%s %d has >1 odd-length sections, starting at %d,%d and %d,%d.",
1247 startodd
%state
->w
, startodd
/state
->w
,
1248 start
%state
->w
, start
/state
->w
));
1252 /* Count the number of remaining empty dominoes in any row/col.
1253 * If that number is equal to the #remaining positive,
1254 * or to the #remaining negative, no empty cells can be neutral. */
1255 static int solve_countdominoes_neutral(game_state
*state
, rowcol rc
, int *counts
)
1257 int i
, j
, ndom
= 0, nonn
= 0, ret
= 0;
1259 if ((rc
.targets
[POSITIVE
] == -1) && (rc
.targets
[NEGATIVE
] == -1))
1260 return 0; /* need at least one target to compare. */
1262 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1263 if (state
->flags
[i
] & GS_SET
) continue;
1264 assert(state
->grid
[i
] == EMPTY
);
1266 /* Skip solo cells, or second cell in domino. */
1267 if ((state
->common
->dominoes
[i
] == i
) ||
1268 (state
->common
->dominoes
[i
] == i
-rc
.di
))
1274 if ((rc
.targets
[POSITIVE
] != -1) &&
1275 (rc
.targets
[POSITIVE
]-counts
[POSITIVE
] == ndom
))
1277 if ((rc
.targets
[NEGATIVE
] != -1) &&
1278 (rc
.targets
[NEGATIVE
]-counts
[NEGATIVE
] == ndom
))
1281 if (!nonn
) return 0;
1283 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1284 if (state
->flags
[i
] & GS_SET
) continue;
1286 if (!(state
->flags
[i
] & GS_NOTNEUTRAL
)) {
1287 if (solve_unflag(state
, i
, NEUTRAL
, "all dominoes +/- [tricky]", &rc
) < 0)
1295 static int solve_domino_count(game_state
*state
, rowcol rc
, int i
, int which
)
1299 /* Skip solo cells or 2nd in domino. */
1300 if ((state
->common
->dominoes
[i
] == i
) ||
1301 (state
->common
->dominoes
[i
] == i
-rc
.di
))
1304 if (state
->flags
[i
] & GS_SET
)
1307 if (POSSIBLE(i
, which
))
1310 if (state
->common
->dominoes
[i
] == i
+rc
.di
) {
1311 /* second cell of domino is on our row: test that too. */
1312 if (POSSIBLE(i
+rc
.di
, which
))
1318 /* Count number of dominoes we could put each of + and - into. If it is equal
1319 * to the #left, any domino we can only put + or - in one cell of must have it. */
1320 static int solve_countdominoes_nonneutral(game_state
*state
, rowcol rc
, int *counts
)
1322 int which
, w
, i
, j
, ndom
= 0, didsth
= 0, toset
;
1324 for (which
= POSITIVE
, w
= 0; w
< 2; which
= OPPOSITE(which
), w
++) {
1325 if (rc
.targets
[which
] == -1) continue;
1327 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1328 if (solve_domino_count(state
, rc
, i
, which
) > 0)
1332 if ((rc
.targets
[which
] - counts
[which
]) != ndom
)
1335 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1336 if (solve_domino_count(state
, rc
, i
, which
) == 1) {
1337 if (POSSIBLE(i
, which
))
1340 /* paranoia, should have been checked by solve_domino_count. */
1341 assert(state
->common
->dominoes
[i
] == i
+rc
.di
);
1342 assert(POSSIBLE(i
+rc
.di
, which
));
1345 if (solve_set(state
, toset
, which
, "all empty dominoes need +/- [tricky]", &rc
) < 0)
1354 /* danger, evil macro. can't use the do { ... } while(0) trick because
1355 * the continue breaks. */
1356 #define SOLVE_FOR_ROWCOLS(fn) \
1357 ret = solve_rowcols(state, fn); \
1358 if (ret < 0) { debug(("%s said impossible, cannot solve", #fn)); return -1; } \
1359 if (ret > 0) continue
1361 static int solve_state(game_state
*state
, int diff
)
1365 debug(("solve_state, difficulty %s", magnets_diffnames
[diff
]));
1367 solve_clearflags(state
);
1368 if (solve_startflags(state
) < 0) return -1;
1371 ret
= solve_force(state
);
1372 if (ret
> 0) continue;
1373 if (ret
< 0) return -1;
1375 ret
= solve_neither(state
);
1376 if (ret
> 0) continue;
1377 if (ret
< 0) return -1;
1379 SOLVE_FOR_ROWCOLS(solve_checkfull
);
1380 SOLVE_FOR_ROWCOLS(solve_oddlength
);
1382 if (diff
< DIFF_TRICKY
) break;
1384 SOLVE_FOR_ROWCOLS(solve_advancedfull
);
1385 SOLVE_FOR_ROWCOLS(solve_nonneutral
);
1386 SOLVE_FOR_ROWCOLS(solve_countdominoes_neutral
);
1387 SOLVE_FOR_ROWCOLS(solve_countdominoes_nonneutral
);
1393 return check_completion(state
);
1397 static char *game_state_diff(game_state
*src
, game_state
*dst
, int issolve
)
1399 char *ret
= NULL
, buf
[80], c
;
1400 int retlen
= 0, x
, y
, i
, k
;
1402 assert(src
->w
== dst
->w
&& src
->h
== dst
->h
);
1405 ret
= sresize(ret
, 3, char);
1406 ret
[0] = 'S'; ret
[1] = ';'; ret
[2] = '\0';
1409 for (x
= 0; x
< dst
->w
; x
++) {
1410 for (y
= 0; y
< dst
->h
; y
++) {
1413 if (src
->common
->dominoes
[i
] == i
) continue;
1415 #define APPEND do { \
1416 ret = sresize(ret, retlen + k + 1, char); \
1417 strcpy(ret + retlen, buf); \
1421 if ((src
->grid
[i
] != dst
->grid
[i
]) ||
1422 ((src
->flags
[i
] & GS_SET
) != (dst
->flags
[i
] & GS_SET
))) {
1423 if (dst
->grid
[i
] == EMPTY
&& !(dst
->flags
[i
] & GS_SET
))
1426 c
= GRID2CHAR(dst
->grid
[i
]);
1427 k
= sprintf(buf
, "%c%d,%d;", (int)c
, x
, y
);
1432 debug(("game_state_diff returns %s", ret
));
1436 static void solve_from_aux(game_state
*state
, char *aux
)
1439 assert(strlen(aux
) == state
->wh
);
1440 for (i
= 0; i
< state
->wh
; i
++) {
1441 state
->grid
[i
] = CHAR2GRID(aux
[i
]);
1442 state
->flags
[i
] |= GS_SET
;
1446 static char *solve_game(game_state
*state
, game_state
*currstate
,
1447 char *aux
, char **error
)
1449 game_state
*solved
= dup_game(currstate
);
1453 if (aux
&& strlen(aux
) == state
->wh
) {
1454 solve_from_aux(solved
, aux
);
1458 if (solve_state(solved
, DIFFCOUNT
) > 0) goto solved
;
1461 solved
= dup_game(state
);
1462 ret
= solve_state(solved
, DIFFCOUNT
);
1463 if (ret
> 0) goto solved
;
1466 *error
= (ret
< 0) ? "Puzzle is impossible." : "Unable to solve puzzle.";
1470 move
= game_state_diff(currstate
, solved
, 1);
1475 static int solve_unnumbered(game_state
*state
)
1479 ret
= solve_force(state
);
1480 if (ret
> 0) continue;
1481 if (ret
< 0) return -1;
1483 ret
= solve_neither(state
);
1484 if (ret
> 0) continue;
1485 if (ret
< 0) return -1;
1489 for (i
= 0; i
< state
->wh
; i
++) {
1490 if (!(state
->flags
[i
] & GS_SET
)) return 0;
1495 static int lay_dominoes(game_state
*state
, random_state
*rs
, int *scratch
)
1497 int n
, i
, ret
= 0, x
, y
, nlaid
= 0, n_initial_neutral
;
1499 for (i
= 0; i
< state
->wh
; i
++) {
1501 state
->grid
[i
] = EMPTY
;
1502 state
->flags
[i
] = (state
->common
->dominoes
[i
] == i
) ? GS_SET
: 0;
1504 shuffle(scratch
, state
->wh
, sizeof(int), rs
);
1506 n_initial_neutral
= (state
->wh
> 100) ? 5 : (state
->wh
/ 10);
1508 for (n
= 0; n
< state
->wh
; n
++) {
1509 /* Find a space ... */
1512 if (state
->flags
[i
] & GS_SET
) continue; /* already laid here. */
1514 /* ...and lay a domino if we can. */
1516 x
= i
%state
->w
; y
= i
/state
->w
;
1517 debug(("Laying domino at i:%d, (%d,%d)\n", i
, x
, y
));
1519 /* The choice of which type of domino to lay here leads to subtle differences
1520 * in the sorts of boards that get produced. Too much bias towards magnets
1521 * leads to games that are too easy.
1523 * Currently, it lays a small set of dominoes at random as neutral, and
1524 * then lays the rest preferring to be magnets -- however, if the
1525 * current layout is such that a magnet won't go there, then it lays
1528 * The number of initially neutral dominoes is limited as grids get bigger:
1529 * too many neutral dominoes invariably ends up with insoluble puzzle at
1530 * this size, and the positioning process means it'll always end up laying
1531 * more than the initial 5 anyway.
1534 /* We should always be able to lay a neutral anywhere. */
1535 assert(!(state
->flags
[i
] & GS_NOTNEUTRAL
));
1537 if (n
< n_initial_neutral
) {
1538 debug((" ...laying neutral\n"));
1539 ret
= solve_set(state
, i
, NEUTRAL
, "layout initial neutral", NULL
);
1541 debug((" ... preferring magnet\n"));
1542 if (!(state
->flags
[i
] & GS_NOTPOSITIVE
))
1543 ret
= solve_set(state
, i
, POSITIVE
, "layout", NULL
);
1544 else if (!(state
->flags
[i
] & GS_NOTNEGATIVE
))
1545 ret
= solve_set(state
, i
, NEGATIVE
, "layout", NULL
);
1547 ret
= solve_set(state
, i
, NEUTRAL
, "layout", NULL
);
1550 debug(("Unable to lay anything at (%d,%d), giving up.", x
, y
));
1556 ret
= solve_unnumbered(state
);
1558 debug(("solve_unnumbered decided impossible.\n"));
1563 debug(("Laid %d dominoes, total %d dominoes.\n", nlaid
, state
->wh
/2));
1564 game_debug(state
, "Final layout");
1568 static void gen_game(game_state
*new, random_state
*rs
)
1571 int *scratch
= snewn(new->wh
, int);
1573 #ifdef STANDALONE_SOLVER
1574 if (verbose
) printf("Generating new game...\n");
1578 sfree(new->common
->dominoes
); /* bit grotty. */
1579 new->common
->dominoes
= domino_layout(new->w
, new->h
, rs
);
1582 ret
= lay_dominoes(new, rs
, scratch
);
1585 /* for each cell, update colcount/rowcount as appropriate. */
1586 memset(new->common
->colcount
, 0, new->w
*3*sizeof(int));
1587 memset(new->common
->rowcount
, 0, new->h
*3*sizeof(int));
1588 for (x
= 0; x
< new->w
; x
++) {
1589 for (y
= 0; y
< new->h
; y
++) {
1590 val
= new->grid
[y
*new->w
+x
];
1591 new->common
->colcount
[x
*3+val
]++;
1592 new->common
->rowcount
[y
*3+val
]++;
1600 static void generate_aux(game_state
*new, char *aux
)
1603 for (i
= 0; i
< new->wh
; i
++)
1604 aux
[i
] = GRID2CHAR(new->grid
[i
]);
1605 aux
[new->wh
] = '\0';
1608 static int check_difficulty(game_params
*params
, game_state
*new,
1611 int *scratch
, *grid_correct
, slen
, i
;
1613 memset(new->grid
, EMPTY
, new->wh
*sizeof(int));
1615 if (params
->diff
> DIFF_EASY
) {
1616 /* If this is too easy, return. */
1617 if (solve_state(new, params
->diff
-1) > 0) {
1618 debug(("Puzzle is too easy."));
1622 if (solve_state(new, params
->diff
) <= 0) {
1623 debug(("Puzzle is not soluble at requested difficulty."));
1626 if (!params
->stripclues
) return 0;
1628 /* Copy the correct grid away. */
1629 grid_correct
= snewn(new->wh
, int);
1630 memcpy(grid_correct
, new->grid
, new->wh
*sizeof(int));
1632 /* Create shuffled array of side-clue locations. */
1633 slen
= new->w
*2 + new->h
*2;
1634 scratch
= snewn(slen
, int);
1635 for (i
= 0; i
< slen
; i
++) scratch
[i
] = i
;
1636 shuffle(scratch
, slen
, sizeof(int), rs
);
1638 /* For each clue, check whether removing it makes the puzzle unsoluble;
1639 * put it back if so. */
1640 for (i
= 0; i
< slen
; i
++) {
1641 int num
= scratch
[i
], which
, roworcol
, target
, targetn
, ret
;
1644 /* work out which clue we meant. */
1645 if (num
< new->w
+new->h
) { which
= POSITIVE
; }
1646 else { which
= NEGATIVE
; num
-= new->w
+new->h
; }
1648 if (num
< new->w
) { roworcol
= COLUMN
; }
1649 else { roworcol
= ROW
; num
-= new->w
; }
1651 /* num is now the row/column index in question. */
1652 rc
= mkrowcol(new, num
, roworcol
);
1654 /* Remove clue, storing original... */
1655 target
= rc
.targets
[which
];
1656 targetn
= rc
.targets
[NEUTRAL
];
1657 rc
.targets
[which
] = -1;
1658 rc
.targets
[NEUTRAL
] = -1;
1660 /* ...and see if we can still solve it. */
1661 game_debug(new, "removed clue, new board:");
1662 memset(new->grid
, EMPTY
, new->wh
* sizeof(int));
1663 ret
= solve_state(new, params
->diff
);
1667 memcmp(new->grid
, grid_correct
, new->wh
*sizeof(int)) != 0) {
1668 /* We made it ambiguous: put clue back. */
1669 debug(("...now impossible/different, put clue back."));
1670 rc
.targets
[which
] = target
;
1671 rc
.targets
[NEUTRAL
] = targetn
;
1675 sfree(grid_correct
);
1680 static char *new_game_desc(game_params
*params
, random_state
*rs
,
1681 char **aux_r
, int interactive
)
1683 game_state
*new = new_state(params
->w
, params
->h
);
1684 char *desc
, *aux
= snewn(new->wh
+1, char);
1688 generate_aux(new, aux
);
1689 } while (check_difficulty(params
, new, rs
) < 0);
1691 /* now we're complete, generate the description string
1692 * and an aux_info for the completed game. */
1693 desc
= generate_desc(new);
1702 int cur_x
, cur_y
, cur_visible
;
1705 static game_ui
*new_ui(game_state
*state
)
1707 game_ui
*ui
= snew(game_ui
);
1708 ui
->cur_x
= ui
->cur_y
= 0;
1709 ui
->cur_visible
= 0;
1713 static void free_ui(game_ui
*ui
)
1718 static char *encode_ui(game_ui
*ui
)
1723 static void decode_ui(game_ui
*ui
, char *encoding
)
1727 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
1728 game_state
*newstate
)
1730 if (!oldstate
->completed
&& newstate
->completed
)
1731 ui
->cur_visible
= 0;
1734 struct game_drawstate
{
1735 int tilesize
, started
, solved
;
1737 unsigned long *what
; /* size w*h */
1738 unsigned long *colwhat
, *rowwhat
; /* size 3*w, 3*h */
1741 #define DS_WHICH_MASK 0xf
1743 #define DS_ERROR 0x10
1744 #define DS_CURSOR 0x20
1746 #define DS_FULL 0x80
1747 #define DS_NOTPOS 0x100
1748 #define DS_NOTNEG 0x200
1749 #define DS_NOTNEU 0x400
1750 #define DS_FLASH 0x800
1752 #define PREFERRED_TILE_SIZE 32
1753 #define TILE_SIZE (ds->tilesize)
1754 #define BORDER (TILE_SIZE / 8)
1756 #define COORD(x) ( (x+1) * TILE_SIZE + BORDER )
1757 #define FROMCOORD(x) ( (x - BORDER) / TILE_SIZE - 1 )
1759 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
1760 int x
, int y
, int button
)
1762 int gx
= FROMCOORD(x
), gy
= FROMCOORD(y
), idx
, curr
;
1763 char *nullret
= NULL
, buf
[80], movech
;
1764 enum { CYCLE_MAGNET
, CYCLE_NEUTRAL
} action
;
1766 if (IS_CURSOR_MOVE(button
)) {
1767 move_cursor(button
, &ui
->cur_x
, &ui
->cur_y
, state
->w
, state
->h
, 0);
1768 ui
->cur_visible
= 1;
1770 } else if (IS_CURSOR_SELECT(button
)) {
1771 if (!ui
->cur_visible
) {
1772 ui
->cur_visible
= 1;
1775 action
= (button
== CURSOR_SELECT
) ? CYCLE_MAGNET
: CYCLE_NEUTRAL
;
1778 } else if (INGRID(state
, gx
, gy
) &&
1779 (button
== LEFT_BUTTON
|| button
== RIGHT_BUTTON
)) {
1780 if (ui
->cur_visible
) {
1781 ui
->cur_visible
= 0;
1784 action
= (button
== LEFT_BUTTON
) ? CYCLE_MAGNET
: CYCLE_NEUTRAL
;
1788 idx
= gy
* state
->w
+ gx
;
1789 if (state
->common
->dominoes
[idx
] == idx
) return nullret
;
1790 curr
= state
->grid
[idx
];
1792 if (action
== CYCLE_MAGNET
) {
1793 /* ... empty --> positive --> negative --> empty ... */
1795 if (state
->grid
[idx
] == NEUTRAL
&& state
->flags
[idx
] & GS_SET
)
1796 return nullret
; /* can't cycle a magnet from a neutral. */
1797 movech
= (curr
== EMPTY
) ? '+' : (curr
== POSITIVE
) ? '-' : ' ';
1798 } else if (action
== CYCLE_NEUTRAL
) {
1799 /* ... empty -> neutral -> !neutral --> empty ... */
1801 if (state
->grid
[idx
] != NEUTRAL
)
1802 return nullret
; /* can't cycle through neutral from a magnet. */
1804 /* All of these are grid == EMPTY == NEUTRAL; it twiddles
1805 * combinations of flags. */
1806 if (state
->flags
[idx
] & GS_SET
) /* neutral */
1808 else if (state
->flags
[idx
] & GS_NOTNEUTRAL
) /* !neutral */
1813 assert(!"unknown action");
1814 movech
= 0; /* placate optimiser */
1817 sprintf(buf
, "%c%d,%d", movech
, gx
, gy
);
1822 static game_state
*execute_move(game_state
*state
, char *move
)
1824 game_state
*ret
= dup_game(state
);
1825 int x
, y
, n
, idx
, idx2
;
1828 if (!*move
) goto badmove
;
1834 } else if (c
== '+' || c
== '-' ||
1835 c
== '.' || c
== ' ' || c
== '?') {
1836 if ((sscanf(move
, "%d,%d%n", &x
, &y
, &n
) != 2) ||
1837 !INGRID(state
, x
, y
)) goto badmove
;
1839 idx
= y
*state
->w
+ x
;
1840 idx2
= state
->common
->dominoes
[idx
];
1841 if (idx
== idx2
) goto badmove
;
1843 ret
->flags
[idx
] &= ~GS_NOTMASK
;
1844 ret
->flags
[idx2
] &= ~GS_NOTMASK
;
1846 if (c
== ' ' || c
== '?') {
1847 ret
->grid
[idx
] = EMPTY
;
1848 ret
->grid
[idx2
] = EMPTY
;
1849 ret
->flags
[idx
] &= ~GS_SET
;
1850 ret
->flags
[idx2
] &= ~GS_SET
;
1852 ret
->flags
[idx
] |= GS_NOTNEUTRAL
;
1853 ret
->flags
[idx2
] |= GS_NOTNEUTRAL
;
1856 ret
->grid
[idx
] = CHAR2GRID(c
);
1857 ret
->grid
[idx2
] = OPPOSITE(CHAR2GRID(c
));
1858 ret
->flags
[idx
] |= GS_SET
;
1859 ret
->flags
[idx2
] |= GS_SET
;
1865 if (*move
== ';') move
++;
1866 else if (*move
) goto badmove
;
1868 if (check_completion(ret
) == 1)
1878 /* ----------------------------------------------------------------------
1882 static void game_compute_size(game_params
*params
, int tilesize
,
1885 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1886 struct { int tilesize
; } ads
, *ds
= &ads
;
1887 ads
.tilesize
= tilesize
;
1889 *x
= TILE_SIZE
* (params
->w
+2) + 2 * BORDER
;
1890 *y
= TILE_SIZE
* (params
->h
+2) + 2 * BORDER
;
1893 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
1894 game_params
*params
, int tilesize
)
1896 ds
->tilesize
= tilesize
;
1899 static float *game_colours(frontend
*fe
, int *ncolours
)
1901 float *ret
= snewn(3 * NCOLOURS
, float);
1904 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
1906 for (i
= 0; i
< 3; i
++) {
1907 ret
[COL_TEXT
* 3 + i
] = 0.0F
;
1908 ret
[COL_NEGATIVE
* 3 + i
] = 0.0F
;
1909 ret
[COL_CURSOR
* 3 + i
] = 0.9F
;
1912 ret
[COL_POSITIVE
* 3 + 0] = 0.8F
;
1913 ret
[COL_POSITIVE
* 3 + 1] = 0.0F
;
1914 ret
[COL_POSITIVE
* 3 + 2] = 0.0F
;
1916 ret
[COL_NEUTRAL
* 3 + 0] = 0.10F
;
1917 ret
[COL_NEUTRAL
* 3 + 1] = 0.60F
;
1918 ret
[COL_NEUTRAL
* 3 + 2] = 0.10F
;
1920 ret
[COL_ERROR
* 3 + 0] = 1.0F
;
1921 ret
[COL_ERROR
* 3 + 1] = 0.0F
;
1922 ret
[COL_ERROR
* 3 + 2] = 0.0F
;
1924 ret
[COL_NOT
* 3 + 0] = 0.2F
;
1925 ret
[COL_NOT
* 3 + 1] = 0.2F
;
1926 ret
[COL_NOT
* 3 + 2] = 1.0F
;
1928 *ncolours
= NCOLOURS
;
1932 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1934 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1936 ds
->tilesize
= ds
->started
= ds
->solved
= 0;
1940 ds
->what
= snewn(state
->wh
, unsigned long);
1941 memset(ds
->what
, 0, state
->wh
*sizeof(unsigned long));
1943 ds
->colwhat
= snewn(state
->w
*3, unsigned long);
1944 memset(ds
->colwhat
, 0, state
->w
*3*sizeof(unsigned long));
1945 ds
->rowwhat
= snewn(state
->h
*3, unsigned long);
1946 memset(ds
->rowwhat
, 0, state
->h
*3*sizeof(unsigned long));
1951 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1959 static void draw_num_col(drawing
*dr
, game_drawstate
*ds
, int rowcol
, int which
,
1960 int idx
, int colbg
, int col
, int num
)
1965 if (num
< 0) return;
1967 sprintf(buf
, "%d", num
);
1968 tsz
= (strlen(buf
) == 1) ? (7*TILE_SIZE
/10) : (9*TILE_SIZE
/10)/strlen(buf
);
1970 if (rowcol
== ROW
) {
1972 if (which
== NEGATIVE
) cx
+= TILE_SIZE
* (ds
->w
+1);
1973 cy
= BORDER
+ TILE_SIZE
* (idx
+1);
1975 cx
= BORDER
+ TILE_SIZE
* (idx
+1);
1977 if (which
== NEGATIVE
) cy
+= TILE_SIZE
* (ds
->h
+1);
1980 draw_rect(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
, colbg
);
1981 draw_text(dr
, cx
+ TILE_SIZE
/2, cy
+ TILE_SIZE
/2, FONT_VARIABLE
, tsz
,
1982 ALIGN_VCENTRE
| ALIGN_HCENTRE
, col
, buf
);
1984 draw_update(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
);
1987 static void draw_num(drawing
*dr
, game_drawstate
*ds
, int rowcol
, int which
,
1988 int idx
, unsigned long c
, int num
)
1990 draw_num_col(dr
, ds
, rowcol
, which
, idx
, COL_BACKGROUND
,
1991 (c
& DS_ERROR
) ? COL_ERROR
: COL_TEXT
, num
);
1994 static void draw_sym(drawing
*dr
, game_drawstate
*ds
, int x
, int y
, int which
, int col
)
1996 int cx
= COORD(x
), cy
= COORD(y
);
1997 int ccx
= cx
+ TILE_SIZE
/2, ccy
= cy
+ TILE_SIZE
/2;
1998 int roff
= TILE_SIZE
/4, rsz
= 2*roff
+1;
1999 int soff
= TILE_SIZE
/16, ssz
= 2*soff
+1;
2001 if (which
== POSITIVE
|| which
== NEGATIVE
) {
2002 draw_rect(dr
, ccx
- roff
, ccy
- soff
, rsz
, ssz
, col
);
2003 if (which
== POSITIVE
)
2004 draw_rect(dr
, ccx
- soff
, ccy
- roff
, ssz
, rsz
, col
);
2005 } else if (col
== COL_NOT
) {
2006 /* not-a-neutral is a blue question mark. */
2007 char qu
[2] = { '?', 0 };
2008 draw_text(dr
, ccx
, ccy
, FONT_VARIABLE
, 7*TILE_SIZE
/10,
2009 ALIGN_VCENTRE
| ALIGN_HCENTRE
, col
, qu
);
2011 draw_line(dr
, ccx
- roff
, ccy
- roff
, ccx
+ roff
, ccy
+ roff
, col
);
2012 draw_line(dr
, ccx
+ roff
, ccy
- roff
, ccx
- roff
, ccy
+ roff
, col
);
2024 /* NOT responsible for redrawing background or updating. */
2025 static void draw_tile_col(drawing
*dr
, game_drawstate
*ds
, int *dominoes
,
2026 int x
, int y
, int which
, int bg
, int fg
, int perc
)
2028 int cx
= COORD(x
), cy
= COORD(y
), i
, other
, type
= TYPE_BLANK
;
2029 int gutter
, radius
, coffset
;
2031 /* gutter is TSZ/16 for 100%, 8*TSZ/16 (TSZ/2) for 0% */
2032 gutter
= (TILE_SIZE
/ 16) + ((100 - perc
) * (7*TILE_SIZE
/ 16))/100;
2033 radius
= (perc
* (TILE_SIZE
/ 8)) / 100;
2034 coffset
= gutter
+ radius
;
2037 other
= dominoes
[i
];
2039 if (other
== i
) return;
2040 else if (other
== i
+1) type
= TYPE_L
;
2041 else if (other
== i
-1) type
= TYPE_R
;
2042 else if (other
== i
+ds
->w
) type
= TYPE_T
;
2043 else if (other
== i
-ds
->w
) type
= TYPE_B
;
2044 else assert(!"mad domino orientation");
2046 /* domino drawing shamelessly stolen from dominosa.c. */
2047 if (type
== TYPE_L
|| type
== TYPE_T
)
2048 draw_circle(dr
, cx
+coffset
, cy
+coffset
,
2050 if (type
== TYPE_R
|| type
== TYPE_T
)
2051 draw_circle(dr
, cx
+TILE_SIZE
-1-coffset
, cy
+coffset
,
2053 if (type
== TYPE_L
|| type
== TYPE_B
)
2054 draw_circle(dr
, cx
+coffset
, cy
+TILE_SIZE
-1-coffset
,
2056 if (type
== TYPE_R
|| type
== TYPE_B
)
2057 draw_circle(dr
, cx
+TILE_SIZE
-1-coffset
,
2058 cy
+TILE_SIZE
-1-coffset
,
2061 for (i
= 0; i
< 2; i
++) {
2064 x1
= cx
+ (i
? gutter
: coffset
);
2065 y1
= cy
+ (i
? coffset
: gutter
);
2066 x2
= cx
+ TILE_SIZE
-1 - (i
? gutter
: coffset
);
2067 y2
= cy
+ TILE_SIZE
-1 - (i
? coffset
: gutter
);
2069 x2
= cx
+ TILE_SIZE
;
2070 else if (type
== TYPE_R
)
2072 else if (type
== TYPE_T
)
2073 y2
= cy
+ TILE_SIZE
;
2074 else if (type
== TYPE_B
)
2077 draw_rect(dr
, x1
, y1
, x2
-x1
+1, y2
-y1
+1, bg
);
2080 if (fg
!= -1) draw_sym(dr
, ds
, x
, y
, which
, fg
);
2083 static void draw_tile(drawing
*dr
, game_drawstate
*ds
, int *dominoes
,
2084 int x
, int y
, unsigned long flags
)
2086 int cx
= COORD(x
), cy
= COORD(y
), bg
, fg
, perc
= 100;
2087 int which
= flags
& DS_WHICH_MASK
;
2089 flags
&= ~DS_WHICH_MASK
;
2091 draw_rect(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
, COL_BACKGROUND
);
2093 if (flags
& DS_CURSOR
)
2094 bg
= COL_CURSOR
; /* off-white white for cursor */
2095 else if (which
== POSITIVE
)
2097 else if (which
== NEGATIVE
)
2099 else if (flags
& DS_SET
)
2100 bg
= COL_NEUTRAL
; /* green inner for neutral cells */
2102 bg
= COL_LOWLIGHT
; /* light grey for empty cells. */
2104 if (which
== EMPTY
&& !(flags
& DS_SET
)) {
2106 fg
= -1; /* don't draw cross unless actually set as neutral. */
2108 if (flags
& DS_NOTPOS
) notwhich
= POSITIVE
;
2109 if (flags
& DS_NOTNEG
) notwhich
= NEGATIVE
;
2110 if (flags
& DS_NOTNEU
) notwhich
= NEUTRAL
;
2111 if (notwhich
!= -1) {
2116 fg
= (flags
& DS_ERROR
) ? COL_ERROR
:
2117 (flags
& DS_CURSOR
) ? COL_TEXT
: COL_BACKGROUND
;
2119 draw_rect(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
, COL_BACKGROUND
);
2121 if (flags
& DS_FLASH
) {
2122 int bordercol
= COL_HIGHLIGHT
;
2123 draw_tile_col(dr
, ds
, dominoes
, x
, y
, which
, bordercol
, -1, perc
);
2126 draw_tile_col(dr
, ds
, dominoes
, x
, y
, which
, bg
, fg
, perc
);
2128 draw_update(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
);
2132 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
2133 game_state
*state
, int dir
, game_ui
*ui
,
2134 float animtime
, float flashtime
)
2136 int x
, y
, w
= state
->w
, h
= state
->h
, which
, i
, j
, flash
;
2137 unsigned long c
= 0;
2139 flash
= (int)(flashtime
* 5 / FLASH_TIME
) % 2;
2142 /* draw background, corner +-. */
2144 TILE_SIZE
* (w
+2) + 2 * BORDER
,
2145 TILE_SIZE
* (h
+2) + 2 * BORDER
,
2148 draw_sym(dr
, ds
, -1, -1, POSITIVE
, COL_TEXT
);
2149 draw_sym(dr
, ds
, state
->w
, state
->h
, NEGATIVE
, COL_TEXT
);
2151 draw_update(dr
, 0, 0,
2152 TILE_SIZE
* (ds
->w
+2) + 2 * BORDER
,
2153 TILE_SIZE
* (ds
->h
+2) + 2 * BORDER
);
2157 for (y
= 0; y
< h
; y
++) {
2158 for (x
= 0; x
< w
; x
++) {
2161 c
= state
->grid
[idx
];
2163 if (state
->flags
[idx
] & GS_ERROR
)
2165 if (state
->flags
[idx
] & GS_SET
)
2168 if (x
== ui
->cur_x
&& y
== ui
->cur_y
&& ui
->cur_visible
)
2174 if (state
->flags
[idx
] & GS_NOTPOSITIVE
)
2176 if (state
->flags
[idx
] & GS_NOTNEGATIVE
)
2178 if (state
->flags
[idx
] & GS_NOTNEUTRAL
)
2181 if (ds
->what
[idx
] != c
|| !ds
->started
) {
2182 draw_tile(dr
, ds
, state
->common
->dominoes
, x
, y
, c
);
2187 /* Draw counts around side */
2188 for (which
= POSITIVE
, j
= 0; j
< 2; which
= OPPOSITE(which
), j
++) {
2190 for (i
= 0; i
< w
; i
++) {
2191 target
= state
->common
->colcount
[i
*3+which
];
2192 count
= count_rowcol(state
, i
, COLUMN
, which
);
2194 if ((count
> target
) ||
2195 (count
< target
&& !count_rowcol(state
, i
, COLUMN
, -1)))
2197 if (count
== target
) c
|= DS_FULL
;
2198 if (c
!= ds
->colwhat
[i
*3+which
] || !ds
->started
) {
2199 draw_num(dr
, ds
, COLUMN
, which
, i
, c
,
2200 state
->common
->colcount
[i
*3+which
]);
2201 ds
->colwhat
[i
*3+which
] = c
;
2204 for (i
= 0; i
< h
; i
++) {
2205 target
= state
->common
->rowcount
[i
*3+which
];
2206 count
= count_rowcol(state
, i
, ROW
, which
);
2208 if ((count
> target
) ||
2209 (count
< target
&& !count_rowcol(state
, i
, ROW
, -1)))
2211 if (count
== target
) c
|= DS_FULL
;
2212 if (c
!= ds
->rowwhat
[i
*3+which
] || !ds
->started
) {
2213 draw_num(dr
, ds
, ROW
, which
, i
, c
,
2214 state
->common
->rowcount
[i
*3+which
]);
2215 ds
->rowwhat
[i
*3+which
] = c
;
2223 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
2224 int dir
, game_ui
*ui
)
2229 static float game_flash_length(game_state
*oldstate
, game_state
*newstate
,
2230 int dir
, game_ui
*ui
)
2232 if (!oldstate
->completed
&& newstate
->completed
&&
2233 !oldstate
->solved
&& !newstate
->solved
)
2238 static int game_timing_state(game_state
*state
, game_ui
*ui
)
2243 static void game_print_size(game_params
*params
, float *x
, float *y
)
2248 * I'll use 6mm squares by default.
2250 game_compute_size(params
, 600, &pw
, &ph
);
2255 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
2257 int w
= state
->w
, h
= state
->h
;
2258 int ink
= print_mono_colour(dr
, 0);
2259 int paper
= print_mono_colour(dr
, 1);
2260 int x
, y
, target
, count
, which
, i
, j
;
2262 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2263 game_drawstate ads
, *ds
= &ads
;
2264 game_set_size(dr
, ds
, NULL
, tilesize
);
2265 ds
->w
= w
; ds
->h
= h
;
2268 print_line_width(dr
, TILE_SIZE
/12);
2270 /* Numbers and +/- for corners. */
2271 draw_sym(dr
, ds
, -1, -1, POSITIVE
, ink
);
2272 draw_sym(dr
, ds
, state
->w
, state
->h
, NEGATIVE
, ink
);
2273 for (which
= POSITIVE
, j
= 0; j
< 2; which
= OPPOSITE(which
), j
++) {
2274 for (i
= 0; i
< w
; i
++) {
2275 target
= state
->common
->colcount
[i
*3+which
];
2276 count
= count_rowcol(state
, i
, COLUMN
, which
);
2277 draw_num_col(dr
, ds
, COLUMN
, which
, i
, paper
, ink
,
2278 state
->common
->colcount
[i
*3+which
]);
2280 for (i
= 0; i
< h
; i
++) {
2281 target
= state
->common
->rowcount
[i
*3+which
];
2282 count
= count_rowcol(state
, i
, ROW
, which
);
2283 draw_num_col(dr
, ds
, ROW
, which
, i
, paper
, ink
,
2284 state
->common
->rowcount
[i
*3+which
]);
2289 for (x
= 0; x
< w
; x
++) {
2290 for (y
= 0; y
< h
; y
++) {
2292 if (state
->common
->dominoes
[i
] == i
+1 ||
2293 state
->common
->dominoes
[i
] == i
+w
) {
2294 int dx
= state
->common
->dominoes
[i
] == i
+1 ? 2 : 1;
2297 int cx
= COORD(x
), cy
= COORD(y
);
2299 print_line_width(dr
, 0);
2301 /* Ink the domino */
2302 for (yy
= 0; yy
< 2; yy
++)
2303 for (xx
= 0; xx
< 2; xx
++)
2305 cx
+xx
*dx
*TILE_SIZE
+(1-2*xx
)*3*TILE_SIZE
/16,
2306 cy
+yy
*dy
*TILE_SIZE
+(1-2*yy
)*3*TILE_SIZE
/16,
2307 TILE_SIZE
/8, ink
, ink
);
2308 draw_rect(dr
, cx
+ TILE_SIZE
/16, cy
+ 3*TILE_SIZE
/16,
2309 dx
*TILE_SIZE
- 2*(TILE_SIZE
/16),
2310 dy
*TILE_SIZE
- 6*(TILE_SIZE
/16), ink
);
2311 draw_rect(dr
, cx
+ 3*TILE_SIZE
/16, cy
+ TILE_SIZE
/16,
2312 dx
*TILE_SIZE
- 6*(TILE_SIZE
/16),
2313 dy
*TILE_SIZE
- 2*(TILE_SIZE
/16), ink
);
2315 /* Un-ink the domino interior */
2316 for (yy
= 0; yy
< 2; yy
++)
2317 for (xx
= 0; xx
< 2; xx
++)
2319 cx
+xx
*dx
*TILE_SIZE
+(1-2*xx
)*3*TILE_SIZE
/16,
2320 cy
+yy
*dy
*TILE_SIZE
+(1-2*yy
)*3*TILE_SIZE
/16,
2321 3*TILE_SIZE
/32, paper
, paper
);
2322 draw_rect(dr
, cx
+ 3*TILE_SIZE
/32, cy
+ 3*TILE_SIZE
/16,
2323 dx
*TILE_SIZE
- 2*(3*TILE_SIZE
/32),
2324 dy
*TILE_SIZE
- 6*(TILE_SIZE
/16), paper
);
2325 draw_rect(dr
, cx
+ 3*TILE_SIZE
/16, cy
+ 3*TILE_SIZE
/32,
2326 dx
*TILE_SIZE
- 6*(TILE_SIZE
/16),
2327 dy
*TILE_SIZE
- 2*(3*TILE_SIZE
/32), paper
);
2332 /* Grid symbols (solution). */
2333 for (x
= 0; x
< w
; x
++) {
2334 for (y
= 0; y
< h
; y
++) {
2336 if ((state
->grid
[i
] != NEUTRAL
) || (state
->flags
[i
] & GS_SET
))
2337 draw_sym(dr
, ds
, x
, y
, state
->grid
[i
], ink
);
2343 #define thegame magnets
2346 const struct game thegame
= {
2347 "Magnets", "games.magnets", "magnets",
2354 TRUE
, game_configure
, custom_params
,
2362 TRUE
, game_can_format_as_text_now
, game_text_format
,
2370 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
2373 game_free_drawstate
,
2377 TRUE
, FALSE
, game_print_size
, game_print
,
2378 FALSE
, /* wants_statusbar */
2379 FALSE
, game_timing_state
,
2380 REQUIRE_RBUTTON
, /* flags */
2383 #ifdef STANDALONE_SOLVER
2388 const char *quis
= NULL
;
2391 void usage(FILE *out
) {
2392 fprintf(out
, "usage: %s [-v] [--print] <params>|<game id>\n", quis
);
2395 void doprint(game_state
*state
)
2397 char *fmt
= game_text_format(state
);
2402 static void pnum(int n
, int ntot
, const char *desc
)
2404 printf("%2.1f%% (%d) %s", (double)n
*100.0 / (double)ntot
, n
, desc
);
2407 static void start_soak(game_params
*p
, random_state
*rs
)
2409 time_t tt_start
, tt_now
, tt_last
;
2412 int n
= 0, nsolved
= 0, nimpossible
= 0, ntricky
= 0, ret
, i
;
2413 long nn
, nn_total
= 0, nn_solved
= 0, nn_tricky
= 0;
2415 tt_start
= tt_now
= time(NULL
);
2418 printf("time, w, h, #generated, #solved, #tricky, #impossible, "
2419 "#neutral, #neutral/solved, #neutral/tricky\n");
2421 printf("Soak-testing a %dx%d grid.\n", p
->w
, p
->h
);
2423 s
= new_state(p
->w
, p
->h
);
2424 aux
= snewn(s
->wh
+1, char);
2430 for (i
= 0; i
< s
->wh
; i
++) {
2431 if (s
->grid
[i
] == NEUTRAL
) nn
++;
2434 generate_aux(s
, aux
);
2435 memset(s
->grid
, EMPTY
, s
->wh
* sizeof(int));
2438 ret
= solve_state(s
, DIFFCOUNT
);
2445 if (solve_state(s2
, DIFF_EASY
) <= 0) {
2449 } else if (ret
< 0) {
2450 char *desc
= generate_desc(s
);
2451 solve_from_aux(s
, aux
);
2452 printf("Game considered impossible:\n %dx%d:%s\n",
2461 tt_last
= time(NULL
);
2462 if (tt_last
> tt_now
) {
2465 printf("%d,%d,%d, %d,%d,%d,%d, %ld,%ld,%ld\n",
2466 (int)(tt_now
- tt_start
), p
->w
, p
->h
,
2467 n
, nsolved
, ntricky
, nimpossible
,
2468 nn_total
, nn_solved
, nn_tricky
);
2470 printf("%d total, %3.1f/s, ",
2471 n
, (double)n
/ ((double)tt_now
- tt_start
));
2472 pnum(nsolved
, n
, "solved"); printf(", ");
2473 pnum(ntricky
, n
, "tricky");
2474 if (nimpossible
> 0)
2475 pnum(nimpossible
, n
, "impossible");
2478 printf(" overall %3.1f%% neutral (%3.1f%% for solved, %3.1f%% for tricky)\n",
2479 (double)(nn_total
* 100) / (double)(p
->w
* p
->h
* n
),
2480 (double)(nn_solved
* 100) / (double)(p
->w
* p
->h
* nsolved
),
2481 (double)(nn_tricky
* 100) / (double)(p
->w
* p
->h
* ntricky
));
2489 int main(int argc
, const char *argv
[])
2491 int print
= 0, soak
= 0, solved
= 0, ret
;
2492 char *id
= NULL
, *desc
, *desc_gen
= NULL
, *err
, *aux
= NULL
;
2493 game_state
*s
= NULL
;
2494 game_params
*p
= NULL
;
2495 random_state
*rs
= NULL
;
2496 time_t seed
= time(NULL
);
2498 setvbuf(stdout
, NULL
, _IONBF
, 0);
2501 while (--argc
> 0) {
2502 char *p
= (char*)(*++argv
);
2503 if (!strcmp(p
, "-v") || !strcmp(p
, "--verbose")) {
2505 } else if (!strcmp(p
, "--csv")) {
2507 } else if (!strcmp(p
, "-e") || !strcmp(p
, "--seed")) {
2508 seed
= atoi(*++argv
);
2510 } else if (!strcmp(p
, "-p") || !strcmp(p
, "--print")) {
2512 } else if (!strcmp(p
, "-s") || !strcmp(p
, "--soak")) {
2514 } else if (*p
== '-') {
2515 fprintf(stderr
, "%s: unrecognised option `%s'\n", argv
[0], p
);
2523 rs
= random_new((void*)&seed
, sizeof(time_t));
2526 fprintf(stderr
, "usage: %s [-v] [--soak] <params> | <game_id>\n", argv
[0]);
2529 desc
= strchr(id
, ':');
2530 if (desc
) *desc
++ = '\0';
2532 p
= default_params();
2533 decode_params(p
, id
);
2534 err
= validate_params(p
, 1);
2536 fprintf(stderr
, "%s: %s", argv
[0], err
);
2542 fprintf(stderr
, "%s: --soak needs parameters, not description.\n", quis
);
2550 desc
= desc_gen
= new_game_desc(p
, rs
, &aux
, 0);
2552 err
= validate_desc(p
, desc
);
2554 fprintf(stderr
, "%s: %s\nDescription: %s\n", quis
, err
, desc
);
2557 s
= new_game(NULL
, p
, desc
);
2558 printf("%s:%s (seed %ld)\n", id
, desc
, seed
);
2560 /* We just generated this ourself. */
2561 if (verbose
|| print
) {
2563 solve_from_aux(s
, aux
);
2569 ret
= solve_state(s
, DIFFCOUNT
);
2570 if (ret
< 0) printf("Puzzle is impossible.\n");
2571 else if (ret
== 0) printf("Puzzle is ambiguous.\n");
2572 else printf("Puzzle was solved.\n");
2576 if (solved
) doprint(s
);
2579 if (desc_gen
) sfree(desc_gen
);
2580 if (p
) free_params(p
);
2581 if (s
) free_game(s
);
2582 if (rs
) random_free(rs
);
2583 if (aux
) sfree(aux
);
2590 /* vim: set shiftwidth=4 tabstop=8: */