char *lines;
unsigned char *line_errors;
+ int exactly_one_loop;
int solved;
int cheated;
A(Great-Dodecagonal,GRID_GREATDODECAGONAL,2,2) \
A(Penrose (kite/dart),GRID_PENROSE_P2,3,3) \
A(Penrose (rhombs),GRID_PENROSE_P3,3,3)
+ A(Great-Great-Dodecagonal,GRID_GREATGREATDODECAGONAL,2,2) \
#define GRID_NAME(title,type,amin,omin) #title,
#define GRID_CONFIG(title,type,amin,omin) ":" #title
ret->line_errors = snewn(state->game_grid->num_edges, unsigned char);
memcpy(ret->line_errors, state->line_errors, state->game_grid->num_edges);
+ ret->exactly_one_loop = state->exactly_one_loop;
ret->grid_type = state->grid_type;
return ret;
{ 3, 3, DIFF_HARD, 8 },
{ 3, 3, DIFF_HARD, 9 },
{ 3, 3, DIFF_HARD, 10 },
+ { 3, 2, DIFF_HARD, 13 },
{ 6, 6, DIFF_HARD, 11 },
{ 6, 6, DIFF_HARD, 12 },
#else
{ 5, 5, DIFF_HARD, 8 },
{ 5, 4, DIFF_HARD, 9 },
{ 5, 4, DIFF_HARD, 10 },
+ { 5, 3, DIFF_HARD, 13 },
{ 10, 10, DIFF_HARD, 11 },
{ 10, 10, DIFF_HARD, 12 }
#endif
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;
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;
grid *g = state->game_grid;
int i, ret;
int *dsf, *component_state;
- int nsilly, nloop, npath, largest_comp, largest_size;
+ 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);
* hence they all consist of either a simple loop, or a simple
* path with two endpoints.
*
- * - If the sensible components are all paths, or if there's
- * exactly one of them and it is a loop, then highlight no
- * further edge errors. (The former case is normal during play,
- * and the latter is a potentially solved puzzle.)
+ * - 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).
*
- * - Otherwise - if there is more than one sensible component
- * _and_ at least one of them is a loop - find the largest of
- * the sensible components, leave that one unhighlighted, and
- * light the rest up in red.
+ * - 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);
* 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 ||
- component_state[i] == COMP_LOOP) {
+ } 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;
- if (component_state[i] == COMP_PATH)
- npath++;
- else if (component_state[i] == COMP_LOOP)
- nloop++;
+ nloop++;
if ((this_size = dsf_size(dsf, i)) > largest_size) {
largest_comp = i;
}
}
}
+ if (largest_size < total_pathsize) {
+ largest_comp = -1; /* means the paths */
+ largest_size = total_pathsize;
+ }
if (nloop > 0 && nloop + npath > 1) {
/*
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_SILLY &&
- comp != largest_comp)
+ if ((component_state[comp] == COMP_PATH &&
+ -1 != largest_comp) ||
+ (component_state[comp] == COMP_LOOP &&
+ comp != largest_comp))
state->line_errors[i] = TRUE;
}
}
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);
for (i = 0; i < g->num_faces; i++) {
grid_face *f = g->faces + i;
int sides = f->order;
+ int yes_order, no_order;
int clue_mistake;
int clue_satisfied;
int n = state->clues[i];
if (n < 0)
continue;
- clue_mistake = (face_order(state, i, LINE_YES) > n ||
- face_order(state, i, LINE_NO ) > (sides-n));
- clue_satisfied = (face_order(state, i, LINE_YES) == n &&
- face_order(state, i, LINE_NO ) == (sides-n));
+ yes_order = face_order(state, i, LINE_YES);
+ if (state->exactly_one_loop) {
+ /*
+ * Special case: if the set of LINE_YES edges in the grid
+ * consists of exactly one loop and nothing else, then we
+ * switch to treating LINE_UNKNOWN the same as LINE_NO for
+ * purposes of clue checking.
+ *
+ * This is because some people like to play Loopy without
+ * using the right-click, i.e. never setting anything to
+ * LINE_NO. Without this special case, if a person playing
+ * in that style fills in what they think is a correct
+ * solution loop but in fact it has an underfilled clue,
+ * then we will display no victory flash and also no error
+ * highlight explaining why not. With this special case,
+ * we light up underfilled clues at the instant the loop
+ * is closed. (Of course, *overfilled* clues are fine
+ * either way.)
+ *
+ * (It might still be considered unfortunate that we can't
+ * warn this style of player any earlier, if they make a
+ * mistake very near the beginning which doesn't show up
+ * until they close the last edge of the loop. One other
+ * thing we _could_ do here is to treat any LINE_UNKNOWN
+ * as LINE_NO if either of its endpoints has yes-degree 2,
+ * reflecting the fact that setting that line to YES would
+ * be an obvious error. But I don't think even that could
+ * catch _all_ clue errors in a timely manner; I think
+ * there are some that won't be displayed until the loop
+ * is filled in, even so, and there's no way to avoid that
+ * with complete reliability except to switch to being a
+ * player who sets things to LINE_NO.)
+ */
+ no_order = sides - yes_order;
+ } else {
+ no_order = face_order(state, i, LINE_NO);
+ }
+
+ clue_mistake = (yes_order > n || no_order > (sides-n));
+ clue_satisfied = (yes_order == n && no_order == (sides-n));
if (clue_mistake != ds->clue_error[i] ||
clue_satisfied != ds->clue_satisfied[i]) {