Java tutorial
// Original work Copyright (c) 2015, 2017, Igor Dimitrijevic // Modified work Copyright (c) 2017-2018 OpenBW Team ////////////////////////////////////////////////////////////////////////// // // This file is part of the BWEM Library. // BWEM is free software, licensed under the MIT/X11 License. // A copy of the license is provided with the library in the LICENSE file. // Copyright (c) 2015, 2017, Igor Dimitrijevic // ////////////////////////////////////////////////////////////////////////// package bwem; import bwem.area.Area; import bwem.area.AreaInitializer; import bwem.area.AreaInitializerImpl; import bwem.area.typedef.AreaId; import bwem.area.typedef.GroupId; import bwem.map.TerrainData; import bwem.map.Map; import bwem.tile.MiniTile; import bwem.tile.Tile; import bwem.tile.TileImpl; import bwem.typedef.Altitude; import bwem.typedef.CPPath; import bwem.typedef.Index; import bwem.unit.Geyser; import bwem.unit.Mineral; import bwem.unit.Neutral; import bwem.unit.StaticBuilding; import bwem.util.BwemExt; import bwem.util.Utils; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.tuple.MutablePair; import org.openbw.bwapi4j.Position; import org.openbw.bwapi4j.TilePosition; import org.openbw.bwapi4j.WalkPosition; import org.openbw.bwapi4j.util.Pair; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.PriorityQueue; import java.util.Queue; public final class Graph { private final Map map; private final List<Area> areas = new ArrayList<>(); private final List<ChokePoint> chokePoints = new ArrayList<>(); private final List<List<List<ChokePoint>>> chokePointsMatrix = new ArrayList<>(); // index == Area::id x Area::id private final List<List<Integer>> chokePointDistanceMatrix = new ArrayList<>(); // index == ChokePoint::index x ChokePoint::index private final List<List<CPPath>> pathsBetweenChokePoints = new ArrayList<>(); // index == ChokePoint::index x ChokePoint::index private final List<Base> bases = new ArrayList<>(); public Graph(Map map) { this.map = map; } public Map getMap() { return map; } public List<Area> getAreas() { return areas; } public int getAreaCount() { return areas.size(); } public Area getArea(final AreaId id) { // bwem_assert(valid(id)); if (!isValid(id)) { throw new IllegalArgumentException(); } return areas.get(id.intValue() - 1); } public Area getArea(final WalkPosition walkPosition) { final AreaId areaId = getMap().getData().getMiniTile(walkPosition).getAreaId(); return (areaId.intValue() > 0) ? getArea(areaId) : null; } public Area getArea(final TilePosition tilePosition) { final AreaId areaId = getMap().getData().getTile(tilePosition).getAreaId(); return (areaId.intValue() > 0) ? getArea(areaId) : null; } public Area getNearestArea(final WalkPosition walkPosition) { final Area area = getArea(walkPosition); if (area != null) { return area; } final WalkPosition w = getMap().breadthFirstSearch(walkPosition, // findCond args -> { final Object ttile = args[0]; if (ttile instanceof MiniTile) { final MiniTile miniTile = (MiniTile) ttile; return (miniTile.getAreaId().intValue() > 0); } else { throw new IllegalArgumentException(); } }, // visitCond args -> true); return getArea(w); } public Area getNearestArea(final TilePosition tilePosition) { final Area area = getArea(tilePosition); if (area != null) { return area; } final TilePosition t = getMap().breadthFirstSearch(tilePosition, // findCond args -> { Object ttile = args[0]; if (ttile instanceof Tile) { Tile tile = (Tile) ttile; return (tile.getAreaId().intValue() > 0); } else { throw new IllegalArgumentException(); } }, // visitCond args -> true); return getArea(t); } // Returns the list of all the getChokePoints in the Map. public List<ChokePoint> getChokePoints() { return chokePoints; } // Returns the getChokePoints between two areas. public List<ChokePoint> getChokePoints(final AreaId a, final AreaId b) { if (!isValid(a)) { // bwem_assert(valid(a)); throw new IllegalArgumentException(); } else if (!isValid(b)) { // bwem_assert(valid(b)); throw new IllegalArgumentException(); } else if (a.intValue() == b.intValue()) { // bwem_assert(a != b); throw new IllegalArgumentException(); } int aVal = a.intValue(); int bVal = b.intValue(); if (aVal > bVal) { int aValTmp = aVal; aVal = bVal; bVal = aValTmp; } return chokePointsMatrix.get(bVal).get(aVal); } // Returns the getChokePoints between two areas. public List<ChokePoint> getChokePoints(final Area a, final Area b) { return getChokePoints(a.getId(), b.getId()); } // Returns the ground distance in pixels between cpA->center() and cpB>center() public int distance(ChokePoint cpA, ChokePoint cpB) { return chokePointDistanceMatrix.get(((ChokePointImpl) cpA).getIndex().intValue()) .get(((ChokePointImpl) cpB).getIndex().intValue()); } // Returns a list of getChokePoints, which is intended to be the shortest walking path from cpA to cpB. public CPPath getPath(ChokePoint cpA, ChokePoint cpB) { return pathsBetweenChokePoints.get(((ChokePointImpl) cpA).getIndex().intValue()) .get(((ChokePointImpl) cpB).getIndex().intValue()); } public CPPath getPath(final Position a, final Position b, final MutableInt pLength) { final Area areaA = getNearestArea(a.toWalkPosition()); final Area areaB = getNearestArea(b.toWalkPosition()); if (areaA.equals(areaB)) { if (pLength != null) { pLength.setValue(BwemExt.getApproxDistance(a, b)); } return new CPPath(); } if (!areaA.isAccessibleFrom(areaB)) { if (pLength != null) { pLength.setValue(-1); } return new CPPath(); } int minDistAB = Integer.MAX_VALUE; ChokePoint pBestCpA = null; ChokePoint pBestCpB = null; for (final ChokePoint cpA : areaA.getChokePoints()) { if (!cpA.isBlocked()) { final int distACpA = BwemExt.getApproxDistance(a, cpA.getCenter().toPosition()); for (final ChokePoint cpB : areaB.getChokePoints()) { if (!cpB.isBlocked()) { final int distBToCPB = BwemExt.getApproxDistance(b, cpB.getCenter().toPosition()); final int distAToB = distACpA + distBToCPB + distance(cpA, cpB); if (distAToB < minDistAB) { minDistAB = distAToB; pBestCpA = cpA; pBestCpB = cpB; } } } } } // bwem_assert(minDistAB != numeric_limits<int>::max()); if (minDistAB == Integer.MAX_VALUE) { throw new IllegalStateException(); } final CPPath path = getPath(pBestCpA, pBestCpB); if (pLength != null) { // bwem_assert(Path.size() >= 1); if (!(path.size() >= 1)) { throw new IllegalStateException(); } pLength.setValue(minDistAB); if (path.size() == 1) { // bwem_assert(pBestCpA == pBestCpB); if (!pBestCpA.equals(pBestCpB)) { throw new IllegalStateException(); } final Position cpEnd1 = BwemExt.center(pBestCpA.getNodePosition(ChokePoint.Node.END1)); final Position cpEnd2 = BwemExt.center(pBestCpA.getNodePosition(ChokePoint.Node.END2)); if (Utils.intersect(a.getX(), a.getY(), b.getX(), b.getY(), cpEnd1.getX(), cpEnd1.getY(), cpEnd2.getX(), cpEnd2.getY())) { pLength.setValue(BwemExt.getApproxDistance(a, b)); } else { for (final ChokePoint.Node node : new ChokePoint.Node[] { ChokePoint.Node.END1, ChokePoint.Node.END2 }) { final Position c = BwemExt.center(pBestCpA.getNodePosition(node)); final int distAToB = BwemExt.getApproxDistance(a, c) + BwemExt.getApproxDistance(b, c); if (distAToB < pLength.intValue()) { pLength.setValue(distAToB); } } } } } return getPath(pBestCpA, pBestCpB); } public CPPath getPath(Position a, Position b) { return getPath(a, b, null); } public List<Base> getBases() { return this.bases; } // Creates a new Area for each pair (top, miniTiles) in areasList (See Area::top() and Area::miniTiles()) public void createAreas(final List<MutablePair<WalkPosition, Integer>> areasList) { for (int id = 1; id <= areasList.size(); ++id) { final WalkPosition top = areasList.get(id - 1).getLeft(); final int miniTileCount = areasList.get(id - 1).getRight(); this.areas.add(new AreaInitializerImpl(getMap(), new AreaId(id), top, miniTileCount)); } } //////////////////////////////////////////////////////////////////////// // Graph::createChokePoints //////////////////////////////////////////////////////////////////////// //---------------------------------------------------------------------- // 1) size the matrix //---------------------------------------------------------------------- private void initializeChokePointsMatrix(final List<List<List<ChokePoint>>> chokePointsMatrix, final int areasCount) { // ChokePointsMatrix.resize(areasCount() + 1); chokePointsMatrix.clear(); for (int i = 0; i < areasCount + 1; ++i) { chokePointsMatrix.add(new ArrayList<>()); } // for (Area::id id = 1 ; id <= areasCount() ; ++id) // m_ChokePointsMatrix[id].resize(id); // triangular matrix for (int id = 1; id <= areasCount; ++id) { // triangular matrix for (int n = 0; n < id; ++n) { chokePointsMatrix.get(id).add(new ArrayList<>()); } } } //---------------------------------------------------------------------- //---------------------------------------------------------------------- // 2) Dispatch the global raw frontier between all the relevant pairs of areas: //---------------------------------------------------------------------- private java.util.Map<MutablePair<AreaId, AreaId>, List<WalkPosition>> createRawFrontierByAreaPairMap( final List<MutablePair<MutablePair<AreaId, AreaId>, WalkPosition>> rawFrontier) { final java.util.Map<MutablePair<AreaId, AreaId>, List<WalkPosition>> rawFrontierByAreaPair = new HashMap<>(); for (final MutablePair<MutablePair<AreaId, AreaId>, WalkPosition> raw : rawFrontier) { int a = raw.getLeft().getLeft().intValue(); int b = raw.getLeft().getRight().intValue(); if (a > b) { final int a_tmp = a; a = b; b = a_tmp; } // bwem_assert(a <= b); if (!(a <= b)) { throw new IllegalStateException(); } // bwem_assert((a >= 1) && (b <= areasCount())); if (!((a >= 1) && (b <= getAreaCount()))) { throw new IllegalStateException(); } final MutablePair<AreaId, AreaId> key = new MutablePair<>(new AreaId(a), new AreaId(b)); rawFrontierByAreaPair.computeIfAbsent(key, mp -> new ArrayList<>()).add(raw.getRight()); } return rawFrontierByAreaPair; } //---------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////// // Creates a new Area for each pair (top, miniTiles) in AreasList (See Area::top() and Area::miniTiles()) public void createChokePoints(final List<StaticBuilding> staticBuildings, final List<Mineral> minerals, final List<MutablePair<MutablePair<AreaId, AreaId>, WalkPosition>> rawFrontier) { Index newIndex = new Index(0); final List<Neutral> blockingNeutrals = new ArrayList<>(); for (final StaticBuilding s : staticBuildings) { if (s.isBlocking()) { blockingNeutrals.add(s); } } for (final Mineral m : minerals) { if (m.isBlocking()) { blockingNeutrals.add(m); } } //Note: pseudoChokePointsToCreate is only used for pre-allocating the GetChokePoints array size. // This number will highly likely be very small. There is no reason to set a minimum size. // int pseudoChokePointsToCreate = 0; // for (final Neutral blockingNeutral : blockingNeutrals) { // if (blockingNeutral.getNextStacked() == null) { // ++pseudoChokePointsToCreate; // } // } // 1) size the matrix initializeChokePointsMatrix(this.chokePointsMatrix, getAreaCount()); // 2) Dispatch the global raw frontier between all the relevant pairs of areas: final java.util.Map<MutablePair<AreaId, AreaId>, List<WalkPosition>> rawFrontierByAreaPair = createRawFrontierByAreaPairMap( rawFrontier); // 3) For each pair of areas (A, B): for (final java.util.Map.Entry<MutablePair<AreaId, AreaId>, List<WalkPosition>> entry : rawFrontierByAreaPair .entrySet()) { MutablePair<AreaId, AreaId> rawleft = entry.getKey(); final List<WalkPosition> rawFrontierAB = entry.getValue(); // Because our dispatching preserved order, // and because Map::m_RawFrontier was populated in descending order of the altitude (see Map::computeAreas), // we know that rawFrontierAB is also ordered the same way, but let's check it: { final List<Altitude> altitudes = new ArrayList<>(); for (final WalkPosition w : rawFrontierAB) { altitudes.add(getMap().getData().getMiniTile(w).getAltitude()); } // Check if the altitudes array is sorted in descending order. // bwem_assert(is_sorted(altitudes.rbegin(), altitudes.rend())); for (int i = 1; i < altitudes.size(); ++i) { final int prev = altitudes.get(i - 1).intValue(); final int curr = altitudes.get(i).intValue(); if (prev < curr) { throw new IllegalStateException(); } } } // 3.1) Use that information to efficiently cluster rawFrontierAB in one or several chokepoints. // Each cluster will be populated starting with the center of a chokepoint (max altitude) // and finishing with the ends (min altitude). final int clusterMinDist = (int) Math.sqrt(BwemExt.lake_max_miniTiles); final List<List<WalkPosition>> clusters = new ArrayList<>(); for (final WalkPosition w : rawFrontierAB) { boolean added = false; for (final List<WalkPosition> cluster : clusters) { final int distToFront = BwemExt.queenWiseDist(cluster.get(0), w); final int distToBack = BwemExt.queenWiseDist(cluster.get(cluster.size() - 1), w); if (Math.min(distToFront, distToBack) <= clusterMinDist) { if (distToFront < distToBack) { cluster.add(0, w); } else { cluster.add(w); } added = true; break; } } if (!added) { final List<WalkPosition> list = new ArrayList<>(); list.add(w); clusters.add(list); } } // 3.2) Create one Chokepoint for each cluster: final AreaId a = rawleft.getLeft(); final AreaId b = rawleft.getRight(); // getChokePoints(a, b).reserve(clusters.size() + pseudoChokePointsToCreate); for (final List<WalkPosition> cluster : clusters) { getChokePoints(a, b).add(new ChokePointImpl(this, newIndex, getArea(a), getArea(b), cluster)); newIndex = newIndex.add(1); } } // 4) Create one Chokepoint for each pair of blocked areas, for each blocking Neutral: for (final Neutral blockingNeutral : blockingNeutrals) { if (blockingNeutral.getNextStacked() == null) { // in the case where several neutrals are stacked, we only consider the top final List<Area> blockedAreas = blockingNeutral.getBlockedAreas(); for (final Area blockedAreaA : blockedAreas) for (final Area blockedAreaB : blockedAreas) { if (blockedAreaB.equals(blockedAreaA)) { break; // breaks symmetry } final WalkPosition center = getMap().breadthFirstSearch( blockingNeutral.getCenter().toWalkPosition(), // findCond args -> { Object ttile = args[0]; if (!(ttile instanceof MiniTile)) { throw new IllegalArgumentException(); } MiniTile miniTile = (MiniTile) ttile; return miniTile.isWalkable(); }, // visitCond args -> true); final List<WalkPosition> list = new ArrayList<>(); list.add(center); getChokePoints(blockedAreaA, blockedAreaB).add(new ChokePointImpl(this, newIndex, blockedAreaA, blockedAreaB, list, blockingNeutral)); newIndex = newIndex.add(1); } } } // 5) Set the references to the freshly created Chokepoints: for (int loopA = 1; loopA <= getAreaCount(); ++loopA) for (int loopB = 1; loopB < loopA; ++loopB) { final AreaId a = new AreaId(loopA); final AreaId b = new AreaId(loopB); if (!getChokePoints(a, b).isEmpty()) { ((AreaInitializer) getArea(a)).addChokePoints(getArea(b), getChokePoints(a, b)); ((AreaInitializer) getArea(b)).addChokePoints(getArea(a), getChokePoints(a, b)); this.chokePoints.addAll(getChokePoints(a, b)); } } } //---------------------------------------------------------------------- // Computes the ground distances between any pair of ChokePoints in pContext // This is achieved by invoking several times pContext->ComputeDistances, // which effectively computes the distances from one starting ChokePoint, using Dijkstra's algorithm. // If Context == Area, Dijkstra's algorithm works on the Tiles inside one Area. // If Context == Graph, Dijkstra's algorithm works on the GetChokePoints between the AreaS. public void computeChokePointDistanceMatrix() { // 1) size the matrix chokePointDistanceMatrix.clear(); // m_ChokePointDistanceMatrix.resize (chokePoints.size()); for (int i = 0; i < chokePoints.size(); ++i) { chokePointDistanceMatrix.add(new ArrayList<>()); } // for (auto & line : chokePointDistanceMatrix) // line.resize (chokePoints.size(), -1); for (int i = 0; i < chokePointDistanceMatrix.size(); ++i) { for (int n = 0; n < chokePoints.size(); ++n) { chokePointDistanceMatrix.get(i).add(-1); } } // m_PathsBetweenChokePoints.resize (chokePoints.size()); pathsBetweenChokePoints.clear(); for (int i = 0; i < chokePoints.size(); ++i) { pathsBetweenChokePoints.add(new ArrayList<>()); } // for (auto & line : pathsBetweenChokePoints) // line.resize (chokePoints.size()); for (int i = 0; i < pathsBetweenChokePoints.size(); ++i) { for (int n = 0; n < chokePoints.size(); ++n) { pathsBetweenChokePoints.get(i).add(new CPPath()); } } // 2) Compute distances inside each Area for (final Area area : getAreas()) { computeChokePointDistances(area); } // 3) Compute distances through connected areas computeChokePointDistances(this); for (final ChokePoint cp : getChokePoints()) { setDistance(cp, cp, 0); CPPath cppath = new CPPath(); cppath.add(cp); setPath(cp, cp, cppath); } // 4) Update Area::m_AccessibleNeighbors for each Area for (final Area area : getAreas()) ((AreaInitializer) area).updateAccessibleNeighbors(); // 5) Update Area::m_groupId for each Area updateGroupIds(); } public void collectInformation() { // 1) Process the whole Map: for (final Mineral mineral : getMap().getNeutralData().getMinerals()) { final Area area = getMap().getMainArea(mineral.getTopLeft(), mineral.getSize()); if (area != null) { ((AreaInitializer) area).addMineral(mineral); } } for (Geyser geyser : getMap().getNeutralData().getGeysers()) { final Area area = getMap().getMainArea(geyser.getTopLeft(), geyser.getSize()); if (area != null) { ((AreaInitializer) area).addGeyser(geyser); } } for (int y = 0; y < getMap().getData().getMapData().getTileSize().getY(); ++y) for (int x = 0; x < getMap().getData().getMapData().getTileSize().getX(); ++x) { final Tile tile = getMap().getData().getTile(new TilePosition(x, y)); if (tile.getAreaId().intValue() > 0) { ((AreaInitializer) getArea(tile.getAreaId())).addTileInformation(new TilePosition(x, y), tile); } } // 2) Post-process each Area separately: for (final Area area : this.areas) { ((AreaInitializer) area).postCollectInformation(); } } public void createBases(final TerrainData terrainData) { this.bases.clear(); for (final Area area : this.areas) { ((AreaInitializer) area).createBases(terrainData); this.bases.addAll(area.getBases()); } } //////////////////////////////////////////////////////////////////////// // Graph::ComputeChokePointDistances //////////////////////////////////////////////////////////////////////// // Computes the ground distances between any pair of getChokePoints in pContext // This is achieved by invoking several times pContext->computeDistances, // which effectively computes the distances from one starting ChokePoint, using Dijkstra's algorithm. // If Context == Area, Dijkstra's algorithm works on the Tiles inside one Area. // If Context == Graph, Dijkstra's algorithm works on the getChokePoints between the AreaS. private void computeChokePointDistances(final Area pContext) { for (final ChokePoint pStart : pContext.getChokePoints()) { final List<ChokePoint> targets = new ArrayList<>(); for (final ChokePoint cp : pContext.getChokePoints()) { if (cp.equals(pStart)) { break; // breaks symmetry } targets.add(cp); } final int[] distanceToTargets = ((AreaInitializer) pContext).computeDistances(pStart, targets); setPathForComputeChokePointDistances(distanceToTargets, pStart, targets, false); } } private void computeChokePointDistances(final Graph pContext) { for (final ChokePoint pStart : pContext.getChokePoints()) { final List<ChokePoint> targets = new ArrayList<>(); for (final ChokePoint cp : pContext.getChokePoints()) { if (cp.equals(pStart)) { break; // breaks symmetry } targets.add(cp); } final int[] distanceToTargets = pContext.computeDistances(pStart, targets); setPathForComputeChokePointDistances(distanceToTargets, pStart, targets, true); } } private void setPathForComputeChokePointDistances(final int[] distanceToTargets, ChokePoint pStart, List<ChokePoint> targets, boolean collectIntermediateChokePoints) { for (int i = 0; i < targets.size(); ++i) { final int newDist = distanceToTargets[i]; final ChokePoint target = targets.get(i); final int existingDist = distance(pStart, target); if (newDist != 0 && ((existingDist == -1) || (newDist < existingDist))) { setDistance(pStart, target, newDist); // Build the path from pStart to targets[i]: final CPPath path = new CPPath(); path.add(pStart); path.add(target); // // if (Context == Graph), there may be intermediate getChokePoints. They have been set by computeDistances, // // so we just have to collect them (in the reverse order) and insert them into Path: // if ((void *)(pContext) == (void *)(this)) // tests (Context == Graph) without warning about constant condition if (collectIntermediateChokePoints) { for (ChokePoint pPrev = ((ChokePointImpl) target).getPathBackTrace(); !pPrev .equals(pStart); pPrev = ((ChokePointImpl) pPrev).getPathBackTrace()) { path.add(1, pPrev); } } setPath(pStart, target, path); } } } //////////////////////////////////////////////////////////////////////// // Returns Distances such that Distances[i] == ground_distance(start, targets[i]) in pixels // Any Distances[i] may be 0 (meaning targets[i] is not reachable). // This may occur in the case where start and targets[i] leave in different continents or due to Bloqued intermediate ChokePoint(s). // For each reached target, the shortest path can be derived using // the backward trace set in cp->getPathBackTrace() for each intermediate ChokePoint cp from the target. // Note: same algo than Area::computeDistances (derived from Dijkstra) private int[] computeDistances(final ChokePoint start, final List<ChokePoint> targets) { final int[] distances = new int[targets.size()]; TileImpl.getStaticMarkable().unmarkAll(); final Queue<Pair<Integer, ChokePoint>> toVisit = new PriorityQueue<>(Comparator.comparingInt(a -> a.first)); toVisit.offer(new Pair<>(0, start)); int remainingTargets = targets.size(); while (!toVisit.isEmpty()) { final Pair<Integer, ChokePoint> distanceAndChokePoint = toVisit.poll(); final int currentDist = distanceAndChokePoint.first; final ChokePoint current = distanceAndChokePoint.second; final Tile currentTile = getMap().getData().getTile(current.getCenter().toTilePosition(), CheckMode.NO_CHECK); // bwem_assert(currentTile.InternalData() == currentDist); if (!(((TileImpl) currentTile).getInternalData() == currentDist)) { throw new IllegalStateException(); } ((TileImpl) currentTile).setInternalData(0); // resets Tile::m_internalData for future usage ((TileImpl) currentTile).getMarkable().setMarked(); for (int i = 0; i < targets.size(); ++i) { if (current == targets.get(i)) { distances[i] = currentDist; --remainingTargets; } } if (remainingTargets == 0) { break; } if (current.isBlocked() && (!current.equals(start))) { continue; } for (final Area pArea : new Area[] { current.getAreas().getFirst(), current.getAreas().getSecond() }) { for (final ChokePoint next : pArea.getChokePoints()) { if (!next.equals(current)) { final int newNextDist = currentDist + distance(current, next); final Tile nextTile = getMap().getData().getTile(next.getCenter().toTilePosition(), CheckMode.NO_CHECK); if (!((TileImpl) nextTile).getMarkable().isMarked()) { if (((TileImpl) nextTile).getInternalData() != 0) { // next already in toVisit if (newNextDist < ((TileImpl) nextTile).getInternalData()) { // nextNewDist < nextOldDist // To update next's distance, we need to remove-insert it from toVisit: // bwem_assert(iNext != range.second); final boolean removed = toVisit .remove(new Pair<>(((TileImpl) nextTile).getInternalData(), next)); if (!removed) { throw new IllegalStateException(); } ((TileImpl) nextTile).setInternalData(newNextDist); ((ChokePointImpl) next).setPathBackTrace(current); toVisit.offer(new Pair<>(newNextDist, next)); } } else { ((TileImpl) nextTile).setInternalData(newNextDist); ((ChokePointImpl) next).setPathBackTrace(current); toVisit.offer(new Pair<>(newNextDist, next)); } } } } } } // // bwem_assert(!remainingTargets); // if (!(remainingTargets == 0)) { // throw new IllegalStateException(); // } // reset Tile::m_internalData for future usage for (Pair<Integer, ChokePoint> distanceToChokePoint : toVisit) { ((TileImpl) getMap().getData().getTile(distanceToChokePoint.second.getCenter().toTilePosition(), CheckMode.NO_CHECK)).setInternalData(0); } return distances; } private void updateGroupIds() { int nextGroupId = 1; AreaInitializerImpl.getStaticMarkable().unmarkAll(); for (final Area start : getAreas()) { if (!((AreaInitializer) start).getMarkable().isMarked()) { final List<Area> toVisit = new ArrayList<>(); toVisit.add(start); while (!toVisit.isEmpty()) { final Area current = toVisit.remove(toVisit.size() - 1); ((AreaInitializer) current).setGroupId(new GroupId(nextGroupId)); for (final Area next : current.getAccessibleNeighbors()) { if (!((AreaInitializer) next).getMarkable().isMarked()) { ((AreaInitializer) next).getMarkable().setMarked(); toVisit.add(next); } } } ++nextGroupId; } } } private void setDistance(final ChokePoint cpA, final ChokePoint cpB, final int value) { final int indexA = ((ChokePointImpl) cpA).getIndex().intValue(); final int indexB = ((ChokePointImpl) cpB).getIndex().intValue(); this.chokePointDistanceMatrix.get(indexA).set(indexB, value); this.chokePointDistanceMatrix.get(indexB).set(indexA, value); } private void setPath(final ChokePoint cpA, final ChokePoint cpB, final CPPath pathAB) { final int indexA = ((ChokePointImpl) cpA).getIndex().intValue(); final int indexB = ((ChokePointImpl) cpB).getIndex().intValue(); this.pathsBetweenChokePoints.get(indexA).set(indexB, pathAB); if (cpA != cpB) { final CPPath reversePath = this.pathsBetweenChokePoints.get(indexB).get(indexA); reversePath.clear(); for (int i = pathAB.size() - 1; i >= 0; --i) { final ChokePoint cp = pathAB.get(i); reversePath.add(cp); } } } private boolean isValid(AreaId id) { return (1 <= id.intValue() && id.intValue() <= getAreaCount()); } }