Optimize large train placement

This commit is contained in:
Arne Keller 2020-03-04 22:22:05 +01:00
parent 70a070d9b1
commit 4fad0a377e
12 changed files with 184 additions and 150 deletions

View File

@ -3,6 +3,7 @@ add track (0,0) -> (-2147483648,0)
list tracks list tracks
create engine electrical T3 Marie 1 false true create engine electrical T3 Marie 1 false true
add train 1 T3-Marie add train 1 T3-Marie
put train 1 at (0,0) in direction 0,-1
put train 1 at (0,0) in direction -1,0 put train 1 at (0,0) in direction -1,0
step 1000 step 1000
delete train 1 delete train 1

View File

@ -4,6 +4,7 @@ t 1 (2147483647,0) -> (0,0) 2147483647
t 2 (0,0) -> (-2147483648,0) 2147483648 t 2 (0,0) -> (-2147483648,0) 2147483648
T3-Marie T3-Marie
electrical engine T3-Marie added to train 1 electrical engine T3-Marie added to train 1
Error, could not place train
OK OK
Train 1 at (-1000,0) Train 1 at (-1000,0)
OK OK

13
long_train_input.txt Normal file
View File

@ -0,0 +1,13 @@
add track (2147483647,0) -> (0,0)
add track (0,0) -> (-2147483648,0)
list tracks
create engine electrical T3 Marie 2147483647 false true
create coach passenger 2147483647 true false
add train 1 T3-Marie
add train 1 W1
put train 1 at (-2147483647,0) in direction -1,0
step 1
step 1
put train 1 at (-2147483647,0) in direction -1,0
step -1
exit

13
long_train_output.txt Normal file
View File

@ -0,0 +1,13 @@
1
2
t 1 (2147483647,0) -> (0,0) 2147483647
t 2 (0,0) -> (-2147483648,0) 2147483648
T3-Marie
1
electrical engine T3-Marie added to train 1
passenger coach W1 added to train 1
OK
Train 1 at (-2147483648,0)
Crash of train 1
OK
Crash of train 1

View File

