001package squidpony.squidai; 002 003import squidpony.squidgrid.LOS; 004import squidpony.squidgrid.Measurement; 005import squidpony.squidgrid.Radius; 006import squidpony.squidgrid.mapping.DungeonUtility; 007import squidpony.squidmath.Coord; 008import squidpony.squidmath.OrderedMap; 009import squidpony.squidmath.OrderedSet; 010 011import java.io.Serializable; 012import java.util.ArrayList; 013import java.util.Arrays; 014import java.util.Collection; 015import java.util.Queue; 016 017import static java.lang.Math.round; 018import static squidpony.squidmath.NumberTools.cos; 019import static squidpony.squidmath.NumberTools.sin; 020 021/** 022 * Beam Area of Effect that affects an slightly expanded (Elias) line from a given origin Coord out to a given length, 023 * plus an optional radius of cells around the path of the line, while respecting obstacles in its path and possibly 024 * stopping if obstructed. There are several ways to specify the BeamAOE's direction and length, including specifying 025 * an endpoint or specifying an angle in degrees and a distance to the end of the line (without the radius included). 026 * You can specify the RadiusType to Radius.DIAMOND for Manhattan distance, RADIUS.SQUARE for Chebyshev, or 027 * RADIUS.CIRCLE for Euclidean. 028 * 029 * You may want the LineAOE class instead of this. LineAOE travels point-to-point and does not restrict length, while 030 * BeamAOE travels a specific length (and may have a radius, like LineAOE) but then stops only after the travel down the 031 * length and radius has reached its end. This difference is relevant if a game has effects that have a definite 032 * area measured in a rectangle or elongated pillbox shape, such as a "20-foot-wide bolt of lightning, 100 feet long." 033 * BeamAOE is more suitable for that effect, while LineAOE may be more suitable for things like focused lasers that 034 * pass through small (likely fleshy) obstacles but stop after hitting the aimed-at target. 035 * 036 * BeamAOE will strike a small area behind the user and in the opposite direction of the target if the radius is 037 * greater than 0. This behavior may be altered in a future version. 038 * 039 * This will produce doubles for its findArea() method which are equal to 1.0. 040 * 041 * This class uses squidpony.squidmath.Elias and squidpony.squidai.DijkstraMap to create its area of effect. 042 * Created by Tommy Ettinger on 7/14/2015. 043 */ 044public class BeamAOE implements AOE, Serializable { 045 private static final long serialVersionUID = 2L; 046 private Coord origin, end; 047 private int radius; 048 private int length; 049 private char[][] dungeon; 050 private DijkstraMap dijkstra; 051 private Radius rt; 052 private LOS los; 053 054 private Reach reach = new Reach(1, 1, Radius.SQUARE, AimLimit.FREE); 055 056 public BeamAOE(Coord origin, Coord end) 057 { 058 dijkstra = new DijkstraMap(); 059 dijkstra.measurement = Measurement.EUCLIDEAN; 060 rt = Radius.SQUARE; 061 this.origin = origin; 062 this.end = end; 063 length =(int) round(rt.radius(origin.x, origin.y, end.x, end.y)); 064 reach.maxDistance = length; 065 radius = 0; 066 los = new LOS(LOS.THICK); 067 } 068 public BeamAOE(Coord origin, Coord end, int radius) 069 { 070 dijkstra = new DijkstraMap(); 071 dijkstra.measurement = Measurement.EUCLIDEAN; 072 rt = Radius.SQUARE; 073 this.origin = origin; 074 this.end = end; 075 this.radius = radius; 076 length =(int) round(rt.radius(origin.x, origin.y, end.x, end.y)); 077 reach.maxDistance = length; 078 los = new LOS(LOS.THICK); 079 } 080 public BeamAOE(Coord origin, Coord end, int radius, Radius radiusType) 081 { 082 dijkstra = new DijkstraMap(); 083 rt = radiusType; 084 switch (radiusType) 085 { 086 case OCTAHEDRON: 087 case DIAMOND: 088 dijkstra.measurement = Measurement.MANHATTAN; 089 break; 090 case CUBE: 091 case SQUARE: 092 dijkstra.measurement = Measurement.CHEBYSHEV; 093 break; 094 default: 095 dijkstra.measurement = Measurement.EUCLIDEAN; 096 break; 097 } 098 this.origin = origin; 099 this.end = end; 100 this.radius = radius; 101 length =(int) round(rt.radius(origin.x, origin.y, end.x, end.y)); 102 reach.maxDistance = length; 103 los = new LOS(LOS.THICK); 104 } 105 106 public BeamAOE(Coord origin, double angle, int length) 107 { 108 dijkstra = new DijkstraMap(); 109 dijkstra.measurement = Measurement.EUCLIDEAN; 110 rt = Radius.SQUARE; 111 this.origin = origin; 112 double theta = Math.toRadians(angle); 113 end = Coord.get((int) round(cos(theta) * length) + origin.x, 114 (int) round(sin(theta) * length) + origin.y); 115 this.length = length; 116 reach.maxDistance = this.length; 117 radius = 0; 118 los = new LOS(LOS.THICK); 119 } 120 public BeamAOE(Coord origin, double angle, int length, int radius) 121 { 122 dijkstra = new DijkstraMap(); 123 dijkstra.measurement = Measurement.EUCLIDEAN; 124 rt = Radius.SQUARE; 125 this.origin = origin; 126 double theta = Math.toRadians(angle); 127 end = Coord.get((int) round(cos(theta) * length) + origin.x, 128 (int) round(sin(theta) * length) + origin.y); 129 this.radius = radius; 130 this.length = length; 131 reach.maxDistance = this.length; 132 los = new LOS(LOS.THICK); 133 } 134 public BeamAOE(Coord origin, double angle, int length, int radius, Radius radiusType) 135 { 136 dijkstra = new DijkstraMap(); 137 rt = radiusType; 138 switch (radiusType) 139 { 140 case OCTAHEDRON: 141 case DIAMOND: 142 dijkstra.measurement = Measurement.MANHATTAN; 143 break; 144 case CUBE: 145 case SQUARE: 146 dijkstra.measurement = Measurement.CHEBYSHEV; 147 break; 148 default: 149 dijkstra.measurement = Measurement.EUCLIDEAN; 150 break; 151 } 152 this.origin = origin; 153 double theta = Math.toRadians(angle); 154 end = Coord.get((int) round(cos(theta) * length) + origin.x, 155 (int) round(sin(theta) * length) + origin.y); 156 this.radius = radius; 157 this.length = length; 158 reach.maxDistance = this.length; 159 los = new LOS(LOS.THICK); 160 } 161 private double[][] initDijkstra() 162 { 163 los.isReachable(dungeon, origin.x, origin.y, end.x, end.y, rt); 164 Queue<Coord> lit = los.getLastPath(); 165 166 dijkstra.initialize(dungeon); 167 for(Coord p : lit) 168 { 169 dijkstra.setGoal(p); 170 } 171 if(radius == 0) 172 return dijkstra.gradientMap; 173 return dijkstra.partialScan(radius, null); 174 } 175 176 @Override 177 public Coord getOrigin() { 178 return origin; 179 } 180 181 @Override 182 public void setOrigin(Coord origin) { 183 this.origin = origin; 184 dijkstra.resetMap(); 185 dijkstra.clearGoals(); 186 } 187 188 @Override 189 public AimLimit getLimitType() { 190 return reach.limit; 191 } 192 193 @Override 194 public int getMinRange() { 195 return reach.minDistance; 196 } 197 198 @Override 199 public int getMaxRange() { 200 return reach.maxDistance; 201 } 202 203 @Override 204 public Radius getMetric() { 205 return reach.metric; 206 } 207 208 /** 209 * Gets the same values returned by getLimitType(), getMinRange(), getMaxRange(), and getMetric() bundled into one 210 * Reach object. 211 * 212 * @return a non-null Reach object. 213 */ 214 @Override 215 public Reach getReach() { 216 return reach; 217 } 218 219 @Override 220 public void setLimitType(AimLimit limitType) { 221 reach.limit = limitType; 222 223 } 224 225 @Override 226 public void setMinRange(int minRange) { 227 reach.minDistance = minRange; 228 } 229 230 @Override 231 public void setMaxRange(int maxRange) { 232 reach.maxDistance = maxRange; 233 length = maxRange; 234 235 } 236 237 @Override 238 public void setMetric(Radius metric) { 239 reach.metric = metric; 240 } 241 242 /** 243 * Sets the same values as setLimitType(), setMinRange(), setMaxRange(), and setMetric() using one Reach object. 244 * 245 * @param reach a non-null Reach object. 246 */ 247 @Override 248 public void setReach(Reach reach) { 249 if(reach != null) 250 this.reach = reach; 251 } 252 253 public Coord getEnd() { 254 return end; 255 } 256 257 public void setEnd(Coord end) { 258 if (AreaUtils.verifyReach(reach, origin, end)) 259 { 260 this.end = rt.extend(origin, end, length, false, dungeon.length, dungeon[0].length); 261 } 262 263 } 264 265 public int getRadius() { 266 return radius; 267 } 268 269 public void setRadius(int radius) { 270 this.radius = radius; 271 } 272 273 public Radius getRadiusType() 274 { 275 return rt; 276 } 277 public void setRadiusType(Radius radiusType) 278 { 279 rt = radiusType; 280 switch (radiusType) 281 { 282 case OCTAHEDRON: 283 case DIAMOND: 284 dijkstra.measurement = Measurement.MANHATTAN; 285 break; 286 case CUBE: 287 case SQUARE: 288 dijkstra.measurement = Measurement.CHEBYSHEV; 289 break; 290 default: 291 dijkstra.measurement = Measurement.EUCLIDEAN; 292 break; 293 } 294 } 295 296 @Override 297 public void shift(Coord aim) { 298 setEnd(aim); 299 } 300 301 @Override 302 public boolean mayContainTarget(Collection<Coord> targets) { 303 for (Coord p : targets) 304 { 305 if(rt.radius(origin.x, origin.y, p.x, p.y) + rt.radius(end.x, end.y, p.x, p.y) - 306 rt.radius(origin.x, origin.y, end.x, end.y) <= 3.0 + radius) 307 return true; 308 } 309 return false; 310 } 311 312 @Override 313 public OrderedMap<Coord, ArrayList<Coord>> idealLocations(Collection<Coord> targets, Collection<Coord> requiredExclusions) { 314 if(targets == null) 315 return new OrderedMap<>(); 316 if(requiredExclusions == null) requiredExclusions = new OrderedSet<>(); 317 318 //requiredExclusions.remove(origin); 319 320 int totalTargets = targets.size(); 321 OrderedMap<Coord, ArrayList<Coord>> bestPoints = new OrderedMap<>(totalTargets * 8); 322 323 if(totalTargets == 0) 324 return bestPoints; 325 326 Coord[] ts = targets.toArray(new Coord[0]); 327 Coord[] exs = requiredExclusions.toArray(new Coord[0]); 328 Coord t; 329 330 double[][][] compositeMap = new double[ts.length][dungeon.length][dungeon[0].length]; 331 332 char[][] dungeonCopy = new char[dungeon.length][dungeon[0].length]; 333 for (int i = 0; i < dungeon.length; i++) { 334 System.arraycopy(dungeon[i], 0, dungeonCopy[i], 0, dungeon[i].length); 335 } 336 DijkstraMap dt = new DijkstraMap(dungeon, dijkstra.measurement); 337 double[][] resMap = DungeonUtility.generateResistances(dungeon); 338 Coord tempPt; 339 for (int i = 0; i < exs.length; ++i) { 340 t = rt.extend(origin, exs[i], length, false, dungeon.length, dungeon[0].length); 341 dt.resetMap(); 342 dt.clearGoals(); 343 los.isReachable(resMap, origin.x, origin.y, t.x, t.y, rt); 344 Queue<Coord> lit = los.getLastPath(); 345 346 347 for(Coord p : lit) 348 { 349 dt.setGoal(p); 350 } 351 if(radius > 0) 352 dt.partialScan(radius, null); 353 354 for (int x = 0; x < dungeon.length; x++) { 355 for (int y = 0; y < dungeon[x].length; y++) { 356 tempPt = Coord.get(x, y); 357 dungeonCopy[x][y] = (dt.gradientMap[x][y] < DijkstraMap.FLOOR || !AreaUtils.verifyReach(reach, origin, tempPt)) ? '!' : dungeonCopy[x][y]; 358 } 359 } 360 } 361 362 //t = rt.extend(origin, ts[0], length, false, dungeon.length, dungeon[0].length); 363 364 for (int i = 0; i < ts.length; ++i) { 365 DijkstraMap dm = new DijkstraMap(dungeon, dijkstra.measurement); 366 367 t = rt.extend(origin, ts[i], length, false, dungeon.length, dungeon[0].length); 368 dt.resetMap(); 369 dt.clearGoals(); 370 los.isReachable(resMap, origin.x, origin.y, t.x, t.y, rt); 371 Queue<Coord> lit = los.getLastPath(); 372 373 for(Coord p : lit) 374 { 375 dt.setGoal(p); 376 } 377 if(radius > 0) 378 dt.partialScan(radius, null); 379 380 381 double dist; 382 for (int x = 0; x < dungeon.length; x++) { 383 for (int y = 0; y < dungeon[x].length; y++) { 384 if (dt.gradientMap[x][y] < DijkstraMap.FLOOR){ 385 dist = reach.metric.radius(origin.x, origin.y, x, y); 386 if(dist <= reach.maxDistance + radius && dist >= reach.minDistance - radius) 387 compositeMap[i][x][y] = dm.physicalMap[x][y]; 388 else 389 compositeMap[i][x][y] = DijkstraMap.WALL; 390 } 391 else compositeMap[i][x][y] = DijkstraMap.WALL; 392 } 393 } 394 if(compositeMap[i][ts[i].x][ts[i].y] > DijkstraMap.FLOOR) 395 { 396 for (int x = 0; x < dungeon.length; x++) { 397 Arrays.fill(compositeMap[i][x], 99999.0); 398 } 399 continue; 400 } 401 402 403 dm.initialize(compositeMap[i]); 404 dm.setGoal(ts[i]); 405 dm.scan(null, null); 406 for (int x = 0; x < dungeon.length; x++) { 407 for (int y = 0; y < dungeon[x].length; y++) { 408 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!') ? dm.gradientMap[x][y] : 99999.0; 409 } 410 } 411 } 412 double bestQuality = 99999 * ts.length; 413 double[][] qualityMap = new double[dungeon.length][dungeon[0].length]; 414 for (int x = 0; x < qualityMap.length; x++) { 415 for (int y = 0; y < qualityMap[x].length; y++) { 416 qualityMap[x][y] = 0.0; 417 long bits = 0; 418 for (int i = 0; i < ts.length; ++i) { 419 qualityMap[x][y] += compositeMap[i][x][y]; 420 if(compositeMap[i][x][y] < 99999.0 && i < 63) 421 bits |= 1 << i; 422 } 423 if(qualityMap[x][y] < bestQuality) 424 { 425 ArrayList<Coord> ap = new ArrayList<>(); 426 427 for (int i = 0; i < ts.length && i < 63; ++i) { 428 if((bits & (1 << i)) != 0) 429 ap.add(ts[i]); 430 } 431 432 if(ap.size() > 0) { 433 bestQuality = qualityMap[x][y]; 434 bestPoints.clear(); 435 bestPoints.put(Coord.get(x, y), ap); 436 } 437 } 438 else if(qualityMap[x][y] == bestQuality) 439 { 440 ArrayList<Coord> ap = new ArrayList<>(); 441 442 for (int i = 0; i < ts.length && i < 63; ++i) { 443 if((bits & (1 << i)) != 0) 444 ap.add(ts[i]); 445 } 446 447 if (ap.size() > 0) { 448 bestPoints.put(Coord.get(x, y), ap); 449 } 450 } 451 } 452 } 453 454 return bestPoints; 455 } 456 457 @Override 458 public OrderedMap<Coord, ArrayList<Coord>> idealLocations(Collection<Coord> priorityTargets, Collection<Coord> lesserTargets, Collection<Coord> requiredExclusions) { 459 if(priorityTargets == null) 460 return idealLocations(lesserTargets, requiredExclusions); 461 if(requiredExclusions == null) requiredExclusions = new OrderedSet<>(); 462 463 //requiredExclusions.remove(origin); 464 int totalTargets = priorityTargets.size() + lesserTargets.size(); 465 OrderedMap<Coord, ArrayList<Coord>> bestPoints = new OrderedMap<>(totalTargets * 8); 466 467 if(totalTargets == 0) 468 return bestPoints; 469 470 Coord[] pts = priorityTargets.toArray(new Coord[0]); 471 Coord[] lts = lesserTargets.toArray(new Coord[0]); 472 Coord[] exs = requiredExclusions.toArray(new Coord[0]); 473 Coord t;// = rt.extend(origin, exs[0], length, false, dungeon.length, dungeon[0].length); 474 475 double[][][] compositeMap = new double[totalTargets][dungeon.length][dungeon[0].length]; 476 477 char[][] dungeonCopy = new char[dungeon.length][dungeon[0].length], 478 dungeonPriorities = new char[dungeon.length][dungeon[0].length]; 479 for (int i = 0; i < dungeon.length; i++) { 480 System.arraycopy(dungeon[i], 0, dungeonCopy[i], 0, dungeon[i].length); 481 Arrays.fill(dungeonPriorities[i], '#'); 482 } 483 DijkstraMap dt = new DijkstraMap(dungeon, dijkstra.measurement); 484 double[][] resMap = DungeonUtility.generateResistances(dungeon); 485 Coord tempPt; 486 for (int i = 0; i < exs.length; ++i) { 487 t = rt.extend(origin, exs[i], length, false, dungeon.length, dungeon[0].length); 488 dt.resetMap(); 489 dt.clearGoals(); 490 los.isReachable(resMap, origin.x, origin.y, t.x, t.y, rt); 491 Queue<Coord> lit = los.getLastPath(); 492 493 for(Coord p : lit) 494 { 495 dt.setGoal(p); 496 } 497 if(radius > 0) 498 dt.partialScan(radius, null); 499 500 for (int x = 0; x < dungeon.length; x++) { 501 for (int y = 0; y < dungeon[x].length; y++) { 502 tempPt = Coord.get(x, y); 503 dungeonCopy[x][y] = (dt.gradientMap[x][y] < DijkstraMap.FLOOR || !AreaUtils.verifyReach(reach, origin, tempPt)) ? '!' : dungeonCopy[x][y]; 504 } 505 } 506 } 507 508 for (int i = 0; i < pts.length; ++i) { 509 DijkstraMap dm = new DijkstraMap(dungeon, dijkstra.measurement); 510 511 t = rt.extend(origin, pts[i], length, false, dungeon.length, dungeon[0].length); 512 dt.resetMap(); 513 dt.clearGoals(); 514 los.isReachable(resMap, origin.x, origin.y, t.x, t.y, rt); 515 Queue<Coord> lit = los.getLastPath(); 516 517 for(Coord p : lit) 518 { 519 dt.setGoal(p); 520 } 521 if(radius > 0) 522 dt.partialScan(radius, null); 523 524 525 double dist; 526 for (int x = 0; x < dungeon.length; x++) { 527 for (int y = 0; y < dungeon[x].length; y++) { 528 if (dt.gradientMap[x][y] < DijkstraMap.FLOOR){ 529 dist = reach.metric.radius(origin.x, origin.y, x, y); 530 if(dist <= reach.maxDistance + radius && dist >= reach.minDistance - radius) { 531 compositeMap[i][x][y] = dm.physicalMap[x][y]; 532 dungeonPriorities[x][y] = dungeon[x][y]; 533 } 534 else 535 compositeMap[i][x][y] = DijkstraMap.WALL; 536 } 537 else compositeMap[i][x][y] = DijkstraMap.WALL; 538 } 539 } 540 if(compositeMap[i][pts[i].x][pts[i].y] > DijkstraMap.FLOOR) 541 { 542 for (int x = 0; x < dungeon.length; x++) { 543 Arrays.fill(compositeMap[i][x], 399999.0); 544 } 545 continue; 546 } 547 548 549 dm.initialize(compositeMap[i]); 550 dm.setGoal(pts[i]); 551 dm.scan(null, null); 552 for (int x = 0; x < dungeon.length; x++) { 553 for (int y = 0; y < dungeon[x].length; y++) { 554 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!') ? dm.gradientMap[x][y] : 399999.0; 555 } 556 } 557 dm.resetMap(); 558 dm.clearGoals(); 559 } 560 561 for (int i = pts.length; i < totalTargets; ++i) { 562 DijkstraMap dm = new DijkstraMap(dungeon, dijkstra.measurement); 563 564 t = rt.extend(origin, lts[i - pts.length], length, false, dungeon.length, dungeon[0].length); 565 dt.resetMap(); 566 dt.clearGoals(); 567 los.isReachable(resMap, origin.x, origin.y, t.x, t.y, rt); 568 Queue<Coord> lit = los.getLastPath(); 569 570 for(Coord p : lit) 571 { 572 dt.setGoal(p); 573 } 574 if(radius > 0) 575 dt.partialScan(radius, null); 576 577 578 double dist; 579 for (int x = 0; x < dungeon.length; x++) { 580 for (int y = 0; y < dungeon[x].length; y++) { 581 if (dt.gradientMap[x][y] < DijkstraMap.FLOOR){ 582 dist = reach.metric.radius(origin.x, origin.y, x, y); 583 if(dist <= reach.maxDistance + radius && dist >= reach.minDistance - radius) 584 compositeMap[i][x][y] = dm.physicalMap[x][y]; 585 else 586 compositeMap[i][x][y] = DijkstraMap.WALL; 587 } 588 else compositeMap[i][x][y] = DijkstraMap.WALL; 589 } 590 } 591 if(compositeMap[i][lts[i - pts.length].x][lts[i - pts.length].y] > DijkstraMap.FLOOR) 592 { 593 for (int x = 0; x < dungeon.length; x++) 594 { 595 Arrays.fill(compositeMap[i][x], 99999.0); 596 } 597 continue; 598 } 599 600 601 dm.initialize(compositeMap[i]); 602 dm.setGoal(lts[i - pts.length]); 603 dm.scan(null, null); 604 for (int x = 0; x < dungeon.length; x++) { 605 for (int y = 0; y < dungeon[x].length; y++) { 606 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!' && dungeonPriorities[x][y] != '#') ? dm.gradientMap[x][y] : 99999.0; 607 } 608 } 609 dm.resetMap(); 610 dm.clearGoals(); 611 } 612 double bestQuality = 99999 * lts.length + 399999 * pts.length; 613 double[][] qualityMap = new double[dungeon.length][dungeon[0].length]; 614 for (int x = 0; x < qualityMap.length; x++) { 615 for (int y = 0; y < qualityMap[x].length; y++) { 616 qualityMap[x][y] = 0.0; 617 long pbits = 0, lbits = 0; 618 for (int i = 0; i < pts.length; ++i) { 619 qualityMap[x][y] += compositeMap[i][x][y]; 620 if(compositeMap[i][x][y] < 399999.0 && i < 63) 621 pbits |= 1 << i; 622 } 623 for (int i = pts.length; i < totalTargets; ++i) { 624 qualityMap[x][y] += compositeMap[i][x][y]; 625 if(compositeMap[i][x][y] < 99999.0 && i < 63) 626 lbits |= 1 << i; 627 } 628 if(qualityMap[x][y] < bestQuality) 629 { 630 ArrayList<Coord> ap = new ArrayList<>(); 631 632 for (int i = 0; i < pts.length && i < 63; ++i) { 633 if((pbits & (1 << i)) != 0) 634 ap.add(pts[i]); 635 } 636 for (int i = pts.length; i < totalTargets && i < 63; ++i) { 637 if((lbits & (1 << i)) != 0) 638 ap.add(lts[i - pts.length]); 639 } 640 641 if(ap.size() > 0) { 642 bestQuality = qualityMap[x][y]; 643 bestPoints.clear(); 644 bestPoints.put(Coord.get(x, y), ap); 645 } 646 } 647 else if(qualityMap[x][y] == bestQuality) 648 { 649 ArrayList<Coord> ap = new ArrayList<>(); 650 651 for (int i = 0; i < pts.length && i < 63; ++i) { 652 if ((pbits & (1 << i)) != 0) { 653 ap.add(pts[i]); 654 ap.add(pts[i]); 655 ap.add(pts[i]); 656 ap.add(pts[i]); 657 } 658 } 659 for (int i = pts.length; i < totalTargets && i < 63; ++i) { 660 if((lbits & (1 << i)) != 0) 661 ap.add(lts[i - pts.length]); 662 } 663 664 if (ap.size() > 0) { 665 bestPoints.put(Coord.get(x, y), ap); 666 } 667 } 668 } 669 } 670 671 return bestPoints; 672 } 673 674 /* 675 @Override 676 public ArrayList<ArrayList<Coord>> idealLocations(Set<Coord> targets, Set<Coord> requiredExclusions) { 677 int totalTargets = targets.size() + 1; 678 int volume = (int)(rt.radius(1, 1, dungeon.length - 2, dungeon[0].length - 2) * radius * 2.1); 679 ArrayList<ArrayList<Coord>> locs = new ArrayList<ArrayList<Coord>>(totalTargets); 680 for(int i = 0; i < totalTargets; i++) 681 { 682 locs.add(new ArrayList<Coord>(volume)); 683 } 684 if(totalTargets == 1) 685 return locs; 686 687 int ctr = 0; 688 689 boolean[][] tested = new boolean[dungeon.length][dungeon[0].length]; 690 for (int x = 1; x < dungeon.length - 1; x += radius) { 691 for (int y = 1; y < dungeon[x].length - 1; y += radius) { 692 693 if(mayContainTarget(requiredExclusions, x, y)) 694 continue; 695 ctr = 0; 696 for(Coord tgt : targets) 697 { 698 if(rt.radius(origin.x, origin.y, tgt.x, tgt.y) + rt.radius(end.x, end.y, tgt.x, tgt.y) - 699 rt.radius(origin.x, origin.y, end.x, end.y) <= 3.0 + radius) 700 ctr++; 701 } 702 if(ctr > 0) 703 locs.get(totalTargets - ctr).add(Coord.get(x, y)); 704 } 705 } 706 Coord it; 707 for(int t = 0; t < totalTargets - 1; t++) 708 { 709 if(locs.get(t).size() > 0) { 710 int numPoints = locs.get(t).size(); 711 for (int i = 0; i < numPoints; i++) { 712 it = locs.get(t).get(i); 713 for (int x = Math.max(1, it.x - radius / 2); x < it.x + (radius + 1) / 2 && x < dungeon.length - 1; x++) { 714 for (int y = Math.max(1, it.y - radius / 2); y <= it.y + (radius - 1) / 2 && y < dungeon[0].length - 1; y++) 715 { 716 if(tested[x][y]) 717 continue; 718 tested[x][y] = true; 719 720 if(mayContainTarget(requiredExclusions, x, y)) 721 continue; 722 723 ctr = 0; 724 for(Coord tgt : targets) 725 { 726 if(rt.radius(origin.x, origin.y, tgt.x, tgt.y) + rt.radius(end.x, end.y, tgt.x, tgt.y) - 727 rt.radius(origin.x, origin.y, end.x, end.y) <= 3.0 + radius) 728 ctr++; 729 } 730 if(ctr > 0) 731 locs.get(totalTargets - ctr).add(Coord.get(x, y)); 732 } 733 } 734 } 735 } 736 } 737 return locs; 738 } 739 */ 740 @Override 741 public void setMap(char[][] map) { 742 dungeon = map; 743 end = rt.extend(origin, end, length, false, map.length, map[0].length); 744 dijkstra.resetMap(); 745 dijkstra.clearGoals(); 746 } 747 748 @Override 749 public OrderedMap<Coord, Double> findArea() { 750 double[][] dmap = initDijkstra(); 751 dmap[origin.x][origin.y] = DijkstraMap.DARK; 752 dijkstra.resetMap(); 753 dijkstra.clearGoals(); 754 return AreaUtils.dijkstraToHashMap(dmap); 755 } 756 757}