diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java b/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java index e9e4159..18cb393 100644 --- a/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java +++ b/Contest/Assignment/src/org/togetherjava/event/elevator/Main.java @@ -19,11 +19,11 @@ public static void main(final String[] args) { // 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 simulation = Simulation.createSingleElevatorSingleHumanSimulation(); +// Simulation simulation = Simulation.createSimpleSimulation(); + Simulation simulation = Simulation.createRandomSimulation(5, 100, 10); +// Simulation simulation = Simulation.createRandomSimulation(3, 100, 100000, 100); +// Simulation simulation = Simulation.createCustomSimulation(); simulation.printSummary(); System.out.println("Starting simulation..."); @@ -35,7 +35,7 @@ public static void main(final String[] args) { simulation.step(); simulation.prettyPrint(); - if (simulation.getStepCount() >= 100_000) { + if (simulation.getStepCount() >= 100_00) { throw new IllegalStateException("Simulation aborted. All humans should have arrived" + " by now, but they did not. There is likely a bug in your code."); } diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/Elevator.java b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/Elevator.java index 51333b2..b8e0fd8 100644 --- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/Elevator.java +++ b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/Elevator.java @@ -1,8 +1,10 @@ package org.togetherjava.event.elevator.elevators; -import java.util.StringJoiner; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import static org.togetherjava.event.elevator.elevators.ElevatorSystem.mod; + /** * A single elevator that can serve a given amount of floors. *

@@ -16,6 +18,10 @@ public final class Elevator implements ElevatorPanel { private final int minFloor; private final int floorsServed; private int currentFloor; + private int numberOfHumansAboard; + private int humanExited; + private List requestedDestinationFloors; + private TravelDirection travellingDirection; /** * Creates a new elevator. @@ -37,6 +43,7 @@ public Elevator(int minFloor, int floorsServed, int currentFloor) { this.minFloor = minFloor; this.currentFloor = currentFloor; this.floorsServed = floorsServed; + this.requestedDestinationFloors = new ArrayList<>(); } @Override @@ -57,12 +64,20 @@ public int getCurrentFloor() { return currentFloor; } + public TravelDirection getTravellingDirection() { + return travellingDirection; + } + @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. + requestedDestinationFloors.add(destinationFloor); + if(destinationFloor > currentFloor) travellingDirection = TravelDirection.UP; + else if(destinationFloor < currentFloor) travellingDirection = TravelDirection.DOWN; + else travellingDirection = null; System.out.println("Request for destination floor received"); } @@ -76,15 +91,91 @@ public void moveOneFloor() { // 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. + if(requestedDestinationFloors.isEmpty()) { + lastResort(); + return; + } + sortingList(); + int currentRequest = requestedDestinationFloors.getLast(); + if(currentRequest > currentFloor) { + currentFloor++; + travellingDirection = TravelDirection.UP; + } + else if(currentRequest < currentFloor) { + currentFloor--; + travellingDirection = TravelDirection.DOWN; + } + else { + requestedDestinationFloors.removeLast(); + removeRequests(); + } System.out.println("Request to move a floor received"); } + /** + * It is possible that the requests ends for the current elevator but there are still humans onboard. + * In that case the paternoster is the last resort to make sure the humans onboard reach the destination floor. + */ + private void lastResort() { + if(currentFloor == minFloor) travellingDirection = TravelDirection.UP; + else if(currentFloor == (minFloor + floorsServed -1)) travellingDirection = TravelDirection.DOWN; + else if(numberOfHumansAboard != 0) { + if(travellingDirection == TravelDirection.UP) currentFloor++; + else if(travellingDirection == TravelDirection.DOWN) currentFloor--; + } + } + + /** + * For instance, a number of humans can exit to the current floor. + * But the request for the current floor can make the elevator come to the current floor again or stay at it for consecutive steps. + * To prevent such loss of steps, this method removes the number of request according to the number of humans exited. + */ + private void removeRequests() { + for(int i = 0; i < humanExited; i++) { + for(int j = 0; j < requestedDestinationFloors.size(); j++) { + if(requestedDestinationFloors.get(j) == currentFloor) { + requestedDestinationFloors.remove(j); + humanExited--; + j--; + } + } + } + } + + /** + * To sort the list based on how far the requested floor is from the current floor + * The idea is to make the elevator go to the nearest or farthest request based on strategy. + */ + + private void sortingList() { + for(int i = 0; i < requestedDestinationFloors.size() - 1; i++) { + if(mod(requestedDestinationFloors.get(i) - currentFloor) < mod(requestedDestinationFloors.get(i+1)) - currentFloor) { + int temp = requestedDestinationFloors.get(i + 1); + requestedDestinationFloors.remove(i + 1); + requestedDestinationFloors.add(i + 1,requestedDestinationFloors.get(i)); + requestedDestinationFloors.remove(i); + requestedDestinationFloors.add(i,temp); + } + } + } + + @Override + public void enteredElevator() { + numberOfHumansAboard++; + } + + @Override + public void exitedElevator() { + numberOfHumansAboard--; + humanExited++; + } + @Override public synchronized String toString() { return new StringJoiner(", ", Elevator.class.getSimpleName() + "[", "]").add("id=" + id) .add("minFloor=" + minFloor) .add("floorsServed=" + floorsServed) - .add("currentFloor=" + currentFloor) + .add("currentFloor=" + this.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 index 386ec77..669869e 100644 --- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorPanel.java +++ b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorPanel.java @@ -25,4 +25,15 @@ public interface ElevatorPanel { * @param destinationFloor the desired destination, must be within the range served by this elevator */ void requestDestinationFloor(int destinationFloor); + + + /** + * Counts the number of humans exited the elevator and updates the number of humans aboard counter. + */ + void exitedElevator(); + + /** + * Counts the number of humans entered the elevator. + */ + void enteredElevator(); } diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorSystem.java b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorSystem.java index fadfe56..49f3b1c 100644 --- a/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorSystem.java +++ b/Contest/Assignment/src/org/togetherjava/event/elevator/elevators/ElevatorSystem.java @@ -2,8 +2,7 @@ import org.togetherjava.event.elevator.humans.ElevatorListener; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** * System controlling all elevators of a building. @@ -16,6 +15,7 @@ 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); } @@ -39,9 +39,48 @@ public void requestElevator(int atFloor, TravelDirection desiredTravelDirection) // 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). + + final Elevator bestElevator = getBestElevator(atFloor, desiredTravelDirection); + bestElevator.requestDestinationFloor(atFloor); + System.out.println("Request for elevator received"); } + /** + * Cases covered :- + * 1. Lift has no requests, that means travelling direction is null. This makes sure each and every elevator moves to + * take requests from humans. + * 2. Lift has requests, and it'll go in the direction of the said human currently requesting. + * This prioritises that the human requesting hops in the elevator moving in their direction. + * 3.Lift has requests, and it'll go against the direction of the said human currently requesting. + * This ensures that humans will get a lift coming in there direction even if they may have to wait longer. + * + * @param atFloor the floor from which the human is requesting from. + * @param desiredTravelDirection the direction said human wants to go, this will help to provide them the best lift. + * @return + */ + private Elevator getBestElevator(int atFloor, TravelDirection desiredTravelDirection) { + Elevator bestElevator = elevators.getFirst(); + for(Elevator elevator : elevators) { + if(elevator.getTravellingDirection() == null && mod((bestElevator.getCurrentFloor() - atFloor)) > mod((elevator.getCurrentFloor() - atFloor))) { + bestElevator = elevator; + } + else if(elevator.getTravellingDirection() == desiredTravelDirection && mod((bestElevator.getCurrentFloor() - atFloor)) > mod((elevator.getCurrentFloor() - atFloor))) { + bestElevator = elevator; + } + else if(mod((bestElevator.getCurrentFloor() - atFloor)) > mod((elevator.getCurrentFloor() - atFloor))) { + bestElevator = elevator; + } + } + return bestElevator; + } + + static int mod(int num) { + int modValue = num; + if(modValue < 0) modValue *= -1; + return modValue; + } + 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/humans/ElevatorListener.java b/Contest/Assignment/src/org/togetherjava/event/elevator/humans/ElevatorListener.java index a124b1b..67e348a 100644 --- a/Contest/Assignment/src/org/togetherjava/event/elevator/humans/ElevatorListener.java +++ b/Contest/Assignment/src/org/togetherjava/event/elevator/humans/ElevatorListener.java @@ -25,4 +25,5 @@ public interface ElevatorListener { * 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 index 0af2511..2265078 100644 --- a/Contest/Assignment/src/org/togetherjava/event/elevator/humans/Human.java +++ b/Contest/Assignment/src/org/togetherjava/event/elevator/humans/Human.java @@ -2,9 +2,11 @@ import org.togetherjava.event.elevator.elevators.ElevatorPanel; import org.togetherjava.event.elevator.elevators.FloorPanelSystem; +import org.togetherjava.event.elevator.elevators.TravelDirection; import java.util.OptionalInt; import java.util.StringJoiner; +import java.util.concurrent.atomic.AtomicInteger; /** * A single human that starts at a given floor and wants to @@ -14,9 +16,12 @@ * for example requesting an elevator, eventually entering and exiting them. */ public final class Human implements ElevatorListener { + private static final AtomicInteger NEXT_ID = new AtomicInteger(0); + private final int id; private State currentState; private final int startingFloor; private final int destinationFloor; + private boolean requestedDestinationFloor; /** * 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. @@ -36,13 +41,16 @@ public Human(int startingFloor, int destinationFloor) { if (startingFloor <= 0 || destinationFloor <= 0) { throw new IllegalArgumentException("Floors must be at least 1"); } - + this.id = NEXT_ID.getAndIncrement(); this.startingFloor = startingFloor; this.destinationFloor = destinationFloor; - - currentState = State.IDLE; + this.requestedDestinationFloor = false; + this.currentState = State.IDLE; } + public int getId() { + return id; + } public State getCurrentState() { return currentState; } @@ -60,6 +68,15 @@ 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. + if(startingFloor == destinationFloor) { + currentState = State.ARRIVED; + return; + } + TravelDirection travelDirection; + if(startingFloor - destinationFloor > 0) travelDirection = TravelDirection.UP; + else travelDirection = TravelDirection.DOWN; + floorPanelSystem.requestElevator(startingFloor, travelDirection); + currentState = State.WAITING_FOR_ELEVATOR; System.out.println("Ready-event received"); } @@ -70,7 +87,25 @@ public void onElevatorArrivedAtFloor(ElevatorPanel elevatorPanel) { // 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"); + if(currentEnteredElevatorId == null) { + if (currentState.equals(State.WAITING_FOR_ELEVATOR) && elevatorPanel.getCurrentFloor() == this.getStartingFloor()) { + currentEnteredElevatorId = elevatorPanel.getId(); + if(!requestedDestinationFloor) { + elevatorPanel.requestDestinationFloor(this.destinationFloor); + elevatorPanel.enteredElevator(); + requestedDestinationFloor = true; + } + this.currentState = State.TRAVELING_WITH_ELEVATOR; + } + } + else { + if (currentState.equals(State.TRAVELING_WITH_ELEVATOR) && elevatorPanel.getId() == this.currentEnteredElevatorId && elevatorPanel.getCurrentFloor() == this.getDestinationFloor()) { + this.currentState = State.ARRIVED; + this.currentEnteredElevatorId = null; + elevatorPanel.exitedElevator(); + System.out.println("Arrived-event received"); + } + } } public OptionalInt getCurrentEnteredElevatorId() { diff --git a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/Simulation.java b/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/Simulation.java index 6f462c6..a10936e 100644 --- a/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/Simulation.java +++ b/Contest/Assignment/src/org/togetherjava/event/elevator/simulation/Simulation.java @@ -64,6 +64,10 @@ public static Simulation createRandomSimulation(long seed, int amountOfElevators return new Simulation(elevators, humans); } + public static Simulation createCustomSimulation() { + return new Simulation(List.of(new Elevator(1, 10, 10)), + List.of(new Human(2, 2),new Human(1, 2))); + } public Simulation(List elevators, List humans) { this.elevators = new ArrayList<>(elevators); @@ -96,7 +100,6 @@ public void start() { public void step() { elevatorSystem.moveOneFloor(); - humanStatistics.forEach(HumanStatistics::step); stepCount++; } diff --git a/Contest/Assignment/test/SimulationTest.java b/Contest/Assignment/test/SimulationTest.java index 440f444..e22076a 100644 --- a/Contest/Assignment/test/SimulationTest.java +++ b/Contest/Assignment/test/SimulationTest.java @@ -41,7 +41,6 @@ void testSimpleSimulation() { assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit), "Simulation obtained by 'Simulation.createSimpleSimulation()' was aborted because it could not finish in time."); - simulationFailed = false; } @@ -52,7 +51,6 @@ void testRandomSimulationSmall() { assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit), "Simulation obtained by 'Simulation.createRandomSimulation(1, 5, 50, 10)' was aborted because it could not finish in time."); - simulationFailed = false; } @@ -61,17 +59,17 @@ void testRandomSimulationMedium() { Simulation simulation = Simulation.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."); - simulationFailed = false; + } @Test void testRandomSimulationBig() { Simulation simulation = Simulation.createRandomSimulation(3, 100, 100_000, 100); int stepLimit = 100_000; - assertDoesNotThrow(() -> simulation.startAndExecuteUntilDone(stepLimit), "Simulation obtained by 'Simulation.createRandomSimulation(3, 100, 100_000, 100)' was aborted because it could not finish in time.");