Tracks: Highlight more counting errors if track looks good

Normally, Tracks puts error marks on row and column clues only when one
of the existing track or no-track marks will have to be removed to
satisfy the clue.  This could lead to a situation where the player had
built a track from A to B and got neither a win nor a highlighted error
because the only error was in a row or column having too few track
segments.

This commit re-arranges the completion checking so that if there's a
complete track from A to B and no spurious track then the game will
highlight any clue that isn't matched by the actually laid track.  This
should mean that any solution with a track from A to B will either be a
win or have a highlighted error.

This should fix Android issue #266.
This commit is contained in:
Ben Harris
2022-12-27 16:14:48 +00:00
parent 4ec2c58045
commit 99d3c31e12

118
tracks.c
View File

@ -1823,7 +1823,7 @@ static int tracks_neighbour(int vertex, void *vctx)
static bool check_completion(game_state *state, bool mark) static bool check_completion(game_state *state, bool mark)
{ {
int w = state->p.w, h = state->p.h, x, y, i, target; int w = state->p.w, h = state->p.h, x, y, i, target;
bool ret = true; bool ret = true, pathret;
int ntrack, nnotrack, ntrackcomplete; int ntrack, nnotrack, ntrackcomplete;
int *dsf, pathclass; int *dsf, pathclass;
struct findloopstate *fls; struct findloopstate *fls;
@ -1844,58 +1844,6 @@ static bool check_completion(game_state *state, bool mark)
} }
} }
/* A cell is 'complete', for the purposes of marking the game as
* finished, if it has two edges marked as TRACK. But it only has
* to have one edge marked as TRACK, or be filled in as trackful
* without any specific edges known, to count towards checking
* row/column clue errors. */
for (x = 0; x < w; x++) {
target = state->numbers->numbers[x];
ntrack = nnotrack = ntrackcomplete = 0;
for (y = 0; y < h; y++) {
if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
state->sflags[y*w+x] & S_TRACK)
ntrack++;
if (S_E_COUNT(state, x, y, E_TRACK) == 2)
ntrackcomplete++;
if (state->sflags[y*w+x] & S_NOTRACK)
nnotrack++;
}
if (mark) {
if (ntrack > target || nnotrack > (h-target)) {
debug(("col %d error: target %d, track %d, notrack %d",
x, target, ntrack, nnotrack));
state->num_errors[x] = 1;
ret = false;
}
}
if (ntrackcomplete != target)
ret = false;
}
for (y = 0; y < h; y++) {
target = state->numbers->numbers[w+y];
ntrack = nnotrack = ntrackcomplete = 0;
for (x = 0; x < w; x++) {
if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
state->sflags[y*w+x] & S_TRACK)
ntrack++;
if (S_E_COUNT(state, x, y, E_TRACK) == 2)
ntrackcomplete++;
if (state->sflags[y*w+x] & S_NOTRACK)
nnotrack++;
}
if (mark) {
if (ntrack > target || nnotrack > (w-target)) {
debug(("row %d error: target %d, track %d, notrack %d",
y, target, ntrack, nnotrack));
state->num_errors[w+y] = 1;
ret = false;
}
}
if (ntrackcomplete != target)
ret = false;
}
dsf = snewn(w*h, int); dsf = snewn(w*h, int);
dsf_init(dsf, w*h); dsf_init(dsf, w*h);
@ -1949,6 +1897,70 @@ static bool check_completion(game_state *state, bool mark)
} }
} }
/*
* A cell is 'complete', for the purposes of marking the game as
* finished, if it has two edges marked as TRACK. But it only has
* to have one edge marked as TRACK, or be filled in as trackful
* without any specific edges known, to count towards checking
* row/column clue errors.
*
* This changes if we haven't found any other errors by this
* point, so the player has constructed a route from A to B. In
* that case, we highlight any row/column where the actually laid
* tracks don't match the clue.
*/
pathret = ret; /* Do we have a plausible solution so far? */
for (x = 0; x < w; x++) {
target = state->numbers->numbers[x];
ntrack = nnotrack = ntrackcomplete = 0;
for (y = 0; y < h; y++) {
if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
state->sflags[y*w+x] & S_TRACK)
ntrack++;
if (S_E_COUNT(state, x, y, E_TRACK) == 2)
ntrackcomplete++;
if (state->sflags[y*w+x] & S_NOTRACK)
nnotrack++;
}
if (mark) {
if (ntrack > target || nnotrack > (h-target) ||
(pathret && ntrackcomplete != target)) {
debug(("col %d error: target %d, track %d, notrack %d, "
"pathret %d, trackcomplete %d",
x, target, ntrack, nnotrack, pathret, ntrackcomplete));
state->num_errors[x] = 1;
ret = false;
}
}
if (ntrackcomplete != target)
ret = false;
}
for (y = 0; y < h; y++) {
target = state->numbers->numbers[w+y];
ntrack = nnotrack = ntrackcomplete = 0;
for (x = 0; x < w; x++) {
if (S_E_COUNT(state, x, y, E_TRACK) > 0 ||
state->sflags[y*w+x] & S_TRACK)
ntrack++;
if (S_E_COUNT(state, x, y, E_TRACK) == 2)
ntrackcomplete++;
if (state->sflags[y*w+x] & S_NOTRACK)
nnotrack++;
}
if (mark) {
if (ntrack > target || nnotrack > (w-target) ||
(pathret && ntrackcomplete != target)) {
debug(("row %d error: target %d, track %d, notrack %d, "
"pathret %d, trackcomplete %d",
y, target, ntrack, nnotrack, pathret, ntrackcomplete));
state->num_errors[w+y] = 1;
ret = false;
}
}
if (ntrackcomplete != target)
ret = false;
}
if (mark) if (mark)
state->completed = ret; state->completed = ret;
sfree(dsf); sfree(dsf);