mirror of
https://gitlab.com/arnekeller/kit-programmieren-ws1920-final1.git
synced 2024-11-27 18:55:55 +00:00
Move step output in UI
This commit is contained in:
parent
54708c2198
commit
0e298492a3
@ -9,6 +9,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
@ -66,6 +68,7 @@ public class ModelRailwaySimulation {
|
||||
|
||||
/**
|
||||
* Remove the specified rail from the rail network.
|
||||
*
|
||||
* @param id identifier of the rail to remove
|
||||
* @return whether the rail could be successfully removed
|
||||
*/
|
||||
@ -275,12 +278,22 @@ public class ModelRailwaySimulation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of trains (their text representation) in the simulation.
|
||||
* Get the trains (their text representation) in the simulation.
|
||||
*
|
||||
* @return list of trains
|
||||
* @return sorted collection of trains
|
||||
*/
|
||||
public List<String> listTrains() {
|
||||
return trainManager.listTrains();
|
||||
public SortedMap<Integer, String> getTrains() {
|
||||
return trainManager.getTrains();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the front position of a train.
|
||||
*
|
||||
* @param trainId train identifier
|
||||
* @return position of the train, or null if not found or placed
|
||||
*/
|
||||
public Vector2D getTrainPosition(int trainId) {
|
||||
return trainManager.getPosition(trainId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,11 +320,13 @@ public class ModelRailwaySimulation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the trains in this simulation, printing their new positions and any collisions.
|
||||
* Move the trains in this simulation.
|
||||
*
|
||||
* @param speed amount of steps to move the trains
|
||||
* @return train collisions, null if no trains are placed
|
||||
* @throws InvalidInputException if the simulation is not yet ready
|
||||
*/
|
||||
public void step(short speed) throws InvalidInputException {
|
||||
trainManager.step(speed);
|
||||
public List<SortedSet<Integer>> step(short speed) throws InvalidInputException {
|
||||
return trainManager.step(speed);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ import java.util.stream.Collectors;
|
||||
* @author Arne Keller
|
||||
* @version 1.1
|
||||
*/
|
||||
public final class Train {
|
||||
public final class Train implements Comparable {
|
||||
/**
|
||||
* Separator between rolling stocks in {@link #show()}.
|
||||
*/
|
||||
@ -85,13 +85,14 @@ public final class Train {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this train is on a particular rail. Note that at least one rolling stock has to be on the rail,
|
||||
* just touching the rail is not enough.
|
||||
* Check whether this train is on a particular rail. Just touching the rail is enough.
|
||||
*
|
||||
* @param rail the rail to check
|
||||
* @return whether this train is on that rail
|
||||
*/
|
||||
public boolean isOnRail(Rail rail) {
|
||||
return isPlaced() && (occupiedRails.stream().anyMatch(blockedRail -> blockedRail == rail)
|
||||
return isPlaced()
|
||||
&& (occupiedRails.stream().anyMatch(blockedRail -> blockedRail == rail)
|
||||
|| positions.stream().anyMatch(rail::canConnectTo));
|
||||
}
|
||||
|
||||
@ -367,4 +368,9 @@ public final class Train {
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
return Integer.compare(this.identifier, ((Train) o).identifier);
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,24 @@
|
||||
package edu.kit.informatik.model;
|
||||
|
||||
import edu.kit.informatik.Terminal;
|
||||
import edu.kit.informatik.ui.InvalidInputException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static edu.kit.informatik.ui.CommandLine.OK;
|
||||
|
||||
/**
|
||||
* Train manager, used for processing train placements and movement on a rail network.
|
||||
*
|
||||
@ -32,7 +31,7 @@ public final class TrainManager {
|
||||
*/
|
||||
private final RailwayNetwork railNetwork;
|
||||
/**
|
||||
* Map of trains simulated.
|
||||
* Map of trains simulated. The train identifier is used as key.
|
||||
*/
|
||||
private final Map<Integer, Train> trains = new HashMap<>();
|
||||
|
||||
@ -46,8 +45,8 @@ public final class TrainManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a train is on the rail with the specified identifier. Note that a train must be partially on that
|
||||
* rail, simply touching one of the end points is not enough.
|
||||
* Check whether a train is on the rail with the specified identifier. Trains only touching the end of the rail
|
||||
* will also be considered.
|
||||
*
|
||||
* @param rail the rail to check
|
||||
* @return whether a train is on that rail
|
||||
@ -87,19 +86,29 @@ public final class TrainManager {
|
||||
throw new InvalidInputException("rolling stock already used");
|
||||
}
|
||||
final Train train = trains.get(trainId);
|
||||
if (train != null && train.isPlaced()) {
|
||||
throw new InvalidInputException("can not add rolling stock to placed train");
|
||||
}
|
||||
if (train != null) {
|
||||
train.add(rollingStock);
|
||||
} else {
|
||||
if (train == null) { // create new train
|
||||
final int correctId = getNextTrainIdentifier();
|
||||
if (trainId != correctId) {
|
||||
throw new InvalidInputException("new train identifier must be next free identifier");
|
||||
}
|
||||
final Train newTrain = new Train(trainId, rollingStock);
|
||||
trains.put(trainId, newTrain);
|
||||
} else {
|
||||
if (train.isPlaced()) {
|
||||
throw new InvalidInputException("can not add rolling stock to placed train");
|
||||
}
|
||||
train.add(rollingStock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next available train identifier. Will fill 'holes' in the identifier list first.
|
||||
*
|
||||
* @return the next train identifier, or -1 if none available
|
||||
*/
|
||||
private int getNextTrainIdentifier() {
|
||||
return IntStream.rangeClosed(1, Integer.MAX_VALUE)
|
||||
.filter(id -> !trains.containsKey(id)).findFirst().orElse(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,20 +127,20 @@ public final class TrainManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sorted list of trains (their text representation) in the simulation.
|
||||
* Get the trains (their text representation) in the simulation.
|
||||
*
|
||||
* @return list of trains
|
||||
* @return sorted collection of trains
|
||||
*/
|
||||
public List<String> listTrains() {
|
||||
public SortedMap<Integer, String> getTrains() {
|
||||
return trains.keySet().stream()
|
||||
.sorted()
|
||||
.map(trains::get)
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.toList());
|
||||
.collect(Collectors.toMap(Train::getIdentifier, Object::toString, (id1, id2) -> id1, TreeMap::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ASCII art representation of a train.
|
||||
*
|
||||
* @param trainId identifier of the train to show
|
||||
* @return ASCII art representation of said train
|
||||
* @throws InvalidInputException if train not found
|
||||
@ -179,19 +188,12 @@ public final class TrainManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next available train identifier.
|
||||
* @return the next train identifier, or -1 if none available
|
||||
* Get collisions of trains currently placed. This implements the first collision checking algorithm (1A):
|
||||
* trains collide if they partially occupy the same rail or touch each other.
|
||||
*
|
||||
* @param collisions list of collisions to expand
|
||||
*/
|
||||
private int getNextTrainIdentifier() {
|
||||
return IntStream.rangeClosed(1, Integer.MAX_VALUE)
|
||||
.filter(id -> !trains.containsKey(id)).findFirst().orElse(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get collisions of trains currently placed.
|
||||
* @param collisions list of collisions (never null, sometimes empty)
|
||||
*/
|
||||
private void getStaticCollisions(List<Set<Train>> collisions) {
|
||||
private void getStaticCollisions(List<SortedSet<Train>> collisions) {
|
||||
final int maxId = trains.keySet().stream().max(Integer::compareTo).orElse(0);
|
||||
for (int id = 1; id <= maxId; id++) {
|
||||
final Train train1 = trains.get(id);
|
||||
@ -200,13 +202,13 @@ public final class TrainManager {
|
||||
}
|
||||
final Set<Rail> occupiedRails = train1.getOccupiedRails();
|
||||
// check for same position, and rail collisions
|
||||
final Set<Train> collision = IntStream.rangeClosed(id + 1, maxId)
|
||||
final SortedSet<Train> collision = IntStream.rangeClosed(id + 1, maxId)
|
||||
.mapToObj(trains::get)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(Train::isPlaced)
|
||||
.filter(train -> train1.touches(train)
|
||||
|| train.getOccupiedRails().stream().anyMatch(occupiedRails::contains))
|
||||
.collect(Collectors.toSet());
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
if (!collision.isEmpty()) {
|
||||
collision.add(train1);
|
||||
addToSetOrAddNew(collisions, collision);
|
||||
@ -215,15 +217,15 @@ public final class TrainManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the silly *second* collision checking algorithm.
|
||||
* Will put two trains in a collision even if they only touch the same rail.
|
||||
* Implementation of the silly second collision checking algorithm (2B):
|
||||
* two trains collide if they touch the same rail or position.
|
||||
*
|
||||
* @return list of collisions
|
||||
*/
|
||||
private List<Set<Train>> getPlacementCollisions() {
|
||||
final List<Set<Train>> collisions = new ArrayList<>();
|
||||
private List<SortedSet<Train>> getPlacementCollisions() {
|
||||
final List<SortedSet<Train>> collisions = new ArrayList<>();
|
||||
trains.values().stream().filter(Train::isPlaced).forEach(train1 ->
|
||||
trains.values().stream().filter(Train::isPlaced).forEach(train2 -> {
|
||||
trains.values().stream().filter(train -> train != train1).filter(Train::isPlaced).forEach(train2 -> {
|
||||
final Set<Rail> occupiedByTrain1 = train1.getOccupiedRails();
|
||||
Collections.addAll(occupiedByTrain1, railNetwork.findTouchingRails(train1.getFrontPosition()));
|
||||
Collections.addAll(occupiedByTrain1, railNetwork.findTouchingRails(train1.getRearPosition()));
|
||||
@ -233,7 +235,8 @@ public final class TrainManager {
|
||||
// calculate intersection
|
||||
occupiedByTrain2.retainAll(occupiedByTrain1);
|
||||
if (!occupiedByTrain2.isEmpty()) {
|
||||
final Set<Train> collision = Stream.of(train1, train2).collect(Collectors.toSet());
|
||||
final SortedSet<Train> collision
|
||||
= Stream.of(train1, train2).collect(Collectors.toCollection(TreeSet::new));
|
||||
addToSetOrAddNew(collisions, collision);
|
||||
}
|
||||
}));
|
||||
@ -243,7 +246,7 @@ public final class TrainManager {
|
||||
/**
|
||||
* Add the set to an existing set (and merge sets), or add it to the list.
|
||||
*/
|
||||
private void addToSetOrAddNew(List<Set<Train>> setList, Set<Train> newSet) {
|
||||
private void addToSetOrAddNew(List<SortedSet<Train>> setList, SortedSet<Train> newSet) {
|
||||
Set<Train> existing = null;
|
||||
int i = 0;
|
||||
while (i < setList.size()) {
|
||||
@ -265,13 +268,23 @@ public final class TrainManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the front position of the specified train.
|
||||
*
|
||||
* @param trainId train identifier
|
||||
* @return front position of that train
|
||||
*/
|
||||
public Vector2D getPosition(int trainId) {
|
||||
return trains.get(trainId).getFrontPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get collisions of moving the trains one step forward, removing crashing trains from the rails in the process.
|
||||
*
|
||||
* @return list of collisions (never null, sometimes empty)
|
||||
*/
|
||||
private List<Set<Train>> getCollisionsOfOneStep() {
|
||||
final List<Set<Train>> collisions = new ArrayList<>();
|
||||
private List<SortedSet<Train>> getCollisionsOfOneStep() {
|
||||
final List<SortedSet<Train>> collisions = new ArrayList<>();
|
||||
trains.values().stream().filter(Train::isPlaced).forEach(train -> {
|
||||
final Vector2D position = train.getFrontPosition();
|
||||
final Vector2D direction = train.getDirection();
|
||||
@ -279,7 +292,7 @@ public final class TrainManager {
|
||||
if (nextPosition == null || nextPosition.equals(position)) {
|
||||
// train is derailing
|
||||
train.moveTo(railNetwork, null);
|
||||
collisions.add(new HashSet<>(Arrays.asList(train)));
|
||||
collisions.add(Stream.of(train).collect(Collectors.toCollection(TreeSet::new)));
|
||||
} else {
|
||||
// train is moving successfully
|
||||
train.moveTo(railNetwork, nextPosition);
|
||||
@ -295,8 +308,8 @@ public final class TrainManager {
|
||||
*
|
||||
* @return list of collisions (never null, sometimes empty)
|
||||
*/
|
||||
private List<Set<Train>> getCollisionsOfOneReverseStep() {
|
||||
final List<Set<Train>> collisions = new ArrayList<>();
|
||||
private List<SortedSet<Train>> getCollisionsOfOneReverseStep() {
|
||||
final List<SortedSet<Train>> collisions = new ArrayList<>();
|
||||
// perform step
|
||||
trains.values().stream().filter(Train::isPlaced).forEach(train -> {
|
||||
final Vector2D position = train.getRearPosition();
|
||||
@ -305,7 +318,7 @@ public final class TrainManager {
|
||||
if (nextPosition == null || nextPosition.equals(position)) {
|
||||
// derailing
|
||||
train.moveBackTo(railNetwork, nextPosition);
|
||||
collisions.add(new HashSet<>(Arrays.asList(train)));
|
||||
collisions.add(Stream.of(train).collect(Collectors.toCollection(TreeSet::new)));
|
||||
} else {
|
||||
// train moving successfully
|
||||
train.moveBackTo(railNetwork, nextPosition);
|
||||
@ -317,42 +330,27 @@ public final class TrainManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the trains in this simulation, printing their new positions and any collisions.
|
||||
* Move the trains in this simulation.
|
||||
*
|
||||
* @param speed amount of steps to move the trains
|
||||
* @return train collisions, null if no trains are placed
|
||||
* @throws InvalidInputException if simulation is not yet ready
|
||||
*/
|
||||
public void step(short speed) throws InvalidInputException {
|
||||
public List<SortedSet<Integer>> step(short speed) throws InvalidInputException {
|
||||
if (!railNetwork.isReadyForTrains()) {
|
||||
throw new InvalidInputException("rail tracks/switches not set up");
|
||||
}
|
||||
if (trains.values().stream().noneMatch(Train::isPlaced)) {
|
||||
Terminal.printLine(OK);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<Set<Train>> collisions = IntStream.range(0, Math.abs(speed))
|
||||
return IntStream.range(0, Math.abs(speed))
|
||||
.mapToObj(step -> speed >= 0 ? getCollisionsOfOneStep() : getCollisionsOfOneReverseStep())
|
||||
.flatMap(List::stream).collect(Collectors.toList());
|
||||
|
||||
for (final int id : trains.keySet().stream().sorted().collect(Collectors.toList())) {
|
||||
final Train train = trains.get(id);
|
||||
final Set<Train> collisionSet = collisions.stream()
|
||||
.filter(collision -> collision.contains(train))
|
||||
.findFirst().orElse(null);
|
||||
if (collisionSet != null) { // print collision
|
||||
final int first = collisionSet.stream()
|
||||
.min(Comparator.comparing(Train::getIdentifier)).get().getIdentifier();
|
||||
if (train.getIdentifier() == first) { // only print each collision once
|
||||
final List<Train> collision = collisionSet.stream()
|
||||
.sorted(Comparator.comparing(Train::getIdentifier))
|
||||
// replace train references with identifiers to prevent modification by caller
|
||||
.flatMap(collisions -> collisions.stream()
|
||||
.map(collision -> collision.stream().map(Train::getIdentifier)
|
||||
.collect(Collectors.toCollection(TreeSet::new))))
|
||||
.sorted(Comparator.comparing(TreeSet::first))
|
||||
.collect(Collectors.toList());
|
||||
Terminal.printLine("Crash of train " + String.join(",", collision.stream()
|
||||
.map(crashedTrain -> Integer.toString(crashedTrain.getIdentifier()))
|
||||
.toArray(String[]::new)));
|
||||
}
|
||||
} else if (train.isPlaced()) { // or position, if not in collision
|
||||
Terminal.printLine(String.format("Train %d at %s", train.getIdentifier(), train.getFrontPosition()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import edu.kit.informatik.Terminal;
|
||||
import edu.kit.informatik.model.ModelRailwaySimulation;
|
||||
import edu.kit.informatik.ui.InvalidInputException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import static edu.kit.informatik.ui.command.CommandFactory.LIST_TRAINS;
|
||||
|
||||
@ -17,11 +17,11 @@ import static edu.kit.informatik.ui.command.CommandFactory.LIST_TRAINS;
|
||||
public class ListTrains extends Command {
|
||||
@Override
|
||||
public void apply(ModelRailwaySimulation simulation) {
|
||||
final List<String> trains = simulation.listTrains();
|
||||
final SortedMap<Integer, String> trains = simulation.getTrains();
|
||||
if (trains.isEmpty()) {
|
||||
Terminal.printLine("No train exists");
|
||||
} else {
|
||||
trains.forEach(Terminal::printLine);
|
||||
trains.values().forEach(Terminal::printLine);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,14 @@
|
||||
package edu.kit.informatik.ui.command;
|
||||
|
||||
import edu.kit.informatik.Terminal;
|
||||
import edu.kit.informatik.model.ModelRailwaySimulation;
|
||||
import edu.kit.informatik.model.Vector2D;
|
||||
import edu.kit.informatik.ui.InvalidInputException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import static edu.kit.informatik.ui.CommandLine.OK;
|
||||
import static edu.kit.informatik.ui.command.CommandFactory.NUMBER;
|
||||
import static edu.kit.informatik.ui.command.CommandFactory.STEP;
|
||||
|
||||
@ -20,7 +26,25 @@ public class Step extends Command {
|
||||
|
||||
@Override
|
||||
public void apply(ModelRailwaySimulation simulation) throws InvalidInputException {
|
||||
simulation.step(speed);
|
||||
final List<SortedSet<Integer>> collisions = simulation.step(speed);
|
||||
if (collisions == null) {
|
||||
Terminal.printLine(OK);
|
||||
} else {
|
||||
for (final int id : simulation.getTrains().keySet()) {
|
||||
final SortedSet<Integer> collisionSet = collisions.stream()
|
||||
.filter(collision -> collision.first() == id)
|
||||
.findFirst().orElse(null);
|
||||
if (collisionSet != null) { // print collision
|
||||
Terminal.printLine("Crash of train " + String.join(",",
|
||||
collisionSet.stream().map(Object::toString).toArray(String[]::new)));
|
||||
} else { // or position, if not in collision
|
||||
final Vector2D position = simulation.getTrainPosition(id);
|
||||
if (position != null) { // only print placed trains
|
||||
Terminal.printLine(String.format("Train %d at %s", id, position));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user