Claude-skill-registry convex-game-management
Manage game lifecycle, player turns, round rotation, scoring, and game state transitions. Use when implementing game creation, turn management, score updates, and game completion flows in PictionAI.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/convex-game-management" ~/.claude/skills/majiayu000-claude-skill-registry-convex-game-management && rm -rf "$T"
skills/data/convex-game-management/SKILL.mdConvex Game Management
Overview
This skill handles the complete game lifecycle for PictionAI's multiplayer Pictionary game, including game creation, player management, turn-based rotation, atomic turn completion with scoring, and game state transitions.
Core Concepts
Game States
- waiting - Game created, players joining in lobby
- started - Game in progress, turns rotating
- finished - All rounds complete, winner determined
Turn States
- drawing - Active turn, timer running (60-120 seconds)
- completed - Turn finished with correct guess
- time_up - Timer expired, no points awarded
Atomic Turn System
Turns are completed atomically to ensure data consistency:
- Start Turn: Drawer calls
mutationstartNewTurn - Draw & Guess Phase: Real-time canvas sync, players submit guesses
- Turn Completion: Three atomic scenarios:
- ✅ Correct guess → Both guesser and drawer earn points
- 🏆 Manual winner selection → Drawer selects winner from guessers
- ⏱️ Time up → No points, proceed to next turn
- Score Updates: Dual scoring system (guesser time bonus + drawer base score)
- Next Turn: Auto round-robin rotation to next player
Scoring System
Guess Scoring (Correct Answer)
- Guesser: Base 50 points + time bonus (50 - elapsed_seconds, min 5 points)
- Drawer: 25% of guesser's score, minimum 10 points
Drawer Scoring (Manual Winner)
- Selected Guesser: 30 points
- Drawer: 25 points
Key Mutations
startNewTurn
Start a new drawing turn, assign card, initialize timer.
mutation startNewTurn { args: { game_id: Id<"games"> } // Returns: { // turn_id: Id<"turns">, // card: { word: string, category: string }, // drawer_id: Id<"users">, // time_limit: number // } }
submitGuessAndCompleteTurn
Submit a guess and complete the turn with atomic scoring.
mutation submitGuessAndCompleteTurn { args: { game_id: Id<"games">, turn_id: Id<"turns">, guesser_id: Id<"users">, guess: string, elapsed_time: number, is_correct?: boolean } // Handles three scenarios atomically: // 1. Correct guess → Award points to both // 2. Manual selection → Drawer chooses winner // 3. Time up → Skip to next turn }
selectWinner (Manual Selection)
Drawer selects a guesser as the winner when time expires.
mutation selectWinner { args: { turn_id: Id<"turns">, selected_guesser_id: Id<"users"> } // Awards 30 points to guesser, 25 to drawer }
Key Queries
getGame
Fetch complete game state with players, current turn, scores.
query getGame { args: { game_id: Id<"games"> } // Returns full game with nested players, turns, scores }
getGameTurns
Get all turns in a game with guesses and results.
query getGameTurns { args: { game_id: Id<"games"> } // Returns array of turns with scoring details }
React Integration
// Start a new turn const startTurn = useMutation(api.mutations.game.startNewTurn); await startTurn({ game_id: gameId }); // Submit guess and complete turn const submitGuess = useMutation(api.mutations.game.submitGuessAndCompleteTurn); await submitGuess({ game_id: gameId, turn_id: turnId, guesser_id: userId, guess: "elephant", elapsed_time: 45, }); // Fetch game state const game = useQuery(api.queries.games.getGame, { game_id: gameId });
Database Schema References
// Games table games: defineTable({ code: v.string(), // Unique game code creator_id: v.id("users"), players: v.array( v.object({ user_id: v.id("users"), score: v.number(), is_drawer: v.boolean(), }) ), state: v.union( v.literal("waiting"), v.literal("started"), v.literal("finished") ), current_turn_index: v.number(), created_at: v.optional(v.number()), round_count: v.number(), }); // Turns table turns: defineTable({ game_id: v.id("games"), drawer_id: v.id("users"), card_id: v.id("cards"), state: v.union( v.literal("drawing"), v.literal("completed"), v.literal("time_up") ), timer_started_at: v.number(), time_limit: v.number(), correct_guesser_id: v.optional(v.id("users")), guesses: v.array( v.object({ guesser_id: v.id("users"), guess: v.string(), timestamp: v.number(), is_correct: v.optional(v.boolean()), }) ), });
Common Patterns
Monitor active turn
const game = useQuery(api.queries.games.getGame, { game_id }); const currentTurn = game?.turns[game.current_turn_index]; const isMyTurn = currentTurn?.drawer_id === userId;
Handle turn completion
// When drawer completes turn with guess result const completeWithCorrect = useMutation( api.mutations.game.submitGuessAndCompleteTurn ); await completeWithCorrect({ game_id: gameId, turn_id: turnId, guesser_id: guesserId, guess: "elephant", elapsed_time: 30, is_correct: true, });
Automatic next turn
The mutation automatically:
- Rotates drawer (round-robin through players)
- Assigns new card
- Starts timer
- Updates game state
Edge Cases
- Multiple guesses per player: Allowed, only latest is scored
- Guess submitted after timer: System checks elapsed time on server
- Late guess arrival: Real-time subscription updates guess feed
- Manual selection ambiguity: Drawer must explicitly select one winner
- Game with 2 players: Drawer rotation still works with round-robin
See Also
- TURN_MANAGEMENT_ANALYSIS.md - Detailed atomic turn analysis
- Complete mutation implementationsconvex/mutations/game.ts
- UI integrationcomponents/game/game-board.tsx