@ -113,6 +113,11 @@ class MainTest {
cmpInOut("newest_ilias_shit_input.txt", "newest_ilias_shit_output.txt"); cmpInOut("newest_ilias_shit_input.txt", "newest_ilias_shit_output.txt");
} }
@Test
void long_train() throws IOException {
cmpInOut("long_train_input.txt", "long_train_output.txt");
}
private void cmpInOut(String in, String out) throws IOException { private void cmpInOut(String in, String out) throws IOException {
System.setIn(new ByteArrayInputStream(readFile(in))); System.setIn(new ByteArrayInputStream(readFile(in)));
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();

View File

@ -87,11 +87,20 @@ public abstract class Rail {
public abstract boolean allowsDirectionFrom(Vector2D position, Vector2D direction); public abstract boolean allowsDirectionFrom(Vector2D position, Vector2D direction);
/** /**
* Move a position along this rail in the specified direction for the specified amount of steps. This method will
* stop at the end of the rail, even if that will omit some steps.
* *
* @param position * @param position position to move from
* @param direction * @param direction direction to go
* @param steps * @param steps amount of steps
* @return * @return moved position
*/ */
public abstract Vector2D move(Vector2D position, Vector2D direction, long steps); public abstract Vector2D move(Vector2D position, Vector2D direction, long steps);
/**
* Calculate the (Manhattan) length of this rail.
*
* @return the length of this rail
*/
public abstract long getLength();
} }

View File

@ -1,14 +1,13 @@
package edu.kit.informatik.model; package edu.kit.informatik.model;
import edu.kit.informatik.Terminal;
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;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -198,20 +197,22 @@ public class RailwayNetwork {
/** /**
* Move a position along the rails of this rail network in the specified direction. * Move a position along the rails of this rail network in the specified direction.
*
* @param position the position to move * @param position the position to move
* @param direction has to be (0,1) (0,-1) (1,0) (-1,0), will be modified if turn is necessary * @param direction has to be (0,1) (0,-1) (1,0) (-1,0), will be modified if turn is necessary
* @return next position, null if off the rails * @param steps amount of steps to go
* @return next position, null if off the rails after single step
*/ */
public Vector2D move(Vector2D position, Vector2D direction) { public Vector2D move(Vector2D position, Vector2D direction, long steps) {
Rail containingRail = findContainingRail(position); Rail containingRail = findContainingRail(position);
if (containingRail != null) { if (containingRail != null) {
return position.add(direction); return containingRail.move(position, direction, steps);
} }
Rail[] touchingRails = findTouchingRails(position); Rail[] touchingRails = findTouchingRails(position);
Vector2D nextPossiblePosition = position.add(direction); Vector2D nextPossiblePosition = position.add(direction);
Rail possibleContainingRail = findRailPotentiallyContaining(nextPossiblePosition); Rail possibleContainingRail = findRailPotentiallyContaining(nextPossiblePosition);
if (possibleContainingRail != null && !possibleContainingRail.contains(position.subtract(direction))) { if (possibleContainingRail != null && !possibleContainingRail.contains(position.subtract(direction))) {
return possibleContainingRail.move(position, direction, 1); return possibleContainingRail.move(position, direction, steps);
} }
if (touchingRails.length == 0) { if (touchingRails.length == 0) {
@ -219,8 +220,8 @@ public class RailwayNetwork {
} else if (touchingRails.length == 1) { } else if (touchingRails.length == 1) {
return touchingRails[0].move(position, direction, 1); return touchingRails[0].move(position, direction, 1);
} }
Vector2D onRailOne = touchingRails[0].move(position, new Vector2D(direction), 1); Vector2D onRailOne = touchingRails[0].move(position, new Vector2D(direction), steps);
Vector2D onRailTwo = touchingRails[1].move(position, new Vector2D(direction), 1); Vector2D onRailTwo = touchingRails[1].move(position, new Vector2D(direction), steps);
if (position.equals(onRailOne) || onRailOne == null) { if (position.equals(onRailOne) || onRailOne == null) {
// we are moving on rail two // we are moving on rail two
Vector2D newDirection = touchingRails[1].getDirectionFrom(position); Vector2D newDirection = touchingRails[1].getDirectionFrom(position);
@ -251,6 +252,20 @@ public class RailwayNetwork {
return rails.values().stream().filter(rail -> rail.canContain(position)).findFirst().orElse(null); return rails.values().stream().filter(rail -> rail.canContain(position)).findFirst().orElse(null);
} }
/**
* Find a rail that connects to or contains the first position and connects to or contains the second position.
*
* @param positionA point which the rail should touch or connect to
* @param positionB point which the rail should touch or connect to
* @return rail satisfying these conditions
*/
public Optional<Rail> findRail(Vector2D positionA, Vector2D positionB) {
return rails.values().stream()
.filter(rail -> rail.connectsTo(positionA) || rail.contains(positionA))
.filter(rail -> rail.connectsTo(positionB) || rail.contains(positionB))
.findFirst();
}
/** /**
* Find all rails that *touch* (not contain) this position. * Find all rails that *touch* (not contain) this position.
* @param position the position to check * @param position the position to check

View File

@ -2,7 +2,6 @@ package edu.kit.informatik.model;
import edu.kit.informatik.ui.InvalidInputException; import edu.kit.informatik.ui.InvalidInputException;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
/** /**
@ -108,20 +107,6 @@ public final class Switch extends Rail {
return selection.getDirectionFrom(position); return selection.getDirectionFrom(position);
} }
private Vector2D[] getPossibleDirectionsFrom(Vector2D position) {
if (positionOne.canConnectTo(position) && positionTwo.canConnectTo(position)) {
return new Vector2D[] {
positionOne.getDirectionFrom(position), positionOne.getDirectionFrom(position).negated(),
positionTwo.getDirectionFrom(position), positionTwo.getDirectionFrom(position).negated()};
} else if (positionOne.canConnectTo(position)) {
return new Vector2D[] {positionOne.getDirectionFrom(position)};
} else if (positionTwo.canConnectTo(position)) {
return new Vector2D[] {positionTwo.getDirectionFrom(position)};
} else {
return new Vector2D[0];
}
}
@Override @Override
public boolean allowsDirectionFrom(Vector2D position, Vector2D direction) { public boolean allowsDirectionFrom(Vector2D position, Vector2D direction) {
return (selection != null && selection.contains(position)) return (selection != null && selection.contains(position))
@ -129,6 +114,11 @@ public final class Switch extends Rail {
|| (positionTwo.connectsTo(position) && positionTwo.allowsDirectionFrom(position, direction)); || (positionTwo.connectsTo(position) && positionTwo.allowsDirectionFrom(position, direction));
} }
@Override
public long getLength() {
return selection.getLength();
}
@Override @Override
public String toString() { public String toString() {
if (selection == null) { if (selection == null) {
@ -136,8 +126,7 @@ public final class Switch extends Rail {
positionOne.getEnd(), positionTwo.getEnd()); positionOne.getEnd(), positionTwo.getEnd());
} else { } else {
return String.format("s %d %s -> %s,%s %d", getIdentifier(), return String.format("s %d %s -> %s,%s %d", getIdentifier(),
positionOne.getStart(), positionOne.getEnd(), positionTwo.getEnd(), positionOne.getStart(), positionOne.getEnd(), positionTwo.getEnd(), getLength());
selection.getLength());
} }
} }

View File

@ -58,6 +58,9 @@ public final class Track extends Rail {
@Override @Override
public boolean contains(Vector2D position) { public boolean contains(Vector2D position) {
if (position == null) {
return false;
}
if (start.getX() == end.getX() && position.getX() == start.getX()) { if (start.getX() == end.getX() && position.getX() == start.getX()) {
int startY = start.getY(); int startY = start.getY();
int endY = end.getY(); int endY = end.getY();
@ -130,6 +133,7 @@ public final class Track extends Rail {
return new Vector2D(end); return new Vector2D(end);
} }
@Override
public long getLength() { public long getLength() {
return start.distanceTo(end); return start.distanceTo(end);
} }

View File

@ -9,17 +9,16 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* A train consisting of one or more rolling stock. Can be placed on a rail network. * A train consisting of one or more rolling stock. Can be placed on a rail network.
* *
* @author Arne Keller * @author Arne Keller
* @version 1.0 * @version 1.1
*/ */
public final class Train { public final class Train {
/** /**
* Numerical identifer of this train. * Numerical identifier of this train.
*/ */
private final int identifier; private final int identifier;
/** /**
@ -27,10 +26,13 @@ public final class Train {
*/ */
private final List<RollingStock> rollingStocks = new ArrayList<>(); private final List<RollingStock> rollingStocks = new ArrayList<>();
/** /**
* List of positions this train touches. This is a linked list because we only have to update the first and last * List of positions this train occupies. More specifically, it only contains:
* positions when moving the train. * the position of the front of the train, any positions on rail connections, the position of the back of the train
*
* This is a linked list because we only have to update
* the positions at the start and end of the list when moving the train.
*/ */
private LinkedList<Vector2D> position; private LinkedList<Vector2D> positions;
/** /**
* Set of rails this train occupies. * Set of rails this train occupies.
*/ */
@ -127,33 +129,29 @@ public final class Train {
*/ */
public boolean isOnRail(final Rail rail) { public boolean isOnRail(final Rail rail) {
return isPlaced() && (occupiedRails.stream().anyMatch(blockedRail -> blockedRail == rail) return isPlaced() && (occupiedRails.stream().anyMatch(blockedRail -> blockedRail == rail)
|| position.stream().anyMatch(rail::canConnectTo)); || positions.stream().anyMatch(rail::canConnectTo));
} }
/** /**
* Put a train on the rail network. * Put a train on the rail network.
*
* @param railNetwork rail network to place the train on * @param railNetwork rail network to place the train on
* @param position where to place the train * @param position where to place the train
* @param rawDirection direction in which the train should initially go * @param rawDirection direction in which the train should initially go
* @return whether the train was successfully placed * @return whether the train was successfully placed
* @throws InvalidInputException when the train is too long to place
*/ */
public boolean placeOn(RailwayNetwork railNetwork, Vector2D position, Vector2D rawDirection) public boolean placeOn(RailwayNetwork railNetwork, Vector2D position, Vector2D rawDirection) {
throws InvalidInputException { if (isPlaced()) {
Vector2D direction = rawDirection.normalized(); return false;
Vector2D positioningDirection = direction.negated(); }
final Vector2D direction = rawDirection.normalized();
final Vector2D positioningDirection = direction.negated();
// size of train that still needs to be placed
long length = getLength(); long length = getLength();
if (length > Integer.MAX_VALUE) {
// TODO: implement this case
throw new InvalidInputException("train length is bigger than 32-bit integer!");
}
LinkedList<Vector2D> positions = new LinkedList<>(); final LinkedList<Vector2D> positionList = new LinkedList<>();
positions.addLast(position); positionList.addLast(position);
Vector2D rollingStockPosition = position; Vector2D rollingStockPosition = position;
if (length > 10000) {
return false; // TODO: remove!
}
// check that the requested direction is equal to the direction of one the tracks // check that the requested direction is equal to the direction of one the tracks
if ((railNetwork.findContainingRail(position) != null if ((railNetwork.findContainingRail(position) != null
&& !railNetwork.findContainingRail(position).allowsDirectionFrom(position, positioningDirection)) && !railNetwork.findContainingRail(position).allowsDirectionFrom(position, positioningDirection))
@ -162,37 +160,37 @@ public final class Train {
.noneMatch(rail -> rail.allowsDirectionFrom(position, direction)))) { .noneMatch(rail -> rail.allowsDirectionFrom(position, direction)))) {
return false; return false;
} }
if (railNetwork.findTouchingRails(position).length > 0 final Set<Rail> occupiedRailsSet = new HashSet<>();
&& (Arrays.stream(railNetwork.findTouchingRails(position)) while (length > 0) {
.noneMatch(rail -> rail.connectsTo(position)))) { rollingStockPosition = railNetwork.move(rollingStockPosition, positioningDirection, length);
if (rollingStockPosition == null) {
// derailed
return false; return false;
} }
// consider fuzz 9 (0,-1) final long distanceToLastPosition = positionList.getLast().distanceTo(rollingStockPosition);
for (int i = 1; i <= length; i++) { if (distanceToLastPosition == 0) {
rollingStockPosition = railNetwork.move(rollingStockPosition, positioningDirection);
if (positions.getLast().distanceTo(rollingStockPosition) == 0) {
// stuck // stuck
return false; return false;
} }
if (rollingStockPosition == null
|| positions.contains(rollingStockPosition) && !positions.get(0).equals(rollingStockPosition)) { // successfully placed part of the train
length -= distanceToLastPosition;
final Rail occupiedRail = railNetwork.findRail(positionList.getLast(), rollingStockPosition).get();
if (occupiedRailsSet.contains(occupiedRail) && positionList.size() >= 4) {
// perhaps a self intersection
final long occupiedAtFrontOfTrain = positionList.get(0).distanceTo(positionList.get(1));
final long occupiedAtBackOfTrain = positionList.getLast().distanceTo(positionList.get(positionList.size() - 2));
if (occupiedAtFrontOfTrain + occupiedAtBackOfTrain > occupiedRail.getLength()) {
// train definitely intersects itself
return false; return false;
} }
positions.addLast(rollingStockPosition); }
occupiedRailsSet.add(occupiedRail);
positionList.addLast(rollingStockPosition);
} }
Set<Rail> occupiedRailsSet = positions.stream().flatMap(point -> { this.positions = positionList;
Rail containingRail = railNetwork.findContainingRail(point);
if (containingRail != null) {
return Stream.of(containingRail);
} else {
return Arrays.stream(railNetwork.findTouchingRails(point))
// ONLY add rails we fully occupy
.filter(rail -> positions.stream().filter(rail::connectsTo).count() == 2);
}
}).collect(Collectors.toSet());
this.position = positions;
this.occupiedRails = occupiedRailsSet; this.occupiedRails = occupiedRailsSet;
return true; return true;
@ -202,7 +200,7 @@ public final class Train {
* @return whether this train is placed on a rail network * @return whether this train is placed on a rail network
*/ */
public boolean isPlaced() { public boolean isPlaced() {
return position != null && occupiedRails != null; return positions != null && occupiedRails != null;
} }
/** /**
@ -222,7 +220,7 @@ public final class Train {
*/ */
public Vector2D getFrontPosition() { public Vector2D getFrontPosition() {
// make sure caller can not modify internal data // make sure caller can not modify internal data
return position != null ? new Vector2D(position.getFirst()) : null; return positions != null ? new Vector2D(positions.getFirst()) : null;
} }
/** /**
@ -230,22 +228,21 @@ public final class Train {
*/ */
public Vector2D getRearPosition() { public Vector2D getRearPosition() {
// make sure caller can not modify internal data // make sure caller can not modify internal data
return position != null ? new Vector2D(position.getLast()) : null; return positions != null ? new Vector2D(positions.getLast()) : null;
} }
/** /**
* @return the direction of this train * @return the direction of this train
*/ */
public Vector2D getDirection() { public Vector2D getDirection() {
// make sure caller can not modify internal state return positions.get(0).subtract(positions.get(1)).normalized();
return position.get(0).subtract(position.get(1));
} }
/** /**
* @return the rear direction of this train * @return the rear direction of this train
*/ */
public Vector2D getRearDirection() { public Vector2D getRearDirection() {
return position.getLast().subtract(position.get(position.size() - 2)); // TODO: consider longer trains return positions.getLast().subtract(positions.get(positions.size() - 2)).normalized();
} }
/** /**
@ -263,15 +260,25 @@ public final class Train {
* @param nextPosition position to move train to * @param nextPosition position to move train to
*/ */
public void moveTo(RailwayNetwork railNetwork, Vector2D nextPosition) { public void moveTo(RailwayNetwork railNetwork, Vector2D nextPosition) {
Vector2D last = position.removeLast(); final Rail railUnderBackOfTrain = railNetwork.findContainingRail(positions.getLast());
positions.getLast().subtractInPlace(getRearDirection());
if (positions.getLast().equals(positions.get(positions.size() - 2))) {
if (railNetwork.findContainingRail(nextPosition) != railUnderBackOfTrain
|| nextPosition == null) {
occupiedRails.remove(railUnderBackOfTrain);
}
positions.removeLast();
}
if (nextPosition != null) { if (nextPosition != null) {
// not derailing // not derailing
position.addFirst(new Vector2D(nextPosition)); positions.addFirst(new Vector2D(nextPosition));
updateOccupiedRails(railNetwork, position.getFirst(), last, position.getLast()); if (positions.size() >= 3 && nextPosition.subtract(positions.get(1)).normalized()
} else { .equals(positions.get(1).subtract(positions.get(2)).normalized())
// derailing && railNetwork.findTouchingRails(positions.get(1)).length == 0) {
updateOccupiedRails(railNetwork, null, last, position.getLast()); positions.remove(1);
} }
occupiedRails.add(railNetwork.findRail(positions.get(1), nextPosition).get());
} // else: derailing
} }
/** /**
@ -279,66 +286,35 @@ public final class Train {
* derailing. * derailing.
* *
* @param railNetwork rail network to move train on * @param railNetwork rail network to move train on
* @param rearPosition position to move rear of the train to * @param backPosition position to move back of the train to
*/ */
public void moveBackTo(final RailwayNetwork railNetwork, final Vector2D rearPosition) { public void moveBackTo(RailwayNetwork railNetwork, Vector2D backPosition) {
Vector2D last = position.removeFirst(); final Rail railUnderFrontOfTrain = railNetwork.findContainingRail(positions.getFirst());
if (rearPosition != null) { positions.getFirst().subtractInPlace(getDirection());
if (positions.getFirst().equals(positions.get(1))) {
if (railNetwork.findContainingRail(backPosition) != railUnderFrontOfTrain
|| backPosition == null) {
occupiedRails.remove(railUnderFrontOfTrain);
}
positions.removeFirst();
}
if (backPosition != null) {
// not derailing // not derailing
position.addLast(new Vector2D(rearPosition)); positions.addLast(new Vector2D(backPosition));
updateOccupiedRails(railNetwork, position.getLast(), last, position.getFirst()); if (positions.size() >= 3 && backPosition.subtract(positions.get(positions.size() - 2)).normalized()
} else { .equals(positions.get(positions.size() - 2).subtract(positions.get(positions.size() - 3)).normalized())
// derailing && railNetwork.findTouchingRails(positions.get(positions.size() - 2)).length == 0) {
updateOccupiedRails(railNetwork, null, last, position.getFirst()); positions.remove(positions.size() - 2);
} }
} occupiedRails.add(railNetwork.findRail(positions.get(positions.size() - 2), backPosition).get());
} // else: derailing
/**
* Update set of occupied rails after modifying position.
*
* @param railNetwork rail network to use
* @param nextPosition where the train just moved (null if derailing)
* @param lastPosition where the train just was
* @param secondToLastPosition where the end of the train is now
*/
private void updateOccupiedRails(final RailwayNetwork railNetwork, final Vector2D nextPosition,
final Vector2D lastPosition, final Vector2D secondToLastPosition) {
if (secondToLastPosition.equals(nextPosition)) {
// we are moving in a loop, no need to update
return;
}
// update occupied blocks, first removing the rail block left behind
Rail leavingRail = railNetwork.findContainingRail(lastPosition);
if (leavingRail != null && !leavingRail.contains(secondToLastPosition)) {
// we are leaving this rail
occupiedRails.remove(leavingRail);
} else if (getLength() == 1 && railNetwork.findContainingRail(secondToLastPosition) == null) {
// we evidently just moved into another rail
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);
} else if (getLength() == 1 && railNetwork.findContainingRail(secondToLastPosition) == null) {
// we evidently just moved into another rail
Rail[] nextTouchingRails = railNetwork.findTouchingRails(nextPosition);
Rail[] alreadyTouchingRails = railNetwork.findTouchingRails(secondToLastPosition);
Arrays.stream(nextTouchingRails)
.filter(rail -> Arrays.stream(alreadyTouchingRails).anyMatch(rail2 -> rail == rail2))
.forEach(occupiedRails::add);
} // else: only touching new rail
} }
/** /**
* Remove this train from the rail network. * Remove this train from the rail network.
*/ */
public void removeFromRails() { public void removeFromRails() {
position = null; positions = null;
occupiedRails = null; occupiedRails = null;
} }
@ -348,7 +324,7 @@ public final class Train {
* @return whether this train touches the other train * @return whether this train touches the other train
*/ */
public boolean touches(final Train other) { public boolean touches(final Train other) {
return position != null && other.isOnAnyPosition(position); return positions != null && other.isOnAnyPosition(positions);
} }
/** /**
@ -358,7 +334,7 @@ public final class Train {
* @return whether this train is on the specified position * @return whether this train is on the specified position
*/ */
public boolean isOnPosition(final Vector2D point) { public boolean isOnPosition(final Vector2D point) {
return position.contains(point); return positions.contains(point);
} }
/** /**
@ -368,7 +344,7 @@ public final class Train {
* @return whether this train is on any of the specified positions * @return whether this train is on any of the specified positions
*/ */
public boolean isOnAnyPosition(List<Vector2D> positions) { public boolean isOnAnyPosition(List<Vector2D> positions) {
return position != null && position.stream().anyMatch(positions::contains); return this.positions != null && this.positions.stream().anyMatch(positions::contains);
} }
/** /**

View File

@ -14,7 +14,6 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -273,9 +272,8 @@ public final class TrainManager {
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, 1);
if (nextPosition == null || nextPosition.equals(position) if (nextPosition == null || nextPosition.equals(position)) {
|| train.isOnPosition(nextPosition) && !train.getRearPosition().equals(train.getFrontPosition())) {
train.moveTo(railNetwork, null); train.moveTo(railNetwork, null);
collisions.add(new HashSet<>(Arrays.asList(train))); collisions.add(new HashSet<>(Arrays.asList(train)));
} else { } else {
@ -295,10 +293,9 @@ public final class TrainManager {
List<Set<Train>> collisions = new ArrayList<>(); List<Set<Train>> collisions = new ArrayList<>();
// perform step // perform step
trains.values().stream().filter(Train::isPlaced).forEach(train -> { trains.values().stream().filter(Train::isPlaced).forEach(train -> {
Vector2D front = train.getFrontPosition();
Vector2D position = train.getRearPosition(); Vector2D position = train.getRearPosition();
Vector2D direction = train.getRearDirection(); Vector2D direction = train.getRearDirection();
Vector2D nextPosition = railNetwork.move(position, direction); Vector2D nextPosition = railNetwork.move(position, direction, 1);
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); train.moveBackTo(railNetwork, nextPosition);

View File

@ -93,6 +93,17 @@ public class Vector2D {
return new Vector2D(x - other.x, y - other.y); return new Vector2D(x - other.x, y - other.y);
} }
/**
* Subtract another vector from this one, modifying this vector.
*
* @param other another vector
* @throws NullPointerException if other is null
*/
public void subtractInPlace(Vector2D other) {
x -= other.x;
y -= other.y;
}
/** /**
* @param movement vector to add * @param movement vector to add
* @return component-wise sum of this vector and the other vector * @return component-wise sum of this vector and the other vector