Advanced Project 5 — Elevator Simulation¶
Difficulty: Advanced · Estimated effort: 2–3 weeks · Prerequisites: Module 10 — Copying and Resource Management, Module 13 — Exception Handling, Module 15 — SOLID and Design Patterns · Demanded pattern decisions: state, strategy
This is one of the designated integration projects, and one of the projects that requires a design-pattern decision: at least two patterns must be named and justified by a requirement, and one alternative must be considered and declined in writing.
No complete solution is provided. The state model is the design decision, and it is yours.
1. Scenario¶
A property company wants to test lift configurations before fitting them: how long people wait, how often the cars are idle, and what happens at the morning peak. Passengers arrive at floors, press up or down, wait, board, ride and leave. Cars open, close, move and are held open.
The simulation must be deterministic given a seed, so two configurations can be compared fairly, and it must record an event log detailed enough to answer "why was car 2 idle for four minutes?"
2. Problem statement¶
Build a discrete-event simulation of a lift system: buildings with floors, cars with doors and movement, passengers with requests, a dispatcher choosing which car serves which request, and an event loop advancing simulated time. States and transitions must be explicit, illegal transitions impossible, and the whole run reproducible from a seed.
3. User requirements¶
- [ ] Configure a building: floors, cars, capacity, speed, door times.
- [ ] Generate passenger arrivals deterministically from a seed and a rate.
- [ ] Run the simulation for a simulated period and see it complete.
- [ ] Get statistics: average and worst wait, average ride time, car utilisation.
- [ ] Read an event log of every state change, in time order.
- [ ] Compare two dispatcher rules on the same arrivals.
4. Functional requirements¶
- [ ]
Building,Floor,CarandPassengertypes with their own invariants. - [ ] An explicit state model for a car: idle, moving, doors opening, doors open, doors closing.
- [ ] A
Dispatcherinterface with at least two implementations (nearest car; directional scan with a turnaround rule). - [ ] A
Scheduler-style event loop over injected simulation time, with no wall-clock reads. - [ ] Capacity enforced: a car cannot carry more than its limit, and boarding is refused when full.
- [ ] Statistics computed from the log, not from counters incremented by hand.
- [ ] A configuration file, with typed errors for impossible buildings (zero cars, negative speed).
5. Non-functional requirements¶
- [ ] Illegal transitions are impossible to perform, not merely detected — the state type enforces which operations are available.
- [ ] A car never moves with its doors open, and never opens its doors while moving; tested across a full run.
- [ ] Passengers are conserved: every arrival either completes a journey or is waiting at the end, and the two counts account for everyone.
- [ ] Identical seed and configuration produce an identical event log, byte for byte.
- [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror.
6. Suggested features¶
- A morning-peak profile: arrival rates per floor that vary over the simulated period.
- Priority for passengers who have waited too long, and a report on whether it helped.
- A record-and-replay mode: write the event log, replay it to rebuild the statistics.
- A second dispatcher that considers predicted future demand, with the comparison table.
7. OOP concepts involved¶
| Concept | Required | Where it appears |
|---|---|---|
| Multiple classes | yes | Building, floors, cars, passengers, dispatchers, event loop, event log, statistics |
| Relationships | yes | Building ◆ floors and cars; passenger ──> car while riding; dispatcher ──> cars it does not own |
| Encapsulation | yes | Car state, door state and occupancy cannot be set from outside; every change is a transition |
| Inheritance or composition | yes | Dispatchers behind an interface; consider whether car states deserve types and answer in the report |
| Polymorphism | yes | Dispatcher and, if you take the pattern route, state behaviour dispatch |
| The STL | yes | std::vector for floors and cars, std::map for statistics, std::stable_sort/priority queue for events |
| Exception handling | yes | Invalid configuration and illegal transition attempts throw distinct types |
8. Suggested design considerations¶
The pattern decision you must make and justify¶
State, for the car. Elevator doors and movement have genuinely different legal operations per
phase: you cannot move with the doors open, and you cannot open while moving. A state object
makes the illegal operation unavailable, while a switch on an enum makes it merely
discouraged. The alternative — an enum plus guard conditions — is a real choice, and you must
say which requirement decided it.
Strategy, for the dispatcher. Which car serves a request is the thing that varies, and the requirement that earns the strategy is compare two rules on identical arrivals. The alternative is a function pointer or a lambda: lighter. Choose one and justify. The one you decline is the declined-pattern item.
Ideas worth weighing and recording: observer, so a car notifies the log of its state changes versus the log being written by the loop (the observer is arguably earned because it keeps the car from knowing about the log); command, for queueing requests as objects (worth considering, probably not earned).
Other design points¶
Simulated time is data. Every event carries its time; nothing reads a clock. This is what makes a full day's simulation run in milliseconds and makes results reproducible.
Derive statistics from the log. Counters incremented by hand drift from the log and then nobody can explain a discrepancy. One source of truth: the log.
Passenger conservation is the acceptance test. If the numbers do not add up at the end, the simulation is wrong somewhere, and finding out where is the point of the invariant.
9. Minimum viable version¶
One car, four floors, arrivals from a fixed list, movement and door transitions, and a printed event log. One dispatcher, no statistics.
10. Intermediate extension¶
Add capacities, the second dispatcher and the statistics derived from the log, plus passenger conservation checked at the end of every run. Then compare the two dispatchers on identical arrivals and report the difference in average wait.
11. Advanced extension¶
Add the peak profile, the record-and-replay mode, and a state-object design for the car (or the written justification for keeping the enum, with the line count of the transition logic as evidence). Then write the design note: the patterns, the declined alternative, and the decision you would reverse — with its cost.
12. Testing checklist¶
- [ ] A single passenger's journey appears in the log with sensible times
- [ ] A car never moves with the doors open — checked for every event in a long run
- [ ] A car never opens its doors while moving — checked the same way
- [ ] Capacity is never exceeded; a full car refuses boarding and the passenger waits
- [ ] Attempting to board a car on another floor is impossible, or refused with a message
- [ ] Passenger conservation holds at the end of the run, for several seeds and configurations
- [ ] The same seed and configuration produce byte-identical logs, twice
- [ ] A different seed produces a different log (checked over several seeds)
- [ ] A request is served by exactly one car, never by two
- [ ] Statistics recomputed from the log match the statistics printed at run time
- [ ] A building with zero cars or negative speed is refused before the run starts
- [ ] Zero arrivals produces an idle log and zero-length waits, not a crash
13. Documentation checklist¶
- [ ] The state diagram for a car, with every legal transition and the trigger for each
- [ ] The class diagram with ownership and the injected dispatcher
- [ ] Both dispatcher rules stated precisely enough to implement from the description
- [ ] The configuration file format and the impossible configurations it rejects
- [ ] The comparison table for the two dispatchers, with the seed used
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| State modelling | Illegal transitions unavailable; state diagram matches the code | 25% |
| Reproducibility | Seeded, clock-injected, byte-identical logs | 20% |
| Pattern decisions | State and strategy justified; the declined alternative argued | 20% |
| Invariants | Capacity, door safety and passenger conservation verified, not asserted | 20% |
| Evidence | Statistics derived from the log; dispatcher comparison on identical arrivals | 10% |
| Documentation | State diagram, class diagram and comparison table written down | 5% |
Related: Advanced projects · Project roadmap · All projects · Module 15 — SOLID and Design Patterns