001package squidpony.squidai;
002
003import squidpony.squidgrid.Measurement;
004import squidpony.squidgrid.Adjacency;
005import squidpony.squidgrid.Adjacency.BasicAdjacency;
006import squidpony.squidgrid.Adjacency.RotationAdjacency;
007import squidpony.squidgrid.Radius;
008import squidpony.squidmath.*;
009
010import java.io.Serializable;
011
012/**
013 * An alternative to AStarSearch when you want to fully explore a search space, or when you want a gradient
014 * floodfill, with customizable rules for what is considered adjacent. This can be used for games where
015 * rotation matters (and possibly costs movement), for games with thin walls (where a wall between cells
016 * prevents travel between those two cells even if the wall doesn't occupy a walkable cell), for games where
017 * the edges between cells may have some requisite to travel across, like a vertical amount that must be
018 * hopped up or down between cells, and for games that have portals between distant cells on the same map.
019 * <br>
020 * As a bit of introduction, the article http://www.roguebasin.com/index.php?title=Dijkstra_Maps_Visualized can
021 * provide some useful information on how these work and how to visualize the information they can produce, while
022 * http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps is an inspiring list of the
023 * various features Dijkstra Maps can enable.
024 * <br>
025 * If you can't remember how to spell this, just remember: Does It Just Know Stuff? That's Really Awesome!
026 * Created by Tommy Ettinger on 4/4/2015.
027 */
028public class CustomDijkstraMap implements Serializable {
029    private static final long serialVersionUID = -2456306898212944440L;
030
031    /**
032     * The main factor in determining the "Custom" behavior of CustomDijkstraMap; using an Adjacency implementation like
033     * {@link BasicAdjacency} should cause this class to mimic {@link DijkstraMap}, but using
034     * {@link RotationAdjacency} will be very different.
035     */
036    public Adjacency adjacency;
037
038    /**
039     * Stores which parts of the map are accessible and which are not. Should not be changed unless the actual physical
040     * terrain has changed. You should call initialize() with a new map instead of changing this directly.
041     */
042    public double[] physicalMap;
043    /**
044     * The frequently-changing values that are often the point of using this class; goals will have a value of 0, and
045     * any cells that can have a character reach a goal in n steps will have a value of n. Cells that cannot be
046     * entered because they are solid will have a very high value equal to the WALL constant in this class, and cells
047     * that cannot be entered because they cannot reach a goal will have a different very high value equal to the
048     * DARK constant in this class.
049     */
050    public double[] gradientMap;
051    /**
052     * This stores the type of each cell for the purposes of determining its cost to enter; in most cases this type is
053     * the char used to represent the cell, but any int can be used if you need more information. An int from costMap
054     * is looked up in {@link Adjacency#costRules} to get the actual cost as a double; this collection should almost
055     * always start with a reasonable default value for when the int key is not present. It's common to simply assign
056     * a char like '#' or '.' to an element in costMap.
057     */
058    public int[] costMap;
059
060    /**
061     * The neighbors map, as produced by adjacency; can be modified by passing neighbors as the first argument to
062     * {@link Adjacency#portal(int[][][], int, int, boolean)} if you want to create portals between non-adjacent cells.
063     */
064    public int[][][] neighbors;
065    /**
066     * Height of the map. Exciting stuff. Don't change this, instead call initialize().
067     */
068    public int height;
069    /**
070     * Width of the map. Exciting stuff. Don't change this, instead call initialize().
071     */
072    public int width;
073    /**
074     * The latest path that was obtained by calling findPath(). It will not contain the value passed as a starting
075     * cell; only steps that require movement will be included, and so if the path has not been found or a valid
076     * path toward a goal is impossible, this ArrayList will be empty.
077     */
078    public IntVLA path;
079    /**
080     * Goals are always marked with 0.
081     */
082    public static final double GOAL = 0;
083    /**
084     * Floor cells, which include any walkable cell, are marked with a high number equal to 999200 .
085     */
086    public static final double FLOOR = 999200;
087    /**
088     * Walls, which are solid no-entry cells, are marked with a high number equal to 999500 .
089     */
090    public static final double WALL = 999500;
091    /**
092     * This is used to mark cells that the scan couldn't reach, and these dark cells are marked with a high number
093     * equal to 999800 .
094     */
095    public static final double DARK = 999800;
096
097    protected IntVLA goals = new IntVLA(256), fresh = new IntVLA(256);
098
099    /**
100     * The RNG used to decide which one of multiple equally-short paths to take.
101     */
102    public IRNG rng;
103    private int frustration;
104
105    private int[] reuse = new int[9];
106
107    private boolean initialized;
108
109    private int mappedCount;
110    private double[] heuristics;
111
112    /**
113     * Construct a CustomDijkstraMap without a level to actually scan. If you use this constructor, you must call an
114     * initialize() method before using this class.
115     */
116    public CustomDijkstraMap() {
117        rng = new GWTRNG();
118        path = new IntVLA();
119    }
120
121    /**
122     * Construct a CustomDijkstraMap without a level to actually scan. This constructor allows you to specify an RNG before
123     * it is ever used in this class. If you use this constructor, you must call an initialize() method before using
124     * any other methods in the class.
125     */
126    public CustomDijkstraMap(IRNG random) {
127        rng = random;
128        path = new IntVLA();
129    }
130
131    /**
132     * Used to construct a CustomDijkstraMap from the output of another.
133     *
134     * @param level
135     */
136    public CustomDijkstraMap(final double[] level, int width, int height) {
137        this(level, new BasicAdjacency(width, height, Measurement.MANHATTAN));
138    }
139
140    /**
141     * Used to construct a CustomDijkstraMap from the output of another, specifying a distance calculation.
142     *
143     * @param level
144     * @param adjacency
145     */
146    public CustomDijkstraMap(final double[] level, Adjacency adjacency) {
147        rng = new GWTRNG();
148        this.adjacency = adjacency;
149        path = new IntVLA();
150
151        initialize(level);
152    }
153
154    /**
155     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
156     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
157     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
158     * map that can be used here.
159     *
160     * @param level
161     */
162    public CustomDijkstraMap(final char[][] level) {
163        this(level, new BasicAdjacency(level.length, level[0].length, Measurement.MANHATTAN), new GWTRNG());
164    }
165
166    /**
167     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
168     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
169     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
170     * map that can be used here. Also takes an RNG that ensures predictable path choices given
171     * otherwise identical inputs and circumstances.
172     *
173     * @param level
174     * @param rng   The RNG to use for certain decisions; only affects find* methods like findPath, not scan.
175     */
176    public CustomDijkstraMap(final char[][] level, IRNG rng) {
177        this(level, new BasicAdjacency(level.length, level[0].length, Measurement.MANHATTAN), rng);
178    }
179
180    /**
181     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
182     * char[][] where one char means a wall and anything else is a walkable tile. If you only have
183     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
184     * map that can be used here. You can specify the character used for walls.
185     *
186     * @param level
187     */
188    public CustomDijkstraMap(final char[][] level, char alternateWall) {
189        rng = new GWTRNG();
190        path = new IntVLA();
191        adjacency = new BasicAdjacency(level.length, level[0].length, Measurement.MANHATTAN);
192
193        initialize(level, alternateWall);
194    }
195
196    /**
197     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
198     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
199     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
200     * map that can be used here. This constructor specifies a distance measurement.
201     *
202     * @param level
203     * @param adjacency
204     */
205    public CustomDijkstraMap(final char[][] level, Adjacency adjacency) {
206        this(level, adjacency, new GWTRNG());
207    }
208
209    /**
210     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
211     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
212     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
213     * map that can be used here. Also takes a distance measurement and an IRNG that can be seeded
214     * to ensure predictable path choices given otherwise identical inputs and circumstances.
215     *
216     * @param level
217     * @param rng   The IRNG, such as an RNG, to use for certain decisions; only affects find* methods like findPath, not scan.
218     */
219    public CustomDijkstraMap(final char[][] level, Adjacency adjacency, IRNG rng) {
220        this.rng = rng;
221        path = new IntVLA();
222        this.adjacency = adjacency;
223
224        initialize(level);
225    }
226
227    /**
228     * Used to initialize or re-initialize a CustomDijkstraMap that needs a new PhysicalMap because it either wasn't given
229     * one when it was constructed, or because the contents of the terrain have changed permanently (not if a
230     * creature moved; for that you pass the positions of creatures that block paths to scan() or findPath() ).
231     *
232     * @param level
233     * @return
234     */
235    public CustomDijkstraMap initialize(final double[] level) {
236        width = adjacency.width;
237        height = adjacency.height;
238        int len = level.length;
239        gradientMap = new double[len];
240        physicalMap = new double[len];
241        costMap = new int[len];
242        System.arraycopy(level, 0, gradientMap, 0, len);
243        System.arraycopy(level, 0, physicalMap, 0, len);
244        for (int i = 0; i < len; i++) {
245            costMap[i] = (gradientMap[i] > FLOOR) ? '#' : '.';
246        }
247        adjacency.costRules.putAt('#', WALL, 0);
248
249        neighbors = adjacency.neighborMaps();
250        heuristics = new double[adjacency.directions.length];
251        for (int i = 0; i < heuristics.length; i++) {
252            heuristics[i] = adjacency.measurement.heuristic(adjacency.directions[i]);
253        }
254        initialized = true;
255        return this;
256    }
257
258    /**
259     * Used to initialize or re-initialize a CustomDijkstraMap that needs a new PhysicalMap because it either wasn't given
260     * one when it was constructed, or because the contents of the terrain have changed permanently (not if a
261     * creature moved; for that you pass the positions of creatures that block paths to scan() or findPath() ).
262     *
263     * @param level
264     * @return
265     */
266    public CustomDijkstraMap initialize(final char[][] level) {
267        return initialize(level, '#');
268    }
269
270    /**
271     * Used to initialize or re-initialize a CustomDijkstraMap that needs a new PhysicalMap because it either wasn't given
272     * one when it was constructed, or because the contents of the terrain have changed permanently (not if a
273     * creature moved; for that you pass the positions of creatures that block paths to scan() or findPath() ). This
274     * initialize() method allows you to specify an alternate wall char other than the default character, '#' .
275     *
276     * @param level
277     * @param alternateWall
278     * @return
279     */
280    public CustomDijkstraMap initialize(final char[][] level, char alternateWall) {
281        width = level.length;
282        height = level[0].length;
283        int rot = adjacency.rotations, len = width*height*rot, dex;
284        gradientMap = new double[len];
285        physicalMap = new double[len];
286        costMap = new int[len];
287        IntDoubleOrderedMap cst = adjacency.costRules;
288        cst.putAt(alternateWall, WALL, 0);
289        int c;
290        for (int x = 0; x < width; x++) {
291            for (int y = 0; y < height; y++) {
292                c = level[x][y];
293                double t = (c == alternateWall) ? WALL : FLOOR;
294                for (int r = 0; r < rot; r++) {
295                    dex = adjacency.composite(x, y, r, 0);
296                    gradientMap[dex] = t;
297                    physicalMap[dex] = t;
298                    costMap[dex] = c;
299                }
300            }
301        }
302        neighbors = adjacency.neighborMaps();
303        heuristics = new double[adjacency.directions.length];
304        for (int i = 0; i < heuristics.length; i++) {
305            heuristics[i] = adjacency.measurement.heuristic(adjacency.directions[i]);
306        }
307        initialized = true;
308        return this;
309    }
310
311    /**
312     * Used to initialize the entry cost modifiers for games that require variable costs to enter squares. This expects
313     * a char[][] of the same exact dimensions as the 2D array that was used to previously initialize() this
314     * CustomDijkstraMap, treating the '#' char as a wall (impassable) and anything else as having a normal cost to enter.
315     * The costs can be accessed later by using costMap directly (which will have a valid value when this does not
316     * throw an exception), or by calling setCost().
317     *
318     * @param level a 2D char array that uses '#' for walls
319     * @return this CustomDijkstraMap for chaining.
320     */
321    public CustomDijkstraMap initializeCost(final char[][] level) {
322        if (!initialized) throw new IllegalStateException("CustomDijkstraMap must be initialized first!");
323        int rot = adjacency.rotations;
324        int c;
325        for (int x = 0; x < width; x++) {
326            for (int y = 0; y < height; y++) {
327                c = level[x][y];
328                for (int r = 0; r < rot; r++) {
329                    costMap[adjacency.composite(x, y, r, 0)] = c;
330                }
331            }
332        }
333        return this;
334    }
335
336    /**
337     * Used to initialize the entry cost modifiers for games that require variable costs to enter squares. This expects
338     * an int[] with length equal to the length of any inner array of neighbors (a field that is given a value during
339     * initialize() by this object's Adjacency value), using the int corresponding to a location as the tile type to
340     * look up for that location, as a key in {@link Adjacency#costRules}, even if an int isn't what this class would
341     * assign normally -- although, walls and other impassable values should be given '#' (which can be put in an int
342     * array) or the value of alternateWall, if this was given one, as a value. The tiles can be accessed later by using
343     * costMap directly (which will have a valid value when this does not throw an exception), or by calling setCost().
344     * <p/>
345     * This method should be slightly more efficient than the other initializeCost methods.
346     *
347     * @param tiles an int array that already has tile types that {@link #adjacency} can find values for
348     * @return this CustomDijkstraMap for chaining.
349     */
350    public CustomDijkstraMap initializeCost(final int[] tiles) {
351        if (!initialized)
352            throw new IllegalStateException("CustomDijkstraMap must be initialized first!");
353        if(tiles.length != gradientMap.length)
354            throw new IllegalArgumentException("costs.length must equal gradientMap.length");
355        costMap = new int[tiles.length];
356        System.arraycopy(tiles, 0, costMap, 0, tiles.length);
357        return this;
358    }
359
360    /**
361     * Gets the appropriate DijkstraMap.Measurement to pass to a constructor if you already have a Radius.
362     * Matches SQUARE or CUBE to CHEBYSHEV, DIAMOND or OCTAHEDRON to MANHATTAN, and CIRCLE or SPHERE to EUCLIDEAN.
363     *
364     * @param radius the Radius to find the corresponding Measurement for
365     * @return a DijkstraMap.Measurement that matches radius; SQUARE to CHEBYSHEV, DIAMOND to MANHATTAN, etc.
366     */
367    public static Measurement findMeasurement(Radius radius) {
368        if (radius.equals2D(Radius.SQUARE))
369            return Measurement.CHEBYSHEV;
370        else if (radius.equals2D(Radius.DIAMOND))
371            return Measurement.MANHATTAN;
372        else
373            return Measurement.EUCLIDEAN;
374    }
375
376    /**
377     * Gets the appropriate Radius corresponding to a DijkstraMap.Measurement.
378     * Matches CHEBYSHEV to SQUARE, MANHATTAN to DIAMOND, and EUCLIDEAN to CIRCLE.
379     *
380     * @param measurement the Measurement to find the corresponding Radius for
381     * @return a DijkstraMap.Measurement that matches radius; CHEBYSHEV to SQUARE, MANHATTAN to DIAMOND, etc.
382     */
383    public static Radius findRadius(Measurement measurement) {
384        switch (measurement) {
385            case CHEBYSHEV:
386                return Radius.SQUARE;
387            case EUCLIDEAN:
388                return Radius.CIRCLE;
389            default:
390                return Radius.DIAMOND;
391        }
392    }
393
394    /**
395     * Resets the gradientMap to its original value from physicalMap.
396     */
397    public void resetMap() {
398        if (!initialized) return;
399        System.arraycopy(physicalMap, 0, gradientMap, 0, physicalMap.length);
400    }
401
402    /**
403     * Resets this CustomDijkstraMap to a state with no goals, no discovered path, and no changes made to gradientMap
404     * relative to physicalMap.
405     */
406    public void reset() {
407        resetMap();
408        goals.clear();
409        fresh.clear();
410        path.clear();
411        frustration = 0;
412    }
413
414    /**
415     * Marks a cell as a goal for pathfinding, unless the cell is a wall or unreachable area (then it does nothing).
416     *
417     * @param pt
418     */
419    public void setGoal(int pt) {
420        if (!initialized || !adjacency.validate(pt)) return;
421        if (physicalMap[pt] > FLOOR) {
422            return;
423        }
424        adjacency.putAllVariants(goals, gradientMap, pt, 0.0);
425    }
426
427    /**
428     * Marks a cell's type for pathfinding cost as tile (it still will look up the tile in the
429     * {@link Adjacency#costRules} field of {@link #adjacency} when it tries to move through one), unless the cell is a
430     * wall or unreachable area (then it always sets the cost to a value that should have the same cost as a wall).
431     * The normal usage of this is something like {@code setCost(position, '.')} for maps without rotation (this sets
432     * the cost of moving into the cell position to the cost of entering a floor marked with '.'; thos is looked up in
433     * the Adjacency's cost rules and those can be set with {@link Adjacency#addCostRule(char, double)}). If the map has
434     * rotation, {@code setCost(position, '.' | 0x10000)} will change the cost to turn while standing on a tile to the
435     * cost of turning on a '.' floor, though this is just one way Adjacency can be implemented (it's how
436     * RotationAdjacency works).
437     *
438     * @param pt the encoded position/rotation/height to set the cost for
439     * @param tile typically a char such as '.' for floors, but if this uses rotation, turns on that tile are different
440     */
441    public void setCost(int pt, int tile) {
442        if (!initialized || !adjacency.validate(pt)) return;
443        if (physicalMap[pt] > FLOOR) {
444            costMap[pt] = adjacency.costRules.keyAt(0);
445            return;
446        }
447        costMap[pt] = tile;
448    }
449
450    /**
451     * Marks a specific cell in gradientMap as completely impossible to enter.
452     *
453     * @param pt
454     */
455    public void setOccupied(int pt) {
456        if (!initialized || !adjacency.validate(pt)) return;
457        gradientMap[pt] = WALL;
458    }
459
460    /**
461     * Reverts a cell to the value stored in the original state of the level as known by physicalMap.
462     *
463     * @param pt
464     */
465    public void resetCell(int pt) {
466        if (!initialized || !adjacency.validate(pt)) return;
467        gradientMap[pt] = physicalMap[pt];
468    }
469
470    /**
471     * Used to remove all goals and undo any changes to gradientMap made by having a goal present.
472     */
473    public void clearGoals() {
474        if (!initialized)
475            return;
476        int sz = goals.size;
477        for (int i = 0; i < sz; i++) {
478            resetCell(goals.pop());
479        }
480    }
481
482    protected void setFresh(final int pt, double counter) {
483        if (!initialized || !adjacency.validate(pt)) return;
484        if(gradientMap[pt] < counter && gradientMap[pt] < FLOOR)
485            return;
486        gradientMap[pt] = counter;
487        fresh.add(pt);
488    }
489
490    public boolean isBlocked(int start, int direction) {
491        return adjacency.isBlocked(start, direction, neighbors, gradientMap, WALL);
492        /*
493        if (rotations != 1) {
494            if (rotations <= 4 || (direction & 1) == 0)
495                return !adjacency.validate(start);
496            return neighbors[1][0][start] < 0 || gradientMap[neighbors[1][0][start]] >= WALL
497                    || neighbors[1][2][start] < 0 || gradientMap[neighbors[1][2][start]] >= WALL;
498        } else {
499            if (direction < 4)
500                return !adjacency.validate(start);
501            int[][] near = neighbors[0];
502            switch (direction) {
503                case 4: //UP_LEFT
504                    return (near[0][start] < 0 || gradientMap[near[0][start]] >= WALL)
505                            && (near[2][start] < 0 || gradientMap[near[2][start]] >= WALL);
506                case 5: //UP_RIGHT
507                    return (near[0][start] < 0 || gradientMap[near[0][start]] >= WALL)
508                            && (near[3][start] < 0 || gradientMap[near[3][start]] >= WALL);
509                case 6: //DOWN_LEFT
510                    return (near[1][start] < 0 || gradientMap[near[1][start]] >= WALL)
511                            && (near[2][start] < 0 || gradientMap[near[2][start]] >= WALL);
512                default: //DOWN_RIGHT
513                    return (near[1][start] < 0 || gradientMap[near[1][start]] >= WALL)
514                            && (near[3][start] < 0 || gradientMap[near[3][start]] >= WALL);
515            }
516        }
517        */
518    }
519
520    /**
521     * Recalculate the CustomDijkstra map and return it. Cells that were marked as goals with setGoal will have
522     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
523     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
524     * which will have a value defined by the WALL constant in this class, and areas that the scan was
525     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
526     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
527     * current measurement.
528     *
529     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
530     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
531     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
532     * @param impassable An array of int keys (encoded by an Adjacency, usually) representing the locations of enemies
533     * or other moving obstacles to a path that cannot be moved through; this can be null (meaning no obstacles).
534     * @return An int array using the dimensions of what this knows about the physical map.
535     */
536    public double[] scan(int usable, int[] impassable) {
537        if(impassable == null)
538            scanInternal(-1, null, -1);
539        else
540            scanInternal(-1, impassable, Math.min(usable, impassable.length));
541        int maxLength = gradientMap.length;
542        double[] gradientClone = new double[maxLength];
543        for (int l = 0; l < maxLength; l++) {
544            if (gradientMap[l] == FLOOR) {
545                gradientMap[l] = DARK;
546            }
547        }
548        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
549        return gradientClone;
550    }
551    /**
552     * Recalculate the CustomDijkstra map and return it. Cells that were marked as goals with setGoal will have
553     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
554     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
555     * which will have a value defined by the WALL constant in this class, and areas that the scan was
556     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
557     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
558     * current measurement.
559     *
560     * @param impassable An array of int keys (encoded by an Adjacency, usually) representing the locations of enemies
561     * or other moving obstacles to a path that cannot be moved through; this can be null (meaning no obstacles).
562     * @return An int array using the dimensions of what this knows about the physical map.
563     */
564    public double[] scan(IntVLA impassable) {
565        if(impassable == null)
566            scanInternal(-1, null, -1);
567        else
568            scanInternal(-1, impassable.items, impassable.size);
569        int maxLength = gradientMap.length;
570        double[] gradientClone = new double[maxLength];
571        for (int l = 0; l < maxLength; l++) {
572            if (gradientMap[l] == FLOOR) {
573                gradientMap[l] = DARK;
574            }
575        }
576        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
577        return gradientClone;
578
579    }
580
581    /**
582     * Recalculate the CustomDijkstra map, stopping early if it has a path from a goal to start, and return that map.
583     * Cells that were marked as goals with setGoal will have
584     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
585     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
586     * which will have a value defined by the WALL constant in this class, and areas that the scan was
587     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
588     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
589     * current measurement.
590     *
591     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
592     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
593     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
594     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
595     * @param impassable An array of int keys (encoded by an Adjacency, usually) representing the locations of enemies
596     * or other moving obstacles to a path that cannot be moved through; this can be null (meaning no obstacles).
597     * @return An int array using the dimensions of what this knows about the physical map.
598     */
599    public double[] scanToStart(int start, int usable, int[] impassable) {
600        if(impassable == null)
601            scanInternal(start, null, -1);
602        else
603            scanInternal(start, impassable, Math.min(usable, impassable.length));
604        return gradientMap;
605    }
606    /**
607     * Recalculate the CustomDijkstra map, stopping early if it has a path from a goal to start, and return that map.
608     * Cells that were marked as goals with setGoal will have
609     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
610     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
611     * which will have a value defined by the WALL constant in this class, and areas that the scan was
612     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
613     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
614     * current measurement.
615     *
616     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
617     * @param impassable An array of int keys (encoded by an Adjacency, usually) representing the locations of enemies
618     * or other moving obstacles to a path that cannot be moved through; this can be null (meaning no obstacles).
619     * @return An int array using the dimensions of what this knows about the physical map.
620     */
621    public double[] scanToStart(int start, IntVLA impassable) {
622        if(impassable == null)
623            scanInternal(start, null, -1);
624        else
625            scanInternal(start, impassable.items, impassable.size);
626        return gradientMap;
627    }
628
629    protected void scanInternal(int start, int[] impassable, int usable)
630    {
631        if (!initialized) return;
632        Adjacency adjacency = this.adjacency;
633        int[][] fromNeighbors = neighbors[0];
634        int near, cen, mid, neighborCount = fromNeighbors.length;
635
636        if (impassable != null) {
637            if(usable > impassable.length)
638                usable = impassable.length;
639            for (int i = 0; i < usable; i++) {
640                adjacency.putAllVariants(null, gradientMap, impassable[i], WALL);
641            }
642        }
643        boolean standardCosts = adjacency.hasStandardCost();
644        mappedCount = goals.size;
645        for (int i = 0; i < mappedCount; i++) {
646            gradientMap[goals.get(i)] = 0;
647        }
648        double currentLowest = 999000, cs, csm, dist;
649        fresh.clear();
650        int maxLength = gradientMap.length;
651        for (int l = 0; l < maxLength; l++) {
652            if (gradientMap[l] <= FLOOR) {
653                if (gradientMap[l] < currentLowest) {
654                    currentLowest = gradientMap[l];
655                    fresh.clear();
656                    fresh.add(l);
657                } else if (gradientMap[l] == currentLowest) {
658                    fresh.add(l);
659                }
660            }
661        }
662        int fsz, numAssigned = fresh.size;
663        IntDoubleOrderedMap costs = adjacency.costRules;
664        while (numAssigned > 0) {
665            numAssigned = 0;
666            fsz = fresh.size;
667            for (int ci = fsz-1; ci >= 0; ci--) {
668                cen = fresh.removeIndex(ci);
669                dist = gradientMap[cen];
670                for (int d = 0; d < neighborCount; d++) {
671                    near = fromNeighbors[d][cen];
672                    if (!adjacency.validate(near))
673                        // Outside the map
674                        continue;
675                    if(isBlocked(cen, d))
676                        continue;
677                    if(adjacency.twoStepRule) {
678                        near = fromNeighbors[d][mid = near];
679                        // Outside the map
680                        if (!adjacency.validate(near))
681                            continue;
682                        if(isBlocked(mid, d))
683                            continue;
684                        csm = (costs.get(costMap[mid]) * heuristics[d]);
685                        cs = (costs.get(costMap[near]) * heuristics[d]);
686                        if ((gradientMap[mid] = dist + csm) + cs < gradientMap[near]) {
687                            setFresh(near, dist + cs + csm);
688                            ++numAssigned;
689                            ++mappedCount;
690                            if(start == near && standardCosts)
691                            {
692                                if (impassable != null)
693                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, 1);
694                                return;
695                            }
696                        }
697                    }
698                    else
699                    {
700                        cs = (costs.get(costMap[near] | (adjacency.extractR(cen) == adjacency.extractR(near) ? 0 : 0x10000))
701                                        * heuristics[d]);
702                        //int h = adjacency.measurement.heuristic(adjacency.directions[d]);
703                        if (gradientMap[cen] + cs < gradientMap[near]) {
704                            setFresh(near, dist + cs);
705                            ++numAssigned;
706                            ++mappedCount;
707                            if(start == near && standardCosts)
708                            {
709                                if (impassable != null)
710                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, 1);
711                                return;
712                            }
713                        }
714                    }
715                }
716            }
717        }
718        if (impassable != null)
719            adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, 1);
720    }
721
722    /**
723     * Recalculate the CustomDijkstra map up to a limit and return it. Cells that were marked as goals with setGoal will have
724     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
725     * from goals will have a value equal to the distance from the nearest goal. If a cell would take more steps to
726     * reach than the given limit, it will have a value of DARK if it was passable instead of the distance. The
727     * exceptions are walls, which will have a value defined by the WALL constant in this class, and areas that the scan
728     * was unable to reach, which will have a value defined by the DARK constant in this class. This uses the
729     * current measurement.
730     *
731     * @param limit      The maximum number of steps to scan outward from a goal.
732     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
733     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
734     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
735     * @param impassable An array or vararg of int keys representing the locations of enemies or other moving obstacles
736     *                   to a path that cannot be moved through; this can be null if there are no such obstacles.
737     * @return A int array using the dimensions of what this knows about the physical map.
738     */
739    public double[] partialScan(int limit, int usable, int[] impassable) {
740        if(impassable == null)
741            partialScanInternal(-1, limit, null, -1);
742        else
743            partialScanInternal(-1, -1, impassable, Math.min(usable, impassable.length));
744        int maxLength = gradientMap.length;
745        double[] gradientClone = new double[maxLength];
746        for (int l = 0; l < maxLength; l++) {
747            if (gradientMap[l] == FLOOR) {
748                gradientMap[l] = DARK;
749            }
750        }
751        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
752        return gradientClone;
753    }
754
755    /**
756     * Recalculate the CustomDijkstra map up to a limit and return it. Cells that were marked as goals with setGoal will have
757     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
758     * from goals will have a value equal to the distance from the nearest goal. If a cell would take more steps to
759     * reach than the given limit, it will have a value of DARK if it was passable instead of the distance. The
760     * exceptions are walls, which will have a value defined by the WALL constant in this class, and areas that the scan
761     * was unable to reach, which will have a value defined by the DARK constant in this class. This uses the
762     * current measurement.
763     *
764     * @param limit      The maximum number of steps to scan outward from a goal.
765     * @param impassable An IntVLA of int keys representing the locations of enemies or other moving obstacles
766     *                   to a path that cannot be moved through; this can be null if there are no such obstacles.
767     * @return A int array using the dimensions of what this knows about the physical map.
768     */
769    public double[] partialScan(int limit, IntVLA impassable) {
770        if(impassable == null)
771            partialScanInternal(-1, limit, null, -1);
772        else
773            partialScanInternal(-1, limit, impassable.items, impassable.size);
774        int maxLength = gradientMap.length;
775        double[] gradientClone = new double[maxLength];
776        for (int l = 0; l < maxLength; l++) {
777            if (gradientMap[l] == FLOOR) {
778                gradientMap[l] = DARK;
779            }
780        }
781        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
782        return gradientClone;
783    }
784
785
786    /**
787     * Recalculate the CustomDijkstra map up to a limit, stopping early if it has a path from a goal to start, and
788     * return it. Cells that were marked as goals with setGoal will have
789     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
790     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
791     * which will have a value defined by the WALL constant in this class, and areas that the scan was
792     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
793     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
794     * current measurement.
795     *
796     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
797     * @param limit      The maximum number of steps to scan outward from a goal.
798     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
799     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
800     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
801     * @param impassable An array of int keys (encoded by an Adjacency, usually) representing the locations of enemies
802     * or other moving obstacles to a path that cannot be moved through; this can be null (meaning no obstacles).
803     * @return An int array using the dimensions of what this knows about the physical map.
804     */
805    public double[] partialScanToStart(int limit, int start, int usable, int[] impassable) {
806        if(impassable == null)
807            partialScanInternal(start, limit, null, -1);
808        else
809            partialScanInternal(start, limit, impassable, Math.min(usable, impassable.length));
810        return gradientMap;
811    }
812    /**
813     * Recalculate the CustomDijkstra map up to a limit, stopping early if it has a path from a goal to start, and
814     * return that map. Cells that were marked as goals with setGoal will have
815     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
816     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
817     * which will have a value defined by the WALL constant in this class, and areas that the scan was
818     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
819     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
820     * current measurement.
821     *
822     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
823     * @param limit      The maximum number of steps to scan outward from a goal.
824     * @param impassable An array of int keys (encoded by an Adjacency, usually) representing the locations of enemies
825     * or other moving obstacles to a path that cannot be moved through; this can be null (meaning no obstacles).
826     * @return An int array using the dimensions of what this knows about the physical map.
827     */
828    public double[] partialScanToStart(int limit, int start, IntVLA impassable) {
829        if(impassable == null)
830            partialScanInternal(start, limit, null, -1);
831        else
832            partialScanInternal(start, limit, impassable.items, impassable.size);
833        return gradientMap;
834    }
835
836    protected void partialScanInternal(int start, int limit, int[] impassable, int usable)
837    {
838        if (!initialized) return;
839        Adjacency adjacency = this.adjacency;
840        int[][] fromNeighbors = neighbors[0];
841        int near, cen, mid, neighborCount = fromNeighbors.length;
842
843        if (impassable != null) {
844            if(usable > impassable.length)
845                usable = impassable.length;
846            for (int i = 0; i < usable; i++) {
847                adjacency.putAllVariants(null, gradientMap, impassable[i], WALL);
848            }
849        }
850        boolean standardCosts = adjacency.hasStandardCost();
851        mappedCount = goals.size;
852        for (int i = 0; i < mappedCount; i++) {
853            gradientMap[goals.get(i)] = 0;
854        }
855        double currentLowest = 999000, cs, csm, dist;
856        fresh.clear();
857        int maxLength = gradientMap.length;
858        for (int l = 0; l < maxLength; l++) {
859            if (gradientMap[l] <= FLOOR) {
860                if (gradientMap[l] < currentLowest) {
861                    currentLowest = gradientMap[l];
862                    fresh.clear();
863                    fresh.add(l);
864                } else if (gradientMap[l] == currentLowest) {
865                    fresh.add(l);
866                }
867            }
868        }
869        int fsz, numAssigned = fresh.size;
870        IntDoubleOrderedMap costs = adjacency.costRules;
871        int iter = 0;
872        while (numAssigned > 0 && iter++ < limit) {
873            numAssigned = 0;
874            fsz = fresh.size;
875            for (int ci = fsz-1; ci >= 0; ci--) {
876                cen = fresh.removeIndex(ci);
877                dist = gradientMap[cen];
878                for (int d = 0; d < neighborCount; d++) {
879                    near = fromNeighbors[d][cen];
880                    if (!adjacency.validate(near))
881                        // Outside the map
882                        continue;
883                    if(isBlocked(cen, d))
884                        continue;
885                    if(adjacency.twoStepRule) {
886                        near = fromNeighbors[d][mid = near];
887                        // Outside the map
888                        if (!adjacency.validate(near))
889                            continue;
890                        if(isBlocked(mid, d))
891                            continue;
892                        csm = (costs.get(costMap[mid]) * heuristics[d]);
893                        cs = (costs.get(costMap[near]) * heuristics[d]);
894                        if ((gradientMap[mid] = dist + csm) + cs < gradientMap[near]) {
895                            setFresh(near, dist + cs + csm);
896                            ++numAssigned;
897                            ++mappedCount;
898                            if(start == near && standardCosts)
899                            {
900                                if (impassable != null)
901                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, 1);
902                                return;
903                            }
904                        }
905                    }
906                    else
907                    {
908                        cs = (costs.get(costMap[near] | (adjacency.extractR(cen) == adjacency.extractR(near) ? 0 : 0x10000))
909                                * heuristics[d]);
910                        //int h = adjacency.measurement.heuristic(adjacency.directions[d]);
911                        if (gradientMap[cen] + cs < gradientMap[near]) {
912                            setFresh(near, dist + cs);
913                            ++numAssigned;
914                            ++mappedCount;
915                            if(start == near && standardCosts)
916                            {
917                                if (impassable != null)
918                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, 1);
919                                return;
920                            }
921                        }
922                    }
923                }
924            }
925        }
926        if (impassable != null)
927            adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, 1);
928    }
929
930    /*
931     * Recalculate the CustomDijkstra map until it reaches a cell index in targets, then returns the first target found.
932     * This uses the current measurement.
933     *
934     * @param start   the cell to use as the origin for finding the nearest target
935     * @param targets the cell indices that this is trying to find; it will stop once it finds one
936     * @return the cell index that it found first.
937     * /
938    public int findNearest(int start, int... targets) {
939        if (!initialized) return -1;
940        if (targets == null)
941            return -1;
942        for (int i = 0; i < targets.length; i++) {
943            if(targets[i] == start)
944                return start;
945        }
946        resetMap();
947        int xShift = width / 8, yShift = height / 8;
948        while (physicalMap[start.x][start.y] >= WALL && frustration < 50) {
949            start2 = Coord.get(Math.min(Math.max(1, start.x + rng.nextInt(1 + xShift * 2) - xShift), width - 2),
950                    Math.min(Math.max(1, start.y + rng.nextInt(1 + yShift * 2) - yShift), height - 2));
951        }
952        if (closed.containsKey(start2.encode()))
953            closed.remove(start2.encode());
954        gradientMap[start2.x][start2.y] = 0;
955
956        for (int y = 0; y < height; y++) {
957            for (int x = 0; x < width; x++) {
958                if (gradientMap[x][y] > FLOOR && !goals.containsKey(Coord.pureEncode(x, y)))
959                    closed.put(Coord.pureEncode(x, y), physicalMap[x][y]);
960            }
961        }
962        int numAssigned = 1;
963        mappedCount = 1;
964        open.put(start2.encode(), 0);
965        int near, cen;
966        int enc;
967
968        while (numAssigned > 0) {
969
970            numAssigned = 0;
971
972            for (IntDoubleOrderedMap.MapEntry cell : open.mapEntrySet()) {
973                cen = cell.getIntKey();
974                for (int d = 0; d < neighbors.length; d++) {
975                    near = neighbors[d][cen];
976                    if (!adjacency.validate(near))
977                        // Outside the map
978                        continue;
979                    dir = adjacency.directions[d];
980                    if(adjacency.isBlocked(cen, d, neighbors, gradientMap, WALL))
981                        continue;
982                    int h = adjacency.measurement.heuristic(dir);
983                    if (!closed.containsKey(near) && !open.containsKey(near) && gradientMap[cen] + h * costMap[near] < gradientMap[near]) {
984                        setFresh(near, cell.getDoubleValue() + h * costMap[near]);
985                        ++numAssigned;
986                        ++mappedCount;
987                    }
988                }
989            }
990            open.clear();
991            open.putAll(fresh);
992            fresh.clear();
993
994
995            numAssigned = 0;
996
997            for (IntDoubleOrderedMap.MapEntry cell : open.mapEntrySet()) {
998                cen = Coord.decode(cell.getIntKey());
999                for (int d = 0; d < dirs.length; d++) {
1000                    adj = cen.translate(dirs[d].deltaX, dirs[d].deltaY);
1001                    if (adj.x < 0 || adj.y < 0 || width <= adj.x || height <= adj.y)
1002                        // Outside the map
1003                        continue;
1004                    enc = adj.encode();
1005                    int h = heuristic(dirs[d]);
1006                    if (!closed.containsKey(enc) && !open.containsKey(enc) &&
1007                            gradientMap[cen.x][cen.y] + h * costMap[adj.x][adj.y] < gradientMap[adj.x][adj.y]) {
1008                        setFresh(adj, cell.getDoubleValue() + h * costMap[adj.x][adj.y]);
1009                        ++numAssigned;
1010                        ++mappedCount;
1011                        if (targets.contains(adj)) {
1012                            fresh.clear();
1013                            closed.clear();
1014                            open.clear();
1015                            return adj;
1016                        }
1017                    }
1018                }
1019            }
1020
1021            open.clear();
1022            open.putAll(fresh);
1023            fresh.clear();
1024        }
1025        closed.clear();
1026        open.clear();
1027        return null;
1028    }
1029    */
1030
1031
1032    /*
1033     * Recalculate the CustomDijkstra map until it reaches a Coord in targets, then returns the first target found.
1034     * This uses the current measurement.
1035     *
1036     * @param start   the cell to use as the origin for finding the nearest target
1037     * @param targets the Coords that this is trying to find; it will stop once it finds one
1038     * @return the Coord that it found first.
1039     *
1040    public Coord findNearest(Coord start, Coord... targets) {
1041        OrderedSet<Coord> tgts = new OrderedSet<>(targets.length);
1042        Collections.addAll(tgts, targets);
1043        return findNearest(start, tgts);
1044    }
1045    */
1046
1047    /*
1048     * If you have a target or group of targets you want to pathfind to without scanning the full map, this can be good.
1049     * It may find sub-optimal paths in the presence of costs to move into cells. It is useful when you want to move in
1050     * a straight line to a known nearby goal.
1051     *
1052     * @param start   your starting location
1053     * @param targets an array or vararg of Coords to pathfind to the nearest of
1054     * @return an ArrayList of Coord that goes from a cell adjacent to start and goes to one of the targets. Copy of path.
1055     *
1056    public ArrayList<Coord> findShortcutPath(Coord start, Coord... targets) {
1057        if (targets.length == 0) {
1058            path.clear();
1059            return new IntVLA(path);
1060        }
1061        Coord currentPos = findNearest(start, targets);
1062        while (true) {
1063            if (frustration > 500) {
1064                path.clear();
1065                break;
1066            }
1067            int best = gradientMap[currentPos.x][currentPos.y];
1068            final Direction[] dirs = appendDirToShuffle(rng);
1069            int choice = rng.nextInt(dirs.length);
1070
1071            for (int d = 0; d < dirs.length; d++) {
1072                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
1073                if (gradientMap[pt.x][pt.y] < best) {
1074                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
1075                        best = gradientMap[pt.x][pt.y];
1076                        choice = d;
1077                    }
1078                }
1079            }
1080
1081            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
1082                path.clear();
1083                break;
1084            }
1085            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
1086            if (gradientMap[currentPos.x][currentPos.y] == 0)
1087                break;
1088            path.add(currentPos);
1089            frustration++;
1090        }
1091        frustration = 0;
1092        Collections.reverse(path);
1093        return new ArrayList<>(path);
1094
1095    }
1096    */
1097
1098
1099    /*
1100     * Recalculate the CustomDijkstra map until it reaches a Coord in targets, then returns the first several targets found,
1101     * up to limit or less if the map is fully searched without finding enough.
1102     * This uses the current measurement.
1103     *
1104     * @param start   the cell to use as the origin for finding the nearest targets
1105     * @param limit   the maximum number of targets to find before returning
1106     * @param targets the Coords that this is trying to find; it will stop once it finds enough (based on limit)
1107     * @return the Coords that it found first.
1108     *
1109    public ArrayList<Coord> findNearestMultiple(Coord start, int limit, Set<Coord> targets) {
1110        if (!initialized) return null;
1111        if (targets == null)
1112            return null;
1113        ArrayList<Coord> found = new ArrayList<>(limit);
1114        if (targets.contains(start))
1115            return found;
1116        Coord start2 = start, adj, cen;
1117        int enc;
1118        while (physicalMap[start.x][start.y] >= WALL && frustration < 50) {
1119            start2 = Coord.get(Math.min(Math.max(1, start.x + rng.nextInt(15) - 7), width - 2),
1120                    Math.min(Math.max(1, start.y + rng.nextInt(15) - 7), height - 2));
1121            frustration++;
1122        }
1123        if (closed.containsKey(start2.encode()))
1124            closed.remove(start2.encode());
1125        gradientMap[start2.x][start2.y] = 0;
1126
1127        for (int y = 0; y < height; y++) {
1128            for (int x = 0; x < width; x++) {
1129                if (gradientMap[x][y] > FLOOR && !goals.containsKey(Coord.pureEncode(x, y)))
1130                    closed.put(Coord.pureEncode(x, y), physicalMap[x][y]);
1131            }
1132        }
1133        int numAssigned = 1;
1134        mappedCount = 1;
1135        open.put(start2.encode(), 0);
1136
1137        Direction[] dirs = (measurement == Measurement.MANHATTAN) ? Direction.CARDINALS : Direction.OUTWARDS;
1138        while (numAssigned > 0) {
1139//            ++iter;
1140            numAssigned = 0;
1141            for (IntDoubleOrderedMap.MapEntry cell : open.mapEntrySet()) {
1142                cen = Coord.decode(cell.getIntKey());
1143                for (int d = 0; d < dirs.length; d++) {
1144                    adj = cen.translate(dirs[d].deltaX, dirs[d].deltaY);
1145                    if (adj.x < 0 || adj.y < 0 || width <= adj.x || height <= adj.y)
1146                        // Outside the map
1147                        continue;
1148                    enc = adj.encode();
1149
1150                    int h = heuristic(dirs[d]);
1151                    if (!closed.containsKey(enc) && !open.containsKey(enc) &&
1152                            gradientMap[cen.x][cen.y] + h * costMap[adj.x][adj.y] < gradientMap[adj.x][adj.y]) {
1153                        setFresh(adj, cell.getDoubleValue() + h * costMap[adj.x][adj.y]);
1154                        ++numAssigned;
1155                        ++mappedCount;
1156                        if (targets.contains(adj)) {
1157                            found.add(adj);
1158                            if (found.size() >= limit) {
1159                                fresh.clear();
1160                                open.clear();
1161                                closed.clear();
1162                                return found;
1163                            }
1164                        }
1165                    }
1166                }
1167            }
1168//            closed.putAll(open);
1169            open = new IntDoubleOrderedMap(fresh);
1170            fresh.clear();
1171        }
1172        closed.clear();
1173        open.clear();
1174        return found;
1175    }
1176    */
1177
1178    /**
1179     * Recalculate the CustomDijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1180     * a cell in the returned CustomDijkstra map assumes that a creature is square, with a side length equal to the passed
1181     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1182     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1183     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1184     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1185     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1186     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1187     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1188     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1189     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1190     * current measurement.
1191     * <br>
1192     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1193     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1194     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1195     * general issues of how to display a bisected, but mobile, creature.
1196     *
1197     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1198     *                   creature. Non-square creatures are not supported because turning is really hard.
1199     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
1200     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
1201     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
1202     * @param impassable An array of encoded int keys representing the locations of enemies or other moving obstacles to
1203     *                   a path that cannot be moved through; this can be null if there are no such obstacles.
1204     * @return An int array using the dimensions/rotations of what this knows about the physical map.
1205     */
1206    public double[] scanLarge(int size, int usable, int[] impassable) {
1207        if(impassable == null)
1208            scanLargeInternal(-1, size, null, -1);
1209        else
1210            scanLargeInternal(-1, size, impassable, Math.min(usable, impassable.length));
1211        int maxLength = gradientMap.length;
1212        double[] gradientClone = new double[maxLength];
1213        for (int l = 0; l < maxLength; l++) {
1214            if (gradientMap[l] == FLOOR) {
1215                gradientMap[l] = DARK;
1216            }
1217        }
1218        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1219        return gradientClone;
1220    }
1221    /**
1222     * Recalculate the CustomDijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1223     * a cell in the returned CustomDijkstra map assumes that a creature is square, with a side length equal to the passed
1224     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1225     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1226     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1227     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1228     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1229     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1230     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1231     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1232     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1233     * current measurement.
1234     * <br>
1235     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1236     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1237     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1238     * general issues of how to display a bisected, but mobile, creature.
1239     *
1240     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1241     *                   creature. Non-square creatures are not supported because turning is really hard.
1242     * @param impassable An IntVLA where items are ints representing the locations of enemies or other moving obstacles
1243     *                   to a path that cannot be moved through; this can be null if there are no such obstacles.
1244     * @return A 2D int[width][height] using the width and height of what this knows about the physical map.
1245     */
1246    public double[] scanLarge(int size, IntVLA impassable) {
1247        if(impassable == null)
1248            scanLargeInternal(-1, size, null, -1);
1249        else
1250            scanLargeInternal(-1, size, impassable.items, impassable.size);
1251        int maxLength = gradientMap.length;
1252        double[] gradientClone = new double[maxLength];
1253        for (int l = 0; l < maxLength; l++) {
1254            if (gradientMap[l] == FLOOR) {
1255                gradientMap[l] = DARK;
1256            }
1257        }
1258        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1259        return gradientClone;
1260    }
1261    /**
1262     * Recalculate the CustomDijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1263     * a cell in the returned CustomDijkstra map assumes that a creature is square, with a side length equal to the passed
1264     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1265     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1266     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1267     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1268     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1269     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1270     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1271     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1272     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1273     * current measurement.
1274     * <br>
1275     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1276     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1277     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1278     * general issues of how to display a bisected, but mobile, creature.
1279     *
1280     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1281     *                   creature. Non-square creatures are not supported because turning is really hard.
1282     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
1283     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
1284     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
1285     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
1286     * @param impassable An array of encoded int keys representing the locations of enemies or other moving obstacles to
1287     *                   a path that cannot be moved through; this can be null if there are no such obstacles.
1288     * @return An int array using the dimensions/rotations of what this knows about the physical map.
1289     */
1290    public double[] scanToStartLarge(int size, int start, int usable, int[] impassable) {
1291        if(impassable == null)
1292            scanLargeInternal(start, size, null, -1);
1293        else
1294            scanLargeInternal(start, size, impassable, Math.min(usable, impassable.length));
1295        int maxLength = gradientMap.length;
1296        double[] gradientClone = new double[maxLength];
1297        for (int l = 0; l < maxLength; l++) {
1298            if (gradientMap[l] == FLOOR) {
1299                gradientMap[l] = DARK;
1300            }
1301        }
1302        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1303        return gradientClone;
1304    }
1305    /**
1306     * Recalculate the CustomDijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1307     * a cell in the returned CustomDijkstra map assumes that a creature is square, with a side length equal to the passed
1308     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1309     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1310     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1311     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1312     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1313     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1314     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1315     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1316     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1317     * current measurement.
1318     * <br>
1319     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1320     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1321     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1322     * general issues of how to display a bisected, but mobile, creature.
1323     *
1324     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1325     *                   creature. Non-square creatures are not supported because turning is really hard.
1326     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
1327     * @param impassable An IntVLA where items are ints representing the locations of enemies or other moving obstacles
1328     *                   to a path that cannot be moved through; this can be null if there are no such obstacles.
1329     * @return A 2D int[width][height] using the width and height of what this knows about the physical map.
1330     */
1331    public double[] scanToStartLarge(int size, int start, IntVLA impassable) {
1332        if(impassable == null)
1333            scanLargeInternal(start, size, null, -1);
1334        else
1335            scanLargeInternal(start, size, impassable.items, impassable.size);
1336        int maxLength = gradientMap.length;
1337        double[] gradientClone = new double[maxLength];
1338        for (int l = 0; l < maxLength; l++) {
1339            if (gradientMap[l] == FLOOR) {
1340                gradientMap[l] = DARK;
1341            }
1342        }
1343        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1344        return gradientClone;
1345    }
1346
1347    protected void scanLargeInternal(int start, int size, int[] impassable, int usable)
1348    {
1349        if (!initialized) return;
1350        Adjacency adjacency = this.adjacency;
1351        int[][] fromNeighbors = neighbors[0];
1352        int near, cen, mid, neighborCount = fromNeighbors.length, sx, sy, sr, sn;
1353
1354        if (impassable != null) {
1355            if(usable > impassable.length)
1356                usable = impassable.length;
1357            for (int i = 0; i < usable; i++) {
1358                adjacency.putAllVariants(null, gradientMap, impassable[i], WALL, -size);
1359            }
1360        }
1361        mappedCount = goals.size;
1362        for (int i = 0; i < mappedCount; i++) {
1363            cen = goals.get(i);
1364            sx = adjacency.extractX(cen);
1365            sy = adjacency.extractY(cen);
1366            sr = adjacency.extractR(cen);
1367            sn = adjacency.extractN(cen);
1368            for (int xx = 0; xx < size; xx++) {
1369                for (int yy = 0; yy < size; yy++) {
1370                    cen = adjacency.composite(sx - xx, sy - yy, sr, sn);
1371                    if(cen >= 0)
1372                        gradientMap[cen] = 0;
1373                }
1374            }
1375        }
1376        double currentLowest = 999000, cs, csm, dist;
1377        fresh.clear();
1378        int maxLength = gradientMap.length;
1379        for (int l = 0; l < maxLength; l++) {
1380            if (gradientMap[l] <= FLOOR) {
1381                if (gradientMap[l] < currentLowest) {
1382                    currentLowest = gradientMap[l];
1383                    fresh.clear();
1384                    fresh.add(l);
1385                } else if (gradientMap[l] == currentLowest) {
1386                    fresh.add(l);
1387                }
1388            }
1389        }
1390        int fsz, numAssigned = fresh.size;
1391        IntDoubleOrderedMap costs = adjacency.costRules;
1392        boolean standardCosts = adjacency.hasStandardCost();
1393        while (numAssigned > 0) {
1394            numAssigned = 0;
1395            fsz = fresh.size;
1396            for (int ci = fsz-1; ci >= 0; ci--) {
1397                cen = fresh.removeIndex(ci);
1398                dist = gradientMap[cen];
1399                for (int d = 0; d < neighborCount; d++) {
1400                    near = fromNeighbors[d][cen];
1401                    if (!adjacency.validate(near))
1402                        // Outside the map
1403                        continue;
1404                    if(isBlocked(cen, d))
1405                        continue;
1406                    if(adjacency.twoStepRule) {
1407                        near = fromNeighbors[d][mid = near];
1408                        // Outside the map
1409                        if (!adjacency.validate(near))
1410                            continue;
1411                        if(isBlocked(mid, d))
1412                            continue;
1413                        csm = (costs.get(costMap[mid]) * heuristics[d]);
1414                        cs = (costs.get(costMap[near]) * heuristics[d]);
1415                        if ((gradientMap[mid] = dist + csm) + cs < gradientMap[near]) {
1416                            setFresh(near, dist + cs + csm);
1417                            ++numAssigned;
1418                            ++mappedCount;
1419                            if(start == near && standardCosts)
1420                            {
1421                                if (impassable != null)
1422                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, -size);
1423                                return;
1424                            }
1425                        }
1426                    }
1427                    else
1428                    {
1429                        cs = (costs.get(costMap[near] | (adjacency.extractR(cen) == adjacency.extractR(near) ? 0 : 0x10000))
1430                                * heuristics[d]);
1431                        //int h = adjacency.measurement.heuristic(adjacency.directions[d]);
1432                        if (gradientMap[cen] + cs < gradientMap[near]) {
1433                            setFresh(near, dist + cs);
1434                            ++numAssigned;
1435                            ++mappedCount;
1436                            if(start == near && standardCosts)
1437                            {
1438                                if (impassable != null)
1439                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, -size);
1440                                return;
1441                            }
1442                        }
1443                    }
1444                }
1445            }
1446        }
1447
1448        if (impassable != null)
1449            adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, -size);
1450    }
1451
1452
1453    /**
1454     * Recalculate the CustomDijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1455     * a cell in the returned CustomDijkstra map assumes that a creature is square, with a side length equal to the passed
1456     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1457     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1458     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1459     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1460     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1461     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1462     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1463     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1464     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1465     * current measurement.
1466     * <br>
1467     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1468     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1469     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1470     * general issues of how to display a bisected, but mobile, creature.
1471     *
1472     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1473     *                   creature. Non-square creatures are not supported because turning is really hard.
1474     * @param limit      The maximum number of steps to scan outward from a goal.
1475     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
1476     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
1477     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
1478     * @param impassable An array of encoded int keys representing the locations of enemies or other moving obstacles to
1479     *                   a path that cannot be moved through; this can be null if there are no such obstacles.
1480     * @return A 2D int[width][height] using the width and height of what this knows about the physical map.
1481     */
1482    public double[] partialScanLarge(int size, int limit, int usable, int[] impassable) {
1483        if(impassable == null)
1484            partialScanLargeInternal(-1, size, limit, null, -1);
1485        else
1486            partialScanLargeInternal(-1, size, limit, impassable, Math.min(usable, impassable.length));
1487        int maxLength = gradientMap.length;
1488        double[] gradientClone = new double[maxLength];
1489        for (int l = 0; l < maxLength; l++) {
1490            if (gradientMap[l] == FLOOR) {
1491                gradientMap[l] = DARK;
1492            }
1493        }
1494        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1495        return gradientClone;
1496    }
1497    /**
1498     * Recalculate the CustomDijkstra map, up to a limit, for a creature that is potentially larger than 1x1 cell and
1499     * return it. The value of a cell in the returned CustomDijkstra map assumes that a creature is square, with a side
1500     * length equal to the passed size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with
1501     * a distance number represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that
1502     * cannot be entered by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x
1503     * and/or maximum-y wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal
1504     * will have a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1505     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1506     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1507     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1508     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1509     * current measurement.
1510     * <br>
1511     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1512     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1513     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1514     * general issues of how to display a bisected, but mobile, creature.
1515     *
1516     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1517     *                   creature. Non-square creatures are not supported because turning is really hard.
1518     * @param limit      The maximum number of steps to scan outward from a goal.
1519     * @param impassable An IntVLA where items are ints representing the locations of enemies or other moving obstacles
1520     *                   to a path that cannot be moved through; this can be null if there are no such obstacles.
1521     * @return A 2D int[width][height] using the width and height of what this knows about the physical map.
1522     */
1523    public double[] partialScanLarge(int size, int limit, IntVLA impassable) {
1524        if(impassable == null)
1525            partialScanLargeInternal(-1, size, limit, null, -1);
1526        else
1527            partialScanLargeInternal(-1, size, limit, impassable.items, impassable.size);
1528        int maxLength = gradientMap.length;
1529        double[] gradientClone = new double[maxLength];
1530        for (int l = 0; l < maxLength; l++) {
1531            if (gradientMap[l] == FLOOR) {
1532                gradientMap[l] = DARK;
1533            }
1534        }
1535        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1536        return gradientClone;
1537    }
1538    /**
1539     * Recalculate the CustomDijkstra map, up to a limit, for a creature that is potentially larger than 1x1 cell,
1540     * stopping early if a path is found between a goal and start, and return that map. The value of
1541     * a cell in the returned CustomDijkstra map assumes that a creature is square, with a side length equal to the passed
1542     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1543     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1544     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1545     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1546     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1547     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1548     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1549     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1550     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1551     * current measurement.
1552     * <br>
1553     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1554     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1555     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1556     * general issues of how to display a bisected, but mobile, creature.
1557     *
1558     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1559     *                   creature. Non-square creatures are not supported because turning is really hard.
1560     * @param limit      The maximum number of steps to scan outward from a goal.
1561     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
1562     * @param usable how much of impassable to actually use; should usually be equal to impassable.length, but can be
1563     *               anything if impassable is null (then, it is ignored). This exists to differentiate this method from
1564     *               the overload that takes an IntVLA when that argument is null, but also to give some flexibility.
1565     * @param impassable An array of encoded int keys representing the locations of enemies or other moving obstacles to
1566     *                   a path that cannot be moved through; this can be null if there are no such obstacles.
1567     * @return An int array using the dimensions/rotations of what this knows about the physical map.
1568     */
1569    public double[] partialScanToStartLarge(int size, int limit, int start, int usable, int[] impassable) {
1570        if(impassable == null)
1571            partialScanLargeInternal(start, size, limit, null, -1);
1572        else
1573            partialScanLargeInternal(start, size, limit, impassable, Math.min(usable, impassable.length));
1574        int maxLength = gradientMap.length;
1575        double[] gradientClone = new double[maxLength];
1576        for (int l = 0; l < maxLength; l++) {
1577            if (gradientMap[l] == FLOOR) {
1578                gradientMap[l] = DARK;
1579            }
1580        }
1581        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1582        return gradientClone;
1583    }
1584    /**
1585     * Recalculate the CustomDijkstra map, up to a limit, for a creature that is potentially larger than 1x1 cell,
1586     * stopping early if a path is found between a goal and start, and return that map. The value of
1587     * a cell in the returned CustomDijkstra map assumes that a creature is square, with a side length equal to the passed
1588     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1589     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1590     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1591     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1592     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1593     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1594     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1595     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1596     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1597     * current measurement.
1598     * <br>
1599     * Portals and wrapping are not currently recommended in conjunction with multi-square creatures, since a
1600     * 2x2 creature could easily occupy two cells on the east edge and two cells on the west edge of the map,
1601     * and that poses all sorts of issues for creatures trying to pathfind to it, not to mention the more
1602     * general issues of how to display a bisected, but mobile, creature.
1603     *
1604     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1605     *                   creature. Non-square creatures are not supported because turning is really hard.
1606     * @param limit      The maximum number of steps to scan outward from a goal.
1607     * @param start the encoded index of the start of the pathfinder; when this has a path from goal to start, it ends
1608     * @param impassable An IntVLA where items are ints representing the locations of enemies or other moving obstacles
1609     *                   to a path that cannot be moved through; this can be null if there are no such obstacles.
1610     * @return A 2D int[width][height] using the width and height of what this knows about the physical map.
1611     */
1612    public double[] partialScanToStartLarge(int size, int limit, int start, IntVLA impassable) {
1613        if(impassable == null)
1614            partialScanLargeInternal(start, size, limit, null, -1);
1615        else
1616            partialScanLargeInternal(start, size, limit, impassable.items, impassable.size);
1617        int maxLength = gradientMap.length;
1618        double[] gradientClone = new double[maxLength];
1619        for (int l = 0; l < maxLength; l++) {
1620            if (gradientMap[l] == FLOOR) {
1621                gradientMap[l] = DARK;
1622            }
1623        }
1624        System.arraycopy(gradientMap, 0, gradientClone, 0, maxLength);
1625        return gradientClone;
1626    }
1627
1628    protected void partialScanLargeInternal(int start, int size, int limit, int[] impassable, int usable)
1629    {
1630        if (!initialized) return;
1631        Adjacency adjacency = this.adjacency;
1632        int[][] fromNeighbors = neighbors[0];
1633        int near, cen, mid, neighborCount = fromNeighbors.length, sx, sy, sr, sn;
1634
1635        if (impassable != null) {
1636            if(usable > impassable.length)
1637                usable = impassable.length;
1638            for (int i = 0; i < usable; i++) {
1639                adjacency.putAllVariants(null, gradientMap, impassable[i], WALL, -size);
1640            }
1641        }
1642        mappedCount = goals.size;
1643        for (int i = 0; i < mappedCount; i++) {
1644            cen = goals.get(i);
1645            sx = adjacency.extractX(cen);
1646            sy = adjacency.extractY(cen);
1647            sr = adjacency.extractR(cen);
1648            sn = adjacency.extractN(cen);
1649            for (int xx = 0; xx < size; xx++) {
1650                for (int yy = 0; yy < size; yy++) {
1651                    cen = adjacency.composite(sx - xx, sy - yy, sr, sn);
1652                    if(cen >= 0)
1653                        gradientMap[cen] = 0;
1654                }
1655            }
1656        }
1657        double currentLowest = 999000, cs, csm, dist;
1658        fresh.clear();
1659        int maxLength = gradientMap.length;
1660        for (int l = 0; l < maxLength; l++) {
1661            if (gradientMap[l] <= FLOOR) {
1662                if (gradientMap[l] < currentLowest) {
1663                    currentLowest = gradientMap[l];
1664                    fresh.clear();
1665                    fresh.add(l);
1666                } else if (gradientMap[l] == currentLowest) {
1667                    fresh.add(l);
1668                }
1669            }
1670        }
1671        int fsz, numAssigned = fresh.size;
1672        IntDoubleOrderedMap costs = adjacency.costRules;
1673        boolean standardCosts = adjacency.hasStandardCost();
1674        int iter = 0;
1675        while (numAssigned > 0 && iter++ < limit) {
1676            numAssigned = 0;
1677            fsz = fresh.size;
1678            for (int ci = fsz-1; ci >= 0; ci--) {
1679                cen = fresh.removeIndex(ci);
1680                dist = gradientMap[cen];
1681                for (int d = 0; d < neighborCount; d++) {
1682                    near = fromNeighbors[d][cen];
1683                    if (!adjacency.validate(near))
1684                        // Outside the map
1685                        continue;
1686                    if(isBlocked(cen, d))
1687                        continue;
1688                    if(adjacency.twoStepRule) {
1689                        near = fromNeighbors[d][mid = near];
1690                        // Outside the map
1691                        if (!adjacency.validate(near))
1692                            continue;
1693                        if(isBlocked(mid, d))
1694                            continue;
1695                        csm = (costs.get(costMap[mid]) * heuristics[d]);
1696                        cs = (costs.get(costMap[near]) * heuristics[d]);
1697                        if ((gradientMap[mid] = dist + csm) + cs < gradientMap[near]) {
1698                            setFresh(near, dist + cs + csm);
1699                            ++numAssigned;
1700                            ++mappedCount;
1701                            if(start == near && standardCosts)
1702                            {
1703                                if (impassable != null)
1704                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, -size);
1705                                return;
1706                            }
1707                        }
1708                    }
1709                    else
1710                    {
1711                        cs = (costs.get(costMap[near] | (adjacency.extractR(cen) == adjacency.extractR(near) ? 0 : 0x10000))
1712                                * heuristics[d]);
1713                        //int h = adjacency.measurement.heuristic(adjacency.directions[d]);
1714                        if (gradientMap[cen] + cs < gradientMap[near]) {
1715                            setFresh(near, dist + cs);
1716                            ++numAssigned;
1717                            ++mappedCount;
1718                            if(start == near && standardCosts)
1719                            {
1720                                if (impassable != null)
1721                                    adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, -size);
1722                                return;
1723                            }
1724                        }
1725                    }
1726                }
1727            }
1728        }
1729
1730        if (impassable != null)
1731            adjacency.resetAllVariants(gradientMap, impassable, usable, physicalMap, -size);
1732    }
1733
1734    /**
1735     * Scans the dungeon using CustomDijkstraMap.scan with the listed goals and start point, and returns a list
1736     * of Coord positions (using the current measurement) needed to get closer to the closest reachable
1737     * goal. The maximum length of the returned list is given by length; if moving the full length of
1738     * the list would place the mover in a position shared by one of the positions in onlyPassable
1739     * (which is typically filled with friendly units that can be passed through in multi-tile-
1740     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
1741     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1742     * through, and will be ignored if there is a goal overlapping one.
1743     * <br>
1744     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1745     * each call to a pathfinding method.
1746     * @param length       the length of the path to calculate
1747     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
1748     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1749     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1750     * @param targets      a vararg or array of Coord that this will try to pathfind toward
1751     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1752     */
1753    public IntVLA findPath(int length, IntVLA impassable,
1754                           IntVLA onlyPassable, int start, int... targets)
1755    {
1756        return findPath(length, -1, impassable, onlyPassable, start, targets);
1757    }
1758
1759
1760    /**
1761     * Scans the dungeon using CustomDijkstraMap.scan with the listed goals and start point, and returns a list
1762     * of Coord positions (using the current measurement) needed to get closer to the closest reachable
1763     * goal. The maximum length of the returned list is given by length; if moving the full length of
1764     * the list would place the mover in a position shared by one of the positions in onlyPassable
1765     * (which is typically filled with friendly units that can be passed through in multi-tile-
1766     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
1767     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1768     * through, and will be ignored if there is a goal overlapping one.
1769     * <br>
1770     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1771     * each call to a pathfinding method.
1772     * @param length       the length of the path to calculate
1773     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
1774     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1775     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1776     * @param targets      a vararg or array of Coord that this will try to pathfind toward
1777     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1778     */
1779    public IntVLA findPath(int length, int scanLimit, IntVLA impassable,
1780                           IntVLA onlyPassable, int start, int... targets)
1781    {
1782        if (!initialized) return null;
1783        path.clear();
1784        IntVLA impassable2;
1785        if (impassable == null)
1786            impassable2 = new IntVLA();
1787        else {
1788            impassable2 = new IntVLA(impassable);
1789            impassable2.removeValue(start);
1790        }
1791        if (onlyPassable == null)
1792            onlyPassable = new IntVLA();
1793
1794        resetMap();
1795        for (int g = 0; g < targets.length; g++) {
1796            setGoal(targets[g]);
1797        }
1798        if (goals.size == 0)
1799            return new IntVLA(path);
1800        Adjacency adjacency = this.adjacency;
1801        int[][] toNeighbors = neighbors[1];
1802        if(length < 0)
1803            length = 0;
1804        if(scanLimit <= 0 || scanLimit < length)
1805            scanInternal(start, impassable2.items, impassable2.size);
1806        else
1807            partialScanInternal(start, scanLimit, impassable2.items, impassable2.size);
1808
1809        int currentPos = start, pt;
1810        int paidLength = 0;
1811        while (true) {
1812            if (frustration > 500) {
1813                path.clear();
1814                break;
1815            }
1816            double best = gradientMap[currentPos];
1817            rng.randomOrdering(adjacency.maxAdjacent, reuse);
1818            int choice = rng.nextInt(adjacency.maxAdjacent);
1819
1820            for (int d = 0; d < adjacency.maxAdjacent; d++) {
1821                pt = toNeighbors[reuse[d]][currentPos];
1822                if(adjacency.twoStepRule)
1823                    pt = toNeighbors[reuse[d]][pt];
1824                if (gradientMap[pt] < best && !path.contains(pt)) {
1825                    best = gradientMap[pt];
1826                    choice = reuse[d];// adjacency.invertAdjacent[reuse[d]];
1827                }
1828            }
1829
1830
1831            if (best >= gradientMap[currentPos] || physicalMap[toNeighbors[choice][currentPos]] > FLOOR) {
1832                break;
1833            }
1834            currentPos = toNeighbors[choice][pt = currentPos];
1835            if(adjacency.twoStepRule)
1836                currentPos = toNeighbors[choice][currentPos];
1837            path.add(currentPos);
1838            paidLength += adjacency.costRules.get(costMap[currentPos] | (adjacency.extractR(pt) == adjacency.extractR(currentPos) ? 0 : 0x10000));
1839            frustration++;
1840            if (paidLength > length - 1.0) {
1841                if (onlyPassable.contains(currentPos)) {
1842                    setOccupied(currentPos);
1843                    impassable2.add(currentPos);
1844                    return findPath(length, scanLimit, impassable2, onlyPassable, start, targets);
1845                }
1846                break;
1847            }
1848            if (gradientMap[currentPos] == 0)
1849                break;
1850        }
1851        frustration = 0;
1852        goals.clear();
1853        return new IntVLA(path);
1854    }
1855
1856
1857    // TODO: Tackle these next two once there's a CustomLOS class
1858    /*
1859     * Scans the dungeon using CustomDijkstraMap.scan with the listed goals and start point, and returns a list
1860     * of Coord positions (using the current measurement) needed to get closer to a goal, until preferredRange is
1861     * reached, or further from a goal if the preferredRange has not been met at the current distance.
1862     * The maximum length of the returned list is given by moveLength; if moving the full length of
1863     * the list would place the mover in a position shared by one of the positions in onlyPassable
1864     * (which is typically filled with friendly units that can be passed through in multi-tile-
1865     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
1866     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1867     * through, and will be ignored if there is a goal overlapping one.
1868     * <br>
1869     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1870     * each call to a pathfinding method.
1871     *
1872     * @param moveLength     the length of the path to calculate
1873     * @param preferredRange the distance this unit will try to keep from a target
1874     * @param los            a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
1875     *                       should be disregarded.
1876     * @param impassable     a Set of impassable Coord positions that may change (not constant like walls); can be null
1877     * @param onlyPassable   a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1878     * @param start          the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1879     * @param targets        a vararg or array of Coord that this will try to pathfind toward
1880     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1881     * /
1882    public ArrayList<Coord> findAttackPath(int moveLength, int preferredRange, LOS los, Set<Coord> impassable,
1883                                           Set<Coord> onlyPassable, Coord start, Coord... targets) {
1884        return findAttackPath(moveLength, preferredRange, preferredRange, los, impassable, onlyPassable, start, targets);
1885    }
1886    */
1887    /*
1888     * Scans the dungeon using CustomDijkstraMap.scan with the listed goals and start point, and returns a list
1889     * of Coord positions (using the current measurement) needed to get closer to a goal, until a cell is reached with
1890     * a distance from a goal that is at least equal to minPreferredRange and no more than maxPreferredRange,
1891     * which may go further from a goal if the minPreferredRange has not been met at the current distance.
1892     * The maximum length of the returned list is given by moveLength; if moving the full length of
1893     * the list would place the mover in a position shared by one of the positions in onlyPassable
1894     * (which is typically filled with friendly units that can be passed through in multi-tile-
1895     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
1896     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1897     * through, and will be ignored if there is a goal overlapping one.
1898     * <br>
1899     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1900     * each call to a pathfinding method.
1901     *
1902     * @param moveLength        the length of the path to calculate
1903     * @param minPreferredRange the (inclusive) lower bound of the distance this unit will try to keep from a target
1904     * @param maxPreferredRange the (inclusive) upper bound of the distance this unit will try to keep from a target
1905     * @param los               a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
1906     *                          should be disregarded.
1907     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
1908     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1909     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1910     * @param targets           a vararg or array of Coord that this will try to pathfind toward
1911     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1912     * /
1913    public ArrayList<Coord> findAttackPath(int moveLength, int minPreferredRange, int maxPreferredRange, LOS los,
1914                                           Set<Coord> impassable, Set<Coord> onlyPassable, Coord start, Coord... targets) {
1915        if (!initialized) return null;
1916        if (minPreferredRange < 0) minPreferredRange = 0;
1917        if (maxPreferredRange < minPreferredRange) maxPreferredRange = minPreferredRange;
1918        int[][] resMap = new int[width][height];
1919        if (los != null) {
1920            for (int x = 0; x < width; x++) {
1921                for (int y = 0; y < height; y++) {
1922                    resMap[x][y] = (physicalMap[x][y] == WALL) ? 1.0 : 0;
1923                }
1924            }
1925        }
1926        path.clear();
1927        OrderedSet<Coord> impassable2;
1928        if (impassable == null)
1929            impassable2 = new OrderedSet<>();
1930        else
1931            impassable2 = new OrderedSet<>(impassable);
1932        if (onlyPassable == null)
1933            onlyPassable = new OrderedSet<>();
1934
1935        resetMap();
1936        for (Coord goal : targets) {
1937            setGoal(goal.x, goal.y);
1938        }
1939        if (goals.isEmpty())
1940            return new ArrayList<>(path);
1941
1942        Measurement mess = measurement;
1943        if (measurement == Measurement.EUCLIDEAN) {
1944            measurement = Measurement.CHEBYSHEV;
1945        }
1946        scan(impassable2);
1947        goals.clear();
1948
1949        for (int x = 0; x < width; x++) {
1950            CELL:
1951            for (int y = 0; y < height; y++) {
1952                if (gradientMap[x][y] == WALL || gradientMap[x][y] == DARK)
1953                    continue;
1954                if (gradientMap[x][y] >= minPreferredRange && gradientMap[x][y] <= maxPreferredRange) {
1955
1956                    for (Coord goal : targets) {
1957                        if (los == null || los.isReachable(resMap, x, y, goal.x, goal.y)) {
1958                            setGoal(x, y);
1959                            gradientMap[x][y] = 0;
1960                            continue CELL;
1961                        }
1962                    }
1963                    gradientMap[x][y] = FLOOR;
1964                } else
1965                    gradientMap[x][y] = FLOOR;
1966            }
1967        }
1968        measurement = mess;
1969        scan(impassable2);
1970
1971        Coord currentPos = start;
1972        int paidLength = 0;
1973        while (true) {
1974            if (frustration > 500) {
1975                path.clear();
1976                break;
1977            }
1978            int best = gradientMap[currentPos.x][currentPos.y];
1979            final Direction[] dirs = appendDirToShuffle(rng);
1980            int choice = rng.nextInt(dirs.length);
1981
1982            for (int d = 0; d < dirs.length; d++) {
1983                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
1984                if (gradientMap[pt.x][pt.y] < best) {
1985                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
1986                        best = gradientMap[pt.x][pt.y];
1987                        choice = d;
1988                    }
1989                }
1990            }
1991
1992            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
1993                path.clear();
1994                break;
1995            }
1996            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
1997            path.add(Coord.get(currentPos.x, currentPos.y));
1998            paidLength += costMap[currentPos.x][currentPos.y];
1999            frustration++;
2000            if (paidLength > moveLength - 1) {
2001
2002                if (onlyPassable.contains(currentPos)) {
2003
2004                    closed.put(currentPos.encode(), WALL);
2005                    impassable2.add(currentPos);
2006                    return findAttackPath(moveLength, minPreferredRange, maxPreferredRange, los, impassable2,
2007                            onlyPassable, start, targets);
2008                }
2009                break;
2010            }
2011            if (gradientMap[currentPos.x][currentPos.y] == 0)
2012                break;
2013        }
2014        frustration = 0;
2015        goals.clear();
2016        return new ArrayList<>(path);
2017    }
2018    */
2019
2020
2021    private double cachedLongerPaths = 1.2;
2022    private long cachedImpassable, cachedFearSources;
2023    private double[] cachedFleeMap;
2024    private int cachedSize = 1;
2025
2026    /**
2027     * Scans the dungeon using CustomDijkstraMap.scan with the listed fearSources and start point, and returns a list
2028     * of Coord positions (using Manhattan distance) needed to get further from the closest fearSources, meant
2029     * for running away. The maximum length of the returned list is given by length; if moving the full
2030     * length of the list would place the mover in a position shared by one of the positions in onlyPassable
2031     * (which is typically filled with friendly units that can be passed through in multi-tile-
2032     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2033     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2034     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
2035     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
2036     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
2037     * The parameters preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
2038     * any subsequent calls that use the same values as the last values passed will avoid recalculating
2039     * unnecessary scans.
2040     * <br>
2041     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2042     * each call to a pathfinding method.
2043     *
2044     * @param length            the length of the path to calculate
2045     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
2046     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2047     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2048     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2049     * @param fearSources       a vararg or array of Coord positions to run away from
2050     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
2051     */
2052    public IntVLA findFleePath(int length, double preferLongerPaths, IntVLA impassable,
2053                                         IntVLA onlyPassable, int start, int... fearSources) {
2054        return findFleePath(length, -1, preferLongerPaths, impassable, onlyPassable, start, fearSources);
2055    }
2056    /**
2057     * Scans the dungeon using CustomDijkstraMap.scan with the listed fearSources and start point, and returns a list
2058     * of Coord positions (using Manhattan distance) needed to get further from the closest fearSources, meant
2059     * for running away. The maximum length of the returned list is given by length; if moving the full
2060     * length of the list would place the mover in a position shared by one of the positions in onlyPassable
2061     * (which is typically filled with friendly units that can be passed through in multi-tile-
2062     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2063     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2064     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
2065     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
2066     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
2067     * The parameters preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
2068     * any subsequent calls that use the same values as the last values passed will avoid recalculating
2069     * unnecessary scans.
2070     * <br>
2071     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2072     * each call to a pathfinding method.
2073     *
2074     * @param length            the length of the path to calculate
2075     * @param scanLimit         how many steps away from a fear source to calculate; negative scans the whole map
2076     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
2077     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2078     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2079     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2080     * @param fearSources       a vararg or array of Coord positions to run away from
2081     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
2082     */
2083    public IntVLA findFleePath(int length, int scanLimit, double preferLongerPaths, IntVLA impassable,
2084                               IntVLA onlyPassable, int start, int... fearSources) {
2085
2086        if (!initialized) return null;
2087        path.clear();
2088        IntVLA impassable2;
2089        if (impassable == null)
2090            impassable2 = new IntVLA();
2091        else
2092            impassable2 = new IntVLA(impassable);
2093
2094        if (onlyPassable == null)
2095            onlyPassable = new IntVLA();
2096        if (fearSources == null || fearSources.length < 1) {
2097            path.clear();
2098            return new IntVLA(1);
2099        }
2100        if (cachedSize == 1 && preferLongerPaths == cachedLongerPaths && impassable2.hash64() == cachedImpassable &&
2101                CrossHash.hash64(fearSources) == cachedFearSources) {
2102            gradientMap = cachedFleeMap;
2103        } else {
2104            cachedLongerPaths = preferLongerPaths;
2105            cachedImpassable = impassable2.hash64();
2106            cachedFearSources = CrossHash.hash64(fearSources);
2107            cachedSize = 1;
2108            resetMap();
2109            for (int g = 0; g < fearSources.length; g++) {
2110                setGoal(fearSources[g]);
2111            }
2112            if (goals.size == 0)
2113                return new IntVLA(path);
2114            if(length < 0) length = 0;
2115            if(scanLimit <= 0 || scanLimit < length)
2116                cachedFleeMap = scan(impassable2);
2117            else
2118                cachedFleeMap = partialScan(scanLimit, impassable2);
2119
2120            for (int l = 0; l < gradientMap.length; l++) {
2121                gradientMap[l] *= (gradientMap[l] >= FLOOR) ? 1 : - preferLongerPaths;
2122            }
2123            if(scanLimit <= 0 || scanLimit < length)
2124                cachedFleeMap = scan(impassable2);
2125            else
2126                cachedFleeMap = partialScan(scanLimit, impassable2);
2127
2128        }
2129        Adjacency adjacency = this.adjacency;
2130        int[][] toNeighbors = neighbors[1];
2131        int currentPos = start, pt;
2132        int paidLength = 0;
2133        while (true) {
2134            if (frustration > 500) {
2135                path.clear();
2136                break;
2137            }
2138            double best = gradientMap[currentPos];
2139            rng.randomOrdering(adjacency.maxAdjacent, reuse);
2140            int choice = rng.nextInt(adjacency.maxAdjacent);
2141
2142            for (int d = 0; d < adjacency.maxAdjacent; d++) {
2143                pt = toNeighbors[reuse[d]][currentPos];
2144                if (gradientMap[pt] < best && !path.contains(pt)) {
2145                    best = gradientMap[pt];
2146                    choice = reuse[d];
2147                }
2148            }
2149
2150
2151            if (best >= gradientMap[currentPos] || physicalMap[toNeighbors[choice][currentPos]] > FLOOR) {
2152                path.clear();
2153                break;
2154            }
2155            currentPos = toNeighbors[choice][pt = currentPos];
2156            path.add(currentPos);
2157            paidLength += adjacency.costRules.get(costMap[currentPos] | (adjacency.extractR(pt) == adjacency.extractR(currentPos) ? 0 : 0x10000));
2158            frustration++;
2159            if (paidLength > length - 1) {
2160                if (onlyPassable.contains(currentPos)) {
2161                    setOccupied(currentPos);
2162                    impassable2.add(currentPos);
2163                    return findFleePath(length, scanLimit, preferLongerPaths, impassable2, onlyPassable, start, fearSources);
2164                }
2165                break;
2166            }
2167        }
2168        frustration = 0;
2169        goals.clear();
2170        return new IntVLA(path);
2171    }
2172
2173    /**
2174     * Scans the dungeon using CustomDijkstraMap.scan with the listed goals and start point, and returns a list
2175     * of Coord positions (using the current measurement) needed to get closer to the closest reachable
2176     * goal. The maximum length of the returned list is given by length; if moving the full length of
2177     * the list would place the mover in a position shared by one of the positions in onlyPassable
2178     * (which is typically filled with friendly units that can be passed through in multi-tile-
2179     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2180     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2181     * through, and will be ignored if there is a goal overlapping one.
2182     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2183     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2184     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2185     * <br>
2186     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2187     * each call to a pathfinding method.
2188     *
2189     * @param size         the side length of the creature trying to find a path
2190     * @param length       the length of the path to calculate
2191     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
2192     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2193     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2194     * @param targets      a vararg or array of Coord that this will try to pathfind toward
2195     * @return an ArrayList of Coord that will contain the min-x, min-y locations of this creature as it goes toward a target. Copy of path.
2196     */
2197    public IntVLA findPathLarge (int size, int length, IntVLA impassable,
2198                                 IntVLA onlyPassable, int start, int... targets) {
2199        return findPathLarge(size, length, -1, impassable, onlyPassable, start, targets);
2200    }
2201    /**
2202     * Scans the dungeon using CustomDijkstraMap.scanLarge with the listed goals and start point, and returns a list
2203     * of Coord positions (using the current measurement) needed to get closer to the closest reachable
2204     * goal. The maximum length of the returned list is given by length; if moving the full length of
2205     * the list would place the mover in a position shared by one of the positions in onlyPassable
2206     * (which is typically filled with friendly units that can be passed through in multi-tile-
2207     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2208     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2209     * through, and will be ignored if there is a goal overlapping one.
2210     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2211     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2212     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2213     * <br>
2214     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2215     * each call to a pathfinding method.
2216     *
2217     * @param size         the side length of the creature trying to find a path
2218     * @param length       the length of the path to calculate
2219     * @param scanLimit    how many steps away from a goal to calculate; negative scans the whole map
2220     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
2221     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2222     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2223     * @param targets      a vararg or array of Coord that this will try to pathfind toward
2224     * @return an ArrayList of Coord that will contain the min-x, min-y locations of this creature as it goes toward a target. Copy of path.
2225     */
2226    public IntVLA findPathLarge (int size, int length, int scanLimit, IntVLA impassable,
2227                                 IntVLA onlyPassable, int start, int... targets) {
2228        if (!initialized) return null;
2229        path.clear();
2230        IntVLA impassable2;
2231        if (impassable == null)
2232            impassable2 = new IntVLA();
2233        else
2234            impassable2 = new IntVLA(impassable);
2235        if (onlyPassable == null)
2236            onlyPassable = new IntVLA();
2237
2238        resetMap();
2239        for (int g = 0; g < targets.length; g++) {
2240            setGoal(targets[g]);
2241        }
2242        if (goals.size == 0)
2243            return new IntVLA(path);
2244        Adjacency adjacency = this.adjacency;
2245        int[][] toNeighbors = neighbors[1];
2246        if(length < 0)
2247            length = 0;
2248        if(scanLimit <= 0 || scanLimit < length)
2249            scanLargeInternal(start, size, impassable2.items, impassable2.size);
2250        else
2251            partialScanLargeInternal(start, size, scanLimit, impassable2.items, impassable2.size);
2252
2253        int currentPos = start, pt;
2254        int paidLength = 0;
2255        while (true) {
2256            if (frustration > 500) {
2257                path.clear();
2258                break;
2259            }
2260            double best = gradientMap[currentPos];
2261            rng.randomOrdering(adjacency.maxAdjacent, reuse);
2262            int choice = rng.nextInt(adjacency.maxAdjacent);
2263
2264            for (int d = 0; d < adjacency.maxAdjacent; d++) {
2265                pt = toNeighbors[reuse[d]][currentPos];
2266                if (gradientMap[pt] < best && !path.contains(pt)) {
2267                    best = gradientMap[pt];
2268                    choice = reuse[d];
2269                }
2270            }
2271
2272
2273            if (best >= gradientMap[currentPos] || physicalMap[toNeighbors[choice][currentPos]] > FLOOR) {
2274                path.clear();
2275                break;
2276            }
2277            currentPos = toNeighbors[choice][pt = currentPos];
2278            path.add(currentPos);
2279            paidLength += adjacency.costRules.get(costMap[currentPos] | (adjacency.extractR(pt) == adjacency.extractR(currentPos) ? 0 : 0x10000));
2280            frustration++;
2281            if (paidLength > length - 1) {
2282                if (onlyPassable.contains(currentPos)) {
2283                    setOccupied(currentPos);
2284                    impassable2.add(currentPos);
2285                    return findPathLarge(size, scanLimit, length, impassable2, onlyPassable, start, targets);
2286                }
2287                break;
2288            }
2289            if (gradientMap[currentPos] == 0)
2290                break;
2291        }
2292        frustration = 0;
2293        goals.clear();
2294        return new IntVLA(path);
2295    }
2296
2297    /*
2298    public ArrayList<Coord> findPathLarge(int size, int length, Set<Coord> impassable,
2299                                          Set<Coord> onlyPassable, Coord start, Coord... targets) {
2300        if (!initialized) return null;
2301        path.clear();
2302        OrderedSet<Coord> impassable2;
2303        if (impassable == null)
2304            impassable2 = new OrderedSet<>();
2305        else
2306            impassable2 = new OrderedSet<>(impassable);
2307
2308        if (onlyPassable == null)
2309            onlyPassable = new OrderedSet<>();
2310
2311        resetMap();
2312        for (Coord goal : targets) {
2313            setGoal(goal.x, goal.y);
2314        }
2315        if (goals.isEmpty())
2316            return new ArrayList<>(path);
2317
2318        scan(impassable2, size);
2319        Coord currentPos = start;
2320        int paidLength = 0;
2321        while (true) {
2322            if (frustration > 500) {
2323                path.clear();
2324                break;
2325            }
2326            int best = gradientMap[currentPos.x][currentPos.y];
2327            final Direction[] dirs = appendDirToShuffle(rng);
2328            int choice = rng.nextInt(dirs.length);
2329
2330            for (int d = 0; d < dirs.length; d++) {
2331                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2332                if (gradientMap[pt.x][pt.y] < best) {
2333                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2334                        best = gradientMap[pt.x][pt.y];
2335                        choice = d;
2336                    }
2337                }
2338            }
2339
2340            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2341                path.clear();
2342                break;
2343            }
2344            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2345
2346            path.add(currentPos);
2347            paidLength += costMap[currentPos.x][currentPos.y];
2348            frustration++;
2349            if (paidLength > length - 1) {
2350                if (onlyPassable.contains(currentPos)) {
2351
2352                    closed.put(currentPos.encode(), WALL);
2353                    impassable2.add(currentPos);
2354                    return findPathLarge(size, length, impassable2, onlyPassable, start, targets);
2355                }
2356                break;
2357            }
2358            if (gradientMap[currentPos.x][currentPos.y] == 0)
2359                break;
2360        }
2361        frustration = 0;
2362        goals.clear();
2363        return new ArrayList<>(path);
2364    }
2365    */
2366    // TODO: Again, this needs CustomLOS
2367    /*
2368     * Scans the dungeon using CustomDijkstraMap.scan with the listed goals and start point, and returns a list
2369     * of Coord positions (using the current measurement) needed to get closer to a goal, until preferredRange is
2370     * reached, or further from a goal if the preferredRange has not been met at the current distance.
2371     * The maximum length of the returned list is given by moveLength; if moving the full length of
2372     * the list would place the mover in a position shared by one of the positions in onlyPassable
2373     * (which is typically filled with friendly units that can be passed through in multi-tile-
2374     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2375     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2376     * through, and will be ignored if there is a goal overlapping one.
2377     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2378     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2379     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2380     * <br>
2381     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2382     * each call to a pathfinding method.
2383     *
2384     * @param size           the side length of the creature trying to find a path
2385     * @param moveLength     the length of the path to calculate
2386     * @param preferredRange the distance this unit will try to keep from a target
2387     * @param los            a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
2388     *                       should be disregarded.
2389     * @param impassable     a Set of impassable Coord positions that may change (not constant like walls); can be null
2390     * @param onlyPassable   a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2391     * @param start          the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2392     * @param targets        a vararg or array of Coord that this will try to pathfind toward
2393     * @return an ArrayList of Coord that will contain the min-x, min-y locations of this creature as it goes toward a target. Copy of path.
2394     * /
2395    public ArrayList<Coord> findAttackPathLarge(int size, int moveLength, int preferredRange, LOS los, Set<Coord> impassable,
2396                                                Set<Coord> onlyPassable, Coord start, Coord... targets) {
2397        if (!initialized) return null;
2398        if (preferredRange < 0) preferredRange = 0;
2399        int[][] resMap = new int[width][height];
2400        if (los != null) {
2401            for (int x = 0; x < width; x++) {
2402                for (int y = 0; y < height; y++) {
2403                    resMap[x][y] = (physicalMap[x][y] == WALL) ? 1 : 0;
2404                }
2405            }
2406        }
2407        path.clear();
2408        OrderedSet<Coord> impassable2;
2409        if (impassable == null)
2410            impassable2 = new OrderedSet<>();
2411        else
2412            impassable2 = new OrderedSet<>(impassable);
2413
2414        if (onlyPassable == null)
2415            onlyPassable = new OrderedSet<>();
2416
2417        resetMap();
2418        for (Coord goal : targets) {
2419            setGoal(goal.x, goal.y);
2420        }
2421        if (goals.isEmpty())
2422            return new ArrayList<>(path);
2423
2424        Measurement mess = measurement;
2425        if (measurement == Measurement.EUCLIDEAN) {
2426            measurement = Measurement.CHEBYSHEV;
2427        }
2428        scan(impassable2, size);
2429        goals.clear();
2430
2431        for (int x = 0; x < width; x++) {
2432            CELL:
2433            for (int y = 0; y < height; y++) {
2434                if (gradientMap[x][y] == WALL || gradientMap[x][y] == DARK)
2435                    continue;
2436                if (x + 2 < width && y + 2 < height && gradientMap[x][y] == preferredRange) {
2437                    for (Coord goal : targets) {
2438                        if (los == null
2439                                || los.isReachable(resMap, x, y, goal.x, goal.y)
2440                                || los.isReachable(resMap, x + 1, y, goal.x, goal.y)
2441                                || los.isReachable(resMap, x, y + 1, goal.x, goal.y)
2442                                || los.isReachable(resMap, x + 1, y + 1, goal.x, goal.y)) {
2443                            setGoal(x, y);
2444                            gradientMap[x][y] = 0;
2445                            continue CELL;
2446                        }
2447                    }
2448                    gradientMap[x][y] = FLOOR;
2449                } else
2450                    gradientMap[x][y] = FLOOR;
2451            }
2452        }
2453        measurement = mess;
2454        scan(impassable2, size);
2455
2456        Coord currentPos = start;
2457        int paidLength = 0;
2458        while (true) {
2459            if (frustration > 500) {
2460                path.clear();
2461                break;
2462            }
2463            int best = gradientMap[currentPos.x][currentPos.y];
2464            final Direction[] dirs = appendDirToShuffle(rng);
2465            int choice = rng.nextInt(dirs.length);
2466
2467            for (int d = 0; d < dirs.length; d++) {
2468                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2469                if (gradientMap[pt.x][pt.y] < best) {
2470                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2471                        best = gradientMap[pt.x][pt.y];
2472                        choice = d;
2473                    }
2474                }
2475            }
2476
2477            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2478                path.clear();
2479                break;
2480            }
2481            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2482            path.add(currentPos);
2483            frustration++;
2484            paidLength += costMap[currentPos.x][currentPos.y];
2485            if (paidLength > moveLength - 1) {
2486                if (onlyPassable.contains(currentPos)) {
2487
2488                    closed.put(currentPos.encode(), WALL);
2489                    impassable2.add(currentPos);
2490                    return findAttackPathLarge(size, moveLength, preferredRange, los, impassable2, onlyPassable, start, targets);
2491                }
2492                break;
2493            }
2494            if (gradientMap[currentPos.x][currentPos.y] == 0)
2495                break;
2496        }
2497        frustration = 0;
2498        goals.clear();
2499        return new ArrayList<>(path);
2500    }
2501
2502    /*
2503     * Scans the dungeon using CustomDijkstraMap.scan with the listed goals and start point, and returns a list
2504     * of Coord positions (using the current measurement) needed to get closer to a goal, until a cell is reached with
2505     * a distance from a goal that is at least equal to minPreferredRange and no more than maxPreferredRange,
2506     * which may go further from a goal if the minPreferredRange has not been met at the current distance.
2507     * The maximum length of the returned list is given by moveLength; if moving the full length of
2508     * the list would place the mover in a position shared by one of the positions in onlyPassable
2509     * (which is typically filled with friendly units that can be passed through in multi-tile-
2510     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2511     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2512     * through, and will be ignored if there is a goal overlapping one.
2513     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2514     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2515     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2516     * <br>
2517     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2518     * each call to a pathfinding method.
2519     *
2520     * @param size              the side length of the creature trying to find a path
2521     * @param moveLength        the length of the path to calculate
2522     * @param minPreferredRange the (inclusive) lower bound of the distance this unit will try to keep from a target
2523     * @param maxPreferredRange the (inclusive) upper bound of the distance this unit will try to keep from a target
2524     * @param los               a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
2525     *                          should be disregarded.
2526     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2527     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2528     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2529     * @param targets           a vararg or array of Coord that this will try to pathfind toward
2530     * @return an ArrayList of Coord that will contain the min-x, min-y locations of this creature as it goes toward a target. Copy of path.
2531     * /
2532    public ArrayList<Coord> findAttackPathLarge(int size, int moveLength, int minPreferredRange, int maxPreferredRange, LOS los,
2533                                                Set<Coord> impassable, Set<Coord> onlyPassable, Coord start, Coord... targets) {
2534        if (!initialized) return null;
2535        if (minPreferredRange < 0) minPreferredRange = 0;
2536        if (maxPreferredRange < minPreferredRange) maxPreferredRange = minPreferredRange;
2537        int[][] resMap = new int[width][height];
2538        if (los != null) {
2539            for (int x = 0; x < width; x++) {
2540                for (int y = 0; y < height; y++) {
2541                    resMap[x][y] = (physicalMap[x][y] == WALL) ? 1 : 0;
2542                }
2543            }
2544        }
2545        path.clear();
2546        OrderedSet<Coord> impassable2;
2547        if (impassable == null)
2548            impassable2 = new OrderedSet<>();
2549        else
2550            impassable2 = new OrderedSet<>(impassable);
2551
2552        if (onlyPassable == null)
2553            onlyPassable = new OrderedSet<>();
2554
2555        resetMap();
2556        for (Coord goal : targets) {
2557            setGoal(goal);
2558        }
2559        if (goals.isEmpty())
2560            return new ArrayList<>(path);
2561
2562        Measurement mess = measurement;
2563        if (measurement == Measurement.EUCLIDEAN) {
2564            measurement = Measurement.CHEBYSHEV;
2565        }
2566        scan(impassable2, size);
2567        goals.clear();
2568
2569        for (int x = 0; x < width; x++) {
2570            CELL:
2571            for (int y = 0; y < height; y++) {
2572                if (gradientMap[x][y] == WALL || gradientMap[x][y] == DARK)
2573                    continue;
2574                if (x + 2 < width && y + 2 < height && gradientMap[x][y] >= minPreferredRange && gradientMap[x][y] <= maxPreferredRange) {
2575                    for (Coord goal : targets) {
2576                        if (los == null
2577                                || los.isReachable(resMap, x, y, goal.x, goal.y)
2578                                || los.isReachable(resMap, x + 1, y, goal.x, goal.y)
2579                                || los.isReachable(resMap, x, y + 1, goal.x, goal.y)
2580                                || los.isReachable(resMap, x + 1, y + 1, goal.x, goal.y)) {
2581                            setGoal(x, y);
2582                            gradientMap[x][y] = 0;
2583                            continue CELL;
2584                        }
2585                    }
2586                    gradientMap[x][y] = FLOOR;
2587                } else
2588                    gradientMap[x][y] = FLOOR;
2589            }
2590        }
2591        measurement = mess;
2592        scan(impassable2, size);
2593
2594        Coord currentPos = start;
2595        int paidLength = 0;
2596        while (true) {
2597            if (frustration > 500) {
2598                path.clear();
2599                break;
2600            }
2601
2602            int best = gradientMap[currentPos.x][currentPos.y];
2603            final Direction[] dirs = appendDirToShuffle(rng);
2604            int choice = rng.nextInt(dirs.length);
2605
2606            for (int d = 0; d < dirs.length; d++) {
2607                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2608                if (gradientMap[pt.x][pt.y] < best) {
2609                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2610                        best = gradientMap[pt.x][pt.y];
2611                        choice = d;
2612                    }
2613                }
2614            }
2615            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2616                path.clear();
2617                break;
2618            }
2619            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2620
2621            path.add(currentPos);
2622            frustration++;
2623            paidLength += costMap[currentPos.x][currentPos.y];
2624            if (paidLength > moveLength - 1) {
2625                if (onlyPassable.contains(currentPos)) {
2626
2627                    closed.put(currentPos.encode(), WALL);
2628                    impassable2.add(currentPos);
2629                    return findAttackPathLarge(size, moveLength, minPreferredRange, maxPreferredRange, los, impassable2,
2630                            onlyPassable, start, targets);
2631                }
2632                break;
2633            }
2634            if (gradientMap[currentPos.x][currentPos.y] == 0)
2635                break;
2636        }
2637        frustration = 0;
2638        goals.clear();
2639        return new ArrayList<>(path);
2640    }
2641    */
2642    /**
2643     * Scans the dungeon using CustomDijkstraMap.scanLarge with the listed fearSources and start point, and returns a list
2644     * of Coord positions (using Manhattan distance) needed to get further from the closest fearSources, meant
2645     * for running away. The maximum length of the returned list is given by length; if moving the full
2646     * length of the list would place the mover in a position shared by one of the positions in onlyPassable
2647     * (which is typically filled with friendly units that can be passed through in multi-tile-
2648     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2649     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2650     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
2651     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
2652     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
2653     * The parameters size, preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
2654     * any subsequent calls that use the same values as the last values passed will avoid recalculating
2655     * unnecessary scans. Calls to findFleePath will cache as if size is 1, and may share a cache with this function.
2656     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2657     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2658     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2659     * <br>
2660     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2661     * each call to a pathfinding method.
2662     *
2663     * @param size              the side length of the creature trying the find a path
2664     * @param length            the length of the path to calculate
2665     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
2666     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2667     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2668     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2669     * @param fearSources       a vararg or array of Coord positions to run away from
2670     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
2671     */
2672    public IntVLA findFleePathLarge(int size, int length, int preferLongerPaths, IntVLA impassable,
2673                                    IntVLA onlyPassable, int start, int... fearSources) {
2674        return findFleePathLarge(size, length, -1, preferLongerPaths, impassable, onlyPassable, start, fearSources);
2675    }
2676
2677
2678    /**
2679     * Scans the dungeon using CustomDijkstraMap.scanLarge with the listed fearSources and start point, and returns a list
2680     * of Coord positions (using Manhattan distance) needed to get further from the closest fearSources, meant
2681     * for running away. The maximum length of the returned list is given by length; if moving the full
2682     * length of the list would place the mover in a position shared by one of the positions in onlyPassable
2683     * (which is typically filled with friendly units that can be passed through in multi-tile-
2684     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2685     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2686     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
2687     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
2688     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
2689     * The parameters size, preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
2690     * any subsequent calls that use the same values as the last values passed will avoid recalculating
2691     * unnecessary scans. Calls to findFleePath will cache as if size is 1, and may share a cache with this function.
2692     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2693     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2694     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2695     * <br>
2696     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2697     * each call to a pathfinding method.
2698     *
2699     * @param size              the side length of the creature trying the find a path
2700     * @param length            the length of the path to calculate
2701     * @param scanLimit         how many steps away from a goal to calculate; negative scans the whole map
2702     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
2703     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2704     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2705     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2706     * @param fearSources       a vararg or array of Coord positions to run away from
2707     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
2708     */
2709    public IntVLA findFleePathLarge(int size, int length, int scanLimit, int preferLongerPaths, IntVLA impassable,
2710                                    IntVLA onlyPassable, int start, int... fearSources) {
2711        if (!initialized) return null;
2712        path.clear();
2713        IntVLA impassable2;
2714        if (impassable == null)
2715            impassable2 = new IntVLA();
2716        else
2717            impassable2 = new IntVLA(impassable);
2718
2719        if (onlyPassable == null)
2720            onlyPassable = new IntVLA();
2721        if (fearSources == null || fearSources.length < 1) {
2722            path.clear();
2723            return new IntVLA(1);
2724        }
2725        if (cachedSize == size && preferLongerPaths == cachedLongerPaths && impassable2.hash64() == cachedImpassable &&
2726                CrossHash.hash64(fearSources) == cachedFearSources) {
2727            gradientMap = cachedFleeMap;
2728        } else {
2729            cachedLongerPaths = preferLongerPaths;
2730            cachedImpassable = impassable2.hash64();
2731            cachedFearSources = CrossHash.hash64(fearSources);
2732            cachedSize = size;
2733            resetMap();
2734            for (int g = 0; g < fearSources.length; g++) {
2735                setGoal(fearSources[g]);
2736            }
2737            if (goals.size == 0)
2738                return new IntVLA(path);
2739
2740            if(scanLimit <= 0 || scanLimit < length)
2741                cachedFleeMap = scanLarge(size, impassable2);
2742            else
2743                cachedFleeMap = partialScanLarge(size, scanLimit, impassable2);
2744
2745            for (int l = 0; l < gradientMap.length; l++) {
2746                gradientMap[l] *= (gradientMap[l] >= FLOOR) ? 1 : -preferLongerPaths;
2747            }
2748            if(scanLimit <= 0 || scanLimit < length)
2749                cachedFleeMap = scanLarge(size, impassable2);
2750            else
2751                cachedFleeMap = partialScanLarge(size, scanLimit, impassable2);
2752        }
2753        Adjacency adjacency = this.adjacency;
2754        int[][] toNeighbors = neighbors[1];
2755        int currentPos = start, pt;
2756        int paidLength = 0;
2757        while (true) {
2758            if (frustration > 500) {
2759                path.clear();
2760                break;
2761            }
2762            double best = gradientMap[currentPos];
2763            rng.randomOrdering(adjacency.maxAdjacent, reuse);
2764            int choice = rng.nextInt(adjacency.maxAdjacent);
2765
2766            for (int d = 0; d < adjacency.maxAdjacent; d++) {
2767                pt = toNeighbors[reuse[d]][currentPos];
2768                if (gradientMap[pt] < best && !path.contains(pt)) {
2769                    best = gradientMap[pt];
2770                    choice = reuse[d];
2771                }
2772            }
2773
2774
2775            if (best >= gradientMap[currentPos] || physicalMap[toNeighbors[choice][currentPos]] > FLOOR) {
2776                path.clear();
2777                break;
2778            }
2779            currentPos = toNeighbors[choice][pt = currentPos];
2780            path.add(currentPos);
2781            paidLength += adjacency.costRules.get(costMap[currentPos] | (adjacency.extractR(pt) == adjacency.extractR(currentPos) ? 0 : 0x10000));
2782            frustration++;
2783            if (paidLength > length - 1) {
2784                if (onlyPassable.contains(currentPos)) {
2785                    setOccupied(currentPos);
2786                    impassable2.add(currentPos);
2787                    return findFleePathLarge(size, scanLimit, length, preferLongerPaths, impassable2, onlyPassable, start, fearSources);
2788                }
2789                break;
2790            }
2791        }
2792        frustration = 0;
2793        goals.clear();
2794        return new IntVLA(path);
2795    }
2796
2797    /*
2798     * Intended primarily for internal use. Needs scan() to already be called and at least one goal to already be set,
2799     * and does not restrict the length of the path or behave as if the pathfinder has allies or enemies.
2800     * <br>
2801     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2802     * each call to a pathfinding method.
2803     *
2804     * @param target the target cell
2805     * @return an ArrayList of Coord that make up the best path. Copy of path.
2806     * /
2807    public ArrayList<Coord> findPathPreScanned(Coord target) {
2808        if (!initialized || goals == null || goals.size == 0) return null;
2809        RNG rng2 = new StatefulRNG(new LightRNG(0xf00d));
2810        path.clear();
2811        Coord currentPos = target;
2812        while (true) {
2813            if (frustration > 2000) {
2814                path.clear();
2815                break;
2816            }
2817            int best = gradientMap[currentPos.x][currentPos.y];
2818            final Direction[] dirs = appendDirToShuffle(rng2);
2819            int choice = rng2.nextInt(dirs.length);
2820
2821            for (int d = 0; d < dirs.length; d++) {
2822                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2823                if (gradientMap[pt.x][pt.y] < best) {
2824                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2825                        best = gradientMap[pt.x][pt.y];
2826                        choice = d;
2827                    }
2828                }
2829            }
2830
2831            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2832                path.clear();
2833                break;
2834            }
2835            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2836            path.add(0, currentPos);
2837            frustration++;
2838
2839            if (gradientMap[currentPos.x][currentPos.y] == 0)
2840                break;
2841        }
2842        frustration = 0;
2843        return new ArrayList<>(path);
2844    }
2845     */
2846    /**
2847     * A simple limited flood-fill that returns a OrderedMap of Coord keys to the Double values in the CustomDijkstraMap, only
2848     * calculating out to a number of steps determined by limit. This can be useful if you need many flood-fills and
2849     * don't need a large area for each, or if you want to have an effect spread to a certain number of cells away.
2850     *
2851     * @param radius the number of steps to take outward from each starting position.
2852     * @param starts a vararg group of Points to step outward from; this often will only need to be one Coord.
2853     * @return A OrderedMap of Coord keys to Double values; the starts are included in this with the value 0.
2854     */
2855    public IntDoubleOrderedMap floodFill(int radius, int... starts) {
2856        if (!initialized || starts == null) return null;
2857        IntDoubleOrderedMap fill = new IntDoubleOrderedMap();
2858
2859        resetMap();
2860        for (int g = 0; g < starts.length; g++) {
2861            setGoal(starts[g]);
2862        }
2863
2864        if (goals.size == 0)
2865            return fill;
2866
2867        partialScan(radius, -1, null);
2868        double temp;
2869        for (int l = 0; l < gradientMap.length; l++) {
2870            temp = gradientMap[l];
2871            if (temp < FLOOR) {
2872                fill.put(l, temp);
2873            }
2874        }
2875        goals.clear();
2876        return fill;
2877    }
2878
2879    public int getMappedCount() {
2880        return mappedCount;
2881    }
2882
2883
2884    /*
2885    public static void main(String[] args) {
2886        squidpony.squidgrid.mapping.DungeonGenerator dungeonGen =
2887                new squidpony.squidgrid.mapping.DungeonGenerator(40, 40, new RNG(0x1337BEEFDEAL));
2888        char[][] map = dungeonGen.generate();
2889        squidpony.squidgrid.mapping.DungeonUtility.debugPrint(map);
2890        squidpony.squidmath.GreasedRegion floors = new squidpony.squidmath.GreasedRegion(map, '.');
2891        System.out.println("Floors: " + floors.size());
2892        System.out.println("Percentage walkable: " + floors.size() / 16.0 + "%");
2893        Adjacency adj = new BasicAdjacency(40, 40, Measurement.EUCLIDEAN);
2894        adj.blockingRule = 2;
2895        RNG rng = new RNG(0x1337BEEF);
2896        CustomDijkstraMap dijkstra = new CustomDijkstraMap(
2897                map, adj, rng);
2898        double[] scanned;
2899        short[][] sMap = new short[40][40];
2900        //for (int x = 1; x < 39; x++) {
2901        //    for (int y = 1; y < 39; y++) {
2902        squidpony.squidmath.Coord c = floors.singleRandom(rng);
2903        dijkstra.setGoal(adj.composite(c.x, c.y, 0, 0));
2904        scanned = dijkstra.scan(null);
2905        dijkstra.clearGoals();
2906        dijkstra.resetMap();
2907        System.out.println("MAPPED: " + dijkstra.getMappedCount());
2908        for (int i = 0; i < 1600; i++) {
2909            sMap[adj.extractX(i)][adj.extractY(i)] = (scanned[i] >= FLOOR ? -999 : (short) (scanned[i] * 100));
2910        }
2911        for (int yy = 0; yy < 40; yy++) {
2912            for (int xx = 0; xx < 40; xx++) {
2913                System.out.print((sMap[xx][yy] == -999) ? "#### " : squidpony.StringKit.hex(sMap[xx][yy]) + ' ');
2914            }
2915            System.out.println();
2916        }
2917        //    }
2918        //}
2919    }
2920    */
2921}