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
* @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);
}
@ -68,9 +68,9 @@ public class ModelRailwaySimulation {
* @param id identifier of the rail to remove
* @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
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
* @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);
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;
}
@ -101,7 +101,7 @@ public class ModelRailwaySimulation {
* @param newEngine the engine to add to the simulation
* @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();
if (Stream.concat(engines.stream(), trainSets.stream())
.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
* @throws InvalidInputException if user input is invalid (e.g. zero-sized coach)
*/
public int createCoach(final CoachType coachType, final int length,
final boolean couplingFront, final boolean couplingBack) throws InvalidInputException {
public int createCoach(CoachType coachType, int length,
boolean couplingFront, boolean couplingBack) throws InvalidInputException {
int id = getNextCoachIdentifier();
if (id < 0) {
return -1;
@ -187,7 +187,7 @@ public class ModelRailwaySimulation {
* @param newTrainSet the train set to add
* @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();
if (Stream.concat(engines.stream(), trainSets.stream())
.anyMatch(rollingStock -> rollingStock.getIdentifier().equals(id))) {
@ -216,7 +216,7 @@ public class ModelRailwaySimulation {
* @param id identifier of the rolling stock to remove
* @return whether the rolling was successfully removed
*/
public boolean deleteRollingStock(final String id) {
public boolean deleteRollingStock(String id) {
RollingStock rollingStock = getRollingStock(id);
if (trainManager.getTrainContainingRollingStock(rollingStock).isPresent()) {
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
* @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()) {
int coachId = Integer.parseInt(id.substring(1));
return coaches.get(coachId);
@ -290,7 +290,7 @@ public class ModelRailwaySimulation {
* @return whether the train was successfully placed
* @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 {
return trainManager.putTrain(trainId, position, direction);
}

View File

@ -2,6 +2,7 @@ package edu.kit.informatik.model;
import edu.kit.informatik.ui.InvalidInputException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
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.
* @param id identifier of the rail to remove
@ -236,7 +246,7 @@ public class RailwayNetwork {
* @param position the position to check
* @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);
}
@ -245,7 +255,7 @@ public class RailwayNetwork {
* @param position the position to check
* @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);
}

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,
* 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
*/
public boolean isOnRail(final int id) {
return isPlaced() && occupiedRails.stream().anyMatch(rail -> rail.getIdentifier() == id);
public boolean isOnRail(final Rail rail) {
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.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
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
* 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
*/
public boolean anyTrainOnRail(int id) {
return trains.values().stream().anyMatch(train -> train.isOnRail(id));
public boolean anyTrainOnRail(Rail rail) {
return rail != null && trains.values().stream().anyMatch(train -> train.isOnRail(rail));
}
/**
* 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) {
trains.values().stream().filter(train -> train.isOnRail(id)).forEach(Train::removeFromRails);
public void removeTrainsOnRail(Rail rail) {
trains.values().stream().filter(train -> train.isOnRail(rail)).forEach(Train::removeFromRails);
}
/**
@ -165,9 +166,7 @@ public final class TrainManager {
// attempt to place train
boolean placed = train.placeOn(railNetwork, position, direction);
// check for collisions
List<Set<Train>> collisions = new ArrayList<>();
getStaticCollisions(collisions);
if (placed && !collisions.isEmpty()) { // TODO: implement 2B
if (placed && !getPlacementCollisions().isEmpty()) {
train.removeFromRails();
return false;
} else {
@ -211,6 +210,11 @@ public final class TrainManager {
.filter(Train::isPlaced)
.filter(train -> train.getOccupiedRails().stream().anyMatch(occupiedRails::contains))
.forEach(collision::add);
if (!collision.isEmpty()) {
collision.add(train1);
addToSetOrAddNew(collisions, collision);
}
/*
if (!collision.isEmpty()) {
// check for existing collision
Set<Train> otherCollision = collisions.stream()
@ -224,6 +228,54 @@ public final class TrainManager {
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);
}
}