diff --git a/Contest/Assignment/src/main/resources/logback.xml b/Contest/Assignment/src/main/resources/logback.xml
new file mode 100644
index 0000000..5e004a6
--- /dev/null
+++ b/Contest/Assignment/src/main/resources/logback.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ ${PATTERN}
+
+
+
+
+
+
+
+
+
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java b/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java
index e9e4159..2d06db7 100644
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java
@@ -1,47 +1,40 @@
package org.togetherjava.event.elevator;
-import org.togetherjava.event.elevator.elevators.Elevator;
-import org.togetherjava.event.elevator.humans.Human;
-import org.togetherjava.event.elevator.simulation.Simulation;
+import org.togetherjava.event.elevator.models.Simulation;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
public final class Main {
- /**
- * Starts the application.
- *
- * Will create an elevator-system simulation, execute it until it is done
- * and pretty print the state to console.
- *
- * @param args Not supported
- */
- public static void main(final String[] args) {
- // Select a desired simulation for trying out your code.
- // Start with the simple simulations first, try out the bigger systems once you got it working.
- // Eventually try out the randomly generated systems. If you want to debug a problem you encountered
- // with one of them, note down the seed that it prints at the beginning and then use the variant that takes this seed.
- // That way, it will generate the same system again, and you can repeat the test.
- Simulation simulation = Simulation.createSingleElevatorSingleHumanSimulation();
- // Simulation simulation = Simulation.createSimpleSimulation();
- // Simulation simulation = Simulation.createRandomSimulation(5, 50, 10);
- // Simulation simulation = Simulation.createRandomSimulation(putDesiredSeedHere, 5, 50, 10);
-
- simulation.printSummary();
-
- System.out.println("Starting simulation...");
- simulation.start();
- simulation.prettyPrint();
-
- while (!simulation.isDone()) {
- System.out.println("\tSimulation step " + simulation.getStepCount());
- simulation.step();
- simulation.prettyPrint();
-
- if (simulation.getStepCount() >= 100_000) {
- throw new IllegalStateException("Simulation aborted. All humans should have arrived"
- + " by now, but they did not. There is likely a bug in your code.");
- }
+
+ static void main() {
+ try {
+ Simulation simulation = SimulationService.createRandomSimulation(2, 4, 4);
+
+ SimulationUtils.printSummary(simulation);
+
+ SimulationService.start(simulation);
+
+ SimulationUtils.prettyPrint(simulation);
+
+ final int stepLimit = 100_000;
+
+ while (!SimulationService.isDone(simulation)) {
+ log.info("\tSimulation step {}", simulation.getStepCount());
+ SimulationService.step(simulation);
+ SimulationUtils.prettyPrint(simulation);
+
+ if (simulation.getStepCount() >= stepLimit) {
+ throw new SimulationException(
+ "Simulation aborted. All humans should have arrived by now, but they did not. There is likely a bug in your code.");
}
- System.out.println("Simulation is done.");
+ }
+ log.info("Elevator Simulation is done.");
+
+ SimulationUtils.printResult(simulation);
- simulation.printResult();
+ } catch (SimulationException ex) {
+ log.error("Elevator Simulation error: {}", ex.getMessage());
}
+ }
}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationException.java b/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationException.java
new file mode 100644
index 0000000..c0a6606
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationException.java
@@ -0,0 +1,11 @@
+package org.togetherjava.event.elevator;
+
+import java.io.Serial;
+
+public final class SimulationException extends RuntimeException {
+ @Serial private static final long serialVersionUID = 1L;
+
+ public SimulationException(String message) {
+ super(message);
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationService.java b/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationService.java
new file mode 100644
index 0000000..de06e99
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationService.java
@@ -0,0 +1,101 @@
+package org.togetherjava.event.elevator;
+
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Stream;
+
+import org.togetherjava.event.elevator.enums.HumanState;
+import org.togetherjava.event.elevator.models.Elevator;
+import org.togetherjava.event.elevator.models.Human;
+import org.togetherjava.event.elevator.models.HumanStatistics;
+import org.togetherjava.event.elevator.models.Simulation;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public final class SimulationService {
+ private SimulationService() {
+ throw new UnsupportedOperationException(SimulationUtils.CANNOT_INSTANTIATE_UTILITY_CLASS);
+ }
+
+ public static Simulation createSingleElevatorSingleHumanSimulation() {
+ return new Simulation(List.of(new Elevator(1, 10, 5)), List.of(new Human(1, 10)));
+ }
+
+ public static Simulation createSimpleSimulation() {
+ int minFloor = 1;
+ int floorsServed = 10;
+
+ return new Simulation(
+ List.of(new Elevator(minFloor, floorsServed, 1), new Elevator(minFloor, floorsServed, 6)),
+ List.of(
+ new Human(1, 2), new Human(1, 5), new Human(8, 10), new Human(9, 3), new Human(10, 1)));
+ }
+
+ public static Simulation createRandomSimulation(
+ int amountOfElevators, int amountOfHumans, int floorsServed) {
+ return createRandomSimulation(
+ ThreadLocalRandom.current().nextLong(), amountOfElevators, amountOfHumans, floorsServed);
+ }
+
+ public static Simulation createRandomSimulation(
+ long seed, int amountOfElevators, int amountOfHumans, int floorsServed) {
+
+ log.debug("Seed for random simulation is: {}", seed);
+
+ // log.info(
+ // "creating random simulation: elevatorsCount {}, humansCount {}, floorsServed {}",
+ // amountOfElevators,
+ // amountOfHumans,
+ // floorsServed);
+
+ Random random = new Random(seed);
+
+ final int minFloor = 1;
+
+ List elevators =
+ Stream.generate(
+ () -> {
+ int currentFloor = minFloor + random.nextInt(floorsServed);
+ return new Elevator(minFloor, floorsServed, currentFloor);
+ })
+ .limit(amountOfElevators)
+ .toList();
+
+ List humans =
+ Stream.generate(
+ () -> {
+ int startingFloor = minFloor + random.nextInt(floorsServed);
+ int destinationFloor = minFloor + random.nextInt(floorsServed);
+ return new Human(startingFloor, destinationFloor);
+ })
+ .limit(amountOfHumans)
+ .toList();
+
+ return new Simulation(elevators, humans);
+ }
+
+ public static void start(Simulation simulation) {
+ // log.info("starting new simulation...");
+ simulation.getElevatorSystem().ready();
+ // log.info("Simulation is ready");
+ }
+
+ public static void step(Simulation simulation) {
+ // log.info("step started");
+
+ simulation.getElevatorSystem().moveOneFloor();
+
+ simulation.getHumanStatistics().forEach(HumanStatistics::step);
+
+ simulation.increaseStepCount();
+
+ // log.info("step finished!");
+ }
+
+ public static boolean isDone(Simulation simulation) {
+ List humans = simulation.getHumans();
+ return humans.stream().map(Human::getCurrentState).allMatch(HumanState.ARRIVED::equals);
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationUtils.java b/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationUtils.java
new file mode 100644
index 0000000..81ea5e0
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/SimulationUtils.java
@@ -0,0 +1,70 @@
+package org.togetherjava.event.elevator;
+
+import java.util.List;
+import java.util.stream.LongStream;
+
+import org.togetherjava.event.elevator.enums.HumanState;
+import org.togetherjava.event.elevator.models.HumanStatistics;
+import org.togetherjava.event.elevator.models.Simulation;
+import org.togetherjava.event.elevator.models.View;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public final class SimulationUtils {
+
+ public static final String CANNOT_INSTANTIATE_UTILITY_CLASS = "Cannot instantiate utility class";
+
+ // public static final String LOG_ELEVATOR_DOESNT_MOVE_NO_PENDING_FLOORS =
+ // "elevator {} doesn't move, no pending floors";
+
+ public static final String LOG_ELEVATOR_MOVING_FROM_TO =
+ "elevator {} moving {} from {} to {} (target: {})";
+
+ private SimulationUtils() {
+ throw new UnsupportedOperationException(CANNOT_INSTANTIATE_UTILITY_CLASS);
+ }
+
+ public static void printSummary(Simulation simulation) {
+ View view = simulation.getView();
+ view.printSummary();
+ }
+
+ public static void prettyPrint(Simulation simulation) {
+ View view = simulation.getView();
+ view.prettyPrint();
+ }
+
+ public static void printResult(Simulation simulation) {
+ log.info("Steps: {}", simulation.getStepCount());
+ log.info("Median time spend per state:");
+
+ for (HumanState state : HumanState.values()) {
+ int averagePercentage = getAverageTimePercentageSpendForState(simulation, state);
+ log.info("\t{}: {}%", state, averagePercentage);
+ }
+ }
+
+ public static int getAverageTimePercentageSpendForState(Simulation simulation, HumanState state) {
+
+ List humanStatistics = simulation.getHumanStatistics();
+
+ long stepCount = simulation.getStepCount();
+
+ if (stepCount == 0) {
+ return 0;
+ }
+
+ LongStream sortedSteps =
+ humanStatistics.stream().mapToLong(stats -> stats.stepsForState(state)).sorted();
+
+ long medianSteps =
+ humanStatistics.size() % 2 == 0
+ ? (long)
+ sortedSteps.skip(humanStatistics.size() / 2 - 1).limit(2).average().orElseThrow()
+ : sortedSteps.skip(humanStatistics.size() / 2).findFirst().orElseThrow();
+
+ long medianPercentage = 100 * medianSteps / stepCount;
+ return (int) medianPercentage;
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/api/ElevatorListener.java b/Contest/Assignment/src/org/togetherjava/event/elevator/api/ElevatorListener.java
new file mode 100644
index 0000000..2985f7a
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/api/ElevatorListener.java
@@ -0,0 +1,25 @@
+package org.togetherjava.event.elevator.api;
+
+/**
+ * Listeners to elevator events. This is mostly interesting for humans who can then request
+ * elevators to move to desired floors.
+ */
+public interface ElevatorListener {
+ /**
+ * Fired when the elevator system is ready to receive requests. Elevators can now move.
+ *
+ * @param floorPanelSystem the system in the corridor that allows requesting elevators to the
+ * current floor
+ */
+ void onElevatorSystemReady(FloorPanelSystem floorPanelSystem);
+
+ /**
+ * Fired when an elevator arrived at a floor. Humans can now enter or exit if desired.
+ *
+ * @param elevatorPanel the system inside the elevator which provides information about the
+ * elevator and can be used to request a destination floor.
+ * @implNote The default implementation fires this event from all elevators to all humans, not
+ * only to humans that are relevant (i.e. humans that can enter the elevator).
+ */
+ void onElevatorArrivedAtFloor(ElevatorPanel elevatorPanel);
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/api/ElevatorPanel.java b/Contest/Assignment/src/org/togetherjava/event/elevator/api/ElevatorPanel.java
new file mode 100644
index 0000000..98d914a
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/api/ElevatorPanel.java
@@ -0,0 +1,29 @@
+package org.togetherjava.event.elevator.api;
+
+/**
+ * The system inside an elevator which provides information about the elevator and can be used to
+ * request a destination floor.
+ */
+public interface ElevatorPanel {
+ /**
+ * The unique ID of the elevator.
+ *
+ * @return the unique ID of the elevator
+ */
+ int getId();
+
+ /**
+ * The floor the elevator is currently at.
+ *
+ * @return the current floor
+ */
+ int getCurrentFloor();
+
+ /**
+ * Requesting the elevator to eventually move to the given destination floor, for humans to exit.
+ *
+ * @param destinationFloor the desired destination, must be within the range served by this
+ * elevator
+ */
+ void requestDestinationFloor(int destinationFloor);
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/api/FloorPanelSystem.java b/Contest/Assignment/src/org/togetherjava/event/elevator/api/FloorPanelSystem.java
new file mode 100644
index 0000000..4975253
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/api/FloorPanelSystem.java
@@ -0,0 +1,17 @@
+package org.togetherjava.event.elevator.api;
+
+import org.togetherjava.event.elevator.enums.TravelDirection;
+
+/** The system in corridors that allows requesting elevators to the current floor. */
+public interface FloorPanelSystem {
+ /**
+ * Requests an elevator to move to the given floor to pick up a human.
+ *
+ * @param atFloor the floor to pick up the human at, must be within the range served by the system
+ * @param desiredTravelDirection the direction the human wants to travel into, can be used for
+ * determination of the best elevator
+ * @apiNote This represents a human standing in the corridor, pressing a button on the wall,
+ * requesting that an elevator comes to pick them up for travel into the given direction.
+ */
+ void requestElevator(int atFloor, TravelDirection desiredTravelDirection);
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/Elevator.java b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/Elevator.java
deleted file mode 100644
index 51333b2..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/Elevator.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.togetherjava.event.elevator.elevators;
-
-import java.util.StringJoiner;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * A single elevator that can serve a given amount of floors.
- *
- * An elevator can take floor requests from either humans or the elevator system itself.
- * The elevator will eventually move towards the requested floor and transport humans to their destinations.
- */
-public final class Elevator implements ElevatorPanel {
- private static final AtomicInteger NEXT_ID = new AtomicInteger(0);
-
- private final int id;
- private final int minFloor;
- private final int floorsServed;
- private int currentFloor;
-
- /**
- * Creates a new elevator.
- *
- * @param minFloor the minimum floor that the elevator can serve, must be greater than or equal to 1.
- * @param floorsServed the amount of floors served in total by this elevator, must be greater than or equal to 2.
- * Together with the minFloor this forms a consecutive range of floors with no gaps in between.
- * @param currentFloor the floor the elevator starts at, must be within the defined range of floors served by the elevator
- */
- public Elevator(int minFloor, int floorsServed, int currentFloor) {
- if (minFloor <= 0 || floorsServed < 2) {
- throw new IllegalArgumentException("Min floor must at least 1, floors served at least 2.");
- }
- if (currentFloor < minFloor || currentFloor >= minFloor + floorsServed) {
- throw new IllegalArgumentException("The current floor must be between the floors served by the elevator.");
- }
-
- this.id = NEXT_ID.getAndIncrement();
- this.minFloor = minFloor;
- this.currentFloor = currentFloor;
- this.floorsServed = floorsServed;
- }
-
- @Override
- public int getId() {
- return id;
- }
-
- public int getMinFloor() {
- return minFloor;
- }
-
- public int getFloorsServed() {
- return floorsServed;
- }
-
- @Override
- public int getCurrentFloor() {
- return currentFloor;
- }
-
- @Override
- public void requestDestinationFloor(int destinationFloor) {
- // TODO Implement. This represents a human or the elevator system
- // itself requesting this elevator to eventually move to the given floor.
- // The elevator is supposed to memorize the destination in a way that
- // it can ensure to eventually reach it.
- System.out.println("Request for destination floor received");
- }
-
- public void moveOneFloor() {
- // TODO Implement. Essentially there are three possibilities:
- // - move up one floor
- // - move down one floor
- // - stand still
- // The elevator is supposed to move in a way that it will eventually reach
- // the floors requested by Humans via requestDestinationFloor(), ideally "fast" but also "fair",
- // meaning that the average time waiting (either in corridor or inside the elevator)
- // is minimized across all humans.
- // It is essential that this method updates the currentFloor field accordingly.
- System.out.println("Request to move a floor received");
- }
-
- @Override
- public synchronized String toString() {
- return new StringJoiner(", ", Elevator.class.getSimpleName() + "[", "]").add("id=" + id)
- .add("minFloor=" + minFloor)
- .add("floorsServed=" + floorsServed)
- .add("currentFloor=" + currentFloor)
- .toString();
- }
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorPanel.java b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorPanel.java
deleted file mode 100644
index 386ec77..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorPanel.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.togetherjava.event.elevator.elevators;
-
-/**
- * The system inside an elevator which provides information about the elevator and can be
- * used to request a destination floor.
- */
-public interface ElevatorPanel {
- /**
- * The unique ID of the elevator.
- *
- * @return the unique ID of the elevator
- */
- int getId();
-
- /**
- * The floor the elevator is currently at.
- *
- * @return the current floor
- */
- int getCurrentFloor();
-
- /**
- * Requesting the elevator to eventually move to the given destination floor, for humans to exit.
- *
- * @param destinationFloor the desired destination, must be within the range served by this elevator
- */
- void requestDestinationFloor(int destinationFloor);
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorSystem.java b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorSystem.java
deleted file mode 100644
index fadfe56..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorSystem.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.togetherjava.event.elevator.elevators;
-
-import org.togetherjava.event.elevator.humans.ElevatorListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * System controlling all elevators of a building.
- *
- * Once all elevators and humans have been registered via {@link #registerElevator(Elevator)}
- * and {@link #registerElevatorListener(ElevatorListener)} respectively,
- * the system can be made ready using {@link #ready()}.
- */
-public final class ElevatorSystem implements FloorPanelSystem {
- private final List elevators = new ArrayList<>();
- private final List elevatorListeners = new ArrayList<>();
-
- public void registerElevator(Elevator elevator) {
- elevators.add(elevator);
- }
-
- public void registerElevatorListener(ElevatorListener listener) {
- elevatorListeners.add(listener);
- }
-
- /**
- * Upon calling this, the system is ready to receive elevator requests. Elevators may now start moving.
- */
- public void ready() {
- elevatorListeners.forEach(listener -> listener.onElevatorSystemReady(this));
- }
-
- @Override
- public void requestElevator(int atFloor, TravelDirection desiredTravelDirection) {
- // TODO Implement. This represents a human standing in the corridor,
- // requesting that an elevator comes to pick them up for travel into the given direction.
- // The system is supposed to make sure that an elevator will eventually reach this floor to pick up the human.
- // The human can then enter the elevator and request their actual destination within the elevator.
- // Ideally this has to select the best elevator among all which can reduce the time
- // for the human spending waiting (either in corridor or in the elevator itself).
- System.out.println("Request for elevator received");
- }
-
- public void moveOneFloor() {
- elevators.forEach(Elevator::moveOneFloor);
- elevators.forEach(elevator -> elevatorListeners.forEach(listener -> listener.onElevatorArrivedAtFloor(elevator)));
- }
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/FloorPanelSystem.java b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/FloorPanelSystem.java
deleted file mode 100644
index 8043228..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/FloorPanelSystem.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.togetherjava.event.elevator.elevators;
-
-/**
- * The system in corridors that allows requesting elevators to the current floor.
- */
-public interface FloorPanelSystem {
- /**
- * Requests an elevator to move to the given floor to pick up a human.
- *
- * @param atFloor the floor to pick up the human at, must be within the range served by the system
- * @param desiredTravelDirection the direction the human wants to travel into,
- * can be used for determination of the best elevator
- * @apiNote This represents a human standing in the corridor, pressing a button on the wall,
- * requesting that an elevator comes to pick them up for travel into the given direction.
- */
- void requestElevator(int atFloor, TravelDirection desiredTravelDirection);
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/TravelDirection.java b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/TravelDirection.java
deleted file mode 100644
index b1c01c0..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/TravelDirection.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.togetherjava.event.elevator.elevators;
-
-public enum TravelDirection {
- UP,
- DOWN,
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/enums/HumanState.java b/Contest/Assignment/src/org/togetherjava/event/elevator/enums/HumanState.java
new file mode 100644
index 0000000..c716552
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/enums/HumanState.java
@@ -0,0 +1,8 @@
+package org.togetherjava.event.elevator.enums;
+
+public enum HumanState {
+ IDLE,
+ WAITING_FOR_ELEVATOR,
+ TRAVELING_WITH_ELEVATOR,
+ ARRIVED
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/enums/TravelDirection.java b/Contest/Assignment/src/org/togetherjava/event/elevator/enums/TravelDirection.java
new file mode 100644
index 0000000..2967eaf
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/enums/TravelDirection.java
@@ -0,0 +1,6 @@
+package org.togetherjava.event.elevator.enums;
+
+public enum TravelDirection {
+ UP,
+ DOWN,
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/humans/ElevatorListener.java b/Contest/Assignment/src/org/togetherjava/event/elevator/humans/ElevatorListener.java
deleted file mode 100644
index a124b1b..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/humans/ElevatorListener.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.togetherjava.event.elevator.humans;
-
-import org.togetherjava.event.elevator.elevators.ElevatorPanel;
-import org.togetherjava.event.elevator.elevators.FloorPanelSystem;
-
-/**
- * Listeners to elevator events. This is mostly interesting for
- * humans who can then request elevators to move to desired floors.
- */
-public interface ElevatorListener {
- /**
- * Fired when the elevator system is ready to receive requests. Elevators can now move.
- *
- * @param floorPanelSystem the system in the corridor that allows
- * requesting elevators to the current floor
- */
- void onElevatorSystemReady(FloorPanelSystem floorPanelSystem);
-
- /**
- * Fired when an elevator arrived at a floor. Humans can now enter or exit if desired.
- *
- * @param elevatorPanel the system inside the elevator which provides information
- * about the elevator and can be used to request a destination floor.
- * @implNote The default implementation fires this event from all elevators to all humans, not only to humans that are
- * relevant (i.e. humans that can enter the elevator).
- */
- void onElevatorArrivedAtFloor(ElevatorPanel elevatorPanel);
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/humans/Human.java b/Contest/Assignment/src/org/togetherjava/event/elevator/humans/Human.java
deleted file mode 100644
index 0af2511..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/humans/Human.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.togetherjava.event.elevator.humans;
-
-import org.togetherjava.event.elevator.elevators.ElevatorPanel;
-import org.togetherjava.event.elevator.elevators.FloorPanelSystem;
-
-import java.util.OptionalInt;
-import java.util.StringJoiner;
-
-/**
- * A single human that starts at a given floor and wants to
- * reach a destination floor via an elevator.
- *
- * The class mainly acts upon given elevator events it listens to,
- * for example requesting an elevator, eventually entering and exiting them.
- */
-public final class Human implements ElevatorListener {
- private State currentState;
- private final int startingFloor;
- private final int destinationFloor;
- /**
- * If the human is currently inside an elevator, this is its unique ID.
- * Otherwise, this is {@code null} to indicate that the human is currently on the corridor.
- */
- private Integer currentEnteredElevatorId;
-
- /**
- * Creates a new human.
- *
- * It is supported that starting and destination floors are equal.
- * The human will then not travel with an elevator at all.
- *
- * @param startingFloor the floor the human currently stands at, must be greater than or equal to 1
- * @param destinationFloor the floor the human eventually wants to reach, must be greater than or equal to 1
- */
- public Human(int startingFloor, int destinationFloor) {
- if (startingFloor <= 0 || destinationFloor <= 0) {
- throw new IllegalArgumentException("Floors must be at least 1");
- }
-
- this.startingFloor = startingFloor;
- this.destinationFloor = destinationFloor;
-
- currentState = State.IDLE;
- }
-
- public State getCurrentState() {
- return currentState;
- }
-
- public int getStartingFloor() {
- return startingFloor;
- }
-
- public int getDestinationFloor() {
- return destinationFloor;
- }
-
- @Override
- public void onElevatorSystemReady(FloorPanelSystem floorPanelSystem) {
- // TODO Implement. The system is now ready and the human should leave
- // their initial IDLE state, requesting an elevator by clicking on the buttons of
- // the floor panel system. The human will now enter the WAITING_FOR_ELEVATOR state.
- System.out.println("Ready-event received");
- }
-
- @Override
- public void onElevatorArrivedAtFloor(ElevatorPanel elevatorPanel) {
- // TODO Implement. If the human is currently waiting for an elevator and
- // this event represents arrival at the humans current floor, the human can now enter the
- // elevator and request their actual destination floor. The state has to change to TRAVELING_WITH_ELEVATOR.
- // If the human is currently traveling with this elevator and the event represents
- // arrival at the human's destination floor, the human can now exit the elevator.
- System.out.println("Arrived-event received");
- }
-
- public OptionalInt getCurrentEnteredElevatorId() {
- return currentEnteredElevatorId == null
- ? OptionalInt.empty()
- : OptionalInt.of(currentEnteredElevatorId);
- }
-
- @Override
- public String toString() {
- return new StringJoiner(", ", Human.class.getSimpleName() + "[", "]")
- .add("currentState=" + currentState)
- .add("startingFloor=" + startingFloor)
- .add("destinationFloor=" + destinationFloor)
- .add("currentEnteredElevatorId=" + currentEnteredElevatorId)
- .toString();
- }
-
- public enum State {
- IDLE,
- WAITING_FOR_ELEVATOR,
- TRAVELING_WITH_ELEVATOR,
- ARRIVED
- }
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/models/Elevator.java b/Contest/Assignment/src/org/togetherjava/event/elevator/models/Elevator.java
new file mode 100644
index 0000000..86db8e2
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/models/Elevator.java
@@ -0,0 +1,190 @@
+package org.togetherjava.event.elevator.models;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.togetherjava.event.elevator.SimulationException;
+import org.togetherjava.event.elevator.SimulationUtils;
+import org.togetherjava.event.elevator.api.ElevatorPanel;
+import org.togetherjava.event.elevator.enums.TravelDirection;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * A single elevator that can serve a given amount of floors.
+ *
+ *
An elevator can take floor requests from either humans or the elevator system itself. The
+ * elevator will eventually move towards the requested floor and transport humans to their
+ * destinations.
+ */
+@Slf4j
+public final class Elevator implements ElevatorPanel {
+ private static final AtomicInteger NEXT_ID = new AtomicInteger(0);
+ private final int id;
+ @Getter private final int minFloor;
+ @Getter private final int floorsServed;
+ private int currentFloor;
+ private final Set pendingFloors = new LinkedHashSet<>();
+ private Integer activeTarget;
+
+ /**
+ * Creates a new elevator.
+ *
+ * @param minFloor the minimum floor that the elevator can serve, must be greater than or equal to
+ * 1.
+ * @param floorsServed the amount of floors served in total by this elevator, must be greater than
+ * or equal to 2. Together with the minFloor this forms a consecutive range of floors with no
+ * gaps in between.
+ * @param currentFloor the floor the elevator starts at, must be within the defined range of
+ * floors served by the elevator
+ */
+ public Elevator(int minFloor, int floorsServed, int currentFloor) {
+ if (minFloor <= 0 || floorsServed < 2) {
+ throw new IllegalArgumentException("Min floor must at least 1, floors served at least 2.");
+ }
+ if (currentFloor < minFloor || currentFloor >= minFloor + floorsServed) {
+ throw new IllegalArgumentException(
+ "The current floor must be between the floors served by the elevator.");
+ }
+
+ this.id = NEXT_ID.getAndIncrement();
+
+ this.minFloor = minFloor;
+ this.currentFloor = currentFloor;
+ this.floorsServed = floorsServed;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public int getCurrentFloor() {
+ return this.currentFloor;
+ }
+
+ /**
+ * Requests this elevator to eventually move to the given floor.
+ *
+ * This represents a human or the elevator system itself requesting this elevator to move to
+ * the given floor. The elevator memorizes the destination to ensure it will eventually reach it.
+ *
+ * @param destinationFloor the floor to move to, must be within the range of floors served
+ * @throws IllegalArgumentException if the destination floor is out of range
+ */
+ @Override
+ public void requestDestinationFloor(int destinationFloor) {
+
+ // log.info("Requesting elevator {} to reach destination floor {}", this.id,
+ // destinationFloor);
+
+ if (destinationFloor < this.minFloor || destinationFloor >= this.minFloor + this.floorsServed) {
+ throw new SimulationException("destination outof range for elevator " + this.id);
+ }
+
+ if (!this.pendingFloors.contains(destinationFloor)) {
+
+ this.pendingFloors.add(destinationFloor);
+
+ if (this.activeTarget == null) {
+ this.activeTarget = destinationFloor;
+ }
+ }
+ }
+
+ /**
+ * Moves the elevator one floor towards its target destination, or stands still.
+ *
+ *
The elevator has three possibilities:
+ *
+ *
+ * - Move up one floor
+ *
- Move down one floor
+ *
- Stand still
+ *
+ *
+ * The elevator moves in a way that eventually reaches all floors requested via {@link
+ * #requestDestinationFloor(int)}, ideally fast and fair, meaning that the average time waiting
+ * (either in corridor or inside the elevator) is minimized across all humans.
+ *
+ * This method updates the {@code currentFloor} field accordingly.
+ */
+ public void moveOneFloor() {
+
+ // log.debug("mooving elevator {} one floor", this.id);
+
+ if (this.activeTarget == null) {
+ if (!this.pendingFloors.isEmpty()) {
+
+ this.activeTarget = this.pendingFloors.iterator().next();
+
+ // log.debug(
+ // "Elevator {}, has no active target, selecting next pending floor: {}",
+ // this.id,
+ // this.activeTarget);
+
+ } else {
+ // log.debug("elevator {} is idle (no pending floors)", this.id);
+ return;
+ }
+ }
+
+ if (this.currentFloor == this.activeTarget) {
+ // log.debug(
+ // "elevator {} reached target floor {}, marking as complete", this.id,
+ // this.activeTarget);
+
+ this.pendingFloors.remove(this.activeTarget);
+
+ this.activeTarget = null;
+
+ if (!this.pendingFloors.isEmpty()) {
+ this.activeTarget = this.pendingFloors.iterator().next();
+ log.debug("elvator {} selecting next target floor: {}", this.id, this.activeTarget);
+ } else {
+ log.debug("Elevator {} has no more pending floors", this.id);
+ return;
+ }
+ }
+
+ if (this.currentFloor < this.activeTarget) {
+ this.currentFloor++;
+
+ log.debug(
+ SimulationUtils.LOG_ELEVATOR_MOVING_FROM_TO,
+ this.id,
+ TravelDirection.UP,
+ this.currentFloor - 1,
+ this.currentFloor,
+ this.activeTarget);
+
+ } else if (this.currentFloor > this.activeTarget) {
+
+ this.currentFloor--;
+
+ log.debug(
+ SimulationUtils.LOG_ELEVATOR_MOVING_FROM_TO,
+ this.id,
+ TravelDirection.DOWN,
+ this.currentFloor + 1,
+ this.currentFloor,
+ this.activeTarget);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", Elevator.class.getSimpleName() + "[", "]")
+ .add("id=" + this.id)
+ .add("minFloor=" + this.minFloor)
+ .add("floorsServed=" + this.floorsServed)
+ .add("currentFloor=" + this.currentFloor)
+ .add("pendingFloors=" + this.pendingFloors)
+ .add("activeTarget=" + this.activeTarget)
+ .toString();
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/models/ElevatorSystem.java b/Contest/Assignment/src/org/togetherjava/event/elevator/models/ElevatorSystem.java
new file mode 100644
index 0000000..ee6314f
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/models/ElevatorSystem.java
@@ -0,0 +1,129 @@
+package org.togetherjava.event.elevator.models;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.togetherjava.event.elevator.SimulationException;
+import org.togetherjava.event.elevator.api.ElevatorListener;
+import org.togetherjava.event.elevator.api.FloorPanelSystem;
+import org.togetherjava.event.elevator.enums.TravelDirection;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * System controlling all elevators of a building.
+ *
+ *
Once all elevators and humans have been registered via {@link #registerElevator(Elevator)} and
+ * {@link #registerElevatorListener(ElevatorListener)} respectively, the system can be made ready
+ * using {@link #ready()}.
+ */
+@Slf4j
+public final class ElevatorSystem implements FloorPanelSystem {
+ private final List elevators = new ArrayList<>();
+ private final List elevatorListeners = new ArrayList<>();
+
+ public ElevatorSystem() {
+ log.info("new ElevatorSystem created");
+ }
+
+ public void registerElevator(Elevator elevator) {
+ log.debug("registering new elevator {}", elevator);
+ this.elevators.add(elevator);
+ }
+
+ public void registerElevatorListener(ElevatorListener listener) {
+ log.debug("registering new listener (human) {}", listener);
+ this.elevatorListeners.add(listener);
+ }
+
+ /**
+ * Upon calling this, the system is ready to receive elevator requests. Elevators may now start
+ * moving.
+ */
+ public void ready() {
+ log.info("Starting new Elevator System");
+ this.elevatorListeners.forEach(listener -> listener.onElevatorSystemReady(this));
+ log.info("Elevator System is ready!");
+ }
+
+ /**
+ * Requests an elevator for a human standing in the corridor.
+ *
+ * This represents a human requesting that an elevator comes to pick them up for travel into
+ * the given direction. The system makes sure that an elevator will eventually reach this floor to
+ * pick up the human. The human can then enter the elevator and request their actual destination
+ * within the elevator.
+ *
+ *
The implementation selects the best elevator among all candidates to reduce the time spent
+ * waiting (either in the corridor or in the elevator itself).
+ *
+ * @param atFloor the floor where the human is waiting
+ * @param desiredTravelDirection the direction the human wants to travel
+ */
+ @Override
+ public void requestElevator(int atFloor, TravelDirection desiredTravelDirection) {
+ // log.info(
+ // "requesting any close elevator to respond to human at floor {}, going {}",
+ // atFloor,
+ // desiredTravelDirection);
+
+ List staging =
+ this.elevators.stream()
+ .filter(
+ elevator -> {
+ int min = elevator.getMinFloor();
+ return atFloor >= min && atFloor < min + elevator.getFloorsServed();
+ })
+ .toList();
+
+ if (staging.isEmpty()) {
+ throw new SimulationException("No elevators can serve floor " + atFloor);
+ }
+
+ Elevator target = staging.getFirst();
+
+ int distanceFlag = Math.abs(atFloor - target.getCurrentFloor());
+
+ for (int i = 1; i < staging.size(); i++) {
+
+ Elevator elevator = staging.get(i);
+
+ final int distance = Math.abs(atFloor - elevator.getCurrentFloor());
+
+ if (distance < distanceFlag
+ || (distance == distanceFlag && elevator.getId() < target.getId())) {
+
+ target = elevator;
+ distanceFlag = distance;
+ }
+ }
+
+ // log.info("selected elevator {}", target.getId());
+
+ target.requestDestinationFloor(atFloor);
+
+ // log.info(
+ // "Selected elevator {} at floor {} for pickup at floor {} (distance {}) direction {}",
+ // target.getId(),
+ // target.getCurrentFloor(),
+ // atFloor,
+ // distanceFlag,
+ // desiredTravelDirection);
+ }
+
+ public void moveOneFloor() {
+
+ // log.info("movement order started");
+
+ this.elevators.forEach(Elevator::moveOneFloor);
+
+ // log.info("dispatching arrived at floor events to all listeners per elevator");
+
+ this.elevators.forEach(
+ elevator ->
+ this.elevatorListeners.forEach(
+ listener -> listener.onElevatorArrivedAtFloor(elevator)));
+
+ // log.info("movement order finished");
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/models/Human.java b/Contest/Assignment/src/org/togetherjava/event/elevator/models/Human.java
new file mode 100644
index 0000000..ffa8623
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/models/Human.java
@@ -0,0 +1,190 @@
+package org.togetherjava.event.elevator.models;
+
+import java.util.OptionalInt;
+import java.util.StringJoiner;
+
+import org.togetherjava.event.elevator.api.ElevatorListener;
+import org.togetherjava.event.elevator.api.ElevatorPanel;
+import org.togetherjava.event.elevator.api.FloorPanelSystem;
+import org.togetherjava.event.elevator.enums.HumanState;
+import org.togetherjava.event.elevator.enums.TravelDirection;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * A single human that starts at a given floor and wants to reach a destination floor via an
+ * elevator.
+ *
+ * The class mainly acts upon given elevator events it listens to, for example requesting an
+ * elevator, eventually entering and exiting them.
+ */
+@Slf4j
+public final class Human implements ElevatorListener {
+ @Getter private HumanState currentState;
+ @Getter private final int startingFloor;
+ @Getter private final int destinationFloor;
+
+ /**
+ * If the human is currently inside an elevator, this is its unique ID. Otherwise, this is {@code
+ * null} to indicate that the human is currently on the corridor.
+ */
+ private Integer currentEnteredElevatorId;
+
+ /**
+ * Creates a new human.
+ *
+ *
It is supported that starting and destination floors are equal. The human will then not
+ * travel with an elevator at all.
+ *
+ * @param startingFloor the floor the human currently stands at, must be greater than or equal to
+ * 1
+ * @param destinationFloor the floor the human eventually wants to reach, must be greater than or
+ * equal to 1
+ */
+ public Human(int startingFloor, int destinationFloor) {
+ if (startingFloor <= 0 || destinationFloor <= 0) {
+ throw new IllegalArgumentException("Floors must be at least 1");
+ }
+ this.startingFloor = startingFloor;
+ this.destinationFloor = destinationFloor;
+ this.currentState = HumanState.IDLE;
+ }
+
+ /**
+ * Invoked once the elevator system is ready to accept requests. Transitions the human out of the
+ * IDLE state. If the starting and destination floors are identical, the human is marked as
+ * arrived immediately. Otherwise, the correct travel direction is determined, an elevator is
+ * requested at the starting floor, and the state changes to WAITING_FOR_ELEVATOR.
+ *
+ * @param floorPanelSystem panel system used to request an elevator
+ */
+ @Override
+ public void onElevatorSystemReady(FloorPanelSystem floorPanelSystem) {
+
+ final int start = this.startingFloor;
+
+ log.info("checking human at floor {}, state", start);
+
+ final int destination = this.destinationFloor;
+
+ if (start == destination) {
+
+ final HumanState state = HumanState.ARRIVED;
+
+ log.info("starting floor is the same as destination floor => marking state as {}", state);
+
+ this.currentState = state;
+
+ return;
+ }
+
+ TravelDirection direction = start < destination ? TravelDirection.UP : TravelDirection.DOWN;
+
+ log.info("computed direction for human at floor {} is {}", start, direction);
+
+ floorPanelSystem.requestElevator(start, direction);
+
+ HumanState newState = HumanState.WAITING_FOR_ELEVATOR;
+
+ log.info("updating human at floor {} state from {} to {}", start, this.currentState, newState);
+
+ this.currentState = newState;
+
+ log.info(" human at floor {} state updated", start);
+
+ log.info(
+ "human startin from floor {}, requesting elevator towards floor {}, direction {}",
+ start,
+ destination,
+ direction);
+ }
+
+ /**
+ * Handles the event when an elevator arrives at a floor.
+ *
+ *
If the human is currently waiting for an elevator and this event represents arrival at the
+ * human's current floor, the human can now enter the elevator and request their actual
+ * destination floor. The state has to change to TRAVELING_WITH_ELEVATOR.
+ *
+ *
If the human is currently traveling with this elevator and the event represents arrival at
+ * the human's destination floor, the human can now exit the elevator.
+ *
+ * @param elevatorPanel the panel of the elevator that arrived at a floor
+ */
+ @Override
+ public void onElevatorArrivedAtFloor(ElevatorPanel elevatorPanel) {
+ final int elevatorId = elevatorPanel.getId();
+ final int elevatorCurrentFloor = elevatorPanel.getCurrentFloor();
+
+ // log.debug(
+ // "human at floor {}, (state: {}) received elevator {} arrival at floor {}",
+ // this.startingFloor,
+ // this.currentState,
+ // elevatorId,
+ // elevatorCurrentFloor);
+
+ if (this.currentState == HumanState.ARRIVED) {
+ // log.debug("human already arrived! ignoring elevator arrival");
+ return;
+ }
+
+ // humanDestinationFloor = this.destinationFloor;
+
+ if (this.currentState == HumanState.WAITING_FOR_ELEVATOR
+ && elevatorCurrentFloor == this.startingFloor) {
+
+ // log.debug(
+ // "uman at floor {}, enters elevator {}, to travel to floor {}",
+ // this.startingFloor,
+ // elevatorId,
+ // this.destinationFloor);
+
+ this.currentEnteredElevatorId = elevatorId;
+
+ this.currentState = HumanState.TRAVELING_WITH_ELEVATOR;
+
+ elevatorPanel.requestDestinationFloor(this.destinationFloor);
+
+ // log.debug("human entered elevator {}, state changed to {}", elevatorId,
+ // this.currentState);
+
+ return;
+ }
+
+ if (this.currentState == HumanState.TRAVELING_WITH_ELEVATOR
+ && this.currentEnteredElevatorId != null
+ && this.currentEnteredElevatorId == elevatorId
+ && elevatorCurrentFloor == this.destinationFloor) {
+
+ // log.debug(
+ // "human is in elevator {}, has reached destination floor {}, exiting",
+ // elevatorId,
+ // this.destinationFloor);
+
+ this.currentEnteredElevatorId = null;
+
+ this.currentState = HumanState.ARRIVED;
+
+ // log.debug(
+ // "human exited elevator {}, the state now becomes {}", elevatorId,
+ // this.currentState);
+ }
+ }
+
+ public OptionalInt getCurrentEnteredElevatorId() {
+ return this.currentEnteredElevatorId == null
+ ? OptionalInt.empty()
+ : OptionalInt.of(this.currentEnteredElevatorId);
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", Human.class.getSimpleName() + "[", "]")
+ .add("currentState=" + this.currentState)
+ .add("startingFloor=" + this.startingFloor)
+ .add("destinationFloor=" + this.destinationFloor)
+ .add("currentEnteredElevatorId=" + this.currentEnteredElevatorId)
+ .toString();
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/models/HumanStatistics.java b/Contest/Assignment/src/org/togetherjava/event/elevator/models/HumanStatistics.java
new file mode 100644
index 0000000..a99941a
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/models/HumanStatistics.java
@@ -0,0 +1,24 @@
+package org.togetherjava.event.elevator.models;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.togetherjava.event.elevator.enums.HumanState;
+
+public final class HumanStatistics {
+ private final Human human;
+ private final Map stateToStepCount = new EnumMap<>(HumanState.class);
+
+ HumanStatistics(Human human) {
+ this.human = human;
+ }
+
+ public void step() {
+ HumanState state = this.human.getCurrentState();
+ this.stateToStepCount.put(state, this.stateToStepCount.getOrDefault(state, 0L) + 1);
+ }
+
+ public long stepsForState(HumanState state) {
+ return this.stateToStepCount.getOrDefault(state, 0L);
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/models/Simulation.java b/Contest/Assignment/src/org/togetherjava/event/elevator/models/Simulation.java
new file mode 100644
index 0000000..20fd16f
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/models/Simulation.java
@@ -0,0 +1,43 @@
+package org.togetherjava.event.elevator.models;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public final class Simulation {
+ private final List humans;
+ private final List elevators;
+ @Getter private final ElevatorSystem elevatorSystem;
+ @Getter private final View view;
+ @Getter private long stepCount;
+ @Getter private final List humanStatistics;
+
+ public Simulation(List elevators, List humans) {
+ log.info("Creating new Simulation");
+ this.elevators = new ArrayList<>(elevators);
+ this.humans = new ArrayList<>(humans);
+
+ this.elevatorSystem = new ElevatorSystem();
+ this.elevators.forEach(this.elevatorSystem::registerElevator);
+ this.humans.forEach(this.elevatorSystem::registerElevatorListener);
+
+ this.humanStatistics = this.humans.stream().map(HumanStatistics::new).toList();
+ this.view = new View(this);
+ }
+
+ public List getHumans() {
+ return Collections.unmodifiableList(this.humans);
+ }
+
+ public List getElevators() {
+ return Collections.unmodifiableList(this.elevators);
+ }
+
+ public void increaseStepCount() {
+ this.stepCount++;
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/models/View.java b/Contest/Assignment/src/org/togetherjava/event/elevator/models/View.java
new file mode 100644
index 0000000..305acfb
--- /dev/null
+++ b/Contest/Assignment/src/org/togetherjava/event/elevator/models/View.java
@@ -0,0 +1,136 @@
+package org.togetherjava.event.elevator.models;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.function.IntFunction;
+import java.util.stream.Collectors;
+
+import org.togetherjava.event.elevator.enums.HumanState;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public final class View {
+ private final Simulation simulation;
+ private static final int ELEVATOR_WIDTH = 7;
+ private static final int CORRIDOR_WIDTH = 9;
+ private static final int FLOOR_LABEL_WIDTH = 4;
+
+ public View(Simulation simulation) {
+ this.simulation = simulation;
+ }
+
+ public void printSummary() {
+ log.info(
+ "Simulation with {} elevators and {} humans.",
+ this.simulation.getElevators().size(),
+ this.simulation.getHumans().size());
+ log.debug("\tElevators: {}", this.simulation.getElevators());
+ log.debug("\tHumans: {}", this.simulation.getHumans());
+ }
+
+ public void prettyPrint() {
+ int totalFloors =
+ this.simulation.getElevators().stream()
+ .mapToInt(elevator -> elevator.getMinFloor() + elevator.getFloorsServed() - 1)
+ .max()
+ .orElseThrow();
+ Map elevatorIdToHumansCount =
+ this.simulation.getHumans().stream()
+ .collect(
+ Collectors.groupingBy(Human::getCurrentEnteredElevatorId, Collectors.counting()));
+
+ log.info(
+ "{}",
+ " ".repeat(FLOOR_LABEL_WIDTH)
+ + "_"
+ .repeat(
+ 1 + CORRIDOR_WIDTH + ELEVATOR_WIDTH * this.simulation.getElevators().size() + 1)
+ + " ");
+ for (int floor = totalFloors; floor >= 1; floor--) {
+ this.printFloor(floor, elevatorIdToHumansCount);
+ }
+ log.info(
+ " ".repeat(FLOOR_LABEL_WIDTH - 2)
+ + "^"
+ .repeat(
+ 2
+ + 2
+ + CORRIDOR_WIDTH
+ + ELEVATOR_WIDTH * this.simulation.getElevators().size()
+ + 2
+ + 2));
+ }
+
+ private void printFloor(int floor, Map elevatorIdToHumansCount) {
+ String emptyLeftLine = " ".repeat(FLOOR_LABEL_WIDTH) + "| ";
+ List leftLines = List.of(emptyLeftLine, "%3s | ".formatted(floor), emptyLeftLine);
+
+ String emptyRightLine = " |";
+ List rightLines = List.of(emptyRightLine, emptyRightLine, emptyRightLine);
+
+ List corridorLines = this.corridorForFloorToLines(floor);
+
+ List> allElevatorLines =
+ this.simulation.getElevators().stream()
+ .map(
+ elevator ->
+ this.elevatorForFloorToLines(
+ floor,
+ elevator,
+ elevatorIdToHumansCount.getOrDefault(OptionalInt.of(elevator.getId()), 0L)))
+ .toList();
+
+ List> allLines = new ArrayList<>();
+ allLines.add(leftLines);
+ allLines.add(corridorLines);
+ allLines.addAll(allElevatorLines);
+ allLines.add(rightLines);
+
+ IntFunction mergeRow =
+ row -> allLines.stream().map(lines -> lines.get(row)).collect(Collectors.joining());
+
+ log.info("{}", mergeRow.apply(0));
+ log.info("{}", mergeRow.apply(1));
+ log.info("{}", mergeRow.apply(2));
+ }
+
+ private List elevatorForFloorToLines(
+ int floor, Elevator elevator, long humansInElevator) {
+ if (floor != elevator.getCurrentFloor()) {
+ String emptyLine = " ".repeat(ELEVATOR_WIDTH / 2) + "." + " ".repeat(ELEVATOR_WIDTH / 2);
+ return List.of(emptyLine, emptyLine, emptyLine);
+ }
+
+ String humansInElevatorText = humansInElevator == 0 ? "" : Long.toString(humansInElevator);
+
+ return List.of(
+ " " + "_".repeat(ELEVATOR_WIDTH - 2) + " ",
+ "| %3s |".formatted(humansInElevatorText),
+ " -" + "%3s".formatted(elevator.getId()).replace(' ', '-') + "- ");
+ }
+
+ private List corridorForFloorToLines(int floor) {
+ long humansArrived =
+ this.simulation.getHumans().stream()
+ .filter(human -> human.getDestinationFloor() == floor)
+ .map(Human::getCurrentState)
+ .filter(HumanState.ARRIVED::equals)
+ .count();
+ long humansWaiting =
+ this.simulation.getHumans().stream()
+ .filter(human -> human.getStartingFloor() == floor)
+ .map(Human::getCurrentState)
+ .filter(
+ state ->
+ state.equals(HumanState.IDLE) || state.equals(HumanState.WAITING_FOR_ELEVATOR))
+ .count();
+
+ return List.of(
+ " %3s A | ".formatted(humansArrived),
+ " %3s W ".formatted(humansWaiting),
+ "~".repeat(CORRIDOR_WIDTH - 2) + "| ");
+ }
+}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/HumanStatistics.java b/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/HumanStatistics.java
deleted file mode 100644
index 9c2c778..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/HumanStatistics.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.togetherjava.event.elevator.simulation;
-
-import org.togetherjava.event.elevator.humans.Human;
-
-import java.util.EnumMap;
-import java.util.Map;
-
-final class HumanStatistics {
- private final Human human;
- private final Map stateToStepCount = new EnumMap<>(Human.State.class);
-
- HumanStatistics(Human human) {
- this.human = human;
- }
-
- void step() {
- Human.State state = human.getCurrentState();
- stateToStepCount.put(state, stateToStepCount.getOrDefault(state, 0L) + 1);
- }
-
- long stepsForState(Human.State state) {
- return stateToStepCount.getOrDefault(state, 0L);
- }
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/Simulation.java b/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/Simulation.java
deleted file mode 100644
index 6f462c6..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/Simulation.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package org.togetherjava.event.elevator.simulation;
-
-import org.togetherjava.event.elevator.elevators.Elevator;
-import org.togetherjava.event.elevator.elevators.ElevatorSystem;
-import org.togetherjava.event.elevator.humans.Human;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.stream.LongStream;
-import java.util.stream.Stream;
-
-public final class Simulation {
- private final List humans;
- private final List elevators;
- private final ElevatorSystem elevatorSystem;
- private final View view;
- private long stepCount;
- private final List humanStatistics;
-
- public static Simulation createSingleElevatorSingleHumanSimulation() {
- return new Simulation(List.of(new Elevator(1, 10, 5)),
- List.of(new Human(1, 10)));
- }
-
- public static Simulation createSimpleSimulation() {
- int minFloor = 1;
- int floorsServed = 10;
-
- return new Simulation(
- List.of(
- new Elevator(minFloor, floorsServed, 1),
- new Elevator(minFloor, floorsServed, 6)),
- List.of(
- new Human(1, 2),
- new Human(1, 5),
- new Human(8, 10),
- new Human(9, 3),
- new Human(10, 1)));
- }
-
- public static Simulation createRandomSimulation(int amountOfElevators, int amountOfHumans, int floorsServed) {
- return createRandomSimulation(ThreadLocalRandom.current().nextLong(), amountOfElevators, amountOfHumans, floorsServed);
- }
-
- public static Simulation createRandomSimulation(long seed, int amountOfElevators, int amountOfHumans, int floorsServed) {
- System.out.println("Seed for random simulation is: " + seed);
- Random random = new Random(seed);
-
- int minFloor = 1;
-
- List elevators = Stream.generate(() -> {
- int currentFloor = minFloor + random.nextInt(floorsServed);
- return new Elevator(minFloor, floorsServed, currentFloor);
- }).limit(amountOfElevators).toList();
-
- List humans = Stream.generate(() -> {
- int startingFloor = minFloor + random.nextInt(floorsServed);
- int destinationFloor = minFloor + random.nextInt(floorsServed);
- return new Human(startingFloor, destinationFloor);
- }).limit(amountOfHumans).toList();
-
- return new Simulation(elevators, humans);
- }
-
- public Simulation(List elevators, List humans) {
- this.elevators = new ArrayList<>(elevators);
- this.humans = new ArrayList<>(humans);
-
- elevatorSystem = new ElevatorSystem();
- this.elevators.forEach(elevatorSystem::registerElevator);
- this.humans.forEach(elevatorSystem::registerElevatorListener);
-
- humanStatistics = this.humans.stream().map(HumanStatistics::new).toList();
- view = new View(this);
- }
-
- public void startAndExecuteUntilDone(int stepLimit) {
- start();
-
- while (!isDone()) {
- step();
-
- if (stepCount >= stepLimit) {
- throw new IllegalStateException("Simulation aborted. All humans should have arrived"
- + " by now, but they did not. There is likely a bug in your code.");
- }
- }
- }
-
- public void start() {
- elevatorSystem.ready();
- }
-
- public void step() {
- elevatorSystem.moveOneFloor();
-
- humanStatistics.forEach(HumanStatistics::step);
- stepCount++;
- }
-
- public boolean isDone() {
- return humans.stream()
- .map(Human::getCurrentState)
- .allMatch(Human.State.ARRIVED::equals);
- }
-
- public long getStepCount() {
- return stepCount;
- }
-
- public List getHumans() {
- return Collections.unmodifiableList(humans);
- }
-
- public List getElevators() {
- return Collections.unmodifiableList(elevators);
- }
-
- public ElevatorSystem getElevatorSystem() {
- return elevatorSystem;
- }
-
- public void printSummary() {
- view.printSummary();
- }
-
- public void prettyPrint() {
- view.prettyPrint();
- }
-
- public void printResult() {
- System.out.println("Steps: " + stepCount);
-
- System.out.println("Median time spend per state:");
- for (Human.State state : Human.State.values()) {
- int averagePercentage = getAverageTimePercentageSpendForState(state);
- System.out.printf("\t%s: %d%%%n", state, averagePercentage);
- }
- }
-
- public int getAverageTimePercentageSpendForState(Human.State state) {
- LongStream sortedSteps = humanStatistics.stream()
- .mapToLong(stats -> stats.stepsForState(state))
- .sorted();
- long medianSteps = humanStatistics.size() % 2 == 0
- ? (long) sortedSteps.skip(humanStatistics.size() / 2 - 1).limit(2).average().orElseThrow()
- : sortedSteps.skip(humanStatistics.size() / 2).findFirst().orElseThrow();
-
- long medianPercentage = 100 * medianSteps / stepCount;
- return (int) medianPercentage;
- }
-}
diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/View.java b/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/View.java
deleted file mode 100644
index e61eb63..0000000
--- a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/View.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package org.togetherjava.event.elevator.simulation;
-
-import org.togetherjava.event.elevator.elevators.Elevator;
-import org.togetherjava.event.elevator.humans.Human;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.OptionalInt;
-import java.util.function.IntFunction;
-import java.util.stream.Collectors;
-
-public final class View {
- private final Simulation simulation;
- private static final int ELEVATOR_WIDTH = 7;
- private static final int CORRIDOR_WIDTH = 9;
- private static final int FLOOR_LABEL_WIDTH = 4;
-
- public View(Simulation simulation) {
- this.simulation = simulation;
- }
-
- public void printSummary() {
- System.out.printf("Simulation with %d elevators and %d humans.%n", simulation.getElevators().size(), simulation.getHumans().size());
- System.out.println("\tElevators: " + simulation.getElevators());
- System.out.println("\tHumans: " + simulation.getHumans());
- }
-
- public void prettyPrint() {
- int totalFloors = simulation.getElevators().stream()
- .mapToInt(elevator -> elevator.getMinFloor() + elevator.getFloorsServed() - 1)
- .max()
- .orElseThrow();
- Map elevatorIdToHumansCount = simulation.getHumans().stream()
- .collect(Collectors.groupingBy(Human::getCurrentEnteredElevatorId,
- Collectors.counting()));
-
- printRoof();
- for (int floor = totalFloors; floor >= 1; floor--) {
- printFloor(floor, elevatorIdToHumansCount);
- }
- printBasement();
- }
-
- private void printRoof() {
- System.out.println(" ".repeat(FLOOR_LABEL_WIDTH)
- + " " + "_".repeat(1 + CORRIDOR_WIDTH + ELEVATOR_WIDTH * simulation.getElevators().size() + 1) + " ");
- }
-
- private void printBasement() {
- System.out.println(" ".repeat(FLOOR_LABEL_WIDTH - 2)
- + "^".repeat(2 + 2 + CORRIDOR_WIDTH + ELEVATOR_WIDTH * simulation.getElevators().size() + 2 + 2));
- }
-
- private void printFloor(int floor, Map elevatorIdToHumansCount) {
- String emptyLeftLine = " ".repeat(FLOOR_LABEL_WIDTH) + "| ";
- List leftLines = List.of(emptyLeftLine,
- "%3s | ".formatted(floor),
- emptyLeftLine);
-
- String emptyRightLine = " |";
- List rightLines = List.of(emptyRightLine, emptyRightLine, emptyRightLine);
-
- List corridorLines = corridorForFloorToLines(floor);
-
- List> allElevatorLines = simulation.getElevators().stream()
- .map(elevator -> elevatorForFloorToLines(floor, elevator,
- elevatorIdToHumansCount.getOrDefault(OptionalInt.of(elevator.getId()), 0L)))
- .toList();
-
- List> allLines = new ArrayList<>();
- allLines.add(leftLines);
- allLines.add(corridorLines);
- allLines.addAll(allElevatorLines);
- allLines.add(rightLines);
-
- IntFunction mergeRow = row -> allLines.stream().map(lines -> lines.get(row)).collect(Collectors.joining());
-
- System.out.println(mergeRow.apply(0));
- System.out.println(mergeRow.apply(1));
- System.out.println(mergeRow.apply(2));
- }
-
- private List elevatorForFloorToLines(int floor, Elevator elevator, long humansInElevator) {
- if (floor != elevator.getCurrentFloor()) {
- String emptyLine = " ".repeat(ELEVATOR_WIDTH / 2) + "." + " ".repeat(ELEVATOR_WIDTH / 2);
- return List.of(emptyLine, emptyLine, emptyLine);
- }
-
- String humansInElevatorText = humansInElevator == 0 ? "" : Long.toString(humansInElevator);
-
- return List.of(" " + "_".repeat(ELEVATOR_WIDTH - 2) + " ",
- "| %3s |".formatted(humansInElevatorText),
- " -" + "%3s".formatted(elevator.getId()).replace(' ', '-') + "- ");
- }
-
- private List corridorForFloorToLines(int floor) {
- long humansArrived = simulation.getHumans().stream()
- .filter(human -> human.getDestinationFloor() == floor)
- .map(Human::getCurrentState)
- .filter(Human.State.ARRIVED::equals)
- .count();
- long humansWaiting = simulation.getHumans().stream()
- .filter(human -> human.getStartingFloor() == floor)
- .map(Human::getCurrentState)
- .filter(state -> state.equals(Human.State.IDLE) || state.equals(Human.State.WAITING_FOR_ELEVATOR))
- .count();
-
- return List.of(" %3s A | ".formatted(humansArrived),
- " %3s W ".formatted(humansWaiting),
- "~".repeat(CORRIDOR_WIDTH - 2) + "| ");
- }
-}
diff --git a/Contest/Assignment/test/PreviousElevatorSystemTest.java b/Contest/Assignment/test/PreviousElevatorSystemTest.java
index 1d060e6..5d60b78 100644
--- a/Contest/Assignment/test/PreviousElevatorSystemTest.java
+++ b/Contest/Assignment/test/PreviousElevatorSystemTest.java
@@ -1,63 +1,69 @@
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.togetherjava.event.elevator.elevators.Elevator;
-import org.togetherjava.event.elevator.elevators.ElevatorSystem;
-import org.togetherjava.event.elevator.elevators.FloorPanelSystem;
-import org.togetherjava.event.elevator.humans.ElevatorListener;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.description;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.*;
+import org.junit.jupiter.api.Test;
+import org.togetherjava.event.elevator.api.ElevatorListener;
+import org.togetherjava.event.elevator.api.FloorPanelSystem;
+import org.togetherjava.event.elevator.models.Elevator;
+import org.togetherjava.event.elevator.models.ElevatorSystem;
final class PreviousElevatorSystemTest {
- @Test
- void testImplementsFloorPanelSystem() {
- assertTrue(FloorPanelSystem.class.isAssignableFrom(ElevatorSystem.class),
- "The ElevatorSystem class is supposed to implement the FloorPanelSystem interface.");
- }
+ @Test
+ void testImplementsFloorPanelSystem() {
+ assertTrue(
+ FloorPanelSystem.class.isAssignableFrom(ElevatorSystem.class),
+ "The ElevatorSystem class is supposed to implement the FloorPanelSystem interface.");
+ }
- @Test
- void testReady() {
- ElevatorSystem system = new ElevatorSystem();
+ @Test
+ void testReady() {
+ ElevatorSystem system = new ElevatorSystem();
- List listeners =
- Stream.generate(() -> mock(ElevatorListener.class)).limit(5).toList();
+ List listeners =
+ Stream.generate(() -> mock(ElevatorListener.class)).limit(5).toList();
- listeners.forEach(system::registerElevatorListener);
+ listeners.forEach(system::registerElevatorListener);
- system.ready();
+ system.ready();
- for (ElevatorListener listener : listeners) {
- verify(listener, description(
- "The 'ready' method of ElevatorSystem is supposed to invoke 'onElevatorSystemReady' on all registered elevator listeners.")).onElevatorSystemReady(
- system);
- }
+ for (ElevatorListener listener : listeners) {
+ verify(
+ listener,
+ description(
+ "The 'ready' method of ElevatorSystem is supposed to invoke 'onElevatorSystemReady' on all registered elevator listeners."))
+ .onElevatorSystemReady(system);
}
+ }
- @Test
- void testMoveOneFloor() {
- ElevatorSystem system = new ElevatorSystem();
+ @Test
+ void testMoveOneFloor() {
+ ElevatorSystem system = new ElevatorSystem();
- Supplier createAnyElevator = () -> new Elevator(1, 5, 2);
- List elevators = Stream.generate(createAnyElevator).limit(3).toList();
+ Supplier createAnyElevator = () -> new Elevator(1, 5, 2);
+ List elevators = Stream.generate(createAnyElevator).limit(3).toList();
- List listeners =
- Stream.generate(() -> mock(ElevatorListener.class)).limit(5).toList();
+ List listeners =
+ Stream.generate(() -> mock(ElevatorListener.class)).limit(5).toList();
- elevators.forEach(system::registerElevator);
- listeners.forEach(system::registerElevatorListener);
- system.ready();
+ elevators.forEach(system::registerElevator);
+ listeners.forEach(system::registerElevatorListener);
+ system.ready();
- system.moveOneFloor();
- for (Elevator elevator : elevators) {
- for (ElevatorListener listener : listeners) {
- verify(listener, description(
- "The 'moveOneFloor' method of ElevatorSystem is supposed to invoke 'onElevatorArrivedAtFloor' on all registered elevator listeners for each registered elevator.")).onElevatorArrivedAtFloor(
- elevator);
- }
- }
+ system.moveOneFloor();
+ for (Elevator elevator : elevators) {
+ for (ElevatorListener listener : listeners) {
+ verify(
+ listener,
+ description(
+ "The 'moveOneFloor' method of ElevatorSystem is supposed to invoke 'onElevatorArrivedAtFloor' on all registered elevator listeners for each registered elevator."))
+ .onElevatorArrivedAtFloor(elevator);
+ }
}
+ }
}
diff --git a/Contest/Assignment/test/PreviousElevatorTest.java b/Contest/Assignment/test/PreviousElevatorTest.java
index 28a8b1e..2185a64 100644
--- a/Contest/Assignment/test/PreviousElevatorTest.java
+++ b/Contest/Assignment/test/PreviousElevatorTest.java
@@ -1,69 +1,77 @@
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.togetherjava.event.elevator.elevators.Elevator;
-import org.togetherjava.event.elevator.elevators.ElevatorPanel;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.Test;
+import org.togetherjava.event.elevator.api.ElevatorPanel;
+import org.togetherjava.event.elevator.models.Elevator;
final class PreviousElevatorTest {
- @Test
- void testGetFloors() {
- int expectedMinFloor = 3;
- int expectedFloorsServed = 4;
- int expectedCurrentFloor = 5;
- Elevator elevator =
- new Elevator(expectedMinFloor, expectedFloorsServed, expectedCurrentFloor);
+ @Test
+ void testGetFloors() {
+ int expectedMinFloor = 3;
+ int expectedFloorsServed = 4;
+ int expectedCurrentFloor = 5;
+ Elevator elevator = new Elevator(expectedMinFloor, expectedFloorsServed, expectedCurrentFloor);
- int actualMinFloor = elevator.getMinFloor();
- int actualFloorsServed = elevator.getFloorsServed();
- int actualCurrentFloor = elevator.getCurrentFloor();
+ int actualMinFloor = elevator.getMinFloor();
+ int actualFloorsServed = elevator.getFloorsServed();
+ int actualCurrentFloor = elevator.getCurrentFloor();
- assertEquals(expectedMinFloor, actualMinFloor,
- "The getMinFloor() method of Elevator needs to return the min floor given in the constructor.");
+ assertEquals(
+ expectedMinFloor,
+ actualMinFloor,
+ "The getMinFloor() method of Elevator needs to return the min floor given in the constructor.");
- assertEquals(expectedFloorsServed, actualFloorsServed,
- "The getFloorsServed() method of Elevator needs to return the served floors given in the constructor.");
+ assertEquals(
+ expectedFloorsServed,
+ actualFloorsServed,
+ "The getFloorsServed() method of Elevator needs to return the served floors given in the constructor.");
- assertEquals(expectedCurrentFloor, actualCurrentFloor,
- "The getCurrentFloor() method of Elevator needs to return the current floor given in the constructor.");
- }
+ assertEquals(
+ expectedCurrentFloor,
+ actualCurrentFloor,
+ "The getCurrentFloor() method of Elevator needs to return the current floor given in the constructor.");
+ }
- @Test
- void testGetId() {
- int anyMinFloor = 3;
- int anyFloorsServed = 4;
- int anyCurrentFloor = 5;
+ @Test
+ void testGetId() {
+ int anyMinFloor = 3;
+ int anyFloorsServed = 4;
+ int anyCurrentFloor = 5;
- Set elevatorIds = new HashSet<>();
- for (int i = 0; i < 500; i++) {
- Elevator elevator = new Elevator(anyMinFloor, anyFloorsServed, anyCurrentFloor);
- int id = elevator.getId();
+ Set elevatorIds = new HashSet<>();
+ for (int i = 0; i < 500; i++) {
+ Elevator elevator = new Elevator(anyMinFloor, anyFloorsServed, anyCurrentFloor);
+ int id = elevator.getId();
- assertFalse(elevatorIds.contains(id),
- "The IDs of elevators must be unique. Found a collision with ID %d when generating a few of them.".formatted(
- id));
- elevatorIds.add(id);
- }
+ assertFalse(
+ elevatorIds.contains(id),
+ "The IDs of elevators must be unique. Found a collision with ID %d when generating a few of them."
+ .formatted(id));
+ elevatorIds.add(id);
}
+ }
- @Test
- void testImplementsElevatorPanel() {
- assertTrue(ElevatorPanel.class.isAssignableFrom(Elevator.class),
- "The Elevator class is supposed to implement the ElevatorPanel interface.");
- }
+ @Test
+ void testImplementsElevatorPanel() {
+ assertTrue(
+ ElevatorPanel.class.isAssignableFrom(Elevator.class),
+ "The Elevator class is supposed to implement the ElevatorPanel interface.");
+ }
- @Test
- void testMoveOneFloor() {
- boolean hasMethod = Arrays.stream(Elevator.class.getMethods())
- .map(Method::getName)
- .anyMatch("moveOneFloor"::equals);
+ @Test
+ void testMoveOneFloor() {
+ boolean hasMethod =
+ Arrays.stream(Elevator.class.getMethods())
+ .map(Method::getName)
+ .anyMatch("moveOneFloor"::equals);
- assertTrue(hasMethod, "The Elevator class is supposed to have a 'void moveOneFloor()' method.");
- }
+ assertTrue(hasMethod, "The Elevator class is supposed to have a 'void moveOneFloor()' method.");
+ }
}
diff --git a/Contest/Assignment/test/PreviousHumanTest.java b/Contest/Assignment/test/PreviousHumanTest.java
index 4cfbf72..e6bab1e 100644
--- a/Contest/Assignment/test/PreviousHumanTest.java
+++ b/Contest/Assignment/test/PreviousHumanTest.java
@@ -1,6 +1,5 @@
-import org.junit.jupiter.api.Test;
-import org.togetherjava.event.elevator.humans.ElevatorListener;
-import org.togetherjava.event.elevator.humans.Human;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.Objects;
@@ -8,82 +7,97 @@
import java.util.Set;
import java.util.stream.Collectors;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.Test;
+import org.togetherjava.event.elevator.api.ElevatorListener;
+import org.togetherjava.event.elevator.enums.HumanState;
+import org.togetherjava.event.elevator.models.Human;
final class PreviousHumanTest {
- @Test
- void testStateEnum() {
- Set actualNames = Arrays.stream(Human.State.values())
- .map(Objects::toString)
- .collect(Collectors.toSet());
-
- Set expectedNames =
- Set.of("IDLE", "WAITING_FOR_ELEVATOR", "TRAVELING_WITH_ELEVATOR", "ARRIVED");
-
- assertEquals(expectedNames, actualNames,
- "The names of the enum values for 'Human.State' do not match. Please check that the spelling is exactly the same.");
- }
-
- @Test
- void testGetCurrentState() {
- int anyStartingFloor = 1;
- int anyDestinationFloor = 3;
- Human human = new Human(anyStartingFloor, anyDestinationFloor);
-
- Human.State currentState = human.getCurrentState();
-
- assertEquals("IDLE", currentState.name(),
- "The getCurrentState() method of Human needs to return the current state, which is supposed to be IDLE for now.");
- }
-
- @Test
- void testGetFloors() {
- int expectedStartingFloor = 1;
- int expectedDestinationFloor = 3;
- Human human = new Human(expectedStartingFloor, expectedDestinationFloor);
-
- int actualStartingFloor = human.getStartingFloor();
- int actualDestinationFloor = human.getDestinationFloor();
-
- assertEquals(expectedStartingFloor, actualStartingFloor,
- "The getStartingFloor() method of Human needs to return the starting floor given in the constructor.");
-
- assertEquals(expectedDestinationFloor, actualDestinationFloor,
- "The getDestinationFloor() method of Human needs to return the destination floor given in the constructor.");
- }
-
- @Test
- void testSameFloorAllowed() {
- int expectedStartingAndDestinationFloor = 2;
- Human human =
- new Human(expectedStartingAndDestinationFloor, expectedStartingAndDestinationFloor);
-
- int actualStartingFloor = human.getStartingFloor();
- int actualDestinationFloor = human.getDestinationFloor();
-
- assertEquals(expectedStartingAndDestinationFloor, actualStartingFloor,
- "The human needs to support that both, starting and destination floor, have the same value.");
-
- assertEquals(expectedStartingAndDestinationFloor, actualDestinationFloor,
- "The human needs to support that both, starting and destination floor, have the same value.");
- }
-
- @Test
- void testCurrentEnteredElevatorId() {
- int anyStartingFloor = 1;
- int anyDestinationFloor = 3;
- Human human = new Human(anyStartingFloor, anyDestinationFloor);
-
- OptionalInt currentEnteredElevatorId = human.getCurrentEnteredElevatorId();
-
- assertTrue(currentEnteredElevatorId.isEmpty(),
- "The getCurrentEnteredElevatorId() method of Human needs to return the current entered elevator ID, which is supposed to be empty for now.");
- }
-
- @Test
- void testImplementsElevatorListener() {
- assertTrue(ElevatorListener.class.isAssignableFrom(Human.class),
- "The Human class is supposed to implement the ElevatorListener interface.");
- }
+ @Test
+ void testStateEnum() {
+ Set actualNames =
+ Arrays.stream(HumanState.values()).map(Objects::toString).collect(Collectors.toSet());
+
+ Set expectedNames =
+ Set.of("IDLE", "WAITING_FOR_ELEVATOR", "TRAVELING_WITH_ELEVATOR", "ARRIVED");
+
+ assertEquals(
+ expectedNames,
+ actualNames,
+ "The names of the enum values for 'Human.State' do not match. Please check that the spelling is exactly the same.");
+ }
+
+ @Test
+ void testGetCurrentState() {
+ int anyStartingFloor = 1;
+ int anyDestinationFloor = 3;
+ Human human = new Human(anyStartingFloor, anyDestinationFloor);
+
+ HumanState currentState = human.getCurrentState();
+
+ assertEquals(
+ "IDLE",
+ currentState.name(),
+ "The getCurrentState() method of Human needs to return the current state, which is supposed to be IDLE for now.");
+ }
+
+ @Test
+ void testGetFloors() {
+ int expectedStartingFloor = 1;
+ int expectedDestinationFloor = 3;
+ Human human = new Human(expectedStartingFloor, expectedDestinationFloor);
+
+ int actualStartingFloor = human.getStartingFloor();
+ int actualDestinationFloor = human.getDestinationFloor();
+
+ assertEquals(
+ expectedStartingFloor,
+ actualStartingFloor,
+ "The getStartingFloor() method of Human needs to return the starting floor given in the constructor.");
+
+ assertEquals(
+ expectedDestinationFloor,
+ actualDestinationFloor,
+ "The getDestinationFloor() method of Human needs to return the destination floor given in the constructor.");
+ }
+
+ @Test
+ void testSameFloorAllowed() {
+ int expectedStartingAndDestinationFloor = 2;
+ Human human =
+ new Human(expectedStartingAndDestinationFloor, expectedStartingAndDestinationFloor);
+
+ int actualStartingFloor = human.getStartingFloor();
+ int actualDestinationFloor = human.getDestinationFloor();
+
+ assertEquals(
+ expectedStartingAndDestinationFloor,
+ actualStartingFloor,
+ "The human needs to support that both, starting and destination floor, have the same value.");
+
+ assertEquals(
+ expectedStartingAndDestinationFloor,
+ actualDestinationFloor,
+ "The human needs to support that both, starting and destination floor, have the same value.");
+ }
+
+ @Test
+ void testCurrentEnteredElevatorId() {
+ int anyStartingFloor = 1;
+ int anyDestinationFloor = 3;
+ Human human = new Human(anyStartingFloor, anyDestinationFloor);
+
+ OptionalInt currentEnteredElevatorId = human.getCurrentEnteredElevatorId();
+
+ assertTrue(
+ currentEnteredElevatorId.isEmpty(),
+ "The getCurrentEnteredElevatorId() method of Human needs to return the current entered elevator ID, which is supposed to be empty for now.");
+ }
+
+ @Test
+ void testImplementsElevatorListener() {
+ assertTrue(
+ ElevatorListener.class.isAssignableFrom(Human.class),
+ "The Human class is supposed to implement the ElevatorListener interface.");
+ }
}
diff --git a/Contest/Assignment/test/SanityTest.java b/Contest/Assignment/test/SanityTest.java
index 7f79e27..8e5a5db 100644
--- a/Contest/Assignment/test/SanityTest.java
+++ b/Contest/Assignment/test/SanityTest.java
@@ -1,191 +1,224 @@
-import org.junit.jupiter.api.Test;
-import org.togetherjava.event.elevator.elevators.Elevator;
-import org.togetherjava.event.elevator.humans.Human;
-import org.togetherjava.event.elevator.simulation.Simulation;
+import static org.junit.jupiter.api.Assertions.*;
import java.util.Map;
import java.util.OptionalInt;
import java.util.function.Function;
import java.util.stream.Collectors;
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
+import org.togetherjava.event.elevator.SimulationService;
+import org.togetherjava.event.elevator.enums.HumanState;
+import org.togetherjava.event.elevator.models.Elevator;
+import org.togetherjava.event.elevator.models.Human;
+import org.togetherjava.event.elevator.models.Simulation;
final class SanityTest {
- private static final String ERROR_MESSAGE_PRE =
- "Sanity checks for 'Simulation.createRandomSimulation(5, 5, 50, 10)' failed. ";
+ private static final String ERROR_MESSAGE_PRE =
+ "Sanity checks for 'SimulationService.createRandomSimulation(5, 5, 50, 10)' failed. ";
- @Test
- void testSimulationSanity() {
- Simulation simulation = Simulation.createRandomSimulation(5, 5, 50, 10);
- int stepLimit = 1_000;
+ @Test
+ void testSimulationSanity() {
+ Simulation simulation = SimulationService.createRandomSimulation(5, 5, 50, 10);
+ int stepLimit = 1_000;
- requiredHumansToBeIdle(simulation);
+ requiredHumansToBeIdle(simulation);
- var previousSnapshot = new SimulationSnapshot(simulation);
+ var previousSnapshot = new SimulationSnapshot(simulation);
- simulation.start();
+ SimulationService.start(simulation);
- var currentSnapshot = new SimulationSnapshot(simulation);
- verifySnapshotSanity(previousSnapshot, currentSnapshot, simulation);
- previousSnapshot = currentSnapshot;
+ var currentSnapshot = new SimulationSnapshot(simulation);
+ verifySnapshotSanity(previousSnapshot, currentSnapshot, simulation);
+ previousSnapshot = currentSnapshot;
- requiredHumansNotToBeIdle(simulation);
+ requiredHumansNotToBeIdle(simulation);
- while (!simulation.isDone()) {
- simulation.step();
+ while (!SimulationService.isDone(simulation)) {
+ SimulationService.step(simulation);
- currentSnapshot = new SimulationSnapshot(simulation);
- verifySnapshotSanity(previousSnapshot, currentSnapshot, simulation);
- previousSnapshot = currentSnapshot;
+ currentSnapshot = new SimulationSnapshot(simulation);
+ verifySnapshotSanity(previousSnapshot, currentSnapshot, simulation);
+ previousSnapshot = currentSnapshot;
- if (simulation.getStepCount() >= stepLimit) {
- fail(ERROR_MESSAGE_PRE + "All humans should have arrived by now, but they did not."
- + " There is likely a bug in your code.");
- }
- }
+ if (simulation.getStepCount() >= stepLimit) {
+ fail(
+ ERROR_MESSAGE_PRE
+ + "All humans should have arrived by now, but they did not."
+ + " There is likely a bug in your code.");
+ }
}
-
- private static void requiredHumansToBeIdle(Simulation simulation) {
- for (Human human : simulation.getHumans()) {
- assertEquals(Human.State.IDLE, human.getCurrentState(), ERROR_MESSAGE_PRE
- + "Before the start of the simulation, all humans should be in the IDLE state. But '%s' was not.".formatted(
- human));
- }
+ }
+
+ private static void requiredHumansToBeIdle(Simulation simulation) {
+ for (Human human : simulation.getHumans()) {
+ assertEquals(
+ HumanState.IDLE,
+ human.getCurrentState(),
+ ERROR_MESSAGE_PRE
+ + "Before the start of the simulation, all humans should be in the IDLE state. But '%s' was not."
+ .formatted(human));
}
-
- private static void requiredHumansNotToBeIdle(Simulation simulation) {
- for (Human human : simulation.getHumans()) {
- assertNotEquals(Human.State.IDLE, human.getCurrentState(), ERROR_MESSAGE_PRE
- + "Right after the start of the simulation, all humans should not be in the IDLE state anymore. But '%s' was.".formatted(
- human));
- }
+ }
+
+ private static void requiredHumansNotToBeIdle(Simulation simulation) {
+ for (Human human : simulation.getHumans()) {
+ assertNotEquals(
+ HumanState.IDLE,
+ human.getCurrentState(),
+ ERROR_MESSAGE_PRE
+ + "Right after the start of the simulation, all humans should not be in the IDLE state anymore. But '%s' was."
+ .formatted(human));
}
-
- private static void verifySnapshotSanity(SimulationSnapshot previousSnapshot,
- SimulationSnapshot currentSnapshot, Simulation simulation) {
- for (Human human : simulation.getHumans()) {
- HumanSnapshot previousHumanSnapshot = previousSnapshot.getHumanSnapshot(human);
- HumanSnapshot currentHumanSnapshot = currentSnapshot.getHumanSnapshot(human);
-
- Human.State previousState = previousHumanSnapshot.state();
- Human.State currentState = currentHumanSnapshot.state();
-
- boolean changedBackToIdle =
- previousState != Human.State.IDLE && currentState == Human.State.IDLE;
- assertFalse(changedBackToIdle, ERROR_MESSAGE_PRE
- + "Humans must never change their state back to IDLE. But '%s' did.".formatted(
- human));
-
- boolean changedOutOfArrived =
- previousState == Human.State.ARRIVED && currentState != Human.State.ARRIVED;
- assertFalse(changedOutOfArrived, ERROR_MESSAGE_PRE
- + "Once a human arrived, they must never change their state again. But '%s' did.".formatted(
- human));
-
- boolean enteredElevator = previousState == Human.State.WAITING_FOR_ELEVATOR
- && currentState == Human.State.TRAVELING_WITH_ELEVATOR;
- if (enteredElevator) {
- OptionalInt maybeElevatorId = currentHumanSnapshot.currentElevatorId();
- assertTrue(maybeElevatorId.isPresent(), ERROR_MESSAGE_PRE
- + "When a human enters an elevator, they need a current elevator id. But '%s' does not.".formatted(
- human));
- assertTrue(previousHumanSnapshot.currentElevatorId().isEmpty(), ERROR_MESSAGE_PRE
- + "When a human enters an elevator, they must not have been in an elevator previously. But '%s' was.".formatted(
- human));
-
- ElevatorSnapshot currentElevatorSnapshot =
- currentSnapshot.getElevatorSnapshot(maybeElevatorId.orElseThrow());
- assertEquals(human.getStartingFloor(), currentElevatorSnapshot.currentFloor(),
- ERROR_MESSAGE_PRE
- + "When a human enters an elevator, the elevator must be at the humans starting floor. But '%s' entered elevator with ID '%d' at a different floor.".formatted(
- human, maybeElevatorId.orElseThrow()));
- }
-
- boolean exitedElevator =
- previousState != Human.State.ARRIVED && currentState == Human.State.ARRIVED;
- if (exitedElevator) {
- assertTrue(currentHumanSnapshot.currentElevatorId().isEmpty(), ERROR_MESSAGE_PRE
- + "When a human exits an elevator, they must not have a current elevator id anymore. But '%s' has.".formatted(
- human));
-
- // Only if the human actually travelled around
- if (human.getStartingFloor() != human.getDestinationFloor()) {
- OptionalInt maybeElevatorId = previousHumanSnapshot.currentElevatorId();
- assertTrue(maybeElevatorId.isPresent(), ERROR_MESSAGE_PRE
- + "When a human exits an elevator, they must have had a current elevator id previously. But '%s' does not.".formatted(
- human));
-
- assertEquals(Human.State.TRAVELING_WITH_ELEVATOR, previousState,
- "When a human exits an elevator, their previous state must be traveling. But '%s' was in state %s.".formatted(
- human, previousState));
-
- ElevatorSnapshot currentElevatorSnapshot =
- currentSnapshot.getElevatorSnapshot(maybeElevatorId.orElseThrow());
- assertEquals(human.getDestinationFloor(),
- currentElevatorSnapshot.currentFloor(), ERROR_MESSAGE_PRE
- + "When a human exits an elevator, the elevator must be at the humans destination floor. But '%s' exited elevator with ID '%d' at a different floor.".formatted(
- human, maybeElevatorId.orElseThrow()));
- }
- }
-
- for (Elevator elevator : simulation.getElevators()) {
- int previousFloor =
- previousSnapshot.getElevatorSnapshot(elevator.getId()).currentFloor();
- int currentFloor =
- currentSnapshot.getElevatorSnapshot(elevator.getId()).currentFloor();
-
- int travelDistance = Math.abs(previousFloor - currentFloor);
- assertTrue(travelDistance <= 1, ERROR_MESSAGE_PRE
- + "Elevators must either travel 0 or 1 floor each step, but '%s' travelled %d floors.".formatted(
- elevator, travelDistance));
-
- int minFloor = elevator.getMinFloor();
- int maxFloor = minFloor + elevator.getFloorsServed() - 1;
-
- assertTrue(currentFloor >= minFloor, ERROR_MESSAGE_PRE
- + "Elevators must never travel beyond their min floor, but '%s' reached floor '%d'.".formatted(
- elevator, currentFloor));
- assertTrue(currentFloor <= maxFloor, ERROR_MESSAGE_PRE
- + "Elevators must never travel beyond their max floor, but '%s' reached floor '%d'.".formatted(
- elevator, currentFloor));
- }
+ }
+
+ private static void verifySnapshotSanity(
+ SimulationSnapshot previousSnapshot,
+ SimulationSnapshot currentSnapshot,
+ Simulation simulation) {
+ for (Human human : simulation.getHumans()) {
+ HumanSnapshot previousHumanSnapshot = previousSnapshot.getHumanSnapshot(human);
+ HumanSnapshot currentHumanSnapshot = currentSnapshot.getHumanSnapshot(human);
+
+ HumanState previousState = previousHumanSnapshot.state();
+ HumanState currentState = currentHumanSnapshot.state();
+
+ boolean changedBackToIdle =
+ previousState != HumanState.IDLE && currentState == HumanState.IDLE;
+ assertFalse(
+ changedBackToIdle,
+ ERROR_MESSAGE_PRE
+ + "Humans must never change their state back to IDLE. But '%s' did."
+ .formatted(human));
+
+ boolean changedOutOfArrived =
+ previousState == HumanState.ARRIVED && currentState != HumanState.ARRIVED;
+ assertFalse(
+ changedOutOfArrived,
+ ERROR_MESSAGE_PRE
+ + "Once a human arrived, they must never change their state again. But '%s' did."
+ .formatted(human));
+
+ boolean enteredElevator =
+ previousState == HumanState.WAITING_FOR_ELEVATOR
+ && currentState == HumanState.TRAVELING_WITH_ELEVATOR;
+ if (enteredElevator) {
+ OptionalInt maybeElevatorId = currentHumanSnapshot.currentElevatorId();
+ assertTrue(
+ maybeElevatorId.isPresent(),
+ ERROR_MESSAGE_PRE
+ + "When a human enters an elevator, they need a current elevator id. But '%s' does not."
+ .formatted(human));
+ assertTrue(
+ previousHumanSnapshot.currentElevatorId().isEmpty(),
+ ERROR_MESSAGE_PRE
+ + "When a human enters an elevator, they must not have been in an elevator previously. But '%s' was."
+ .formatted(human));
+
+ ElevatorSnapshot currentElevatorSnapshot =
+ currentSnapshot.getElevatorSnapshot(maybeElevatorId.orElseThrow());
+ assertEquals(
+ human.getStartingFloor(),
+ currentElevatorSnapshot.currentFloor(),
+ ERROR_MESSAGE_PRE
+ + "When a human enters an elevator, the elevator must be at the humans starting floor. But '%s' entered elevator with ID '%d' at a different floor."
+ .formatted(human, maybeElevatorId.orElseThrow()));
+ }
+
+ boolean exitedElevator =
+ previousState != HumanState.ARRIVED && currentState == HumanState.ARRIVED;
+ if (exitedElevator) {
+ assertTrue(
+ currentHumanSnapshot.currentElevatorId().isEmpty(),
+ ERROR_MESSAGE_PRE
+ + "When a human exits an elevator, they must not have a current elevator id anymore. But '%s' has."
+ .formatted(human));
+
+ // Only if the human actually travelled around
+ if (human.getStartingFloor() != human.getDestinationFloor()) {
+ OptionalInt maybeElevatorId = previousHumanSnapshot.currentElevatorId();
+ assertTrue(
+ maybeElevatorId.isPresent(),
+ ERROR_MESSAGE_PRE
+ + "When a human exits an elevator, they must have had a current elevator id previously. But '%s' does not."
+ .formatted(human));
+
+ assertEquals(
+ HumanState.TRAVELING_WITH_ELEVATOR,
+ previousState,
+ "When a human exits an elevator, their previous state must be traveling. But '%s' was in state %s."
+ .formatted(human, previousState));
+
+ ElevatorSnapshot currentElevatorSnapshot =
+ currentSnapshot.getElevatorSnapshot(maybeElevatorId.orElseThrow());
+ assertEquals(
+ human.getDestinationFloor(),
+ currentElevatorSnapshot.currentFloor(),
+ ERROR_MESSAGE_PRE
+ + "When a human exits an elevator, the elevator must be at the humans destination floor. But '%s' exited elevator with ID '%d' at a different floor."
+ .formatted(human, maybeElevatorId.orElseThrow()));
}
+ }
+
+ for (Elevator elevator : simulation.getElevators()) {
+ int previousFloor = previousSnapshot.getElevatorSnapshot(elevator.getId()).currentFloor();
+ int currentFloor = currentSnapshot.getElevatorSnapshot(elevator.getId()).currentFloor();
+
+ int travelDistance = Math.abs(previousFloor - currentFloor);
+ assertTrue(
+ travelDistance <= 1,
+ ERROR_MESSAGE_PRE
+ + "Elevators must either travel 0 or 1 floor each step, but '%s' travelled %d floors."
+ .formatted(elevator, travelDistance));
+
+ int minFloor = elevator.getMinFloor();
+ int maxFloor = minFloor + elevator.getFloorsServed() - 1;
+
+ assertTrue(
+ currentFloor >= minFloor,
+ ERROR_MESSAGE_PRE
+ + "Elevators must never travel beyond their min floor, but '%s' reached floor '%d'."
+ .formatted(elevator, currentFloor));
+ assertTrue(
+ currentFloor <= maxFloor,
+ ERROR_MESSAGE_PRE
+ + "Elevators must never travel beyond their max floor, but '%s' reached floor '%d'."
+ .formatted(elevator, currentFloor));
+ }
}
+ }
- private record HumanSnapshot(Human.State state, OptionalInt currentElevatorId) {
- static HumanSnapshot of(Human human) {
- return new HumanSnapshot(human.getCurrentState(), human.getCurrentEnteredElevatorId());
- }
+ private record HumanSnapshot(HumanState state, OptionalInt currentElevatorId) {
+ static HumanSnapshot of(Human human) {
+ return new HumanSnapshot(human.getCurrentState(), human.getCurrentEnteredElevatorId());
}
+ }
-
- private record ElevatorSnapshot(int currentFloor) {
- static ElevatorSnapshot of(Elevator elevator) {
- return new ElevatorSnapshot(elevator.getCurrentFloor());
- }
+ private record ElevatorSnapshot(int currentFloor) {
+ static ElevatorSnapshot of(Elevator elevator) {
+ return new ElevatorSnapshot(elevator.getCurrentFloor());
}
+ }
+ private static class SimulationSnapshot {
+ private final Map humanToSnapshots;
+ private final Map elevatorIdToSnapshots;
- private static class SimulationSnapshot {
- private final Map humanToSnapshots;
- private final Map elevatorIdToSnapshots;
+ SimulationSnapshot(Simulation simulation) {
+ this.humanToSnapshots =
+ simulation.getHumans().stream()
+ .collect(Collectors.toMap(Function.identity(), HumanSnapshot::of));
- SimulationSnapshot(Simulation simulation) {
- humanToSnapshots = simulation.getHumans()
- .stream()
- .collect(Collectors.toMap(Function.identity(), HumanSnapshot::of));
-
- elevatorIdToSnapshots = simulation.getElevators()
- .stream()
- .collect(Collectors.toMap(Elevator::getId, ElevatorSnapshot::of));
- }
+ this.elevatorIdToSnapshots =
+ simulation.getElevators().stream()
+ .collect(Collectors.toMap(Elevator::getId, ElevatorSnapshot::of));
+ }
- HumanSnapshot getHumanSnapshot(Human human) {
- return humanToSnapshots.get(human);
- }
+ HumanSnapshot getHumanSnapshot(Human human) {
+ return this.humanToSnapshots.get(human);
+ }
- ElevatorSnapshot getElevatorSnapshot(int elevatorId) {
- return elevatorIdToSnapshots.get(elevatorId);
- }
+ ElevatorSnapshot getElevatorSnapshot(int elevatorId) {
+ return this.elevatorIdToSnapshots.get(elevatorId);
}
+ }
}
diff --git a/Contest/Assignment/test/SimulationTest.java b/Contest/Assignment/test/SimulationTest.java
index 440f444..6da53d7 100644
--- a/Contest/Assignment/test/SimulationTest.java
+++ b/Contest/Assignment/test/SimulationTest.java
@@ -1,80 +1,99 @@
-import org.junit.jupiter.api.*;
-import org.togetherjava.event.elevator.simulation.Simulation;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.togetherjava.event.elevator.SimulationService;
+import org.togetherjava.event.elevator.models.Simulation;
+
final class SimulationTest {
- private boolean simulationFailed;
- private static boolean stopSimulations = false;
+ private boolean simulationFailed;
+ private static boolean stopSimulations = false;
- @BeforeEach
- void setUp() {
- assumeFalse(stopSimulations,
- "A previous simulation failed already, skipping the remaining.");
+ @BeforeEach
+ void setUp() {
+ assumeFalse(stopSimulations, "A previous simulation failed already, skipping the remaining.");
- simulationFailed = true;
- }
+ this.simulationFailed = true;
+ }
- @AfterEach
- void tearDown() {
- if (simulationFailed) {
- stopSimulations = true;
- }
+ @AfterEach
+ void tearDown() {
+ if (this.simulationFailed) {
+ stopSimulations = true;
}
+ }
- @Test
- void testSingleElevatorSingleHumanSimulation() {
- Simulation simulation = Simulation.createSingleElevatorSingleHumanSimulation();
- int stepLimit = 200;
+ @Test
+ void testSingleElevatorSingleHumanSimulation() {
+ Simulation simulation = SimulationService.createSingleElevatorSingleHumanSimulation();
+ int stepLimit = 200;
- assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit),
- "Simulation obtained by 'Simulation.createSingleElevatorSingleHumanSimulation()' was aborted because it could not finish in time.");
+ assertDoesNotThrow(
+ () -> startAndExecuteUntilDone(simulation, stepLimit),
+ "Simulation obtained by 'SimulationService.createSingleElevatorSingleHumanSimulation()' was aborted because it could not finish in time.");
- simulationFailed = false;
- }
+ this.simulationFailed = false;
+ }
- @Test
- void testSimpleSimulation() {
- Simulation simulation = Simulation.createSimpleSimulation();
- int stepLimit = 500;
+ @Test
+ void testSimpleSimulation() {
+ Simulation simulation = SimulationService.createSimpleSimulation();
+ int stepLimit = 500;
- assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit),
- "Simulation obtained by 'Simulation.createSimpleSimulation()' was aborted because it could not finish in time.");
+ assertDoesNotThrow(
+ () -> startAndExecuteUntilDone(simulation, stepLimit),
+ "Simulation obtained by 'SimulationService.createSimpleSimulation()' was aborted because it could not finish in time.");
- simulationFailed = false;
- }
+ this.simulationFailed = false;
+ }
- @Test
- void testRandomSimulationSmall() {
- Simulation simulation = Simulation.createRandomSimulation(1, 5, 50, 10);
- int stepLimit = 1_000;
+ @Test
+ void testRandomSimulationSmall() {
+ Simulation simulation = SimulationService.createRandomSimulation(1, 5, 50, 10);
+ int stepLimit = 1_000;
- assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit),
- "Simulation obtained by 'Simulation.createRandomSimulation(1, 5, 50, 10)' was aborted because it could not finish in time.");
+ assertDoesNotThrow(
+ () -> startAndExecuteUntilDone(simulation, stepLimit),
+ "Simulation obtained by 'SimulationService.createRandomSimulation(1, 5, 50, 10)' was aborted because it could not finish in time.");
- simulationFailed = false;
- }
+ this.simulationFailed = false;
+ }
- @Test
- void testRandomSimulationMedium() {
- Simulation simulation = Simulation.createRandomSimulation(2, 20, 1_000, 50);
- int stepLimit = 10_000;
+ @Test
+ void testRandomSimulationMedium() {
+ Simulation simulation = SimulationService.createRandomSimulation(2, 20, 1_000, 50);
+ int stepLimit = 10_000;
- assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit),
- "Simulation obtained by 'Simulation.createRandomSimulation(2, 20, 1_000, 50)' was aborted because it could not finish in time.");
+ assertDoesNotThrow(
+ () -> startAndExecuteUntilDone(simulation, stepLimit),
+ "Simulation obtained by 'SimulationService.createRandomSimulation(2, 20, 1_000, 50)' was aborted because it could not finish in time.");
- simulationFailed = false;
- }
+ this.simulationFailed = false;
+ }
+
+ @Test
+ void testRandomSimulationBig() {
+ Simulation simulation = SimulationService.createRandomSimulation(3, 100, 100_000, 100);
+ int stepLimit = 100_000;
+
+ assertDoesNotThrow(
+ () -> startAndExecuteUntilDone(simulation, stepLimit),
+ "Simulation obtained by 'SimulationService.createRandomSimulation(3, 100, 100_000, 100)' was aborted because it could not finish in time.");
- @Test
- void testRandomSimulationBig() {
- Simulation simulation = Simulation.createRandomSimulation(3, 100, 100_000, 100);
- int stepLimit = 100_000;
+ this.simulationFailed = false;
+ }
- assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit),
- "Simulation obtained by 'Simulation.createRandomSimulation(3, 100, 100_000, 100)' was aborted because it could not finish in time.");
+ private static void startAndExecuteUntilDone(Simulation simulation, int stepLimit) {
+ SimulationService.start(simulation);
- simulationFailed = false;
+ while (!SimulationService.isDone(simulation)) {
+ SimulationService.step(simulation);
+ if (simulation.getStepCount() >= stepLimit) {
+ throw new IllegalStateException(
+ "Simulation aborted. All humans should have arrived by now, but they did not. There is likely a bug in your code.");
+ }
}
+ }
}