Functional Scope (In-Scope)
- N Floors & M Elevators: Support customizable scale, controlling multiple distinct elevator cabins traversing standard floor heights.
- Dual-Request Ingestion: Process external hall calls (floor + direction button pressed outside) and internal cabin calls (target floor button pressed inside).
- Minimized Wait Times: Swappable dispatch strategies to minimize elevator distance and passenger wait cycles.
- Dynamic Stop Sorting (SCAN): Cabins should process floors efficiently on their path (e.g. stopping for floor 3 when moving from 0 to 5) before switching direction.
Explicit Boundaries (Out-of-Scope)
- No Weight Sensors / Hardware Alarms: Omit physical cabin components (brakes, cable tension, overload warning lights).
- No Interactive Floor Displays: UI feedback is aggregated as status prints in the controller event tick loop.
Clean reference designs showcasing operational patterns and thread-safety in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
enum Direction { UP, DOWN, IDLE }
class Request {
private final int targetFloor;
private final Direction direction;
public Request(int targetFloor, Direction direction) {
this.targetFloor = targetFloor;
this.direction = direction;
}
public int getTargetFloor() { return targetFloor; }
public Direction getDirection() { return direction; }
}
class Elevator {
private final int id;
private int currentFloor = 0;
private Direction direction = Direction.IDLE;
private final TreeSet<Integer> upRequests = new TreeSet<>();
private final TreeSet<Integer> downRequests = new TreeSet<>();
public Elevator(int id) {
this.id = id;
}
public int getId() { return id; }
public synchronized int getCurrentFloor() { return currentFloor; }
public synchronized Direction getDirection() { return direction; }
public synchronized void addRequest(int floor) {
if (floor > currentFloor) {
upRequests.add(floor);
if (direction == Direction.IDLE) {
direction = Direction.UP;
}
} else if (floor < currentFloor) {
downRequests.add(floor);
if (direction == Direction.IDLE) {
direction = Direction.DOWN;
}
} else {
System.out.printf("[Elevator %d] Already at floor %d. Doors open.\n", id, floor);
}
}
public synchronized void step() {
if (direction == Direction.UP) {
if (!upRequests.isEmpty()) {
currentFloor++;
System.out.printf("[Elevator %d] Moving UP: reached floor %d\n", id, currentFloor);
if (upRequests.contains(currentFloor)) {
upRequests.remove(currentFloor);
System.out.printf("[Elevator %d] STOPPED at floor %d. Doors open/close.\n", id, currentFloor);
}
if (upRequests.isEmpty()) {
direction = downRequests.isEmpty() ? Direction.IDLE : Direction.DOWN;
}
} else {
direction = downRequests.isEmpty() ? Direction.IDLE : Direction.DOWN;
}
} else if (direction == Direction.DOWN) {
if (!downRequests.isEmpty()) {
currentFloor--;
System.out.printf("[Elevator %d] Moving DOWN: reached floor %d\n", id, currentFloor);
if (downRequests.contains(currentFloor)) {
downRequests.remove(currentFloor);
System.out.printf("[Elevator %d] STOPPED at floor %d. Doors open/close.\n", id, currentFloor);
}
if (downRequests.isEmpty()) {
direction = upRequests.isEmpty() ? Direction.IDLE : Direction.UP;
}
} else {
direction = upRequests.isEmpty() ? Direction.IDLE : Direction.UP;
}
}
}
public synchronized boolean hasRequests() {
return !upRequests.isEmpty() || !downRequests.isEmpty();
}
}
interface DispatchStrategy {
Elevator selectElevator(List<Elevator> elevators, Request request);
}
class OptimalDispatchStrategy implements DispatchStrategy {
@Override
public Elevator selectElevator(List<Elevator> elevators, Request request) {
Elevator best = null;
int minCost = Integer.MAX_VALUE;
for (Elevator e : elevators) {
int cost = calculateCost(e, request);
if (cost < minCost) {
minCost = cost;
best = e;
}
}
return best != null ? best : elevators.get(0);
}
private int calculateCost(Elevator e, Request r) {
int currentFloor = e.getCurrentFloor();
Direction dir = e.getDirection();
int target = r.getTargetFloor();
if (dir == Direction.IDLE) {
return Math.abs(currentFloor - target);
} else if (dir == Direction.UP && target >= currentFloor && r.getDirection() == Direction.UP) {
return target - currentFloor;
} else if (dir == Direction.DOWN && target <= currentFloor && r.getDirection() == Direction.DOWN) {
return currentFloor - target;
} else {
return Math.abs(currentFloor - target) + 100;
}
}
}
class ElevatorController {
private final List<Elevator> elevators;
private final DispatchStrategy strategy;
public ElevatorController(int elevatorCount, DispatchStrategy strategy) {
this.elevators = new CopyOnWriteArrayList<>();
for (int i = 1; i <= elevatorCount; i++) {
elevators.add(new Elevator(i));
}
this.strategy = strategy;
}
public void requestElevator(int floor, Direction direction) {
Request req = new Request(floor, direction);
Elevator selected = strategy.selectElevator(elevators, req);
System.out.printf("[Controller] Dispatched Elevator %d to floor %d (%s)\n", selected.getId(), floor, direction);
selected.addRequest(floor);
}
public void step() {
for (Elevator e : elevators) {
e.step();
}
}
public boolean hasActiveRequests() {
for (Elevator e : elevators) {
if (e.hasRequests()) return true;
}
return false;
}
public List<Elevator> getElevators() { return elevators; }
}
public class Main {
public static void main(String[] args) {
ElevatorController controller = new ElevatorController(2, new OptimalDispatchStrategy());
controller.requestElevator(3, Direction.UP);
controller.requestElevator(7, Direction.DOWN);
int ticks = 0;
while (controller.hasActiveRequests() && ticks < 15) {
System.out.printf("--- Tick %d ---\n", ++ticks);
controller.step();
}
}
}