X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/6f2d8d7c70f6bbf8bce982ced1fa879e967afbbf..de60d8bdd0fe4db3c41945e03f997e0a9885d5fa:/solo.c diff --git a/solo.c b/solo.c index 4b4adea..3cb050d 100644 --- a/solo.c +++ b/solo.c @@ -107,7 +107,7 @@ struct game_state { int c, r; digit *grid; unsigned char *immutable; /* marks which digits are clues */ - int completed; + int completed, cheated; }; static game_params *default_params(void) @@ -144,6 +144,7 @@ static int game_fetch_preset(int i, char **name, game_params **params) { "3x3 Basic", { 3, 3, SYMM_ROT2, DIFF_SIMPLE } }, { "3x3 Intermediate", { 3, 3, SYMM_ROT2, DIFF_INTERSECT } }, { "3x3 Advanced", { 3, 3, SYMM_ROT2, DIFF_SET } }, + { "3x3 Unreasonable", { 3, 3, SYMM_ROT2, DIFF_RECURSIVE } }, { "3x4 Basic", { 3, 4, SYMM_ROT2, DIFF_SIMPLE } }, { "4x4 Basic", { 4, 4, SYMM_ROT2, DIFF_SIMPLE } }, }; @@ -193,6 +194,8 @@ static game_params *decode_params(char const *string) string++, ret->diff = DIFF_INTERSECT; else if (*string == 'a') /* advanced */ string++, ret->diff = DIFF_SET; + else if (*string == 'u') /* unreasonable */ + string++, ret->diff = DIFF_RECURSIVE; } else string++; /* eat unknown character */ } @@ -239,7 +242,7 @@ static config_item *game_configure(game_params *params) ret[3].name = "Difficulty"; ret[3].type = C_CHOICES; - ret[3].sval = ":Trivial:Basic:Intermediate:Advanced"; + ret[3].sval = ":Trivial:Basic:Intermediate:Advanced:Unreasonable"; ret[3].ival = params->diff; ret[4].name = NULL; @@ -1351,6 +1354,11 @@ static int symmetries(game_params *params, int x, int y, int *output, int s) return i; } +struct game_aux_info { + int c, r; + digit *grid; +}; + static char *new_game_seed(game_params *params, random_state *rs, game_aux_info **aux) { @@ -1363,7 +1371,7 @@ static char *new_game_seed(game_params *params, random_state *rs, char *seed; int coords[16], ncoords; int xlim, ylim; - int maxdiff; + int maxdiff, recursing; /* * Adjust the maximum difficulty level to be consistent with @@ -1394,11 +1402,24 @@ static char *new_game_seed(game_params *params, random_state *rs, assert(ret == 1); assert(check_valid(c, r, grid)); + /* + * Save the solved grid in the aux_info. + */ + { + game_aux_info *ai = snew(game_aux_info); + ai->c = c; + ai->r = r; + ai->grid = snewn(cr * cr, digit); + memcpy(ai->grid, grid, cr * cr * sizeof(digit)); + *aux = ai; + } + /* * Now we have a solved grid, start removing things from it * while preserving solubility. */ symmetry_limit(params, &xlim, &ylim, params->symm); + recursing = FALSE; while (1) { int x, y, i, j; @@ -1435,6 +1456,8 @@ static char *new_game_seed(game_params *params, random_state *rs, * nsolve. */ for (i = 0; i < nlocs; i++) { + int ret; + x = locs[i].x; y = locs[i].y; @@ -1443,7 +1466,12 @@ static char *new_game_seed(game_params *params, random_state *rs, for (j = 0; j < ncoords; j++) grid2[coords[2*j+1]*cr+coords[2*j]] = 0; - if (nsolve(c, r, grid2) <= maxdiff) { + if (recursing) + ret = (rsolve(c, r, grid2, NULL, 2) == 1); + else + ret = (nsolve(c, r, grid2) <= maxdiff); + + if (ret) { for (j = 0; j < ncoords; j++) grid[coords[2*j+1]*cr+coords[2*j]] = 0; break; @@ -1452,15 +1480,22 @@ static char *new_game_seed(game_params *params, random_state *rs, if (i == nlocs) { /* - * There was nothing we could remove without destroying - * solvability. + * There was nothing we could remove without + * destroying solvability. If we're trying to + * generate a recursion-only grid and haven't + * switched over to rsolve yet, we now do; + * otherwise we give up. */ - break; + if (maxdiff == DIFF_RECURSIVE && !recursing) { + recursing = TRUE; + } else { + break; + } } } memcpy(grid2, grid, area); - } while (nsolve(c, r, grid2) != maxdiff); + } while (nsolve(c, r, grid2) < maxdiff); sfree(grid2); sfree(locs); @@ -1514,9 +1549,10 @@ static char *new_game_seed(game_params *params, random_state *rs, return seed; } -void game_free_aux_info(game_aux_info *aux) +static void game_free_aux_info(game_aux_info *aux) { - assert(!"Shouldn't happen"); + sfree(aux->grid); + sfree(aux); } static char *validate_seed(game_params *params, char *seed) @@ -1560,7 +1596,7 @@ static game_state *new_game(game_params *params, char *seed) state->immutable = snewn(area, unsigned char); memset(state->immutable, FALSE, area); - state->completed = FALSE; + state->completed = state->cheated = FALSE; i = 0; while (*seed) { @@ -1602,6 +1638,7 @@ static game_state *dup_game(game_state *state) memcpy(ret->immutable, state->immutable, area); ret->completed = state->completed; + ret->cheated = state->cheated; return ret; } @@ -1613,6 +1650,42 @@ static void free_game(game_state *state) sfree(state); } +static game_state *solve_game(game_state *state, game_aux_info *ai, + char **error) +{ + game_state *ret; + int c = state->c, r = state->r, cr = c*r; + int rsolve_ret; + + ret = dup_game(state); + ret->completed = ret->cheated = TRUE; + + /* + * If we already have the solution in the aux_info, save + * ourselves some time. + */ + if (ai) { + + assert(c == ai->c); + assert(r == ai->r); + memcpy(ret->grid, ai->grid, cr * cr * sizeof(digit)); + + } else { + rsolve_ret = rsolve(c, r, ret->grid, NULL, 2); + + if (rsolve_ret != 1) { + free_game(ret); + if (rsolve_ret == 0) + *error = "No solution exists for this puzzle"; + else + *error = "Multiple solutions exist for this puzzle"; + return NULL; + } + } + + return ret; +} + static char *grid_text_format(int c, int r, digit *grid) { int cr = c*r; @@ -1940,7 +2013,8 @@ static float game_anim_length(game_state *oldstate, game_state *newstate, static float game_flash_length(game_state *oldstate, game_state *newstate, int dir) { - if (!oldstate->completed && newstate->completed) + if (!oldstate->completed && newstate->completed && + !oldstate->cheated && !newstate->cheated) return FLASH_TIME; return 0.0F; } @@ -1970,6 +2044,7 @@ const struct game thegame = { new_game, dup_game, free_game, + TRUE, solve_game, TRUE, game_text_format, new_ui, free_ui,