Skip to main content

Quick Start

Say to your Alexa device:

"Alexa, open Flip Match"

  1. 1Pick a board size — from easy 3x2 to challenging 6x6
  2. 2Flip cards by calling out positions
  3. 3Match pairs to clear the board and score points
  4. 4Beat your personal best or challenge friends on the leaderboard

Overview

Status: COMPLETE — Full design documented

Last updated: October 9, 2026

Flip Match — Game Design Overview

Status: COMPLETE — Full design documented

Doc coverage:: COMPLETE — Full code extraction done. All mechanics verified against Java source.

PLAYTEST READY: All handlers call addText() and set storyOptionsList. Wrapper has fallback for unhandled input. All core bugs fixed (hints reveal cards, abandon updates stats, achievements wired, par times rebalanced). Ready for pipeline testing.

Flip Match (internally "Memory Snap") is a grid-based memory/matching card game. Players flip cards using chess-style coordinates (A1, B3, H8) to find matching pairs. Features three difficulty levels with time limits, four themes, a scoring system with streak bonuses, achievements, and leaderboards.

Game Identity

Property Value
play_game ID memory_snap
skill_identifier MEMORY_SNAP
Genre Memory/matching card game
Engine Grid-based card flip mechanics
DB Schema memory_snap

Design Pillars

  1. Simple Core, Deep Mastery — Easy to understand (flip two cards, find matches), hard to perfect (minimum turns, streak bonuses, time pressure)
  2. Accessibility — Chess-style coordinates work across voice (Alexa), web, and API; case-insensitive, "flip" prefix optional
  3. Replayability — Randomized boards, four themes, three difficulties, achievements, and leaderboards drive replay
  4. Progressive Challenge — EASY (no timer, 3 hints) → MEDIUM (4-min timer, 2 hints) → HARD (5-min timer, 1 hint)

Core Loop

Player starts game (selects difficulty + theme):
  → Board generated with shuffled card pairs
    → Player flips first card ("flip A1") — card revealed
      → Player flips second card ("flip B3")
        → MATCH: Both cards stay revealed, score increases, streak builds
        → NO MATCH: Both cards flip back, streak resets
          → Turn counter increments
    → Optional: Use hint (reveals a matching pair, -50 score penalty)
    → Optional: Time expires (MEDIUM/HARD only) → game abandoned
  → Game ends when all pairs found
    → Final score calculated: (base + bonuses - deductions) × difficulty multiplier
      → Personal best updated if higher
      → Leaderboard updated if score qualifies
      → Achievement progress updated

Board Configuration

Difficulty Levels

Difficulty Board Size Pairs Cards Hints Time Limit Par Time Score Multiplier
EASY 4×4 8 16 3 None 60s 1.0×
MEDIUM 6×6 18 36 2 240s (4 min) 120s 1.5×
HARD 8×8 32 64 1 300s (5 min) 150s 2.0×

Default: MEDIUM difficulty, ANIMALS theme (stored in player preferences).

Par time is always below the time limit. Completing under par earns +100 bonus points.

Themes (4)

Theme Display Name Description
ANIMALS Animals Cute and wild animals from around the world
FLOWERS Flowers Beautiful flowers and blossoms
FOOD Food Delicious foods and treats
TRANSPORT Transport Vehicles and ways to travel

Themes are database-driven via MemorySnapCardTheme and MemorySnapCardItem tables. Each theme contains card items with:

  • itemName (e.g., "lion")
  • itemDisplayName (e.g., "Lion")
  • itemEmoji (e.g., "🦁")
  • itemDescription (e.g., "The king of beasts")
  • audioName — audio file reference for Alexa
  • difficultyWeight — weighted distribution for board generation
  • isActive flag and optional unlockRequirement on themes

Store pack decks and card backs

What each STARTER and seasonal pack promises beside its Hint Credits is MemorySnapStorePackGrants, Flip Match's part of the shared pack-grant path ( § Store pack items). The listing copy is GAME_COPY["memory_snap"] in scripts/alexa/smapi_products.py.

