From: Pasi Kallinen Date: Sat, 26 Mar 2022 21:58:25 +0000 (+0200) Subject: Fix travel getting stuck oscillating between two locations X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1d78d3c3f27d986e24b925a709a6c6d8afc1a2e0;p=nethack Fix travel getting stuck oscillating between two locations There's been occasional reports (perhaps once or twice a year) of travel getting stuck moving repeatedly between two locations next to each other, but it has never been reproduced before. This special level lua code fragment is a minimal test case which triggers it: --- special level lua fragment, indented des.map([[ --##--- ###---- #-+---- ####--L ]]); des.door("open", 2,2) --- end of special level lua fragment The open door is required. Magic map the level. Start from somewhere NW of the door, and try to travel to the lava pool. Hero will get stuck oscillating between NW of the door and two steps west of the door. Here are the maps of the travel[][] array values from findtravelpath() in those two steps in the above map: ------------- ------------- | . . 2 3 | | . . 1 2 | | 1 1 2 . | | 1 @ 1 . | | @ . . . | | 1 . 2 . | | 1 1 2 . | | 2 . 3 4 | ------------- ------------- There are two possible closest locations to the lava pool, the one marked with "3" on the left map, and "4" on the right map. Based on that alone, both would be valid places to path to. But, in the left case, hero could not see the bottom location, so the code won't even consider pathing to it, so it will start moving towards the "3". When hero moves to the second position, in the right map, now the "4" could be seen. Now there are two possible closest locations we could choose from. The code that scans the possible locations goes from top-left to bottom-right, first going down (y-axis). So, the code sees the "2" on the right. distmin() to there is 2. Good, we pick that location to path to. Next, going down, the code considers the "4" ... which is also equally close to the lava pool. and distmin does not consider terrain, so ignores the door, so it has the same distmin value of "2", so the code picks this location. But: this was just a guess, because there's no known valid path to the lava pool. The code loops back up to rebuild the travel[][] array with a new starting location as the "4" from the right map, pathing back to hero. This is that travel array map: ------------- | . . . . | | 4 @ 3 . | | 3 . 2 . | | 3 2 1 x | ------------- The way travelstepx and travelstepy arrays are built means that the first location that considers the hero's location is the "3" SW of hero, so hero will move there next. Repeat from beginning. If there was no door, the travelsteps would reach hero's location first from SE. (I left the travel[][] array rebuild and travelstepx/travelstrepy build off from the other movement position, as it's not relevant) The fix: When considering which of the two possible closest places to the lava pool to path to, use the one with the lowest value in the travel array. That value is the real number of moves it takes for the hero to walk there, so the code will consistently path to the upper location, as it is "2", instead of considering the "4" below it. Also some minor code reorg, so it considers couldsee first instead of later in two separate places. --- diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 23b5e9c24..d9636aa7c 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -849,6 +849,7 @@ hide-under monsters who can be turned to stone aren't able to hide under a don't stop travel when going past a closed door (eg. when traveling along a room wall) some monster corpses can now convey temporary acid or stoning resistance +fix travel getting stuck oscillating between two locations Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/src/hack.c b/src/hack.c index 53f0efd50..219dda937 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1274,26 +1274,29 @@ findtravelpath(int mode) if (mode == TRAVP_GUESS) { int px = tx, py = ty; /* pick location */ int dist, nxtdist, d2, nd2; + int ctrav, ptrav = COLNO*ROWNO; dist = distmin(ux, uy, tx, ty); d2 = dist2(ux, uy, tx, ty); for (tx = 1; tx < COLNO; ++tx) for (ty = 0; ty < ROWNO; ++ty) - if (travel[tx][ty]) { + if (couldsee(tx, ty) && (ctrav = travel[tx][ty]) > 0) { nxtdist = distmin(ux, uy, tx, ty); - if (nxtdist == dist && couldsee(tx, ty)) { + if (nxtdist == dist && ctrav < ptrav) { nd2 = dist2(ux, uy, tx, ty); if (nd2 < d2) { /* prefer non-zigzag path */ px = tx; py = ty; d2 = nd2; + ptrav = ctrav; } - } else if (nxtdist < dist && couldsee(tx, ty)) { + } else if (nxtdist < dist) { px = tx; py = ty; dist = nxtdist; d2 = dist2(ux, uy, tx, ty); + ptrav = ctrav; } }