latin_solver_alloc: handle clashing numbers in input grid.

In the setup phase of the centralised latin.c solver, we start by
going over the input grid containing already-placed clue numbers, and
calling latin_solver_place to enter each on into the solver's data
structure. This has the side effect of ruling out each number from the
rest of the row and column, and _also_ checking by assertion that the
number being placed is not ruled out.

Those are a bad combination, because it means that if you give an
obviously inconsistent input grid to latin_solver_alloc (e.g. with two
identical numbers in a row already), it will fail an assertion. In
that situation, you want the solver run as a whole to return
diff_impossible so that the error is reported cleanly.

This assertion failure could be provoked by giving either Towers or
Group a manually-constructed game description inconsistent in that
way, and hitting Solve. Worse, it could be provoked during live play
in Unequal, by filling in a number clashing with a clue and then
pressing 'h' to get hints.
This commit is contained in:
Simon Tatham
2023-02-05 10:29:42 +00:00
parent 517b14e666
commit 5030d87903
4 changed files with 60 additions and 41 deletions

47
latin.c
View File

@ -563,7 +563,7 @@ void latin_solver_free_scratch(struct latin_solver_scratch *scratch)
sfree(scratch); sfree(scratch);
} }
void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o) bool latin_solver_alloc(struct latin_solver *solver, digit *grid, int o)
{ {
int x, y; int x, y;
@ -577,14 +577,23 @@ void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o)
memset(solver->row, 0, o*o); memset(solver->row, 0, o*o);
memset(solver->col, 0, o*o); memset(solver->col, 0, o*o);
for (x = 0; x < o; x++)
for (y = 0; y < o; y++)
if (grid[y*o+x])
latin_solver_place(solver, x, y, grid[y*o+x]);
#ifdef STANDALONE_SOLVER #ifdef STANDALONE_SOLVER
solver->names = NULL; solver->names = NULL;
#endif #endif
for (x = 0; x < o; x++) {
for (y = 0; y < o; y++) {
int n = grid[y*o+x];
if (n) {
if (cube(x, y, n))
latin_solver_place(solver, x, y, n);
else
return false; /* puzzle is already inconsistent */
}
}
}
return true;
} }
void latin_solver_free(struct latin_solver *solver) void latin_solver_free(struct latin_solver *solver)
@ -810,15 +819,17 @@ static int latin_solver_recurse
} else { } else {
newctx = ctx; newctx = ctx;
} }
latin_solver_alloc(&subsolver, outgrid, o);
#ifdef STANDALONE_SOLVER #ifdef STANDALONE_SOLVER
subsolver.names = solver->names; subsolver.names = solver->names;
#endif #endif
ret = latin_solver_top(&subsolver, diff_recursive, if (latin_solver_alloc(&subsolver, outgrid, o))
diff_simple, diff_set_0, diff_set_1, ret = latin_solver_top(&subsolver, diff_recursive,
diff_forcing, diff_recursive, diff_simple, diff_set_0, diff_set_1,
usersolvers, valid, newctx, diff_forcing, diff_recursive,
ctxnew, ctxfree); usersolvers, valid, newctx,
ctxnew, ctxfree);
else
ret = diff_impossible;
latin_solver_free(&subsolver); latin_solver_free(&subsolver);
if (ctxnew) if (ctxnew)
ctxfree(newctx); ctxfree(newctx);
@ -1059,11 +1070,13 @@ int latin_solver(digit *grid, int o, int maxdiff,
struct latin_solver solver; struct latin_solver solver;
int diff; int diff;
latin_solver_alloc(&solver, grid, o); if (latin_solver_alloc(&solver, grid, o))
diff = latin_solver_main(&solver, maxdiff, diff = latin_solver_main(&solver, maxdiff,
diff_simple, diff_set_0, diff_set_1, diff_simple, diff_set_0, diff_set_1,
diff_forcing, diff_recursive, diff_forcing, diff_recursive,
usersolvers, valid, ctx, ctxnew, ctxfree); usersolvers, valid, ctx, ctxnew, ctxfree);
else
diff = diff_impossible;
latin_solver_free(&solver); latin_solver_free(&solver);
return diff; return diff;
} }

View File