Pack Deck Card back Consumable
STARTER, Sharp Focus Starter none Sharp Focus Two perfect round bonus rolls
Spring Garden SPRING_GARDEN, 32 cards Bloom none
Summer Beach SEASIDE, 32 cards Shell none
Autumn Forest FOREST, 32 cards Leaf none
Winter Frost FROST, 32 cards Snowflake none
  • Held while a completed purchase stands, read from game_log.purchase_transactions through StorePackHoldings; nothing is written on delivery. A subscriber holds every pack deck too, as the subscription promises every premium theme. The pack card backs are held by the pack alone.
  • Settings chooses a held deck or card back by name, and names the pack that sells one the player does not hold. The card back list marks each unheld pack design with its pack.
  • A perfect round bonus roll is spent when the last pair is found on a solo board that was not perfect: the round scores the +500 perfect game bonus anyway. A board that was already perfect keeps the roll.
  • The decks are content rows in memory_snap.card_theme and card_item, 32 items each, with premium set on the theme enum. Until those rows exist, settings treats a deck as unseeded and offers it to no one.
  • A refund takes back the starter's unused bonus rolls, and the card back and deck once no other delivery of that pack stands; a preferred card back or deck from the refunded pack goes back to Classic or Animals.

Coordinate System

Chess-style letter-number coordinates:

  • Columns: A-D (4×4), A-F (6×6), A-H (8×8)
  • Rows: 1-4 (4×4), 1-6 (6×6), 1-8 (8×8)
  • Case insensitive: a1 = A1
  • "flip" prefix optional: "flip A1" = "A1"
  • Coordinate regex: ([A-Ha-h])\s*([1-8])
  • Internal mapping: positionToCoordinate(row, col) → e.g., (0,0) → "A1"

Card & Board Model

MemorySnapCard

Each card on the board:

  • itemId — references the card content
  • itemName / displayName / emoji — card identity
  • revealed (boolean, default false) — currently face-up
  • matched (boolean, default false) — successfully paired

Display logic: getDisplay() returns emoji if revealed/matched, else "🎴" (hidden card).

MemorySnapBoard

2D grid of cards:

  • size — board dimension (4, 6, or 8)
  • cards — MemorySnapCard[size][size] 2D array
  • matchedPositions — Set<String> of matched coordinates (e.g., "A1", "B3")
  • firstFlip / secondFlip — current turn's flipped coordinates
  • currentStreak — consecutive successful matches

Key methods:

  • flipCard(coordinate) → MemorySnapFlipResult
  • hideUnmatchedCards() — reset unmatched cards after failed flip
  • isGameComplete() — all pairs found
  • getPairsRemaining() — unmatched pairs count
  • getPlayerView() → MemorySnapBoardView (safe for display)
  • toAsciiArt() — ASCII board representation

Board is serialized as JSON in the database via (MyBatis type handler).

Board Generation

:

  1. Fetch theme by name
  2. Get random items from theme (matching required pair count)
  3. Create two cards per item (the pair)
  4. Shuffle all cards
  5. Arrange in 2D grid

Game State

MemorySnapGameSession

Field Type Purpose
sessionId Long Primary key
accountId Long Player reference
gameState MemorySnapGameState ACTIVE, COMPLETED, ABANDONED
boardSize int 4, 6, or 8
difficulty MemorySnapDifficulty Enum
theme String Theme name
board MemorySnapBoard Serialized game board (JSON)
turnsTaken int Flip turns completed
hintsUsed int Hints consumed
score int Current/final score
longestStreak int Best consecutive match streak
startedAt LocalDateTime Game start
completedAt LocalDateTime Game end
timeElapsedSeconds int Elapsed time

Game State Enum

State Description
ACTIVE Game in progress
COMPLETED All pairs found
ABANDONED Player quit or time expired

Flip Result

