Elevator System
Problem statement
Design the control system for a building with multiple elevators: handle hall calls (up/down buttons on floors) and car calls (floor buttons inside), move elevators efficiently, open/close doors, and respect capacity and emergency states.
Requirements
Functional
Multiple elevators; N floors; external (hall) and internal (car) requests.
Dispatch a hall call to the best elevator; serve requests in an efficient order.
Door open/close with timers and obstruction sensor; overload detection.
Maintenance/emergency modes; display current floor and direction.
Non-functional
Real-time responsiveness; no starvation of any floor.
Pluggable dispatch algorithms (nearest car, SCAN/LOOK, load-balanced).
Thread-safe request queues; each elevator runs independently.
API
void ElevatorSystem.requestElevator(int floor, Direction dir) // hall button
void Elevator.selectFloor(int floor) // car button
void Elevator.step() // advance one tick (simulation) / run loop
ElevatorStatus Elevator.status()
void ElevatorSystem.setMaintenance(int elevatorId, boolean on)
Data model
ElevatorSystem(controller):List<Elevator>,DispatchStrategy, hall-call queues per floor.Elevator: id,currentFloor,Direction(UP/DOWN/IDLE),ElevatorState(MOVING/STOPPED/DOOR_OPEN/MAINTENANCE),TreeSet<Integer> upStops,TreeSet<Integer> downStops,Door, capacity/load.Request: floor, direction (hall) or target floor (car), timestamp.Door: state, open/close with timer.DispatchStrategy(interface):Elevator select(List<Elevator>, HallRequest).Display,Buttonpanels as observers.
Design
- Per-elevator LOOK algorithm: keep two sorted sets of stops (up and down). While moving up, serve the next stop ≥ current from
upStops; when none remain, reverse and servedownStops. This is the disk-scheduling LOOK algorithm and avoids starvation. - Dispatch strategy for hall calls: score each elevator by estimated arrival time considering direction and pending stops (an idle elevator or one moving toward the floor in the same direction scores best); assign to the minimum.
- State machine per elevator:
IDLE → MOVING → STOPPED → DOOR_OPEN → DOOR_CLOSING → (obstruction? DOOR_OPEN) → MOVING/IDLE;MAINTENANCEis a terminal state until cleared. - Observer: displays and hall lights subscribe to elevator events (
ArrivedAt(floor),DirectionChanged). - Threading: one worker thread per elevator consuming its stop sets; controller adds stops under a lock per elevator.
Class diagram
Deep dives
Why two sorted sets: the next stop in the current direction is a ceiling/floor lookup — O(log n) — and direction reversal is just switching sets.
Nearest-car scoring example: if elevator is at 3 going UP and the call is at 7 UP → distance 4; call at 2 DOWN → must finish up-run first, so cost = (top stop − 3) + (top stop − 2).
Starvation: LOOK guarantees each pending stop is reached within one sweep.
Capacity: overloaded elevator ignores new hall calls until load drops; car calls still honoured.
Bottlenecks & mitigations
Single controller lock for all elevators → lock per elevator; controller only reads snapshots for scoring.
Many hall calls at rush hour → batch assignment; consider destination dispatch (user enters floor at the hall).
Scoring cost O(elevators × stops) → fine for real buildings (≤ 32 elevators).
Failure modes
- Door obstruction: sensor event re-opens the door; after N retries alert maintenance.
- Elevator fault: state → MAINTENANCE; controller redistributes its pending hall calls; car calls cancelled with an announcement.
- Power failure: elevators move to the nearest floor on backup power and open doors.
- Duplicate button presses: stops are sets — idempotent.
Code skeleton
from enum import Enum
from bisect import insort, bisect_left, bisect_right
from threading import Lock
class Dir(Enum): UP=1; DOWN=-1; IDLE=0
class Elevator:
def __init__(self, eid, floors):
self.id, self.floor, self.dir = eid, 0, Dir.IDLE
self.up, self.down, self.lock = [], [], Lock()
def add_stop(self, f):
with self.lock:
if f > self.floor: insort(self.up, f)
elif f < self.floor: insort(self.down, f)
if self.dir == Dir.IDLE: self.dir = Dir.UP if f > self.floor else Dir.DOWN
def step(self):
with self.lock:
if self.dir == Dir.UP:
if self.up: self.floor += 1
if self.up and self.up[0] == self.floor: self.up.pop(0); self.open_door()
if not self.up: self.dir = Dir.DOWN if self.down else Dir.IDLE
elif self.dir == Dir.DOWN:
if self.down: self.floor -= 1
if self.down and self.down[-1] == self.floor: self.down.pop(); self.open_door()
if not self.down: self.dir = Dir.UP if self.up else Dir.IDLE
def open_door(self): print(f"E{self.id} door open at {self.floor}")
def cost(self, floor, d):
if self.dir == Dir.IDLE: return abs(self.floor - floor)
same_dir_ahead = (self.dir == Dir.UP and d == Dir.UP and floor >= self.floor) or \
(self.dir == Dir.DOWN and d == Dir.DOWN and floor <= self.floor)
if same_dir_ahead: return abs(self.floor - floor)
far = (self.up[-1] if self.up else self.floor) if self.dir == Dir.UP else (self.down[0] if self.down else self.floor)
return abs(self.floor - far) + abs(far - floor)
class ElevatorSystem:
def __init__(self, n, floors): self.elevators = [Elevator(i, floors) for i in range(n)]
def request(self, floor, d):
best = min(self.elevators, key=lambda e: e.cost(floor, d))
best.add_stop(floor); return best.id
Follow-up questions interviewers ask
Implement destination dispatch (enter destination before boarding).
Add express elevators serving only certain floors.
How would you simulate and measure average wait time?