← Back to Home

FIFA World Cup Draft Pool

Fantasy draft order for 12 friends — determined by World Cup results, calibrated by Monte Carlo simulation.

View Live App ↗ Live
Workers KV TypeScript

The Problem

12 friends enter a pool. Each person is randomly assigned 4 World Cup teams — one from each tier. As the tournament plays out, your teams earn fantasy points. Most points = first pick in the NFL fantasy football draft. But without accounting for team strength, the person who gets Spain and France always wins. The scoring needed to be fair — every tier should matter.

What I Built

A full-stack web app that manages the entire pool lifecycle: draft randomization with group-conflict constraints, live match result entry, fantasy point calculation with tier-weighted multipliers, and a real-time leaderboard. Three independent pools share the same codebase with separate KV namespaces, and a central admin hub fans out match results to all three simultaneously.

The scoring system was calibrated using Monte Carlo simulation — 5,000 full tournament runs using real betting odds. The simulation revealed that without tier multipliers, Tier 1 teams dominated ~96% of every owner's score. The chosen multipliers (1x/2x/4x/8x) produce balanced contribution across tiers — each tier accounts for roughly 25-30% of the winning owner's score.

FIFA Draft Pool leaderboard showing ranked owners with team breakdowns and fantasy points Match results page with fantasy point impact chips per owner Central admin hub fanning out match results to 3 pools simultaneously

Architecture

┌──────────────────────────────────────────────────────────┐
│                    Cloudflare Edge                        │
│                                                          │
│  ┌─────────────────┐     ┌─────────────────────────────┐ │
│  │  Workers (V8)   │────▶│     KV Namespace             │ │
│  │                 │     │  ┌─────────────────────────┐ │ │
│  │  - Router       │     │  │ owners: [...12 owners]  │ │ │
│  │  - Draft logic  │     │  │ results: [...matches]   │ │ │
│  │  - Scoring calc │     │  │ owner_names: [...names] │ │ │
│  │  - SSR HTML     │     │  └─────────────────────────┘ │ │
│  └─────────────────┘     └─────────────────────────────┘ │
│                                                          │
│  Pool 1: fifa.macksportreport.com                        │
│  Pool 2: ox.macksportreport.com                          │
│  Pool 3: bbff.macksportreport.com                        │
│  (Separate Workers + separate KV namespaces)             │
│                                                          │
│  ┌─────────────────────────────────────────────────────┐ │
│  │  Admin Hub (admin.macksportreport.com)              │ │
│  │  Enter scores once → fans out to all 3 pool APIs   │ │
│  └─────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
Technical Deep Dive

Scoring System

Points = Round Weight × Tier Multiplier. Group wins earn 1 point (base), while the Final earns 5 points. Tier multipliers scale these: T1 teams get 1x, T2 gets 2x, T3 gets 4x, and T4 (Cinderellas) get 8x. A T4 team winning a group match earns 8 points — the same as a T1 team reaching the semifinals.

Draft Algorithm

The draft randomizer assigns 1 team per tier to each owner with a group conflict constraint: no owner can have two teams from the same FIFA group (they'd play each other, creating a conflict of interest). The algorithm shuffles teams per tier and assigns greedily with backtracking, retrying up to 200 times if a shuffle produces an unsolvable assignment.

Multi-Pool Support

Three pools run on identical source code deployed as separate Workers with separate KV namespaces. A fourth Worker (the Admin Hub) acts as a fan-out proxy — when a match result is submitted, it fires 3 parallel fetch() calls to all pool APIs simultaneously. Each pool's response is shown individually so the admin knows if any pool had an issue.

Monte Carlo Calibration

The simulation/ directory contains Python scripts that ran 5,000 full tournament simulations using real betting odds. The simulation tested various multiplier combinations and measured tier contribution variance. The 1x/2x/4x/8x scheme was chosen because it produces the most balanced expected contribution: each tier accounts for ~25-30% of the winning owner's total score.

Cloudflare Products Used

Workers — Application runtime. Full-stack SSR with routing, API handlers, draft logic, scoring calculations, and HTML rendering. Zero cold starts, global distribution. ~1,500 lines of TypeScript, no framework.

KV — Global key-value storage. Three keys per pool (owners, results, owner_names) serve the entire tournament lifecycle. Sub-10ms reads at the edge.

Custom Domains — Each pool has its own domain via Cloudflare DNS + Workers routes. The same codebase deploys to 3 domains with different KV bindings.

Wrangler CLI — Single-command deployment. wrangler deploy pushes to 300+ edge locations instantly.

What I Learned

Career Relevance