MemorySnapFlipResult returned from each card flip:

  • success — whether flip was valid
  • coordinate — flipped position
  • card — the card object
  • isMatch — whether second flip matched
  • isSecondFlip — first vs second flip
  • gameComplete — whether this flip finished the game
  • score — score for this action
  • streak / streakBonus — streak state

Game Rules

  • Cannot flip an already-matched card
  • Cannot flip the same card twice in one turn
  • Mismatched cards flip back automatically (hideUnmatchedCards())
  • Turn counter increments on each pair attempt (match or not)
  • Time limit enforced on MEDIUM (4 min) and HARD (5 min)
  • Abandoned games produce no leaderboard entry
  • One active game per player at a time

Scoring System

Formula

Base Score = 1000

Par Time = difficulty.getParTimeSeconds()
  EASY:   60s
  MEDIUM: 120s
  HARD:   150s

Deductions:
  Per extra turn beyond minimum:  -10 points
  Per second over par time:       -1 point
  Per hint used:                  -50 points

Bonuses:
  Perfect game (minimum turns):   +500 points
  Under par time:                 +100 points
  No hints used:                  +100 points
  Streak bonus:                   +10 per consecutive match

Final Score = (Base + Bonuses - Deductions) × Difficulty Multiplier

Minimum Turns

Minimum turns = number of pairs (one turn per pair in a perfect game):

  • EASY: 8 turns minimum
  • MEDIUM: 18 turns minimum
  • HARD: 32 turns minimum

Par Time

Par times are fixed per difficulty (not calculated from pairs):

Difficulty Par Time Time Limit Headroom
EASY 60s None Unlimited
MEDIUM 120s 240s 120s
HARD 150s 300s 150s

Streak Bonus

Consecutive matches build a streak: +10, +20, +30... (cumulative +10 per consecutive match). Streak resets after any non-match.

Score Examples

Scenario Calculation Final
Perfect 4×4 (8 turns, no hints, under par, max streak 8) (1000 + 500 + 100 + 100 + 80) × 1.0 1,780
Good 4×4 (12 turns, no hints) (1000 - 40 + 100) × 1.0 1,060
4×4 with 2 hints (10 turns) (1000 - 20 - 100) × 1.0 880
Hard 8×8 (40 turns, 8 extra) (1000 - 80) × 2.0 1,840

Every final score also adds the longest-streak bonus, +10 per consecutive match (see Streak Bonus above). A perfect game's streak equals the pair count, so a Perfect 4×4 includes +80 (max streak 8). The non-perfect rows omit a play-dependent streak for illustration.

Score Calculator

provides:

  • calculateFinalScore(session) — complete game final score
  • calculateCurrentScore(session) — in-progress estimated score
  • isPerfectGame(session) — minimum turns check
  • getParTime(difficulty) — par time for difficulty

Hint System

Difficulty Starting Hints
EASY 3
MEDIUM 2
HARD 1
  • Effect: Reveals one unmatched pair's locations (both coordinates shown)
  • Cost: -50 points per hint used
  • Bonus: +100 points for completing without using any hints
  • Limit: Cannot use hints when none remaining

Player Account

MemorySnapAccount

Field Type Purpose
accountId Long Primary key
userId String User identifier
displayName String Display name
totalGamesPlayed int Lifetime games started
totalGamesWon int Games completed
bestScore4x4 Integer Personal best (EASY)
bestScore6x6 Integer Personal best (MEDIUM)
bestScore8x8 Integer Personal best (HARD)
totalMatchesFound int Lifetime pairs matched
currentStreak int Current win streak
longestStreak int Best win streak
preferredDifficulty String Default difficulty ("MEDIUM")
preferredTheme String Default theme ("ANIMALS")
soundEnabled boolean Sound preference (true)
hintsEnabled boolean Hints preference (true)

Key methods: updateBestScore(), recordGameCompletion(), getWinRate()


Leaderboard

MemorySnapLeaderboardEntry

