+static int game_has_unique_soln(const game_state *state, int diff)
+{
+ int ret;
+ solver_state *sstate_new;
+ solver_state *sstate = new_solver_state((game_state *)state, diff);
+
+ sstate_new = solve_game_rec(sstate);
+
+ assert(sstate_new->solver_status != SOLVER_MISTAKE);
+ ret = (sstate_new->solver_status == SOLVER_SOLVED);
+
+ free_solver_state(sstate_new);
+ free_solver_state(sstate);
+
+ return ret;
+}
+
+
+/* Remove clues one at a time at random. */
+static game_state *remove_clues(game_state *state, random_state *rs,
+ int diff)
+{
+ int *face_list;
+ int num_faces = state->game_grid->num_faces;
+ game_state *ret = dup_game(state), *saved_ret;
+ int n;
+
+ /* We need to remove some clues. We'll do this by forming a list of all
+ * available clues, shuffling it, then going along one at a
+ * time clearing each clue in turn for which doing so doesn't render the
+ * board unsolvable. */
+ face_list = snewn(num_faces, int);
+ for (n = 0; n < num_faces; ++n) {
+ face_list[n] = n;
+ }
+
+ shuffle(face_list, num_faces, sizeof(int), rs);
+
+ for (n = 0; n < num_faces; ++n) {
+ saved_ret = dup_game(ret);
+ ret->clues[face_list[n]] = -1;
+
+ if (game_has_unique_soln(ret, diff)) {
+ free_game(saved_ret);
+ } else {
+ free_game(ret);
+ ret = saved_ret;
+ }
+ }
+ sfree(face_list);
+
+ return ret;
+}
+
+
+static char *new_game_desc(const game_params *params, random_state *rs,
+ char **aux, int interactive)
+{
+ /* solution and description both use run-length encoding in obvious ways */
+ char *retval, *game_desc, *grid_desc;
+ grid *g;
+ game_state *state = snew(game_state);
+ game_state *state_new;
+
+ grid_desc = grid_new_desc(grid_types[params->type], params->w, params->h, rs);
+ state->game_grid = g = loopy_generate_grid(params, grid_desc);
+
+ state->clues = snewn(g->num_faces, signed char);
+ state->lines = snewn(g->num_edges, char);
+ state->line_errors = snewn(g->num_edges, unsigned char);
+ state->exactly_one_loop = FALSE;
+
+ state->grid_type = params->type;
+
+ newboard_please:
+
+ memset(state->lines, LINE_UNKNOWN, g->num_edges);
+ memset(state->line_errors, 0, g->num_edges);
+
+ state->solved = state->cheated = FALSE;
+
+ /* Get a new random solvable board with all its clues filled in. Yes, this
+ * can loop for ever if the params are suitably unfavourable, but
+ * preventing games smaller than 4x4 seems to stop this happening */
+ do {
+ add_full_clues(state, rs);
+ } while (!game_has_unique_soln(state, params->diff));
+
+ state_new = remove_clues(state, rs, params->diff);
+ free_game(state);
+ state = state_new;
+
+
+ if (params->diff > 0 && game_has_unique_soln(state, params->diff-1)) {
+#ifdef SHOW_WORKING
+ fprintf(stderr, "Rejecting board, it is too easy\n");
+#endif
+ goto newboard_please;
+ }
+
+ game_desc = state_to_text(state);
+
+ free_game(state);
+
+ if (grid_desc) {
+ retval = snewn(strlen(grid_desc) + 1 + strlen(game_desc) + 1, char);
+ sprintf(retval, "%s%c%s", grid_desc, (int)GRID_DESC_SEP, game_desc);
+ sfree(grid_desc);
+ sfree(game_desc);
+ } else {
+ retval = game_desc;
+ }
+
+ assert(!validate_desc(params, retval));
+
+ return retval;
+}
+
+static game_state *new_game(midend *me, const game_params *params,
+ const char *desc)
+{
+ int i;
+ game_state *state = snew(game_state);
+ int empties_to_make = 0;
+ int n,n2;
+ const char *dp;
+ char *grid_desc;
+ grid *g;
+ int num_faces, num_edges;
+
+ grid_desc = extract_grid_desc(&desc);
+ state->game_grid = g = loopy_generate_grid(params, grid_desc);
+ if (grid_desc) sfree(grid_desc);
+
+ dp = desc;
+
+ num_faces = g->num_faces;
+ num_edges = g->num_edges;
+
+ state->clues = snewn(num_faces, signed char);
+ state->lines = snewn(num_edges, char);
+ state->line_errors = snewn(num_edges, unsigned char);
+ state->exactly_one_loop = FALSE;
+
+ state->solved = state->cheated = FALSE;
+
+ state->grid_type = params->type;
+
+ for (i = 0; i < num_faces; i++) {
+ if (empties_to_make) {
+ empties_to_make--;
+ state->clues[i] = -1;
+ continue;
+ }
+
+ assert(*dp);
+ n = *dp - '0';
+ n2 = *dp - 'A' + 10;
+ if (n >= 0 && n < 10) {
+ state->clues[i] = n;
+ } else if (n2 >= 10 && n2 < 36) {
+ state->clues[i] = n2;
+ } else {
+ n = *dp - 'a' + 1;
+ assert(n > 0);
+ state->clues[i] = -1;
+ empties_to_make = n - 1;
+ }
+ ++dp;
+ }
+
+ memset(state->lines, LINE_UNKNOWN, num_edges);
+ memset(state->line_errors, 0, num_edges);
+ return state;
+}
+
+/* Calculates the line_errors data, and checks if the current state is a
+ * solution */
+static int check_completion(game_state *state)
+{
+ grid *g = state->game_grid;
+ int i, ret;
+ int *dsf, *component_state;
+ int nsilly, nloop, npath, largest_comp, largest_size, total_pathsize;
+ enum { COMP_NONE, COMP_LOOP, COMP_PATH, COMP_SILLY, COMP_EMPTY };
+
+ memset(state->line_errors, 0, g->num_edges);
+
+ /*
+ * Find loops in the grid, and determine whether the puzzle is
+ * solved.
+ *
+ * Loopy is a bit more complicated than most puzzles that care
+ * about loop detection. In most of them, loops are simply
+ * _forbidden_; so the obviously right way to do
+ * error-highlighting during play is to light up a graph edge red
+ * iff it is part of a loop, which is exactly what the centralised
+ * findloop.c makes easy.
+ *
+ * But Loopy is unusual in that you're _supposed_ to be making a
+ * loop - and yet _some_ loops are not the right loop. So we need
+ * to be more discriminating, by identifying loops one by one and
+ * then thinking about which ones to highlight, and so findloop.c
+ * isn't quite the right tool for the job in this case.
+ *
+ * Worse still, consider situations in which the grid contains a
+ * loop and also some non-loop edges: there are some cases like
+ * this in which the user's intuitive expectation would be to
+ * highlight the loop (if you're only about half way through the
+ * puzzle and have accidentally made a little loop in some corner
+ * of the grid), and others in which they'd be more likely to
+ * expect you to highlight the non-loop edges (if you've just
+ * closed off a whole loop that you thought was the entire
+ * solution, but forgot some disconnected edges in a corner
+ * somewhere). So while it's easy enough to check whether the
+ * solution is _right_, highlighting the wrong parts is a tricky
+ * problem for this puzzle!
+ *
+ * I'd quite like, in some situations, to identify the largest
+ * loop among the player's YES edges, and then light up everything
+ * other than that. But finding the longest cycle in a graph is an
+ * NP-complete problem (because, in particular, it must return a
+ * Hamilton cycle if one exists).
+ *
+ * However, I think we can make the problem tractable by
+ * exercising the Puzzles principle that it isn't absolutely
+ * necessary to highlight _all_ errors: the key point is that by
+ * the time the user has filled in the whole grid, they should
+ * either have seen a completion flash, or have _some_ error
+ * highlight showing them why the solution isn't right. So in
+ * principle it would be *just about* good enough to highlight
+ * just one error in the whole grid, if there was really no better
+ * way. But we'd like to highlight as many errors as possible.
+ *
+ * In this case, I think the simple approach is to make use of the
+ * fact that no vertex may have degree > 2, and that's really
+ * simple to detect. So the plan goes like this:
+ *
+ * - Form the dsf of connected components of the graph vertices.
+ *
+ * - Highlight an error at any vertex with degree > 2. (It so
+ * happens that we do this by lighting up all the edges
+ * incident to that vertex, but that's an output detail.)
+ *
+ * - Any component that contains such a vertex is now excluded
+ * from further consideration, because it already has a
+ * highlight.
+ *
+ * - The remaining components have no vertex with degree > 2, and
+ * hence they all consist of either a simple loop, or a simple
+ * path with two endpoints.
+ *
+ * - For these purposes, group together all the paths and imagine
+ * them to be a single component (because in most normal
+ * situations the player will gradually build up the solution
+ * _not_ all in one connected segment, but as lots of separate
+ * little path pieces that gradually connect to each other).
+ *
+ * - After doing that, if there is exactly one (sensible)
+ * component - be it a collection of paths or a loop - then
+ * highlight no further edge errors. (The former case is normal
+ * during play, and the latter is a potentially solved puzzle.)
+ *
+ * - Otherwise, find the largest of the sensible components,
+ * leave that one unhighlighted, and light the rest up in red.
+ */
+
+ dsf = snew_dsf(g->num_dots);
+
+ /* Build the dsf. */
+ for (i = 0; i < g->num_edges; i++) {
+ if (state->lines[i] == LINE_YES) {
+ grid_edge *e = g->edges + i;
+ int d1 = e->dot1 - g->dots, d2 = e->dot2 - g->dots;
+ dsf_merge(dsf, d1, d2);
+ }
+ }
+
+ /* Initialise a state variable for each connected component. */
+ component_state = snewn(g->num_dots, int);
+ for (i = 0; i < g->num_dots; i++) {
+ if (dsf_canonify(dsf, i) == i)
+ component_state[i] = COMP_LOOP;
+ else
+ component_state[i] = COMP_NONE;
+ }
+
+ /* Check for dots with degree > 3. Here we also spot dots of
+ * degree 1 in which the user has marked all the non-edges as
+ * LINE_NO, because those are also clear vertex-level errors, so
+ * we give them the same treatment of excluding their connected
+ * component from the subsequent loop analysis. */
+ for (i = 0; i < g->num_dots; i++) {
+ int comp = dsf_canonify(dsf, i);
+ int yes = dot_order(state, i, LINE_YES);
+ int unknown = dot_order(state, i, LINE_UNKNOWN);
+ if ((yes == 1 && unknown == 0) || (yes >= 3)) {
+ /* violation, so mark all YES edges as errors */
+ grid_dot *d = g->dots + i;
+ int j;
+ for (j = 0; j < d->order; j++) {
+ int e = d->edges[j] - g->edges;
+ if (state->lines[e] == LINE_YES)
+ state->line_errors[e] = TRUE;
+ }
+ /* And mark this component as not worthy of further
+ * consideration. */
+ component_state[comp] = COMP_SILLY;
+
+ } else if (yes == 0) {
+ /* A completely isolated dot must also be excluded it from
+ * the subsequent loop highlighting pass, but we tag it
+ * with a different enum value to avoid it counting
+ * towards the components that inhibit returning a win
+ * status. */
+ component_state[comp] = COMP_EMPTY;
+ } else if (yes == 1) {
+ /* A dot with degree 1 that didn't fall into the 'clearly
+ * erroneous' case above indicates that this connected
+ * component will be a path rather than a loop - unless
+ * something worse elsewhere in the component has
+ * classified it as silly. */
+ if (component_state[comp] != COMP_SILLY)
+ component_state[comp] = COMP_PATH;
+ }
+ }
+
+ /* Count up the components. Also, find the largest sensible
+ * component. (Tie-breaking condition is derived from the order of
+ * vertices in the grid data structure, which is fairly arbitrary
+ * but at least stays stable throughout the game.) */
+ nsilly = nloop = npath = 0;
+ total_pathsize = 0;
+ largest_comp = largest_size = -1;
+ for (i = 0; i < g->num_dots; i++) {
+ if (component_state[i] == COMP_SILLY) {
+ nsilly++;
+ } else if (component_state[i] == COMP_PATH) {
+ total_pathsize += dsf_size(dsf, i);
+ npath = 1;
+ } else if (component_state[i] == COMP_LOOP) {
+ int this_size;
+
+ nloop++;
+
+ if ((this_size = dsf_size(dsf, i)) > largest_size) {
+ largest_comp = i;
+ largest_size = this_size;
+ }
+ }
+ }
+ if (largest_size < total_pathsize) {
+ largest_comp = -1; /* means the paths */
+ largest_size = total_pathsize;
+ }
+
+ if (nloop > 0 && nloop + npath > 1) {
+ /*
+ * If there are at least two sensible components including at
+ * least one loop, highlight all edges in every sensible
+ * component that is not the largest one.
+ */
+ for (i = 0; i < g->num_edges; i++) {
+ if (state->lines[i] == LINE_YES) {
+ grid_edge *e = g->edges + i;
+ int d1 = e->dot1 - g->dots; /* either endpoint is good enough */
+ int comp = dsf_canonify(dsf, d1);
+ if ((component_state[comp] == COMP_PATH &&
+ -1 != largest_comp) ||
+ (component_state[comp] == COMP_LOOP &&
+ comp != largest_comp))
+ state->line_errors[i] = TRUE;
+ }
+ }
+ }
+
+ if (nloop == 1 && npath == 0 && nsilly == 0) {
+ /*
+ * If there is exactly one component and it is a loop, then
+ * the puzzle is potentially complete, so check the clues.
+ */
+ ret = TRUE;
+
+ for (i = 0; i < g->num_faces; i++) {
+ int c = state->clues[i];
+ if (c >= 0 && face_order(state, i, LINE_YES) != c) {
+ ret = FALSE;
+ break;
+ }
+ }
+
+ /*
+ * Also, whether or not the puzzle is actually complete, set
+ * the flag that says this game_state has exactly one loop and
+ * nothing else, which will be used to vary the semantics of
+ * clue highlighting at display time.
+ */
+ state->exactly_one_loop = TRUE;
+ } else {
+ ret = FALSE;
+ state->exactly_one_loop = FALSE;
+ }
+
+ sfree(component_state);
+ sfree(dsf);
+
+ return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Solver logic
+ *
+ * Our solver modes operate as follows. Each mode also uses the modes above it.
+ *
+ * Easy Mode
+ * Just implement the rules of the game.
+ *
+ * Normal and Tricky Modes
+ * 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
+ * know both or neither is on that's already stored more directly.)
+ *
+ * Advanced Mode
+ * Use edsf data structure to make equivalence classes of lines that are
+ * known identical to or opposite to one another.
+ */
+
+
+/* DLines:
+ * For general grids, we consider "dlines" to be pairs of lines joined
+ * at a dot. The lines must be adjacent around the dot, so we can think of
+ * a dline as being a dot+face combination. Or, a dot+edge combination where
+ * the second edge is taken to be the next clockwise edge from the dot.
+ * Original loopy code didn't have this extra restriction of the lines being
+ * adjacent. From my tests with square grids, this extra restriction seems to
+ * take little, if anything, away from the quality of the puzzles.
+ * A dline can be uniquely identified by an edge/dot combination, given that
+ * a dline-pair always goes clockwise around its common dot. The edge/dot
+ * combination can be represented by an edge/bool combination - if bool is
+ * TRUE, use edge->dot1 else use edge->dot2. So the total number of dlines is
+ * exactly twice the number of edges in the grid - although the dlines
+ * spanning the infinite face are not all that useful to the solver.
+ * Note that, by convention, a dline goes clockwise around its common dot,
+ * which means the dline goes anti-clockwise around its common face.
+ */
+
+/* Helper functions for obtaining an index into an array of dlines, given
+ * various information. We assume the grid layout conventions about how
+ * the various lists are interleaved - see grid_make_consistent() for
+ * details. */
+
+/* i points to the first edge of the dline pair, reading clockwise around
+ * the dot. */
+static int dline_index_from_dot(grid *g, grid_dot *d, int i)
+{
+ grid_edge *e = d->edges[i];
+ int ret;
+#ifdef DEBUG_DLINES
+ grid_edge *e2;
+ int i2 = i+1;
+ if (i2 == d->order) i2 = 0;
+ e2 = d->edges[i2];
+#endif
+ ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0);
+#ifdef DEBUG_DLINES
+ printf("dline_index_from_dot: d=%d,i=%d, edges [%d,%d] - %d\n",
+ (int)(d - g->dots), i, (int)(e - g->edges),
+ (int)(e2 - g->edges), ret);
+#endif
+ return ret;
+}
+/* i points to the second edge of the dline pair, reading clockwise around
+ * the face. That is, the edges of the dline, starting at edge{i}, read
+ * anti-clockwise around the face. By layout conventions, the common dot
+ * of the dline will be f->dots[i] */
+static int dline_index_from_face(grid *g, grid_face *f, int i)
+{
+ grid_edge *e = f->edges[i];
+ grid_dot *d = f->dots[i];
+ int ret;
+#ifdef DEBUG_DLINES
+ grid_edge *e2;
+ int i2 = i - 1;
+ if (i2 < 0) i2 += f->order;
+ e2 = f->edges[i2];
+#endif
+ ret = 2 * (e - g->edges) + ((e->dot1 == d) ? 1 : 0);
+#ifdef DEBUG_DLINES
+ printf("dline_index_from_face: f=%d,i=%d, edges [%d,%d] - %d\n",
+ (int)(f - g->faces), i, (int)(e - g->edges),
+ (int)(e2 - g->edges), ret);
+#endif
+ return ret;
+}
+static int is_atleastone(const char *dline_array, int index)
+{
+ return BIT_SET(dline_array[index], 0);
+}
+static int set_atleastone(char *dline_array, int index)
+{
+ return SET_BIT(dline_array[index], 0);
+}
+static int is_atmostone(const char *dline_array, int index)
+{
+ return BIT_SET(dline_array[index], 1);
+}
+static int set_atmostone(char *dline_array, int index)
+{
+ return SET_BIT(dline_array[index], 1);
+}
+
+static void array_setall(char *array, char from, char to, int len)
+{
+ char *p = array, *p_old = p;
+ int len_remaining = len;
+
+ while ((p = memchr(p, from, len_remaining))) {
+ *p = to;
+ len_remaining -= p - p_old;
+ p_old = p;
+ }
+}
+
+/* Helper, called when doing dline dot deductions, in the case where we
+ * have 4 UNKNOWNs, and two of them (adjacent) have *exactly* one YES between
+ * them (because of dline atmostone/atleastone).
+ * On entry, edge points to the first of these two UNKNOWNs. This function
+ * will find the opposite UNKNOWNS (if they are adjacent to one another)
+ * and set their corresponding dline to atleastone. (Setting atmostone
+ * already happens in earlier dline deductions) */
+static int dline_set_opp_atleastone(solver_state *sstate,
+ grid_dot *d, int edge)
+{
+ game_state *state = sstate->state;
+ grid *g = state->game_grid;
+ int N = d->order;
+ int opp, opp2;
+ for (opp = 0; opp < N; opp++) {
+ int opp_dline_index;
+ if (opp == edge || opp == edge+1 || opp == edge-1)
+ continue;
+ if (opp == 0 && edge == N-1)
+ continue;
+ if (opp == N-1 && edge == 0)
+ continue;
+ opp2 = opp + 1;
+ if (opp2 == N) opp2 = 0;
+ /* Check if opp, opp2 point to LINE_UNKNOWNs */
+ if (state->lines[d->edges[opp] - g->edges] != LINE_UNKNOWN)
+ continue;
+ if (state->lines[d->edges[opp2] - g->edges] != LINE_UNKNOWN)
+ continue;
+ /* Found opposite UNKNOWNS and they're next to each other */
+ opp_dline_index = dline_index_from_dot(g, d, opp);
+ return set_atleastone(sstate->dlines, opp_dline_index);
+ }
+ return FALSE;
+}
+
+
+/* Set pairs of lines around this face which are known to be identical, to
+ * the given line_state */
+static int face_setall_identical(solver_state *sstate, int face_index,
+ enum line_state line_new)