Pipeline 2 · LLM enrichment
Two depth tiers — wide (terse, ~5 fields) and deep (paragraphs + theme axis).
Two depth tiers, one orchestrator. Both produce structured extractions over BGG game descriptions; they differ in volume and depth.
Wide pass (v2)
- Schema:
prompts/extraction_v2.md— terse phrases, ~5 fields per game (core_verb,decision_shape,reward_schedule,aesthetics,core_loop_pitch,direct_digital_port, etc.). - Volume: runs across most translation-eligible games — currently ~2k of them.
- Lands in:
game_llm_primitivestable.
python scripts/enrich_llm.py prepare --depth wide --n 50
# (run an LLM agent against the prepared input batches; outputs land as JSONL)
python scripts/enrich_llm.py aggregate --depth wide
Deep pass (v3)
- Schema:
prompts/extraction_v3.md— paragraph-length fields plus a separate theme axis (promise,setting,narrative,audience,art_direction,digital_meta_layer_ideas,live_service_potential, etc.). - Volume: ONLY top translation candidates already flagged by track A — ~40 games.
- Lands in:
game_llm_deeptable.
Never run v3 wide. Never downgrade v3 outputs into the v2 schema.
How to steer
When QA-ing an output:
- Read it next to the BGG description. The model should be paraphrasing, not inventing.
- Check
confidence— if it's < 0.5 and the verb feels off, the extraction is likely wrong. - Spot-check JSONL with
python -m json.tool(single line → pretty JSON), not custom labeled layouts.
When a field consistently misses, edit prompts/extraction_vN.md, re-prepare a small batch, and compare before/after.
QA tooling
scripts/preview_llm.py flags borderline extractions. Run it before aggregating into SQLite — once a bad row lands, it's only fixable by re-running the batch or hand-patching.
Wide pass (v2)
Terse phrases across most translation-eligible games. Schema in prompts/extraction_v2.md.
Show v2 prompt (10,612 chars)
# Game Mechanic Primitive Extraction — v2
You are decomposing board games into design primitives that go beyond BGG's coarse mechanism tags. The goal is to find translation candidates for the next "Dominion → Slay the Spire" or "Smash Up → Marvel Snap" mobile/Steam game.
For each game, output a JSON object with the following schema. **Be precise. Do not pad.** If you don't have strong knowledge of a game, set `confidence` lower; do not invent.
## Schema (v2 — note the splitting of mobile_analog into two fields)
```json
{
"game_id": <int>,
"name": "<string>",
"core_verb": "<2-5 words>",
"decision_shape": "<enum>",
"reward_schedule": "<enum>",
"aesthetics": ["<MDA aesthetic>", ...],
"core_loop_pitch": "<one sentence, <120 chars>",
"mobile_translation_difficulty": "<Easy|Medium|Hard>",
"translation_difficulty_reason": "<1-2 sentences>",
"direct_digital_port": "<port name with platform> or null",
"direct_digital_port_kind": "<first_party|bga|unofficial> or null",
"closest_loop_translation": "<game name> or 'none yet'",
"primitive_tags": ["<snake_case tag>", ...],
"confidence": <0.0-1.0>
}
```
## CRITICAL — the two analog fields, distinguished
**`direct_digital_port`** — Has THIS GAME been ported to digital (PC/Steam/iOS/Android)?
- If yes, give the port's name and platform: `"Splendor (mobile, Asmodee Digital)"`, `"Through the Ages (Steam, CGE)"`, `"Twilight Struggle (Steam, Playdek)"`
- If no port exists, set to `null` (JSON null, not the string "null")
- A port is a 1:1 digital implementation, even if it has AI opponents. Wingspan mobile is a port.
- Direct ports are NOT what we're hunting for — they tell us this game has been digitized but not *translated*.
**`direct_digital_port_kind`** — Categorizes the port. Set whenever `direct_digital_port` is not null; otherwise null.
- `first_party` — Official publisher port. Examples: Asmodee Digital, Dire Wolf Digital, Digidiced, CGE Digital, Playdek, Marmalade Game Studio, the publisher's own studio. Wingspan mobile, Splendor mobile, Through the Ages on Steam.
- `bga` — Board Game Arena licensed implementation (browser-based, asynchronous-friendly, often the only digital home for Eurogames whose publisher hasn't made a standalone app). Examples: Welcome To…, Sushi Go Party!, Decrypto, Codenames Duet (BGA in addition to a separate browser implementation).
- `unofficial` — Community / fan-made browser or mobile implementation; no publisher backing. Often the only digital option for licensing-sensitive games. Examples: The Resistance: Avalon (community web/mobile state-trackers), 18xx.games (community-run), Secret Hitler (CC license forces fan-only).
- If a game has BOTH a first-party port AND a BGA implementation, prefer `first_party` (the more official channel wins).
- BGA-or-similar arena counts as a port for ranking purposes, but the kind flag preserves the distinction for downstream filtering.
**`closest_loop_translation`** — A DIFFERENT digital game that took the same core loop and added a digital-native meta-layer (the StS pattern).
- The canonical example: Dominion's `closest_loop_translation` is `"Slay the Spire"` — a different game that took deck-building and added roguelike runs.
- For Carcassonne: `"Dorfromantik"` (took tile-laying, added zen mode + infinite quest stack).
- For Smash Up: `"Marvel Snap"` (took faction-decks-at-locations, added the Snap stake-doubling).
- For Quacks: `"Balatro"` (loosely — took push-your-luck escalation and recombined with poker)
- **A digital port of THIS game is NOT a `closest_loop_translation`. Put that in `direct_digital_port` instead.**
- If no digital game has taken THIS game's loop and translated it (added a meta-layer beyond a port), set to `"none yet"`.
- `"none yet"` is the gold signal — that's what the project is hunting for.
### Decision tree for filling these two fields
```
Has the game itself been digitized (Steam, mobile, browser)?
├── YES → direct_digital_port = "Game Name (platform, publisher)"
│ direct_digital_port_kind = first_party | bga | unofficial
└── NO → direct_digital_port = null
direct_digital_port_kind = null
Has a DIFFERENT digital game taken this game's core loop and added a digital meta-layer?
├── YES → closest_loop_translation = "Other Game Name"
└── NO → closest_loop_translation = "none yet"
```
Both fields can be populated simultaneously (e.g., Dominion has BOTH a direct port AND Slay the Spire as its loop translation).
## Field definitions (unchanged from v1 unless noted)
**`core_verb`** — the action you repeat every 3–10 seconds of play. Bias toward concrete actions you'd describe to a 10-year-old. Examples:
- "draft and play card" (Slay the Spire, Dominion)
- "place worker on action space" (Agricola)
- "roll and assign dice" (Sagrada)
- "place tile, score adjacency" (Carcassonne)
- "draw chip from bag" (Quacks)
- "play minion at lane" (Marvel Snap, Smash Up)
**`decision_shape`** — the *shape* of the choice space. Pick ONE primary, optionally compound:
- `combinatorial` — finite discrete options, optimal-ish
- `probabilistic` — outcomes stochastic, decisions manage risk
- `spatial` — placement on a grid/board where geometry matters
- `social` — choices depend on other players' state/intent
- `mixed:X+Y` — clearly two of the above
**`reward_schedule`** — when does payoff arrive?
- `immediate` — point/effect this turn
- `delayed` — payoff in N turns or end-game
- `variable` — payoff is stochastic
- `engine_compounding` — early investments produce increasing returns
- `mixed:X+Y`
**`aesthetics`** — array of 1–3 from MDA: `Discovery`, `Challenge`, `Sensation`, `Fellowship`, `Expression`, `Submission`, `Narrative`, `Fantasy`.
**`core_loop_pitch`** — describe what you're DOING moment-to-moment in one tight sentence (≤120 chars). NOT what the game is about.
**`mobile_translation_difficulty`** — `Easy` / `Medium` / `Hard`.
**`translation_difficulty_reason`** — 1–2 sentences. Be specific.
**`primitive_tags`** — 3–7 snake_case tags that go BEYOND BGG's vocabulary. Bad tags (too BGG-like): `worker_placement`, `dice_rolling`, `card_drafting`. Good tags: `action_spaces_with_blocking`, `bag_composition_meta`, `card_with_double_edged_event`.
**`confidence`** — your self-rated confidence: 1.0 = played; 0.7 = know by reputation; 0.4 = description-only; 0.2 = guessing.
## Calibration examples (study these before extracting)
```json
{
"game_id": 36218,
"name": "Dominion",
"core_verb": "draft and play card",
"decision_shape": "combinatorial",
"reward_schedule": "engine_compounding",
"aesthetics": ["Challenge", "Discovery"],
"core_loop_pitch": "Buy cards into your discard, reshuffle, draw new hand, repeat.",
"mobile_translation_difficulty": "Easy",
"translation_difficulty_reason": "Already done — Slay the Spire is the canonical example. The shuffle/draw cycle compresses well to touch input.",
"direct_digital_port": "Dominion Online (browser, Shuffle iT)",
"direct_digital_port_kind": "first_party",
"closest_loop_translation": "Slay the Spire",
"primitive_tags": ["deck_growing", "card_combo_chaining", "tableau_market_refresh", "incremental_economy"],
"confidence": 1.0
}
```
```json
{
"game_id": 822,
"name": "Carcassonne",
"core_verb": "place tile, score adjacency",
"decision_shape": "spatial",
"reward_schedule": "mixed:immediate+delayed",
"aesthetics": ["Discovery", "Expression"],
"core_loop_pitch": "Draw and place a tile to extend roads/cities/fields; place a meeple to claim what you complete.",
"mobile_translation_difficulty": "Easy",
"translation_difficulty_reason": "Tile placement is naturally touch-friendly; Dorfromantik proved the zen translation works.",
"direct_digital_port": "Carcassonne (mobile/Steam, Asmodee Digital)",
"direct_digital_port_kind": "first_party",
"closest_loop_translation": "Dorfromantik",
"primitive_tags": ["spatial_adjacency_scoring", "incremental_map_reveal", "claim_at_completion", "polyomino_constraint_relaxed"],
"confidence": 1.0
}
```
```json
{
"game_id": 31260,
"name": "Agricola",
"core_verb": "place worker on action space",
"decision_shape": "combinatorial",
"reward_schedule": "delayed",
"aesthetics": ["Challenge", "Submission"],
"core_loop_pitch": "Place a worker to claim one of finite shared actions; rebuild your farm before the harvest punishes you.",
"mobile_translation_difficulty": "Hard",
"translation_difficulty_reason": "The blocking dynamic between players IS the game. Solo AI doesn't reproduce the constraint, and the long planning arc fights phone session length.",
"direct_digital_port": "Agricola: All Creatures Big and Small (mobile, Digidiced)",
"direct_digital_port_kind": "first_party",
"closest_loop_translation": "none yet",
"primitive_tags": ["action_spaces_with_blocking", "feeding_pressure", "tech_tree_unlock", "tableau_personal_board"],
"confidence": 1.0
}
```
```json
{
"game_id": 178900,
"name": "Codenames",
"core_verb": "give one-word clue",
"decision_shape": "social",
"reward_schedule": "immediate",
"aesthetics": ["Fellowship", "Expression", "Challenge"],
"core_loop_pitch": "Spymaster gives a single word linking multiple agent words; team guesses without hitting bombs.",
"mobile_translation_difficulty": "Hard",
"translation_difficulty_reason": "The fun is the social negotiation around interpreting clues; AI clue-giver kills the joke.",
"direct_digital_port": "Codenames Online (browser, Czech Games)",
"direct_digital_port_kind": "first_party",
"closest_loop_translation": "none yet",
"primitive_tags": ["semantic_overloading", "team_communication_constraint", "pattern_matching_under_ambiguity"],
"confidence": 1.0
}
```
## Process per game
1. Read the provided BGG data (name, description, mechanisms, categories, weight, year).
2. WebSearch `<game name> board game gameplay how to play review` and skim the top 1–2 results.
3. **Crucially: search for digital ports.** Use a query like `<game name> digital app Steam mobile` — be honest about what exists. Asmodee Digital, CGE Digital, Digidiced, Dire Wolf Digital, and Playdek port a lot of Eurogames.
4. Synthesize the JSON. Distinguish `direct_digital_port` (the game itself, digitized) from `closest_loop_translation` (a different game that took the loop).
5. Append the JSON object as a single line to the output JSONL file.
## Output format
Write one JSON object per game as a single line in a `.jsonl` file at the path specified by the orchestrator. Do NOT wrap in markdown code fences. Do NOT add commentary between objects. Just JSONL.
[
{
"game_id": 174430,
"name": "Gloomhaven",
"rank": 1,
"year": 2017,
"weight": 3.9183,
"bayes": 8.29928,
"playtime_min": 120,
"players": "1-4",
"usersrated": 67033,
"description": "Gloomhaven is a game of Euro-inspired tactical combat in a persistent world of shifting motives. Players will take on the roles of wandering adventurers with their own special sets of skills and their own reasons for traveling to this dark corner of the world. Players must work together out of necessity to clear out menacing dungeons and forgotten ruins. In the process, they will enhance their abilities with experience and loot, discover new locations to explore and plunder, and expand an ever-branching story fueled by the decisions they make.\n This is a game with a persistent and changing world that is ideally played over many game sessions. After a scenario, players will make decisions about what to do next, which will determine how the story continues, kind of like a “Choose Your Own Adventure” book. Playing through a scenario is a co-operative affair where players will fight against automated monsters using an innovative card system to determine the order of play and what a player does on their turn.\n\nEach turn, a player chooses two cards to play out of their hand. The number on the top card determines their initiative for the round. Each card also has a top and bottom power, and when it is a player’s turn in the initiative order, they determine whether to use the top power of one card and the bottom power of the other, or vice-versa. Players must be careful, though, because over time they will permanently lose cards from their hands. If they take too long to clear a dungeon, they may end u exhausted and be forced to retreat.\n\n",
"bgg_mechanisms": [
"Modular Board",
"Variable Player Powers",
"Campaign / Battle Card Driven",
"Simultaneous Action Selection",
"Cooperative Game",
"Hexagon Grid",
"Storytelling",
"Role Playing",
"Hand Management",
"Grid Movement",
"Action Queue",
"Solo / Solitaire Game",
"Scenario / Mission / Campaign Game",
"Legacy Game",
"Action Retrieval",
"Once-Per-Game Abilities",
"Narrative Choice / Paragraph",
"Critical Hits and Failures",
"Card Play Conflict Resolution",
"Communication Limits",
"Line of Sight",
"Deck Construction",
"Multi-Use Cards",
"Tags"
],
"bgg_categories": [
"Fantasy",
"Exploration",
"Adventure",
"Fighting",
"Miniatures"
]
}
]{
"game_id": 174430,
"name": "Gloomhaven",
"core_verb": "play two cards, pick top/bottom action",
"decision_shape": "mixed:combinatorial+spatial",
"reward_schedule": "mixed:delayed+engine_compounding",
"aesthetics": [
"Challenge",
"Narrative",
"Discovery"
],
"core_loop_pitch": "Each round pick two ability cards; combine one top action with one bottom to fight monsters on a hex grid before your hand burns out.",
"mobile_translation_difficulty": "Medium",
"translation_difficulty_reason": "The card-pair selection and exhaustion economy port well, but the campaign legacy meta and party coordination create UI and session-length friction.",
"direct_digital_port": "Gloomhaven (Steam/console, Asmodee Digital + Flaming Fowl)",
"closest_loop_translation": "none yet",
"primitive_tags": [
"card_burn_attrition",
"top_bottom_dual_use_card",
"initiative_via_card_value",
"modifier_deck_variance",
"branching_campaign_unlock"
],
"confidence": 0.9
}Deep pass (v3)
Paragraph-length structured analysis on top translation candidates only. Schema in prompts/extraction_v3.md.
Show v3 prompt (9,856 chars)
# Game Mechanic Primitive Extraction — v3 (Deep)
You are doing **deep enrichment** on top translation candidates surfaced by the v2 wide pass. Where v2 captured terse phrases (`core_verb`: 2-5 words), v3 captures **paragraphs of structured analysis** plus a separate **theme axis** that v2 didn't cover at all.
Run this only on games already flagged as strong translation candidates — this is not a wide pass.
## Quantitative data is NOT for the LLM
`playingtime`, `minplaytime`, `maxplaytime`, `minplayers`, `maxplayers`, `averageweight`, `yearpublished`, `bgg_mechanisms`, `bgg_categories` come from the BGG API (already in `data/bgg.sqlite`). Do not re-output them.
## Schema (v3)
```json
{
"game_id": <int>,
"name": "<string>",
"mechanics": {
"core_verb_long": "<2-4 sentence paragraph: the physical action you repeat — what your hands do, the cadence, the immediate sensory feedback>",
"core_loop_long": "<3-5 sentence paragraph: full turn / round structure, what feeds what, scoring trigger, end-game condition. Be specific about phase order.>",
"decision_space": "<3-5 sentences: what choices the player makes, what tradeoffs they balance, what makes a 'good move' hard to identify, the size of the option space at a typical decision>",
"skill_expression": "<3-5 sentences: which skills separate strong players from weak ones — pattern recognition, risk modeling, opponent reading, spatial planning, memory, hand management, mental arithmetic, tempo, dexterity. Pick the dominant 2-3 and say what they look like in this game specifically.>",
"tactile_dependency": "<none|low|medium|high>",
"tactile_dependency_reason": "<1-2 sentences. 'high' = the game needs your hands (Crokinole's flick, Junk Art's stack, Klask's magnet). 'medium' = physical components carry information density that's hard to compress (Through the Ages' hand of cards, Twilight Imperium's map). 'low' = rooted in legible information that maps cleanly to a phone (Splendor, Dominion, Carcassonne). 'none' = abstracts that already work as pure logic (chess, Go, Hive).>"
},
"theme": {
"promise": "<one tight sentence: what the box promises the buyer in plain English. 'Be a Viking trader managing your house's economy.' / 'Race a 60s F1 car and feel like a stunt driver.' / 'Solve a wire-cutting bomb-defusal puzzle with your friends.'>",
"setting": "<setting/genre tags, comma-separated: medieval, sci-fi, fantasy, modern, abstract, historical, horror, etc.>",
"narrative": "<is there a story layer? options: 'campaign — N scenarios with branching outcomes' / 'legacy — permanent world changes' / 'embedded — story unfolds via cards' / 'none — abstract' / 'none — pasted-on theme'. Be honest: many heavy Eurogames are mechanically deep but narratively pasted-on.>",
"audience": "<who's this for: 'gateway' / 'family' / 'hobbyist Eurogamer' / 'hardcore strategist' / 'wargamer' / 'kids' / 'party' / 'designer-game-aficionado'. Often more than one — comma-separate the primary 2.>",
"art_direction": "<illustration style, palette, period feel, named illustrator if notable. 'Vincent Dutrait painterly fantasy', 'flat-vector pop-art', 'Klemens Franz earthen agrarian', 'sci-fi minimalist with neon accents'.>"
},
"translation": {
"digital_meta_layer_ideas": [
"<2-4 concrete StS-pattern meta-layers this game's loop could anchor: 'roguelite season run', 'merge-2 hybrid', 'auto-battler tournament ladder', 'daily challenge with leaderboard', 'persistent guild progression', etc. Each idea should be specific enough to pitch — name the meta-layer, name how it grafts onto the loop.>"
],
"closest_mobile_genre": "<the mobile genre this would translate INTO, not the genre it currently is: 'roguelite deckbuilder' / 'async PvP card battler' / 'merge puzzle' / 'tactics RPG' / 'idle/incremental' / 'real-time strategy lite' / 'puzzle/zen' / 'word/trivia'>",
"live_service_potential": "<none|low|medium|high — can this support weekly content drops, seasonal events, evolving meta? Most pure-strategy Eurogames are 'low' (premium one-and-done). Auction games and deckbuilders are 'medium-high'.>"
},
"confidence": <0.0-1.0>,
"extraction_version": "v3"
}
```
## Process per game
1. Read input game data: BGG description, mechanisms, categories, weight, year, playtime.
2. WebSearch: `<game name> rules how to play` — read 1 detailed rules summary.
3. WebSearch: `<game name> review strategy` — get the reviewer-perspective on what makes the game tick.
4. WebSearch: `<game name> designer interview` — pull designer-voice if available.
5. WebSearch: `<game name> art direction illustrator` (only if game has notable art).
6. Synthesize each field as a paragraph or full sentence — **do not pad, but do not be terse**. The point of v3 is depth.
7. Append the JSON object as one JSONL line.
## Quality bar
- **Mechanics paragraphs:** Concrete verbs, named components, numeric specifics ("hand of 7 cards drawn from a 60-card market", not "you draw cards"). If the game has a famous load-bearing mechanic (the Heat overheat economy, Dominion's deck-thinning), name it.
- **Theme paragraph:** *Honest about pasted-on themes.* Many Eurogames have mechanically rich loops with thin theme — say so. ("Mechanically: a worker-placement engine. Thematically: paint by numbers — Trajan's Roman setting is decorative.")
- **Skill expression:** This is the most signal-rich field for translation work. Mobile genres reward different skills than tabletop — identifying which skills the tabletop game *actually* tests is what predicts mobile fit.
- **Tactile dependency:** Be ruthless. A game can have physical components but low tactile dependency if those components encode legible information (Splendor's chips → digital chips). A game has high tactile dependency if removing the hands removes the experience (Crokinole, PitchCar, Klask, Junk Art).
- **Translation ideas:** *Specific meta-layers, not generic platitudes.* "Add a daily challenge" is not a meta-layer — it's a UI feature. "5-race procgen season with relic drafts between races" IS a meta-layer.
- **Confidence:** 1.0 = played extensively. 0.7 = read the rules + 2-3 reviews + reputation. 0.4 = description-only. Lower the confidence rather than fabricate.
## Calibration example — Heat: Pedal to the Metal
```json
{
"game_id": 366013,
"name": "Heat: Pedal to the Metal",
"mechanics": {
"core_verb_long": "Each round you grip a hand of 7 speed cards and choose a gear (1-4); the gear is the number of cards you must play face-down this turn. You commit cards behind a player screen, simultaneously with everyone else, then flip and total your speed. Optional: spend Heat to boost (flip the top of your deck for bonus speed) or shift gears more than one step.",
"core_loop_long": "Phase 1: choose gear, paying Heat for any shift greater than ±1. Phase 2: select N cards (N = gear), play face-down. Phase 3: simultaneous reveal — sum speeds, optionally boost. Phase 4: move; check each corner crossed, paying Heat per point of speed-limit overage. Phase 5: cooldown — gears 1-2 let you flush Heat from hand back to engine reserve. Phase 6: discard, redraw to 7. End-game: first to cross start/finish line at race end.",
"decision_space": "Per turn you balance gear-against-hand (which gear can I afford to play given my current cards), corner-against-tempo (downshift now to handle the apex, or push and pay Heat overage), and engine-against-deck-pollution (boost burns Heat which clogs future hands). The choice space is moderate — ~3-7 reasonable gear+hand combos at a typical turn — but the planning depth is 2-3 turns ahead because of corner positioning.",
"skill_expression": "Strongest skills: tempo planning (knowing when to sandbag in 1st gear to flush Heat) and corner-positioning forecast (judging whether a high-card hand will arrive in time for the chicane). Secondary: opponent reading (gauging when to slipstream off a leader). Almost no memory load. Math is simple addition. The game rewards drivers who think two corners ahead and punishes those who play turn-by-turn.",
"tactile_dependency": "low",
"tactile_dependency_reason": "Cards are face-down/face-up information; the simultaneous reveal is the only dramatically physical moment, and it can be replicated digitally with an opponent-first telegraph plus animated reveal. The board, dial, tokens are all purely informational."
},
"theme": {
"promise": "You are a 1960s F1 driver. Late-brake into the chicane, scream past the leader, feel like a stunt driver — without simulating tire physics.",
"setting": "Historical, motorsport, 1960s F1",
"narrative": "none — abstract race with optional Championship campaign overlay (5 race meta-game with persistent driver upgrades). Story is pasted-on; the drama is in the lap, not the lore.",
"audience": "gateway, hobbyist",
"art_direction": "Vincent Dutrait period-romance illustration: warm sepia-and-cream palette, hand-painted cars and circuits, deliberate echoes of 1960s sports magazines. Distinctive enough that the game is recognizable on a shelf at 10 feet."
},
"translation": {
"digital_meta_layer_ideas": [
"5-race roguelite season: procgen tracks, draftable upgrade cards between races, garage relics that warp the Heat economy, run ends if you finish last twice",
"Career championship: persistent driver, sponsor perks, season ladder",
"Daily challenge: fixed track + fixed AI rivals + leaderboard by finish position"
],
"closest_mobile_genre": "roguelite deckbuilder",
"live_service_potential": "medium"
},
"confidence": 1.0,
"extraction_version": "v3"
}
```
## Output format
Write one JSON object per game as a single JSONL line. **Do not** wrap in markdown fences. **Do not** add commentary between objects.
[
{
"game_id": 446497,
"name": "Hot Streak",
"rank": 380,
"year": 2025,
"weight": 1.2198,
"bayes": 7.10678,
"playtime_min": 20,
"minplaytime": 20,
"maxplaytime": 20,
"players": "2-8",
"usersrated": 5016,
"description": "Hot Streak is a game for hard luck gamblers who love to bet on and scream at the racers, who in this case happen to be mascots who may (or may not) keep running in the right direction.\n\nAt the start of the game, set up the racing deck with one card for each mascot and a number of random cards based on the player count. Reveal these cards to all players, after which players draft a betting ticket from those on display, then in reverse order draft a second bet. For each bet, you can play it safe — or flip it to the risky side, which might pay out more - or cost you money if you lose. After betting, each player chooses one of three cards in their hand to secretly add to the racing deck.\n\nShuffle the deck, burn three cards, then reveal cards one by one from the deck, moving the mascots along the track, with them sometimes swerving into another lane and knocking over another racer, sometimes turning around, sometimes all moving at once, and sometimes just going backwards! If a racer runs off the track or would be knocked over while already fallen, they're disqualified. If needed, shuffle all cards in the deck, burn three cards again, and keep racing until all four spots on the box podium are filled. Pay out bets based on these results.\n\nFor races #2-3, first deal each player a random card from the deck, then place bets again, then have each player contribute a card from their hand to the deck. After race #3, everyone tallies their money.\n\n",
"bgg_mechanisms": [
"Betting and Bluffing",
"Open Drafting"
],
"bgg_categories": [
"Party Game",
"Racing"
]
}
]{
"game_id": 446497,
"name": "Hot Streak",
"mechanics": {
"core_verb_long": "You hold a small hand of race cards, look over the publicly displayed betting tickets, and make two main physical actions: drafting a ticket from a shared row, then secretly slipping one of your hand cards face-down into the racing deck. Once everything is committed, the table flips into spectator mode — the deck is shuffled, three cards burned, and cards are revealed one at a time, scooting four chunky mascot meeples (Hurley the hot dog, Gobbler the bear, Dangler the anglerfish, Mum the monarch) along a roll-out track. Your hands stop moving; the cadence becomes pure call-and-response cheering and groaning as each flipped card sometimes scoots a mascot, sometimes turns one around, sometimes knocks one over.",
"core_loop_long": "Setup: build a racing deck of one card per mascot plus N random cards (N scales with player count) and lay it face-up so everyone can read the deck composition. Phase 1 (Draft): players draft one betting ticket from a public row in turn order, then a second ticket in reverse order, choosing safe-side or flipping to the risky-side payout. Phase 2 (Seed): each player secretly contributes one card from their 3-card hand into the racing deck. Phase 3 (Race): shuffle the deck, burn three cards, then flip cards one at a time, resolving moves, swerves, knockdowns, and reversals until four podium slots are filled — disqualifications happen when a mascot leaves the track or is knocked over while down. Pay bets, deal a fresh card to each player, repeat for races 2 and 3; whoever has the most money after three races wins.",
"decision_space": "The interesting decisions all happen before the race starts. Which ticket to draft is a function of three intersecting unknowns: which cards you saw in the public deck, which cards you and others are seeding in (and pulling out), and which risky/safe payout matches your read of the field. The seed choice is a small but meaningful sabotage/promotion lever — three options, but each one ripples through every other player's odds. The option space is small (typically 4-6 viable ticket-and-seed combos) but the information game is rich because you're partly reading other players' draft picks for tells. Once the race begins, decisions stop entirely.",
"skill_expression": "Dominant skills are odds estimation under hidden information (you know what's in the deck visible to all, plus what's in your hand, but not what others contributed) and opponent reading from draft order — when someone grabs a ticket on Hurley early, you update on what they probably seeded. Secondary: bankroll management across three races, since blowing a risky bet in race 1 can leave you chasing in race 3. There is essentially zero arithmetic, no memory load beyond a few cards, and no spatial planning. Strong players are the ones who treat the public deck as a probability tree and watch other players' eyes during the draft.",
"tactile_dependency": "low",
"tactile_dependency_reason": "All components are pure information: cards, tickets, mascot meeples on a track. The mascot figures and roll-out track are charming as objects but the spectacle is replicable digitally with animation; nothing here demands fingers."
},
"theme": {
"promise": "Be a degenerate gambler at an off-brand mascot race track — bet, scream, lose your shirt to a hot dog man.",
"setting": "Modern, comedic, racetrack/Vegas, mascot/cartoon",
"narrative": "none — pasted-on theme; the gambling-at-the-track flavor is the joke wrapper around a betting-and-drafting puzzle.",
"audience": "party, family",
"art_direction": "Cécile Gariépy bright flat-vector illustration with a retro-mid-century travel-poster sensibility; chunky whimsical mascot sculpts by Josh Divine visible through a box window. Bold primary palette, deliberately tacky off-brand mascot designs (Hurley the hot dog, Dangler the anglerfish), readable from across the room."
},
"translation": {
"digital_meta_layer_ideas": [
"Async friend-league pari-mutuel: 6 players bet on procgen mascot races over a 24h window, payouts settled when the simulation runs at a daily reset, persistent cash standings across a 4-week season",
"Twitch-stream race-track mode: streamer hosts the race, viewers buy tickets with channel currency in a shared betting window, race resolves live for the audience; designed for spectator chat virality",
"Daily off-brand-mascot-of-the-day: rotating mascot rosters with shifting card distributions, leaderboard by bankroll over a fixed 3-race set",
"Roguelike bookie mode: solo player runs the house against AI bettors, must price tickets correctly across an escalating ladder of races with weird modifier cards"
],
"closest_mobile_genre": "async PvP card battler",
"live_service_potential": "medium"
},
"confidence": 0.7,
"extraction_version": "v3"
}