Field Type Purpose
leaderboardId Long Primary key
accountId Long Player reference
boardSize int 4, 6, or 8
difficulty MemorySnapDifficulty Game difficulty
score int Final score
turnsTaken int Turns used
timeSeconds int Time elapsed
theme String Theme played
achievedAt LocalDateTime When achieved
displayName String Player name (joined)
rank Integer Calculated, not stored
  • Separate leaderboards per difficulty level
  • Only COMPLETED games qualify (not ABANDONED)
  • Score-based ranking within difficulty/board size combination
  • getFormattedTime() returns MM:SS format

Achievement System

Achievement Definitions

MemorySnapAchievement defines achievable milestones:

Field Type Purpose
achievementId int Primary key
achievementKey String Unique key (e.g., "first_win")
achievementName String Display name
achievementDescription String Description
iconEmoji String Achievement icon
requirementType RequirementType Category
requirementValue int Target value
requirementBoardSize Integer Specific board (null = any)
points int Points awarded

Requirement Types (6)

Type Description
GAMES_WON Win N games total
PERFECT_GAME Achieve a perfect game (minimum turns)
STREAK Reach N consecutive matches
SPEED Complete under N seconds
MATCHES Find N total matches lifetime
THEME_COMPLETE Complete all themes

Player Achievement Progress

MemorySnapAccountAchievement tracks per-player progress:

  • progress (int) — current progress toward goal
  • unlocked (boolean) — whether achieved
  • unlockedAt (LocalDateTime)
  • Methods: getProgressPercent(), unlock(), updateProgress()

Commands

Command Action
start [difficulty] [theme] Start new game
flip [coordinate] / [coordinate] Flip a card
hint Use a hint
status Show game state
leaderboard [difficulty] View high scores
settings View/change preferences
quit Abandon current game
help Show instructions

Known Design Issues

Issue Description
Alexa integration incomplete 2/12 Alexa integration steps complete. What exists: (1) alexaSpeech field on all DTOs (MemorySnapFlipResult, MemorySnapGameStatus) with speech text populated by MemorySnapBoard.flipCard() and , (2) SkillIdentifier.MEMORY_SNAP registered in the skill identifier enum. What's missing: No Alexa entry point handler (no file in engines/entry/alexa/ for Memory Snap), no Alexa skill manifest or interaction model, no intent handlers mapped to Memory Snap commands, no voice-specific coordinate parsing, no SSML formatting, no Alexa-specific session management, no skill certification, no skill store listing, no multi-turn dialog for coordinate disambiguation, no voice-optimized board state readback. The backend generates alexaSpeech text on every flip/hint/status response, but there is no Alexa handler to consume it.
Theme item counts Each theme must have 32+ items for HARD mode — verify counts
Premium themes unseeded The four premium theme enums (Mythical Creatures, Space, Ocean, Dinosaurs) have no memory_snap.card_theme or card_item rows. Settings gates them as "coming soon"; the board generator falls back to a shipped theme if a stale premium preference is loaded. Seed 4 × 32 items to ship them.

Launch / Exit / Help Baseline Audit (2026-06-17)

Review of whether Flip Match's launch, exit, and help systems are SURFACED, WELL BUILT, and ride the shared baseline framework with per-game OVERRIDES (vs bespoke reimplementations). Verified by play_game target:'local' + handler/NLU inspection. Source of truth for the framework: engines/handlers/common/baseline/.java (exit), engines/handlers/common/baseline/.java

  • engines/handlers/common/help/.java (help), engines/entry/shared/ (entry/launch lifecycle), (NLU tiers).

Summary

System Surfaced? Well built? Rides shared baseline w/ overrides? Verdict
Launch — first-time onboarding Yes Yes Yes ( + WelcomeTextUtility.BETA_DISCLAIMER_AND_ASK) PASS
Launch — returning-player resume Partial No Infra shared, but resume UX is a bespoke fallback FIXED (FM-LEH-3)
Exit (exit/stop/goodbye/bye/quit) Yes but wrong No NO — bespoke override bypasses FIXED (FM-LEH-1)
Help (help/what can I say/options) Yes (real menu) Partial NO — bespoke static string, no FIXED (FM-LEH-2)
Branding (spoken/text name) n/a No n/a FIXED (FM-LEH-4)

