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 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 if (solve_set(state
, i
, which
, "forced by flags", NULL
) < 0)
1073 static int solve_neither(game_state
*state
)
1075 int i
, j
, didsth
= 0;
1077 for (i
= 0; i
< state
->wh
; i
++) {
1078 if (state
->flags
[i
] & GS_SET
) continue;
1079 j
= state
->common
->dominoes
[i
];
1080 if (i
== j
) continue;
1082 if (((state
->flags
[i
] & GS_NOTPOSITIVE
) &&
1083 (state
->flags
[j
] & GS_NOTPOSITIVE
)) ||
1084 ((state
->flags
[i
] & GS_NOTNEGATIVE
) &&
1085 (state
->flags
[j
] & GS_NOTNEGATIVE
))) {
1086 if (solve_set(state
, i
, NEUTRAL
, "neither tile magnet", NULL
) < 0)
1094 static int solve_advancedfull(game_state
*state
, rowcol rc
, int *counts
)
1096 int i
, j
, nfound
= 0, clearpos
= 0, clearneg
= 0, ret
= 0;
1098 /* For this row/col, look for a domino entirely within the row where
1099 * both ends can only be + or - (but isn't held).
1100 * The +/- counts can thus be decremented by 1 each, and the 'unset'
1103 * Once that's done for all such dominoes (and they're marked), try
1104 * and made usual deductions about rest of the row based on new totals. */
1106 if (rc
.targets
[POSITIVE
] == -1 && rc
.targets
[NEGATIVE
] == -1)
1107 return 0; /* don't have a target for either colour, nothing to do. */
1108 if ((rc
.targets
[POSITIVE
] >= 0 && counts
[POSITIVE
] == rc
.targets
[POSITIVE
]) &&
1109 (rc
.targets
[NEGATIVE
] >= 0 && counts
[NEGATIVE
] == rc
.targets
[NEGATIVE
]))
1110 return 0; /* both colours are full up already, nothing to do. */
1112 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++)
1113 state
->flags
[i
] &= ~GS_MARK
;
1115 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1116 if (state
->flags
[i
] & GS_SET
) continue;
1118 /* We're looking for a domino in our row/col, thus if
1119 * dominoes[i] -> i+di we've found one. */
1120 if (state
->common
->dominoes
[i
] != i
+rc
.di
) continue;
1122 /* We need both squares of this domino to be either + or -
1123 * (i.e. both NOTNEUTRAL only). */
1124 if (((state
->flags
[i
] & GS_NOTMASK
) != GS_NOTNEUTRAL
) ||
1125 ((state
->flags
[i
+rc
.di
] & GS_NOTMASK
) != GS_NOTNEUTRAL
))
1128 debug(("Domino in %s %d at (%d,%d) must be polarised.",
1129 rc
.name
, rc
.num
, i
%state
->w
, i
/state
->w
));
1130 state
->flags
[i
] |= GS_MARK
;
1131 state
->flags
[i
+rc
.di
] |= GS_MARK
;
1134 if (nfound
== 0) return 0;
1136 /* nfound is #dominoes we matched, which will all be marked. */
1137 counts
[POSITIVE
] += nfound
;
1138 counts
[NEGATIVE
] += nfound
;
1140 if (rc
.targets
[POSITIVE
] >= 0 && counts
[POSITIVE
] == rc
.targets
[POSITIVE
]) {
1141 debug(("%s %d has now filled POSITIVE:", rc
.name
, rc
.num
));
1144 if (rc
.targets
[NEGATIVE
] >= 0 && counts
[NEGATIVE
] == rc
.targets
[NEGATIVE
]) {
1145 debug(("%s %d has now filled NEGATIVE:", rc
.name
, rc
.num
));
1149 if (!clearpos
&& !clearneg
) return 0;
1151 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1152 if (state
->flags
[i
] & GS_SET
) continue;
1153 if (state
->flags
[i
] & GS_MARK
) continue;
1155 if (clearpos
&& !(state
->flags
[i
] & GS_NOTPOSITIVE
)) {
1156 if (solve_unflag(state
, i
, POSITIVE
, "row/col full (+ve) [tricky]", &rc
) < 0)
1160 if (clearneg
&& !(state
->flags
[i
] & GS_NOTNEGATIVE
)) {
1161 if (solve_unflag(state
, i
, NEGATIVE
, "row/col full (-ve) [tricky]", &rc
) < 0)
1170 /* If we only have one neutral still to place on a row/column then no
1171 dominoes entirely in that row/column can be neutral. */
1172 static int solve_nonneutral(game_state
*state
, rowcol rc
, int *counts
)
1176 if (rc
.targets
[NEUTRAL
] != counts
[NEUTRAL
]+1)
1179 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1180 if (state
->flags
[i
] & GS_SET
) continue;
1181 if (state
->common
->dominoes
[i
] != i
+rc
.di
) continue;
1183 if (!(state
->flags
[i
] & GS_NOTNEUTRAL
)) {
1184 if (solve_unflag(state
, i
, NEUTRAL
, "single neutral in row/col [tricky]", &rc
) < 0)
1192 /* If we need to fill all unfilled cells with +-, and we need 1 more of
1193 * one than the other, and we have a single odd-numbered region of unfilled
1194 * cells, that odd-numbered region must start and end with the extra number. */
1195 static int solve_oddlength(game_state
*state
, rowcol rc
, int *counts
)
1197 int i
, j
, ret
= 0, extra
, tpos
, tneg
;
1198 int start
= -1, length
= 0, inempty
= 0, startodd
= -1;
1200 /* need zero neutral cells still to find... */
1201 if (rc
.targets
[NEUTRAL
] != counts
[NEUTRAL
])
1204 /* ...and #positive and #negative to differ by one. */
1205 tpos
= rc
.targets
[POSITIVE
] - counts
[POSITIVE
];
1206 tneg
= rc
.targets
[NEGATIVE
] - counts
[NEGATIVE
];
1209 else if (tneg
== tpos
+1)
1213 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1214 if (state
->flags
[i
] & GS_SET
) {
1217 /* we've just finished an odd-length section. */
1218 if (startodd
!= -1) goto twoodd
;
1233 if (inempty
&& (length
% 2)) {
1234 if (startodd
!= -1) goto twoodd
;
1238 ret
= solve_set(state
, startodd
, extra
, "odd-length section start", &rc
);
1243 debug(("%s %d has >1 odd-length sections, starting at %d,%d and %d,%d.",
1245 startodd
%state
->w
, startodd
/state
->w
,
1246 start
%state
->w
, start
/state
->w
));
1250 /* Count the number of remaining empty dominoes in any row/col.
1251 * If that number is equal to the #remaining positive,
1252 * or to the #remaining negative, no empty cells can be neutral. */
1253 static int solve_countdominoes_neutral(game_state
*state
, rowcol rc
, int *counts
)
1255 int i
, j
, ndom
= 0, nonn
= 0, ret
= 0;
1257 if ((rc
.targets
[POSITIVE
] == -1) && (rc
.targets
[NEGATIVE
] == -1))
1258 return 0; /* need at least one target to compare. */
1260 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1261 if (state
->flags
[i
] & GS_SET
) continue;
1262 assert(state
->grid
[i
] == EMPTY
);
1264 /* Skip solo cells, or second cell in domino. */
1265 if ((state
->common
->dominoes
[i
] == i
) ||
1266 (state
->common
->dominoes
[i
] == i
-rc
.di
))
1272 if ((rc
.targets
[POSITIVE
] != -1) &&
1273 (rc
.targets
[POSITIVE
]-counts
[POSITIVE
] == ndom
))
1275 if ((rc
.targets
[NEGATIVE
] != -1) &&
1276 (rc
.targets
[NEGATIVE
]-counts
[NEGATIVE
] == ndom
))
1279 if (!nonn
) return 0;
1281 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1282 if (state
->flags
[i
] & GS_SET
) continue;
1284 if (!(state
->flags
[i
] & GS_NOTNEUTRAL
)) {
1285 if (solve_unflag(state
, i
, NEUTRAL
, "all dominoes +/- [tricky]", &rc
) < 0)
1293 static int solve_domino_count(game_state
*state
, rowcol rc
, int i
, int which
)
1297 /* Skip solo cells or 2nd in domino. */
1298 if ((state
->common
->dominoes
[i
] == i
) ||
1299 (state
->common
->dominoes
[i
] == i
-rc
.di
))
1302 if (state
->flags
[i
] & GS_SET
)
1305 if (POSSIBLE(i
, which
))
1308 if (state
->common
->dominoes
[i
] == i
+rc
.di
) {
1309 /* second cell of domino is on our row: test that too. */
1310 if (POSSIBLE(i
+rc
.di
, which
))
1316 /* Count number of dominoes we could put each of + and - into. If it is equal
1317 * to the #left, any domino we can only put + or - in one cell of must have it. */
1318 static int solve_countdominoes_nonneutral(game_state
*state
, rowcol rc
, int *counts
)
1320 int which
, w
, i
, j
, ndom
= 0, didsth
= 0, toset
;
1322 for (which
= POSITIVE
, w
= 0; w
< 2; which
= OPPOSITE(which
), w
++) {
1323 if (rc
.targets
[which
] == -1) continue;
1325 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1326 if (solve_domino_count(state
, rc
, i
, which
) > 0)
1330 if ((rc
.targets
[which
] - counts
[which
]) != ndom
)
1333 for (i
= rc
.i
, j
= 0; j
< rc
.n
; i
+= rc
.di
, j
++) {
1334 if (solve_domino_count(state
, rc
, i
, which
) == 1) {
1335 if (POSSIBLE(i
, which
))
1338 /* paranoia, should have been checked by solve_domino_count. */
1339 assert(state
->common
->dominoes
[i
] == i
+rc
.di
);
1340 assert(POSSIBLE(i
+rc
.di
, which
));
1343 if (solve_set(state
, toset
, which
, "all empty dominoes need +/- [tricky]", &rc
) < 0)
1352 /* danger, evil macro. can't use the do { ... } while(0) trick because
1353 * the continue breaks. */
1354 #define SOLVE_FOR_ROWCOLS(fn) \
1355 ret = solve_rowcols(state, fn); \
1356 if (ret < 0) { debug(("%s said impossible, cannot solve", #fn)); return -1; } \
1357 if (ret > 0) continue
1359 static int solve_state(game_state
*state
, int diff
)
1363 debug(("solve_state, difficulty %s", magnets_diffnames
[diff
]));
1365 solve_clearflags(state
);
1366 if (solve_startflags(state
) < 0) return -1;
1369 ret
= solve_force(state
);
1370 if (ret
> 0) continue;
1371 if (ret
< 0) return -1;
1373 ret
= solve_neither(state
);
1374 if (ret
> 0) continue;
1375 if (ret
< 0) return -1;
1377 SOLVE_FOR_ROWCOLS(solve_checkfull
);
1378 SOLVE_FOR_ROWCOLS(solve_oddlength
);
1380 if (diff
< DIFF_TRICKY
) break;
1382 SOLVE_FOR_ROWCOLS(solve_advancedfull
);
1383 SOLVE_FOR_ROWCOLS(solve_nonneutral
);
1384 SOLVE_FOR_ROWCOLS(solve_countdominoes_neutral
);
1385 SOLVE_FOR_ROWCOLS(solve_countdominoes_nonneutral
);
1391 return check_completion(state
);
1395 static char *game_state_diff(game_state
*src
, game_state
*dst
, int issolve
)
1397 char *ret
= NULL
, buf
[80], c
;
1398 int retlen
= 0, x
, y
, i
, k
;
1400 assert(src
->w
== dst
->w
&& src
->h
== dst
->h
);
1403 ret
= sresize(ret
, 3, char);
1404 ret
[0] = 'S'; ret
[1] = ';'; ret
[2] = '\0';
1407 for (x
= 0; x
< dst
->w
; x
++) {
1408 for (y
= 0; y
< dst
->h
; y
++) {
1411 if (src
->common
->dominoes
[i
] == i
) continue;
1413 #define APPEND do { \
1414 ret = sresize(ret, retlen + k + 1, char); \
1415 strcpy(ret + retlen, buf); \
1419 if ((src
->grid
[i
] != dst
->grid
[i
]) ||
1420 ((src
->flags
[i
] & GS_SET
) != (dst
->flags
[i
] & GS_SET
))) {
1421 if (dst
->grid
[i
] == EMPTY
&& !(dst
->flags
[i
] & GS_SET
))
1424 c
= GRID2CHAR(dst
->grid
[i
]);
1425 k
= sprintf(buf
, "%c%d,%d;", (int)c
, x
, y
);
1430 debug(("game_state_diff returns %s", ret
));
1434 static void solve_from_aux(game_state
*state
, char *aux
)
1437 assert(strlen(aux
) == state
->wh
);
1438 for (i
= 0; i
< state
->wh
; i
++) {
1439 state
->grid
[i
] = CHAR2GRID(aux
[i
]);
1440 state
->flags
[i
] |= GS_SET
;
1444 static char *solve_game(game_state
*state
, game_state
*currstate
,
1445 char *aux
, char **error
)
1447 game_state
*solved
= dup_game(currstate
);
1451 if (aux
&& strlen(aux
) == state
->wh
) {
1452 solve_from_aux(solved
, aux
);
1456 if (solve_state(solved
, DIFFCOUNT
) > 0) goto solved
;
1459 solved
= dup_game(state
);
1460 ret
= solve_state(solved
, DIFFCOUNT
);
1461 if (ret
> 0) goto solved
;
1464 *error
= (ret
< 0) ? "Puzzle is impossible." : "Unable to solve puzzle.";
1468 move
= game_state_diff(currstate
, solved
, 1);
1473 static int solve_unnumbered(game_state
*state
)
1477 ret
= solve_force(state
);
1478 if (ret
> 0) continue;
1479 if (ret
< 0) return -1;
1481 ret
= solve_neither(state
);
1482 if (ret
> 0) continue;
1483 if (ret
< 0) return -1;
1487 for (i
= 0; i
< state
->wh
; i
++) {
1488 if (!(state
->flags
[i
] & GS_SET
)) return 0;
1493 static int lay_dominoes(game_state
*state
, random_state
*rs
, int *scratch
)
1495 int n
, i
, ret
= 0, nlaid
= 0, n_initial_neutral
;
1497 for (i
= 0; i
< state
->wh
; i
++) {
1499 state
->grid
[i
] = EMPTY
;
1500 state
->flags
[i
] = (state
->common
->dominoes
[i
] == i
) ? GS_SET
: 0;
1502 shuffle(scratch
, state
->wh
, sizeof(int), rs
);
1504 n_initial_neutral
= (state
->wh
> 100) ? 5 : (state
->wh
/ 10);
1506 for (n
= 0; n
< state
->wh
; n
++) {
1507 /* Find a space ... */
1510 if (state
->flags
[i
] & GS_SET
) continue; /* already laid here. */
1512 /* ...and lay a domino if we can. */
1514 debug(("Laying domino at i:%d, (%d,%d)\n", i
, i
%state
->w
, i
/state
->w
));
1516 /* The choice of which type of domino to lay here leads to subtle differences
1517 * in the sorts of boards that get produced. Too much bias towards magnets
1518 * leads to games that are too easy.
1520 * Currently, it lays a small set of dominoes at random as neutral, and
1521 * then lays the rest preferring to be magnets -- however, if the
1522 * current layout is such that a magnet won't go there, then it lays
1525 * The number of initially neutral dominoes is limited as grids get bigger:
1526 * too many neutral dominoes invariably ends up with insoluble puzzle at
1527 * this size, and the positioning process means it'll always end up laying
1528 * more than the initial 5 anyway.
1531 /* We should always be able to lay a neutral anywhere. */
1532 assert(!(state
->flags
[i
] & GS_NOTNEUTRAL
));
1534 if (n
< n_initial_neutral
) {
1535 debug((" ...laying neutral\n"));
1536 ret
= solve_set(state
, i
, NEUTRAL
, "layout initial neutral", NULL
);
1538 debug((" ... preferring magnet\n"));
1539 if (!(state
->flags
[i
] & GS_NOTPOSITIVE
))
1540 ret
= solve_set(state
, i
, POSITIVE
, "layout", NULL
);
1541 else if (!(state
->flags
[i
] & GS_NOTNEGATIVE
))
1542 ret
= solve_set(state
, i
, NEGATIVE
, "layout", NULL
);
1544 ret
= solve_set(state
, i
, NEUTRAL
, "layout", NULL
);
1547 debug(("Unable to lay anything at (%d,%d), giving up.",
1548 i
%state
->w
, i
/state
->w
));
1554 ret
= solve_unnumbered(state
);
1556 debug(("solve_unnumbered decided impossible.\n"));
1561 debug(("Laid %d dominoes, total %d dominoes.\n", nlaid
, state
->wh
/2));
1562 game_debug(state
, "Final layout");
1566 static void gen_game(game_state
*new, random_state
*rs
)
1569 int *scratch
= snewn(new->wh
, int);
1571 #ifdef STANDALONE_SOLVER
1572 if (verbose
) printf("Generating new game...\n");
1576 sfree(new->common
->dominoes
); /* bit grotty. */
1577 new->common
->dominoes
= domino_layout(new->w
, new->h
, rs
);
1580 ret
= lay_dominoes(new, rs
, scratch
);
1583 /* for each cell, update colcount/rowcount as appropriate. */
1584 memset(new->common
->colcount
, 0, new->w
*3*sizeof(int));
1585 memset(new->common
->rowcount
, 0, new->h
*3*sizeof(int));
1586 for (x
= 0; x
< new->w
; x
++) {
1587 for (y
= 0; y
< new->h
; y
++) {
1588 val
= new->grid
[y
*new->w
+x
];
1589 new->common
->colcount
[x
*3+val
]++;
1590 new->common
->rowcount
[y
*3+val
]++;
1598 static void generate_aux(game_state
*new, char *aux
)
1601 for (i
= 0; i
< new->wh
; i
++)
1602 aux
[i
] = GRID2CHAR(new->grid
[i
]);
1603 aux
[new->wh
] = '\0';
1606 static int check_difficulty(game_params
*params
, game_state
*new,
1609 int *scratch
, *grid_correct
, slen
, i
;
1611 memset(new->grid
, EMPTY
, new->wh
*sizeof(int));
1613 if (params
->diff
> DIFF_EASY
) {
1614 /* If this is too easy, return. */
1615 if (solve_state(new, params
->diff
-1) > 0) {
1616 debug(("Puzzle is too easy."));
1620 if (solve_state(new, params
->diff
) <= 0) {
1621 debug(("Puzzle is not soluble at requested difficulty."));
1624 if (!params
->stripclues
) return 0;
1626 /* Copy the correct grid away. */
1627 grid_correct
= snewn(new->wh
, int);
1628 memcpy(grid_correct
, new->grid
, new->wh
*sizeof(int));
1630 /* Create shuffled array of side-clue locations. */
1631 slen
= new->w
*2 + new->h
*2;
1632 scratch
= snewn(slen
, int);
1633 for (i
= 0; i
< slen
; i
++) scratch
[i
] = i
;
1634 shuffle(scratch
, slen
, sizeof(int), rs
);
1636 /* For each clue, check whether removing it makes the puzzle unsoluble;
1637 * put it back if so. */
1638 for (i
= 0; i
< slen
; i
++) {
1639 int num
= scratch
[i
], which
, roworcol
, target
, targetn
, ret
;
1642 /* work out which clue we meant. */
1643 if (num
< new->w
+new->h
) { which
= POSITIVE
; }
1644 else { which
= NEGATIVE
; num
-= new->w
+new->h
; }
1646 if (num
< new->w
) { roworcol
= COLUMN
; }
1647 else { roworcol
= ROW
; num
-= new->w
; }
1649 /* num is now the row/column index in question. */
1650 rc
= mkrowcol(new, num
, roworcol
);
1652 /* Remove clue, storing original... */
1653 target
= rc
.targets
[which
];
1654 targetn
= rc
.targets
[NEUTRAL
];
1655 rc
.targets
[which
] = -1;
1656 rc
.targets
[NEUTRAL
] = -1;
1658 /* ...and see if we can still solve it. */
1659 game_debug(new, "removed clue, new board:");
1660 memset(new->grid
, EMPTY
, new->wh
* sizeof(int));
1661 ret
= solve_state(new, params
->diff
);
1665 memcmp(new->grid
, grid_correct
, new->wh
*sizeof(int)) != 0) {
1666 /* We made it ambiguous: put clue back. */
1667 debug(("...now impossible/different, put clue back."));
1668 rc
.targets
[which
] = target
;
1669 rc
.targets
[NEUTRAL
] = targetn
;
1673 sfree(grid_correct
);
1678 static char *new_game_desc(game_params
*params
, random_state
*rs
,
1679 char **aux_r
, int interactive
)
1681 game_state
*new = new_state(params
->w
, params
->h
);
1682 char *desc
, *aux
= snewn(new->wh
+1, char);
1686 generate_aux(new, aux
);
1687 } while (check_difficulty(params
, new, rs
) < 0);
1689 /* now we're complete, generate the description string
1690 * and an aux_info for the completed game. */
1691 desc
= generate_desc(new);
1700 int cur_x
, cur_y
, cur_visible
;
1703 static game_ui
*new_ui(game_state
*state
)
1705 game_ui
*ui
= snew(game_ui
);
1706 ui
->cur_x
= ui
->cur_y
= 0;
1707 ui
->cur_visible
= 0;
1711 static void free_ui(game_ui
*ui
)
1716 static char *encode_ui(game_ui
*ui
)
1721 static void decode_ui(game_ui
*ui
, char *encoding
)
1725 static void game_changed_state(game_ui
*ui
, game_state
*oldstate
,
1726 game_state
*newstate
)
1728 if (!oldstate
->completed
&& newstate
->completed
)
1729 ui
->cur_visible
= 0;
1732 struct game_drawstate
{
1733 int tilesize
, started
, solved
;
1735 unsigned long *what
; /* size w*h */
1736 unsigned long *colwhat
, *rowwhat
; /* size 3*w, 3*h */
1739 #define DS_WHICH_MASK 0xf
1741 #define DS_ERROR 0x10
1742 #define DS_CURSOR 0x20
1744 #define DS_FULL 0x80
1745 #define DS_NOTPOS 0x100
1746 #define DS_NOTNEG 0x200
1747 #define DS_NOTNEU 0x400
1748 #define DS_FLASH 0x800
1750 #define PREFERRED_TILE_SIZE 32
1751 #define TILE_SIZE (ds->tilesize)
1752 #define BORDER (TILE_SIZE / 8)
1754 #define COORD(x) ( (x+1) * TILE_SIZE + BORDER )
1755 #define FROMCOORD(x) ( (x - BORDER) / TILE_SIZE - 1 )
1757 static char *interpret_move(game_state
*state
, game_ui
*ui
, game_drawstate
*ds
,
1758 int x
, int y
, int button
)
1760 int gx
= FROMCOORD(x
), gy
= FROMCOORD(y
), idx
, curr
;
1761 char *nullret
= NULL
, buf
[80], movech
;
1762 enum { CYCLE_MAGNET
, CYCLE_NEUTRAL
} action
;
1764 if (IS_CURSOR_MOVE(button
)) {
1765 move_cursor(button
, &ui
->cur_x
, &ui
->cur_y
, state
->w
, state
->h
, 0);
1766 ui
->cur_visible
= 1;
1768 } else if (IS_CURSOR_SELECT(button
)) {
1769 if (!ui
->cur_visible
) {
1770 ui
->cur_visible
= 1;
1773 action
= (button
== CURSOR_SELECT
) ? CYCLE_MAGNET
: CYCLE_NEUTRAL
;
1776 } else if (INGRID(state
, gx
, gy
) &&
1777 (button
== LEFT_BUTTON
|| button
== RIGHT_BUTTON
)) {
1778 if (ui
->cur_visible
) {
1779 ui
->cur_visible
= 0;
1782 action
= (button
== LEFT_BUTTON
) ? CYCLE_MAGNET
: CYCLE_NEUTRAL
;
1786 idx
= gy
* state
->w
+ gx
;
1787 if (state
->common
->dominoes
[idx
] == idx
) return nullret
;
1788 curr
= state
->grid
[idx
];
1790 if (action
== CYCLE_MAGNET
) {
1791 /* ... empty --> positive --> negative --> empty ... */
1793 if (state
->grid
[idx
] == NEUTRAL
&& state
->flags
[idx
] & GS_SET
)
1794 return nullret
; /* can't cycle a magnet from a neutral. */
1795 movech
= (curr
== EMPTY
) ? '+' : (curr
== POSITIVE
) ? '-' : ' ';
1796 } else if (action
== CYCLE_NEUTRAL
) {
1797 /* ... empty -> neutral -> !neutral --> empty ... */
1799 if (state
->grid
[idx
] != NEUTRAL
)
1800 return nullret
; /* can't cycle through neutral from a magnet. */
1802 /* All of these are grid == EMPTY == NEUTRAL; it twiddles
1803 * combinations of flags. */
1804 if (state
->flags
[idx
] & GS_SET
) /* neutral */
1806 else if (state
->flags
[idx
] & GS_NOTNEUTRAL
) /* !neutral */
1811 assert(!"unknown action");
1812 movech
= 0; /* placate optimiser */
1815 sprintf(buf
, "%c%d,%d", movech
, gx
, gy
);
1820 static game_state
*execute_move(game_state
*state
, char *move
)
1822 game_state
*ret
= dup_game(state
);
1823 int x
, y
, n
, idx
, idx2
;
1826 if (!*move
) goto badmove
;
1832 } else if (c
== '+' || c
== '-' ||
1833 c
== '.' || c
== ' ' || c
== '?') {
1834 if ((sscanf(move
, "%d,%d%n", &x
, &y
, &n
) != 2) ||
1835 !INGRID(state
, x
, y
)) goto badmove
;
1837 idx
= y
*state
->w
+ x
;
1838 idx2
= state
->common
->dominoes
[idx
];
1839 if (idx
== idx2
) goto badmove
;
1841 ret
->flags
[idx
] &= ~GS_NOTMASK
;
1842 ret
->flags
[idx2
] &= ~GS_NOTMASK
;
1844 if (c
== ' ' || c
== '?') {
1845 ret
->grid
[idx
] = EMPTY
;
1846 ret
->grid
[idx2
] = EMPTY
;
1847 ret
->flags
[idx
] &= ~GS_SET
;
1848 ret
->flags
[idx2
] &= ~GS_SET
;
1850 ret
->flags
[idx
] |= GS_NOTNEUTRAL
;
1851 ret
->flags
[idx2
] |= GS_NOTNEUTRAL
;
1854 ret
->grid
[idx
] = CHAR2GRID(c
);
1855 ret
->grid
[idx2
] = OPPOSITE(CHAR2GRID(c
));
1856 ret
->flags
[idx
] |= GS_SET
;
1857 ret
->flags
[idx2
] |= GS_SET
;
1863 if (*move
== ';') move
++;
1864 else if (*move
) goto badmove
;
1866 if (check_completion(ret
) == 1)
1876 /* ----------------------------------------------------------------------
1880 static void game_compute_size(game_params
*params
, int tilesize
,
1883 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
1884 struct { int tilesize
; } ads
, *ds
= &ads
;
1885 ads
.tilesize
= tilesize
;
1887 *x
= TILE_SIZE
* (params
->w
+2) + 2 * BORDER
;
1888 *y
= TILE_SIZE
* (params
->h
+2) + 2 * BORDER
;
1891 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
1892 game_params
*params
, int tilesize
)
1894 ds
->tilesize
= tilesize
;
1897 static float *game_colours(frontend
*fe
, int *ncolours
)
1899 float *ret
= snewn(3 * NCOLOURS
, float);
1902 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
1904 for (i
= 0; i
< 3; i
++) {
1905 ret
[COL_TEXT
* 3 + i
] = 0.0F
;
1906 ret
[COL_NEGATIVE
* 3 + i
] = 0.0F
;
1907 ret
[COL_CURSOR
* 3 + i
] = 0.9F
;
1910 ret
[COL_POSITIVE
* 3 + 0] = 0.8F
;
1911 ret
[COL_POSITIVE
* 3 + 1] = 0.0F
;
1912 ret
[COL_POSITIVE
* 3 + 2] = 0.0F
;
1914 ret
[COL_NEUTRAL
* 3 + 0] = 0.10F
;
1915 ret
[COL_NEUTRAL
* 3 + 1] = 0.60F
;
1916 ret
[COL_NEUTRAL
* 3 + 2] = 0.10F
;
1918 ret
[COL_ERROR
* 3 + 0] = 1.0F
;
1919 ret
[COL_ERROR
* 3 + 1] = 0.0F
;
1920 ret
[COL_ERROR
* 3 + 2] = 0.0F
;
1922 ret
[COL_NOT
* 3 + 0] = 0.2F
;
1923 ret
[COL_NOT
* 3 + 1] = 0.2F
;
1924 ret
[COL_NOT
* 3 + 2] = 1.0F
;
1926 *ncolours
= NCOLOURS
;
1930 static game_drawstate
*game_new_drawstate(drawing
*dr
, game_state
*state
)
1932 struct game_drawstate
*ds
= snew(struct game_drawstate
);
1934 ds
->tilesize
= ds
->started
= ds
->solved
= 0;
1938 ds
->what
= snewn(state
->wh
, unsigned long);
1939 memset(ds
->what
, 0, state
->wh
*sizeof(unsigned long));
1941 ds
->colwhat
= snewn(state
->w
*3, unsigned long);
1942 memset(ds
->colwhat
, 0, state
->w
*3*sizeof(unsigned long));
1943 ds
->rowwhat
= snewn(state
->h
*3, unsigned long);
1944 memset(ds
->rowwhat
, 0, state
->h
*3*sizeof(unsigned long));
1949 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
1957 static void draw_num_col(drawing
*dr
, game_drawstate
*ds
, int rowcol
, int which
,
1958 int idx
, int colbg
, int col
, int num
)
1963 if (num
< 0) return;
1965 sprintf(buf
, "%d", num
);
1966 tsz
= (strlen(buf
) == 1) ? (7*TILE_SIZE
/10) : (9*TILE_SIZE
/10)/strlen(buf
);
1968 if (rowcol
== ROW
) {
1970 if (which
== NEGATIVE
) cx
+= TILE_SIZE
* (ds
->w
+1);
1971 cy
= BORDER
+ TILE_SIZE
* (idx
+1);
1973 cx
= BORDER
+ TILE_SIZE
* (idx
+1);
1975 if (which
== NEGATIVE
) cy
+= TILE_SIZE
* (ds
->h
+1);
1978 draw_rect(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
, colbg
);
1979 draw_text(dr
, cx
+ TILE_SIZE
/2, cy
+ TILE_SIZE
/2, FONT_VARIABLE
, tsz
,
1980 ALIGN_VCENTRE
| ALIGN_HCENTRE
, col
, buf
);
1982 draw_update(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
);
1985 static void draw_num(drawing
*dr
, game_drawstate
*ds
, int rowcol
, int which
,
1986 int idx
, unsigned long c
, int num
)
1988 draw_num_col(dr
, ds
, rowcol
, which
, idx
, COL_BACKGROUND
,
1989 (c
& DS_ERROR
) ? COL_ERROR
: COL_TEXT
, num
);
1992 static void draw_sym(drawing
*dr
, game_drawstate
*ds
, int x
, int y
, int which
, int col
)
1994 int cx
= COORD(x
), cy
= COORD(y
);
1995 int ccx
= cx
+ TILE_SIZE
/2, ccy
= cy
+ TILE_SIZE
/2;
1996 int roff
= TILE_SIZE
/4, rsz
= 2*roff
+1;
1997 int soff
= TILE_SIZE
/16, ssz
= 2*soff
+1;
1999 if (which
== POSITIVE
|| which
== NEGATIVE
) {
2000 draw_rect(dr
, ccx
- roff
, ccy
- soff
, rsz
, ssz
, col
);
2001 if (which
== POSITIVE
)
2002 draw_rect(dr
, ccx
- soff
, ccy
- roff
, ssz
, rsz
, col
);
2003 } else if (col
== COL_NOT
) {
2004 /* not-a-neutral is a blue question mark. */
2005 char qu
[2] = { '?', 0 };
2006 draw_text(dr
, ccx
, ccy
, FONT_VARIABLE
, 7*TILE_SIZE
/10,
2007 ALIGN_VCENTRE
| ALIGN_HCENTRE
, col
, qu
);
2009 draw_line(dr
, ccx
- roff
, ccy
- roff
, ccx
+ roff
, ccy
+ roff
, col
);
2010 draw_line(dr
, ccx
+ roff
, ccy
- roff
, ccx
- roff
, ccy
+ roff
, col
);
2022 /* NOT responsible for redrawing background or updating. */
2023 static void draw_tile_col(drawing
*dr
, game_drawstate
*ds
, int *dominoes
,
2024 int x
, int y
, int which
, int bg
, int fg
, int perc
)
2026 int cx
= COORD(x
), cy
= COORD(y
), i
, other
, type
= TYPE_BLANK
;
2027 int gutter
, radius
, coffset
;
2029 /* gutter is TSZ/16 for 100%, 8*TSZ/16 (TSZ/2) for 0% */
2030 gutter
= (TILE_SIZE
/ 16) + ((100 - perc
) * (7*TILE_SIZE
/ 16))/100;
2031 radius
= (perc
* (TILE_SIZE
/ 8)) / 100;
2032 coffset
= gutter
+ radius
;
2035 other
= dominoes
[i
];
2037 if (other
== i
) return;
2038 else if (other
== i
+1) type
= TYPE_L
;
2039 else if (other
== i
-1) type
= TYPE_R
;
2040 else if (other
== i
+ds
->w
) type
= TYPE_T
;
2041 else if (other
== i
-ds
->w
) type
= TYPE_B
;
2042 else assert(!"mad domino orientation");
2044 /* domino drawing shamelessly stolen from dominosa.c. */
2045 if (type
== TYPE_L
|| type
== TYPE_T
)
2046 draw_circle(dr
, cx
+coffset
, cy
+coffset
,
2048 if (type
== TYPE_R
|| type
== TYPE_T
)
2049 draw_circle(dr
, cx
+TILE_SIZE
-1-coffset
, cy
+coffset
,
2051 if (type
== TYPE_L
|| type
== TYPE_B
)
2052 draw_circle(dr
, cx
+coffset
, cy
+TILE_SIZE
-1-coffset
,
2054 if (type
== TYPE_R
|| type
== TYPE_B
)
2055 draw_circle(dr
, cx
+TILE_SIZE
-1-coffset
,
2056 cy
+TILE_SIZE
-1-coffset
,
2059 for (i
= 0; i
< 2; i
++) {
2062 x1
= cx
+ (i
? gutter
: coffset
);
2063 y1
= cy
+ (i
? coffset
: gutter
);
2064 x2
= cx
+ TILE_SIZE
-1 - (i
? gutter
: coffset
);
2065 y2
= cy
+ TILE_SIZE
-1 - (i
? coffset
: gutter
);
2067 x2
= cx
+ TILE_SIZE
;
2068 else if (type
== TYPE_R
)
2070 else if (type
== TYPE_T
)
2071 y2
= cy
+ TILE_SIZE
;
2072 else if (type
== TYPE_B
)
2075 draw_rect(dr
, x1
, y1
, x2
-x1
+1, y2
-y1
+1, bg
);
2078 if (fg
!= -1) draw_sym(dr
, ds
, x
, y
, which
, fg
);
2081 static void draw_tile(drawing
*dr
, game_drawstate
*ds
, int *dominoes
,
2082 int x
, int y
, unsigned long flags
)
2084 int cx
= COORD(x
), cy
= COORD(y
), bg
, fg
, perc
= 100;
2085 int which
= flags
& DS_WHICH_MASK
;
2087 flags
&= ~DS_WHICH_MASK
;
2089 draw_rect(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
, COL_BACKGROUND
);
2091 if (flags
& DS_CURSOR
)
2092 bg
= COL_CURSOR
; /* off-white white for cursor */
2093 else if (which
== POSITIVE
)
2095 else if (which
== NEGATIVE
)
2097 else if (flags
& DS_SET
)
2098 bg
= COL_NEUTRAL
; /* green inner for neutral cells */
2100 bg
= COL_LOWLIGHT
; /* light grey for empty cells. */
2102 if (which
== EMPTY
&& !(flags
& DS_SET
)) {
2104 fg
= -1; /* don't draw cross unless actually set as neutral. */
2106 if (flags
& DS_NOTPOS
) notwhich
= POSITIVE
;
2107 if (flags
& DS_NOTNEG
) notwhich
= NEGATIVE
;
2108 if (flags
& DS_NOTNEU
) notwhich
= NEUTRAL
;
2109 if (notwhich
!= -1) {
2114 fg
= (flags
& DS_ERROR
) ? COL_ERROR
:
2115 (flags
& DS_CURSOR
) ? COL_TEXT
: COL_BACKGROUND
;
2117 draw_rect(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
, COL_BACKGROUND
);
2119 if (flags
& DS_FLASH
) {
2120 int bordercol
= COL_HIGHLIGHT
;
2121 draw_tile_col(dr
, ds
, dominoes
, x
, y
, which
, bordercol
, -1, perc
);
2124 draw_tile_col(dr
, ds
, dominoes
, x
, y
, which
, bg
, fg
, perc
);
2126 draw_update(dr
, cx
, cy
, TILE_SIZE
, TILE_SIZE
);
2130 static void game_redraw(drawing
*dr
, game_drawstate
*ds
, game_state
*oldstate
,
2131 game_state
*state
, int dir
, game_ui
*ui
,
2132 float animtime
, float flashtime
)
2134 int x
, y
, w
= state
->w
, h
= state
->h
, which
, i
, j
, flash
;
2135 unsigned long c
= 0;
2137 flash
= (int)(flashtime
* 5 / FLASH_TIME
) % 2;
2140 /* draw background, corner +-. */
2142 TILE_SIZE
* (w
+2) + 2 * BORDER
,
2143 TILE_SIZE
* (h
+2) + 2 * BORDER
,
2146 draw_sym(dr
, ds
, -1, -1, POSITIVE
, COL_TEXT
);
2147 draw_sym(dr
, ds
, state
->w
, state
->h
, NEGATIVE
, COL_TEXT
);
2149 draw_update(dr
, 0, 0,
2150 TILE_SIZE
* (ds
->w
+2) + 2 * BORDER
,
2151 TILE_SIZE
* (ds
->h
+2) + 2 * BORDER
);
2155 for (y
= 0; y
< h
; y
++) {
2156 for (x
= 0; x
< w
; x
++) {
2159 c
= state
->grid
[idx
];
2161 if (state
->flags
[idx
] & GS_ERROR
)
2163 if (state
->flags
[idx
] & GS_SET
)
2166 if (x
== ui
->cur_x
&& y
== ui
->cur_y
&& ui
->cur_visible
)
2172 if (state
->flags
[idx
] & GS_NOTPOSITIVE
)
2174 if (state
->flags
[idx
] & GS_NOTNEGATIVE
)
2176 if (state
->flags
[idx
] & GS_NOTNEUTRAL
)
2179 if (ds
->what
[idx
] != c
|| !ds
->started
) {
2180 draw_tile(dr
, ds
, state
->common
->dominoes
, x
, y
, c
);
2185 /* Draw counts around side */
2186 for (which
= POSITIVE
, j
= 0; j
< 2; which
= OPPOSITE(which
), j
++) {
2188 for (i
= 0; i
< w
; i
++) {
2189 target
= state
->common
->colcount
[i
*3+which
];
2190 count
= count_rowcol(state
, i
, COLUMN
, which
);
2192 if ((count
> target
) ||
2193 (count
< target
&& !count_rowcol(state
, i
, COLUMN
, -1)))
2195 if (count
== target
) c
|= DS_FULL
;
2196 if (c
!= ds
->colwhat
[i
*3+which
] || !ds
->started
) {
2197 draw_num(dr
, ds
, COLUMN
, which
, i
, c
,
2198 state
->common
->colcount
[i
*3+which
]);
2199 ds
->colwhat
[i
*3+which
] = c
;
2202 for (i
= 0; i
< h
; i
++) {
2203 target
= state
->common
->rowcount
[i
*3+which
];
2204 count
= count_rowcol(state
, i
, ROW
, which
);
2206 if ((count
> target
) ||
2207 (count
< target
&& !count_rowcol(state
, i
, ROW
, -1)))
2209 if (count
== target
) c
|= DS_FULL
;
2210 if (c
!= ds
->rowwhat
[i
*3+which
] || !ds
->started
) {
2211 draw_num(dr
, ds
, ROW
, which
, i
, c
,
2212 state
->common
->rowcount
[i
*3+which
]);
2213 ds
->rowwhat
[i
*3+which
] = c
;
2221 static float game_anim_length(game_state
*oldstate
, game_state
*newstate
,
2222 int dir
, game_ui
*ui
)
2227 static float game_flash_length(game_state
*oldstate
, game_state
*newstate
,
2228 int dir
, game_ui
*ui
)
2230 if (!oldstate
->completed
&& newstate
->completed
&&
2231 !oldstate
->solved
&& !newstate
->solved
)
2236 static int game_status(game_state
*state
)
2238 return state
->completed
? +1 : 0;
2241 static int game_timing_state(game_state
*state
, game_ui
*ui
)
2246 static void game_print_size(game_params
*params
, float *x
, float *y
)
2251 * I'll use 6mm squares by default.
2253 game_compute_size(params
, 600, &pw
, &ph
);
2258 static void game_print(drawing
*dr
, game_state
*state
, int tilesize
)
2260 int w
= state
->w
, h
= state
->h
;
2261 int ink
= print_mono_colour(dr
, 0);
2262 int paper
= print_mono_colour(dr
, 1);
2263 int x
, y
, which
, i
, j
;
2265 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
2266 game_drawstate ads
, *ds
= &ads
;
2267 game_set_size(dr
, ds
, NULL
, tilesize
);
2268 ds
->w
= w
; ds
->h
= h
;
2271 print_line_width(dr
, TILE_SIZE
/12);
2273 /* Numbers and +/- for corners. */
2274 draw_sym(dr
, ds
, -1, -1, POSITIVE
, ink
);
2275 draw_sym(dr
, ds
, state
->w
, state
->h
, NEGATIVE
, ink
);
2276 for (which
= POSITIVE
, j
= 0; j
< 2; which
= OPPOSITE(which
), j
++) {
2277 for (i
= 0; i
< w
; i
++) {
2278 draw_num_col(dr
, ds
, COLUMN
, which
, i
, paper
, ink
,
2279 state
->common
->colcount
[i
*3+which
]);
2281 for (i
= 0; i
< h
; i
++) {
2282 draw_num_col(dr
, ds
, ROW
, which
, i
, paper
, ink
,
2283 state
->common
->rowcount
[i
*3+which
]);
2288 for (x
= 0; x
< w
; x
++) {
2289 for (y
= 0; y
< h
; y
++) {
2291 if (state
->common
->dominoes
[i
] == i
+1 ||
2292 state
->common
->dominoes
[i
] == i
+w
) {
2293 int dx
= state
->common
->dominoes
[i
] == i
+1 ? 2 : 1;
2296 int cx
= COORD(x
), cy
= COORD(y
);
2298 print_line_width(dr
, 0);
2300 /* Ink the domino */
2301 for (yy
= 0; yy
< 2; yy
++)
2302 for (xx
= 0; xx
< 2; xx
++)
2304 cx
+xx
*dx
*TILE_SIZE
+(1-2*xx
)*3*TILE_SIZE
/16,
2305 cy
+yy
*dy
*TILE_SIZE
+(1-2*yy
)*3*TILE_SIZE
/16,
2306 TILE_SIZE
/8, ink
, ink
);
2307 draw_rect(dr
, cx
+ TILE_SIZE
/16, cy
+ 3*TILE_SIZE
/16,
2308 dx
*TILE_SIZE
- 2*(TILE_SIZE
/16),
2309 dy
*TILE_SIZE
- 6*(TILE_SIZE
/16), ink
);
2310 draw_rect(dr
, cx
+ 3*TILE_SIZE
/16, cy
+ TILE_SIZE
/16,
2311 dx
*TILE_SIZE
- 6*(TILE_SIZE
/16),
2312 dy
*TILE_SIZE
- 2*(TILE_SIZE
/16), ink
);
2314 /* Un-ink the domino interior */
2315 for (yy
= 0; yy
< 2; yy
++)
2316 for (xx
= 0; xx
< 2; xx
++)
2318 cx
+xx
*dx
*TILE_SIZE
+(1-2*xx
)*3*TILE_SIZE
/16,
2319 cy
+yy
*dy
*TILE_SIZE
+(1-2*yy
)*3*TILE_SIZE
/16,
2320 3*TILE_SIZE
/32, paper
, paper
);
2321 draw_rect(dr
, cx
+ 3*TILE_SIZE
/32, cy
+ 3*TILE_SIZE
/16,
2322 dx
*TILE_SIZE
- 2*(3*TILE_SIZE
/32),
2323 dy
*TILE_SIZE
- 6*(TILE_SIZE
/16), paper
);
2324 draw_rect(dr
, cx
+ 3*TILE_SIZE
/16, cy
+ 3*TILE_SIZE
/32,
2325 dx
*TILE_SIZE
- 6*(TILE_SIZE
/16),
2326 dy
*TILE_SIZE
- 2*(3*TILE_SIZE
/32), paper
);
2331 /* Grid symbols (solution). */
2332 for (x
= 0; x
< w
; x
++) {
2333 for (y
= 0; y
< h
; y
++) {
2335 if ((state
->grid
[i
] != NEUTRAL
) || (state
->flags
[i
] & GS_SET
))
2336 draw_sym(dr
, ds
, x
, y
, state
->grid
[i
], ink
);
2342 #define thegame magnets
2345 const struct game thegame
= {
2346 "Magnets", "games.magnets", "magnets",
2353 TRUE
, game_configure
, custom_params
,
2361 TRUE
, game_can_format_as_text_now
, game_text_format
,
2369 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
2372 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: */