Implement model 1A

This commit is contained in:
Arne Keller 2020-02-29 08:26:24 +01:00
parent 68b28f3c37
commit 8f8baf6701
4 changed files with 44 additions and 101 deletions

View File

@ -256,10 +256,14 @@ public final class Train {
* @param nextPosition position to move train to * @param nextPosition position to move train to
*/ */
public void moveTo(final RailwayNetwork railNetwork, final Vector2D nextPosition) { 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(); 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) { public void moveBackTo(final RailwayNetwork railNetwork, final Vector2D rearPosition) {
Vector2D last = position.removeFirst(); Vector2D last = position.removeFirst();
position.addLast(new Vector2D(rearPosition)); if (rearPosition != null) {
updateOccupiedRails(railNetwork, position.getLast(), last, position.getFirst()); // 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, private void updateOccupiedRails(final RailwayNetwork railNetwork, final Vector2D nextPosition,
final Vector2D lastPosition, final Vector2D secondToLastPosition) { final Vector2D lastPosition, final Vector2D secondToLastPosition) {
if (nextPosition.equals(secondToLastPosition)) { if (secondToLastPosition.equals(nextPosition)) {
// we are moving in a loop, no need to update // we are moving in a loop, no need to update
return; return;
} }
@ -296,6 +306,10 @@ public final class Train {
occupiedRails.clear(); occupiedRails.clear();
} // else: only touched rail } // else: only touched rail
// update occupied rails, adding next rail // update occupied rails, adding next rail
if (nextPosition == null) {
// derailing
return;
}
Rail nextRail = railNetwork.findContainingRail(nextPosition); Rail nextRail = railNetwork.findContainingRail(nextPosition);
if (nextRail != null) { if (nextRail != null) {
occupiedRails.add(nextRail); occupiedRails.add(nextRail);

View File

@ -165,7 +165,9 @@ 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
if (placed && !getStaticCollisions().isEmpty()) { List<Set<Train>> collisions = new ArrayList<>();
getStaticCollisions(collisions);
if (placed && !collisions.isEmpty()) { // TODO: implement 2B
train.removeFromRails(); train.removeFromRails();
return false; return false;
} else { } else {
@ -184,10 +186,9 @@ public final class TrainManager {
/** /**
* Get collisions of trains currently placed. * 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<HashSet<Train>> getStaticCollisions() { private void getStaticCollisions(List<Set<Train>> collisions) {
List<HashSet<Train>> collisions = new ArrayList<>();
int maxId = trains.keySet().stream().max(Integer::compareTo).orElse(0); int maxId = trains.keySet().stream().max(Integer::compareTo).orElse(0);
for (int id1 = 1; id1 <= maxId; id1++) { for (int id1 = 1; id1 <= maxId; id1++) {
Train train1 = trains.get(id1); Train train1 = trains.get(id1);
@ -195,12 +196,21 @@ public final class TrainManager {
continue; continue;
} }
HashSet<Train> collision = new HashSet<>(); HashSet<Train> collision = new HashSet<>();
// check for same position
IntStream.rangeClosed(id1 + 1, maxId) IntStream.rangeClosed(id1 + 1, maxId)
.mapToObj(trains::get) .mapToObj(trains::get)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.filter(Train::isPlaced) .filter(Train::isPlaced)
.filter(train1::touches) .filter(train1::touches)
.forEach(collision::add); .forEach(collision::add);
// check for rail collisions
Set<Rail> 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()) { if (!collision.isEmpty()) {
// check for existing collision // check for existing collision
Set<Train> otherCollision = collisions.stream() Set<Train> otherCollision = collisions.stream()
@ -215,7 +225,6 @@ public final class TrainManager {
} }
} }
} }
return collisions;
} }
/** /**
@ -224,31 +233,21 @@ public final class TrainManager {
*/ */
private List<Set<Train>> getCollisionsOfOneStep() { private List<Set<Train>> getCollisionsOfOneStep() {
List<Set<Train>> collisions = new ArrayList<>(); List<Set<Train>> collisions = new ArrayList<>();
Map<Train, Set<Rail>> occupiedRails = trains.values().stream().filter(Train::isPlaced)
.collect(Collectors.toMap(Function.identity(), Train::getOccupiedRails));
// perform step
Map<Train, Set<Rail>> nextOccupiedRails = new HashMap<>();
trains.values().stream().filter(Train::isPlaced).forEach(train -> { trains.values().stream().filter(Train::isPlaced).forEach(train -> {
Vector2D position = train.getFrontPosition(); Vector2D position = train.getFrontPosition();
Vector2D direction = train.getDirection(); Vector2D direction = train.getDirection();
Vector2D nextPosition = railNetwork.move(position, direction); Vector2D nextPosition = railNetwork.move(position, direction);
if (nextPosition == null if (nextPosition == null
|| train.isOnPosition(nextPosition) && !train.getRearPosition().equals(train.getFrontPosition())) { || train.isOnPosition(nextPosition) && !train.getRearPosition().equals(train.getFrontPosition())) {
train.moveTo(railNetwork, nextPosition);
collisions.add(new HashSet<>(Arrays.asList(train))); 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 { } else {
train.moveTo(railNetwork, nextPosition); train.moveTo(railNetwork, nextPosition);
train.setDirection(direction); train.setDirection(direction);
nextOccupiedRails.put(train, train.getOccupiedRails());
} }
}); });
checkForBlockCollisions(collisions, occupiedRails, nextOccupiedRails); getStaticCollisions(collisions);
collisions.forEach(collision -> collision.forEach(Train::removeFromRails));
return collisions; return collisions;
} }
@ -258,10 +257,7 @@ public final class TrainManager {
*/ */
private List<Set<Train>> getCollisionsOfOneReverseStep() { private List<Set<Train>> getCollisionsOfOneReverseStep() {
List<Set<Train>> collisions = new ArrayList<>(); List<Set<Train>> collisions = new ArrayList<>();
Map<Train, Set<Rail>> occupiedRails = trains.values().stream().filter(Train::isPlaced)
.collect(Collectors.toMap(Function.identity(), Train::getOccupiedRails));
// perform step // perform step
Map<Train, Set<Rail>> nextOccupiedRails = new HashMap<>();
trains.values().stream().filter(Train::isPlaced).forEach(train -> { trains.values().stream().filter(Train::isPlaced).forEach(train -> {
Vector2D front = train.getFrontPosition(); Vector2D front = train.getFrontPosition();
Vector2D position = train.getRearPosition(); Vector2D position = train.getRearPosition();
@ -269,86 +265,18 @@ public final class TrainManager {
Vector2D nextPosition = railNetwork.move(position, direction); Vector2D nextPosition = railNetwork.move(position, direction);
if (nextPosition == null if (nextPosition == null
|| train.isOnPosition(nextPosition) && !train.getRearPosition().equals(train.getFrontPosition())) { || train.isOnPosition(nextPosition) && !train.getRearPosition().equals(train.getFrontPosition())) {
train.moveBackTo(railNetwork, nextPosition);
collisions.add(new HashSet<>(Arrays.asList(train))); 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 { } else {
train.moveBackTo(railNetwork, nextPosition); train.moveBackTo(railNetwork, nextPosition);
train.setDirection(front.subtract(train.getFrontPosition())); 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; 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<Set<Train>> collisions, Map<Train, Set<Rail>> occupiedRails,
Map<Train, Set<Rail>> 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<Rail> occupiedByThisTrain = nextOccupiedRails.get(train);
// check each other train
trains.values().stream().filter(x -> x != train).forEach(otherTrain -> {
Set<Rail> occupiedByOtherTrainPreviously = occupiedRails.get(otherTrain);
Set<Rail> 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<Train> existingCollision = null;
for (Set<Train> 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. * Move the trains in this simulation, printing their new positions and any collisions.
* @param speed amount of steps to move the trains * @param speed amount of steps to move the trains

View File

@ -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 3 at (20,0) in direction -1,0
put train 2 at (30,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 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) -> (10,10),(40,40)
add switch (40,0) -> (40,40),(10,10) add switch (40,0) -> (40,40),(10,10)
add switch (40,0),(10,0),(0,10) add switch (40,0),(10,0),(0,10)
@ -92,7 +92,7 @@ create engine really good one pls
list engines I own list engines I own
create coach with water slide create coach with water slide
list coaches with food list coaches with food
create train-set with electric chair create train-set with silent doors
create train-set W 15 1 true true create train-set W 15 1 true true
list train-sets in this city list train-sets in this city
delete rolling stock your mom delete rolling stock your mom

View File

@ -106,7 +106,8 @@ OK
OK OK
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, 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 Error, invalid add switch argument syntax