FM-LEH-1 (HIGH) — Exit bypasses the shared baseline ; player cannot cleanly exit

game_process.process_action_handlers has (story_action=EXIT, game_type=MEMORY_SNAP) -> memorySnapQuitHandler.quitGame. Memory Snap is the only game with a per-game EXIT override (query confirmed across all games). Because the global skill-exit words all resolve to EXIT at Tier 1 PRIORITY_COMMANDS (exit/stop/goodbye/good bye/bye/quit/leave game each have a PRIORITY_COMMANDS -> EXIT direct-match row), and the claims EXIT for the MEMORY_SNAP game type, EXIT never reaches the shared .

Result (reproduced on local):

  • exit / stop / goodbye from the menu -> "You don't have an active game. Say start game to begin a new Memory Snap game." (.java:38-40) — and the skill stays open (quitGame returns CONTINUE_GAME).
  • quit mid-game -> "Game abandoned. You found 0 of 8 pairs..." (.java:55-58) — abandons the board, stays in the skill.
  • Compare Willow Farm exit / Zoo goodbye -> shared : "Your game is saved on every interaction, so you will pick up exactly where you left off when you return. Thanks for playing! Please consider leaving a rating or review... Bye bye / Farewell" + actual session end (.java:41-89, returns !CONTINUE_GAME).

So Flip Match FAILS both "cleanly exit" and "save on exit" — it abandons (discards) the game instead of saving, never says goodbye, never ends the session, and never runs the account-link / review nudge.

Proper baseline-with-override: REMOVE the (EXIT, MEMORY_SNAP) process_action_handlers row so EXIT flows to like every other game. If Flip Match needs exit-time cleanup, implement a GameStateClearer provider (the SPI already invokes, .java:97-103) — do NOT override the EXIT action. Keep MEMORY_SNAP_QUIT (in-game "leave game") as the explicit abandon path. Note "quit" has both a PRIORITY_COMMANDS -> EXIT row and a MEMORY_SNAP -> MEMORY_SNAP_QUIT row; once the EXIT override is removed, "quit" resolves to EXIT (skill exit) at Tier 1. Decide (design) whether "quit" should mean skill-exit (leave the PRIORITY row) or in-game abandon (drop the PRIORITY "quit" row so it falls to the MEMORY_SNAP row).

FM-LEH-2 (MEDIUM) — Help is a bespoke static string, not the shared /

(HELP, MEMORY_SNAP) -> memorySnapHelpHandler.showHelp AND (MEMORY_SNAP_HELP) -> showHelp. (.java:47-72) hand-builds a static string ("Memory Snap. Match pairs of cards to win...") and hardcodes options via addOptionNoSynonym. It never touches the shared (.java:32, dynamic, aggregates beans by supports() + priority) and no exists. Every other game (DarkTower, DarknessFalls, Lineage, Mystery, Zoo, WillowFarm) implements (engines/handlers/common/help/.java:16).

Help DOES surface a real menu (NOT a canned "not available"), so this is a quality/architecture gap, not a broken-help gap. Proper pattern: add implements and let the global HELP flow to (remove the (HELP, MEMORY_SNAP) override; a per-game MEMORY_SNAP_HELP option may still point at a provider-backed handler).

FOLLOW-UP (2026-06-21): DB rows repointed — the missing step of 22339. added and deleted the bespoke class but never repointed the process_action_handlers rows, which still named the now-deleted bean . A real player saying help in Flip Match hit DatabaseDrivenActionResolver.getBean("memorySnapHelpHandler") -> (prod error 2026-06-21 15:42). Likewise (MEMORY_SNAP_LEADERBOARD) named (the real class is , bean ). Fix (live DB config, no code change, applied to prod + local): (HELP)/(MEMORY_SNAP_HELP) -> (the shared aggregator, which now picks up ); (MEMORY_SNAP_LEADERBOARD) -> . Guarded by scenario_tests 2155/2028 (help, un-quarantined) + 2340 (leaderboard). process_action_handlers is pure live DB config (no code seeder), so the data fix is persistent and the was cleared on both targets to take effect immediately.

