Skip to main content

GuessIt

Family-Feud-style guessing game. Participants submit short free‑text answers; the system groups similar answers into categories (with optional LLM clustering), selects the most-mentioned category, then reveals a board of predefined answers with scores. The round repeats until all answers are revealed or the team hits the fail limit.

Example structure

{
"type": "GuessIt",
"data": {
"text": "Nenne einen Bereich, wo man KI einsetzen kann.",
"answers": [
{ "position": 1, "answer": "Bildbearbeitung", "score": 35, "synonyms": ["Foto", "Bilderkennung"] },
{ "position": 2, "answer": "Kundensupport", "score": 25, "synonyms": ["Support", "Chatbot"] },
{ "position": 3, "answer": "Medizin", "score": 20, "synonyms": ["Gesundheit", "Diagnose"] },
{ "position": 4, "answer": "Bildung", "score": 15, "synonyms": ["Lehre", "Lernen"] },
{ "position": 5, "answer": "Finanzen", "score": 5, "synonyms": ["Bank", "Trading"] }
],
"answerTime": 180,
"maxFails": 3
}
}

Properties

KeyTypeDefaultDescription
data.textstringThe question prompt displayed to players.
data.answersarrayPredefined board answers. Each entry: { position: 1..N, answer: string, score: number, synonyms?: string[] }. Scores typically sum to 100 to symbolize 100 AI's.
data.answerTimenumber180Seconds for the guess phase; > 0 enables auto‑advance on timeout.
data.maxFailsnumber3Number of failed reveals allowed before game over.

Synonyms make matching more forgiving. The server uses normalization + fuzzy matching; short answers can match via token or similarity. Unknown answers can be clustered via LLM into a new or existing category.

Phases (server index)

  1. Guess (0): Players submit free text answer. Auto‑advance when the timer ends or all connected players have valid answers.
  2. Tally (1): Host reviews categories; if there’s a tie, host chooses one.
  3. Reveal Answer (2): Host sees the selected category that will be checked.
  4. Reveal GameBoard (3): If the selected category matches a board answer, reveal it and add its score; otherwise increment fails.
  5. Post‑Reveal (4): Either continue to next round (reset inputs/whiteboard) or transition to game‑over states.
  6. Game Over (5): If max fails reached, reveal remaining answers flagged as revealed-by-gameover.
  7. Scoreboard (6): Final results: total score (0–100) and number of correct reveals.

Client protocol

OpCodes

  • ADD (0): Submit an answer
  • MOVE (3): Drag whiteboard notes (host; internal from Whiteboard)
  • SELECT_CATEGORY (5): Host picks a category when there’s a tie
  • REPORT_WRONG_CATEGORY (6): Player reports a wrong categorization

Client → Server payloads

Submit answer (participants):

{
"opCode": 0,
"data": { "text": "%3Canswer%3E" }
}

Select tied category (host):

{
"opCode": 5,
"data": { "category": "%3CCategory%3E" }
}

Report wrong categorization (participant):

{
"opCode": 6,
"data": { "text": "<answer>", "category": "<shown category>" }
}

Server → Client update

interface GuessItData {
text: string; // question
index: 0|1|2|3|4|5|6; // phase index
answerTimeLeft?: number|null; // only in index 0
clusteredAnswer?: string|null; // index 2 and later
gameBoard?: {
entries: Array<{ position: number; answer: string|null; score: number|null; isRevealed: boolean; revealedByGameOver?: boolean }>;
currentScore: number;
currentFails: number;
maxFails: number;
revealedCount: number;
};
whiteboard?: any[]; // category + answer notes (host-visible)
tiedCategories?: string[]; // index 1 when tie detected
waitingForCategorySelection?: boolean;
scoreboard?: { totalScore: number; correctAnswers: number } | null; // index 6
// Personalized to each participant during early phases:
needsSecondAnswer?: boolean; // if answer invalid or already revealed
rejectionReason?: 'already_revealed'|'invalid_answer'|null;
playerSubmission?: { answer: string; category: string|null; isValid: boolean };
}

Behavior highlights

  • Inputs are encoded on send. The server decodes, normalizes (e.g., accents, case), and matches against answers or their synonyms.
  • Unknown answers are sent to an LLM clustering endpoint (optional); while pending, the participant sees a waiting state.
  • The whiteboard shows categories and grouped answers, with counts and colors per category.
  • Auto‑advance logic considers only connected, non‑host participants with valid answers.
  • Fail reasons also create whiteboard categories like “Fehlantwort” or “Bereits aufgedeckt”.

Use cases

  • Warm‑up brainstorming, icebreakers, opinion polls.
  • Debriefs that converge open inputs onto canonical targets.
  • Classroom or workshop games mirroring Family Feud mechanics.

LLM Integration

  • Cluster endpoint: the web app calls /api/llm/guessit/cluster (Next.js API route) which in turn forwards the result to Nakama via RPC guessit_llm_cluster_result.
  • Secure with NEXT_LLM_SHARED_SECRET (present in both apps) so only trusted calls are accepted.

Environment Variables

Next.js (apps/web/.env.local)

OPENAI_API_KEY=<your-openai-key>
NEXT_LLM_SHARED_SECRET=<shared-secret>
NAKAMA_LLM_RPC_URL=http://localhost:7350 (local) | http://nakama:7350 (docker)
NAKAMA_HTTP_KEY=<nakama-http-key>
OPENAI_API_URL=<custom-openai-endpoint>
OPENAI_CLUSTER_MODEL=<default-model-name-for-clustering>
OPENAI_GENERATE_MODEL=<default-model-name-for-generation>

Nakama (apps/nakama/.env)

NEXT_LLM_URL_CLUSTER=http://host.docker.internal:3000/api/llm/guessit/cluster (local) | http://next-js:3000/api/llm/guessit/cluster (docker)
NEXT_LLM_URL_GENERATE=http://host.docker.internal:3000/api/llm/guessit/generate (local) | http://next-js:3000/api/llm/guessit/generate (docker)
NEXT_LLM_SHARED_SECRET=<shared-secret>

The NEXT_LLM_SHARED_SECRET must match between Nakama and Next.js. NAKAMA_LLM_RPC_URL points to your Nakama server; the GuessIt web API forwards results to the RPC endpoints using NAKAMA_HTTP_KEY. The Nakama backend calls the web clustering/generating endpoint defined by NEXT_LLM_URL_CLUSTER/GENERATE.