A browser-based, real-time hidden-movement game played over an actual graph of Wellington's transport network — not a fictional board, the real bus routes, train lines, ferry crossing, and e-scooter zones of the city, as 216 nodes and 372 edges one player can vanish into and up to five others have to search.
One player is Mr X. Everyone else is a Detective. Mr X's position is hidden from the moment the round starts, surfacing only on five scheduled reveal rounds — everyone else has to reconstruct where he's gone from nothing but the transport ticket he was forced to spend to get there. Detectives see each other and coordinate in the open; Mr X sees everyone but plays alone against the clock, 24 rounds, no rematch.
| Role | Count | Knows |
|---|---|---|
| Mr X | 1 | Everyone's position. Moves first each round. |
| Detective | 1–5 | Every other detective's position, always. Mr X's, only on a reveal round. |
Movement runs on four transport modes, each tied to a ticket and a colour on the map:
| Mode | Ticket |
|---|---|
| E-scooter | ESCOOTER |
| Bus | BUS |
| Train | TRAIN |
| Ferry | FERRY |
A move needs a ticket matching the mode of the edge being crossed. Detectives hold a fixed budget per game (12 escooter / 8 bus / 6 train / 2 ferry, configurable) and never get more — spend them badly and you're stuck. Mr X never runs out of the four regular tickets, but also holds a small number of two special ones:
- Invisible (
BLACK) — travel any edge, any mode, no matching ticket required. Detectives see that an invisible ticket was used, never which mode it disguised. He gets one per detective. DOUBLE— take two moves in one turn before any detective responds. Rare (2 per game) and the only way to put real distance between two reveals.
Reveal rounds are fixed: 2, 8, 13, 18, 24. On those rounds, the node Mr X ends his move on is shown to every detective and logged. Between reveals he's a ticket trail and nothing else.
The game ends the instant a detective's move lands on Mr X's node — detectives win. They also win if Mr X can't move when his turn starts because they hold every node next to him. If round 24 completes with Mr X still free, he wins. If any player leaves, or the player whose turn it is doesn't move for 15 minutes, the game is aborted for everyone; a dropped connection on its own doesn't end it.
Full mechanical detail — turn ordering, movement constraints, double-move sequencing — lives in documentation/spec.md.
Browser (Vue 3 + MapLibre GL)
│
├── REST HTTP/JSON ───────────────────────┐ lobby, move submission
│ ▼
└── WebSocket (STOMP/SockJS) ──► Spring Boot backend
├── REST controllers
├── STOMP broker
├── game engine (pure Java, in memory)
└── ConcurrentHashMap<gameId, Game>
All authoritative state lives server-side in memory — no database, restarting the server ends every active game. The client only ever holds a role-filtered display copy pushed over WebSocket, which is how Mr X's hidden position stays hidden: the server never sends it to detectives outside a reveal round in the first place. The Wellington graph itself is a static JSON file the backend loads at startup and the game board fetches once from /api/map, not a live routing API — see documentation/spec.md for why that tradeoff was made.
| Layer | Technology |
|---|---|
| Frontend | Vue 3, Vite, Tailwind CSS, TypeScript |
| Map | MapLibre GL |
| Backend | Java 21, Spring Boot 4, Maven |
| Real-time | STOMP over SockJS (WebSocket) |
| API contract | documentation/openapi.yaml — REST + WebSocket, kept in sync with every API change |
Backend (serves the API on :8999):
cd backend
mvn spring-boot:runFrontend (dev server on :5173, proxies /api and /ws to the backend):
cd frontend
npm install
npm run devGame settings (the idle turn limit, detective ticket budgets, which map file to load) are in backend/src/main/resources/application.properties. By default the backend loads the full 216-node Wellington map (map.json); a 5-node test-map.json is also bundled for fast manual testing.
To regenerate or edit the map itself, open mapCreator/map-creator.html — a standalone HTML tool for placing nodes/edges on Wellington and generating the train, bus and e-scooter networks.
cd backend
mvn test # all tests except the latency test; coverage report in target/site/jacoco/
mvn test -Dgroups=perf -DexcludedGroups= # multiplayer latency test only
mvn test-compile org.pitest:pitest-maven:mutationCoverage # mutation testing; report in target/pit-reports/The suite covers unit, mock, lifecycle, integration (HTTP and STOMP), full-game functional, property-based and fuzz tests; backend/doc.md lists which class does what. No browser or driver is needed.
Licensed under GPL-3.0.
Game mechanics are based on the board game Scotland Yard, designed by Ravensburger — used here with their permission, for this non-commercial ENGR489 student project at Victoria University of Wellington. This project is not affiliated with or endorsed by Ravensburger, and the Scotland Yard name/brand is not used in connection with it.