FM-LEH-3 (MEDIUM) — Returning-player resume does not re-render the board ("I didn't understand")

First-time onboarding is GOOD: fresh launch shows the shared beta-disclaimer welcome + start screen (start game / start easy / start hard / leaderboard / help), not a dead-end, via the shared . But on re-entry mid-game, (.java:126-144) leads with "I didn't understand that command." and adds in-game options but does NOT re-render the board or greet the returning player. The resume infrastructure exists (populateGameDetails loads the active session at .java:97-98; status re-renders the full board) — it just isn't used on re-entry. Recoverable (options present), so not a dead-end, but not the clean adaptive "welcome back, here's your board" resume the launch standard wants. Fix: have the active-session re-entry path render the board (reuse the status board view) with a "welcome back" lead instead of "I didn't understand".

FM-LEH-4 (LOW) — Internal name "Memory Snap" leaks in spoken/text copy

Public name is "Flip Match" (launch banner + return "Flip Match"), but multiple handlers speak the internal name "Memory Snap": start ("Welcome to Memory Snap!"), help ("Memory Snap. Match pairs..."), status ("Memory Snap Status:"), quit ("begin a new Memory Snap game"). Voice-facing copy should use the published title consistently. Replace hardcoded "Memory Snap" in player-facing strings with the game title.


Resolution (2026-06-17)

All four findings resolved with Memory Snap-specific changes; the shared baseline / were left untouched.

FM-LEH-1 (Exit) — FIXED. The (story_action=EXIT, game_type=MEMORY_SNAP) row in game_process.process_action_handlers (id 342) is disabled (enabled=0; re-applied 2026-06-21 via after a prod->local resync re-exposed it still present and enabled, which had re-broken Memory Snap exit — scenario test 2154 caught it), so exit / stop / goodbye / bye / quit flow to the shared -> like every other game (game saved on every interaction, thanks-for-playing + review nudge, session ends). No GameStateClearer provider was added: Flip Match has no transient exit-time state to clear and the active board should be preserved (saved), not discarded — the old override's quitGame abandon was exactly the bug. The explicit in-game abandon path is unchanged: MEMORY_SNAP_QUIT (row 290) -> , reached by the displayed "leave game" option.

FM-LEH-1 design sub-decision — "quit" means skill-exit. quit resolves to EXIT at Tier 1 via the hardcoded global PRIORITY_COMMANDS set (in ), which is shared across every game — it is NOT a Memory Snap-scoped synonym row. Dropping it would change quit behaviour for all games and would mean editing shared code, both out of scope. So we keep quit = clean skill-exit (consistent with every other game), and keep in-game abandon on the explicit "leave game" option -> MEMORY_SNAP_QUIT. This is the deliberate choice; no global synonym row was touched.

FM-LEH-2 (Help) — FIXED. New implements (engines/handlers/memorysnap/help/.java, priority 10, supports = MemorySnap account loaded) contributes four Flip Match help categories. The (HELP, MEMORY_SNAP) override (row 341) is removed so global help flows to the shared menu; the MEMORY_SNAP_HELP option (row 293) is repointed to (provider-backed). The bespoke (+ its test) is deleted.

FM-LEH-3 (Resume) — FIXED. active-session branch now re-renders the board (reusing the status board view via getGameStatus) led by "Welcome back to Flip Match." instead of "I didn't understand that command." (new helper renderBoardWithWelcomeBack).

