001package squidpony.squidai;
002
003import squidpony.ArrayTools;
004import squidpony.Maker;
005import squidpony.squidgrid.Direction;
006import squidpony.squidgrid.LOS;
007import squidpony.squidgrid.Measurement;
008import squidpony.squidgrid.Radius;
009import squidpony.squidmath.*;
010
011import java.io.Serializable;
012import java.util.*;
013
014/**
015 * An alternative to AStarSearch when you want to fully explore a search space, or when you want a gradient floodfill.
016 * It's currently significantly faster that AStarSearch, and also supports pathfinding to the nearest of multiple
017 * goals, which is not possible with AStarSearch. This last feature enables a whole host of others, like pathfinding
018 * for creatures that can attack targets between a specified minimum and maximum distance, and there's also the
019 * standard uses of Dijkstra Maps such as finding ideal paths to run away. One unique optimization made possible by
020 * Dijkstra Maps is for when only one endpoint of a path can change in some section of a game, such as when you want to
021 * draw a path from the player's current cell to the cell the mouse is over, and while the mouse can move quickly, the
022 * player doesn't move until instructed to. This can be done very efficiently by setting the player as a goal with
023 * {@link #setGoal(Coord)}, scanning the map to find distances with {@link #scan(Collection)}, and then as long as the
024 * player's position is unchanged (and no obstacles are added/moved), you can get the path by calling
025 * {@link #findPathPreScanned(Coord)} and giving it the mouse position as a Coord. If various parts of the path can
026 * change instead of just one (such as other NPCs moving around), then you should set a goal or goals and call
027 * {@link #findPath(int, Collection, Collection, Coord, Coord...)}. The parameters for this are used in various methods
028 * in this class with only slight differences: length is the length of path that can be moved "in one go," so 1 for most
029 * roguelikes and more for most strategy games, impassable used for enemies and solid moving obstacles, onlyPassable can
030 * be null in most roguelikes but in strategy games should contain ally positions that can be moved through as long as
031 * no one stops in them, start is the NPC's starting position, and targets is an array or vararg of Coord that the NPC
032 * should pathfind toward (it could be just one Coord, with or without explicitly putting it in an array, or it could be
033 * more and the NPC will pick the closest).
034 * <br>
035 * As a bit of introduction, the article http://www.roguebasin.com/index.php?title=Dijkstra_Maps_Visualized can
036 * provide some useful information on how these work and how to visualize the information they can produce, while
037 * http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps is an inspiring list of the
038 * various features Dijkstra Maps can enable.
039 * <br>
040 * If you can't remember how to spell this, just remember: Does It Just Know Stuff? That's Really Awesome!
041 * Created by Tommy Ettinger on 4/4/2015.
042 */
043public class DijkstraMap implements Serializable {
044    private static final long serialVersionUID = -2456306898212944441L;
045
046    /**
047     * This affects how distance is measured on diagonal directions vs. orthogonal directions. MANHATTAN should form a
048     * diamond shape on a featureless map, while CHEBYSHEV and EUCLIDEAN will form a square. EUCLIDEAN does not affect
049     * the length of paths, though it will change the DijkstraMap's gradientMap to have many non-integer values, and
050     * that in turn will make paths this finds much more realistic and smooth (favoring orthogonal directions unless a
051     * diagonal one is a better option).
052     */
053    public Measurement measurement = Measurement.MANHATTAN;
054
055
056    /**
057     * Stores which parts of the map are accessible and which are not. Should not be changed unless the actual physical
058     * terrain has changed. You should call initialize() with a new map instead of changing this directly.
059     */
060    public double[][] physicalMap;
061    /**
062     * The frequently-changing values that are often the point of using this class; goals will have a value of 0, and
063     * any cells that can have a character reach a goal in n steps will have a value of n. Cells that cannot be
064     * entered because they are solid will have a very high value equal to the WALL constant in this class, and cells
065     * that cannot be entered because they cannot reach a goal will have a different very high value equal to the
066     * DARK constant in this class.
067     */
068    public double[][] gradientMap;
069    /**
070     * This stores the entry cost multipliers for each cell; that is, a value of 1.0 is a normal, unmodified cell, but
071     * a value of 0.5 can be entered easily (two cells of its cost can be entered for the cost of one 1.0 cell), and a
072     * value of 2.0 can only be entered with difficulty (one cell of its cost can be entered for the cost of two 1.0
073     * cells). Unlike the measurement field, this does affect the length of paths, as well as the numbers assigned
074     * to gradientMap during a scan. The values for walls are identical to the value used by gradientMap, that is, this
075     * class' WALL static final field. Floors, however, are never given FLOOR as a value, and default to 1.0 .
076     */
077    public double[][] costMap;
078
079    public boolean standardCosts = true;
080    /**
081     * Height of the map. Exciting stuff. Don't change this, instead call initialize().
082     */
083    public int height;
084    /**
085     * Width of the map. Exciting stuff. Don't change this, instead call initialize().
086     */
087    public int width;
088    /**
089     * The latest path that was obtained by calling findPath(). It will not contain the value passed as a starting
090     * cell; only steps that require movement will be included, and so if the path has not been found or a valid
091     * path toward a goal is impossible, this ArrayList will be empty.
092     */
093    public ArrayList<Coord> path;
094
095    private HashSet<Coord> impassable2, friends, tempSet;
096    
097    public boolean cutShort;
098
099    /**
100     * Goals are always marked with 0.
101     */
102    public static final double GOAL = 0.0;
103    /**
104     * Floor cells, which include any walkable cell, are marked with a high number equal to 999200.0 .
105     */
106    public static final double FLOOR = 999200.0;
107    /**
108     * Walls, which are solid no-entry cells, are marked with a high number equal to 999500.0 .
109     */
110    public static final double WALL = 999500.0;
111    /**
112     * This is used to mark cells that the scan couldn't reach, and these dark cells are marked with a high number
113     * equal to 999800.0 .
114     */
115    public static final double DARK = 999800.0;
116    /**
117     * Goals that pathfinding will seek out. The Double value should almost always be 0.0 , the same as the static GOAL
118     * constant in this class.
119     */
120    protected IntVLA goals = new IntVLA(256), fresh = new IntVLA(256);
121
122    /**
123     * The IRNG used to decide which one of multiple equally-short paths to take. You may want to give this a
124     * {@link GWTRNG} if you may target web browsers with GWT, or a {@link RNG} or {@link StatefulRNG} otherwise.
125     */
126    public IRNG rng;
127    private int frustration;
128    public Coord[][] targetMap;
129
130    private Direction[] dirs = new Direction[9];
131
132    private boolean initialized;
133
134    private int mappedCount;
135
136    private int blockingRequirement = 2;
137
138    /**
139     * Construct a DijkstraMap without a level to actually scan. If you use this constructor, you must call an
140     * initialize() method before using this class.
141     */
142    public DijkstraMap() {
143        rng = new GWTRNG();
144        path = new ArrayList<>();
145    }
146
147    /**
148     * Construct a DijkstraMap without a level to actually scan. This constructor allows you to specify an IRNG, such as
149     * an {@link RNG}, before it is ever used in this class. If you use this constructor, you must call an initialize()
150     * method before using any other methods in the class.
151     */
152    public DijkstraMap(IRNG random) {
153        rng = random;
154        path = new ArrayList<>();
155    }
156
157    /**
158     * Used to construct a DijkstraMap from the output of another.
159     *
160     * @param level
161     */
162    public DijkstraMap(final double[][] level) {
163        this(level, Measurement.MANHATTAN);
164    }
165
166    /**
167     * Used to construct a DijkstraMap from the output of another, specifying a distance calculation.
168     *
169     * @param level
170     * @param measurement
171     */
172    public DijkstraMap(final double[][] level, Measurement measurement) {
173        rng = new GWTRNG();
174        this.measurement = measurement;
175        path = new ArrayList<>();
176        initialize(level);
177    }
178
179    /**
180     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
181     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
182     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
183     * map that can be used here.
184     *
185     * @param level
186     */
187    public DijkstraMap(final char[][] level) {
188        this(level, Measurement.MANHATTAN, new GWTRNG());
189    }
190
191    /**
192     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
193     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
194     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
195     * map that can be used here. Also takes an IRNG, such as an {@link RNG}, that ensures
196     * predictable path choices given otherwise identical inputs and circumstances.
197     *
198     * @param level
199     * @param rng   The RNG to use for certain decisions; only affects find* methods like findPath, not scan.
200     */
201    public DijkstraMap(final char[][] level, IRNG rng) {
202        this(level, Measurement.MANHATTAN, rng);
203    }
204
205    /**
206     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
207     * char[][] where one char means a wall and anything else is a walkable tile. If you only have
208     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
209     * map that can be used here. You can specify the character used for walls.
210     *
211     * @param level
212     */
213    public DijkstraMap(final char[][] level, char alternateWall) {
214        rng = new GWTRNG();
215        path = new ArrayList<>();
216
217        initialize(level, alternateWall);
218    }
219
220    /**
221     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
222     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
223     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
224     * map that can be used here. This constructor specifies a distance measurement.
225     *
226     * @param level
227     * @param measurement
228     */
229    public DijkstraMap(final char[][] level, Measurement measurement) {
230        this(level, measurement, new GWTRNG());
231    }
232
233    /**
234     * Constructor meant to take a char[][] returned by DungeonBoneGen.generate(), or any other
235     * char[][] where '#' means a wall and anything else is a walkable tile. If you only have
236     * a map that uses box-drawing characters, use DungeonUtility.linesToHashes() to get a
237     * map that can be used here. Also takes a distance measurement and an RNG that ensures
238     * predictable path choices given otherwise identical inputs and circumstances.
239     *
240     * @param level
241     * @param rng   The RNG to use for certain decisions; only affects find* methods like findPath, not scan.
242     */
243    public DijkstraMap(final char[][] level, Measurement measurement, IRNG rng) {
244        this.rng = rng;
245        path = new ArrayList<>();
246        this.measurement = measurement;
247
248        initialize(level);
249    }
250
251    /**
252     * Used to initialize or re-initialize a DijkstraMap that needs a new physicalMap because it either wasn't given
253     * one when it was constructed, or because the contents of the terrain have changed permanently (not if a
254     * creature moved; for that you pass the positions of creatures that block paths to scan() or findPath() ).
255     *
256     * @param level a 2D double array that should be used as the physicalMap for this DijkstraMap
257     * @return this for chaining
258     */
259    public DijkstraMap initialize(final double[][] level) {
260        width = level.length;
261        height = level[0].length;
262        gradientMap = new double[width][height];
263        physicalMap = new double[width][height];
264        costMap = new double[width][height];
265        targetMap = new Coord[width][height];
266        for (int x = 0; x < width; x++) {
267            System.arraycopy(level[x], 0, gradientMap[x], 0, height);
268            System.arraycopy(level[x], 0, physicalMap[x], 0, height);
269            Arrays.fill(costMap[x], 1.0);
270        }
271        standardCosts = true;
272        impassable2 = new HashSet<>(32, 0.375f);
273        friends = new HashSet<>(32, 0.375f);
274        tempSet = new HashSet<>(32, 0.375f);
275        initialized = true;
276        return this;
277    }
278
279    /**
280     * Used to initialize or re-initialize a DijkstraMap that needs a new physicalMap because it either wasn't given
281     * one when it was constructed, or because the contents of the terrain have changed permanently (not if a
282     * creature moved; for that you pass the positions of creatures that block paths to scan() or findPath() ).
283     *
284     * @param level a 2D char array that this will use to establish which cells are walls ('#' as wall, others as floor)
285     * @return this for chaining
286     */
287    public DijkstraMap initialize(final char[][] level) {
288        width = level.length;
289        height = level[0].length;
290        gradientMap = new double[width][height];
291        physicalMap = new double[width][height];
292        costMap = new double[width][height];
293        targetMap = new Coord[width][height];
294        for (int x = 0; x < width; x++) {
295            Arrays.fill(costMap[x], 1.0);
296            for (int y = 0; y < height; y++) {
297                double t = (level[x][y] == '#') ? WALL : FLOOR;
298                gradientMap[x][y] = t;
299                physicalMap[x][y] = t;
300            }
301        }
302        standardCosts = true;
303        impassable2 = new HashSet<>(32, 0.375f);
304        friends = new HashSet<>(32, 0.375f);
305        tempSet = new HashSet<>(32, 0.375f);
306        initialized = true;
307        return this;
308    }
309
310    /**
311     * Used to initialize or re-initialize a DijkstraMap that needs a new PhysicalMap because it either wasn't given
312     * one when it was constructed, or because the contents of the terrain have changed permanently (not if a
313     * creature moved; for that you pass the positions of creatures that block paths to scan() or findPath() ). This
314     * initialize() method allows you to specify an alternate wall char other than the default character, '#' .
315     *
316     * @param level a 2D char array that this will use to establish which cells are walls (alternateWall defines the wall char, everything else is floor)
317     * @param alternateWall the char to consider a wall when it appears in level
318     * @return this for chaining
319     */
320    public DijkstraMap initialize(final char[][] level, char alternateWall) {
321        width = level.length;
322        height = level[0].length;
323        gradientMap = new double[width][height];
324        physicalMap = new double[width][height];
325        costMap = new double[width][height];
326        targetMap = new Coord[width][height];
327        for (int x = 0; x < width; x++) {
328            Arrays.fill(costMap[x], 1.0);
329            for (int y = 0; y < height; y++) {
330                double t = (level[x][y] == alternateWall) ? WALL : FLOOR;
331                gradientMap[x][y] = t;
332                physicalMap[x][y] = t;
333            }
334        }
335        standardCosts = true;
336        impassable2 = new HashSet<>(32, 0.375f);
337        friends = new HashSet<>(32, 0.375f);
338        tempSet = new HashSet<>(32, 0.375f);
339        initialized = true;
340        return this;
341    }
342
343    /**
344     * Used to initialize the entry cost modifiers for games that require variable costs to enter squares. This expects
345     * a char[][] of the same exact dimensions as the 2D array that was used to previously initialize() this
346     * DijkstraMap, treating the '#' char as a wall (impassable) and anything else as having a normal cost to enter.
347     * The costs can be accessed later by using costMap directly (which will have a valid value when this does not
348     * throw an exception), or by calling setCost().
349     *
350     * @param level a 2D char array that uses '#' for walls
351     * @return this DijkstraMap for chaining.
352     */
353    public DijkstraMap initializeCost(final char[][] level) {
354        if (!initialized) throw new IllegalStateException("DijkstraMap must be initialized first!");
355        ArrayTools.fill(costMap, 1.0);
356        standardCosts = true;
357        return this;
358    }
359
360    /**
361     * Used to initialize the entry cost modifiers for games that require variable costs to enter squares. This expects
362     * a char[][] of the same exact dimensions as the 2D array that was used to previously initialize() this
363     * DijkstraMap, treating the '#' char as a wall (impassable) and anything else as having a normal cost to enter.
364     * The costs can be accessed later by using costMap directly (which will have a valid value when this does not
365     * throw an exception), or by calling setCost().
366     * <p/>
367     * This method allows you to specify an alternate wall char other than the default character, '#' .
368     *
369     * @param level         a 2D char array that uses alternateChar for walls.
370     * @param alternateWall a char to use to represent walls.
371     * @return this DijkstraMap for chaining.
372     */
373    public DijkstraMap initializeCost(final char[][] level, char alternateWall) {
374        if (!initialized) throw new IllegalStateException("DijkstraMap must be initialized first!");
375        ArrayTools.fill(costMap, 1.0);
376        standardCosts = true;
377        return this;
378    }
379
380    /**
381     * Used to initialize the entry cost modifiers for games that require variable costs to enter squares. This expects
382     * a double[][] of the same exact dimensions as the 2D array that was used to previously initialize() this
383     * DijkstraMap, using the exact values given in costs as the values to enter cells, even if they aren't what this
384     * class would assign normally -- walls and other impassable values should be given WALL as a value, however.
385     * The costs can be accessed later by using costMap directly (which will have a valid value when this does not
386     * throw an exception), or by calling setCost(). Causes findPath() to always explore the full map instead of
387     * stopping as soon as it finds any path, since unequal costs could make some paths cost less but be discovered
388     * later in the pathfinding process.
389     * <p/>
390     * This method should be slightly more efficient than the other initializeCost methods.
391     *
392     * @param costs a 2D double array that already has the desired cost values
393     * @return this DijkstraMap for chaining.
394     */
395    public DijkstraMap initializeCost(final double[][] costs) {
396        if (!initialized) throw new IllegalStateException("DijkstraMap must be initialized first!");
397        costMap = new double[width][height];
398        for (int x = 0; x < width; x++) {
399            System.arraycopy(costs[x], 0, costMap[x], 0, height);
400        }
401        standardCosts = false;
402        return this;
403    }
404
405    /**
406     * Internally, DijkstraMap uses int primitives instead of Coord objects, but the specific encoding depends on
407     * this DijkstraMap's width and height. This method converts from a Coord to an encoded int that stores the same
408     * information, but is specific to this width and height and is somewhat more efficient to work with.
409     * @param point a Coord to find an encoded int for
410     * @return an int that encodes the given Coord for this DijkstraMap's width and height
411     */
412    public int encode(final Coord point)
413    {
414        return width * point.y + point.x;
415    }
416    /**
417     * Internally, DijkstraMap uses int primitives instead of Coord objects, but the specific encoding depends on
418     * this DijkstraMap's width and height. This method converts from an x,y point to an encoded int that stores the
419     * same information, but is specific to this width and height and is somewhat more efficient to work with.
420     * @param x the x component of the point to find an encoded int for
421     * @param y the y component of the point to find an encoded int for
422     * @return an int that encodes the given x,y point for this DijkstraMap's width and height
423     */
424    public int encode(final int x, final int y)
425    {
426        return width * y + x;
427    }
428
429    /**
430     * If you for some reason have one of the internally-used ints produced by {@link #encode(Coord)}, this will convert
431     * it back to a Coord if you need it as such. You may prefer using {@link #decodeX(int)} and  {@link #decodeY(int)}
432     * to get the x and y components independently and without involving objects.
433     * @param encoded an encoded int specific to this DijkstraMap's height and width; see {@link #encode(Coord)}
434     * @return the Coord that represents the same x,y position that the given encoded int stores
435     */
436    public Coord decode(final int encoded)
437    {
438        return Coord.get(encoded % width, encoded / width);
439    }
440
441    /**
442     * If you for some reason have one of the internally-used ints produced by {@link #encode(Coord)}, this will decode
443     * the x component of the point encoded in that int. This is an extremely simple method that is equivalent to the
444     * code {@code encoded % width}, where width is a public field in this class. You probably would use this method in
445     * conjunction with {@link #decodeY(int)}, or would instead use {@link #decode(int)} to get a Coord.
446     * @param encoded an encoded int specific to this DijkstraMap's height and width; see {@link #encode(Coord)}
447     * @return the x component of the position that the given encoded int stores
448     */
449    public int decodeX(final int encoded)
450    {
451        return encoded % width;
452    }
453
454    /**
455     * If you for some reason have one of the internally-used ints produced by {@link #encode(Coord)}, this will decode
456     * the y component of the point encoded in that int. This is an extremely simple method that is equivalent to the
457     * code {@code encoded / width}, where width is a public field in this class. You probably would use this method in
458     * conjunction with {@link #decodeX(int)}, or would instead use {@link #decode(int)} to get a Coord.
459     * @param encoded an encoded int specific to this DijkstraMap's height and width; see {@link #encode(Coord)}
460     * @return the y component of the position that the given encoded int stores
461     */
462    public int decodeY(final int encoded)
463    {
464        return encoded / width;
465    }
466
467    /**
468     * Gets the appropriate Measurement to pass to a constructor if you already have a Radius.
469     * Matches SQUARE or CUBE to CHEBYSHEV, DIAMOND or OCTAHEDRON to MANHATTAN, and CIRCLE or SPHERE to EUCLIDEAN.
470     *
471     * @param radius the Radius to find the corresponding Measurement for
472     * @return a Measurement that matches radius; SQUARE to CHEBYSHEV, DIAMOND to MANHATTAN, etc.
473     */
474    public static Measurement findMeasurement(Radius radius) {
475        switch (radius)
476        {
477            case CUBE:
478            case SQUARE:
479                return Measurement.CHEBYSHEV;
480            case DIAMOND:
481            case OCTAHEDRON:
482                return Measurement.MANHATTAN;
483            default:
484                return Measurement.EUCLIDEAN;
485        }
486    }
487
488    /**
489     * Gets the appropriate Radius corresponding to a Measurement.
490     * Matches CHEBYSHEV to SQUARE, MANHATTAN to DIAMOND, and EUCLIDEAN to CIRCLE.
491     * <br>
492     * See also {@link Measurement#matchingRadius()} as a method on Measurement.
493     * @param measurement the Measurement to find the corresponding Radius for
494     * @return a Radius that matches measurement; CHEBYSHEV to SQUARE, MANHATTAN to DIAMOND, etc.
495     */
496    public static Radius findRadius(Measurement measurement) {
497        switch (measurement) {
498            case CHEBYSHEV:
499                return Radius.SQUARE;
500            case EUCLIDEAN:
501                return Radius.CIRCLE;
502            default:
503                return Radius.DIAMOND;
504        }
505    }
506
507    /**
508     * Resets the gradientMap to its original value from physicalMap.
509     */
510    public void resetMap() {
511        if (!initialized) return;
512        for (int x = 0; x < width; x++) {
513            System.arraycopy(physicalMap[x], 0, gradientMap[x], 0, height);
514        }
515    }
516
517    /**
518     * Resets the targetMap (which is only assigned in the first place if you use findTechniquePath() ).
519     */
520    public void resetTargetMap() {
521        if (!initialized) return;
522        for (int x = 0; x < width; x++) {
523            for (int y = 0; y < height; y++) {
524                targetMap[x][y] = null;
525            }
526        }
527    }
528
529
530    /**
531     * Resets this DijkstraMap to a state with no goals, no discovered path, and no changes made to gradientMap
532     * relative to physicalMap.
533     */
534    public void reset() {
535        resetMap();
536        resetTargetMap();
537        goals.clear();
538        path.clear();
539        fresh.clear();
540        frustration = 0;
541    }
542
543    /**
544     * Marks a cell as a goal for pathfinding, unless the cell is a wall or unreachable area (then it does nothing).
545     *
546     * @param x
547     * @param y
548     */
549    public void setGoal(int x, int y) {
550        if (!initialized || x < 0 || x >= width || y < 0 || y >= height) return;
551        if (physicalMap[x][y] > FLOOR) {
552            return;
553        }
554
555        goals.add(encode(x, y));
556        gradientMap[x][y] = 0.0;
557    }
558
559    /**
560     * Marks a cell as a goal for pathfinding, unless the cell is a wall or unreachable area (then it does nothing).
561     *
562     * @param pt
563     */
564    public void setGoal(Coord pt) {
565        if (!initialized || !pt.isWithin(width, height)) return;
566        if (physicalMap[pt.x][pt.y] > FLOOR) {
567            return;
568        }
569
570        goals.add(encode(pt));
571        gradientMap[pt.x][pt.y] = 0.0;
572
573    }
574    /**
575     * Marks many cells as goals for pathfinding, ignoring cells in walls or unreachable areas. Possibly more efficient
576     * than a loop that calls {@link #setGoal(Coord)} over and over, since this doesn't need to do a bounds check. The
577     * GreasedRegion passed to this should have the same (or smaller) width and height as this DijkstraMap.
578     *
579     * @param pts a GreasedRegion containing "on" cells to treat as goals; should have the same width and height as this
580     */
581    public void setGoals(GreasedRegion pts) {
582        if (!initialized || pts.width > width || pts.height > height) return;
583        for(Coord c : pts)
584        {
585            if(physicalMap[c.x][c.y] <= FLOOR) {
586                goals.add(encode(c));
587                gradientMap[c.x][c.y] = 0.0;
588            }
589        }
590    }
591
592    /**
593     * Marks many cells as goals for pathfinding, ignoring cells in walls or unreachable areas. Simply loops through
594     * pts and calls {@link #setGoal(Coord)} on each Coord in pts.
595     * If you have a GreasedRegion, you should use it with {@link #setGoals(GreasedRegion)}, which is faster.
596     * @param pts any Iterable of Coord, which can be a List, Set, Queue, etc. of Coords to mark as goals
597     */
598    public void setGoals(Iterable<Coord> pts) {
599        if (!initialized) return;
600        for(Coord c : pts)
601        {
602            setGoal(c);
603        }
604    }
605    /**
606     * Marks many cells as goals for pathfinding, ignoring cells in walls or unreachable areas. Simply loops through
607     * pts and calls {@link #setGoal(Coord)} on each Coord in pts.
608     * @param pts an array of Coord to mark as goals
609     */
610    public void setGoals(Coord[] pts) {
611        if (!initialized) return;
612        for(int i = 0; i < pts.length; i++)
613        {
614            setGoal(pts[i]);
615        }
616    }
617
618    /**
619     * Marks a cell's cost for pathfinding as cost, unless the cell is a wall or unreachable area (then it always sets
620     * the cost to the value of the WALL field).
621     *
622     * @param pt
623     * @param cost
624     */
625    public void setCost(Coord pt, double cost) {
626        if (!initialized || !pt.isWithin(width, height)) return;
627        if (physicalMap[pt.x][pt.y] > FLOOR) {
628            costMap[pt.x][pt.y] = 1.0;
629            return;
630        }
631        if(cost != 1.0)
632            standardCosts = false;
633        costMap[pt.x][pt.y] = cost;
634    }
635
636    /**
637     * Marks a cell's cost for pathfinding as cost, unless the cell is a wall or unreachable area (then it always sets
638     * the cost to the value of the WALL field).
639     *
640     * @param x
641     * @param y
642     * @param cost
643     */
644    public void setCost(int x, int y, double cost) {
645        if (!initialized || x < 0 || x >= width || y < 0 || y >= height) return;
646        if (physicalMap[x][y] > FLOOR) {
647            costMap[x][y] = 1.0;
648            return;
649        }
650        if(cost != 1.0)
651            standardCosts = false;
652        costMap[x][y] = cost;
653    }
654
655    /**
656     * Marks a specific cell in gradientMap as completely impossible to enter.
657     *
658     * @param x
659     * @param y
660     */
661    public void setOccupied(int x, int y) {
662        if (!initialized || x < 0 || x >= width || y < 0 || y >= height) return;
663        gradientMap[x][y] = WALL;
664    }
665
666    /**
667     * Reverts a cell to the value stored in the original state of the level as known by physicalMap.
668     *
669     * @param x
670     * @param y
671     */
672    public void resetCell(int x, int y) {
673        if (!initialized || x < 0 || x >= width || y < 0 || y >= height) return;
674        gradientMap[x][y] = physicalMap[x][y];
675    }
676
677    /**
678     * Reverts a cell to the value stored in the original state of the level as known by physicalMap.
679     *
680     * @param pt
681     */
682    public void resetCell(Coord pt) {
683        if (!initialized || !pt.isWithin(width, height)) return;
684        gradientMap[pt.x][pt.y] = physicalMap[pt.x][pt.y];
685    }
686
687    /**
688     * Used to remove all goals and undo any changes to gradientMap made by having a goal present.
689     */
690    public void clearGoals() {
691        if (!initialized)
692            return;
693        int sz = goals.size, t;
694        for (int i = 0; i < sz; i++) {
695            resetCell(decodeX(t = goals.pop()), decodeY(t));
696        }
697    }
698
699    private void setFresh(final int x, final int y, double counter) {
700        if (x < 0 || x >= width || y < 0 || y >= height || gradientMap[x][y] < counter)
701            return;
702        gradientMap[x][y] = counter;
703        fresh.add(encode(x, y));
704    }
705
706    private void setFresh(final Coord pt, double counter) {
707        if (!pt.isWithin(width, height) || gradientMap[pt.x][pt.y] < counter)
708            return;
709        gradientMap[pt.x][pt.y] = counter;
710        fresh.add(encode(pt));
711    }
712
713    /**
714     * Recalculate the Dijkstra map and return it. Cells that were marked as goals with setGoal will have
715     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
716     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
717     * which will have a value defined by the WALL constant in this class, and areas that the scan was
718     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
719     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
720     * current measurement. The result is stored in the {@link #gradientMap} field and a copy is returned.
721     *
722     * @return A 2D double[width][height] using the width and height of what this knows about the physical map.
723     */
724    public double[][] scan() {
725        return scan(null);
726    }
727
728    /**
729     * Recalculate the Dijkstra map and return it. Cells that were marked as goals with setGoal will have
730     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
731     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
732     * which will have a value defined by the WALL constant in this class, and areas that the scan was
733     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
734     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
735     * current measurement. The result is stored in the {@link #gradientMap} field and a copy is returned.
736     *
737     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
738     *                   path that cannot be moved through; this can be null if there are no such obstacles.
739     * @return A 2D double[width][height] using the width and height of what this knows about the physical map.
740     */
741    public double[][] scan(final Collection<Coord> impassable) {
742        scan(null, impassable);
743        double[][] gradientClone = new double[width][height];
744        for (int x = 0; x < width; x++) {
745            for (int y = 0; y < height; y++) {
746                if (gradientMap[x][y] == FLOOR) {
747                    gradientMap[x][y] = DARK;
748                }
749            }
750            System.arraycopy(gradientMap[x], 0, gradientClone[x], 0, height);
751        }
752
753        return gradientClone;
754    }
755
756    /**
757     * Recalculate the Dijkstra map and return it. Cells that were marked as goals with setGoal will have
758     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
759     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
760     * which will have a value defined by the WALL constant in this class, and areas that the scan was
761     * unable to reach, which will have a value defined by the DARK constant in this class (typically,
762     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
763     * current measurement. The result is stored in the {@link #gradientMap} field, and nothing is returned.
764     * If you want the data returned, you can use {@link #scan(Collection)} (which calls this method with
765     * null for the start parameter, then modifies the gradientMap field and returns a copy), or you can
766     * just retrieve the gradientMap (maybe copying it; {@link squidpony.ArrayTools#copy(double[][])} is a
767     * convenient option for copying a 2D double array). If start is non-null, which is usually used when
768     * finding a single path, then cells that didn't need to be explored (because they were further than the
769     * path needed to go from start to goal) will have the value {@link #FLOOR}. You may wish to assign a
770     * different value to these cells in some cases (especially if start is null, which means any cells that
771     * are still FLOOR could not be reached from any goal), and the overloads of scan that return 2D double
772     * arrays do change FLOOR to {@link #DARK}, which is usually treated similarly to {@link #WALL}.
773     *
774     * @param start a Coord representing the location of the pathfinder; may be null, which has this scan the whole map
775     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
776     *                   path that cannot be moved through; this can be null if there are no such obstacles.
777     */
778    public void scan(final Coord start, final Collection<Coord> impassable) {
779        scan(start, impassable, false);
780    }
781
782    /**
783     * Recalculate the Dijkstra map and return it. Cells in {@link #gradientMap} that had the lowest value
784     * will be treated as goals if {@code nonZeroOptimum} is true; otherwise, only cells marked as goals with
785     * {@link #setGoal(Coord)} will be considered goals and some overhead will be saved. The cells adjacent
786     * to goals will have a value of 1, and cells progressively further from goals will have a value equal to
787     * the distance from the nearest goal. The exceptions are walls, which will have a value defined by the
788     * {@link #WALL} constant in this class, and areas that the scan was unable to reach, which will have a
789     * value defined by the {@link #DARK} constant in this class (typically, these areas should not be used to
790     * place NPCs or items and should be filled with walls). This uses the current {@link #measurement}. The
791     * result is stored in the {@link #gradientMap} field, and nothing is returned. If you want the data
792     * returned, you can use {@link #scan(Collection)} (which calls this method with null for the start
793     * parameter, then modifies the gradientMap field and returns a copy), or you can
794     * just retrieve the gradientMap (maybe copying it; {@link squidpony.ArrayTools#copy(double[][])} is a
795     * convenient option for copying a 2D double array). If start is non-null, which is usually used when
796     * finding a single path, then cells that didn't need to be explored (because they were further than the
797     * path needed to go from start to goal) will have the value {@link #FLOOR}. You may wish to assign a
798     * different value to these cells in some cases (especially if start is null, which means any cells that
799     * are still FLOOR could not be reached from any goal), and the overloads of scan that return 2D double
800     * arrays do change FLOOR to {@link #DARK}, which is usually treated similarly to {@link #WALL}.
801     *
802     * @param start a Coord representing the location of the pathfinder; may be null, which has this scan the whole map
803     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
804     *                   path that cannot be moved through; this can be null if there are no such obstacles.
805     * @param nonZeroOptimum if the cell to pathfind toward should have a value of {@link #GOAL} (0.0), this should be
806     *                       false; if it should have a different value or if you don't know, it should be true 
807     */
808    public void scan(final Coord start, final Collection<Coord> impassable, final boolean nonZeroOptimum) {
809
810        if (!initialized) return;
811        if (impassable != null && !impassable.isEmpty()) {
812            for (Coord pt : impassable) {
813                if(pt != null && pt.isWithin(width, height))
814                    gradientMap[pt.x][pt.y] = WALL;
815            }
816        }
817        int dec, adjX, adjY, cen, cenX, cenY;
818        fresh.clear();
819        fresh.addAll(goals);
820        for (int i = 0; i < goals.size; i++) {
821            dec = goals.get(i);
822            gradientMap[decodeX(dec)][decodeY(dec)] = GOAL;
823        }
824        double cs, dist;
825        if(nonZeroOptimum) {
826            double currentLowest = 999000.0;
827            for (int x = 0; x < width; x++) {
828                for (int y = 0; y < height; y++) {
829                    if (gradientMap[x][y] <= FLOOR) {
830                        if (gradientMap[x][y] < currentLowest) {
831                            currentLowest = gradientMap[x][y];
832                            fresh.clear();
833                            fresh.add(encode(x, y));
834                        } else if (gradientMap[x][y] == currentLowest) {
835                            fresh.add(encode(x, y));
836                        }
837                    }
838                }
839            }
840        }
841        int fsz, numAssigned = fresh.size;
842        mappedCount = goals.size;
843        Direction[] moveDirs = (measurement == Measurement.MANHATTAN) ? Direction.CARDINALS : Direction.OUTWARDS;
844
845        while (numAssigned > 0) {
846            numAssigned = 0;
847            fsz = fresh.size;
848            for (int ci = fsz-1; ci >= 0; ci--) {
849                cen = fresh.removeIndex(ci);
850                cenX = decodeX(cen);
851                cenY = decodeY(cen);
852                dist = gradientMap[cenX][cenY];
853
854                for (int d = 0; d < moveDirs.length; d++) {
855                    adjX = cenX + moveDirs[d].deltaX;
856                    adjY = cenY + moveDirs[d].deltaY;
857                    if (adjX < 0 || adjY < 0 || width <= adjX || height <= adjY)
858                        /* Outside the map */
859                        continue;
860                    if(d >= 4 && blockingRequirement > 0) // diagonal
861                    {
862                        if((gradientMap[adjX][cenY] > FLOOR ? 1 : 0)
863                                + (gradientMap[cenX][adjY] > FLOOR ? 1 : 0)
864                                >= blockingRequirement)
865                        {
866                            continue;
867                        }
868                    }
869                    double h = measurement.heuristic(moveDirs[d]);
870                    cs = dist + h * costMap[adjX][adjY];
871                    if (gradientMap[adjX][adjY] <= FLOOR && cs < gradientMap[adjX][adjY]) {
872                        setFresh(adjX, adjY, cs);
873                        ++numAssigned;
874                        ++mappedCount;
875                        if(start != null && start.x == adjX && start.y == adjY && standardCosts)
876                        {
877                            if (impassable != null && !impassable.isEmpty()) {
878                                for (Coord pt : impassable) {
879                                    if(pt != null && pt.isWithin(width, height))
880                                        gradientMap[pt.x][pt.y] = physicalMap[pt.x][pt.y];
881                                }
882                            }
883                            return;
884                        }
885                    }
886                }
887            }
888        }
889        if (impassable != null && !impassable.isEmpty()) {
890            for (Coord pt : impassable) {
891                if(pt != null && pt.isWithin(width, height))
892                    gradientMap[pt.x][pt.y] = physicalMap[pt.x][pt.y];
893            }
894        }
895    }
896
897    /**
898     * Recalculate the Dijkstra map up to a limit and return it. Cells that were marked as goals with setGoal will have
899     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
900     * from goals will have a value equal to the distance from the nearest goal. If a cell would take more steps to
901     * reach than the given limit, it will have a value of DARK if it was passable instead of the distance. The
902     * exceptions are walls, which will have a value defined by the WALL constant in this class, and areas that the scan
903     * was unable to reach, which will have a value defined by the DARK constant in this class. This uses the
904     * current measurement. The result is stored in the {@link #gradientMap} field and a copy is returned.
905     *
906     * @param limit      The maximum number of steps to scan outward from a goal.
907     * @return A 2D double[width][height] using the width and height of what this knows about the physical map.
908     */
909    public double[][] partialScan(final int limit) {
910        return partialScan(limit, null);
911    }
912
913    /**
914     * Recalculate the Dijkstra map up to a limit and return it. Cells that were marked as goals with setGoal will have
915     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
916     * from goals will have a value equal to the distance from the nearest goal. If a cell would take more steps to
917     * reach than the given limit, it will have a value of DARK if it was passable instead of the distance. The
918     * exceptions are walls, which will have a value defined by the WALL constant in this class, and areas that the scan
919     * was unable to reach, which will have a value defined by the DARK constant in this class. This uses the
920     * current measurement. The result is stored in the {@link #gradientMap} field and a copy is returned.
921     *
922     * @param limit      The maximum number of steps to scan outward from a goal.
923     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
924     *                   path that cannot be moved through; this can be null if there are no such obstacles.
925     * @return A 2D double[width][height] using the width and height of what this knows about the physical map.
926     */
927    public double[][] partialScan(final int limit, final Collection<Coord> impassable) {
928        partialScan(null, limit, impassable);
929        double[][] gradientClone = new double[width][height];
930        for (int x = 0; x < width; x++) {
931            for (int y = 0; y < height; y++) {
932                if (gradientMap[x][y] == FLOOR) {
933                    gradientMap[x][y] = DARK;
934                }
935            }
936            System.arraycopy(gradientMap[x], 0, gradientClone[x], 0, height);
937        }
938
939        return gradientClone;
940    }
941
942    /**
943     * Recalculate the Dijkstra map up to a limit and return it. Cells that were marked as goals with setGoal will have
944     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
945     * from goals will have a value equal to the distance from the nearest goal. If a cell would take more steps to
946     * reach than the given limit, or if it was otherwise unreachable, it will have a value of {@link #FLOOR} or greater
947     * if it was passable instead of the distance. The exceptions are walls, which will have a value defined by the
948     * {@link #WALL} constant in this class. This uses the current {@link #measurement}. The result is stored in the
949     * {@link #gradientMap} field, and nothing is returned.If you want the data returned, you can use
950     * {@link #partialScan(int, Collection)} (which calls this method with null for the start parameter, then modifies
951     * the gradientMap field and returns a copy), or you can just retrieve the gradientMap (maybe copying it;
952     * {@link squidpony.ArrayTools#copy(double[][])} is a convenient option for copying a 2D double array).
953     * <br>
954     * If start is non-null, which is usually used when finding a single path, then cells that didn't need to be
955     * explored (because they were further than the path needed to go from start to goal) will have the value
956     * {@link #FLOOR}. You may wish to assign a different value to these cells in some cases (especially if start is
957     * null, which means any cells that are still FLOOR could not be reached from any goal), and the overloads of
958     * partialScan that return 2D double arrays do change FLOOR to {@link #DARK}, which is usually treated similarly to
959     * {@link #WALL}.
960     *
961     * @param start a Coord representing the location of the pathfinder; may be null to have this scan more of the map
962     * @param limit      The maximum number of steps to scan outward from a goal.
963     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
964     *                   path that cannot be moved through; this can be null if there are no such obstacles.
965     */
966    public void partialScan(final Coord start, final int limit, final Collection<Coord> impassable) {
967        partialScan(start, limit, impassable, false);
968    }
969
970    /**
971     * Recalculate the Dijkstra map up to a limit and return it. Cells in {@link #gradientMap} that had the lowest value
972     * will be treated as goals if {@code nonZeroOptimum} is true; otherwise, only cells marked as goals with
973     * {@link #setGoal(Coord)} will be considered goals and some overhead will be saved. If a cell would take more steps
974     * to reach than the given limit, or if it was otherwise unreachable, it will have a value of {@link #FLOOR} or
975     * greater if it was passable instead of the distance. The exceptions are walls, which will have a value defined by
976     * the {@link #WALL} constant in this class. This uses the current {@link #measurement}. The result is stored in the
977     * {@link #gradientMap} field, and nothing is returned.If you want the data returned, you can use
978     * {@link #partialScan(int, Collection)} (which calls this method with null for the start parameter, then modifies
979     * the gradientMap field and returns a copy), or you can just retrieve the gradientMap (maybe copying it;
980     * {@link squidpony.ArrayTools#copy(double[][])} is a convenient option for copying a 2D double array).
981     * <br>
982     * If start is non-null, which is usually used when finding a single path, then cells that didn't need to be
983     * explored (because they were further than the path needed to go from start to goal) will have the value
984     * {@link #FLOOR}. You may wish to assign a different value to these cells in some cases (especially if start is
985     * null, which means any cells that are still FLOOR could not be reached from any goal), and the overloads of
986     * partialScan that return 2D double arrays do change FLOOR to {@link #DARK}, which is usually treated similarly to
987     * {@link #WALL}.
988     *
989     * @param start a Coord representing the location of the pathfinder; may be null to have this scan more of the map
990     * @param limit      The maximum number of steps to scan outward from a goal.
991     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
992     *                   path that cannot be moved through; this can be null if there are no such obstacles.
993     * @param nonZeroOptimum if the cell to pathfind toward should have a value of {@link #GOAL} (0.0), this should be
994     *                       false; if it should have a different value or if you don't know, it should be true
995     */
996    public void partialScan(final Coord start, final int limit, final Collection<Coord> impassable, final boolean nonZeroOptimum) {
997
998        if (!initialized || limit <= 0) return;
999        if (impassable != null && !impassable.isEmpty()) {
1000            for (Coord pt : impassable) {
1001                if(pt != null && pt.isWithin(width, height))
1002                    gradientMap[pt.x][pt.y] = WALL;
1003            }
1004        }
1005        int dec, adjX, adjY, cen, cenX, cenY;
1006        fresh.clear();
1007        fresh.addAll(goals);
1008        for (int i = 0; i < goals.size; i++) {
1009            //if (closed.containsKey(entry.getIntKey()))
1010            //    continue;
1011            //    closed.remove(entry.getIntKey());
1012            dec = goals.get(i);
1013            gradientMap[decodeX(dec)][decodeY(dec)] = GOAL;
1014        }
1015        double cs, dist;
1016        if(nonZeroOptimum) {
1017            double currentLowest = 999000;
1018            if (start == null) {
1019                for (int x = 0; x < width; x++) {
1020                    for (int y = 0; y < height; y++) {
1021                        if (gradientMap[x][y] <= FLOOR) {
1022                            if (gradientMap[x][y] < currentLowest) {
1023                                currentLowest = gradientMap[x][y];
1024                                fresh.clear();
1025                                fresh.add(encode(x, y));
1026                            } else if (gradientMap[x][y] == currentLowest) {
1027                                fresh.add(encode(x, y));
1028                            }
1029                        }
1030                    }
1031                }
1032            } else {
1033                final int x0 = Math.max(0, start.x - limit), x1 = Math.min(start.x + limit + 1, width),
1034                        y0 = Math.max(0, start.y - limit), y1 = Math.min(start.y + limit + 1, height);
1035                for (int x = x0; x < x1; x++) {
1036                    for (int y = y0; y < y1; y++) {
1037                        if (gradientMap[x][y] <= FLOOR) {
1038                            if (gradientMap[x][y] < currentLowest) {
1039                                currentLowest = gradientMap[x][y];
1040                                fresh.clear();
1041                                fresh.add(encode(x, y));
1042                            } else if (gradientMap[x][y] == currentLowest) {
1043                                fresh.add(encode(x, y));
1044                            }
1045                        }
1046                    }
1047                }
1048            }
1049        }
1050        int fsz, numAssigned = fresh.size;
1051        mappedCount = goals.size;
1052        Direction[] moveDirs = (measurement == Measurement.MANHATTAN) ? Direction.CARDINALS : Direction.OUTWARDS;
1053
1054        int iter = 0;
1055        while (numAssigned > 0 && iter++ < limit) {
1056            numAssigned = 0;
1057            fsz = fresh.size;
1058            for (int ci = fsz-1; ci >= 0; ci--) {
1059                cen = fresh.removeIndex(ci);
1060                cenX = decodeX(cen);
1061                cenY = decodeY(cen);
1062                dist = gradientMap[cenX][cenY];
1063
1064                for (int d = 0; d < moveDirs.length; d++) {
1065                    adjX = cenX + moveDirs[d].deltaX;
1066                    adjY = cenY + moveDirs[d].deltaY;
1067                    if (adjX < 0 || adjY < 0 || width <= adjX || height <= adjY)
1068                        /* Outside the map */
1069                        continue;
1070                    if(d >= 4 && blockingRequirement > 0) // diagonal
1071                    {
1072                        if((gradientMap[adjX][cenY] > FLOOR ? 1 : 0)
1073                                + (gradientMap[cenX][adjY] > FLOOR ? 1 : 0)
1074                                >= blockingRequirement)
1075                        {
1076                            continue;
1077                        }
1078                    }
1079                    double h = measurement.heuristic(moveDirs[d]);
1080                    cs = dist + h * costMap[adjX][adjY];
1081                    if (gradientMap[adjX][adjY] <= FLOOR && cs < gradientMap[adjX][adjY]) {
1082                        setFresh(adjX, adjY, cs);
1083                        ++numAssigned;
1084                        ++mappedCount;
1085                        if(start != null && start.x == adjX && start.y == adjY && standardCosts)
1086                        {
1087                            if (impassable != null && !impassable.isEmpty()) {
1088                                for (Coord pt : impassable) {
1089                                    if(pt != null && pt.isWithin(width, height))
1090                                        gradientMap[pt.x][pt.y] = physicalMap[pt.x][pt.y];
1091                                }
1092                            }
1093                            return;
1094                        }
1095                    }
1096                }
1097            }
1098        }
1099        if (impassable != null && !impassable.isEmpty()) {
1100            for (Coord pt : impassable) {
1101                if(pt != null && pt.isWithin(width, height))
1102                    gradientMap[pt.x][pt.y] = physicalMap[pt.x][pt.y];
1103            }
1104        }
1105    }
1106
1107    /**
1108     * Recalculate the Dijkstra map until it reaches a Coord in targets, then returns the first target found.
1109     * This uses the current measurement.
1110     *
1111     * @param start   the cell to use as the origin for finding the nearest target
1112     * @param targets the Coords that this is trying to find; it will stop once it finds one
1113     * @return the Coord that it found first.
1114     */
1115    public Coord findNearest(Coord start, Collection<Coord> targets) {
1116        if (!initialized) return null;
1117        if (targets == null)
1118            return null;
1119        if (targets.contains(start))
1120            return start;
1121        resetMap();
1122        Coord start2 = start;
1123        int xShift = width / 6, yShift = height / 6;
1124        while (physicalMap[start.x][start.y] >= WALL && frustration < 50) {
1125            start2 = Coord.get(Math.min(Math.max(1, start.x + rng.nextInt(1 + xShift * 2) - xShift), width - 2),
1126                    Math.min(Math.max(1, start.y + rng.nextInt(1 + yShift * 2) - yShift), height - 2));
1127        }
1128        gradientMap[start2.x][start2.y] = 0.0;
1129        int adjX, adjY, cen, cenX, cenY;
1130        double cs, dist;
1131        Coord adj;
1132        fresh.clear();
1133        fresh.add(encode(start2));
1134        int fsz, numAssigned = 1;
1135        mappedCount = 1;
1136        Direction[] moveDirs = (measurement == Measurement.MANHATTAN) ? Direction.CARDINALS : Direction.OUTWARDS;
1137
1138        while (numAssigned > 0) {
1139            numAssigned = 0;
1140            fsz = fresh.size;
1141            for (int ci = fsz-1; ci >= 0; ci--) {
1142                cen = fresh.removeIndex(ci);
1143                cenX = decodeX(cen);
1144                cenY = decodeY(cen);
1145                dist = gradientMap[cenX][cenY];
1146
1147                for (int d = 0; d < moveDirs.length; d++) {
1148                    adjX = cenX + moveDirs[d].deltaX;
1149                    adjY = cenY + moveDirs[d].deltaY;
1150                    if (adjX < 0 || adjY < 0 || width <= adjX || height <= adjY)
1151                        /* Outside the map */
1152                        continue;
1153                    if(d >= 4 && blockingRequirement > 0) // diagonal
1154                    {
1155                        if((gradientMap[adjX][cenY] > FLOOR ? 1 : 0)
1156                                + (gradientMap[cenX][adjY] > FLOOR ? 1 : 0)
1157                                >= blockingRequirement)
1158                        {
1159                            continue;
1160                        }
1161                    }
1162                    double h = measurement.heuristic(moveDirs[d]);
1163                    cs = dist + h * costMap[adjX][adjY];
1164                    if (gradientMap[adjX][adjY] <= FLOOR && cs < gradientMap[adjX][adjY]) {
1165                        ++mappedCount;
1166                        if (targets.contains(adj = Coord.get(adjX, adjY))) {
1167                            fresh.clear();
1168                            return adj;
1169                        }
1170                        setFresh(adjX, adjY, cs);
1171                        ++numAssigned;
1172                    }
1173                }
1174            }
1175        }
1176
1177        return null;
1178    }
1179
1180    /**
1181     * Recalculate the Dijkstra map until it reaches a Coord in targets, then returns the first target found.
1182     * This uses the current measurement.
1183     *
1184     * @param start   the cell to use as the origin for finding the nearest target
1185     * @param targets the Coords that this is trying to find; it will stop once it finds one
1186     * @return the Coord that it found first.
1187     */
1188    public Coord findNearest(Coord start, Coord... targets) {
1189        return findNearest(start, Maker.makeHS(targets));
1190    }
1191
1192    /**
1193     * If you have a target or group of targets you want to pathfind to without scanning the full map, this can be good.
1194     * It may find sub-optimal paths in the presence of costs to move into cells. It is useful when you want to move in
1195     * a straight line to a known nearby goal.
1196     *
1197     * @param start   your starting location
1198     * @param targets an array or vararg of Coords to pathfind to the nearest of
1199     * @return an ArrayList of Coord that goes from a cell adjacent to start and goes to one of the targets. Copy of path.
1200     */
1201    public ArrayList<Coord> findShortcutPath(Coord start, Coord... targets) {
1202        if (targets.length == 0) {
1203            cutShort = true;
1204            path.clear();
1205            return new ArrayList<>(path);
1206        }
1207        Coord currentPos = findNearest(start, targets);
1208        while (true) {
1209            if (frustration > 500) {
1210                path.clear();
1211                break;
1212            }
1213            double best = gradientMap[currentPos.x][currentPos.y];
1214            appendDirToShuffle(rng);
1215            int choice = 0;
1216
1217            for (int d = 0; d < measurement.directionCount() + 1; d++) {
1218                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
1219                if(!pt.isWithin(width, height))
1220                    continue;
1221                if (gradientMap[pt.x][pt.y] < best) {
1222                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
1223                        best = gradientMap[pt.x][pt.y];
1224                        choice = d;
1225                    }
1226                }
1227            }
1228
1229            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
1230                cutShort = true;
1231                frustration = 0;
1232                return new ArrayList<>(path);
1233            }
1234            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
1235            if (gradientMap[currentPos.x][currentPos.y] == 0)
1236                break;
1237            path.add(currentPos);
1238            frustration++;
1239        }
1240        frustration = 0;
1241        cutShort = false;
1242        Collections.reverse(path);
1243        return new ArrayList<>(path);
1244
1245    }
1246
1247    /**
1248     * Recalculate the Dijkstra map until it reaches a Coord in targets, then returns the first several targets found,
1249     * up to limit or less if the map is fully searched without finding enough.
1250     * This uses the current measurement.
1251     *
1252     * @param start   the cell to use as the origin for finding the nearest targets
1253     * @param limit   the maximum number of targets to find before returning
1254     * @param targets the Coords that this is trying to find; it will stop once it finds enough (based on limit)
1255     * @return the Coords that it found first.
1256     */
1257    public ArrayList<Coord> findNearestMultiple(Coord start, int limit, Collection<Coord> targets) {
1258        if (!initialized) return null;
1259        ArrayList<Coord> found = new ArrayList<>(limit);
1260        if (targets == null)
1261            return found;
1262        if (targets.contains(start))
1263            return found;
1264        resetMap();
1265        Coord start2 = start;
1266        int xShift = width / 6, yShift = height / 6;
1267        while (physicalMap[start.x][start.y] >= WALL && frustration < 50) {
1268            start2 = Coord.get(Math.min(Math.max(1, start.x + rng.nextInt(1 + xShift * 2) - xShift), width - 2),
1269                    Math.min(Math.max(1, start.y + rng.nextInt(1 + yShift * 2) - yShift), height - 2));
1270        }
1271        gradientMap[start2.x][start2.y] = 0.0;
1272        int adjX, adjY, cen, cenX, cenY;
1273        double cs, dist;
1274        Coord adj;
1275        fresh.clear();
1276        fresh.add(encode(start2));
1277        int fsz, numAssigned = 1;
1278        mappedCount = 1;
1279        Direction[] moveDirs = (measurement == Measurement.MANHATTAN) ? Direction.CARDINALS : Direction.OUTWARDS;
1280
1281        while (numAssigned > 0) {
1282            numAssigned = 0;
1283            fsz = fresh.size;
1284            for (int ci = fsz-1; ci >= 0; ci--) {
1285                cen = fresh.removeIndex(ci);
1286                cenX = decodeX(cen);
1287                cenY = decodeY(cen);
1288                dist = gradientMap[cenX][cenY];
1289
1290                for (int d = 0; d < moveDirs.length; d++) {
1291                    adjX = cenX + moveDirs[d].deltaX;
1292                    adjY = cenY + moveDirs[d].deltaY;
1293                    if (adjX < 0 || adjY < 0 || width <= adjX || height <= adjY)
1294                        /* Outside the map */
1295                        continue;
1296                    if(d >= 4 && blockingRequirement > 0) // diagonal
1297                    {
1298                        if((gradientMap[adjX][cenY] > FLOOR ? 1 : 0)
1299                                + (gradientMap[cenX][adjY] > FLOOR ? 1 : 0)
1300                                >= blockingRequirement)
1301                        {
1302                            continue;
1303                        }
1304                    }
1305                    double h = measurement.heuristic(moveDirs[d]);
1306                    cs = dist + h * costMap[adjX][adjY];
1307                    if (gradientMap[adjX][adjY] <= FLOOR && cs < gradientMap[adjX][adjY]) {
1308                        ++mappedCount;
1309                        if (targets.contains(adj = Coord.get(adjX, adjY))) {
1310                            found.add(adj);
1311                            if (found.size() >= limit) {
1312                                fresh.clear();
1313                                return found;
1314                            }
1315                        }
1316                        setFresh(adjX, adjY, cs);
1317                        ++numAssigned;
1318                    }
1319                }
1320            }
1321        }
1322        return found;
1323    }
1324
1325    /**
1326     * Recalculate the Dijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1327     * a cell in the returned Dijkstra map assumes that a creature is square, with a side length equal to the passed
1328     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1329     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1330     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1331     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1332     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1333     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1334     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1335     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1336     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1337     * current measurement.  The result is stored in the {@link #gradientMap} field and a copy is returned.
1338     *
1339     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
1340     *                   path that cannot be moved through; this can be null if there are no such obstacles.
1341     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1342     *                   creature. Non-square creatures are not supported because turning is really hard.
1343     * @return A 2D double[width][height] using the width and height of what this knows about the physical map.
1344     */
1345    public double[][] scan(final Collection<Coord> impassable, final int size) {
1346        scan(null, impassable, size);
1347        double[][] gradientClone = new double[width][height];
1348        for (int x = 0; x < width; x++) {
1349            for (int y = 0; y < height; y++) {
1350                if (gradientMap[x][y] == FLOOR) {
1351                    gradientMap[x][y] = DARK;
1352                }
1353            }
1354            System.arraycopy(gradientMap[x], 0, gradientClone[x], 0, height);
1355        }
1356        return gradientClone;
1357
1358    }
1359
1360    /**
1361     * Recalculate the Dijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1362     * a cell in the returned Dijkstra map assumes that a creature is square, with a side length equal to the passed
1363     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1364     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1365     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1366     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1367     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1368     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1369     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1370     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1371     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1372     * current measurement.  The result is stored in the {@link #gradientMap} field, and nothing is returned.
1373     * If you want the data returned, you can use {@link #scan(Collection, int)} (which calls this method with
1374     * null for the start parameter, then modifies the gradientMap field and returns a copy), or you can
1375     * just retrieve the gradientMap (maybe copying it; {@link squidpony.ArrayTools#copy(double[][])} is a
1376     * convenient option for copying a 2D double array). If start is non-null, which is usually used when
1377     * finding a single path, then cells that didn't need to be explored (because they were further than the
1378     * path needed to go from start to goal) will have the value {@link #FLOOR}. You may wish to assign a
1379     * different value to these cells in some cases (especially if start is null, which means any cells that
1380     * are still FLOOR could not be reached from any goal), and the overloads of scan that return 2D double
1381     * arrays do change FLOOR to {@link #DARK}, which is usually treated similarly to {@link #WALL}.
1382     *
1383     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
1384     *                   path that cannot be moved through; this can be null if there are no such obstacles.
1385     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1386     *                   creature. Non-square creatures are not supported because turning is really hard.
1387     */
1388    public void scan(final Coord start, final Collection<Coord> impassable, final int size) {
1389
1390        if (!initialized) return;
1391        double[][] gradientClone = ArrayTools.copy(gradientMap);
1392        if (impassable != null && !impassable.isEmpty()) {
1393            for (Coord pt : impassable) {
1394                if(pt != null && pt.isWithin(width, height))
1395                    gradientMap[pt.x][pt.y] = WALL;
1396            }
1397        }
1398        for (int xx = size; xx < width; xx++) {
1399            for (int yy = size; yy < height; yy++) {
1400                if(gradientMap[xx][yy] > FLOOR) {
1401                    for (int xs = xx, xi = 0; xi < size && xs >= 0; xs--, xi++) {
1402                        for (int ys = yy, yi = 0; yi < size && ys >= 0; ys--, yi++) {
1403                            gradientClone[xs][ys] = WALL;
1404                        }
1405                    }
1406                }
1407            }
1408        }
1409        int dec, adjX, adjY, cen, cenX, cenY;
1410
1411        PER_GOAL:
1412        for (int i = 0; i < goals.size; i++) {
1413            dec = goals.get(i);
1414            for (int xs = decodeX(dec), xi = 0; xi < size && xs >= 0; xs--, xi++) {
1415                for (int ys = decodeY(dec), yi = 0; yi < size && ys >= 0; ys--, yi++) {
1416                    if(physicalMap[xs][ys] > FLOOR)
1417                        continue PER_GOAL;
1418                    gradientClone[xs][ys] = GOAL;
1419                }
1420            }
1421        }
1422        double currentLowest = 999000, cs, dist;
1423        fresh.clear();
1424        for (int x = 0; x < width; x++) {
1425            for (int y = 0; y < height; y++) {
1426                if (gradientClone[x][y] <= FLOOR) {
1427                    if (gradientClone[x][y] < currentLowest) {
1428                        currentLowest = gradientClone[x][y];
1429                        fresh.clear();
1430                        fresh.add(encode(x, y));
1431                    } else if (gradientClone[x][y] == currentLowest) {
1432                        fresh.add(encode(x, y));
1433                    }
1434                }
1435            }
1436        }
1437        int fsz, numAssigned = fresh.size;
1438        mappedCount = goals.size;
1439        Direction[] moveDirs = (measurement == Measurement.MANHATTAN) ? Direction.CARDINALS : Direction.OUTWARDS;
1440
1441        while (numAssigned > 0) {
1442            numAssigned = 0;
1443            fsz = fresh.size;
1444            for (int ci = fsz-1; ci >= 0; ci--) {
1445                cen = fresh.removeIndex(ci);
1446                cenX = decodeX(cen);
1447                cenY = decodeY(cen);
1448                dist = gradientClone[cenX][cenY];
1449
1450                for (int d = 0; d < moveDirs.length; d++) {
1451                    adjX = cenX + moveDirs[d].deltaX;
1452                    adjY = cenY + moveDirs[d].deltaY;
1453                    if (adjX < 0 || adjY < 0 || width <= adjX || height <= adjY)
1454                        /* Outside the map */
1455                        continue;
1456                    if(d >= 4 && blockingRequirement > 0) // diagonal
1457                    {
1458                        if((gradientClone[adjX][cenY] > FLOOR ? 1 : 0)
1459                                + (gradientClone[cenX][adjY] > FLOOR ? 1 : 0)
1460                                >= blockingRequirement)
1461                        {
1462                            continue;
1463                        }
1464                    }
1465                    double h = measurement.heuristic(moveDirs[d]);
1466                    cs = dist + h * costMap[adjX][adjY];
1467                    if (gradientClone[adjX][adjY] <= FLOOR && cs < gradientClone[adjX][adjY]) {
1468                        setFresh(adjX, adjY, cs);
1469                        ++numAssigned;
1470                        ++mappedCount;
1471                        if(start != null && start.x == adjX && start.y == adjY && standardCosts)
1472                        {
1473                            if (impassable != null && !impassable.isEmpty()) {
1474                                for (Coord pt : impassable) {
1475                                    if(pt != null && pt.isWithin(width, height)) {
1476                                        for (int xs = pt.x, xi = 0; xi < size && xs >= 0; xs--, xi++) {
1477                                            for (int ys = pt.y, yi = 0; yi < size && ys >= 0; ys--, yi++) {
1478                                                gradientClone[xs][ys] = physicalMap[xs][ys];
1479                                            }
1480                                        }
1481                                    }
1482                                }
1483                            }
1484                            gradientMap = gradientClone;
1485                            return;
1486                        }
1487                    }
1488                }
1489            }
1490        }
1491        if (impassable != null && !impassable.isEmpty()) {
1492            for (Coord pt : impassable) {
1493                if(pt != null && pt.isWithin(width, height)) {
1494                    for (int xs = pt.x, xi = 0; xi < size && xs >= 0; xs--, xi++) {
1495                        for (int ys = pt.y, yi = 0; yi < size && ys >= 0; ys--, yi++) {
1496                            gradientClone[xs][ys] = physicalMap[xs][ys];
1497                        }
1498                    }
1499                }
1500            }
1501        }
1502        gradientMap = gradientClone;
1503    }
1504
1505
1506    /**
1507     * Recalculate the Dijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1508     * a cell in the returned Dijkstra map assumes that a creature is square, with a side length equal to the passed
1509     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1510     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1511     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1512     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1513     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1514     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1515     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1516     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1517     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1518     * current measurement.  The result is stored in the {@link #gradientMap} field and a copy is returned.
1519     *
1520     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
1521     *                   path that cannot be moved through; this can be null if there are no such obstacles.
1522     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1523     *                   creature. Non-square creatures are not supported because turning is really hard.
1524     * @return A 2D double[width][height] using the width and height of what this knows about the physical map.
1525     */
1526    public double[][] partialScan(final int limit, final Collection<Coord> impassable, final int size) {
1527        partialScan(limit,null, impassable, size);
1528        double[][] gradientClone = new double[width][height];
1529        for (int x = 0; x < width; x++) {
1530            for (int y = 0; y < height; y++) {
1531                if (gradientMap[x][y] == FLOOR) {
1532                    gradientMap[x][y] = DARK;
1533                }
1534            }
1535            System.arraycopy(gradientMap[x], 0, gradientClone[x], 0, height);
1536        }
1537        return gradientClone;
1538
1539    }
1540
1541    /**
1542     * Recalculate the Dijkstra map for a creature that is potentially larger than 1x1 cell and return it. The value of
1543     * a cell in the returned Dijkstra map assumes that a creature is square, with a side length equal to the passed
1544     * size, that its minimum-x, minimum-y cell is the starting cell, and that any cell with a distance number
1545     * represents the distance for the creature's minimum-x, minimum-y cell to reach it. Cells that cannot be entered
1546     * by the minimum-x, minimum-y cell because of sizing (such as a floor cell next to a maximum-x and/or maximum-y
1547     * wall if size is &gt; 1) will be marked as DARK. Cells that were marked as goals with setGoal will have
1548     * a value of 0, the cells adjacent to goals will have a value of 1, and cells progressively further
1549     * from goals will have a value equal to the distance from the nearest goal. The exceptions are walls,
1550     * which will have a value defined by the WALL constant in this class, and areas that the scan was
1551     * unable to reach, which will have a value defined by the DARK constant in this class. (typically,
1552     * these areas should not be used to place NPCs or items and should be filled with walls). This uses the
1553     * current measurement.  The result is stored in the {@link #gradientMap} field, and nothing is returned.
1554     * If you want the data returned, you can use {@link #partialScan(int, Collection, int)} (which calls this method
1555     * with null for the start parameter, then modifies the gradientMap field and returns a copy), or you can
1556     * just retrieve the gradientMap (maybe copying it; {@link squidpony.ArrayTools#copy(double[][])} is a
1557     * convenient option for copying a 2D double array). If start is non-null, which is usually used when
1558     * finding a single path, then cells that didn't need to be explored (because they were further than the
1559     * path needed to go from start to goal) will have the value {@link #FLOOR}. You may wish to assign a
1560     * different value to these cells in some cases (especially if start is null, which means any cells that
1561     * are still FLOOR could not be reached from any goal), and the overloads of partialScan that return 2D double
1562     * arrays do change FLOOR to {@link #DARK}, which is usually treated similarly to {@link #WALL}.
1563     *
1564     * @param impassable A Collection of Coord keys representing the locations of enemies or other moving obstacles to a
1565     *                   path that cannot be moved through; this can be null if there are no such obstacles.
1566     * @param size       The length of one side of a square creature using this to find a path, i.e. 2 for a 2x2 cell
1567     *                   creature. Non-square creatures are not supported because turning is really hard.
1568     */
1569    public void partialScan(final int limit, final Coord start, final Collection<Coord> impassable, final int size) {
1570
1571        if (!initialized || limit <= 0) return;
1572        double[][] gradientClone = ArrayTools.copy(gradientMap);
1573        if (impassable != null && !impassable.isEmpty()) {
1574            for (Coord pt : impassable) {
1575                if(pt != null && pt.isWithin(width, height))
1576                    gradientMap[pt.x][pt.y] = WALL;
1577            }
1578        }
1579        for (int xx = size; xx < width; xx++) {
1580            for (int yy = size; yy < height; yy++) {
1581                if(gradientMap[xx][yy] > FLOOR) {
1582                    for (int xs = xx, xi = 0; xi < size && xs >= 0; xs--, xi++) {
1583                        for (int ys = yy, yi = 0; yi < size && ys >= 0; ys--, yi++) {
1584                            gradientClone[xs][ys] = WALL;
1585                        }
1586                    }
1587                }
1588            }
1589        }
1590        int dec, adjX, adjY, cen, cenX, cenY;
1591
1592        PER_GOAL:
1593        for (int i = 0; i < goals.size; i++) {
1594            dec = goals.get(i);
1595            for (int xs = decodeX(dec), xi = 0; xi < size && xs >= 0; xs--, xi++) {
1596                for (int ys = decodeY(dec), yi = 0; yi < size && ys >= 0; ys--, yi++) {
1597                    if(physicalMap[xs][ys] > FLOOR)
1598                        continue PER_GOAL;
1599                    gradientClone[xs][ys] = GOAL;
1600                }
1601            }
1602        }
1603        double currentLowest = 999000, cs, dist;
1604        fresh.clear();
1605        for (int x = 0; x < width; x++) {
1606            for (int y = 0; y < height; y++) {
1607                if (gradientClone[x][y] <= FLOOR) {
1608                    if (gradientClone[x][y] < currentLowest) {
1609                        currentLowest = gradientClone[x][y];
1610                        fresh.clear();
1611                        fresh.add(encode(x, y));
1612                    } else if (gradientClone[x][y] == currentLowest) {
1613                        fresh.add(encode(x, y));
1614                    }
1615                }
1616            }
1617        }
1618        int fsz, numAssigned = fresh.size;
1619        mappedCount = goals.size;
1620        Direction[] moveDirs = (measurement == Measurement.MANHATTAN) ? Direction.CARDINALS : Direction.OUTWARDS;
1621
1622        int iter = 0;
1623        while (numAssigned > 0 && iter++ < limit) {
1624            numAssigned = 0;
1625            fsz = fresh.size;
1626            for (int ci = fsz-1; ci >= 0; ci--) {
1627                cen = fresh.removeIndex(ci);
1628                cenX = decodeX(cen);
1629                cenY = decodeY(cen);
1630                dist = gradientClone[cenX][cenY];
1631
1632                for (int d = 0; d < moveDirs.length; d++) {
1633                    adjX = cenX + moveDirs[d].deltaX;
1634                    adjY = cenY + moveDirs[d].deltaY;
1635                    if (adjX < 0 || adjY < 0 || width <= adjX || height <= adjY)
1636                        /* Outside the map */
1637                        continue;
1638                    if(d >= 4 && blockingRequirement > 0) // diagonal
1639                    {
1640                        if((gradientClone[adjX][cenY] > FLOOR ? 1 : 0)
1641                                + (gradientClone[cenX][adjY] > FLOOR ? 1 : 0)
1642                                >= blockingRequirement)
1643                        {
1644                            continue;
1645                        }
1646                    }
1647                    double h = measurement.heuristic(moveDirs[d]);
1648                    cs = dist + h * costMap[adjX][adjY];
1649                    if (gradientClone[adjX][adjY] <= FLOOR && cs < gradientClone[adjX][adjY]) {
1650                        setFresh(adjX, adjY, cs);
1651                        ++numAssigned;
1652                        ++mappedCount;
1653                        if(start != null && start.x == adjX && start.y == adjY && standardCosts)
1654                        {
1655                            if (impassable != null && !impassable.isEmpty()) {
1656                                for (Coord pt : impassable) {
1657                                    if(pt != null && pt.isWithin(width, height)) {
1658                                        for (int xs = pt.x, xi = 0; xi < size && xs >= 0; xs--, xi++) {
1659                                            for (int ys = pt.y, yi = 0; yi < size && ys >= 0; ys--, yi++) {
1660                                                gradientClone[xs][ys] = physicalMap[xs][ys];
1661                                            }
1662                                        }
1663                                    }
1664                                }
1665                            }
1666                            gradientMap = gradientClone;
1667                            return;
1668                        }
1669                    }
1670                }
1671            }
1672        }
1673        if (impassable != null && !impassable.isEmpty()) {
1674            for (Coord pt : impassable) {
1675                if(pt != null && pt.isWithin(width, height)) {
1676                    for (int xs = pt.x, xi = 0; xi < size && xs >= 0; xs--, xi++) {
1677                        for (int ys = pt.y, yi = 0; yi < size && ys >= 0; ys--, yi++) {
1678                            gradientClone[xs][ys] = physicalMap[xs][ys];
1679                        }
1680                    }
1681                }
1682            }
1683        }
1684        gradientMap = gradientClone;
1685    }
1686
1687    /**
1688     * Scans the dungeon using DijkstraMap.scan with the listed goals and start point, and returns a list
1689     * of Coord positions (using the current measurement) needed to get closer to the closest reachable
1690     * goal. The maximum length of the returned list is given by length, which represents movement in a system where
1691     * a single move can be multiple cells if length is greater than 1 and should usually be 1 in standard roguelikes;
1692     * if moving the full length of the list would place the mover in a position shared  by one of the positions in
1693     * onlyPassable (which is typically filled with friendly units that can be passed through in multi-cell-movement
1694     * scenarios), it will recalculate a move so that it does not pass into that cell. The keys in impassable should
1695     * be the positions of enemies and obstacles that cannot be moved  through, and will be ignored if there is a goal
1696     * overlapping one. This overload always scans the whole map; use
1697     * {@link #findPath(int, int, Collection, Collection, Coord, Coord...)} to scan a smaller area for performance reasons.
1698     * <br>
1699     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1700     * each call to a pathfinding method.
1701     * @param length       the length of the path to calculate
1702     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
1703     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1704     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1705     * @param targets      a vararg or array of Coord that this will try to pathfind toward
1706     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1707     */
1708    public ArrayList<Coord> findPath(int length, Collection<Coord> impassable,
1709                                     Collection<Coord> onlyPassable, Coord start, Coord... targets) {
1710        return findPath(length, -1, impassable, onlyPassable, start, targets);
1711    }
1712
1713    /**
1714     * Scans the dungeon using DijkstraMap.scan or DijkstraMap.partialScan with the listed goals and start
1715     * point, and returns a list of Coord positions (using the current measurement) needed to get closer
1716     * to the closest reachable goal. The maximum length of the returned list is given by length, which represents
1717     * movement in a system where a single move can be multiple cells if length is greater than 1 and should usually
1718     * be 1 in standard roguelikes; if moving the full length of the list would place the mover in a position shared
1719     * by one of the positions in onlyPassable (which is typically filled with friendly units that can be passed
1720     * through in multi-cell-movement scenarios), it will recalculate a move so that it does not pass into that cell.
1721     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1722     * through, and will be ignored if there is a goal overlapping one.
1723     * The full map will only be scanned if scanLimit is 0 or less; for positive scanLimit values this will scan only
1724     * that distance out from each goal, which can save processing time on maps where only a small part matters.
1725     * Generally, scanLimit should be significantly greater than length.
1726     * <br>
1727     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1728     * each call to a pathfinding method.
1729     *
1730     * @param length       the length of the path to calculate
1731     * @param scanLimit    how many cells away from a goal to actually process; negative to process whole map
1732     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
1733     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1734     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1735     * @param targets      a vararg or array of Coord that this will try to pathfind toward
1736     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1737     */
1738    public ArrayList<Coord> findPath(int length, int scanLimit, Collection<Coord> impassable,
1739                                     Collection<Coord> onlyPassable, Coord start, Coord... targets) {
1740        return findPath(null, length, scanLimit, impassable, onlyPassable, start, targets);
1741    }
1742    /**
1743     * Scans the dungeon using DijkstraMap.scan or DijkstraMap.partialScan with the listed goals and start
1744     * point, and returns a list of Coord positions (using the current measurement) needed to get closer
1745     * to the closest reachable goal. The maximum length of the returned list is given by length, which represents
1746     * movement in a system where a single move can be multiple cells if length is greater than 1 and should usually
1747     * be 1 in standard roguelikes; if moving the full length of the list would place the mover in a position shared
1748     * by one of the positions in onlyPassable (which is typically filled with friendly units that can be passed
1749     * through in multi-cell-movement scenarios), it will recalculate a move so that it does not pass into that cell.
1750     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1751     * through, and will be ignored if there is a goal overlapping one.
1752     * The full map will only be scanned if scanLimit is 0 or less; for positive scanLimit values this will scan only
1753     * that distance out from each goal, which can save processing time on maps where only a small part matters.
1754     * Generally, scanLimit should be significantly greater than length.
1755     * <br>
1756     * This overload takes a buffer parameter, an ArrayList of Coord, that the results will be appended to. If the
1757     * buffer is null, a new ArrayList will be made and appended to. This caches its result in a member field, path,
1758     * which can be fetched after finding a path and will change with each call to a pathfinding method. Any existing
1759     * contents of buffer will not affect the path field of this DijkstraMap.
1760     *
1761     * @param buffer       an existing ArrayList of Coord that will have the result appended to it (in-place); if null, this will make a new ArrayList
1762     * @param length       the length of the path to calculate
1763     * @param scanLimit    how many cells away from a goal to actually process; negative to process whole map
1764     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
1765     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1766     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1767     * @param targets      a vararg or array of Coord that this will try to pathfind toward
1768     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1769     */
1770    public ArrayList<Coord> findPath(ArrayList<Coord> buffer, int length, int scanLimit, Collection<Coord> impassable,
1771                                     Collection<Coord> onlyPassable, Coord start, Coord... targets) {
1772        path.clear();
1773        if (!initialized || length <= 0)
1774        {
1775            cutShort = true;
1776            if(buffer == null)
1777                return new ArrayList<>();
1778            else
1779            {
1780                return buffer;
1781            }
1782        }
1783        if (impassable == null)
1784            impassable2.clear();
1785        else
1786        {
1787            impassable2.clear();
1788            impassable2.addAll(impassable);
1789        }
1790        if (onlyPassable != null && length == 1) 
1791            impassable2.addAll(onlyPassable);
1792        
1793        resetMap();
1794        setGoals(targets);
1795        if (goals.isEmpty())
1796        {
1797            cutShort = true;
1798            if(buffer == null)
1799                return new ArrayList<>();
1800            else
1801            {
1802                return buffer;
1803            }
1804        }
1805        if(scanLimit <= 0 || scanLimit < length)
1806            scan(start, impassable2);
1807        else
1808            partialScan(start, scanLimit, impassable2);
1809        Coord currentPos = start;
1810        double paidLength = 0.0;
1811        while (true) {
1812            if (frustration > 500) {
1813                path.clear();
1814                break;
1815            }
1816            double best = gradientMap[currentPos.x][currentPos.y];
1817            appendDirToShuffle(rng);
1818            int choice = 0;
1819
1820            for (int d = 0; d <= measurement.directionCount(); d++) {
1821                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
1822                if(!pt.isWithin(width, height))
1823                    continue;
1824                if (gradientMap[pt.x][pt.y] < best && !impassable2.contains(pt)) {
1825                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
1826                        best = gradientMap[pt.x][pt.y];
1827                        choice = d;
1828                    }
1829                }
1830            }
1831
1832            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
1833                cutShort = true;
1834                frustration = 0;
1835                if(buffer == null)
1836                    return new ArrayList<>(path);
1837                else
1838                {
1839                    buffer.addAll(path);
1840                    return buffer;
1841                }
1842            }
1843            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
1844            path.add(currentPos);
1845            paidLength += costMap[currentPos.x][currentPos.y];
1846            frustration++;
1847            if (paidLength > length - 1.0) {
1848                if (onlyPassable != null && onlyPassable.contains(currentPos)) {
1849                    tempSet.clear();
1850                    tempSet.addAll(impassable2);
1851                    tempSet.add(currentPos);
1852                    return findPath(buffer, length, scanLimit, tempSet, onlyPassable, start, targets);
1853                }
1854                break;
1855            }
1856            if (gradientMap[currentPos.x][currentPos.y] == 0)
1857                break;
1858        }
1859        cutShort = false;
1860        frustration = 0;
1861        goals.clear();
1862        if(buffer == null)
1863            return new ArrayList<>(path);
1864        else
1865        {
1866            buffer.addAll(path);
1867            return buffer;
1868        }
1869    }
1870
1871    /**
1872     * Scans the dungeon using DijkstraMap.scan with the listed goals and start point, and returns a list
1873     * of Coord positions (using the current measurement) needed to get closer to a goal, until preferredRange is
1874     * reached, or further from a goal if the preferredRange has not been met at the current distance.
1875     * The maximum length of the returned list is given by moveLength; if moving the full length of
1876     * the list would place the mover in a position shared by one of the positions in onlyPassable
1877     * (which is typically filled with friendly units that can be passed through in multi-tile-
1878     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
1879     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1880     * through, and will be ignored if there is a goal overlapping one.
1881     * <br>
1882     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1883     * each call to a pathfinding method.
1884     *
1885     * @param moveLength     the length of the path to calculate
1886     * @param preferredRange the distance this unit will try to keep from a target
1887     * @param los            a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
1888     *                       should be disregarded.
1889     * @param impassable     a Set of impassable Coord positions that may change (not constant like walls); can be null
1890     * @param onlyPassable   a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1891     * @param start          the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1892     * @param targets        a vararg or array of Coord that this will try to pathfind toward
1893     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1894     */
1895    public ArrayList<Coord> findAttackPath(int moveLength, int preferredRange, LOS los, Collection<Coord> impassable,
1896                                           Collection<Coord> onlyPassable, Coord start, Coord... targets) {
1897        return findAttackPath(moveLength, preferredRange, preferredRange, los, impassable, onlyPassable, start, targets);
1898    }
1899
1900    /**
1901     * Scans the dungeon using DijkstraMap.scan with the listed goals and start point, and returns a list
1902     * of Coord positions (using the current measurement) needed to get closer to a goal, until a cell is reached with
1903     * a distance from a goal that is at least equal to minPreferredRange and no more than maxPreferredRange,
1904     * which may go further from a goal if the minPreferredRange has not been met at the current distance.
1905     * The maximum length of the returned list is given by moveLength; if moving the full length of
1906     * the list would place the mover in a position shared by one of the positions in onlyPassable
1907     * (which is typically filled with friendly units that can be passed through in multi-tile-
1908     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
1909     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1910     * through, and will be ignored if there is a goal overlapping one.
1911     * <br>
1912     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
1913     * each call to a pathfinding method.
1914     *
1915     * @param moveLength        the length of the path to calculate
1916     * @param minPreferredRange the (inclusive) lower bound of the distance this unit will try to keep from a target
1917     * @param maxPreferredRange the (inclusive) upper bound of the distance this unit will try to keep from a target
1918     * @param los               a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
1919     *                          should be disregarded.
1920     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
1921     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1922     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1923     * @param targets           a vararg or array of Coord that this will try to pathfind toward
1924     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1925     */
1926    public ArrayList<Coord> findAttackPath(int moveLength, int minPreferredRange, int maxPreferredRange, LOS los,
1927                                           Collection<Coord> impassable, Collection<Coord> onlyPassable, Coord start, Coord... targets) {
1928        return findAttackPath(null, moveLength, minPreferredRange, maxPreferredRange, los, impassable, onlyPassable, start, targets);
1929    }
1930    /**
1931     * Scans the dungeon using DijkstraMap.scan with the listed goals and start point, and returns a list
1932     * of Coord positions (using the current measurement) needed to get closer to a goal, until a cell is reached with
1933     * a distance from a goal that is at least equal to minPreferredRange and no more than maxPreferredRange,
1934     * which may go further from a goal if the minPreferredRange has not been met at the current distance.
1935     * The maximum length of the returned list is given by moveLength; if moving the full length of
1936     * the list would place the mover in a position shared by one of the positions in onlyPassable
1937     * (which is typically filled with friendly units that can be passed through in multi-tile-
1938     * movement scenarios), it will recalculate a move so that it does not pass into that cell. In most roguelikes where
1939     * movement happens one cell at a time, moveLength should be 1; if it is higher then the path will prefer getting
1940     * further away from the target (using up most or all of moveLength) while minPreferredRange and maxPreferredRange
1941     * can be satisfied. This does ensure a pathfinder with a ranged weapon stays far from melee range, but it may not
1942     * be the expected behavior because it will try to find the best path rather than the shortest it can attack from.
1943     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
1944     * through, and will be ignored if there is a goal overlapping one.
1945     * <br>
1946     * This overload takes a buffer parameter, an ArrayList of Coord, that the results will be appended to. If the
1947     * buffer is null, a new ArrayList will be made and appended to. This caches its result in a member field, path,
1948     * which can be fetched after finding a path and will change with each call to a pathfinding method. Any existing
1949     * contents of buffer will not affect the path field of this DijkstraMap.
1950     *
1951     * @param buffer            an existing ArrayList of Coord that will have the result appended to it (in-place); if null, this will make a new ArrayList
1952     * @param moveLength        the length of the path to calculate; almost always, the pathfinder will try to use this length in full to obtain the best range
1953     * @param minPreferredRange the (inclusive) lower bound of the distance this unit will try to keep from a target
1954     * @param maxPreferredRange the (inclusive) upper bound of the distance this unit will try to keep from a target
1955     * @param los               a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
1956     *                          should be disregarded.
1957     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
1958     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
1959     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
1960     * @param targets           a vararg or array of Coord that this will try to pathfind toward
1961     * @return an ArrayList of Coord that will contain the locations of this creature as it goes toward a target. Copy of path.
1962     */
1963    public ArrayList<Coord> findAttackPath(ArrayList<Coord> buffer, int moveLength, int minPreferredRange, int maxPreferredRange, LOS los,
1964                                           Collection<Coord> impassable, Collection<Coord> onlyPassable, Coord start, Coord... targets) {
1965        if (!initialized || moveLength <= 0)
1966        {
1967            cutShort = true;
1968            if(buffer == null)
1969                return new ArrayList<>();
1970            else
1971            {
1972                return buffer;
1973            }
1974        }
1975        if (minPreferredRange < 0) minPreferredRange = 0;
1976        if (maxPreferredRange < minPreferredRange) maxPreferredRange = minPreferredRange;
1977        double[][] resMap = new double[width][height];
1978        if (los != null) {
1979            for (int x = 0; x < width; x++) {
1980                for (int y = 0; y < height; y++) {
1981                    resMap[x][y] = (physicalMap[x][y] == WALL) ? 1.0 : 0.0;
1982                }
1983            }
1984        }
1985        path.clear();
1986        if (impassable == null)
1987            impassable2.clear();
1988        else
1989        {
1990            impassable2.clear();
1991            impassable2.addAll(impassable);
1992        }
1993        if (onlyPassable != null && moveLength == 1)
1994            impassable2.addAll(onlyPassable);
1995
1996        resetMap();
1997        for (Coord goal : targets) {
1998            setGoal(goal.x, goal.y);
1999        }
2000        if (goals.isEmpty())
2001        {
2002            cutShort = true;
2003            if(buffer == null)
2004                return new ArrayList<>();
2005            else
2006            {
2007                return buffer;
2008            }
2009        }
2010
2011        Measurement mess = measurement;
2012        if (measurement == Measurement.EUCLIDEAN) {
2013            measurement = Measurement.CHEBYSHEV;
2014        }
2015        scan(null, impassable2);
2016        for (int x = 0; x < width; x++) {
2017            for (int y = 0; y < height; y++) {
2018                if (gradientMap[x][y] == FLOOR) {
2019                    gradientMap[x][y] = DARK;
2020                }
2021            }
2022        }
2023        goals.clear();
2024
2025        for (int x = 0; x < width; x++) {
2026            CELL:
2027            for (int y = 0; y < height; y++) {
2028                if (gradientMap[x][y] == WALL || gradientMap[x][y] == DARK)
2029                    continue;
2030                if (gradientMap[x][y] >= minPreferredRange && gradientMap[x][y] <= maxPreferredRange) {
2031
2032                    for (Coord goal : targets) {
2033                        if (los == null || los.isReachable(resMap, x, y, goal.x, goal.y)) {
2034                            setGoal(x, y);
2035                            gradientMap[x][y] = 0;
2036                            continue CELL;
2037                        }
2038                    }
2039                    gradientMap[x][y] = FLOOR;
2040                } else
2041                    gradientMap[x][y] = FLOOR;
2042            }
2043        }
2044        measurement = mess;
2045        scan(null, impassable2);
2046        for (int x = 0; x < width; x++) {
2047            for (int y = 0; y < height; y++) {
2048                if (gradientMap[x][y] == FLOOR) {
2049                    gradientMap[x][y] = DARK;
2050                }
2051            }
2052        }
2053        if(gradientMap[start.x][start.y] <= 0.0)
2054        {
2055            cutShort = false;
2056            frustration = 0;
2057            goals.clear();
2058            if(buffer == null)
2059                return new ArrayList<>(path);
2060            else
2061            {
2062                buffer.addAll(path);
2063                return buffer;
2064            }
2065
2066        }
2067        Coord currentPos = start;
2068        double paidLength = 0.0;
2069        while (true) {
2070            if (frustration > 500) {
2071                path.clear();
2072                break;
2073            }
2074            double best = gradientMap[currentPos.x][currentPos.y];
2075            appendDirToShuffle(rng);
2076            int choice = 0;
2077
2078            for (int d = 0; d <= measurement.directionCount(); d++) {
2079                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2080                if(!pt.isWithin(width, height))
2081                    continue;
2082                if (gradientMap[pt.x][pt.y] < best  && !impassable2.contains(pt)) {
2083                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2084                        best = gradientMap[pt.x][pt.y];
2085                        choice = d;
2086                    }
2087                }
2088            }
2089
2090            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2091                cutShort = true;
2092                frustration = 0;
2093                if(buffer == null)
2094                    return new ArrayList<>(path);
2095                else
2096                {
2097                    buffer.addAll(path);
2098                    return buffer;
2099                }
2100            }
2101            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2102            path.add(Coord.get(currentPos.x, currentPos.y));
2103            paidLength += costMap[currentPos.x][currentPos.y];
2104            frustration++;
2105            if (paidLength > moveLength - 1.0) {
2106
2107                if (onlyPassable != null && onlyPassable.contains(currentPos)) {
2108                    tempSet.clear();
2109                    tempSet.addAll(impassable2);
2110                    tempSet.add(currentPos);
2111                    return findAttackPath(buffer, moveLength, minPreferredRange, maxPreferredRange, los, tempSet,
2112                            onlyPassable, start, targets);
2113                }
2114                break;
2115            }
2116            if (gradientMap[currentPos.x][currentPos.y] == 0)
2117                break;
2118        }
2119        cutShort = false;
2120        frustration = 0;
2121        goals.clear();
2122        if(buffer == null)
2123            return new ArrayList<>(path);
2124        else
2125        {
2126            buffer.addAll(path);
2127            return buffer;
2128        }
2129    }
2130
2131    /**
2132     * Scans the dungeon using DijkstraMap.scan with the listed goals and start point, and returns a list
2133     * of Coord positions (using the current measurement) needed to get closer to a goal, where goals are
2134     * considered valid if they are at a valid range for the given Technique to hit at least one target
2135     * and ideal if that Technique can affect as many targets as possible from a cell that can be moved
2136     * to with at most movelength steps.
2137     * <br>
2138     * The return value of this method is the path to get to a location to attack, but on its own it
2139     * does not tell the user how to perform the attack.  It does set the targetMap 2D Coord array field
2140     * so that if your position at the end of the returned path is non-null in targetMap, it will be
2141     * a Coord that can be used as a target position for Technique.apply() . If your position at the end
2142     * of the returned path is null, then an ideal attack position was not reachable by the path.
2143     * <br>
2144     * This needs a char[][] dungeon as an argument because DijkstraMap does not always have a char[][]
2145     * version of the map available to it, and certain AOE implementations that a Technique uses may
2146     * need a char[][] specifically to determine what they affect.
2147     * <br>
2148     * The maximum length of the returned list is given by moveLength; if moving the full length of
2149     * the list would place the mover in a position shared by one of the positions in allies
2150     * (which is typically filled with friendly units that can be passed through in multi-tile-
2151     * movement scenarios, and is also used considered an undesirable thing to affect for the Technique),
2152     * it will recalculate a move so that it does not pass into that cell.
2153     * <br>
2154     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2155     * through, and will be ignored if there is a target overlapping one.
2156     * <br>
2157     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2158     * each call to a pathfinding method.
2159     *
2160     * @param moveLength the maximum distance to try to pathfind out to; if a spot to use a Technique can be found
2161     *                   while moving no more than this distance, then the targetMap field in this object will have a
2162     *                   target Coord that is ideal for the given Technique at the x, y indices corresponding to the
2163     *                   last Coord in the returned path.
2164     * @param tech       a Technique that we will try to find an ideal place to use, and/or a path toward that place.
2165     * @param dungeon    a char 2D array with '#' for walls.
2166     * @param los        a squidgrid.LOS object if the preferred range should try to stay in line of sight, or null if LoS
2167     *                   should be disregarded.
2168     * @param impassable locations of enemies or mobile hazards/obstacles that aren't in the map as walls
2169     * @param allies     called onlyPassable in other methods, here it also represents allies for Technique things
2170     * @param start      the Coord the pathfinder starts at.
2171     * @param targets    a Set of Coord, not an array of Coord or variable argument list as in other methods.
2172     * @return an ArrayList of Coord that represents a path to travel to get to an ideal place to use tech. Copy of path.
2173     */
2174    public ArrayList<Coord> findTechniquePath(int moveLength, Technique tech, char[][] dungeon, LOS los,
2175                                              Collection<Coord> impassable, Collection<Coord> allies, Coord start, Collection<Coord> targets) {
2176        return findTechniquePath(null, moveLength, tech, dungeon, los, impassable, allies, start, targets);
2177    }
2178    /**
2179     * Scans the dungeon using DijkstraMap.scan with the listed goals and start point, and returns a list
2180     * of Coord positions (using the current measurement) needed to get closer to a goal, where goals are
2181     * considered valid if they are at a valid range for the given Technique to hit at least one target
2182     * and ideal if that Technique can affect as many targets as possible from a cell that can be moved
2183     * to with at most movelength steps.
2184     * <br>
2185     * The return value of this method is the path to get to a location to attack, but on its own it
2186     * does not tell the user how to perform the attack.  It does set the targetMap 2D Coord array field
2187     * so that if your position at the end of the returned path is non-null in targetMap, it will be
2188     * a Coord that can be used as a target position for Technique.apply() . If your position at the end
2189     * of the returned path is null, then an ideal attack position was not reachable by the path.
2190     * <br>
2191     * This needs a char[][] dungeon as an argument because DijkstraMap does not always have a char[][]
2192     * version of the map available to it, and certain AOE implementations that a Technique uses may
2193     * need a char[][] specifically to determine what they affect.
2194     * <br>
2195     * The maximum length of the returned list is given by moveLength; if moving the full length of
2196     * the list would place the mover in a position shared by one of the positions in allies
2197     * (which is typically filled with friendly units that can be passed through in multi-tile-
2198     * movement scenarios, and is also used considered an undesirable thing to affect for the Technique),
2199     * it will recalculate a move so that it does not pass into that cell.
2200     * <br>
2201     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2202     * through, and will be ignored if there is a target overlapping one.
2203     * <br>
2204     * This overload takes a buffer parameter, an ArrayList of Coord, that the results will be appended to. If the
2205     * buffer is null, a new ArrayList will be made and appended to. This caches its result in a member field, path,
2206     * which can be fetched after finding a path and will change with each call to a pathfinding method. Any existing
2207     * contents of buffer will not affect the path field of this DijkstraMap.
2208     *
2209     * @param buffer     an existing ArrayList of Coord that will have the result appended to it (in-place); if null, this will make a new ArrayList
2210     * @param moveLength the maximum distance to try to pathfind out to; if a spot to use a Technique can be found
2211     *                   while moving no more than this distance, then the targetMap field in this object will have a
2212     *                   target Coord that is ideal for the given Technique at the x, y indices corresponding to the
2213     *                   last Coord in the returned path.
2214     * @param tech       a Technique that we will try to find an ideal place to use, and/or a path toward that place.
2215     * @param dungeon    a char 2D array with '#' for walls.
2216     * @param los        a squidgrid.LOS object if the preferred range should try to stay in line of sight, or null if LoS
2217     *                   should be disregarded.
2218     * @param impassable locations of enemies or mobile hazards/obstacles that aren't in the map as walls
2219     * @param allies     called onlyPassable in other methods, here it also represents allies for Technique things
2220     * @param start      the Coord the pathfinder starts at.
2221     * @param targets    a Set of Coord, not an array of Coord or variable argument list as in other methods.
2222     * @return an ArrayList of Coord that represents a path to travel to get to an ideal place to use tech. Copy of path.
2223     */
2224    public ArrayList<Coord> findTechniquePath(ArrayList<Coord> buffer, int moveLength, Technique tech, char[][] dungeon, LOS los,
2225                                              Collection<Coord> impassable, Collection<Coord> allies, Coord start, Collection<Coord> targets) {
2226        if (!initialized || moveLength <= 0)
2227        {
2228            cutShort = true;
2229            if(buffer == null)
2230                return new ArrayList<>();
2231            else
2232            {
2233                return buffer;
2234            }
2235        }
2236        tech.setMap(dungeon);
2237        double[][] resMap = new double[width][height];
2238        double[][] worthMap = new double[width][height];
2239        double[][] userDistanceMap;
2240        double paidLength = 0.0;
2241        
2242        for (int x = 0; x < width; x++) {
2243            for (int y = 0; y < height; y++) {
2244                resMap[x][y] = (physicalMap[x][y] == WALL) ? 1.0 : 0.0;
2245                targetMap[x][y] = null;
2246            }
2247        }
2248
2249        path.clear();
2250        if (targets == null || targets.size() == 0)
2251        {
2252            cutShort = true;
2253            if(buffer == null)
2254                return new ArrayList<>();
2255            else
2256            {
2257                return buffer;
2258            }
2259        }
2260        if (impassable == null)
2261            impassable2.clear();
2262        else {
2263            impassable2.clear();
2264            impassable2.addAll(impassable);
2265        }
2266        if (allies == null)
2267            friends.clear();
2268        else {
2269            friends.clear();
2270            friends.addAll(allies);
2271            friends.remove(start);
2272        }
2273
2274        resetMap();
2275        setGoal(start);
2276        userDistanceMap = scan(impassable2);
2277        clearGoals();
2278        resetMap();
2279        for (Coord goal : targets) {
2280            setGoal(goal.x, goal.y);
2281        }
2282        if (goals.isEmpty())
2283        {
2284            cutShort = true;
2285            if(buffer == null)
2286                return new ArrayList<>();
2287            else
2288            {
2289                return buffer;
2290            }
2291        }
2292
2293        Measurement mess = measurement;
2294        /*
2295        if(measurement == Measurement.EUCLIDEAN)
2296        {
2297            measurement = Measurement.CHEBYSHEV;
2298        }
2299        */
2300        scan(null, impassable2);
2301        for (int x = 0; x < width; x++) {
2302            for (int y = 0; y < height; y++) {
2303                if (gradientMap[x][y] == FLOOR) {
2304                    gradientMap[x][y] = DARK;
2305                }
2306            }
2307        }
2308
2309        clearGoals();
2310
2311        Coord tempPt;
2312        OrderedMap<Coord, ArrayList<Coord>> ideal;
2313        // generate an array of the single best location to attack when you are in a given cell.
2314        for (int x = 0; x < width; x++) {
2315            CELL:
2316            for (int y = 0; y < height; y++) {
2317                tempPt = Coord.get(x, y);
2318                if (gradientMap[x][y] == WALL || gradientMap[x][y] == DARK || userDistanceMap[x][y] > moveLength * 2.0)
2319                    continue;
2320                if (gradientMap[x][y] >= tech.aoe.getMinRange() && gradientMap[x][y] <= tech.aoe.getMaxRange()) {
2321                    for (Coord tgt : targets) {
2322                        if (los == null || los.isReachable(resMap, x, y, tgt.x, tgt.y)) {
2323                            ideal = tech.idealLocations(tempPt, targets, friends);
2324                            if (!ideal.isEmpty()) {
2325                                targetMap[x][y] = ideal.keyAt(0);
2326                                worthMap[x][y] = ideal.getAt(0).size();
2327                                setGoal(x, y);
2328                            }
2329                            continue CELL;
2330                        }
2331                    }
2332                    gradientMap[x][y] = FLOOR;
2333                } else
2334                    gradientMap[x][y] = FLOOR;
2335            }
2336        }
2337        scan(null,impassable2);
2338
2339        double currentDistance = gradientMap[start.x][start.y];
2340        if (currentDistance <= moveLength) {
2341            int[] g_arr = goals.toArray();
2342
2343            goals.clear();
2344            setGoal(start);
2345            scan(null, impassable2, false);
2346            gradientMap[start.x][start.y] = moveLength;
2347            int decX, decY;
2348            double bestWorth = 0.0;
2349            for (int g, ig = 0; ig < g_arr.length; ig++) {
2350                g = g_arr[ig];
2351                decX = decodeX(g);
2352                decY = decodeY(g);
2353                if (gradientMap[decX][decY] <= moveLength && worthMap[decX][decY] > bestWorth) {
2354                    goals.clear();
2355                    goals.add(g);
2356                    bestWorth = worthMap[decX][decY];
2357                }
2358                else if (gradientMap[decX][decY] <= moveLength && bestWorth > 0 && worthMap[decX][decY] == bestWorth)
2359                {
2360                    goals.add(g);
2361                }
2362            }
2363            resetMap();
2364           /* for(Coord g : goals.keySet())
2365            {
2366                gradientMap[g.x][g.y] = 0.0 - worthMap[g.x][g.y];
2367            }*/
2368            scan(impassable2);
2369
2370        }
2371
2372        measurement = mess;
2373
2374        Coord currentPos = Coord.get(start.x, start.y);
2375        while (true) {
2376            if (frustration > 500) {
2377                path.clear();
2378                break;
2379            }
2380            double best = gradientMap[currentPos.x][currentPos.y];
2381            appendDirToShuffle(rng);
2382            int choice = 0;
2383
2384            for (int d = 0; d <= measurement.directionCount(); d++) {
2385                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2386                if(!pt.isWithin(width, height))
2387                    continue;
2388                if (gradientMap[pt.x][pt.y] < best && !impassable2.contains(pt)) {
2389                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2390                        best = gradientMap[pt.x][pt.y];
2391                        choice = d;
2392                    }
2393                }
2394            }
2395            if (best >= gradientMap[currentPos.x][currentPos.y]) {
2396                if (friends.contains(currentPos)) {
2397                    tempSet.clear();
2398                    tempSet.addAll(impassable2);
2399                    tempSet.add(currentPos);
2400                    return findTechniquePath(buffer, moveLength, tech, dungeon, los, tempSet,
2401                            friends, start, targets);
2402                }
2403                break;
2404            }
2405            if (best > gradientMap[start.x][start.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2406                cutShort = true;
2407                frustration = 0;
2408                if(buffer == null)
2409                    return new ArrayList<>(path);
2410                else
2411                {
2412                    buffer.addAll(path);
2413                    return buffer;
2414                }
2415            }
2416            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2417            path.add(currentPos);
2418            paidLength += costMap[currentPos.x][currentPos.y];
2419            frustration++;
2420            if (paidLength > moveLength - 1.0) {
2421                if (friends.contains(currentPos)) {
2422                    tempSet.clear();
2423                    tempSet.addAll(impassable2);
2424                    tempSet.add(currentPos);
2425                    return findTechniquePath(buffer, moveLength, tech, dungeon, los, tempSet,
2426                            friends, start, targets);
2427                }
2428                break;
2429            }
2430//            if(gradientMap[currentPos.x][currentPos.y] == 0)
2431//                break;
2432        }
2433        cutShort = false;
2434        frustration = 0;
2435        goals.clear();
2436        if(buffer == null)
2437            return new ArrayList<>(path);
2438        else
2439        {
2440            buffer.addAll(path);
2441            return buffer;
2442        }
2443    }
2444
2445
2446    private double cachedLongerPaths = 1.2;
2447    private HashSet<Coord> cachedImpassable = new HashSet<>(32, 0.25f);
2448    private Coord[] cachedFearSources;
2449    private double[][] cachedFleeMap;
2450    private int cachedSize = 1;
2451
2452    /**
2453     * Scans the dungeon using DijkstraMap.scan with the listed fearSources and start point, and returns a list
2454     * of Coord positions (using Manhattan distance) needed to get further from the closest fearSources, meant
2455     * for running away. The maximum length of the returned list is given by length; if moving the full
2456     * length of the list would place the mover in a position shared by one of the positions in onlyPassable
2457     * (which is typically filled with friendly units that can be passed through in multi-tile-
2458     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2459     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2460     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
2461     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
2462     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
2463     * The parameters preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
2464     * any subsequent calls that use the same values as the last values passed will avoid recalculating
2465     * unnecessary scans.
2466     * <br>
2467     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2468     * each call to a pathfinding method.
2469     *
2470     * @param length            the length of the path to calculate
2471     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
2472     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2473     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2474     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2475     * @param fearSources       a vararg or array of Coord positions to run away from
2476     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
2477     */
2478    public ArrayList<Coord> findFleePath(int length, double preferLongerPaths, Collection<Coord> impassable,
2479                                         Collection<Coord> onlyPassable, Coord start, Coord... fearSources) {
2480        return findFleePath(null, length, -1, preferLongerPaths, impassable, onlyPassable, start, fearSources);
2481    }
2482
2483    /**
2484     * Scans the dungeon using DijkstraMap.scan or DijkstraMap.partialScan with the listed fearSources and start
2485     * point, and returns a list of Coord positions (using this DijkstraMap's metric) needed to get further from
2486     * the closest fearSources, meant for running away. The maximum length of the returned list is given by length,
2487     * which represents movement in a system where a single move can be multiple cells if length is greater than 1 and
2488     * should usually be 1 in standard roguelikes; if moving the full length of the list would place the mover in a
2489     * position shared by one of the positions in onlyPassable (which is typically filled with friendly units that can
2490     * be passed through in multi-cell-movement scenarios), it will recalculate a move so that it does not pass into
2491     * that cell. The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2492     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
2493     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
2494     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
2495     * The parameters preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
2496     * any subsequent calls that use the same values as the last values passed will avoid recalculating
2497     * unnecessary scans. However, scanLimit is not cached; if you use scanLimit then it is assumed you are using some
2498     * value for it that shouldn't change relative to the other parameters (like twice the length).
2499     * The full map will only be scanned if scanLimit is 0 or less; for positive scanLimit values this will scan only
2500     * that distance out from each goal, which can save processing time on maps where only a small part matters.
2501     * Generally, scanLimit should be significantly greater than length.
2502     * <br>
2503     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2504     * each call to a pathfinding method.
2505     *
2506     * @param length            the length of the path to calculate
2507     * @param scanLimit         how many steps away from a fear source to calculate; negative scans the whole map
2508     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
2509     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2510     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2511     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2512     * @param fearSources       a vararg or array of Coord positions to run away from
2513     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
2514     */
2515    public ArrayList<Coord> findFleePath(int length, int scanLimit, double preferLongerPaths, Collection<Coord> impassable,
2516                                         Collection<Coord> onlyPassable, Coord start, Coord... fearSources) {
2517        return findFleePath(null, length, scanLimit, preferLongerPaths, impassable, onlyPassable, start, fearSources);
2518    }
2519    /**
2520     * Scans the dungeon using DijkstraMap.scan or DijkstraMap.partialScan with the listed fearSources and start
2521     * point, and returns a list of Coord positions (using this DijkstraMap's metric) needed to get further from
2522     * the closest fearSources, meant for running away. The maximum length of the returned list is given by length,
2523     * which represents movement in a system where a single move can be multiple cells if length is greater than 1 and
2524     * should usually be 1 in standard roguelikes; if moving the full length of the list would place the mover in a
2525     * position shared by one of the positions in onlyPassable (which is typically filled with friendly units that can
2526     * be passed through in multi-cell-movement scenarios), it will recalculate a move so that it does not pass into
2527     * that cell. The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2528     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
2529     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
2530     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
2531     * The parameters preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
2532     * any subsequent calls that use the same values as the last values passed will avoid recalculating
2533     * unnecessary scans. However, scanLimit is not cached; if you use scanLimit then it is assumed you are using some
2534     * value for it that shouldn't change relative to the other parameters (like twice the length).
2535     * The full map will only be scanned if scanLimit is 0 or less; for positive scanLimit values this will scan only
2536     * that distance out from each goal, which can save processing time on maps where only a small part matters.
2537     * Generally, scanLimit should be significantly greater than length.
2538     * <br>
2539     * This overload takes a buffer parameter, an ArrayList of Coord, that the results will be appended to. If the
2540     * buffer is null, a new ArrayList will be made and appended to. This caches its result in a member field, path,
2541     * which can be fetched after finding a path and will change with each call to a pathfinding method. Any existing
2542     * contents of buffer will not affect the path field of this DijkstraMap.
2543     *
2544     * @param buffer            an existing ArrayList of Coord that will have the result appended to it (in-place); if null, this will make a new ArrayList
2545     * @param length            the length of the path to calculate
2546     * @param scanLimit         how many steps away from a fear source to calculate; negative scans the whole map
2547     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
2548     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2549     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2550     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2551     * @param fearSources       a vararg or array of Coord positions to run away from
2552     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
2553     */
2554    public ArrayList<Coord> findFleePath(ArrayList<Coord> buffer, int length, int scanLimit, double preferLongerPaths, Collection<Coord> impassable,
2555                                         Collection<Coord> onlyPassable, Coord start, Coord... fearSources) {
2556        if (!initialized || length <= 0)
2557        {
2558            cutShort = true;
2559            if(buffer == null)
2560                return new ArrayList<>();
2561            else
2562            {
2563                return buffer;
2564            }
2565        }
2566        path.clear();
2567        if (fearSources == null || fearSources.length < 1) {
2568            cutShort = true;
2569            if(buffer == null)
2570                return new ArrayList<>();
2571            else
2572            {
2573                return buffer;
2574            }
2575        }
2576
2577        if (impassable == null)
2578            impassable2.clear();
2579        else
2580        {
2581            impassable2.clear();
2582            impassable2.addAll(impassable);
2583        }
2584        if(onlyPassable != null && length == 1)
2585            impassable2.addAll(onlyPassable);
2586        if (cachedSize == 1 && preferLongerPaths == cachedLongerPaths && impassable2.equals(cachedImpassable) &&
2587                Arrays.equals(fearSources, cachedFearSources)) {
2588            gradientMap = cachedFleeMap;
2589        } else {
2590            cachedLongerPaths = preferLongerPaths;
2591            cachedImpassable.clear();
2592            cachedImpassable.addAll(impassable2);
2593            cachedFearSources = new Coord[fearSources.length];
2594            System.arraycopy(fearSources, 0, cachedFearSources, 0, fearSources.length);
2595            cachedSize = 1;
2596            resetMap();
2597            setGoals(fearSources);
2598            if (goals.isEmpty())
2599            {
2600                cutShort = true;
2601                if(buffer == null)
2602                    return new ArrayList<>();
2603                else
2604                {
2605                    return buffer;
2606                }
2607            }
2608
2609            if(scanLimit <= 0 || scanLimit < length)
2610                cachedFleeMap = scan(impassable2);
2611            else
2612                cachedFleeMap = partialScan(scanLimit, impassable2);
2613
2614
2615            for (int x = 0; x < gradientMap.length; x++) {
2616                for (int y = 0; y < gradientMap[x].length; y++) {
2617                    gradientMap[x][y] *= (gradientMap[x][y] >= FLOOR) ? 1.0 : -preferLongerPaths;
2618                }
2619            }
2620
2621            if(scanLimit <= 0 || scanLimit < length)
2622            {
2623                scan(null, impassable2, true);
2624                for (int x = 0; x < width; x++) {
2625                    for (int y = 0; y < height; y++) {
2626                        if (gradientMap[x][y] == FLOOR) {
2627                            gradientMap[x][y] = DARK;
2628                        }
2629                    }
2630                    System.arraycopy(gradientMap[x], 0, cachedFleeMap[x], 0, height);
2631                }
2632            }
2633            else
2634                cachedFleeMap = partialScan(scanLimit, impassable2);
2635        }
2636        Coord currentPos = start;
2637        double paidLength = 0.0;
2638        while (true) {
2639            if (frustration > 500) {
2640                path.clear();
2641                break;
2642            }
2643            double best = gradientMap[currentPos.x][currentPos.y];
2644            appendDirToShuffle(rng);
2645            int choice = 0;
2646
2647            for (int d = 0; d <= measurement.directionCount(); d++) {
2648                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2649                if(!pt.isWithin(width, height))
2650                    continue;
2651                if (gradientMap[pt.x][pt.y] < best && !impassable2.contains(pt)) {
2652                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2653                        best = gradientMap[pt.x][pt.y];
2654                        choice = d;
2655                    }
2656                }
2657            }
2658            if (best >= gradientMap[start.x][start.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2659                cutShort = true;
2660                frustration = 0;
2661                if(buffer == null)
2662                    return new ArrayList<>(path);
2663                else
2664                {
2665                    buffer.addAll(path);
2666                    return buffer;
2667                }
2668            }
2669            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2670            if (path.size() > 0) {
2671                Coord last = path.get(path.size() - 1);
2672                if (gradientMap[last.x][last.y] <= gradientMap[currentPos.x][currentPos.y])
2673                    break;
2674            }
2675            path.add(currentPos);
2676            frustration++;
2677            paidLength += costMap[currentPos.x][currentPos.y];
2678            if (paidLength > length - 1.0) {
2679                if (onlyPassable != null && onlyPassable.contains(currentPos)) {
2680                    tempSet.clear();
2681                    tempSet.addAll(impassable2);
2682                    tempSet.add(currentPos);
2683                    return findFleePath(buffer, length, scanLimit, preferLongerPaths, tempSet, onlyPassable, start, fearSources);
2684                }
2685                break;
2686            }
2687        }
2688        cutShort = false;
2689        frustration = 0;
2690        goals.clear();
2691        if(buffer == null) 
2692            return new ArrayList<>(path);
2693        else
2694        {
2695            buffer.addAll(path);
2696            return buffer;
2697        }
2698    }
2699
2700    /**
2701     * For pathfinding creatures larger than 1x1 cell; scans the dungeon using DijkstraMap.scan with the listed goals
2702     * and start point, and returns a list
2703     * of Coord positions (using the current measurement) needed to get closer to the closest reachable
2704     * goal. The maximum length of the returned list is given by length; if moving the full length of
2705     * the list would place the mover in a position shared by one of the positions in onlyPassable
2706     * (which is typically filled with friendly units that can be passed through in multi-tile-
2707     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2708     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2709     * through, and will be ignored if there is a goal overlapping one.
2710     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2711     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2712     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2713     * <br>
2714     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2715     * each call to a pathfinding method.
2716     *
2717     * @param size         the side length of the creature trying to find a path
2718     * @param length       the length of the path to calculate
2719     * @param impassable   a Set of impassable Coord positions that may change (not constant like walls); can be null
2720     * @param onlyPassable a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2721     * @param start        the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2722     * @param targets      a vararg or array of Coord that this will try to pathfind toward
2723     * @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.
2724     */
2725
2726    public ArrayList<Coord> findPathLarge(final int size, int length, Collection<Coord> impassable,
2727                                          Collection<Coord> onlyPassable, Coord start, Coord... targets) {
2728        return findPathLarge(size, length, -1, impassable, onlyPassable, start, targets);
2729    }
2730    public ArrayList<Coord> findPathLarge(final int size, int length, final int scanLimit, Collection<Coord> impassable,
2731                                          Collection<Coord> onlyPassable, Coord start, Coord... targets) {
2732
2733        if (!initialized) return null;
2734        path.clear();
2735        if (impassable == null)
2736            impassable2.clear();
2737        else
2738        {
2739            impassable2.clear();
2740            impassable2.addAll(impassable);
2741        }
2742        if(onlyPassable != null && length == 1)
2743            impassable2.addAll(onlyPassable);
2744
2745        resetMap();
2746        for (Coord goal : targets) {
2747            setGoal(goal.x, goal.y);
2748        }
2749        if (goals.isEmpty())
2750        {
2751            cutShort = true;
2752            return new ArrayList<>(path);
2753        }
2754
2755        if(length < 0)
2756            length = 0;
2757        if(scanLimit <= 0 || scanLimit < length)
2758            scan(start, impassable2, size);
2759        else
2760            partialScan(scanLimit, start, impassable2, size);
2761
2762        Coord currentPos = start;
2763        double paidLength = 0.0;
2764        while (true) {
2765            if (frustration > 500) {
2766                path.clear();
2767                break;
2768            }
2769            double best = gradientMap[currentPos.x][currentPos.y];
2770            appendDirToShuffle(rng);
2771            int choice = 0;
2772
2773            for (int d = 0; d <= measurement.directionCount(); d++) {
2774                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2775                if(!pt.isWithin(width, height))
2776                    continue;
2777                if (gradientMap[pt.x][pt.y] < best && !impassable2.contains(pt)) {
2778                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2779                        best = gradientMap[pt.x][pt.y];
2780                        choice = d;
2781                    }
2782                }
2783            }
2784
2785            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2786                cutShort = true;
2787                frustration = 0;
2788                return new ArrayList<>(path);
2789            }
2790            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2791
2792            path.add(currentPos);
2793            paidLength += costMap[currentPos.x][currentPos.y];
2794            frustration++;
2795            if (paidLength > length - 1.0) {
2796                if (onlyPassable != null && onlyPassable.contains(currentPos)) {
2797                    tempSet.clear();
2798                    tempSet.addAll(impassable2);
2799                    tempSet.add(currentPos);
2800                    return findPathLarge(size, length, tempSet, onlyPassable, start, targets);
2801                }
2802                break;
2803            }
2804            if (gradientMap[currentPos.x][currentPos.y] == 0)
2805                break;
2806        }
2807        cutShort = false;
2808        frustration = 0;
2809        goals.clear();
2810        return new ArrayList<>(path);
2811    }
2812
2813    /**
2814     * For pathfinding creatures larger than 1x1 cell; scans the dungeon using DijkstraMap.scan with the listed goals
2815     * and start point, and returns a list
2816     * of Coord positions (using the current measurement) needed to get closer to a goal, until preferredRange is
2817     * reached, or further from a goal if the preferredRange has not been met at the current distance.
2818     * The maximum length of the returned list is given by moveLength; if moving the full length of
2819     * the list would place the mover in a position shared by one of the positions in onlyPassable
2820     * (which is typically filled with friendly units that can be passed through in multi-tile-
2821     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2822     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2823     * through, and will be ignored if there is a goal overlapping one.
2824     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2825     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2826     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2827     * <br>
2828     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2829     * each call to a pathfinding method.
2830     *
2831     * @param size           the side length of the creature trying to find a path
2832     * @param moveLength     the length of the path to calculate
2833     * @param preferredRange the distance this unit will try to keep from a target
2834     * @param los            a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
2835     *                       should be disregarded.
2836     * @param impassable     a Set of impassable Coord positions that may change (not constant like walls); can be null
2837     * @param onlyPassable   a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2838     * @param start          the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2839     * @param targets        a vararg or array of Coord that this will try to pathfind toward
2840     * @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.
2841     */
2842    public ArrayList<Coord> findAttackPathLarge(int size, int moveLength, int preferredRange, LOS los, Collection<Coord> impassable,
2843                                                Collection<Coord> onlyPassable, Coord start, Coord... targets) {
2844        if (!initialized) return null;
2845        if (preferredRange < 0) preferredRange = 0;
2846        double[][] resMap = new double[width][height];
2847        if (los != null) {
2848            for (int x = 0; x < width; x++) {
2849                for (int y = 0; y < height; y++) {
2850                    resMap[x][y] = (physicalMap[x][y] == WALL) ? 1.0 : 0.0;
2851                }
2852            }
2853        }
2854        path.clear();
2855        if (impassable == null)
2856            impassable2.clear();
2857        else
2858        {
2859            impassable2.clear();
2860            impassable2.addAll(impassable);
2861        }
2862        if(onlyPassable != null && moveLength == 1)
2863            impassable2.addAll(onlyPassable);
2864
2865        resetMap();
2866        for (Coord goal : targets) {
2867            setGoal(goal.x, goal.y);
2868        }
2869        if (goals.isEmpty())
2870        {
2871            cutShort = true;
2872            return new ArrayList<>(path);
2873        }
2874
2875        Measurement mess = measurement;
2876        if (measurement == Measurement.EUCLIDEAN) {
2877            measurement = Measurement.CHEBYSHEV;
2878        }
2879        scan(impassable2, size);
2880        goals.clear();
2881
2882        for (int x = 0; x < width; x++) {
2883            CELL:
2884            for (int y = 0; y < height; y++) {
2885                if (gradientMap[x][y] == WALL || gradientMap[x][y] == DARK)
2886                    continue;
2887                if (x + 2 < width && y + 2 < height && gradientMap[x][y] == preferredRange) {
2888                    for (Coord goal : targets) {
2889                        if (los == null
2890                                || los.isReachable(resMap, x, y, goal.x, goal.y)
2891                                || los.isReachable(resMap, x + 1, y, goal.x, goal.y)
2892                                || los.isReachable(resMap, x, y + 1, goal.x, goal.y)
2893                                || los.isReachable(resMap, x + 1, y + 1, goal.x, goal.y)) {
2894                            setGoal(x, y);
2895                            gradientMap[x][y] = 0;
2896                            continue CELL;
2897                        }
2898                    }
2899                    gradientMap[x][y] = FLOOR;
2900                } else
2901                    gradientMap[x][y] = FLOOR;
2902            }
2903        }
2904        measurement = mess;
2905        scan(impassable2, size);
2906
2907        Coord currentPos = start;
2908        double paidLength = 0.0;
2909        while (true) {
2910            if (frustration > 500) {
2911                path.clear();
2912                break;
2913            }
2914            double best = gradientMap[currentPos.x][currentPos.y];
2915            appendDirToShuffle(rng);
2916            int choice = 0;
2917
2918            for (int d = 0; d <= measurement.directionCount(); d++) {
2919                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
2920                if(!pt.isWithin(width, height))
2921                    continue;
2922                if (gradientMap[pt.x][pt.y] < best && !impassable2.contains(pt)) {
2923                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
2924                        best = gradientMap[pt.x][pt.y];
2925                        choice = d;
2926                    }
2927                }
2928            }
2929
2930            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
2931                cutShort = true;
2932                frustration = 0;
2933                return new ArrayList<>(path);
2934            }
2935            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
2936            path.add(currentPos);
2937            frustration++;
2938            paidLength += costMap[currentPos.x][currentPos.y];
2939            if (paidLength > moveLength - 1.0) {
2940                if (onlyPassable != null && onlyPassable.contains(currentPos)) {
2941                    tempSet.clear();
2942                    tempSet.addAll(impassable2);
2943                    tempSet.add(currentPos);
2944                    return findAttackPathLarge(size, moveLength, preferredRange, los, tempSet, onlyPassable, start, targets);
2945                }
2946                break;
2947            }
2948            if (gradientMap[currentPos.x][currentPos.y] == 0)
2949                break;
2950        }
2951        cutShort = false;
2952        frustration = 0;
2953        goals.clear();
2954        return new ArrayList<>(path);
2955    }
2956
2957    /**
2958     * Scans the dungeon using DijkstraMap.scan with the listed goals and start point, and returns a list
2959     * of Coord positions (using the current measurement) needed to get closer to a goal, until a cell is reached with
2960     * a distance from a goal that is at least equal to minPreferredRange and no more than maxPreferredRange,
2961     * which may go further from a goal if the minPreferredRange has not been met at the current distance.
2962     * The maximum length of the returned list is given by moveLength; if moving the full length of
2963     * the list would place the mover in a position shared by one of the positions in onlyPassable
2964     * (which is typically filled with friendly units that can be passed through in multi-tile-
2965     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
2966     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
2967     * through, and will be ignored if there is a goal overlapping one.
2968     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
2969     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
2970     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
2971     * <br>
2972     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
2973     * each call to a pathfinding method.
2974     *
2975     * @param size              the side length of the creature trying to find a path
2976     * @param moveLength        the length of the path to calculate
2977     * @param minPreferredRange the (inclusive) lower bound of the distance this unit will try to keep from a target
2978     * @param maxPreferredRange the (inclusive) upper bound of the distance this unit will try to keep from a target
2979     * @param los               a squidgrid.LOS object if the preferredRange should try to stay in line of sight, or null if LoS
2980     *                          should be disregarded.
2981     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
2982     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
2983     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
2984     * @param targets           a vararg or array of Coord that this will try to pathfind toward
2985     * @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.
2986     */
2987    public ArrayList<Coord> findAttackPathLarge(int size, int moveLength, int minPreferredRange, int maxPreferredRange, LOS los,
2988                                                Collection<Coord> impassable, Collection<Coord> onlyPassable, Coord start, Coord... targets) {
2989        if (!initialized) return null;
2990        if (minPreferredRange < 0) minPreferredRange = 0;
2991        if (maxPreferredRange < minPreferredRange) maxPreferredRange = minPreferredRange;
2992        double[][] resMap = new double[width][height];
2993        if (los != null) {
2994            for (int x = 0; x < width; x++) {
2995                for (int y = 0; y < height; y++) {
2996                    resMap[x][y] = (physicalMap[x][y] == WALL) ? 1.0 : 0.0;
2997                }
2998            }
2999        }
3000        path.clear();
3001        if (impassable == null)
3002            impassable2.clear();
3003        else
3004        {
3005            impassable2.clear();
3006            impassable2.addAll(impassable);
3007        }
3008        if(onlyPassable != null && moveLength == 1)
3009            impassable2.addAll(onlyPassable);
3010
3011        resetMap();
3012        for (Coord goal : targets) {
3013            setGoal(goal);
3014        }
3015        if (goals.isEmpty())
3016        {
3017            cutShort = true;
3018            return new ArrayList<>(path);
3019        }
3020
3021        Measurement mess = measurement;
3022        if (measurement == Measurement.EUCLIDEAN) {
3023            measurement = Measurement.CHEBYSHEV;
3024        }
3025        scan(impassable2, size);
3026        goals.clear();
3027
3028        for (int x = 0; x < width; x++) {
3029            CELL:
3030            for (int y = 0; y < height; y++) {
3031                if (gradientMap[x][y] == WALL || gradientMap[x][y] == DARK)
3032                    continue;
3033                if (x + 2 < width && y + 2 < height && gradientMap[x][y] >= minPreferredRange && gradientMap[x][y] <= maxPreferredRange) {
3034                    for (Coord goal : targets) {
3035                        if (los == null
3036                                || los.isReachable(resMap, x, y, goal.x, goal.y)
3037                                || los.isReachable(resMap, x + 1, y, goal.x, goal.y)
3038                                || los.isReachable(resMap, x, y + 1, goal.x, goal.y)
3039                                || los.isReachable(resMap, x + 1, y + 1, goal.x, goal.y)) {
3040                            setGoal(x, y);
3041                            gradientMap[x][y] = 0;
3042                            continue CELL;
3043                        }
3044                    }
3045                    gradientMap[x][y] = FLOOR;
3046                } else
3047                    gradientMap[x][y] = FLOOR;
3048            }
3049        }
3050        measurement = mess;
3051        scan(impassable2, size);
3052
3053        Coord currentPos = start;
3054        double paidLength = 0.0;
3055        while (true) {
3056            if (frustration > 500) {
3057                path.clear();
3058                break;
3059            }
3060
3061            double best = gradientMap[currentPos.x][currentPos.y];
3062            appendDirToShuffle(rng);
3063            int choice = 0;
3064
3065            for (int d = 0; d <= measurement.directionCount(); d++) {
3066                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
3067                if(!pt.isWithin(width, height))
3068                    continue;
3069                if (gradientMap[pt.x][pt.y] < best && !impassable2.contains(pt)) {
3070                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
3071                        best = gradientMap[pt.x][pt.y];
3072                        choice = d;
3073                    }
3074                }
3075            }
3076            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
3077                cutShort = true;
3078                frustration = 0;
3079                return new ArrayList<>(path);
3080            }
3081            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
3082
3083            path.add(currentPos);
3084            frustration++;
3085            paidLength += costMap[currentPos.x][currentPos.y];
3086            if (paidLength > moveLength - 1.0) {
3087                if (onlyPassable != null && onlyPassable.contains(currentPos)) {
3088                    tempSet.clear();
3089                    tempSet.addAll(impassable2);
3090                    tempSet.add(currentPos);
3091                    return findAttackPathLarge(size, moveLength, minPreferredRange, maxPreferredRange, los, tempSet,
3092                            onlyPassable, start, targets);
3093                }
3094                break;
3095            }
3096            if (gradientMap[currentPos.x][currentPos.y] == 0)
3097                break;
3098        }
3099        cutShort = false;
3100        frustration = 0;
3101        goals.clear();
3102        return new ArrayList<>(path);
3103    }
3104
3105    /**
3106     * Scans the dungeon using DijkstraMap.scan with the listed fearSources and start point, and returns a list
3107     * of Coord positions (using Manhattan distance) needed to get further from the closest fearSources, meant
3108     * for running away. The maximum length of the returned list is given by length; if moving the full
3109     * length of the list would place the mover in a position shared by one of the positions in onlyPassable
3110     * (which is typically filled with friendly units that can be passed through in multi-tile-
3111     * movement scenarios), it will recalculate a move so that it does not pass into that cell.
3112     * The keys in impassable should be the positions of enemies and obstacles that cannot be moved
3113     * through, and will be ignored if there is a fearSource overlapping one. The preferLongerPaths parameter
3114     * is meant to be tweaked and adjusted; higher values should make creatures prefer to escape out of
3115     * doorways instead of hiding in the closest corner, and a value of 1.2 should be typical for many maps.
3116     * The parameters size, preferLongerPaths, impassable, and the varargs used for fearSources will be cached, and
3117     * any subsequent calls that use the same values as the last values passed will avoid recalculating
3118     * unnecessary scans. Calls to findFleePath will cache as if size is 1, and may share a cache with this function.
3119     * The parameter size refers to the side length of a square unit, such as 2 for a 2x2 unit. The
3120     * parameter start must refer to the minimum-x, minimum-y cell of that unit if size is &gt; 1, and
3121     * all positions in the returned path will refer to movement of the minimum-x, minimum-y cell.
3122     * <br>
3123     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
3124     * each call to a pathfinding method.
3125     *
3126     * @param size              the side length of the creature trying the find a path
3127     * @param length            the length of the path to calculate
3128     * @param preferLongerPaths Set this to 1.2 if you aren't sure; it will probably need tweaking for different maps.
3129     * @param impassable        a Set of impassable Coord positions that may change (not constant like walls); can be null
3130     * @param onlyPassable      a Set of Coord positions that this pathfinder cannot end a path occupying (typically allies); can be null
3131     * @param start             the start of the path, should correspond to the minimum-x, minimum-y position of the pathfinder
3132     * @param fearSources       a vararg or array of Coord positions to run away from
3133     * @return an ArrayList of Coord that will contain the locations of this creature as it goes away from fear sources. Copy of path.
3134     */
3135    public ArrayList<Coord> findFleePathLarge(int size, int length, double preferLongerPaths, Collection<Coord> impassable,
3136                                              Collection<Coord> onlyPassable, Coord start, Coord... fearSources) {
3137        if (!initialized) return null;
3138        path.clear();
3139        if (impassable == null)
3140            impassable2.clear();
3141        else {
3142            impassable2.clear();
3143            impassable2.addAll(impassable);
3144        }
3145        if(onlyPassable != null && length == 1)
3146            impassable2.addAll(onlyPassable);
3147        if (fearSources == null || fearSources.length < 1) {
3148            cutShort = true;
3149            return new ArrayList<>(path);
3150        }
3151        if (size == cachedSize && preferLongerPaths == cachedLongerPaths && impassable2.equals(cachedImpassable)
3152                && Arrays.equals(fearSources, cachedFearSources)) {
3153            gradientMap = cachedFleeMap;
3154        } else {
3155            cachedLongerPaths = preferLongerPaths;
3156            cachedImpassable.clear();
3157            cachedImpassable.addAll(impassable2);
3158            cachedFearSources = new Coord[fearSources.length];
3159            System.arraycopy(fearSources, 0, cachedFearSources, 0, fearSources.length);
3160            cachedSize = size;
3161            resetMap();
3162            setGoals(fearSources);
3163            if (goals.isEmpty())
3164            {
3165                cutShort = true;
3166                return new ArrayList<>(path);
3167            }
3168
3169            scan(impassable2, size);
3170
3171            for (int x = 0; x < gradientMap.length; x++) {
3172                for (int y = 0; y < gradientMap[x].length; y++) {
3173                    gradientMap[x][y] *= (gradientMap[x][y] >= FLOOR) ? 1.0 : (0.0 - preferLongerPaths);
3174                }
3175            }
3176            cachedFleeMap = scan(impassable2, size);
3177        }
3178        Coord currentPos = start;
3179        double paidLength = 0.0;
3180        while (true) {
3181            if (frustration > 500) {
3182                path.clear();
3183                break;
3184            }
3185
3186            double best = gradientMap[currentPos.x][currentPos.y];
3187            appendDirToShuffle(rng);
3188            int choice = 0;
3189
3190            for (int d = 0; d <= measurement.directionCount(); d++) {
3191                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
3192                if(!pt.isWithin(width, height))
3193                    continue;
3194                if (gradientMap[pt.x][pt.y] < best && !impassable2.contains(pt)) {
3195                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
3196                        best = gradientMap[pt.x][pt.y];
3197                        choice = d;
3198                    }
3199                }
3200            }
3201            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
3202                cutShort = true;
3203                frustration = 0;
3204                return new ArrayList<>(path);
3205            }
3206            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
3207
3208            if (path.size() > 0) {
3209                Coord last = path.get(path.size() - 1);
3210                if (gradientMap[last.x][last.y] <= gradientMap[currentPos.x][currentPos.y])
3211                    break;
3212            }
3213            path.add(currentPos);
3214            frustration++;
3215            paidLength += costMap[currentPos.x][currentPos.y];
3216            if (paidLength > length - 1.0) {
3217                if (onlyPassable != null && onlyPassable.contains(currentPos)) {
3218                    tempSet.clear();
3219                    tempSet.addAll(impassable2);
3220                    tempSet.add(currentPos);
3221                    return findFleePathLarge(size, length, preferLongerPaths, tempSet, onlyPassable, start, fearSources);
3222                }
3223                break;
3224            }
3225        }
3226        cutShort = false;
3227        frustration = 0;
3228        goals.clear();
3229        return new ArrayList<>(path);
3230    }
3231
3232
3233    /**
3234     * When you can control how often the (relatively time-intensive) scan() method is called, but may need simple paths
3235     * very frequently (such as for a path that follows the mouse), you can use this method to reduce the amount of work
3236     * needed to find paths. Needs scan() or partialScan() to already be called and at least one goal to already be set,
3237     * and does not restrict the length of the path or behave as if the pathfinder has allies or enemies.
3238     * <br>
3239     * This caches its result in a member field, path, which can be fetched after finding a path and will change with
3240     * each call to a pathfinding method.
3241     *
3242     * @param target the target cell
3243     * @return an ArrayList of Coord that make up the best path. Copy of path.
3244     */
3245    public ArrayList<Coord> findPathPreScanned(Coord target) {
3246        return findPathPreScanned(null, target);
3247    }
3248    /**
3249     * When you can control how often the (relatively time-intensive) scan() method is called, but may need simple paths
3250     * very frequently (such as for a path that follows the mouse), you can use this method to reduce the amount of work
3251     * needed to find paths. Needs scan() or partialScan() to already be called and at least one goal to already be set,
3252     * and does not restrict the length of the path or behave as if the pathfinder has allies or enemies.
3253     * <br>
3254     * This overload takes a buffer parameter, an ArrayList of Coord, that the results will be appended to. If the
3255     * buffer is null, a new ArrayList will be made and appended to. This caches its result in a member field, path,
3256     * which can be fetched after finding a path and will change with each call to a pathfinding method. Any existing
3257     * contents of buffer will not affect the path field of this DijkstraMap.
3258     *
3259     * @param buffer an existing ArrayList of Coord that will have the result appended to it (in-place); if null, this will make a new ArrayList
3260     * @param target the target cell
3261     * @return an ArrayList of Coord that make up the best path, appended to buffer (if non-null)
3262     */
3263    public ArrayList<Coord> findPathPreScanned(ArrayList<Coord> buffer, Coord target) {
3264        path.clear();
3265        if (!initialized || goals == null || goals.isEmpty())
3266        {
3267            if(buffer == null)
3268                return new ArrayList<>();
3269            else
3270            {
3271                return buffer;
3272            }
3273        }
3274        Coord currentPos = target;
3275        if(gradientMap[currentPos.x][currentPos.y] <= FLOOR)
3276            path.add(currentPos);
3277        else
3278        {
3279            if(buffer == null)
3280                return new ArrayList<>();
3281            else
3282            {
3283                return buffer;
3284            }
3285
3286        }
3287        while (true) {
3288            if (frustration > 2000) {
3289                path.clear();
3290                break;
3291            }
3292            double best = gradientMap[currentPos.x][currentPos.y];
3293            appendDirToShuffle(rng);
3294            int choice = 0;
3295
3296            for (int d = 0; d <= measurement.directionCount(); d++) {
3297                Coord pt = Coord.get(currentPos.x + dirs[d].deltaX, currentPos.y + dirs[d].deltaY);
3298                if(!pt.isWithin(width, height))
3299                    continue;
3300                if (gradientMap[pt.x][pt.y] < best) {
3301                    if (dirs[choice] == Direction.NONE || !path.contains(pt)) {
3302                        best = gradientMap[pt.x][pt.y];
3303                        choice = d;
3304                    }
3305                }
3306            }
3307
3308            if (best >= gradientMap[currentPos.x][currentPos.y] || physicalMap[currentPos.x + dirs[choice].deltaX][currentPos.y + dirs[choice].deltaY] > FLOOR) {
3309                cutShort = true;
3310                frustration = 0;
3311                if(buffer == null)
3312                    return new ArrayList<>(path);
3313                else
3314                {
3315                    buffer.addAll(path);
3316                    return buffer;
3317                }
3318            }
3319            currentPos = currentPos.translate(dirs[choice].deltaX, dirs[choice].deltaY);
3320            path.add(0, currentPos);
3321            frustration++;
3322
3323            if (gradientMap[currentPos.x][currentPos.y] == 0)
3324                break;
3325        }
3326        cutShort = false;
3327        frustration = 0;
3328        if(buffer == null)
3329            return new ArrayList<>(path);
3330        else
3331        {
3332            buffer.addAll(path);
3333            return buffer;
3334        }
3335
3336    }
3337
3338    /**
3339     * A simple limited flood-fill that returns a OrderedMap of Coord keys to the Double values in the DijkstraMap, only
3340     * calculating out to a number of steps determined by limit. This can be useful if you need many flood-fills and
3341     * don't need a large area for each, or if you want to have an effect spread to a certain number of cells away.
3342     *
3343     * @param radius the number of steps to take outward from each starting position.
3344     * @param starts a vararg group of Points to step outward from; this often will only need to be one Coord.
3345     * @return A OrderedMap of Coord keys to Double values; the starts are included in this with the value 0.0.
3346     */
3347    public OrderedMap<Coord, Double> floodFill(int radius, Coord... starts) {
3348        if (!initialized) return null;
3349        OrderedMap<Coord, Double> fill = new OrderedMap<>();
3350
3351        resetMap();
3352        for (Coord goal : starts) {
3353            setGoal(goal.x, goal.y);
3354        }
3355        if (goals.isEmpty())
3356            return fill;
3357
3358        partialScan(radius, null);
3359        double temp;
3360        for (int x = 1; x < width - 1; x++) {
3361            for (int y = 1; y < height - 1; y++) {
3362                temp = gradientMap[x][y];
3363                if (temp < FLOOR) {
3364                    fill.put(Coord.get(x, y), temp);
3365                }
3366            }
3367        }
3368        goals.clear();
3369        return fill;
3370    }
3371
3372    public int getMappedCount() {
3373        return mappedCount;
3374    }
3375
3376    /**
3377     * If you want obstacles present in orthogonal cells to prevent pathfinding along the diagonal between them, this
3378     * can be used to make thin diagonal walls non-viable to move through, or even to prevent diagonal movement if any
3379     * one obstacle is orthogonally adjacent to both the start and target cell of a diagonal move. If you haven't set
3380     * this yet, then the default is 2.
3381     * <br>
3382     * If this is 0, as a special case no orthogonal obstacles will block diagonal moves.
3383     * <br>
3384     * If this is 1, having one orthogonal obstacle adjacent to both the current cell and the cell the pathfinder is
3385     * trying to diagonally enter will block diagonal moves. This generally blocks movement around corners, the "hard
3386     * corner" rule used in some games.
3387     * <br>
3388     * If this is 2 (the default), having two orthogonal obstacles adjacent to both the current cell and the cell the
3389     * pathfinder is trying to diagonally enter will block diagonal moves. As an example, if there is a wall to the
3390     * north and a wall to the east, then the pathfinder won't be able to move northeast even if there is a floor there.
3391     * @return the current level of blocking required to stop a diagonal move
3392     */
3393    public int getBlockingRequirement() {
3394        return blockingRequirement;
3395    }
3396
3397    /**
3398     * If you want obstacles present in orthogonal cells to prevent pathfinding along the diagonal between them, this
3399     * can be used to make thin diagonal walls non-viable to move through, or even to prevent diagonal movement if any
3400     * one obstacle is orthogonally adjacent to both the start and target cell of a diagonal move. If you haven't set
3401     * this yet, then the default is 2.
3402     * <br>
3403     * If this is 0, as a special case no orthogonal obstacles will block diagonal moves.
3404     * <br>
3405     * If this is 1, having one orthogonal obstacle adjacent to both the current cell and the cell the pathfinder is
3406     * trying to diagonally enter will block diagonal moves. This generally blocks movement around corners, the "hard
3407     * corner" rule used in some games.
3408     * <br>
3409     * If this is 2 (the default), having two orthogonal obstacles adjacent to both the current cell and the cell the
3410     * pathfinder is trying to diagonally enter will block diagonal moves. As an example, if there is a wall to the
3411     * north and a wall to the east, then the pathfinder won't be able to move northeast even if there is a floor there.
3412     * @param blockingRequirement the desired level of blocking required to stop a diagonal move
3413     */
3414    public void setBlockingRequirement(int blockingRequirement) {
3415        this.blockingRequirement = blockingRequirement > 2 ? 2 : Math.max(blockingRequirement, 0);
3416    }
3417
3418    private void appendDirToShuffle(IRNG rng) {
3419        final Direction[] src = measurement == Measurement.MANHATTAN
3420                ? Direction.CARDINALS : Direction.OUTWARDS;
3421        final int n = measurement.directionCount();
3422        System.arraycopy(src, 0, dirs, 0, n);
3423        for (int i = n - 1; i > 0; i--) {
3424            final int r = rng.nextInt(i+1);
3425            Direction t = dirs[r];
3426            dirs[r] = dirs[i];
3427            dirs[i] = t;
3428        }
3429        dirs[n] = Direction.NONE;
3430    }
3431}