diff --git a/src/edu/kit/informatik/model/Train.java b/src/edu/kit/informatik/model/Train.java index 4f62ed2..d18e536 100644 --- a/src/edu/kit/informatik/model/Train.java +++ b/src/edu/kit/informatik/model/Train.java @@ -256,10 +256,14 @@ public final class Train { * @param nextPosition position to move train to */ public void moveTo(final RailwayNetwork railNetwork, final Vector2D nextPosition) { - // make sure caller can not modiy internal state - position.addFirst(new Vector2D(nextPosition)); Vector2D last = position.removeLast(); - updateOccupiedRails(railNetwork, position.getFirst(), last, position.getLast()); + if (nextPosition != null) { + // not derailing + position.addFirst(new Vector2D(nextPosition)); + updateOccupiedRails(railNetwork, position.getFirst(), last, position.getLast()); + } else { + updateOccupiedRails(railNetwork, null, last, position.getLast()); + } } /** @@ -269,8 +273,14 @@ public final class Train { */ public void moveBackTo(final RailwayNetwork railNetwork, final Vector2D rearPosition) { Vector2D last = position.removeFirst(); - position.addLast(new Vector2D(rearPosition)); - updateOccupiedRails(railNetwork, position.getLast(), last, position.getFirst()); + if (rearPosition != null) { + // not derailing + position.addLast(new Vector2D(rearPosition)); + updateOccupiedRails(railNetwork, position.getLast(), last, position.getFirst()); + } else { + // derailing + updateOccupiedRails(railNetwork, null, last, position.getFirst()); + } } /** @@ -282,7 +292,7 @@ public final class Train { */ private void updateOccupiedRails(final RailwayNetwork railNetwork, final Vector2D nextPosition, final Vector2D lastPosition, final Vector2D secondToLastPosition) { - if (nextPosition.equals(secondToLastPosition)) { + if (secondToLastPosition.equals(nextPosition)) { // we are moving in a loop, no need to update return; } @@ -296,6 +306,10 @@ public final class Train { occupiedRails.clear(); } // else: only touched rail // update occupied rails, adding next rail + if (nextPosition == null) { + // derailing + return; + } Rail nextRail = railNetwork.findContainingRail(nextPosition); if (nextRail != null) { occupiedRails.add(nextRail); diff --git a/src/edu/kit/informatik/model/TrainManager.java b/src/edu/kit/informatik/model/TrainManager.java index a575325..a38c4c6 100644 --- a/src/edu/kit/informatik/model/TrainManager.java +++ b/src/edu/kit/informatik/model/TrainManager.java @@ -165,7 +165,9 @@ public final class TrainManager { // attempt to place train boolean placed = train.placeOn(railNetwork, position, direction); // check for collisions - if (placed && !getStaticCollisions().isEmpty()) { + List> collisions = new ArrayList<>(); + getStaticCollisions(collisions); + if (placed && !collisions.isEmpty()) { // TODO: implement 2B train.removeFromRails(); return false; } else { @@ -184,10 +186,9 @@ public final class TrainManager { /** * Get collisions of trains currently placed. - * @return list of collisions (never null, sometimes empty) + * @param collisions list of collisions (never null, sometimes empty) */ - private List> getStaticCollisions() { - List> collisions = new ArrayList<>(); + private void getStaticCollisions(List> collisions) { int maxId = trains.keySet().stream().max(Integer::compareTo).orElse(0); for (int id1 = 1; id1 <= maxId; id1++) { Train train1 = trains.get(id1); @@ -195,12 +196,21 @@ public final class TrainManager { continue; } HashSet collision = new HashSet<>(); + // check for same position IntStream.rangeClosed(id1 + 1, maxId) .mapToObj(trains::get) .filter(Objects::nonNull) .filter(Train::isPlaced) .filter(train1::touches) .forEach(collision::add); + // check for rail collisions + Set occupiedRails = train1.getOccupiedRails(); + IntStream.rangeClosed(id1 + 1, maxId) + .mapToObj(trains::get) + .filter(Objects::nonNull) + .filter(Train::isPlaced) + .filter(train -> train.getOccupiedRails().stream().anyMatch(occupiedRails::contains)) + .forEach(collision::add); if (!collision.isEmpty()) { // check for existing collision Set otherCollision = collisions.stream() @@ -215,7 +225,6 @@ public final class TrainManager { } } } - return collisions; } /** @@ -224,31 +233,21 @@ public final class TrainManager { */ private List> getCollisionsOfOneStep() { List> collisions = new ArrayList<>(); - Map> occupiedRails = trains.values().stream().filter(Train::isPlaced) - .collect(Collectors.toMap(Function.identity(), Train::getOccupiedRails)); - // perform step - Map> nextOccupiedRails = new HashMap<>(); trains.values().stream().filter(Train::isPlaced).forEach(train -> { Vector2D position = train.getFrontPosition(); Vector2D direction = train.getDirection(); Vector2D nextPosition = railNetwork.move(position, direction); if (nextPosition == null || train.isOnPosition(nextPosition) && !train.getRearPosition().equals(train.getFrontPosition())) { + train.moveTo(railNetwork, nextPosition); collisions.add(new HashSet<>(Arrays.asList(train))); - train.removeFromRails(); - // see https://ilias.studium.kit.edu/goto.php?target=frm_996924_138875&client_id=produktiv - if (train.getLength() > 1) { - nextOccupiedRails.put(train, occupiedRails.get(train)); - } else { - nextOccupiedRails.put(train, null); - } } else { train.moveTo(railNetwork, nextPosition); train.setDirection(direction); - nextOccupiedRails.put(train, train.getOccupiedRails()); } }); - checkForBlockCollisions(collisions, occupiedRails, nextOccupiedRails); + getStaticCollisions(collisions); + collisions.forEach(collision -> collision.forEach(Train::removeFromRails)); return collisions; } @@ -258,10 +257,7 @@ public final class TrainManager { */ private List> getCollisionsOfOneReverseStep() { List> collisions = new ArrayList<>(); - Map> occupiedRails = trains.values().stream().filter(Train::isPlaced) - .collect(Collectors.toMap(Function.identity(), Train::getOccupiedRails)); // perform step - Map> nextOccupiedRails = new HashMap<>(); trains.values().stream().filter(Train::isPlaced).forEach(train -> { Vector2D front = train.getFrontPosition(); Vector2D position = train.getRearPosition(); @@ -269,86 +265,18 @@ public final class TrainManager { Vector2D nextPosition = railNetwork.move(position, direction); if (nextPosition == null || train.isOnPosition(nextPosition) && !train.getRearPosition().equals(train.getFrontPosition())) { + train.moveBackTo(railNetwork, nextPosition); collisions.add(new HashSet<>(Arrays.asList(train))); - train.removeFromRails(); - // see https://ilias.studium.kit.edu/goto.php?target=frm_996924_138875&client_id=produktiv - if (train.getLength() > 1) { - nextOccupiedRails.put(train, occupiedRails.get(train)); - } else { - nextOccupiedRails.put(train, null); - } } else { train.moveBackTo(railNetwork, nextPosition); train.setDirection(front.subtract(train.getFrontPosition())); - nextOccupiedRails.put(train, train.getOccupiedRails()); } }); - checkForBlockCollisions(collisions, occupiedRails, nextOccupiedRails); + getStaticCollisions(collisions); + collisions.forEach(collision -> collision.forEach(Train::removeFromRails)); return collisions; } - /** - * Calculate train collisions, removing crashing trains in the process. - * - * @param collisions list of collisions to expand - * @param occupiedRails map of occupied rails before the step - * @param nextOccupiedRails map of occupied rails after the step - */ - private void checkForBlockCollisions(List> collisions, Map> occupiedRails, - Map> nextOccupiedRails) { - trains.values().stream() - // only process placed trains - .filter(Train::isPlaced) - // if already in a collision, do not process again - .filter(train -> collisions.stream().noneMatch(x -> x.contains(train))) - .forEach(train -> { - Set occupiedByThisTrain = nextOccupiedRails.get(train); - // check each other train - trains.values().stream().filter(x -> x != train).forEach(otherTrain -> { - Set occupiedByOtherTrainPreviously = occupiedRails.get(otherTrain); - Set occupiedByOtherTrain = nextOccupiedRails.get(otherTrain); - // if train did not occupy any rails (it is not placed, or just derailed with length 1) - if (occupiedByOtherTrain == null) { - // skip - return; - } - // first check if train is on the same rails - boolean anyIntersection = Stream.concat( - occupiedByOtherTrain.stream(), occupiedByOtherTrainPreviously.stream()) - // only search in currently occupied rails, previously occupied rails - // will be checked once the other train is processed in the outer loop - .anyMatch(occupiedByThisTrain::contains); - // or simply touches the other train - if (anyIntersection || train.touches(otherTrain)) { - // remove crashing trains - train.removeFromRails(); - otherTrain.removeFromRails(); - // try to find/merge existing collisions - Set existingCollision = null; - for (Set collision : collisions) { - if (collision.contains(otherTrain) || collision.contains(train)) { - if (existingCollision == null) { - existingCollision = collision; - collision.add(train); - collision.add(otherTrain); - } else { - existingCollision.addAll(collision); - existingCollision.add(train); - existingCollision.add(otherTrain); - collisions.remove(collision); - break; - } - } - } - if (existingCollision == null) { - // create a new collision - collisions.add(Stream.of(train, otherTrain).collect(Collectors.toSet())); - } - } - }); - }); - } - /** * Move the trains in this simulation, printing their new positions and any collisions. * @param speed amount of steps to move the trains diff --git a/testcoverage_input.txt b/testcoverage_input.txt index b06d5b4..c5fb251 100644 --- a/testcoverage_input.txt +++ b/testcoverage_input.txt @@ -82,7 +82,7 @@ put train 1 at (0,0) in direction 1,0 put train 3 at (20,0) in direction -1,0 put train 2 at (30,0) in direction -1,0 put train 5 at (15,0) in direction 1,0 -step 1 +step 41 add switch (40,0) -> (10,10),(40,40) add switch (40,0) -> (40,40),(10,10) add switch (40,0),(10,0),(0,10) @@ -92,7 +92,7 @@ create engine really good one pls list engines I own create coach with water slide list coaches with food -create train-set with electric chair +create train-set with silent doors create train-set W 15 1 true true list train-sets in this city delete rolling stock your mom diff --git a/testcoverage_output.txt b/testcoverage_output.txt index faf3ec6..d3f3089 100644 --- a/testcoverage_output.txt +++ b/testcoverage_output.txt @@ -106,7 +106,8 @@ OK OK OK OK -Crash of train 1,2,3,4,5 +Crash of train 1,3,4,5 +Crash of train 2 Error, start has to be connected in straight lines to end positions! Error, start has to be connected in straight lines to end positions! Error, invalid add switch argument syntax