mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Patch from Lambros to make the Normal difficulty level easier, since
people have generally seemed to think Loopy is one of the more difficult puzzles in the collection. There's a new level called Tricky, between Normal and Hard, which is equivalent to the old Normal. [originally from svn r8398]
This commit is contained in:
242
loopy.c
242
loopy.c
@ -133,17 +133,6 @@ enum solver_status {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* ------ Solver state ------ */
|
/* ------ Solver state ------ */
|
||||||
typedef struct normal {
|
|
||||||
/* For each dline, store a bitmask for whether we know:
|
|
||||||
* (bit 0) at least one is YES
|
|
||||||
* (bit 1) at most one is YES */
|
|
||||||
char *dlines;
|
|
||||||
} normal_mode_state;
|
|
||||||
|
|
||||||
typedef struct hard {
|
|
||||||
int *linedsf;
|
|
||||||
} hard_mode_state;
|
|
||||||
|
|
||||||
typedef struct solver_state {
|
typedef struct solver_state {
|
||||||
game_state *state;
|
game_state *state;
|
||||||
enum solver_status solver_status;
|
enum solver_status solver_status;
|
||||||
@ -151,6 +140,10 @@ typedef struct solver_state {
|
|||||||
* looplen of 1 means there are no lines to a particular dot */
|
* looplen of 1 means there are no lines to a particular dot */
|
||||||
int *looplen;
|
int *looplen;
|
||||||
|
|
||||||
|
/* Difficulty level of solver. Used by solver functions that want to
|
||||||
|
* vary their behaviour depending on the requested difficulty level. */
|
||||||
|
int diff;
|
||||||
|
|
||||||
/* caches */
|
/* caches */
|
||||||
char *dot_yes_count;
|
char *dot_yes_count;
|
||||||
char *dot_no_count;
|
char *dot_no_count;
|
||||||
@ -159,8 +152,14 @@ typedef struct solver_state {
|
|||||||
char *dot_solved, *face_solved;
|
char *dot_solved, *face_solved;
|
||||||
int *dotdsf;
|
int *dotdsf;
|
||||||
|
|
||||||
normal_mode_state *normal;
|
/* Information for Normal level deductions:
|
||||||
hard_mode_state *hard;
|
* For each dline, store a bitmask for whether we know:
|
||||||
|
* (bit 0) at least one is YES
|
||||||
|
* (bit 1) at most one is YES */
|
||||||
|
char *dlines;
|
||||||
|
|
||||||
|
/* Hard level information */
|
||||||
|
int *linedsf;
|
||||||
} solver_state;
|
} solver_state;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -169,21 +168,39 @@ typedef struct solver_state {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define DIFFLIST(A) \
|
#define DIFFLIST(A) \
|
||||||
A(EASY,Easy,e,easy_mode_deductions) \
|
A(EASY,Easy,e) \
|
||||||
A(NORMAL,Normal,n,normal_mode_deductions) \
|
A(NORMAL,Normal,n) \
|
||||||
A(HARD,Hard,h,hard_mode_deductions)
|
A(TRICKY,Tricky,t) \
|
||||||
#define ENUM(upper,title,lower,fn) DIFF_ ## upper,
|
A(HARD,Hard,h)
|
||||||
#define TITLE(upper,title,lower,fn) #title,
|
#define ENUM(upper,title,lower) DIFF_ ## upper,
|
||||||
#define ENCODE(upper,title,lower,fn) #lower
|
#define TITLE(upper,title,lower) #title,
|
||||||
#define CONFIG(upper,title,lower,fn) ":" #title
|
#define ENCODE(upper,title,lower) #lower
|
||||||
#define SOLVER_FN_DECL(upper,title,lower,fn) static int fn(solver_state *);
|
#define CONFIG(upper,title,lower) ":" #title
|
||||||
#define SOLVER_FN(upper,title,lower,fn) &fn,
|
|
||||||
enum { DIFFLIST(ENUM) DIFF_MAX };
|
enum { DIFFLIST(ENUM) DIFF_MAX };
|
||||||
static char const *const diffnames[] = { DIFFLIST(TITLE) };
|
static char const *const diffnames[] = { DIFFLIST(TITLE) };
|
||||||
static char const diffchars[] = DIFFLIST(ENCODE);
|
static char const diffchars[] = DIFFLIST(ENCODE);
|
||||||
#define DIFFCONFIG DIFFLIST(CONFIG)
|
#define DIFFCONFIG DIFFLIST(CONFIG)
|
||||||
DIFFLIST(SOLVER_FN_DECL)
|
|
||||||
static int (*(solver_fns[]))(solver_state *) = { DIFFLIST(SOLVER_FN) };
|
/*
|
||||||
|
* Solver routines, sorted roughly in order of computational cost.
|
||||||
|
* The solver will run the faster deductions first, and slower deductions are
|
||||||
|
* only invoked when the faster deductions are unable to make progress.
|
||||||
|
* Each function is associated with a difficulty level, so that the generated
|
||||||
|
* puzzles are solvable by applying only the functions with the chosen
|
||||||
|
* difficulty level or lower.
|
||||||
|
*/
|
||||||
|
#define SOLVERLIST(A) \
|
||||||
|
A(trivial_deductions, DIFF_EASY) \
|
||||||
|
A(dline_deductions, DIFF_NORMAL) \
|
||||||
|
A(linedsf_deductions, DIFF_HARD) \
|
||||||
|
A(loop_deductions, DIFF_EASY)
|
||||||
|
#define SOLVER_FN_DECL(fn,diff) static int fn(solver_state *);
|
||||||
|
#define SOLVER_FN(fn,diff) &fn,
|
||||||
|
#define SOLVER_DIFF(fn,diff) diff,
|
||||||
|
SOLVERLIST(SOLVER_FN_DECL)
|
||||||
|
static int (*(solver_fns[]))(solver_state *) = { SOLVERLIST(SOLVER_FN) };
|
||||||
|
static int const solver_diffs[] = { SOLVERLIST(SOLVER_DIFF) };
|
||||||
|
const int NUM_SOLVERS = sizeof(solver_diffs)/sizeof(*solver_diffs);
|
||||||
|
|
||||||
struct game_params {
|
struct game_params {
|
||||||
int w, h;
|
int w, h;
|
||||||
@ -218,8 +235,7 @@ struct game_drawstate {
|
|||||||
static char *validate_desc(game_params *params, char *desc);
|
static char *validate_desc(game_params *params, char *desc);
|
||||||
static int dot_order(const game_state* state, int i, char line_type);
|
static int dot_order(const game_state* state, int i, char line_type);
|
||||||
static int face_order(const game_state* state, int i, char line_type);
|
static int face_order(const game_state* state, int i, char line_type);
|
||||||
static solver_state *solve_game_rec(const solver_state *sstate,
|
static solver_state *solve_game_rec(const solver_state *sstate);
|
||||||
int diff);
|
|
||||||
|
|
||||||
#ifdef DEBUG_CACHES
|
#ifdef DEBUG_CACHES
|
||||||
static void check_caches(const solver_state* sstate);
|
static void check_caches(const solver_state* sstate);
|
||||||
@ -333,6 +349,7 @@ static solver_state *new_solver_state(game_state *state, int diff) {
|
|||||||
ret->state = dup_game(state);
|
ret->state = dup_game(state);
|
||||||
|
|
||||||
ret->solver_status = SOLVER_INCOMPLETE;
|
ret->solver_status = SOLVER_INCOMPLETE;
|
||||||
|
ret->diff = diff;
|
||||||
|
|
||||||
ret->dotdsf = snew_dsf(num_dots);
|
ret->dotdsf = snew_dsf(num_dots);
|
||||||
ret->looplen = snewn(num_dots, int);
|
ret->looplen = snewn(num_dots, int);
|
||||||
@ -356,18 +373,16 @@ static solver_state *new_solver_state(game_state *state, int diff) {
|
|||||||
memset(ret->face_no_count, 0, num_faces);
|
memset(ret->face_no_count, 0, num_faces);
|
||||||
|
|
||||||
if (diff < DIFF_NORMAL) {
|
if (diff < DIFF_NORMAL) {
|
||||||
ret->normal = NULL;
|
ret->dlines = NULL;
|
||||||
} else {
|
} else {
|
||||||
ret->normal = snew(normal_mode_state);
|
ret->dlines = snewn(2*num_edges, char);
|
||||||
ret->normal->dlines = snewn(2*num_edges, char);
|
memset(ret->dlines, 0, 2*num_edges);
|
||||||
memset(ret->normal->dlines, 0, 2*num_edges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff < DIFF_HARD) {
|
if (diff < DIFF_HARD) {
|
||||||
ret->hard = NULL;
|
ret->linedsf = NULL;
|
||||||
} else {
|
} else {
|
||||||
ret->hard = snew(hard_mode_state);
|
ret->linedsf = snew_dsf(state->game_grid->num_edges);
|
||||||
ret->hard->linedsf = snew_dsf(state->game_grid->num_edges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -385,15 +400,9 @@ static void free_solver_state(solver_state *sstate) {
|
|||||||
sfree(sstate->face_yes_count);
|
sfree(sstate->face_yes_count);
|
||||||
sfree(sstate->face_no_count);
|
sfree(sstate->face_no_count);
|
||||||
|
|
||||||
if (sstate->normal) {
|
/* OK, because sfree(NULL) is a no-op */
|
||||||
sfree(sstate->normal->dlines);
|
sfree(sstate->dlines);
|
||||||
sfree(sstate->normal);
|
sfree(sstate->linedsf);
|
||||||
}
|
|
||||||
|
|
||||||
if (sstate->hard) {
|
|
||||||
sfree(sstate->hard->linedsf);
|
|
||||||
sfree(sstate->hard);
|
|
||||||
}
|
|
||||||
|
|
||||||
sfree(sstate);
|
sfree(sstate);
|
||||||
}
|
}
|
||||||
@ -409,6 +418,7 @@ static solver_state *dup_solver_state(const solver_state *sstate) {
|
|||||||
ret->state = state = dup_game(sstate->state);
|
ret->state = state = dup_game(sstate->state);
|
||||||
|
|
||||||
ret->solver_status = sstate->solver_status;
|
ret->solver_status = sstate->solver_status;
|
||||||
|
ret->diff = sstate->diff;
|
||||||
|
|
||||||
ret->dotdsf = snewn(num_dots, int);
|
ret->dotdsf = snewn(num_dots, int);
|
||||||
ret->looplen = snewn(num_dots, int);
|
ret->looplen = snewn(num_dots, int);
|
||||||
@ -432,22 +442,20 @@ static solver_state *dup_solver_state(const solver_state *sstate) {
|
|||||||
ret->face_no_count = snewn(num_faces, char);
|
ret->face_no_count = snewn(num_faces, char);
|
||||||
memcpy(ret->face_no_count, sstate->face_no_count, num_faces);
|
memcpy(ret->face_no_count, sstate->face_no_count, num_faces);
|
||||||
|
|
||||||
if (sstate->normal) {
|
if (sstate->dlines) {
|
||||||
ret->normal = snew(normal_mode_state);
|
ret->dlines = snewn(2*num_edges, char);
|
||||||
ret->normal->dlines = snewn(2*num_edges, char);
|
memcpy(ret->dlines, sstate->dlines,
|
||||||
memcpy(ret->normal->dlines, sstate->normal->dlines,
|
|
||||||
2*num_edges);
|
2*num_edges);
|
||||||
} else {
|
} else {
|
||||||
ret->normal = NULL;
|
ret->dlines = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sstate->hard) {
|
if (sstate->linedsf) {
|
||||||
ret->hard = snew(hard_mode_state);
|
ret->linedsf = snewn(num_edges, int);
|
||||||
ret->hard->linedsf = snewn(num_edges, int);
|
memcpy(ret->linedsf, sstate->linedsf,
|
||||||
memcpy(ret->hard->linedsf, sstate->hard->linedsf,
|
|
||||||
num_edges * sizeof(int));
|
num_edges * sizeof(int));
|
||||||
} else {
|
} else {
|
||||||
ret->hard = NULL;
|
ret->linedsf = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1105,12 +1113,12 @@ static int merge_lines(solver_state *sstate, int i, int j, int inverse
|
|||||||
assert(i < sstate->state->game_grid->num_edges);
|
assert(i < sstate->state->game_grid->num_edges);
|
||||||
assert(j < sstate->state->game_grid->num_edges);
|
assert(j < sstate->state->game_grid->num_edges);
|
||||||
|
|
||||||
i = edsf_canonify(sstate->hard->linedsf, i, &inv_tmp);
|
i = edsf_canonify(sstate->linedsf, i, &inv_tmp);
|
||||||
inverse ^= inv_tmp;
|
inverse ^= inv_tmp;
|
||||||
j = edsf_canonify(sstate->hard->linedsf, j, &inv_tmp);
|
j = edsf_canonify(sstate->linedsf, j, &inv_tmp);
|
||||||
inverse ^= inv_tmp;
|
inverse ^= inv_tmp;
|
||||||
|
|
||||||
edsf_merge(sstate->hard->linedsf, i, j, inverse);
|
edsf_merge(sstate->linedsf, i, j, inverse);
|
||||||
|
|
||||||
#ifdef SHOW_WORKING
|
#ifdef SHOW_WORKING
|
||||||
if (i != j) {
|
if (i != j) {
|
||||||
@ -1713,7 +1721,7 @@ static int game_has_unique_soln(const game_state *state, int diff)
|
|||||||
solver_state *sstate_new;
|
solver_state *sstate_new;
|
||||||
solver_state *sstate = new_solver_state((game_state *)state, diff);
|
solver_state *sstate = new_solver_state((game_state *)state, diff);
|
||||||
|
|
||||||
sstate_new = solve_game_rec(sstate, diff);
|
sstate_new = solve_game_rec(sstate);
|
||||||
|
|
||||||
assert(sstate_new->solver_status != SOLVER_MISTAKE);
|
assert(sstate_new->solver_status != SOLVER_MISTAKE);
|
||||||
ret = (sstate_new->solver_status == SOLVER_SOLVED);
|
ret = (sstate_new->solver_status == SOLVER_SOLVED);
|
||||||
@ -2027,7 +2035,7 @@ static int check_completion(game_state *state)
|
|||||||
* Easy Mode
|
* Easy Mode
|
||||||
* Just implement the rules of the game.
|
* Just implement the rules of the game.
|
||||||
*
|
*
|
||||||
* Normal Mode
|
* Normal and Tricky Modes
|
||||||
* For each (adjacent) pair of lines through each dot we store a bit for
|
* For each (adjacent) pair of lines through each dot we store a bit for
|
||||||
* whether at least one of them is on and whether at most one is on. (If we
|
* whether at least one of them is on and whether at most one is on. (If we
|
||||||
* know both or neither is on that's already stored more directly.)
|
* know both or neither is on that's already stored more directly.)
|
||||||
@ -2164,7 +2172,7 @@ static int dline_set_opp_atleastone(solver_state *sstate,
|
|||||||
continue;
|
continue;
|
||||||
/* Found opposite UNKNOWNS and they're next to each other */
|
/* Found opposite UNKNOWNS and they're next to each other */
|
||||||
opp_dline_index = dline_index_from_dot(g, d, opp);
|
opp_dline_index = dline_index_from_dot(g, d, opp);
|
||||||
return set_atleastone(sstate->normal->dlines, opp_dline_index);
|
return set_atleastone(sstate->dlines, opp_dline_index);
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -2197,8 +2205,8 @@ static int face_setall_identical(solver_state *sstate, int face_index,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Found two UNKNOWNS */
|
/* Found two UNKNOWNS */
|
||||||
can1 = edsf_canonify(sstate->hard->linedsf, line1_index, &inv1);
|
can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1);
|
||||||
can2 = edsf_canonify(sstate->hard->linedsf, line2_index, &inv2);
|
can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2);
|
||||||
if (can1 == can2 && inv1 == inv2) {
|
if (can1 == can2 && inv1 == inv2) {
|
||||||
solver_set_line(sstate, line1_index, line_new);
|
solver_set_line(sstate, line1_index, line_new);
|
||||||
solver_set_line(sstate, line2_index, line_new);
|
solver_set_line(sstate, line2_index, line_new);
|
||||||
@ -2239,7 +2247,7 @@ static int parity_deductions(solver_state *sstate,
|
|||||||
{
|
{
|
||||||
game_state *state = sstate->state;
|
game_state *state = sstate->state;
|
||||||
int diff = DIFF_MAX;
|
int diff = DIFF_MAX;
|
||||||
int *linedsf = sstate->hard->linedsf;
|
int *linedsf = sstate->linedsf;
|
||||||
|
|
||||||
if (unknown_count == 2) {
|
if (unknown_count == 2) {
|
||||||
/* Lines are known alike/opposite, depending on inv. */
|
/* Lines are known alike/opposite, depending on inv. */
|
||||||
@ -2338,7 +2346,7 @@ static int parity_deductions(solver_state *sstate,
|
|||||||
* Answer: first all squares then all dots.
|
* Answer: first all squares then all dots.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int easy_mode_deductions(solver_state *sstate)
|
static int trivial_deductions(solver_state *sstate)
|
||||||
{
|
{
|
||||||
int i, current_yes, current_no;
|
int i, current_yes, current_no;
|
||||||
game_state *state = sstate->state;
|
game_state *state = sstate->state;
|
||||||
@ -2433,11 +2441,11 @@ static int easy_mode_deductions(solver_state *sstate)
|
|||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int normal_mode_deductions(solver_state *sstate)
|
static int dline_deductions(solver_state *sstate)
|
||||||
{
|
{
|
||||||
game_state *state = sstate->state;
|
game_state *state = sstate->state;
|
||||||
grid *g = state->game_grid;
|
grid *g = state->game_grid;
|
||||||
char *dlines = sstate->normal->dlines;
|
char *dlines = sstate->dlines;
|
||||||
int i;
|
int i;
|
||||||
int diff = DIFF_MAX;
|
int diff = DIFF_MAX;
|
||||||
|
|
||||||
@ -2583,6 +2591,10 @@ static int normal_mode_deductions(solver_state *sstate)
|
|||||||
diff = min(diff, DIFF_EASY);
|
diff = min(diff, DIFF_EASY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* More advanced deduction that allows propagation along diagonal
|
||||||
|
* chains of faces connected by dots, for example, 3-2-...-2-3
|
||||||
|
* in square grids. */
|
||||||
|
if (sstate->diff >= DIFF_TRICKY) {
|
||||||
/* Now see if we can make dline deduction for edges{j,j+1} */
|
/* Now see if we can make dline deduction for edges{j,j+1} */
|
||||||
e = f->edges[k];
|
e = f->edges[k];
|
||||||
if (state->lines[e - g->edges] != LINE_UNKNOWN)
|
if (state->lines[e - g->edges] != LINE_UNKNOWN)
|
||||||
@ -2609,6 +2621,7 @@ static int normal_mode_deductions(solver_state *sstate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (diff < DIFF_NORMAL)
|
if (diff < DIFF_NORMAL)
|
||||||
return diff;
|
return diff;
|
||||||
@ -2699,6 +2712,10 @@ static int normal_mode_deductions(solver_state *sstate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* More advanced deduction that allows propagation along diagonal
|
||||||
|
* chains of faces connected by dots, for example: 3-2-...-2-3
|
||||||
|
* in square grids. */
|
||||||
|
if (sstate->diff >= DIFF_TRICKY) {
|
||||||
/* If we have atleastone set for this dline, infer
|
/* If we have atleastone set for this dline, infer
|
||||||
* atmostone for each "opposite" dline (that is, each
|
* atmostone for each "opposite" dline (that is, each
|
||||||
* dline without edges in common with this one).
|
* dline without edges in common with this one).
|
||||||
@ -2719,7 +2736,6 @@ static int normal_mode_deductions(solver_state *sstate)
|
|||||||
if (set_atmostone(dlines, opp_dline_index))
|
if (set_atmostone(dlines, opp_dline_index))
|
||||||
diff = min(diff, DIFF_NORMAL);
|
diff = min(diff, DIFF_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yes == 0 && is_atmostone(dlines, dline_index)) {
|
if (yes == 0 && is_atmostone(dlines, dline_index)) {
|
||||||
/* This dline has *exactly* one YES and there are no
|
/* This dline has *exactly* one YES and there are no
|
||||||
* other YESs. This allows more deductions. */
|
* other YESs. This allows more deductions. */
|
||||||
@ -2731,13 +2747,15 @@ static int normal_mode_deductions(solver_state *sstate)
|
|||||||
continue;
|
continue;
|
||||||
opp_index = d->edges[opp] - g->edges;
|
opp_index = d->edges[opp] - g->edges;
|
||||||
if (state->lines[opp_index] == LINE_UNKNOWN) {
|
if (state->lines[opp_index] == LINE_UNKNOWN) {
|
||||||
solver_set_line(sstate, opp_index, LINE_YES);
|
solver_set_line(sstate, opp_index,
|
||||||
|
LINE_YES);
|
||||||
diff = min(diff, DIFF_EASY);
|
diff = min(diff, DIFF_EASY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (unknown == 4) {
|
} else if (unknown == 4) {
|
||||||
/* Exactly one of opposite UNKNOWNS is YES. We've
|
/* Exactly one of opposite UNKNOWNS is YES. We've
|
||||||
* already set atmostone, so set atleastone as well.
|
* already set atmostone, so set atleastone as
|
||||||
|
* well.
|
||||||
*/
|
*/
|
||||||
if (dline_set_opp_atleastone(sstate, d, j))
|
if (dline_set_opp_atleastone(sstate, d, j))
|
||||||
diff = min(diff, DIFF_NORMAL);
|
diff = min(diff, DIFF_NORMAL);
|
||||||
@ -2746,14 +2764,15 @@ static int normal_mode_deductions(solver_state *sstate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hard_mode_deductions(solver_state *sstate)
|
static int linedsf_deductions(solver_state *sstate)
|
||||||
{
|
{
|
||||||
game_state *state = sstate->state;
|
game_state *state = sstate->state;
|
||||||
grid *g = state->game_grid;
|
grid *g = state->game_grid;
|
||||||
char *dlines = sstate->normal->dlines;
|
char *dlines = sstate->dlines;
|
||||||
int i;
|
int i;
|
||||||
int diff = DIFF_MAX;
|
int diff = DIFF_MAX;
|
||||||
int diff_tmp;
|
int diff_tmp;
|
||||||
@ -2823,8 +2842,8 @@ static int hard_mode_deductions(solver_state *sstate)
|
|||||||
if (state->lines[line2_index] != LINE_UNKNOWN)
|
if (state->lines[line2_index] != LINE_UNKNOWN)
|
||||||
continue;
|
continue;
|
||||||
/* Infer dline flags from linedsf */
|
/* Infer dline flags from linedsf */
|
||||||
can1 = edsf_canonify(sstate->hard->linedsf, line1_index, &inv1);
|
can1 = edsf_canonify(sstate->linedsf, line1_index, &inv1);
|
||||||
can2 = edsf_canonify(sstate->hard->linedsf, line2_index, &inv2);
|
can2 = edsf_canonify(sstate->linedsf, line2_index, &inv2);
|
||||||
if (can1 == can2 && inv1 != inv2) {
|
if (can1 == can2 && inv1 != inv2) {
|
||||||
/* These are opposites, so set dline atmostone/atleastone */
|
/* These are opposites, so set dline atmostone/atleastone */
|
||||||
if (set_atmostone(dlines, dline_index))
|
if (set_atmostone(dlines, dline_index))
|
||||||
@ -2858,7 +2877,7 @@ static int hard_mode_deductions(solver_state *sstate)
|
|||||||
for (i = 0; i < g->num_edges; i++) {
|
for (i = 0; i < g->num_edges; i++) {
|
||||||
int can, inv;
|
int can, inv;
|
||||||
enum line_state s;
|
enum line_state s;
|
||||||
can = edsf_canonify(sstate->hard->linedsf, i, &inv);
|
can = edsf_canonify(sstate->linedsf, i, &inv);
|
||||||
if (can == i)
|
if (can == i)
|
||||||
continue;
|
continue;
|
||||||
s = sstate->state->lines[can];
|
s = sstate->state->lines[can];
|
||||||
@ -3031,52 +3050,59 @@ static int loop_deductions(solver_state *sstate)
|
|||||||
|
|
||||||
/* This will return a dynamically allocated solver_state containing the (more)
|
/* This will return a dynamically allocated solver_state containing the (more)
|
||||||
* solved grid */
|
* solved grid */
|
||||||
static solver_state *solve_game_rec(const solver_state *sstate_start,
|
static solver_state *solve_game_rec(const solver_state *sstate_start)
|
||||||
int diff)
|
|
||||||
{
|
{
|
||||||
solver_state *sstate, *sstate_saved;
|
solver_state *sstate;
|
||||||
int solver_progress;
|
|
||||||
game_state *state;
|
/* Index of the solver we should call next. */
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
/* As a speed-optimisation, we avoid re-running solvers that we know
|
||||||
|
* won't make any progress. This happens when a high-difficulty
|
||||||
|
* solver makes a deduction that can only help other high-difficulty
|
||||||
|
* solvers.
|
||||||
|
* For example: if a new 'dline' flag is set by dline_deductions, the
|
||||||
|
* trivial_deductions solver cannot do anything with this information.
|
||||||
|
* If we've already run the trivial_deductions solver (because it's
|
||||||
|
* earlier in the list), there's no point running it again.
|
||||||
|
*
|
||||||
|
* Therefore: if a solver is earlier in the list than "threshold_index",
|
||||||
|
* we don't bother running it if it's difficulty level is less than
|
||||||
|
* "threshold_diff".
|
||||||
|
*/
|
||||||
|
int threshold_diff = 0;
|
||||||
|
int threshold_index = 0;
|
||||||
|
|
||||||
/* Indicates which solver we should call next. This is a sensible starting
|
|
||||||
* point */
|
|
||||||
int current_solver = DIFF_EASY, next_solver;
|
|
||||||
sstate = dup_solver_state(sstate_start);
|
sstate = dup_solver_state(sstate_start);
|
||||||
|
|
||||||
/* Cache the values of some variables for readability */
|
|
||||||
state = sstate->state;
|
|
||||||
|
|
||||||
sstate_saved = NULL;
|
|
||||||
|
|
||||||
solver_progress = FALSE;
|
|
||||||
|
|
||||||
check_caches(sstate);
|
check_caches(sstate);
|
||||||
|
|
||||||
do {
|
while (i < NUM_SOLVERS) {
|
||||||
if (sstate->solver_status == SOLVER_MISTAKE)
|
if (sstate->solver_status == SOLVER_MISTAKE)
|
||||||
return sstate;
|
return sstate;
|
||||||
|
|
||||||
next_solver = solver_fns[current_solver](sstate);
|
|
||||||
|
|
||||||
if (next_solver == DIFF_MAX) {
|
|
||||||
if (current_solver < diff && current_solver + 1 < DIFF_MAX) {
|
|
||||||
/* Try next beefier solver */
|
|
||||||
next_solver = current_solver + 1;
|
|
||||||
} else {
|
|
||||||
next_solver = loop_deductions(sstate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sstate->solver_status == SOLVER_SOLVED ||
|
if (sstate->solver_status == SOLVER_SOLVED ||
|
||||||
sstate->solver_status == SOLVER_AMBIGUOUS) {
|
sstate->solver_status == SOLVER_AMBIGUOUS) {
|
||||||
/* fprintf(stderr, "Solver completed\n"); */
|
/* solver finished */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Once we've looped over all permitted solvers then the loop
|
if ((solver_diffs[i] >= threshold_diff || i >= threshold_index)
|
||||||
* deductions without making any progress, we'll exit this while loop */
|
&& solver_diffs[i] <= sstate->diff) {
|
||||||
current_solver = next_solver;
|
/* current_solver is eligible, so use it */
|
||||||
} while (current_solver < DIFF_MAX);
|
int next_diff = solver_fns[i](sstate);
|
||||||
|
if (next_diff != DIFF_MAX) {
|
||||||
|
/* solver made progress, so use new thresholds and
|
||||||
|
* start again at top of list. */
|
||||||
|
threshold_diff = next_diff;
|
||||||
|
threshold_index = i;
|
||||||
|
i = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* current_solver is ineligible, or failed to make progress, so
|
||||||
|
* go to the next solver in the list */
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
if (sstate->solver_status == SOLVER_SOLVED ||
|
if (sstate->solver_status == SOLVER_SOLVED ||
|
||||||
sstate->solver_status == SOLVER_AMBIGUOUS) {
|
sstate->solver_status == SOLVER_AMBIGUOUS) {
|
||||||
@ -3096,7 +3122,7 @@ static char *solve_game(game_state *state, game_state *currstate,
|
|||||||
solver_state *sstate, *new_sstate;
|
solver_state *sstate, *new_sstate;
|
||||||
|
|
||||||
sstate = new_solver_state(state, DIFF_MAX);
|
sstate = new_solver_state(state, DIFF_MAX);
|
||||||
new_sstate = solve_game_rec(sstate, DIFF_MAX);
|
new_sstate = solve_game_rec(sstate);
|
||||||
|
|
||||||
if (new_sstate->solver_status == SOLVER_SOLVED) {
|
if (new_sstate->solver_status == SOLVER_SOLVED) {
|
||||||
soln = encode_solve_move(new_sstate->state);
|
soln = encode_solve_move(new_sstate->state);
|
||||||
@ -3707,7 +3733,7 @@ int main(int argc, char **argv)
|
|||||||
solver_state *sstate_new;
|
solver_state *sstate_new;
|
||||||
solver_state *sstate = new_solver_state((game_state *)s, diff);
|
solver_state *sstate = new_solver_state((game_state *)s, diff);
|
||||||
|
|
||||||
sstate_new = solve_game_rec(sstate, diff);
|
sstate_new = solve_game_rec(sstate);
|
||||||
|
|
||||||
if (sstate_new->solver_status == SOLVER_MISTAKE)
|
if (sstate_new->solver_status == SOLVER_MISTAKE)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -3740,7 +3766,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
/* If we supported a verbose solver, we'd set verbosity here */
|
/* If we supported a verbose solver, we'd set verbosity here */
|
||||||
|
|
||||||
sstate_new = solve_game_rec(sstate, diff);
|
sstate_new = solve_game_rec(sstate);
|
||||||
|
|
||||||
if (sstate_new->solver_status == SOLVER_MISTAKE)
|
if (sstate_new->solver_status == SOLVER_MISTAKE)
|
||||||
printf("Puzzle is inconsistent\n");
|
printf("Puzzle is inconsistent\n");
|
||||||
|
Reference in New Issue
Block a user