001package squidpony.squidgrid;
002
003import squidpony.squidmath.Coord;
004import squidpony.squidmath.IntDoubleOrderedMap;
005import squidpony.squidmath.IntVLA;
006
007import java.io.Serializable;
008
009/**
010 * Some classes need detailed information about what cells are considered adjacent to other cells, and may
011 * need to construct a customized mapping of cells to their neighbors. Implementations of this abstract
012 * class provide information about all sorts of things, including the distance metric (from DijkstraMap),
013 * but also the maximum number of states that can be moved to in one step (including rotations at the same
014 * point in space, in some cases), and whether the type of map uses a "two-step" rule that needs two
015 * sequential moves in the same direction to be viable and unobstructed to allow movement (which is
016 * important in thin-wall maps).
017 * <br>
018 * When CustomDijkstraMap and similar classes need to store more information about a point than just its
019 * (x,y) position, they also use implementations of this class to cram more information in a single int.
020 * This abstract class provides methods to obtain four different numbers from a single int, though not all
021 * implementations may provide all four as viable options. It also provides a utility to get a Coord from an
022 * int. X and Y are exactly what they always mean in 2D Coords, R is typically used for rotation, and N is
023 * typically used for anything else when it is present. The convention is to use N for the Z-axis when
024 * elevation/depth should be tracked, or for any more specialized extensions to the information carried at
025 * a point. The composite() method produces a compressed int from X, Y, R, and N values, and the validate()
026 * method allows code to quickly check if an int is valid data this class can use. Other information is
027 * tracked by fields, such as height, width, rotations, and depths, where the maximum number of possible
028 * states is given by height * width * rotations * depths, and the minimum for any of these int fields is 1.
029 * <br>
030 * Lastly, the neighborMaps() method produces very important information about what neighbors each cell has,
031 * and by modifying the returned int[][], you can produce "portal" effects, wraparound, and other useful
032 * concepts. The value it returns consists of an array (with length == maxAdjacent) of arrays (each with the
033 * same size, length == width * height * rotations * depth). The values in the inner arrays can be any int
034 * between 0 and (width * height * rotations * depth), which refers to the index in any of the inner arrays of
035 * a neighboring cell, or can be -1 if there is no neighbor possible here (typically at edges or corners of the
036 * map, some of the neighbors are not valid and so use -1). In normal usage, a for loop is used from 0 to
037 * maxAdjacent, and in each iteration the same index is looked up (the current cell, encoded as by composite()
038 * or obtained as an already-composited neighbor earlier), and this normally gets a different neighbor every
039 * time. In methods that do a full-map search or act in a way that can possibly loop back over an existing cell
040 * in the presence of wrapping (toroidal or "modulus" maps) or portals, you may want to consider tracking a
041 * count of how many cells have been processed and terminate any processing of further cells if the count
042 * significantly exceeds the number of cells on the map (terminating when 4 times the cell count is reached may
043 * be the most extreme case for very-portal-heavy maps).
044 * Created by Tommy Ettinger on 8/12/2016.
045 */
046public abstract class Adjacency implements Serializable {
047    private static final long serialVersionUID = 0L;
048    /**
049     * The array of all possible directions this allows, regardless of cost.
050     */
051    public Direction[] directions;
052    /**
053     * The maximum number of states that can be considered adjacent; when rotations are present and have a
054     * cost this is almost always 3 (move forward, turn left, turn right), and in most other cases this is
055     * 4 (when using Manhattan distance) or 8 (for other distance metrics).
056     */
057    public int maxAdjacent;
058    /**
059     * Only needed for thin-wall maps; this requires two steps in the same direction to both be valid moves
060     * for that direction to be considered, and always moves the pathfinder two steps, typically to cells
061     * with even numbers for both x and y (where odd-number-position cells are used for edges or corners
062     * between cells, and can still be obstacles or possible to pass through, but not stay on).
063     */
064    public boolean twoStepRule;
065    /**
066     * If you want obstacles present in orthogonal cells to prevent pathfinding along the diagonal between them, this
067     * can be used to make single-cell diagonal walls non-viable to move through, or even to prevent diagonal movement if any
068     * one obstacle is orthogonally adjacent to both the start and target cell of a diagonal move.
069     * <br>
070     * If this is 0, as a special case no orthogonal obstacles will block diagonal moves.
071     * <br>
072     * If this is 1, having one orthogonal obstacle adjacent to both the current cell and the cell the pathfinder is
073     * trying to diagonally enter will block diagonal moves. This generally blocks movement around corners, the "hard
074     * corner" rule used in some games.
075     * <br>
076     * If this is 2, having two orthogonal obstacles adjacent to both the current cell and the cell the pathfinder is
077     * trying to diagonally enter will block diagonal moves. As an example, if there is a wall to the north and a wall
078     * to the east, then the pathfinder won't be able to move northeast even if there is a floor there.
079     * <br>
080     * A similar effect can be achieved with a little more control by using thin walls, where the presence of
081     * a "thin corner" can block diagonal movement through that corner, or the absence of a blocking wall in
082     * a corner space allows movement through it.
083     */
084    public int blockingRule;
085    /**
086     * This affects how distance is measured on diagonal directions vs. orthogonal directions. MANHATTAN should form a
087     * diamond shape on a featureless map, while CHEBYSHEV and EUCLIDEAN will form a square. EUCLIDEAN does not affect
088     * the length of paths, though it will change the DijkstraMap's gradientMap to have many non-integer values, and
089     * that in turn will make paths this finds much more realistic and smooth (favoring orthogonal directions unless a
090     * diagonal one is a better option).
091     */
092    public Measurement measurement;
093    /**
094     * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this.
095     */
096    public int width,
097    /**
098     * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this.
099     */
100    height,
101    /**
102     * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this.
103     */
104    rotations,
105    /**
106     * Can be changed if the map changes; you should get the neighbors from neighborMaps() again after changing this.
107     */
108    depths;
109
110    protected boolean standardCost = true;
111
112    public boolean hasStandardCost()
113    {
114        return standardCost;
115    }
116    /**
117     * Used in place of a double[][] of costs in CustomDijkstraMap; allows you to set the costs to enter tiles (via
118     * {@link #addCostRule(char, double)} or {@link #addCostRule(char, double, boolean)} if the map has rotations).
119     * A cost of 1.0 is normal for most implementations; higher costs make a movement harder to perform and take more
120     * time if the game uses that mechanic, while lower costs (which should always be greater than 0.0) make a move
121     * easier to perform. Most games can do perfectly well with just 1.0 and 2.0, if they use this at all, plus possibly
122     * a very high value for impossible moves (say, 9999.0 for something like a submarine trying to enter suburbia).
123     * <br>
124     * You should not alter costRules in most cases except through the Adjacency's addCostRule method; most Adjacency
125     * implementations will set a flag if any cost is set through addCostRule that is different from the default, and
126     * this flag determines early-stop behavior in pathfinding (it can be checked with {@link #hasStandardCost()}, but
127     * cannot be set directly).
128     * <br>
129     * Adjacency implementations are expected to set a reasonable default value for when missing keys are queried, using
130     * {@link IntDoubleOrderedMap#defaultReturnValue(double)}; there may be a reason for user code to call this as well.
131     */
132    public IntDoubleOrderedMap costRules = new IntDoubleOrderedMap(32);
133
134    public abstract int extractX(int data);
135
136    public abstract int extractY(int data);
137
138    public abstract int extractR(int data);
139
140    public abstract int extractN(int data);
141
142    /**
143     * Encodes up to four components used by this Adjacency, putting them into one int.
144     * Returns -1 if the encoded position is out of bounds or otherwise invalid, otherwise any int is possible.
145     * You can get the individual values with {@link #extractX(int)}, {@link #extractY(int)}, {@link #extractR(int)},
146     * and {@link #extractN(int)}, though not all implementations use R and N.
147     * @param x the x component to encode
148     * @param y the y component to encode
149     * @param r the rotation component to encode; not all implementations use rotation and the max value varies
150     * @param n the bonus component to encode; this can be used for height or other extra data in some implementations
151     * @return the encoded position as an int; -1 if invalid, non-negative for valid positions
152     */
153    public abstract int composite(int x, int y, int r, int n);
154
155    public abstract boolean validate(int data);
156
157    public Coord extractCoord(int data) {
158        return Coord.get(extractX(data), extractY(data));
159    }
160
161    public int move(int start, int x, int y, int r, int n)
162    {
163        return composite(extractX(start) + x, extractY(start) + y, extractR(start) + r, extractN(start) + n);
164    }
165    public int move(int start, int x, int y)
166    {
167        return move(start, x, y, 0, 0);
168    }
169
170    public abstract int[][][] neighborMaps();
171
172    public abstract void portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay);
173
174    public abstract boolean isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall);
175
176    public IntDoubleOrderedMap addCostRule(char tile, double cost)
177    {
178        return addCostRule(tile, cost, false);
179    }
180    public abstract IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation);
181
182    public IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value)
183    {
184        return putAllVariants(map, key, value, 1);
185    }
186    public abstract IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value, int size);
187
188    public void putAllVariants(IntVLA list, double[] map, int key, double value)
189    {
190        putAllVariants(list, map, key, value,1);
191    }
192    public abstract void putAllVariants(IntVLA list, double[] map, int key, double value, int size);
193
194    public void resetAllVariants(double[] map, int[] keys, double[] values)
195    {
196        resetAllVariants(map, keys, keys.length, values,1);
197    }
198
199    public void resetAllVariants(double[] map, int[] keys, double[] values, int size)
200    {
201        resetAllVariants(map, keys, keys.length, values,1);
202    }
203
204    public abstract void resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size);
205
206    public int[] invertAdjacent;
207
208    public String show(int data)
209    {
210        if(data < 0)
211            return "(-)";
212        if(rotations <= 1)
213        {
214            if(depths <= 1)
215                return "(" + extractX(data) + ',' + extractY(data) + ')';
216            return "(" + extractX(data) + ',' + extractY(data) + ',' + extractN(data) + ')';
217        }
218        if(depths <= 1)
219            return "(" + extractX(data) + ',' + extractY(data) + ',' + extractR(data) + ')';
220        return "(" + extractX(data) + ',' + extractY(data) + ',' + extractR(data) + ',' + extractN(data) + ')';
221    }
222    public String showMap(int[] map, int r)
223    {
224        r %= rotations;
225        StringBuilder sb = new StringBuilder(width * height * 8);
226        for (int y = 0; y < height; y++) {
227            for (int x = 0; x < width; x++) {
228                sb.append(show(map[(y * width + x) * rotations + r])).append(' ');
229            }
230            sb.append('\n');
231        }
232        return sb.toString();
233    }
234
235    public static class BasicAdjacency extends Adjacency implements Serializable {
236        private static final long serialVersionUID = 0L;
237
238        private BasicAdjacency() {
239            this(20, 20, Measurement.MANHATTAN);
240        }
241
242        public BasicAdjacency(int width, int height, Measurement metric) {
243            this.width = width;
244            this.height = height;
245            rotations = 1;
246            depths = 1;
247            measurement = metric;
248            if(metric == Measurement.MANHATTAN)
249            {
250                directions = Direction.CARDINALS;
251                maxAdjacent = 4;
252                invertAdjacent = new int[]{1, 0, 3, 2};
253            }
254            else
255            {
256                directions = Direction.OUTWARDS;
257                maxAdjacent = 8;
258                invertAdjacent = new int[]{1, 0, 3, 2, 7, 6, 5, 4};
259            }
260            twoStepRule = false;
261            blockingRule = 2;
262            costRules.defaultReturnValue(1.0);
263        }
264
265        @Override
266        public int extractX(int data) {
267            return data % width;
268        }
269
270        @Override
271        public int extractY(int data) {
272            return data / width;
273        }
274
275        @Override
276        public int extractR(int data) {
277            return 0;
278        }
279
280        @Override
281        public int extractN(int data) {
282            return 0;
283        }
284
285        @Override
286        public int composite(int x, int y, int r, int n) {
287            if(x < 0 || y < 0 || x >= width || y >= height)
288                return -1;
289            return y * width + x;
290        }
291
292        @Override
293        public int move(int start, int x, int y) {
294            int xx = (start % width) + x, yy = (start / width) + y;
295            if(xx < 0 || yy < 0 || xx >= width || yy >= height)
296                return -1;
297            return yy * width + xx;
298        }
299
300        @Override
301        public boolean validate(int data) {
302            return data >= 0 && extractY(data) < height;
303        }
304
305        @Override
306        public int[][][] neighborMaps() {
307            int[][][] maps = new int[2][maxAdjacent][width * height * rotations * depths];
308            for (int m = 0; m < maxAdjacent; m++) {
309                Direction dir = directions[m];
310                for (int x = 0; x < width; x++) {
311                    for (int y = 0; y < height; y++) {
312                        maps[0][m][y * width + x] = composite(x - dir.deltaX, y - dir.deltaY, 0, 0);
313                        maps[1][m][y * width + x] = composite(x + dir.deltaX, y + dir.deltaY, 0, 0);
314                    }
315                }
316            }
317            return maps;
318        }
319
320        @Override
321        public boolean isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall) {
322            if(direction < 4)
323                return !validate(start);
324            int[][] near = neighbors[0];
325            switch (direction)
326            {
327                case 4: //UP_LEFT
328                    return (near[0][start] < 0 || map[near[0][start]] >= wall)
329                            && (near[2][start] < 0 || map[near[2][start]] >= wall);
330                case 5: //UP_RIGHT
331                    return (near[0][start] < 0 || map[near[0][start]] >= wall)
332                            && (near[3][start] < 0 || map[near[3][start]] >= wall);
333                case 6: //DOWN_LEFT
334                    return (near[1][start] < 0 || map[near[1][start]] >= wall)
335                            && (near[2][start] < 0 || map[near[2][start]] >= wall);
336                default: //DOWN_RIGHT
337                    return (near[1][start] < 0 || map[near[1][start]] >= wall)
338                            && (near[3][start] < 0 || map[near[3][start]] >= wall);
339            }
340        }
341
342        @Override
343        public void portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay) {
344            if(neighbors == null || !validate(inputPortal) || !validate(outputPortal)
345                    || neighbors.length != maxAdjacent)
346                return;
347            for (int d = 0; d < maxAdjacent; d++) {
348                for (int i = 0; i < width * height; i++) {
349                    if(neighbors[1][d][i] == inputPortal)
350                    {
351                        neighbors[1][d][i] = outputPortal;
352                    }
353                    else if(twoWay && neighbors[1][d][i] == outputPortal)
354                    {
355                        neighbors[1][d][i] = inputPortal;
356                    }
357
358                    if(neighbors[0][d][i] == outputPortal)
359                    {
360                        neighbors[0][d][i] = inputPortal;
361                    }
362                    else if(twoWay && neighbors[0][d][i] == inputPortal)
363                    {
364                        neighbors[0][d][i] = outputPortal;
365                    }
366                }
367            }
368        }
369
370        @Override
371        public IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation) {
372            costRules.put(tile, cost);
373            if(cost != costRules.defaultReturnValue())
374                standardCost = false;
375            return costRules;
376        }
377
378        @Override
379        public IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value, int size) {
380            int baseX = key % width, baseY = key / width, comp;
381            if (key >= 0 && baseY < height) {
382                if (size < 0) {
383                    for (int x = size+1; x <= 0; x++) {
384                        for (int y = size+1; y <= 0; y++) {
385                            comp = composite(baseX + x, baseY + y, 0, 0);
386                            if(comp >= 0)
387                                map.put(comp, value);
388                        }
389                    }
390                } else {
391                    for (int x = 0; x < size; x++) {
392                        for (int y = 0; y < size; y++) {
393                            comp = composite(baseX + x, baseY + y, 0, 0);
394                            if(comp >= 0)
395                                map.put(comp, value);
396                        }
397                    }
398                }
399            }
400            return map;
401        }
402
403        @Override
404        public void putAllVariants(IntVLA list, double[] map, int key, double value, int size) {
405            int baseX = key % width, baseY = key / width, comp;
406            if (key >= 0 && baseY < height) {
407                if (size < 0) {
408                    if(list == null)
409                    {
410                        for (int x = size + 1; x <= 0; x++) {
411                            for (int y = size + 1; y <= 0; y++) {
412                                comp = composite(baseX + x, baseY + y, 0, 0);
413                                if (comp >= 0) {
414                                    map[comp] = value;
415                                }
416                            }
417                        }
418                    }
419                    else {
420                        for (int x = size + 1; x <= 0; x++) {
421                            for (int y = size + 1; y <= 0; y++) {
422                                comp = composite(baseX + x, baseY + y, 0, 0);
423                                if (comp >= 0 && !list.contains(comp)) {
424                                    list.add(comp);
425                                    map[comp] = value;
426                                }
427                            }
428                        }
429                    }
430                } else {
431                    if (list == null) {
432                        for (int x = 0; x < size; x++) {
433                            for (int y = 0; y < size; y++) {
434                                comp = composite(baseX + x, baseY + y, 0, 0);
435                                if (comp >= 0) {
436                                    map[comp] = value;
437                                }
438                            }
439                        }
440                    } else {
441                        for (int x = 0; x < size; x++) {
442                            for (int y = 0; y < size; y++) {
443                                comp = composite(baseX + x, baseY + y, 0, 0);
444                                if (comp >= 0 && !list.contains(comp)) {
445                                    list.add(comp);
446                                    map[comp] = value;
447                                }
448                            }
449                        }
450                    }
451                }
452            }
453        }
454
455        @Override
456        public void resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size) {
457            int key;
458            for (int i = 0; i < usable && i < keys.length; i++) {
459                key = keys[i];
460                int baseX = key % width, baseY = key / width, comp;
461                if (key >= 0 && baseY < height) {
462                    if (size < 0) {
463                        for (int x = size + 1; x <= 0; x++) {
464                            for (int y = size + 1; y <= 0; y++) {
465                                comp = composite(baseX + x, baseY + y, 0, 0);
466                                if (comp >= 0) {
467                                    map[comp] = values[comp];
468                                }
469                            }
470                        }
471                    } else {
472                        for (int x = 0; x < size; x++) {
473                            for (int y = 0; y < size; y++) {
474                                comp = composite(baseX + x, baseY + y, 0, 0);
475                                if (comp >= 0) {
476                                    map[comp] = values[comp];
477                                }
478                            }
479                        }
480                    }
481                }
482            }
483        }
484    }
485
486    public static class ThinWallAdjacency extends BasicAdjacency implements Serializable {
487        private static final long serialVersionUID = 0L;
488
489        private ThinWallAdjacency() {
490            this(20, 20, Measurement.MANHATTAN);
491        }
492
493        public ThinWallAdjacency(int width, int height, Measurement metric) {
494            super(width, height, metric);
495            twoStepRule = true;
496            costRules.defaultReturnValue(0.5);
497        }
498
499        @Override
500        public IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation) {
501            costRules.put(tile, cost * 0.5);
502            if(cost * 0.5 != costRules.defaultReturnValue())
503                standardCost = false;
504            return costRules;
505        }
506    }
507
508    public static class RotationAdjacency extends Adjacency implements Serializable {
509        private static final long serialVersionUID = 0L;
510
511        private RotationAdjacency() {
512            this(20, 20, Measurement.MANHATTAN);
513        }
514        private int shift;
515        public RotationAdjacency(int width, int height, Measurement metric) {
516            this.width = width;
517            this.height = height;
518            measurement = metric;
519            if(metric == Measurement.MANHATTAN)
520            {
521                rotations = 4;
522                shift = 2;
523                directions = Direction.CARDINALS_CLOCKWISE;
524                invertAdjacent = new int[]{2, 3, 0, 1};
525            }
526            else
527            {
528                rotations = 8;
529                shift = 3;
530                directions = Direction.CLOCKWISE;
531                invertAdjacent = new int[]{4, 5, 6, 7, 0, 1, 2, 3};
532            }
533            depths = 1;
534            maxAdjacent = 3;
535            twoStepRule = false;
536            blockingRule = 2;
537            costRules.defaultReturnValue(1.0);
538            //invertAdjacent = new int[]{2, 1, 0};
539        }
540
541        @Override
542        public int extractX(int data) {
543            return (data >>> shift) % width;
544        }
545
546        @Override
547        public int extractY(int data) {
548            return (data >>> shift) / width;
549        }
550
551        @Override
552        public int extractR(int data) {
553            return data & (rotations - 1);
554        }
555
556        @Override
557        public int extractN(int data) {
558            return 0;
559        }
560
561        @Override
562        public int composite(int x, int y, int r, int n) {
563            if(x < 0 || y < 0 || x >= width || y >= height || r < 0 || r >= rotations)
564                return -1;
565            return ((y * width + x) << shift) | r;
566        }
567
568        @Override
569        public boolean validate(int data) {
570            return data >= 0 && extractY(data) < height;
571        }
572
573        @Override
574        public int[][][] neighborMaps() {
575            int[][][] maps = new int[2][maxAdjacent][width * height * rotations * depths];
576            int current;
577            Direction dir;
578            for (int r = 0; r < rotations; r++) {
579                dir = directions[r];
580                for (int x = 0; x < width; x++) {
581                    for (int y = 0; y < height; y++) {
582                        current = ((y * width + x) << shift) | r;
583                        maps[0][1][current] = composite(x - dir.deltaX, y - dir.deltaY, r, 0);
584                        maps[1][1][current] = composite(x + dir.deltaX, y + dir.deltaY, r, 0);
585                        maps[0][0][current] = maps[1][0][current] = composite(x, y, r - 1 & (rotations - 1), 0);
586                        maps[0][2][current] = maps[1][2][current] = composite(x, y, r + 1 & (rotations - 1), 0);
587                        //maps[0][composite(x, y, r - 1 & (rotations - 1), 0)] = current;
588                        //maps[2][composite(x, y, r + 1 & (rotations - 1), 0)] = current;
589                    }
590                }
591            }
592            return maps;
593        }
594
595        @Override
596        public boolean isBlocked(int start, int direction, int[][][] neighbors, double[] map, double wall) {
597            if(rotations <= 4 || (direction & 1) == 0)
598                return !validate(start);
599
600            return neighbors[0][0][start] < 0 || map[neighbors[0][0][start]] >= wall
601                    || neighbors[0][2][start] < 0 || map[neighbors[0][2][start]] >= wall;
602        }
603
604        @Override
605        public void portal(int[][][] neighbors, int inputPortal, int outputPortal, boolean twoWay) {
606            if(neighbors == null || !validate(inputPortal) || !validate(outputPortal)
607                    || neighbors.length != maxAdjacent)
608                return;
609            for (int i = 0; i < width * height * rotations; i++) {
610                if (neighbors[0][1][i] == inputPortal) {
611                    neighbors[0][1][i] = outputPortal;
612                } else if (twoWay && neighbors[0][1][i] == outputPortal) {
613                    neighbors[0][1][i] = inputPortal;
614                }
615
616                if (neighbors[1][1][i] == outputPortal) {
617                    neighbors[1][1][i] = inputPortal;
618                } else if (twoWay && neighbors[1][1][i] == inputPortal) {
619                    neighbors[1][1][i] = outputPortal;
620                }
621            }
622        }
623
624        @Override
625        public IntDoubleOrderedMap addCostRule(char tile, double cost, boolean isRotation) {
626            if(isRotation)
627            {
628                costRules.put(tile | 0x10000, Math.max(0.001, cost));
629                if(Math.max(0.001, cost) != costRules.defaultReturnValue())
630                    standardCost = false;
631            }
632            else {
633                costRules.put(tile, cost);
634                if(cost != costRules.defaultReturnValue())
635                    standardCost = false;
636            }
637            return costRules;
638        }
639
640
641        @Override
642        public IntDoubleOrderedMap putAllVariants(IntDoubleOrderedMap map, int key, double value, int size) {
643            int baseX = (key >>> shift) % width, baseY = (key>>>shift) / width, comp;
644            if (key >= 0 && baseY < height) {
645                if(size == 1)
646                {
647                    for (int r = 0; r < rotations; r++) {
648                        comp = composite(baseX, baseY, r, 0);
649                        if(comp >= 0)
650                            map.put(comp, value);
651                    }
652                }
653                else if (size < 0) {
654                    for (int x = size+1; x <= 0; x++) {
655                        for (int y = size+1; y <= 0; y++) {
656                            for (int r = 0; r < rotations; r++) {
657                                comp = composite(baseX + x, baseY + y, r, 0);
658                                if(comp >= 0)
659                                    map.put(comp, value);
660                            }
661                        }
662                    }
663                } else {
664                    for (int x = 0; x < size; x++) {
665                        for (int y = 0; y < size; y++) {
666                            for (int r = 0; r < rotations; r++) {
667                                comp = composite(baseX + x, baseY + y, r, 0);
668                                if(comp >= 0)
669                                    map.put(comp, value);
670                            }
671                        }
672                    }
673                }
674            }
675            return map;
676        }
677
678        @Override
679        public void putAllVariants(IntVLA list, double[] map, int key, double value, int size) {
680            int baseX = (key >>> shift) % width, baseY = (key>>>shift) / width, comp;
681            if (key >= 0 && baseY < height) {
682                if(size == 1)
683                {
684                    if(list == null)
685                    {
686                        for (int r = 0; r < rotations; r++) {
687                            comp = composite(baseX, baseY, r, 0);
688                            if(comp >= 0) {
689                                map[comp] = value;
690                            }
691                        }
692                    }
693                    else
694                    {
695                        for (int r = 0; r < rotations; r++) {
696                            comp = composite(baseX, baseY, r, 0);
697                            if(comp >= 0 && !list.contains(comp)) {
698                                list.add(comp);
699                                map[comp] = value;
700                            }
701                        }
702                    }
703                }
704                else if (size < 0) {
705                    if(list == null)
706                    {
707                        for (int x = size+1; x <= 0; x++) {
708                            for (int y = size+1; y <= 0; y++) {
709                                for (int r = 0; r < rotations; r++) {
710                                    comp = composite(baseX + x, baseY + y, r, 0);
711                                    if(comp >= 0) {
712                                        map[comp] = value;
713                                    }
714                                }
715                            }
716                        }
717                    }
718                    else {
719                        for (int x = size+1; x <= 0; x++) {
720                            for (int y = size+1; y <= 0; y++) {
721                                for (int r = 0; r < rotations; r++) {
722                                    comp = composite(baseX + x, baseY + y, r, 0);
723                                    if(comp >= 0 && !list.contains(comp)) {
724                                        list.add(comp);
725                                        map[comp] = value;
726                                    }
727                                }
728                            }
729                        }
730                    }
731                } else {
732                    if(list == null)
733                    {
734                        for (int x = 0; x < size; x++) {
735                            for (int y = 0; y < size; y++) {
736                                for (int r = 0; r < rotations; r++) {
737                                    comp = composite(baseX + x, baseY + y, r, 0);
738                                    if(comp >= 0) {
739                                        map[comp] = value;
740                                    }
741                                }
742                            }
743                        }
744                    }
745                    else {
746                        for (int x = 0; x < size; x++) {
747                            for (int y = 0; y < size; y++) {
748                                for (int r = 0; r < rotations; r++) {
749                                    comp = composite(baseX + x, baseY + y, r, 0);
750                                    if(comp >= 0 && !list.contains(comp)) {
751                                        list.add(comp);
752                                        map[comp] = value;
753                                    }
754                                }
755                            }
756                        }
757                    }
758                }
759            }
760        }
761
762        @Override
763        public void resetAllVariants(double[] map, int[] keys, int usable, double[] values, int size) {
764            int key;
765            for (int i = 0; i < usable && i < keys.length; i++) {
766                key = keys[i];
767                int baseX = (key >>> shift) % width, baseY = (key >>> shift) / width, comp;
768                if (key >= 0 && baseY < height) {
769                    if (size == 1) {
770                        for (int r = 0; r < rotations; r++) {
771                            comp = composite(baseX, baseY, r, 0);
772                            if (comp >= 0) {
773                                map[comp] = values[comp];
774                            }
775                        }
776                    } else if (size < 0) {
777                        for (int x = size + 1; x <= 0; x++) {
778                            for (int y = size + 1; y <= 0; y++) {
779                                for (int r = 0; r < rotations; r++) {
780                                    comp = composite(baseX + x, baseY + y, r, 0);
781                                    if (comp >= 0) {
782                                        map[comp] = values[comp];
783                                    }
784                                }
785                            }
786                        }
787                    } else {
788                        for (int x = 0; x < size; x++) {
789                            for (int y = 0; y < size; y++) {
790                                for (int r = 0; r < rotations; r++) {
791                                    comp = composite(baseX + x, baseY + y, r, 0);
792                                    if (comp >= 0) {
793                                        map[comp] = values[comp];
794                                    }
795                                }
796                            }
797                        }
798                    }
799                }
800            }
801        }
802    }
803}