Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Contest/Assignment/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<variable name="ROOT_LEVEL" value="${LOG_LEVEL:-INFO}"/>
<property name="PATTERN" value="%d{HH:mm:ss.SSS} %-5level [%thread] %logger - %msg%n"/>

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>

<root level="${ROOT_LEVEL}">
<appender-ref ref="CONSOLE"/>
</root>

</configuration>

71 changes: 32 additions & 39 deletions Contest/Assignment/src/org/togetherjava/event/elevator/Main.java
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should append the exception itself as last param log.error(..., e)

}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
Comment on lines +47 to +51
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dangling log


Random random = new Random(seed);

final int minFloor = 1;

List<Elevator> elevators =
Stream.generate(
() -> {
int currentFloor = minFloor + random.nextInt(floorsServed);
return new Elevator(minFloor, floorsServed, currentFloor);
})
.limit(amountOfElevators)
.toList();

List<Human> 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<Human> humans = simulation.getHumans();
return humans.stream().map(Human::getCurrentState).allMatch(HumanState.ARRIVED::equals);
}
}
Original file line number Diff line number Diff line change
@@ -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";
Comment on lines +18 to +19
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover code


public static final String LOG_ELEVATOR_MOVING_FROM_TO =
"elevator {} moving {} from {} to {} (target: {})";
Comment on lines +21 to +22
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pattern-strings should have sth like PATTERN in the name so its clear that they still need a formatter


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();
}
Comment on lines +28 to +36
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels super obsolete and doesnt really save anyone anything.
simulation.prettyPrint() versus SimulationUtils.prettyPrint(simulation)


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> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
Loading