@ -61,10 +61,13 @@ int latin_solver_forcing(struct latin_solver *solver,
/* --- Solver allocation --- */ /* --- Solver allocation --- */
/* Fills in (and allocates members for) a latin_solver struct. /* Fills in (and allocates members for) a latin_solver struct.
* Will allocate members of snew, but not snew itself * Will allocate members of solver, but not solver itself
* (allowing 'struct latin_solver' to be the first element in a larger * (allowing 'struct latin_solver' to be the first element in a larger
* struct, for example). */ * struct, for example).
void latin_solver_alloc(struct latin_solver *solver, digit *grid, int o); *
* latin_solver_alloc returns false if the digits already in the grid
* could not be legally placed. */
bool latin_solver_alloc(struct latin_solver *solver, digit *grid, int o);
void latin_solver_free(struct latin_solver *solver); void latin_solver_free(struct latin_solver *solver);
/* Allocates scratch space (for _set and _forcing) */ /* Allocates scratch space (for _set and _forcing) */

View File

@ -890,13 +890,14 @@ static int solver_state(game_state *state, int maxdiff)
struct latin_solver solver; struct latin_solver solver;
int diff; int diff;
latin_solver_alloc(&solver, state->nums, state->order); if (!latin_solver_alloc(&solver, state->nums, state->order))
diff = latin_solver_main(&solver, maxdiff,
diff = latin_solver_main(&solver, maxdiff, DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
DIFF_LATIN, DIFF_SET, DIFF_EXTREME, DIFF_EXTREME, DIFF_RECURSIVE,
DIFF_EXTREME, DIFF_RECURSIVE, unequal_solvers, unequal_valid, ctx,
unequal_solvers, unequal_valid, ctx, clone_ctx, free_ctx);
clone_ctx, free_ctx); else
diff = DIFF_IMPOSSIBLE;
memcpy(state->hints, solver.cube, state->order*state->order*state->order); memcpy(state->hints, solver.cube, state->order*state->order*state->order);
@ -2256,13 +2257,14 @@ static int solve(game_params *p, char *desc, int debug)
solver_show_working = debug; solver_show_working = debug;
game_debug(state); game_debug(state);
latin_solver_alloc(&solver, state->nums, state->order); if (latin_solver_alloc(&solver, state->nums, state->order))
diff = latin_solver_main(&solver, DIFF_RECURSIVE,
diff = latin_solver_main(&solver, DIFF_RECURSIVE, DIFF_LATIN, DIFF_SET, DIFF_EXTREME,
DIFF_LATIN, DIFF_SET, DIFF_EXTREME, DIFF_EXTREME, DIFF_RECURSIVE,
DIFF_EXTREME, DIFF_RECURSIVE, unequal_solvers, unequal_valid, ctx,
unequal_solvers, unequal_valid, ctx, clone_ctx, free_ctx);
clone_ctx, free_ctx); else
diff = DIFF_IMPOSSIBLE;
free_ctx(ctx); free_ctx(ctx);

View File

@ -580,13 +580,11 @@ static int solver(const game_params *params, digit *grid, int maxdiff)
int w = params->w; int w = params->w;
int ret; int ret;
struct latin_solver solver; struct latin_solver solver;
#ifdef STANDALONE_SOLVER #ifdef STANDALONE_SOLVER
char *p, text[100], *names[50]; char *p, text[100], *names[50];
int i; int i;
#endif
latin_solver_alloc(&solver, grid, w);
#ifdef STANDALONE_SOLVER
for (i = 0, p = text; i < w; i++) { for (i = 0, p = text; i < w; i++) {
names[i] = p; names[i] = p;
*p++ = TOCHAR(i+1, params->id); *p++ = TOCHAR(i+1, params->id);
@ -595,10 +593,13 @@ static int solver(const game_params *params, digit *grid, int maxdiff)
solver.names = names; solver.names = names;
#endif #endif
ret = latin_solver_main(&solver, maxdiff, if (latin_solver_alloc(&solver, grid, w))
DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME, ret = latin_solver_main(&solver, maxdiff,
DIFF_EXTREME, DIFF_UNREASONABLE, DIFF_TRIVIAL, DIFF_HARD, DIFF_EXTREME,
group_solvers, group_valid, NULL, NULL, NULL); DIFF_EXTREME, DIFF_UNREASONABLE,
group_solvers, group_valid, NULL, NULL, NULL);
else
ret = diff_impossible;
latin_solver_free(&solver); latin_solver_free(&solver);