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
*/
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);

View File

@ -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<Set<Train>> 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<HashSet<Train>> getStaticCollisions() {
List<HashSet<Train>> collisions = new ArrayList<>();
private void getStaticCollisions(List<Set<Train>> 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<Train> 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<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()) {
// check for existing collision
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() {
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 -> {
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<Set<Train>> getCollisionsOfOneReverseStep() {
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 -> {
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<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.
* @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 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

View File

@ -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