Skip to content

Latest commit

 

History

History
365 lines (296 loc) · 12 KB

File metadata and controls

365 lines (296 loc) · 12 KB

- Party / social‑deception game for 3–15 players; each round has:
  - 1 public **Judge**, 1 secret **Honest** player, remaining secret **Liars**.
  - 1 topic card: visible **term** for all, hidden **explanation** text seen only by Honest during a short "dark" phase.
- Flow per round:
  1. Assign roles, draw topic card, Honest briefly reads real explanation while others "close eyes".
  2. All non‑judge players explain the term in turn; Honest tells the truth, Liars improvise plausible nonsense.
  3. Judge selects who is Honest (and optionally condemns obvious Liars); scoring is applied.

The implementation should support **realtime multiplayer** via WebSockets and be fully server‑authoritative.stLiar Web Game – Implementation Plan

This document describes how to implement an online, realtime web version of **HonestLiar**, suitable for a Copilot Agent to follow and scaffold code from.

---

## 1. Game Overview

- Party / social‑deduction game for 3–9 players; each round has:
  - 1 public **Judge** (“想想”), 1 secret **Honest** player, remaining secret **Liars** (9upper).[web:48][web:49][web:70]
  - 1 topic card: visible **term** for all, hidden **explanation** text seen only by Honest during a short “dark” phase.[web:48][web:49][web:70]
- Flow per round:
  1. Assign roles, draw topic card, Honest briefly reads real explanation while others “close eyes”.[web:48][web:49][web:70]
  2. All non‑judge players explain the term in turn; Honest tells the truth, Liars improvise plausible nonsense.[web:48][web:49][web:70]
  3. Judge selects who is Honest (and optionally condemns obvious Liars); scoring is applied.[web:48][web:49][web:70]

The implementation should support **realtime multiplayer** via WebSockets and be fully server‑authoritative.

---

## 2. Tech Stack & High‑Level Architecture

### 2.1 Suggested Stack (configurable)

- **Frontend**: React (or Next.js) + TypeScript.
- **Backend**: Node.js + TypeScript + WebSocket (e.g. Socket.IO).
- **Data storage**:
  - Static JSON or DB (PostgreSQL / MongoDB) for topic cards.
  - In‑memory room state (e.g. Map) + optional Redis for horizontal scaling.

### 2.2 Process Architecture

- **API / WebSocket server**
  - Handles lobby, room state, role assignment, round state machine, scoring.
- **Client SPA**
  - Renders lobby, role screens, timers, discussion UI, judge decision UI, scoreboard.
- **Static card loader**
  - Loads the topic card set (term + hidden explanation) from JSON or DB.

Backend must be authoritative: topic explanations and roles never leaked to unintended clients.

---

## 3. Data Model

### 3.1 Core Types

