Skip to content

eric-sims/quipnotes

Repository files navigation

Quipnotes — game server

The multiplayer game server for Quipnotes, a party game where players arrange word tiles into a "ransom note" answering a prompt, then a rotating judge picks a favorite. This Go + Gin service is the source of truth for game state: it hosts many concurrent games at once (Jackbox-style, each a 4-digit code) and pushes live updates to every screen over WebSockets.

Quipnotes is a mash-up of two games we love — Ransom Notes by Very Special Games and Quiplash by Jackbox Games — hence the name. The best in-person experience is still to buy Ransom Notes and use its cards; this project makes it playable on phones, no table full of tiny tiles required.

The system

Quipnotes is three independently-versioned projects (each its own git repo):

Project Role Stack
quipnotes (this repo) Game server — owns all state, one HTTP/WS API per game code Go + Gin
quipnotesclient Player client — join with a name + code, draw tiles, submit notes, judge Vue 3 + Vite
quipnotesmanager Host/manager client — start a game, show the code, drive the board Vue 3 + Vite

Everything the server exposes is scoped to a 4-digit game code. State lives in memory only — restarting the server wipes every game.

Quick start

cp .env.example .env      # then edit — see Configuration below
go run .                  # serves on :8081 (override with PORT)

Requires Go 1.21+. The server panics on startup if .env is missing or WORDS_FILE_PATH doesn't point to a readable CSV. Once running, the interactive API docs are at http://localhost:8081/swagger/index.html.

go build -o quipnotes     # build a binary
go test ./...             # run the test suite
swag init                 # regenerate Swagger docs in docs/ from handler annotations

Configuration

Set via .env (see .env.example):

Variable Required Default Purpose
WORDS_FILE_PATH yes Path to the word-tile CSV. Missing/unreadable → panic on boot.
PROMPTS_FILE_PATH no built-in bank Path to the prompts file. Unset/unreadable → log a warning and use a built-in family-friendly bank.
PORT no 8081 Port to bind.
GIN_MODE no debug release in production to quiet Gin.

words.csv

Ransom Notes' word tiles are proprietary, so no words.csv is committed. Buy a copy and index the tiles (about 15 minutes), or bring any word list you like.

  • One column, no header row — one word per line.
  • Duplicate words are fine; each row becomes a distinct tile keyed "<rowIndex>|<word>".

prompts.txt (optional)

Prompts are the round cards a player writes a note about. Supply your own list, or skip it entirely and the server falls back to a small built-in family-friendly bank so it still boots with a playable game. See data/prompts.example.txt.

  • One prompt per line — line-based (not CSV), so prompts may contain commas.
  • Blank lines are ignored; a line starting with # is a comment.
  • Family-friendly mode: prefix a line with the [adult] marker to rate it adult. Adult prompts are withheld from games a host starts in family-friendly mode (a lobby toggle), and the marker is stripped from the text. Every other line is family-friendly.
# A comment. Everything below is a prompt, one per line.
The worst possible thing to say on a first date
A terrible name for a boat
[adult] Whisper something seductive to your date during a movie

How the game plays

Play runs in rounds — one active prompt each:

  1. The host starts a game (POST /games → a 4-digit code) and shares the code; players join from their phones.
  2. A round begins when the next prompt is drawn (POST /games/:code/rounds). It's pushed live to every screen.
  3. Each round rotates a judge (round-robin, so everyone judges once per cycle before repeating). The judge sits out; everyone else submits one note.
  4. Judging opens automatically once every non-judge has submitted (or the judge forces it), then the judge flips notes face-up and picks a favorite — the author scores a point. A round with fewer than 2 players is judge-less and plays freely.
  5. Anyone eligible draws the next prompt, which clears the board and starts the next round.

Architecture

  • In-memory, multi-game. A package-level game.Registry (internal/game/game.go) maps each 4-digit code to a game.Manager — one game with its own players, tile pool, submitted notes, prompt deck, scores, and judging state, each behind its own mutex. No database.
  • Tile wire format "<id>|<word>". The numeric id (CSV row index) keeps duplicate words distinct across the whole protocol. Notes are stored as ordered token lists — tile keys plus a reserved "\n" break token for line breaks — never flattened to a string.
  • WebSockets for push. GET /games/:code/events streams round/judging/roster events and replays the current round's lifecycle on connect so refreshes and late joins recover mid-round. Player actions still go over the REST endpoints; the socket is push-only.

The routes, handlers (with Swagger annotations), and the full event catalog live in internal/game/router.go; pure game logic in game.go; CSV/prompt loading in words.go and prompts.go; the WebSocket hub in hub.go. See CLAUDE.md for a detailed map of the state model and every endpoint. Interactive API docs are served at /swagger/index.html.

Deployment

Production runs in Docker behind Caddy. See DEPLOY.md for the full setup. Note the known port mismatch: main.go binds :8081 while the base Dockerfile/docker-compose.yaml reference 8080 — verify ports before relying on the container.

Development notes

  • CORS allows all origins with credentials — a local-development posture only.
  • Each sub-project is its own git repo. Branch off the latest master and open a PR rather than committing straight to master.

License

The code in this repository is licensed under the MIT License.

The Ransom Notes word tiles and prompt cards are proprietary to Very Special Games and are not covered by this license — none are committed here. Supply your own words.csv and (optionally) prompts file as described under Configuration.

About

A lazy version of a popular table-top game.

Resources

License

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors