package squidpony.squidai; import squidpony.squidmath.Coord; import squidpony.squidmath.OrderedMap; /** * Static utilities for use in AOE and anything else that might need HashMaps of Coord keys to Double values. * Created by Tommy Ettinger on 7/13/2015. */ public class AreaUtils { /** * This takes a 2D boolean array and returns a HashMap of Coord keys to Double values, but will only use the value * 1.0, and only for positions in map that have as their boolean element true. * @param map width by height, commonly generated by FOV methods * @return a HashMap of Coord keys to Double values, but the only value used is 1.0 */ public static OrderedMap<Coord, Double> arrayToHashMap(boolean[][] map) { OrderedMap<Coord, Double> ret = new OrderedMap<>(); for(int i = 0; i < map.length; i++) { for(int j = 0; j < map[i].length; j++) { if(map[i][j]) ret.put(Coord.get(i, j), 1.0); } } return ret; } /** * This takes a 2D double array called map and returns a HashMap of Coord keys to Double values, and will have a key * for every position in map that is greater than 0.0, with values equal to those in map. * @param map width by height, commonly generated by FOV methods * @return a HashMap of Coord keys to Double values, with values all greater than 0.0 */ public static OrderedMap<Coord, Double> arrayToHashMap(double[][] map) { OrderedMap<Coord, Double> ret = new OrderedMap<>(); for(int i = 0; i < map.length; i++) { for(int j = 0; j < map[i].length; j++) { if(map[i][j] > 0.0) ret.put(Coord.get(i, j), map[i][j]); } } return ret; } /** * This takes a 2D double array and returns a HashMap of Coord keys to Double values, but will only use the value * 1.0, and only does this if the passed double[][] has a value at that position that is greater than cutoff. * For example, a cutoff of 0.3 will make all elements in the 2D array that are 0.3 or less be ignored and not put * into the HashMap, but all elements that are greater than 0.3 will be placed in as 1.0. * @param map width by height, commonly generated by FOV methods * @param cutoff any elements greater than this will be 1.0 in the return, anything else will be ignored * @return a HashMap of Coord keys to Double values, but the only value used is 1.0 */ public static OrderedMap<Coord, Double> arrayToHashMap(double[][] map, double cutoff) { OrderedMap<Coord, Double> ret = new OrderedMap<>(); for(int i = 0; i < map.length; i++) { for(int j = 0; j < map[i].length; j++) { if(map[i][j] > cutoff) ret.put(Coord.get(i, j), 1.0); } } return ret; } /** * This takes a DijkstraMap that has already completed a scan() and returns a HashMap of Coord keys to Double * values, and will have a key for every position that was reached in the DijkstraMap, with 1.0 as the only value. * @param map a double[][] returned by a DijkstraMap running its scan() * @return a HashMap of Coord keys to Double values, with values of 1.0 only */ public static OrderedMap<Coord, Double> dijkstraToHashMap(double[][] map) { OrderedMap<Coord, Double> ret = new OrderedMap<>(); for(int i = 0; i < map.length; i++) { for(int j = 0; j < map[i].length; j++) { if(map[i][j] < DijkstraMap.WALL) ret.put(Coord.get(i, j), 1.0); } } return ret; } /** * Checks that the given end Coord can be targeted from the given origin Coord given the directional targeting * rules specified by limit. If any of the arguments are null, returns true (it assumes that any limits are not * valid and don't restrict anything). The following AimLimit enum values for limit have the following meanings: * * <ul> * <li>AimLimit.FREE makes no restrictions; it is equivalent here to passing null for limit.</li> * <li>AimLimit.EIGHT_WAY will only consider Points to be valid targets * if they are along a straight line with an angle that is a multiple of 45 degrees, relative to the positive x * axis. Essentially, this limits the points to those a queen could move to in chess.</li> * <li>AimLimit.ORTHOGONAL will cause the AOE to only consider Points to be valid targets if * they are along a straight line with an angle that is a multiple of 90 degrees, relative to the positive x * axis. Essentially, this limits the points to those a rook could move to in chess.</li> * <li>AimLimit.DIAGONAL will cause the AOE to only consider Points to be valid targets if they are along a * straight line with an angle that is 45 degrees greater than a multiple of 90 degrees, relative to the * positive x axis. Essentially, this limits the points to those a bishop could move to in chess.</li> * </ul> * * @param limit an AimLimit enum that restricts valid points unless it is AimLimit.FREE or null * @param origin where the user is * @param end where the point we want to verify is * @return true if the point is a valid target or if the limits are invalid (non-restricting), false otherwise */ public static boolean verifyLimit(AimLimit limit, Coord origin, Coord end) { if (limit != null && origin != null && end != null) { switch (limit) { case EIGHT_WAY: if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y) || end.x == origin.x || end.y == origin.y) { return true; } break; case DIAGONAL: if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y)) { return true; } break; case ORTHOGONAL: if(end.x == origin.x || end.y == origin.y) { return true; } break; case FREE: return true; } return false; } return true; } /** * Checks that the given end Coord can be targeted from the given origin Coord given the complete targeting rules * specified by reach. If any of the arguments are null, returns true (it assumes that any limits are not * valid and don't restrict anything). If reach.limit is null, it also returns true. Otherwise, it uses the metric, * minDistance, and maxDistance from reach to calculate if end is target-able from origin assuming an unobstructed * playing field. * * @param reach a Reach object that, if non-null, gives limits for how targeting can proceed. * @param origin where the user is * @param end where the point we want to verify is * @return true if the point is a valid target or if the limits are invalid (non-restricting), false otherwise */ public static boolean verifyReach(Reach reach, Coord origin, Coord end) { if (reach != null && reach.limit != null && origin != null && end != null) { switch (reach.limit) { case EIGHT_WAY: if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y) || end.x == origin.x || end.y == origin.y) { return reach.metric.inRange(origin.x, origin.y, end.x, end.y, reach.minDistance, reach.maxDistance); } break; case DIAGONAL: if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y)) { return reach.metric.inRange(origin.x, origin.y, end.x, end.y, reach.minDistance, reach.maxDistance); } break; case ORTHOGONAL: if(end.x == origin.x || end.y == origin.y) { return reach.metric.inRange(origin.x, origin.y, end.x, end.y, reach.minDistance, reach.maxDistance); } break; case FREE: return reach.metric.inRange(origin.x, origin.y, end.x, end.y, reach.minDistance, reach.maxDistance); } return false; } return true; } }