Run the final solution-reduction pass in both directions, since

Gareth managed to find an example (10x8#458168771440033 in r6289)
where running it in only one direction failed to eliminate an
obviously redundant piece of path.

[originally from svn r6290]
[r6289 == b25fcc3f2621b0b41f3ae7cdabe57ed07f62d2c2]
This commit is contained in:
Simon Tatham
2005-09-11 14:22:32 +00:00
parent b25fcc3f26
commit 3d3d00991a

274
inertia.c
View File

@ -1160,7 +1160,7 @@ static char *solve_game(game_state *state, game_state *currstate,
} }
} }
#ifdef TSP_DIAGNOSTICS #ifndef TSP_DIAGNOSTICS
printf("before reduction, moves are "); printf("before reduction, moves are ");
x = nodes[circuit[0]] / DP1 % w; x = nodes[circuit[0]] / DP1 % w;
y = nodes[circuit[0]] / DP1 / w; y = nodes[circuit[0]] / DP1 / w;
@ -1198,159 +1198,179 @@ static char *solve_game(game_state *state, game_state *currstate,
*/ */
while (1) { while (1) {
int oldlen = circuitlen; int oldlen = circuitlen;
int dir;
for (i = 0; i < wh; i++) for (dir = +1; dir >= -1; dir -= 2) {
unvisited[i] = 0;
for (i = 0; i < circuitlen; i++) {
int xy = nodes[circuit[i]] / DP1;
if (currstate->grid[xy] == GEM)
unvisited[xy]++;
}
/* for (i = 0; i < wh; i++)
* If there's any gem we didn't end up visiting at all, unvisited[i] = 0;
* give up. for (i = 0; i < circuitlen; i++) {
*/ int xy = nodes[circuit[i]] / DP1;
for (i = 0; i < wh; i++) { if (currstate->grid[xy] == GEM)
if (currstate->grid[i] == GEM && unvisited[i] == 0) { unvisited[xy]++;
err = "Unable to find a solution from this starting point";
break;
} }
}
if (i < wh)
break;
for (i = j = 0; i < circuitlen; i++) { /*
int xy = nodes[circuit[i]] / DP1; * If there's any gem we didn't end up visiting at all,
if (currstate->grid[xy] == GEM && unvisited[xy] > 1) { * give up.
unvisited[xy]--; */
} else if (currstate->grid[xy] == GEM || i == circuitlen-1) { for (i = 0; i < wh; i++) {
/* if (currstate->grid[i] == GEM && unvisited[i] == 0) {
* circuit[i] collects a gem for the only time, or is err = "Unable to find a solution from this starting point";
* the last node in the circuit. Therefore it cannot be break;
* removed; so we now want to replace the path from }
* circuit[j] to circuit[i] with a bfs-shortest path. }
*/ if (i < wh)
int k, dest, ni, ti, thisdist; break;
#ifdef TSP_DIAGNOSTICS for (i = j = (dir > 0 ? 0 : circuitlen-1);
printf("optimising section from %d - %d\n", j, i); i < circuitlen && i >= 0;
i += dir) {
int xy = nodes[circuit[i]] / DP1;
if (currstate->grid[xy] == GEM && unvisited[xy] > 1) {
unvisited[xy]--;
} else if (currstate->grid[xy] == GEM || i == circuitlen-1) {
/*
* circuit[i] collects a gem for the only time,
* or is the last node in the circuit.
* Therefore it cannot be removed; so we now
* want to replace the path from circuit[j] to
* circuit[i] with a bfs-shortest path.
*/
int p, q, k, dest, ni, ti, thisdist;
/*
* Set up the upper and lower bounds of the
* reduced section.
*/
p = min(i, j);
q = max(i, j);
#ifndef TSP_DIAGNOSTICS
printf("optimising section from %d - %d\n", p, q);
#endif #endif
for (k = 0; k < n; k++) for (k = 0; k < n; k++)
dist[k] = -1; dist[k] = -1;
head = tail = 0; head = tail = 0;
dist[circuit[j]] = 0; dist[circuit[p]] = 0;
list[tail++] = circuit[j]; list[tail++] = circuit[p];
while (head < tail && dist[circuit[i]] < 0) { while (head < tail && dist[circuit[q]] < 0) {
int ni = list[head++]; int ni = list[head++];
for (k = edgei[ni]; k < edgei[ni+1]; k++) { for (k = edgei[ni]; k < edgei[ni+1]; k++) {
int ti = edges[k]; int ti = edges[k];
if (ti >= 0 && dist[ti] < 0) { if (ti >= 0 && dist[ti] < 0) {
dist[ti] = dist[ni] + 1; dist[ti] = dist[ni] + 1;
list[tail++] = ti; list[tail++] = ti;
}
} }
} }
}
thisdist = dist[circuit[i]]; thisdist = dist[circuit[q]];
assert(thisdist >= 0 && thisdist <= i-j); assert(thisdist >= 0 && thisdist <= q-p);
memmove(circuit+j+thisdist, circuit+i, memmove(circuit+p+thisdist, circuit+q,
(circuitlen - i) * sizeof(int)); (circuitlen - q) * sizeof(int));
circuitlen -= i-j; circuitlen -= q-p;
i = j + thisdist; q = p + thisdist;
circuitlen += i-j; circuitlen += q-p;
#ifdef TSP_DIAGNOSTICS if (dir > 0)
printf("new section runs from %d - %d\n", j, i); i = q; /* resume loop from the right place */
#ifndef TSP_DIAGNOSTICS
printf("new section runs from %d - %d\n", p, q);
#endif #endif
dest = i; dest = q;
assert(dest >= 0); assert(dest >= 0);
ni = circuit[i]; ni = circuit[q];
while (1) { while (1) {
/* printf("dest=%d circuitlen=%d ni=%d dist[ni]=%d\n", dest, circuitlen, ni, dist[ni]); */ /* printf("dest=%d circuitlen=%d ni=%d dist[ni]=%d\n", dest, circuitlen, ni, dist[ni]); */
circuit[dest] = ni; circuit[dest] = ni;
if (dist[ni] == 0) if (dist[ni] == 0)
break;
dest--;
ti = -1;
for (k = backedgei[ni]; k < backedgei[ni+1]; k++) {
ti = backedges[k];
if (ti >= 0 && dist[ti] == dist[ni] - 1)
break; break;
dest--;
ti = -1;
for (k = backedgei[ni]; k < backedgei[ni+1]; k++) {
ti = backedges[k];
if (ti >= 0 && dist[ti] == dist[ni] - 1)
break;
}
assert(k < backedgei[ni+1] && ti >= 0);
ni = ti;
} }
assert(k < backedgei[ni+1] && ti >= 0);
ni = ti;
}
/* /*
* Now re-increment the visit counts for the new * Now re-increment the visit counts for the
* path. * new path.
*/ */
while (++j < i) { while (++p < q) {
int xy = nodes[circuit[j]] / DP1; int xy = nodes[circuit[p]] / DP1;
if (currstate->grid[xy] == GEM) if (currstate->grid[xy] == GEM)
unvisited[xy]++; unvisited[xy]++;
} }
#ifdef TSP_DIAGNOSTICS j = i;
printf("during reduction, circuit is");
for (k = 0; k < circuitlen; k++) { #ifndef TSP_DIAGNOSTICS
int nc = nodes[circuit[k]]; printf("during reduction, circuit is");
printf(" (%d,%d,%d)", nc/DP1%w, nc/(DP1*w), nc%DP1); for (k = 0; k < circuitlen; k++) {
} int nc = nodes[circuit[k]];
printf("\n"); printf(" (%d,%d,%d)", nc/DP1%w, nc/(DP1*w), nc%DP1);
printf("moves are "); }
x = nodes[circuit[0]] / DP1 % w; printf("\n");
y = nodes[circuit[0]] / DP1 / w; printf("moves are ");
for (k = 1; k < circuitlen; k++) { x = nodes[circuit[0]] / DP1 % w;
int x2, y2, dx, dy; y = nodes[circuit[0]] / DP1 / w;
if (nodes[circuit[k]] % DP1 != DIRECTIONS) for (k = 1; k < circuitlen; k++) {
continue; int x2, y2, dx, dy;
x2 = nodes[circuit[k]] / DP1 % w; if (nodes[circuit[k]] % DP1 != DIRECTIONS)
y2 = nodes[circuit[k]] / DP1 / w; continue;
dx = (x2 > x ? +1 : x2 < x ? -1 : 0); x2 = nodes[circuit[k]] / DP1 % w;
dy = (y2 > y ? +1 : y2 < y ? -1 : 0); y2 = nodes[circuit[k]] / DP1 / w;
for (d = 0; d < DIRECTIONS; d++) dx = (x2 > x ? +1 : x2 < x ? -1 : 0);
if (DX(d) == dx && DY(d) == dy) dy = (y2 > y ? +1 : y2 < y ? -1 : 0);
printf("%c", "89632147"[d]); for (d = 0; d < DIRECTIONS; d++)
x = x2; if (DX(d) == dx && DY(d) == dy)
y = y2; printf("%c", "89632147"[d]);
} x = x2;
printf("\n"); y = y2;
}
printf("\n");
#endif #endif
}
} }
}
#ifdef TSP_DIAGNOSTICS #ifndef TSP_DIAGNOSTICS
printf("after reduction, moves are "); printf("after reduction, moves are ");
x = nodes[circuit[0]] / DP1 % w; x = nodes[circuit[0]] / DP1 % w;
y = nodes[circuit[0]] / DP1 / w; y = nodes[circuit[0]] / DP1 / w;
for (i = 1; i < circuitlen; i++) { for (i = 1; i < circuitlen; i++) {
int x2, y2, dx, dy; int x2, y2, dx, dy;
if (nodes[circuit[i]] % DP1 != DIRECTIONS) if (nodes[circuit[i]] % DP1 != DIRECTIONS)
continue; continue;
x2 = nodes[circuit[i]] / DP1 % w; x2 = nodes[circuit[i]] / DP1 % w;
y2 = nodes[circuit[i]] / DP1 / w; y2 = nodes[circuit[i]] / DP1 / w;
dx = (x2 > x ? +1 : x2 < x ? -1 : 0); dx = (x2 > x ? +1 : x2 < x ? -1 : 0);
dy = (y2 > y ? +1 : y2 < y ? -1 : 0); dy = (y2 > y ? +1 : y2 < y ? -1 : 0);
for (d = 0; d < DIRECTIONS; d++) for (d = 0; d < DIRECTIONS; d++)
if (DX(d) == dx && DY(d) == dy) if (DX(d) == dx && DY(d) == dy)
printf("%c", "89632147"[d]); printf("%c", "89632147"[d]);
x = x2; x = x2;
y = y2; y = y2;
} }
printf("\n"); printf("\n");
#endif #endif
}
/* /*
* If we've managed an entire reduction pass and not made * If we've managed an entire reduction pass in each
* the solution any shorter, we're _really_ done. * direction and not made the solution any shorter, we're
* _really_ done.
*/ */
if (circuitlen == oldlen) if (circuitlen == oldlen)
break; break;