- 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.
Goal: create/join rooms, configure game, then start when enough players (≥3).
create_room(client → server)- Input: host nickname, preferred settings.
- Output: room id, host player object.
join_room- Input: room id, nickname.
- Output: player object, current room state.
- Lobby behavior:
- Players can see who joined.
- Host can change settings.
- Host can call
start_gamewhenplayers.length >= 3.
- Server sets
state = "ROUND_INIT". - Draw topic card:
- Pop one from
deck, push todiscardPile. - Set
currentRound.topicCardId.
- Pop one from
- Assign roles:
- Determine
judgeId:- Option A (recommended): rotate judge each round (roundNumber mod playerCount).
- Option B: random.
- Randomly pick
honestIdfrom non‑judge players. - Assign
"LIAR"to all others.
- Determine
- Broadcast:
- Public: topic
title,judgeId. - Private to each player:
- Their
role. - For Honest only:
explanation(during dark phase).
- Their
- Public: topic
- Server sets
phase = "DARK", starts timersettings.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.
- Server sets
phase = "DISCUSSION". - Compute
speakingOrder:- All non‑judge players, randomly shuffled.
- For each index
iinspeakingOrder:- 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.
- Set
- Optional “free discussion” sub‑phase:
- Fixed timer (e.g. 60 seconds).
- Everyone can talk / send chat before judge decides.
- 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_decisionevent. - Server validates:
pickHonestIdis a non‑judge player.condemnedIdssubset of non‑judge players.
- Store on
currentRoundand advance to Scoring.
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:
- If
judgePickHonestId === honestId:players[judgeId].score += SCORE_JUDGE_CORRECT;players[honestId].score += SCORE_HONEST_PICKED;
- Else:
players[judgePickHonestId].score += SCORE_LIAR_FOOLED;players[judgeId].score += PENALTY_JUDGE_WRONG;
- Condemn (if enabled):
- For each
cidinjudgeCondemnedIds:- 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;
- If
- For each
- Broadcast:
- Reveal all roles.
- Show per‑player delta and new totals.
- If
roundNumber >= settings.maxRounds:- Set
state = "ENDED". - Broadcast final scoreboard, highlight highest score.
- Set
- Else:
- Increment
roundNumber, return to Round Init after short delay or when host clicks “Next Round”.
- Increment
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 }
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.
- Landing
- Create room / Join room (room code + nickname).
- Lobby
- List players, host badge, settings panel, “Start Game” button.
- Role Reveal
- Big card: “You are JUDGE / HONEST / LIAR”.
- Short explanation of objective per role.
- Topic Card
- All: title text.
- Honest: explanation during dark phase with countdown.
- Discussion
- Avatar list with highlight on current speaker.
- Timer bar for each turn.
- Optional chat/log of text explanations.
- Judging
- Judge: selectable cards for each candidate; optional condemn checkboxes.
- Others: “Judge is making a decision…”.
- Reveal & Scoreboard
- Flip animations (role reveal).
- Per‑player row: name, role, round delta, total score.
- End Game
- Final scoreboard with winner highlight.
- Buttons: “Play again” (reuse room) / “Back to lobby”.
- 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.
- 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, currentround, andyour_role. - If Honest reconnects after dark phase, do not re‑send explanation unless explicitly allowed.
- On reconnect, re‑emit
- Project setup
- Scaffold React/Next.js frontend and Node.js/TypeScript backend.
- Integrate WebSocket (Socket.IO or ws).
- Domain model
- Implement
Room,Player,TopicCard,RoundState,Phasetypes. - Implement
RoomManager(in‑memory for MVP).
- Implement
- Deck loading
- Create JSON file for cards with
titleandexplanation(or mock subset).[web:51][web:73] - Implement shuffle/draw/discard.
- Create JSON file for cards with
- Lobby APIs
- Implement create/join room and settings updates.
- State machine
- Implement transitions:
- LOBBY → ROUND_INIT → DARK → DISCUSSION → JUDGING → SCORING → (next round or ENDED).
- Implement transitions:
- Scoring
- Implement scoring constants and calculation.
- Client UI
- Build pages/components for screens in section 6.
- Hook up WebSocket events to state and views.
- Testing
- Simulate 3–5 bots to play basic rounds for load tests.
- Test reconnection, role secrecy, and card reuse behavior.
- Deployment
- Containerize backend & frontend.
- Deploy to cloud (e.g. Docker + k8s / managed PaaS).
- Support game expansions (e.g. picture cards, education edition) by:
- Adding
typefield toTopicCard(e.g."text" | "picture" | "challenge"). - Adapting UI to show images instead of text where appropriate.
- Adding
- 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.