FM-LEH-4 (Branding) — FIXED. Player-facing spoken/text copy now uses the published title "Flip Match" across the start banner, status header, leaderboard header, achievements header, settings header, quit/flip/hint/race no-active-game prompts, the delegate resume lead, and the daily login-reward readout. Internal identifiers (getGameName() = "MemorySnap", package/DB names, log lines, Javadoc) are unchanged.

Tests. New game_test.scenario_tests rows (MEMORY_SNAP): id 2154 (exit -> baseline goodbye, not "no active game"), 2155 (help -> shared menu), 2156 (start banner says "Flip Match"). Existing locale-fallback test 2028 was updated to the shared help-menu prompt (intentional design change per FM-LEH-2). The MEMORY_SNAP scenario suite is green locally (13/13) and the three new rows pass against the patched backend.

Deploy note (config is code-coupled). The three process_action_handlers data changes (delete rows 341 + 342, repoint row 293 -> ) are applied to the LOCAL test DB for the gate. The PROD rows must land together with the code deploy, because the new bean must exist before the (HELP, MEMORY_SNAP) override is removed on prod. Exact prod SQL: DELETE FROM game_process.process_action_handlers WHERE id IN (341,342); UPDATE game_process.process_action_handlers SET handler_bean='helpGenerator', handler_method='help' WHERE id=293; then evict the ACTION_HANDLER_CACHE (or restart) so the running backend re-reads the rows.

Subscription Benefits

Active subscribers (SUBSCRIPTION) unlock four benefits in Flip Match. Each benefit has an explicit upsell touchpoint for free users so they always know what they are missing.

Benefit What Subscribers Get Free-User Upsell Touchpoint
Unlimited hints Hint limit bypassed per game; hint button stays available even when free allotment is spent; response shows "Unlimited hints active" When free user runs out of hints, adds "Subscribers get unlimited hints... Say subscribe to learn more" alongside the buy pitch
Premium themes (pending content) Mythical Creatures, Space, Ocean, and Dinosaurs are defined in the theme enum but have no seeded card content, so none are selectable yet. The settings handler gates any premium theme as "coming soon" for everyone — subscriber or not — until its memory_snap.card_theme rows exist. When a player picks an unseeded premium theme, responds "coming soon" and steers them to the four shipped themes. No theme-pack product is sold.
Exclusive card backs 4 subscriber-only card-back designs (Gold Filigree, Obsidian, Crystal Lattice, Royal Sigil) plus free Classic; persisted to memory_snap.account.preferred_card_back When free user says "card backs" from settings, designs list marks premium entries as "subscribers only" and closes with subscription upsell
Priority leaderboard placement Current player's row shows ", Subscriber" marker when they appear on a leaderboard they rank on, plus confirmation line in footer Non-subscribers see "Subscribers earn a priority placement indicator... Say subscribe to learn more" in leaderboard footer

Premium themes are not yet live. The "all premium themes unlocked" benefit is pending card content — the four premium theme enums (Mythical Creatures, Space, Ocean, Dinosaurs) have no rows in memory_snap.card_theme or card_item. Until seeded, the settings handler gates them as "coming soon" for all players and the board generator falls back to a shipped theme when a stale premium preference is loaded. The other three subscriber benefits (unlimited hints, exclusive card backs, priority leaderboard) are live.

Implementation references:

  • Subscription check:
  • Handlers: , ,
  • Service overload: .useHint(session, unlimitedHints) with matching MemorySnapGameSession.useHint(boolean)
  • Card back enum: llitd.model.memorysnap.enums.MemorySnapCardBack (CLASSIC free, GOLD/OBSIDIAN/CRYSTAL/ROYAL premium)

Subsystem Design Docs

Subsystem Design Doc Covers
Board board.md Grid config, card model, theme system, generation algorithm, flip FSM
Scoring scoring.md Score formula, par time, streaks, achievements, leaderboard
Multiplayer multiplayer.md Duel, Speed Race, Team Snap modes; party/challenge integration; multiplayer scoring

Books by Lincoln Cole

Read the worlds behind the games

All books at llitd.com ↗