From 3ed1aedeb73e82e7339e06690524b22c2c79fc78 Mon Sep 17 00:00:00 2001
From: Tung Nguyen <tungtn3@gmail.com>
Date: Fri, 11 Mar 2016 00:24:40 +1100
Subject: [PATCH] Fix travel moving player back and forth infinitely

This fixes a bug where hundreds of turns are wasted by the travel system
moving the player back and forth when the player targets an unreachable
space and sight-blocking obstacles occur in certain formations
in-between.  The player will only be stopped if they're interrupted
externally, e.g. growing hungry or being hit by a monster.  See the
comment in the code for full details.

Based on DynaHack commit 02da53e (Fix travel moving player back and
forth repeatedly) by me.
---
 src/hack.c | 37 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/src/hack.c b/src/hack.c
index 6ce28907a..0669978cb 100644
--- a/src/hack.c
+++ b/src/hack.c
@@ -929,7 +929,42 @@ boolean guess;
                     int nx = x + xdir[ordered[dir]];
                     int ny = y + ydir[ordered[dir]];
 
-                    if (!isok(nx, ny))
+                    /*
+                     * When guessing and trying to travel as close as possible
+                     * to an unreachable target space, don't include spaces
+                     * that would never be picked as a guessed target in the
+                     * travel matrix describing player-reachable spaces.
+                     * This stops travel from getting confused and moving the
+                     * player back and forth in certain degenerate configurations
+                     * of sight-blocking obstacles, e.g.
+                     *
+                     *    T         1. Dig this out and carry enough to not be
+                     *      ####       able to squeeze through diagonal gaps.
+                     *      #--.---    Stand at @ and target travel at space T.
+                     *       @.....
+                     *       |.....
+                     *
+                     *    T         2. couldsee() marks spaces marked a and x as
+                     *      ####       eligible guess spaces to move the player
+                     *      a--.---    towards.  Space a is closest to T, so it gets
+                     *       @xxxxx    chosen.  Travel system moves @ right to travel
+                     *       |xxxxx    to space a.
+                     *
+                     *    T         3. couldsee() marks spaces marked b, c and x
+                     *      ####       as eligible guess spaces to move the player
+                     *      a--c---    towards.  Since findtravelpath() is called
+                     *       b@xxxx    repeatedly during travel, it doesn't remember
+                     *       |xxxxx    that it wanted to go to space a, so in
+                     *                 comparing spaces b and c, b is chosen, since
+                     *                 it seems like the closest eligible space to T.
+                     *                 Travel system moves @ left to go to space b.
+                     *
+                     *              4. Go to 2.
+                     *
+                     * By limiting the travel matrix here, space a in the example
+                     * above is never included in it, preventing the cycle.
+                     */
+                    if (!isok(nx, ny) || (guess && !couldsee(nx, ny)))
                         continue;
                     if ((!Passes_walls && !can_ooze(&youmonst)
                          && closed_door(x, y)) || sobj_at(BOULDER, x, y)
-- 
2.40.0