```ts
type Role = "JUDGE" | "HONEST" | "LIAR";

interface Player {
  id: string;           // connection or auth id
  name: string;
  score: number;
  connected: boolean;
}

interface TopicCard {
  id: string;
  title: string;        // visible term
  explanation: string;  // hidden text, Honest only
}

type Phase =
  | "LOBBY"
  | "ROUND_INIT"
  | "DARK"
  | "DISCUSSION"
  | "JUDGING"
  | "SCORING"
  | "ENDED";

interface RoundState {
  roundNumber: number;
  topicCardId: string;
  judgeId: string;
  honestId: string;
  roles: Record<string, Role>;       // playerId -> Role
  phase: Phase;
  speakingOrder: string[];           // playerIds (excluding judge)
  currentSpeakerIndex: number;
  judgePickHonestId?: string;
  judgeCondemnedIds?: string[];
}

interface Room {
  id: string;                         // room code
  hostId: string;
  players: Record<string, Player>;
  maxPlayers: number;
  settings: {
    maxRounds: number;
    enableCondemnRule: boolean;
    language: "zh-Hant" | "en";
    speakingTimeSec: number;
    darkPhaseSec: number;
  };
  deck: TopicCard[];
  discardPile: string[];              // card ids
  currentRound?: RoundState;
  state: Phase;
}

All server logic should operate on these structures or close variants.


4. Game Logic & State Machine

4.1 Room / Lobby

Goal: create/join rooms, configure game, then start when enough players (≥3).

  1. create_room (client → server)
    • Input: host nickname, preferred settings.
    • Output: room id, host player object.
  2. join_room
    • Input: room id, nickname.
    • Output: player object, current room state.
  3. Lobby behavior:
    • Players can see who joined.
    • Host can change settings.
    • Host can call start_game when players.length >= 3.

4.2 Round Init

  1. Server sets state = "ROUND_INIT".
  2. Draw topic card:
    • Pop one from deck, push to discardPile.
    • Set currentRound.topicCardId.
  3. Assign roles:
    • Determine judgeId:
      • Option A (recommended): rotate judge each round (roundNumber mod playerCount).
      • Option B: random.
    • Randomly pick honestId from non‑judge players.
    • Assign "LIAR" to all others.
  4. Broadcast:
    • Public: topic title, judgeId.
    • Private to each player:
      • Their role.
      • For Honest only: explanation (during dark phase).

4.3 Dark Phase

  • Server sets phase = "DARK", starts timer settings.darkPhaseSec (e.g. 9–20 seconds).[web:48][web:49]
  • Honest client:
    • Show full explanation text + countdown.
  • Others:
    • Show “Close your eyes / Wait” screen.
  • When timer ends:
    • Optionally hide explanation for Honest (config).
    • Advance to Discussion.

4.4 Discussion Phase

  • Server sets phase = "DISCUSSION".
  • Compute speakingOrder:
    • All non‑judge players, randomly shuffled.
  • For each index i in speakingOrder:
    • Set currentSpeakerIndex = i.
    • Broadcast currentSpeakerId.
    • Start speaking timer settings.speakingTimeSec (e.g. 30–60 seconds).[web:49][web:70]
    • Client behavior:
      • Active speaker sees “Your turn to explain”.
      • Other players see “Listening – [Player X]”.
    • Implementation options:
      • Voice over external voice chat (Discord / IRL).
      • Optional text area for typed explanation; server can log or broadcast text.
  • Optional “free discussion” sub‑phase:
    • Fixed timer (e.g. 60 seconds).
    • Everyone can talk / send chat before judge decides.

4.5 Judging Phase

  • Server sets phase = "JUDGING".
  • Judge client receives:
    • List of non‑judge players with identifiers and speaking order.
  • Judge selects:
    • pickHonestId (mandatory).
    • Optional condemnedIds[] if condemn rule enabled.
  • Client sends judge_decision event.
  • Server validates:
    • pickHonestId is a non‑judge player.
    • condemnedIds subset of non‑judge players.
  • Store on currentRound and advance to Scoring.

4.6 Scoring Phase

Implement configurable scoring constants.

Example defaults:

const SCORE_HONEST_PICKED = 2;
const SCORE_JUDGE_CORRECT = 2;
const SCORE_LIAR_FOOLED = 3;
const PENALTY_JUDGE_WRONG = -1;

const SCORE_JUDGE_CORRECT_CONDEMN = 1;
const PENALTY_LIAR_CONDEMNED = -1;
const PENALTY_JUDGE_MISCONDEMN = -3;

Algorithm:

  1. If judgePickHonestId === honestId:
    • players[judgeId].score += SCORE_JUDGE_CORRECT;
    • players[honestId].score += SCORE_HONEST_PICKED;
  2. Else:
    • players[judgePickHonestId].score += SCORE_LIAR_FOOLED;
    • players[judgeId].score += PENALTY_JUDGE_WRONG;
  3. Condemn (if enabled):
    • For each cid in judgeCondemnedIds:
      • If roles[cid] === "LIAR":
        • players[judgeId].score += SCORE_JUDGE_CORRECT_CONDEMN;
        • players[cid].score += PENALTY_LIAR_CONDEMNED;
      • Else if cid === honestId:
        • players[judgeId].score += PENALTY_JUDGE_MISCONDEMN;
  4. Broadcast:
    • Reveal all roles.
    • Show per‑player delta and new totals.

4.7 End Round / End Game

  • If roundNumber >= settings.maxRounds:
    • Set state = "ENDED".
    • Broadcast final scoreboard, highlight highest score.
  • Else:
    • Increment roundNumber, return to Round Init after short delay or when host clicks “Next Round”.

5. Network Protocol (Events)

5.1 Client → Server

  • create_room { name, settings? }
  • join_room { roomId, name }
  • start_game {}
  • judge_decision { roomId, pickHonestId, condemnedIds?: string[] }
  • request_next_round {}
  • Optional:
    • send_chat { message }
    • update_settings { settings }

5.2 Server → Client

  • room_state { room: RoomSummary }
  • game_started { roomId }
  • round_started { round: RoundPublicInfo }
  • phase_changed { phase, timers? }
  • your_role { role, explanation? }
  • topic_public { title }
  • speaker_changed { speakerId }
  • judge_prompt { candidates: PlayerSummary[] }
  • round_result { roles, scoreDeltas, totals }
  • game_over { totals, winners }
  • error { code, message }

Where RoundPublicInfo excludes hidden roles and explanations.


6. UI / UX Requirements

6.1 Screens

  1. Landing
    • Create room / Join room (room code + nickname).
  2. Lobby
    • List players, host badge, settings panel, “Start Game” button.
  3. Role Reveal
    • Big card: “You are JUDGE / HONEST / LIAR”.
    • Short explanation of objective per role.
  4. Topic Card
    • All: title text.
    • Honest: explanation during dark phase with countdown.
  5. Discussion
    • Avatar list with highlight on current speaker.
    • Timer bar for each turn.
    • Optional chat/log of text explanations.
  6. Judging
    • Judge: selectable cards for each candidate; optional condemn checkboxes.
    • Others: “Judge is making a decision…”.
  7. Reveal & Scoreboard
    • Flip animations (role reveal).
    • Per‑player row: name, role, round delta, total score.
  8. End Game
    • Final scoreboard with winner highlight.
    • Buttons: “Play again” (reuse room) / “Back to lobby”.

6.2 Responsiveness & Localization

  • Mobile‑first layout; large touch targets for judge selections.
  • Language support:
    • At least Traditional Chinese for card content and UI labels.[web:49][web:73]
    • Texts structured for easy i18n.

7. Security, Integrity, and Scaling

  • Server‑authoritative:
    • Only server assigns roles and cards, and sends explanations to Honest only.
    • Judge and Liars never receive explanation string.
  • No logic in clients beyond UI; clients cannot influence roles, scoring, or deck.
  • Cheat resistance:
    • Do not echo explanation text in public messages.
    • Optionally store logs server‑side without revealing hidden text to players.
  • Reconnection:
    • On reconnect, re‑emit room_state, current round, and your_role.
    • If Honest reconnects after dark phase, do not re‑send explanation unless explicitly allowed.

8. Implementation Tasks Checklist

  1. Project setup
    • Scaffold React/Next.js frontend and Node.js/TypeScript backend.
    • Integrate WebSocket (Socket.IO or ws).
  2. Domain model
    • Implement Room, Player, TopicCard, RoundState, Phase types.
    • Implement RoomManager (in‑memory for MVP).
  3. Deck loading
    • Create JSON file for cards with title and explanation (or mock subset).[web:51][web:73]
    • Implement shuffle/draw/discard.
  4. Lobby APIs
    • Implement create/join room and settings updates.
  5. State machine
    • Implement transitions:
      • LOBBY → ROUND_INIT → DARK → DISCUSSION → JUDGING → SCORING → (next round or ENDED).
  6. Scoring
    • Implement scoring constants and calculation.
  7. Client UI
    • Build pages/components for screens in section 6.
    • Hook up WebSocket events to state and views.
  8. Testing
    • Simulate 3–5 bots to play basic rounds for load tests.
    • Test reconnection, role secrecy, and card reuse behavior.
  9. Deployment
    • Containerize backend & frontend.
    • Deploy to cloud (e.g. Docker + k8s / managed PaaS).

9. Extensibility

  • Support game expansions (e.g. picture cards, education edition) by:
    • Adding type field to TopicCard (e.g. "text" | "picture" | "challenge").
    • Adapting UI to show images instead of text where appropriate.
  • Add async / turn‑based mode using persisted text explanations instead of live voice.

This README should provide enough structure for an agent to scaffold and implement a full multiplayer web version of HonestLiar with room management, realtime rounds, and scoring consistent with social deduction games.