We’re designing a simplified elevator control system for a single building. The system must assign elevators to pickup requests and move them efficiently across floors while serving passenger drop-offs.
To keep the scope focused, this system operates entirely in memory and assumes a fixed number of floors and elevators. There is no persistence, no distributed coordination, and no modeling of real-world details like door timing, passenger capacity, or hardware faults. Movement is simulated in discrete steps rather than continuous motion.
The API exposed by the controller is:
request_pickup(floor, direction)request_dropoff(elevator_id, floor)step()— advances the simulation by one time unit
Here are some ideas you might have
Always choose the nearest elevator
A naive approach is assigning the elevator with the smallest absolute distance. The problems with this approach are:
- Elevator might be moving in the opposite direction
- Requests behind the elevator become inefficient
- Causes frequent direction changes
- Leads to poor passenger wait times
First available elevator
Another idea is assigning the first idle elevator. The problems are:
- Uneven load distribution
- Active elevators may ignore nearby requests
- Idle elevators may be far away
- Wastes elevator movement and increases response time
What must always be true
Before designing the system, define correctness rules.
- Elevators must only move within building floor bounds
- Pickup requests must eventually be served
- Elevators should continue in their current direction while requests exist in that direction
- Direction changes occur only when no requests remain in the current direction
- The most appropriate elevator should serve each pickup request
Core Scheduling Idea
The system models each elevator as a stateful agent that maintains its own request queues. Requests are separated by direction up_stops and down_stops
This mirrors how real elevators operate:
- While moving up, serve only upward stops
- While moving down, serve only downward stops
This prevents inefficient zig-zag movement.
State Model
Elevator
- id
- current_floor
- direction
- up_stops
- down_stops
ElevatorController
- elevators
Elevator
An elevator represents an independent moving unit.
State:
- current_floor — current position
- direction — UP, DOWN, or IDLE
- up_stops — floors to visit while moving upward
- down_stops — floors to visit while moving downward
Requests are stored in sets because:
- duplicate stops are unnecessary
- membership checks are constant time
ElevatorController
The controller coordinates multiple elevators. The responsibilities are:
- accept pickup requests
- choose the best elevator
- forward dropoff requests
- advance system simulation
Request Types
Pickup request
Generated when a passenger presses a hall button. The controller chooses the best elevator and inserts the stop into that elevator’s queue.
Dropoff request
Generated inside the elevator. The destination floor is added to the elevator’s stops based on direction.
Choosing the Best Elevator
Each elevator computes a distance score representing how costly it would be to serve the request. The distance depends on:
- elevator direction
- elevator position
- existing scheduled stops
Case 1: Elevator moving toward request
Example:
Elevator: floor 3 → going UP
Request: floor 6 → UP
Distance is floor - current_floor. The elevator can serve it naturally.
Case 2: Elevator moving away from request
Example:
Elevator: floor 6 → going UP
Request: floor 3 → DOWN
The elevator must:
- Finish current upward stops
- Reverse direction
- Come back down
Distance becomes distance_to_top + distance_back
Case 3: Elevator idle
Idle elevators simply use absolute distance abs(current_floor - request_floor)
Simulation Loop
The controller advances the system with step().
Algorithm:
- Iterate through all elevators
- Execute one step for each elevator
- Elevators update their own state independently
This creates a simple time-based simulation of elevator movement.
Minimal Class Diagram
classDiagram
class Elevator {
id: int
current_floor: int
direction: Direction
up_stops: Set
down_stops: Set
add_pickup()
add_dropoff()
step()
}
class ElevatorController {
elevators: List~Elevator~
request_pickup()
request_dropoff()
step()
}
ElevatorController o-- Elevator
Implementation (Python)
from typing import List, Literal, Set
type Direction = Literal["UP", "DOWN", "IDLE"]
NUM_FLOORS = 10
class Elevator:
def __init__(self, eid: int):
self.id = eid
self.current_floor: int = 0
self.direction: Direction = "IDLE"
self.up_stops: Set[int] = set()
self.down_stops: Set[int] = set()
def add_dropoff(self, floor: int) -> None:
if not (0 <= floor < NUM_FLOORS):
return
if floor > self.current_floor:
self.up_stops.add(floor)
elif floor < self.current_floor:
self.down_stops.add(floor)
def add_pickup(self, floor: int, direction: Direction) -> None:
if not (0 <= floor < NUM_FLOORS):
return
if direction == "UP":
self.up_stops.add(floor)
elif direction == "DOWN":
self.down_stops.add(floor)
def distance(self, floor: int, direction: Direction) -> int:
if self.direction == "UP":
if floor >= self.current_floor and direction == "UP":
return floor - self.current_floor
top = max(self.up_stops, default=self.current_floor)
return (top - self.current_floor) + abs(top - floor)
if self.direction == "DOWN":
if floor <= self.current_floor and direction == "DOWN":
return self.current_floor - floor
bottom = min(self.down_stops, default=self.current_floor)
return (self.current_floor - bottom) + abs(bottom - floor)
return abs(self.current_floor - floor)
def step(self) -> None:
self._serve_floor()
self._move()
self._update_direction()
def _move(self) -> None:
if self.direction == "UP":
if self.current_floor < NUM_FLOORS - 1:
self.current_floor += 1
elif self.direction == "DOWN":
if self.current_floor > 0:
self.current_floor -= 1
def _serve_floor(self) -> None:
if self.direction == "UP":
if self.current_floor in self.up_stops:
self._open()
self.up_stops.remove(self.current_floor)
self._close()
elif self.direction == "DOWN":
if self.current_floor in self.down_stops:
self._open()
self.down_stops.remove(self.current_floor)
self._close()
elif self.direction == "IDLE":
if self.current_floor in self.up_stops:
self._open()
self.up_stops.remove(self.current_floor)
self._close()
elif self.current_floor in self.down_stops:
self._open()
self.down_stops.remove(self.current_floor)
self._close()
def _update_direction(self) -> None:
if self.direction == "UP":
if self.up_stops:
return
if self.down_stops:
self.direction = "DOWN"
else:
self.direction = "IDLE"
elif self.direction == "DOWN":
if self.down_stops:
return
if self.up_stops:
self.direction = "UP"
else:
self.direction = "IDLE"
else:
if self.up_stops:
self.direction = "UP"
elif self.down_stops:
self.direction = "DOWN"
def _open(self) -> None:
pass
def _close(self) -> None:
pass
class ElevatorController:
def __init__(self, num_elevators: int = 3):
self.elevators: List[Elevator] = [
Elevator(i) for i in range(num_elevators)
]
def request_pickup(self, floor: int, direction: Direction) -> None:
if not (0 <= floor < NUM_FLOORS):
return
best_elevator = None
best_distance = float("inf")
for elevator in self.elevators:
dist = elevator.distance(floor, direction)
if dist < best_distance:
best_distance = dist
best_elevator = elevator
if best_elevator:
best_elevator.add_pickup(floor, direction)
def request_dropoff(self, elevator_id: int, floor: int) -> None:
if not (0 <= elevator_id < len(self.elevators)):
return
self.elevators[elevator_id].add_dropoff(floor)
def step(self) -> None:
for elevator in self.elevators:
elevator.step()
Limits and Extensions
This model captures the basic mechanics of elevator scheduling, but real systems are more complex.
Passenger capacity is one common extension. Elevators must consider how many passengers they can carry before accepting new requests.
Another extension is smarter dispatch logic. Modern systems often predict traffic patterns and group requests heading in the same direction.
Large buildings also introduce strategies like zoning or express elevators that skip certain floors to reduce travel time.
Despite these additions, the core idea remains the same: elevators maintain directional stop queues and serve them efficiently before reversing direction.