Implement model 2B

This commit is contained in:
Arne Keller 2020-03-02 23:29:59 +01:00
parent c11fadbb9d
commit 5267177f4e
4 changed files with 89 additions and 26 deletions

View File

@ -59,7 +59,7 @@ public class ModelRailwaySimulation {
* @return the positive identifier of the switch if successful, -1 otherwise * @return the positive identifier of the switch if successful, -1 otherwise
* @throws InvalidInputException if the new switch would be invalid * @throws InvalidInputException if the new switch would be invalid
*/ */
public int addSwitch(final Vector2D start, final Vector2D end1, final Vector2D end2) throws InvalidInputException { public int addSwitch(Vector2D start, Vector2D end1, Vector2D end2) throws InvalidInputException {
return railNetwork.addSwitch(start, end1, end2); return railNetwork.addSwitch(start, end1, end2);
} }
@ -68,9 +68,9 @@ public class ModelRailwaySimulation {
* @param id identifier of the rail to remove * @param id identifier of the rail to remove
* @return whether the rail could be successfully removed * @return whether the rail could be successfully removed
*/ */
public boolean removeRail(final int id) { public boolean removeRail(int id) {
// check whether any trains are on this rail // check whether any trains are on this rail
return !trainManager.anyTrainOnRail(id) && railNetwork.removeRail(id); return !trainManager.anyTrainOnRail(railNetwork.getRail(id)) && railNetwork.removeRail(id);
} }
/** /**
@ -89,10 +89,10 @@ public class ModelRailwaySimulation {
* @param position position to set the switch to * @param position position to set the switch to
* @return whether the switch could be set * @return whether the switch could be set
*/ */
public boolean setSwitch(final int id, final Vector2D position) { public boolean setSwitch(int id, Vector2D position) {
boolean success = railNetwork.setSwitch(id, position); boolean success = railNetwork.setSwitch(id, position);
if (success) { // derail trains on switch, explicitly not (!) printing any removed trains (source: forum post) if (success) { // derail trains on switch, explicitly not (!) printing any removed trains (source: forum post)
trainManager.removeTrainsOnRail(id); trainManager.removeTrainsOnRail(railNetwork.getRail(id));
} }
return success; return success;
} }
@ -101,7 +101,7 @@ public class ModelRailwaySimulation {
* @param newEngine the engine to add to the simulation * @param newEngine the engine to add to the simulation
* @throws InvalidInputException when the identifier is already in use * @throws InvalidInputException when the identifier is already in use
*/ */
public void createEngine(final Engine newEngine) throws InvalidInputException { public void createEngine(Engine newEngine) throws InvalidInputException {
String id = newEngine.getIdentifier(); String id = newEngine.getIdentifier();
if (Stream.concat(engines.stream(), trainSets.stream()) if (Stream.concat(engines.stream(), trainSets.stream())
.anyMatch(rollingStock -> rollingStock.getIdentifier().equals(id))) { .anyMatch(rollingStock -> rollingStock.getIdentifier().equals(id))) {
@ -134,8 +134,8 @@ public class ModelRailwaySimulation {
* @return the identifier of the coach if successfully added, -1 if none available * @return the identifier of the coach if successfully added, -1 if none available
* @throws InvalidInputException if user input is invalid (e.g. zero-sized coach) * @throws InvalidInputException if user input is invalid (e.g. zero-sized coach)
*/ */
public int createCoach(final CoachType coachType, final int length, public int createCoach(CoachType coachType, int length,
final boolean couplingFront, final boolean couplingBack) throws InvalidInputException { boolean couplingFront, boolean couplingBack) throws InvalidInputException {
int id = getNextCoachIdentifier(); int id = getNextCoachIdentifier();
if (id < 0) { if (id < 0) {
return -1; return -1;
@ -187,7 +187,7 @@ public class ModelRailwaySimulation {
* @param newTrainSet the train set to add * @param newTrainSet the train set to add
* @throws InvalidInputException if the identifier is already used * @throws InvalidInputException if the identifier is already used
*/ */
public void createTrainSet(final TrainSet newTrainSet) throws InvalidInputException { public void createTrainSet(TrainSet newTrainSet) throws InvalidInputException {
String id = newTrainSet.getIdentifier(); String id = newTrainSet.getIdentifier();
if (Stream.concat(engines.stream(), trainSets.stream()) if (Stream.concat(engines.stream(), trainSets.stream())
.anyMatch(rollingStock -> rollingStock.getIdentifier().equals(id))) { .anyMatch(rollingStock -> rollingStock.getIdentifier().equals(id))) {
@ -216,7 +216,7 @@ public class ModelRailwaySimulation {
* @param id identifier of the rolling stock to remove * @param id identifier of the rolling stock to remove
* @return whether the rolling was successfully removed * @return whether the rolling was successfully removed
*/ */
public boolean deleteRollingStock(final String id) { public boolean deleteRollingStock(String id) {
RollingStock rollingStock = getRollingStock(id); RollingStock rollingStock = getRollingStock(id);
if (trainManager.getTrainContainingRollingStock(rollingStock).isPresent()) { if (trainManager.getTrainContainingRollingStock(rollingStock).isPresent()) {
return false; // can not delete rolling stock in use return false; // can not delete rolling stock in use
@ -229,7 +229,7 @@ public class ModelRailwaySimulation {
* @param id identifier of the rolling stock to find * @param id identifier of the rolling stock to find
* @return the specified rolling stock, or null if not found * @return the specified rolling stock, or null if not found
*/ */
public RollingStock getRollingStock(final String id) { public RollingStock getRollingStock(String id) {
if (Coach.IDENTIFIER_PATTERN.matcher(id).matches()) { if (Coach.IDENTIFIER_PATTERN.matcher(id).matches()) {
int coachId = Integer.parseInt(id.substring(1)); int coachId = Integer.parseInt(id.substring(1));
return coaches.get(coachId); return coaches.get(coachId);
@ -290,7 +290,7 @@ public class ModelRailwaySimulation {
* @return whether the train was successfully placed * @return whether the train was successfully placed
* @throws InvalidInputException when the train is too long * @throws InvalidInputException when the train is too long
*/ */
public boolean putTrain(final int trainId, final Vector2D position, final Vector2D direction) public boolean putTrain(int trainId, Vector2D position, Vector2D direction)
throws InvalidInputException { throws InvalidInputException {
return trainManager.putTrain(trainId, position, direction); return trainManager.putTrain(trainId, position, direction);
} }

View File

@ -2,6 +2,7 @@ package edu.kit.informatik.model;
import edu.kit.informatik.ui.InvalidInputException; import edu.kit.informatik.ui.InvalidInputException;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -97,6 +98,15 @@ public class RailwayNetwork {
} }
} }
/**
* Get the rail with the specified identifier.
* @param id rail identifier to get
* @return the rail, or null if not found
*/
public Rail getRail(int id) {
return rails.get(id);
}
/** /**
* Remove the specified rail from the rail network. * Remove the specified rail from the rail network.
* @param id identifier of the rail to remove * @param id identifier of the rail to remove
@ -236,7 +246,7 @@ public class RailwayNetwork {
* @param position the position to check * @param position the position to check
* @return the rail that contains the position, null if none found * @return the rail that contains the position, null if none found
*/ */
public Rail findContainingRail(final Vector2D position) { public Rail findContainingRail(Vector2D position) {
return rails.values().stream().filter(rail -> rail.contains(position)).findFirst().orElse(null); return rails.values().stream().filter(rail -> rail.contains(position)).findFirst().orElse(null);
} }
@ -245,7 +255,7 @@ public class RailwayNetwork {
* @param position the position to check * @param position the position to check
* @return the rail(s) that touch this position * @return the rail(s) that touch this position
*/ */
public Rail[] findTouchingRails(final Vector2D position) { public Rail[] findTouchingRails(Vector2D position) {
return rails.values().stream().filter(rail -> rail.canConnectTo(position)).toArray(Rail[]::new); return rails.values().stream().filter(rail -> rail.canConnectTo(position)).toArray(Rail[]::new);
} }

View File

@ -126,11 +126,12 @@ public final class Train {
/** /**
* Check whether this train is on a particular rail. Note that at least one rolling stock has to be on the rail, * Check whether this train is on a particular rail. Note that at least one rolling stock has to be on the rail,
* just touching the rail is not enough. * just touching the rail is not enough.
* @param id identifier of a rail * @param rail the rail to check
* @return whether this train is on that rail * @return whether this train is on that rail
*/ */
public boolean isOnRail(final int id) { public boolean isOnRail(final Rail rail) {
return isPlaced() && occupiedRails.stream().anyMatch(rail -> rail.getIdentifier() == id); return isPlaced() && (occupiedRails.stream().anyMatch(blockedRail -> blockedRail == rail)
|| position.stream().anyMatch(rail::canConnectTo));
} }
/** /**

View File

@ -5,6 +5,7 @@ import edu.kit.informatik.ui.InvalidInputException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -47,19 +48,19 @@ public final class TrainManager {
* Check whether a train is on the rail with the specified identifier. Note that a train must be partially on that * Check whether a train is on the rail with the specified identifier. Note that a train must be partially on that
* rail, simply touching one of the end points is not enough. * rail, simply touching one of the end points is not enough.
* *
* @param id identfier of the rail to check * @param rail the rail to check
* @return whether a train is on that rail * @return whether a train is on that rail
*/ */
public boolean anyTrainOnRail(int id) { public boolean anyTrainOnRail(Rail rail) {
return trains.values().stream().anyMatch(train -> train.isOnRail(id)); return rail != null && trains.values().stream().anyMatch(train -> train.isOnRail(rail));
} }
/** /**
* Remove any trains on the rail with the specified identifier. * Remove any trains on the rail with the specified identifier.
* @param id identifier of the rail * @param rail rail to clear
*/ */
public void removeTrainsOnRail(int id) { public void removeTrainsOnRail(Rail rail) {
trains.values().stream().filter(train -> train.isOnRail(id)).forEach(Train::removeFromRails); trains.values().stream().filter(train -> train.isOnRail(rail)).forEach(Train::removeFromRails);
} }
/** /**
@ -165,9 +166,7 @@ public final class TrainManager {
// attempt to place train // attempt to place train
boolean placed = train.placeOn(railNetwork, position, direction); boolean placed = train.placeOn(railNetwork, position, direction);
// check for collisions // check for collisions
List<Set<Train>> collisions = new ArrayList<>(); if (placed && !getPlacementCollisions().isEmpty()) {
getStaticCollisions(collisions);
if (placed && !collisions.isEmpty()) { // TODO: implement 2B
train.removeFromRails(); train.removeFromRails();
return false; return false;
} else { } else {
@ -211,6 +210,11 @@ public final class TrainManager {
.filter(Train::isPlaced) .filter(Train::isPlaced)
.filter(train -> train.getOccupiedRails().stream().anyMatch(occupiedRails::contains)) .filter(train -> train.getOccupiedRails().stream().anyMatch(occupiedRails::contains))
.forEach(collision::add); .forEach(collision::add);
if (!collision.isEmpty()) {
collision.add(train1);
addToSetOrAddNew(collisions, collision);
}
/*
if (!collision.isEmpty()) { if (!collision.isEmpty()) {
// check for existing collision // check for existing collision
Set<Train> otherCollision = collisions.stream() Set<Train> otherCollision = collisions.stream()
@ -224,6 +228,54 @@ public final class TrainManager {
collisions.add(collision); collisions.add(collision);
} }
} }
*/
}
}
/**
* Implementation of the silly *second* collision checking algorithm.
* @return list of collisions
*/
private List<Set<Train>> getPlacementCollisions() {
List<Set<Train>> collisions = new ArrayList<>();
trains.values().stream().filter(Train::isPlaced).forEach(train1 -> {
trains.values().stream().filter(train -> train != train1).filter(Train::isPlaced).forEach(train2 -> {
Set<Rail> occupiedByTrain1 = train1.getOccupiedRails();
Collections.addAll(occupiedByTrain1, railNetwork.findTouchingRails(train1.getFrontPosition()));
Collections.addAll(occupiedByTrain1, railNetwork.findTouchingRails(train1.getRearPosition()));
Set<Rail> occupiedByTrain2 = train2.getOccupiedRails();
Collections.addAll(occupiedByTrain2, railNetwork.findTouchingRails(train2.getFrontPosition()));
Collections.addAll(occupiedByTrain2, railNetwork.findTouchingRails(train2.getRearPosition()));
occupiedByTrain2.retainAll(occupiedByTrain1);
if (!occupiedByTrain2.isEmpty()) {
Set<Train> collision = Stream.of(train1, train2).collect(Collectors.toSet());
addToSetOrAddNew(collisions, collision);
}
});
});
return collisions;
}
/**
* Add the set to an existing set (and merge sets), or add it to the list.
*/
private void addToSetOrAddNew(List<Set<Train>> setList, Set<Train> newSet) {
Set<Train> existing = null;
for (int i = 0; i < setList.size(); i++) {
Set<Train> otherSet = setList.get(i);
if (otherSet.stream().anyMatch(newSet::contains)) {
if (existing == null) {
existing = otherSet;
existing.addAll(newSet);
} else {
existing.addAll(otherSet);
setList.remove(i);
i--;
}
}
}
if (existing == null) {
setList.add(newSet);
} }
} }