aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Kölker <jonaskoelker@yahoo.com>2015-10-08 12:55:52 +0200
committerSimon Tatham <anakin@pobox.com>2015-10-14 20:29:32 +0100
commitcd67072556c6b5934005b1777a465aca1e9df545 (patch)
tree1f644d7f68a25b4cd222dbcb91593b8b1069c231
parent12fabc4add608622da87096bb3bed586efee10d9 (diff)
downloadpuzzles-cd67072556c6b5934005b1777a465aca1e9df545.zip
puzzles-cd67072556c6b5934005b1777a465aca1e9df545.tar.gz
puzzles-cd67072556c6b5934005b1777a465aca1e9df545.tar.bz2
puzzles-cd67072556c6b5934005b1777a465aca1e9df545.tar.xz
Add standalone Fifteen solver, based on the hint feature.
Recall that the hint feature is really an incremental solver. Apply it repeatedly until the board is solved. Grade puzzles as solvable or unsolvable by checking their parity.
-rw-r--r--fifteen.R3
-rw-r--r--fifteen.c99
2 files changed, 99 insertions, 3 deletions
diff --git a/fifteen.R b/fifteen.R
index 38711a5..b2292ac 100644
--- a/fifteen.R
+++ b/fifteen.R
@@ -4,6 +4,9 @@ fifteen : [X] GTK COMMON fifteen fifteen-icon|no-icon
fifteen : [G] WINDOWS COMMON fifteen fifteen.res|noicon.res
+fifteensolver : [U] fifteen[STANDALONE_SOLVER] STANDALONE
+fifteensolver : [C] fifteen[STANDALONE_SOLVER] STANDALONE
+
ALL += fifteen[COMBINED]
!begin am gtk
diff --git a/fifteen.c b/fifteen.c
index ffb5d25..6482714 100644
--- a/fifteen.c
+++ b/fifteen.c
@@ -25,6 +25,11 @@
#define Y(state, i) ( (i) / (state)->w )
#define C(state, x, y) ( (y) * (state)->w + (x) )
+#define PARITY_P(params, gap) (((X((params), (gap)) - ((params)->w - 1)) ^ \
+ (Y((params), (gap)) - ((params)->h - 1)) ^ \
+ (((params)->w * (params)->h) + 1)) & 1)
+#define PARITY_S(state) PARITY_P((state), ((state)->gap_pos))
+
enum {
COL_BACKGROUND,
COL_TEXT,
@@ -231,9 +236,7 @@ static char *new_game_desc(const game_params *params, random_state *rs,
* rather than 0,...,n-1; this is a cyclic permutation of
* the starting point and hence is odd iff n is even.)
*/
- parity = ((X(params, gap) - (params->w-1)) ^
- (Y(params, gap) - (params->h-1)) ^
- (n+1)) & 1;
+ parity = PARITY_P(params, gap);
/*
* Try the last two tiles one way round. If that fails, swap
@@ -1120,3 +1123,93 @@ const struct game thegame = {
FALSE, game_timing_state,
0, /* flags */
};
+
+#ifdef STANDALONE_SOLVER
+
+int main(int argc, char **argv)
+{
+ game_params *params;
+ game_state *state;
+ char *id = NULL, *desc, *err;
+ int grade = FALSE;
+ char *progname = argv[0];
+
+ char buf[80];
+ int limit, x, y, solvable;
+
+ while (--argc > 0) {
+ char *p = *++argv;
+ if (!strcmp(p, "-v")) {
+ /* solver_show_working = TRUE; */
+ } else if (!strcmp(p, "-g")) {
+ grade = TRUE;
+ } else if (*p == '-') {
+ fprintf(stderr, "%s: unrecognised option `%s'\n", progname, p);
+ return 1;
+ } else {
+ id = p;
+ }
+ }
+
+ if (!id) {
+ fprintf(stderr, "usage: %s [-g | -v] <game_id>\n", argv[0]);
+ return 1;
+ }
+
+ desc = strchr(id, ':');
+ if (!desc) {
+ fprintf(stderr, "%s: game id expects a colon in it\n", argv[0]);
+ return 1;
+ }
+ *desc++ = '\0';
+
+ params = default_params();
+ decode_params(params, id);
+ err = validate_desc(params, desc);
+ if (err) {
+ free_params(params);
+ fprintf(stderr, "%s: %s\n", argv[0], err);
+ return 1;
+ }
+
+ state = new_game(NULL, params, desc);
+ free_params(params);
+
+ solvable = (PARITY_S(state) == perm_parity(state->tiles, state->n));
+ if (grade || !solvable) {
+ free_game(state);
+ fputs(solvable ? "Game is solvable" : "Game is unsolvable",
+ grade ? stdout : stderr);
+ return !grade;
+ }
+
+ for (limit = 5 * state->n * state->n * state->n; limit; --limit) {
+ game_state *next_state;
+ if (!compute_hint(state, &x, &y)) {
+ fprintf(stderr, "couldn't compute next move while solving %s:%s",
+ id, desc);
+ return 1;
+ }
+ printf("Move the space to (%d, %d), moving %d into the space\n",
+ x + 1, y + 1, state->tiles[C(state, x, y)]);
+ sprintf(buf, "M%d,%d", x, y);
+ next_state = execute_move(state, buf);
+
+ free_game(state);
+ if (!next_state) {
+ fprintf(stderr, "invalid move when solving %s:%s\n", id, desc);
+ return 1;
+ }
+ state = next_state;
+ if (next_state->completed) {
+ free_game(state);
+ return 0;
+ }
+ }
+
+ free_game(state);
+ fprintf(stderr, "ran out of moves for %s:%s\n", id, desc);
+ return 1;
+}
+
+#endif