Skip to content

ak811/zuzu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zuzu

A Discord word‑similarity chain game powered by an LLM “judge”.

Players start from a seed word and try to reach a goal word using one-word steps.
Every submission is judged for semantic relatedness (plus a little spelling/shape resemblance), and the bot reacts with a 0️⃣–🔟 similarity meter. That number becomes your rating for the play.


Gameplay rules

Valid plays

  • Message must be one word matching: ^[A-Za-z][A-Za-z'\-]*$
    (letters, apostrophe, hyphen allowed; no spaces, no emojis)

Turns

  • No back-to-back plays from the same user. If you try, your message is deleted.
  • If you post the same word as the last judged submission, your message is deleted.
  • Per-user cooldown is enforced (default 2s). On cooldown, the bot reacts with 🕒.

Judging

  • Bot calls OpenAI to judge similarity between the previous accepted word and your candidate.
  • A play is accepted if:
    • the model returns decision: "pass", or
    • the numeric similarity score is ≥ SIMILARITY_THRESHOLD.

Scoring

  • Every judged message receives a rating from 0–10 (based on similarity meter emoji).
  • Global leaderboard (scores) uses sum of ratings across all time (with one exception):
    • Submitting the seed as the first word does not add points.
  • End-of-round summary shows average rating per user for that round:
    • avg = sum(rating) / count(plays) (includes fails)

Round lifecycle

  • A round starts with last_word = seed.
  • When someone submits the goal (case-insensitive), the bot posts:
    • the full accepted chain for the round
    • a scoreboard (avg rating)
    • pings participants (up to 20)
  • Then it resets state and auto-restarts in 10 seconds.

Commands

All commands are under the parent command (default: !zuzu).
Prefix and parent command are configurable via environment variables.

Admin commands

  • !zuzu start
    Starts a new round and pins the game to the current channel (unless already set)
  • !zuzu stop
    Stops the game in the channel
  • !zuzu reset
    Resets the round progress clock (new start will post a new seed→goal)
  • !zuzu set-threshold <0-1>
    Sets SIMILARITY_THRESHOLD (in-memory/env; restart to persist if desired)
  • !zuzu set-cooldown <seconds>
    Sets COOLDOWN_SECONDS (0–3600)
  • !zuzu set-model <model-name>
    Sets the OpenAI model name used by the judge
  • !zuzu set-channel [#channel]
    Restricts bot/game to one channel per guild

User commands

  • !zuzu rules
    Shows how to play and current threshold/cooldown
  • !zuzu score
    Shows your all-time rating total
  • !zuzu leaderboard [N]
    Shows top N players by all-time rating total
  • !zuzu status
    Shows current config + seed/goal + round progress + current chain
  • !zuzu last
    Shows last accepted word and who played it (note: see “Known issues” below)

Requirements

  • Python 3.10+ recommended
  • Discord.py 2.4+
  • OpenAI Python SDK 1.40+
  • a Discord bot token + OpenAI API key

Dependencies are in requirements.txt:

discord.py>=2.4
aiosqlite>=0.20
python-dotenv>=1.0
openai>=1.40

Setup

1) Create a Discord application + bot

  • Go to the Discord Developer Portal
  • Create an app, add a bot, copy the bot token
  • Enable Privileged Gateway Intents → Message Content (required for reading normal chat messages)

2) Configure environment variables

Copy .env.example to .env and fill in values:

cp .env.example .env

Key settings:

Variable Default What it does
DISCORD_TOKEN (none) Discord bot token
OPENAI_API_KEY (none) OpenAI API key for judging
BOT_PREFIX ! Command prefix
PARENT_COMMAND zuzu Parent command group name
DB_PATH ./data/bot.sqlite SQLite database path
MESSAGE_CONTENT_INTENT 1 Must be 1 to read messages
SIMILARITY_THRESHOLD 0.38 Accept threshold (0–1)
NEAR_MISS_BAND 0.08 Display-only band for status/rules
COOLDOWN_SECONDS 2 Per-user cooldown
MODEL_NAME gpt-4o-mini OpenAI model used by judge
OPENAI_TIMEOUT_SECONDS 15 Request timeout
OPENAI_RETRIES 2 Retries on transient errors
SEED_GOALS_JSON data/seed_goal.json Seed→goal list source
DEBUG_ZUZU 1 Print debug logs when non-zero
CRAFTS_PER_ROUND 100 Target count shown in status

3) Install and run

python -m venv .venv
# Windows:
.venv\Scripts\activate
# macOS/Linux:
source .venv/bin/activate

pip install -r requirements.txt
python bot.py

Seed → Goal pairs

Zuzu supports a configurable pool of seed/goal pairs.

SEED_GOALS_JSON can be either:

  1. JSON text (directly in the env var), or
  2. a path to a JSON file (relative or absolute)

Accepted formats:

Option A: plain list

[
  ["water", "ocean"],
  ["cat", "tiger"]
]

Option B: object with "pairs"

{
  "pairs": [
    ["book", "library"],
    ["snow", "winter"]
  ]
}

Rules:

  • Pairs must be 2-element arrays
  • Each item must be a single word (no spaces)

The bot tracks which pairs have been used per guild and avoids repeats until the list is exhausted.
Usage is stored in:

  • DB table: used_seed_goals
  • JSON mirror (default): ./data/used_seed_goals.json (configurable via USED_PAIRS_JSON)

Database (SQLite)

Zuzu uses SQLite for persistence.

Primary tables:

  • game_state
    Per guild+channel: active flag, last word, last user, round start time, seed, goal
  • scores
    Per guild+user: all-time points (sum of ratings)
  • submissions
    Every judged message: word, ok/fail, rating, timestamp
  • guild_settings
    Allowed channel per guild
  • used_seed_goals
    Which seed→goal pairs have been used per guild

DB initializes and performs lightweight migrations automatically on startup (utils/db.py).


License

MIT. See LICENSE.

About

Zuzu

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages