Skip to content

Repository files navigation

My Dobble (Spot It!) - Custom Photo Cards PWA

A TypeScript implementation of the Dobble card generation algorithm based on projective plane geometry, plus an installable Progressive Web App (built with Vite) that lets you build your own Dobble deck from your own photos and play it right in the browser — fully offline.

What is Dobble?

Dobble (also known as Spot It!) is a card game where:

  • Each card has several symbols on it
  • Any two cards have exactly one symbol in common
  • Players race to find the matching symbol between cards

The Algorithm

The algorithm uses mathematical properties of projective planes:

For a prime number p:

  • Total cards: p² + p + 1
  • Symbols per card: p + 1
  • Total unique symbols: p² + p + 1

Example with p=7

  • 57 cards (7² + 7 + 1)
  • 8 symbols per card (7 + 1)
  • 57 unique symbols total

Progressive Web App: Make Your Own Deck

This repo is a Vite + TypeScript project. The app lives in src/ (app.ts, storage.ts, layout.ts, dobble.ts) and index.html; vite-plugin-pwa generates the web manifest and service worker at build time.

Setup

npm install

Develop

npm run dev
# opens a dev server (usually http://localhost:5173) with instant hot-reload

Build & preview a production bundle

npm run build     # type-checks with tsc, then bundles to dist/
npm run preview   # serves dist/ locally, including the real service worker/manifest

Deploying is just publishing the contents of dist/ (e.g. GitHub Pages, Netlify, any static host) - see the project's CI/CD docs for wiring a build step into your deploy of choice.

How it works

  1. Choose a deck size - pick a prime p; the app shows how many cards and how many photos you'll need (p² + p + 1 photos, p + 1 per card).
  2. Add your photos - tap to choose files, or drag-and-drop / paste images. Photos are downscaled and stored locally in IndexedDB on your device - nothing is uploaded anywhere.
  3. Generate My Dobble Deck - the app randomly assigns your photos to symbol slots and runs the same projective-plane algorithm from src/dobble.ts to build a deck where any two cards share exactly one photo.
  4. View Deck - see every card rendered as a circular Dobble card with your photos packed into a randomized, non-overlapping layout (src/layout.ts) - shuffle the layout for a new look, or print physical cards.
  5. Play - a "tower" style single-player mode: spot the one photo shared between the center card and the top card of the pile, tap it, and race to clear the whole deck. Tracks correct taps, mistakes, and cards remaining.

Your photos and generated deck persist in IndexedDB, so closing the tab (or installing the app to your home screen and going offline) keeps everything intact until you hit Reset Everything.

Installing as an app

Visit the page in a supported browser (Chrome, Edge, Safari on iOS via "Add to Home Screen", etc.) and use the install prompt/banner, or your browser's "Install app" menu option. Once installed, the service worker caches the app shell so it keeps working with no network connection - only the initial photo upload needs the device to render/store your images, which also happens fully offline.

Usage

Basic Usage

import { generateDobbleCards, verifyDobbleCards } from './src/dobble.ts';

// Generate cards using prime number 7
const cards = generateDobbleCards(7);
console.log(`Generated ${cards.length} cards`);

// Verify that all cards follow the Dobble rule
const verification = verifyDobbleCards(cards);
console.log('Valid:', verification.success);

With Custom Symbols

import { generateDobbleCards, convertToSymbols } from './src/dobble.ts';

const cards = generateDobbleCards(3); // Smaller set: 13 cards

const symbols = ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻',
                 '🐼', '🐨', '🐯', '🦁', '🐮', '🐷'];

const emojiCards = convertToSymbols(cards, symbols);
console.log(emojiCards);

Run the CLI Demo

npm run demo

Runs src/cli.ts (via tsx, no compile step needed) to print a sample deck and verification results to the console, same as the original node dobble.js demo.

Type-check only

npm test

Example Output

API

generateDobbleCards(p)

Generates a complete Dobble card set.

  • Parameters: p (number) - A prime number
  • Returns: Array of cards, where each card is an array of symbol IDs
  • Throws: Error if p is not prime

verifyDobbleCards(cards)

Verifies that all cards follow the Dobble rule.

  • Parameters: cards (Array) - Array of cards to verify
  • Returns: Object with verification results

convertToSymbols(cards, symbols)

Converts numeric symbol IDs to custom labels.

  • Parameters:
    • cards (Array) - Cards with numeric IDs
    • symbols (Array) - Array of symbol labels
  • Returns: Cards with custom labels

isPrime(n)

Checks if a number is prime.

  • Parameters: n (number) - Number to check
  • Returns: Boolean

Common Prime Numbers for Dobble

Prime (p) Cards Symbols per Card Total Symbols
2 7 3 7
3 13 4 13
5 31 6 31
7 57 8 57
11 133 12 133
13 183 14 183

The original Dobble game uses p=7, resulting in 55 cards with 8 symbols each (with 2 cards removed for manufacturing reasons).

How It Works

The algorithm uses finite projective plane geometry, specifically an affine plane extended with a "line at infinity":

Symbol Structure

  • Direction symbols (0 to p): Represent "points at infinity" where parallel lines meet
    • Symbol 0: Where all vertical lines meet
    • Symbols 1 to p: Where lines of each slope (0 to p-1) meet
  • Point symbols (p+1 to p²+p): Represent points in the affine plane, arranged as (p+1) + row×p + col

Card Construction

  1. Card 0 - Line at Infinity

    • Contains all direction symbols: [0, 1, 2, ..., p]
    • This card shares exactly one "direction" with every other card
  2. Cards 1 to p² - Affine Lines (y = mx + b)

    • For each slope m (0 to p-1) and y-intercept b (0 to p-1):
    • Contains: direction symbol (m+1) + p points on the line y ≡ mx + b (mod p)
    • Each line has a unique slope-intercept combination
  3. Cards p²+1 to p²+p - Vertical Lines (x = c)

    • For each column c (0 to p-1):
    • Contains: direction symbol 0 + p points where x = c
    • All vertical lines are parallel and meet at symbol 0

Why It Works

The construction ensures that:

  • Any two non-parallel affine lines intersect at exactly one affine point
  • Any two lines with the same slope (including verticals) share their direction symbol
  • The line at infinity intersects each affine/vertical line at its direction symbol
  • Every pair of cards shares exactly one symbol - either a direction or an affine point

License

MIT

About

A TypeScript implementation of the Dobble card generation algorithm based on projective plane geometry, plus an installable Progressive Web App (built with Vite) that lets you build your own Dobble deck from your own photos and play it right in the browser, fully offline.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages