From d6781acbf6f44923b369051d00a0fef12cf24c44 Mon Sep 17 00:00:00 2001 From: Timo van Veen Date: Mon, 30 Oct 2023 01:16:16 +0100 Subject: [PATCH] Multiple stations 1 call --- .../entity/CarriageContraptionEntity.java | 3 + .../content/trains/entity/Navigation.java | 287 +++++++++++------- .../content/trains/graph/DiscoveredPath.java | 2 - .../trains/schedule/ScheduleRuntime.java | 21 +- 4 files changed, 189 insertions(+), 124 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java index dc750775d..fdf358405 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/CarriageContraptionEntity.java @@ -1,6 +1,7 @@ package com.simibubi.create.content.trains.entity; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -54,6 +55,8 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.network.PacketDistributor; +import org.openjdk.nashorn.internal.objects.Global; + public class CarriageContraptionEntity extends OrientedContraptionEntity { private static final EntityDataAccessor CARRIAGE_DATA = diff --git a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java index c2415cb06..76fd7e6f8 100644 --- a/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/trains/entity/Navigation.java @@ -439,6 +439,13 @@ public class Navigation { @Nullable public DiscoveredPath findPathTo(GlobalStation destination, double maxCost) { + ArrayList destinations = new ArrayList<>(); + destinations.add(destination); + return findPathTo(destinations, maxCost); + } + + @Nullable + public DiscoveredPath findPathTo(ArrayList destinations, double maxCost) { TrackGraph graph = train.graph; if (graph == null) return null; @@ -458,34 +465,36 @@ public class Navigation { : graph.getConnectionsFrom(initialPoint.node2) .get(initialPoint.node1); - search(Double.MAX_VALUE, maxCost, forward, destination, (distance, cost, reachedVia, currentEntry, globalStation) -> { - if (globalStation != destination) - return false; + search(Double.MAX_VALUE, maxCost, forward, destinations, (distance, cost, reachedVia, currentEntry, globalStation) -> { + for (GlobalStation destination : destinations){ + if (globalStation == destination) { + TrackEdge edge = currentEntry.getSecond(); + TrackNode node1 = currentEntry.getFirst() + .getFirst(); + TrackNode node2 = currentEntry.getFirst() + .getSecond(); - TrackEdge edge = currentEntry.getSecond(); - TrackNode node1 = currentEntry.getFirst() - .getFirst(); - TrackNode node2 = currentEntry.getFirst() - .getSecond(); + List> currentPath = new ArrayList<>(); + Pair> backTrack = reachedVia.get(edge); + Couple toReach = Couple.create(node1, node2); + TrackEdge edgeReached = edge; + while (backTrack != null) { + if (edgeReached == initialEdge) + break; + if (backTrack.getFirst()) + currentPath.add(0, toReach); + toReach = backTrack.getSecond(); + edgeReached = graph.getConnection(toReach); + backTrack = reachedVia.get(edgeReached); + } - List> currentPath = new ArrayList<>(); - Pair> backTrack = reachedVia.get(edge); - Couple toReach = Couple.create(node1, node2); - TrackEdge edgeReached = edge; - while (backTrack != null) { - if (edgeReached == initialEdge) - break; - if (backTrack.getFirst()) - currentPath.add(0, toReach); - toReach = backTrack.getSecond(); - edgeReached = graph.getConnection(toReach); - backTrack = reachedVia.get(edgeReached); + double position = edge.getLength() - destination.getLocationOn(edge); + double distanceToDestination = distance - position; + results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath, destination)); + return true; + } } - - double position = edge.getLength() - destination.getLocationOn(edge); - double distanceToDestination = distance - position; - results.set(forward, new DiscoveredPath((forward ? 1 : -1) * distanceToDestination, cost, currentPath, destination)); - return true; + return false; }); } @@ -534,18 +543,15 @@ public class Navigation { return result.getValue(); } - public void search(double maxDistance, boolean forward, GlobalStation destination, StationTest stationTest) { - search(maxDistance, -1, forward, destination, stationTest); + public void search(double maxDistance, boolean forward, ArrayList destinations, StationTest stationTest) { + search(maxDistance, -1, forward, destinations, stationTest); } - public void search(double maxDistance, double maxCost, boolean forward, GlobalStation destination, StationTest stationTest) { + public void search(double maxDistance, double maxCost, boolean forward, ArrayList destinations, StationTest stationTest) { TrackGraph graph = train.graph; if (graph == null) return; - // Cache the position of a node on the station edge if provided - Vec3 destinationNodePosition = destination == null ? null : destination.edgeLocation.getSecond().getLocation(); - // Cache the list of track types that the train can travel on Set validTypes = new HashSet<>(); for (int i = 0; i < train.carriages.size(); i++) { @@ -603,10 +609,57 @@ public class Navigation { double distanceToNode2 = forward ? initialEdge.getLength() - startingPoint.position : startingPoint.position; - frontier.add(new FrontierEntry(distanceToNode2, 0, initialNode1, initialNode2, initialEdge)); int signalWeight = Mth.clamp(ticksWaitingForSignal * 2, Train.Penalties.RED_SIGNAL, 200); - Search: while (!frontier.isEmpty()) { + int initialPenalty = 0; + if (costRelevant) + initialPenalty += penalties.getOrDefault(initialEdge, 0); + + EdgeData initialSignalData = initialEdge.getEdgeData(); + if (initialSignalData.hasPoints()) { + for (TrackEdgePoint point : initialSignalData.getPoints()) { + if (point.getLocationOn(initialEdge) < initialEdge.getLength() - distanceToNode2) + continue; + if (costRelevant && distanceToNode2 + initialPenalty > maxCost) + return; + if (!point.canNavigateVia(initialNode2)) + return; + if (point instanceof SignalBoundary signal) { + if (signal.isForcedRed(initialNode2)) { + initialPenalty += Train.Penalties.REDSTONE_RED_SIGNAL; + continue; + } + UUID group = signal.getGroup(initialNode2); + if (group == null) + continue; + SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(group); + if (signalEdgeGroup == null) + continue; + if (signalEdgeGroup.isOccupiedUnless(signal)) { + initialPenalty += signalWeight; + signalWeight /= 2; + } + } + if (point instanceof GlobalStation station) { + Train presentTrain = station.getPresentTrain(); + boolean isOwnStation = presentTrain == train; + if (presentTrain != null && !isOwnStation) + initialPenalty += Train.Penalties.STATION_WITH_TRAIN; + if (station.canApproachFrom(initialNode2) && stationTest.test(distanceToNode2, distanceToNode2 + initialPenalty, reachedVia, + Pair.of(Couple.create(initialNode1, initialNode2), initialEdge), station)) + return; + if (!isOwnStation) + initialPenalty += Train.Penalties.STATION; + } + } + } + + if (costRelevant && distanceToNode2 + initialPenalty > maxCost) + return; + + frontier.add(new FrontierEntry(distanceToNode2, initialPenalty, false, initialNode1, initialNode2, initialEdge)); + + while (!frontier.isEmpty()) { FrontierEntry entry = frontier.poll(); if (!visited.add(entry.edge)) continue; @@ -621,51 +674,19 @@ public class Navigation { TrackNode node1 = entry.node1; TrackNode node2 = entry.node2; - if (costRelevant) - penalty += penalties.getOrDefault(edge, 0); - - EdgeData signalData = edge.getEdgeData(); - if (signalData.hasPoints()) { - for (TrackEdgePoint point : signalData.getPoints()) { - if (node1 == initialNode1 && point.getLocationOn(edge) < edge.getLength() - distanceToNode2) - continue; - if (costRelevant && distance + penalty > maxCost) - continue Search; - if (!point.canNavigateVia(node2)) - continue Search; - if (point instanceof SignalBoundary signal) { - if (signal.isForcedRed(node2)) { - penalty += Train.Penalties.REDSTONE_RED_SIGNAL; - continue; + if (entry.hasDestination) { + EdgeData signalData = edge.getEdgeData(); + if (signalData.hasPoints()) { + for (TrackEdgePoint point : signalData.getPoints()) { + if (point instanceof GlobalStation station) { + if (station.canApproachFrom(node2) && stationTest.test(distance, penalty, reachedVia, + Pair.of(Couple.create(node1, node2), edge), station)) + return; } - UUID group = signal.getGroup(node2); - if (group == null) - continue; - SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(group); - if (signalEdgeGroup == null) - continue; - if (signalEdgeGroup.isOccupiedUnless(signal)) { - penalty += signalWeight; - signalWeight /= 2; - } - } - if (point instanceof GlobalStation station) { - Train presentTrain = station.getPresentTrain(); - boolean isOwnStation = presentTrain == train; - if (presentTrain != null && !isOwnStation) - penalty += Train.Penalties.STATION_WITH_TRAIN; - if (station.canApproachFrom(node2) && stationTest.test(distance, distance + penalty, reachedVia, - Pair.of(Couple.create(node1, node2), edge), station)) - return; - if (!isOwnStation) - penalty += Train.Penalties.STATION; } } } - if (costRelevant && distance + penalty > maxCost) - continue; - List> validTargets = new ArrayList<>(); Map connectionsFrom = graph.getConnectionsFrom(node2); for (Entry connection : connectionsFrom.entrySet()) { @@ -679,45 +700,100 @@ public class Navigation { if (validTargets.isEmpty()) continue; - for (Entry target : validTargets) { + Search: for (Entry target : validTargets) { if (!validTypes.contains(target.getValue().getTrackMaterial().trackType)) continue; TrackNode newNode = target.getKey(); TrackEdge newEdge = target.getValue(); - double newDistance = newEdge.getLength() + distance; - double remainingDist = 0; + int newPenalty = penalty; + double edgeLength = newEdge.getLength(); + double newDistance = distance + edgeLength; - if (destination != null) { - Vec3 newNodePosition = newNode.getLocation().getLocation(); - double dMin = Math.abs(newNodePosition.x - destinationNodePosition.x); - double dMid = Math.abs(newNodePosition.y - destinationNodePosition.y); - double dMax = Math.abs(newNodePosition.z - destinationNodePosition.z); - // Sort distance vector in ascending order - double temp; - if (dMin > dMid) { - temp = dMid; - dMid = dMin; - dMin = temp; + if (costRelevant) + newPenalty += penalties.getOrDefault(newEdge, 0); + + boolean hasDestination = false; + EdgeData signalData = newEdge.getEdgeData(); + if (signalData.hasPoints()) { + for (TrackEdgePoint point : signalData.getPoints()) { + if (node2 == initialNode1 && point.getLocationOn(newEdge) < edgeLength - distanceToNode2) + continue; + if (costRelevant && newDistance + newPenalty > maxCost) + continue Search; + if (!point.canNavigateVia(newNode)) + continue Search; + if (point instanceof SignalBoundary signal) { + if (signal.isForcedRed(newNode)) { + newPenalty += Train.Penalties.REDSTONE_RED_SIGNAL; + continue; + } + UUID group = signal.getGroup(newNode); + if (group == null) + continue; + SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(group); + if (signalEdgeGroup == null) + continue; + if (signalEdgeGroup.isOccupiedUnless(signal)) { + newPenalty += signalWeight; + signalWeight /= 2; + } + } + if (point instanceof GlobalStation station) { + Train presentTrain = station.getPresentTrain(); + boolean isOwnStation = presentTrain == train; + if (presentTrain != null && !isOwnStation) + newPenalty += Train.Penalties.STATION_WITH_TRAIN; + if (station.canApproachFrom(newNode) && stationTest.test(newDistance, newDistance + newPenalty, reachedVia, + Pair.of(Couple.create(node2, newNode), newEdge), station)) { + hasDestination = true; + continue; + } + if (!isOwnStation) + newPenalty += Train.Penalties.STATION; + } } - if (dMin > dMax) { - temp = dMax; - dMax = dMin; - dMin = temp; - } - if (dMid > dMax) { - temp = dMax; - dMax = dMid; - dMid = temp; - } - // Octile distance from newNode to station node - remainingDist = 0.317837245 * dMin + 0.414213562 * dMid + dMax; } - if (destination != null && Math.round(remainingDist) == 0) - remainingDist = -999999; // Ensure edges containing the station node get checked first + if (costRelevant && newDistance + newPenalty > maxCost) + continue; + + double remainingDist = 0; + + if (destinations != null && !destinations.isEmpty()) { + remainingDist = Double.MAX_VALUE; + Vec3 newNodePosition = newNode.getLocation().getLocation(); + for (GlobalStation destination : destinations) { + TrackNodeLocation destinationNode = destination.edgeLocation.getFirst(); + double dMin = Math.abs(newNodePosition.x - destinationNode.getLocation().x); + double dMid = Math.abs(newNodePosition.y - destinationNode.getLocation().y); + double dMax = Math.abs(newNodePosition.z - destinationNode.getLocation().z); + // Sort distance vector in ascending order + double temp; + if (dMin > dMid) { + temp = dMid; + dMid = dMin; + dMin = temp; + } + if (dMin > dMax) { + temp = dMax; + dMax = dMin; + dMin = temp; + } + if (dMid > dMax) { + temp = dMax; + dMax = dMid; + dMid = temp; + } + // Octile distance from newNode to station node + double currentRemaining = 0.317837245 * dMin + 0.414213562 * dMid + dMax + destination.position; + if (node2.getLocation().equals(destinationNode)) + currentRemaining -= newEdge.getLength() * 2; // Correct the distance estimator for station edge + remainingDist = Math.min(remainingDist, currentRemaining); + } + } reachedVia.putIfAbsent(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); - frontier.add(new FrontierEntry(newDistance, penalty, remainingDist, node2, newNode, newEdge)); + frontier.add(new FrontierEntry(newDistance, newPenalty, remainingDist, hasDestination, node2, newNode, newEdge)); } } } @@ -727,22 +803,25 @@ public class Navigation { double distance; int penalty; double remaining; + boolean hasDestination; TrackNode node1; TrackNode node2; TrackEdge edge; - public FrontierEntry(double distance, int penalty, TrackNode node1, TrackNode node2, TrackEdge edge) { + public FrontierEntry(double distance, int penalty, boolean hasDestination, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; this.remaining = 0; + this.hasDestination = hasDestination; this.node1 = node1; this.node2 = node2; this.edge = edge; } - public FrontierEntry(double distance, int penalty, double remaining, TrackNode node1, TrackNode node2, TrackEdge edge) { + public FrontierEntry(double distance, int penalty, double remaining, boolean hasDestination, TrackNode node1, TrackNode node2, TrackEdge edge) { this.distance = distance; this.penalty = penalty; this.remaining = remaining; + this.hasDestination = hasDestination; this.node1 = node1; this.node2 = node2; this.edge = edge; diff --git a/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java b/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java index 9b2be0aaa..398b261e1 100644 --- a/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java +++ b/src/main/java/com/simibubi/create/content/trains/graph/DiscoveredPath.java @@ -3,8 +3,6 @@ package com.simibubi.create.content.trains.graph; import com.simibubi.create.content.trains.station.GlobalStation; import com.simibubi.create.foundation.utility.Couple; -import org.openjdk.nashorn.internal.objects.Global; - import java.util.List; public class DiscoveredPath { diff --git a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java index c78056d7a..53fd20b38 100644 --- a/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/trains/schedule/ScheduleRuntime.java @@ -175,9 +175,8 @@ public class ScheduleRuntime { if (instruction instanceof DestinationInstruction destination) { String regex = destination.getFilterForRegex(); - DiscoveredPath best = null; - double bestCost = Double.MAX_VALUE; boolean anyMatch = false; + ArrayList validStations = new ArrayList<>(); if (!train.hasForwardConductor() && !train.hasBackwardConductor()) { train.status.missingConductor(); @@ -189,23 +188,9 @@ public class ScheduleRuntime { if (!globalStation.name.matches(regex)) continue; anyMatch = true; - boolean matchesCurrent = train.currentStation != null && train.currentStation.equals(globalStation.id); - double cost; - DiscoveredPath path = train.navigation.findPathTo(globalStation, bestCost); - if (matchesCurrent) { - cost = 0; - } else { - cost = path == null ? -1 : path.cost; - } - - if (cost < 0) - continue; - if (cost > bestCost) - continue; - best = path; - bestCost = cost; + validStations.add(globalStation); } - + DiscoveredPath best = train.navigation.findPathTo(validStations, Double.MAX_VALUE); if (best == null) { if (anyMatch) train.status.failedNavigation();