mirror of
https://gitlab.com/arnekeller/kit-programmieren-ws1920-final1.git
synced 2024-11-08 18:00:38 +00:00
Optimize large train placement
This commit is contained in:
parent
70a070d9b1
commit
4fad0a377e
@ -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
|
||||||
|
@ -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
13
long_train_input.txt
Normal 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
13
long_train_output.txt
Normal 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
|
@ -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();
|
||||||
|
@ -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
|
||||||
* @param position
|
* stop at the end of the rail, even if that will omit some steps.
|
||||||
* @param direction
|
*
|
||||||
* @param steps
|
* @param position position to move from
|
||||||
* @return
|
* @param direction direction to go
|
||||||
|
* @param steps amount of steps
|
||||||
|
* @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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
return false;
|
if (rollingStockPosition == null) {
|
||||||
}
|
// derailed
|
||||||
// consider fuzz 9 (0,-1)
|
return false;
|
||||||
for (int i = 1; i <= length; i++) {
|
}
|
||||||
rollingStockPosition = railNetwork.move(rollingStockPosition, positioningDirection);
|
final long distanceToLastPosition = positionList.getLast().distanceTo(rollingStockPosition);
|
||||||
if (positions.getLast().distanceTo(rollingStockPosition) == 0) {
|
if (distanceToLastPosition == 0) {
|
||||||
// stuck
|
// stuck
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (rollingStockPosition == null
|
|
||||||
|| positions.contains(rollingStockPosition) && !positions.get(0).equals(rollingStockPosition)) {
|
// successfully placed part of the train
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user