Skip to content

About

Multiplayer Online Scotland Yard game

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Hunting Mr. X: Wellington Edition

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.

Java Spring Boot Vue TypeScript MapLibre GL License

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.


How it plays

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.


Architecture

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

Running it locally

Backend (serves the API on :8999):

cd backend
mvn spring-boot:run

Frontend (dev server on :5173, proxies /api and /ws to the backend):

cd frontend
npm install
npm run dev

Game 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.


Testing

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.


License & attribution

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.

About

Multiplayer Online Scotland Yard game

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages