7-IDEATIONS DEMO · FANTASY DRAFT ANALYTICS

The Algorithm Behind
Every Pick.

A 12-team snake draft simulator powered by VOR-based ranking, 9 AI strategy archetypes, a real-time pick engine, and live NFL projections from SportsData.io.

📊 220+ Players 🤖 9 AI Strategies 📈 VOR-Ranked ⚡ Live API Data 🏆 Post-Draft Grade

228 Picks. 9 Strategies. 1 Algorithm.

What looks like a game is actually a multi-agent optimization problem — every AI bot runs a different strategy, reacts to positional scarcity, and competes on a shared ranking surface built from projected statistics.

📊
Step 01

Player Pool Assembled

220+ players loaded with projected stats for 2026 — or live data from SportsData.io if available. Each player gets 5 raw stats mapped by position: pass yards, rush TDs, receptions, etc.

🔢
Step 02

VOR Rankings Computed

Value Over Replacement normalizes fantasy points across positions. A RB scoring 280 pts outranks a QB at 350 pts once scarcity is factored — the drop-off from elite to replacement RBs is far steeper.

🤖
Step 03

9 AI Archetypes Ready

Each bot runs a named strategy (Robust RB, Zero RB, Hero RB, TE Premium, Late QB, Stars & Scrubs, Balanced BPA, Contrarian, Anchor RB) with distinct positional weight multipliers.

Step 04

Snake Draft Executes

19 rounds, 12 teams, 228 picks. Pick order reverses each round — the snake format. A real-time engine fires picks with a configurable timer, live ticker, and feed showing every selection.

📉
Step 05

Positional Runs Emerge

When 3+ players at a position are drafted in a short window, the AI urgency multiplier kicks in for remaining players at that spot — simulating the cascading runs seen in real draft rooms.

🏆
Step 06

Draft Grade Calculated

Post-draft analytics rank all 12 teams on starter projected points, total VOR captured, bye week concentration, and bench depth. Grades A+ through D are assigned by rank position.

Try It: Two Ways to Explore

ELI5 explains the draft strategy concept in plain English. Engineer mode runs the full 19-round simulator with live data, real-time pick feed, and post-draft analytics.

Pick Your Draft Strategy

Each archetype bets differently on where value hides. Pick one and see which players your algorithm would target in the first 8 rounds.

🏈
The Casual Fan
Big names, run-first. Take elite RBs early and fill out from there.
Strategy: Robust RB (RB weight: 1.55×)
📊
The Analyst
Pure algorithm. Best VOR available regardless of position.
Strategy: Balanced BPA (all weights: 1.0×)
Zero RB
Punt RBs early, stack elite WRs, find RBs on waivers.
Strategy: Zero RB (RB weight: 0.45×, WR: 1.5×)
🎯
The Contrarian
Fade the crowd. Target positions others ignore, find undervalued QBs and TEs.
Strategy: Contrarian (QB: 1.15×, TE: 1.25×)
🏈

2026 Fantasy Draft Room

12-team snake draft simulator with 9 AI archetypes, VOR-based rankings, and real-time analytics.

220+
Players
6
Positions
19
Rounds
9
AI Strategies
228
Total Picks

Your Competition

Six Concepts That Drive Every Pick

From the math behind VOR to the game theory of positional scarcity — understand the engine before you run it.

📊
Concept 01 of 06

Why VOR Beats Raw Points

A QB projecting 350 pts and a RB projecting 280 pts are not directly comparable. VOR (Value Over Replacement) subtracts the replacement level for each position — the best player freely available on waivers. The position with the steeper drop-off to replacement yields more VOR, and therefore more actual draft value.

vor = projected_pts - replacement_level_pts # QB example (replacement = QB13, ~260 pts) vor_qb = 350 - 260 = 90 # RB example (replacement = RB25, ~120 pts) vor_rb = 280 - 120 = 160 # The RB ranks HIGHER despite fewer raw points. # Scarcity wins.

Key insight: RBs have far fewer viable starters than WRs, so the drop-off from elite to replacement RB is steeper. This is why elite RBs consistently go before elite QBs in real drafts.

🐍
Concept 02 of 06

Snake Draft: Position Is Everything

In a 12-team snake draft, pick order alternates each round. This creates an asymmetric information problem: you must predict which players will be available at your next pick slot, 23 picks away.

Pick SlotRound 1 PickRound 2 PickGapAdvantage
Pick 1#1#2423 picksBest player, long wait
Pick 6#6#1913 picksBalanced exposure
Pick 12#12#131 pickImmediate double-dip

Pick 12 gets back-to-back picks in rounds 1–2, effectively securing two round-1-equivalent players. This is why the late-first-round slot is often considered the most strategically flexible position.

🤖
Concept 03 of 06

9 AI Archetypes, 9 Draft Stories

Each bot applies position-specific VOR multipliers. A RB with VOR 80 scores 124 composite points for Robust RB (80 × 1.55) but only 36 for Zero RB (80 × 0.45). Same player, wildly different perceived value.

StrategyQBRBWRTE
Robust RB0.50×1.55×0.85×0.60×
Zero RB0.70×0.45×1.50×1.15×
Hero RB0.60×1.10×1.35×0.75×
TE Premium0.65×1.00×1.00×1.70×
Late QB0.25×1.25×1.25×0.90×
Balanced BPA1.00×1.00×1.00×1.00×
Contrarian1.15×0.85×0.85×1.25×
📉
Concept 04 of 06

Positional Scarcity: The Invisible Clock

Not all positions run dry at the same rate. In a 12-team, 2-RB, 2-WR league, there are 24 starting RB slots and 24 WR slots — but there are only 12–15 truly elite RBs while 28–32 viable WRs exist. The scarcity cliff hits RBs far harder.

PositionElite StartersReplacement Drop-offDraft Priority
RB12–15High (RB25 = 48% of RB1)🔴 Urgent early
WR28–32Medium (WR31 = 72% of WR1)🟡 Can wait
TE3–5 eliteExtreme (TE13 = 62% of TE1)🟠 Bi-modal
QB12 startersLow (QB13 = 85% of QB1)🟢 Wait late

TE is the most polarized: a Kelce or Bowers is worth a 2nd-round pick; the 13th TE is barely worth rostering. TE Premium bets on this gap; most others ignore it entirely.

💡
Concept 05 of 06

Finding Draft Value: ADP vs VOR

Average Draft Position (ADP) is the consensus market rank — where the crowd drafts a player on average. VOR rank is the algorithmic rank based purely on replacement-adjusted projected points. When these diverge, there is arbitrage.

# ADP vs VOR Arbitrage Detector for player in sorted_by_vor: adp_rank = player.adp vor_rank = player.vor_rank discount = adp_rank - vor_rank # positive = undervalued if discount >= 15: print(f"{player.name}: drafted at pick {adp_rank}, " f"VOR says pick {vor_rank} (+{discount} free picks)") # Example output: # Trey Benson: drafted pick 105, VOR rank 71 (+34) # Jerome Ford: drafted pick 91, VOR rank 68 (+23)

The ADP ±8% jitter in the AI engine deliberately creates these gaps, simulating the cognitive biases and preference divergence that generate real draft value windows.

🏆
Concept 06 of 06

How the A–F Grade Is Calculated

Post-draft grading uses four metrics to rank all 12 teams and assign A+ through D letter grades based on finishing position in the league standings simulation.

MetricWhat It MeasuresPenalty
Starter PtsProjected points from starting lineup (QB+2RB+2WR+TE+FLEX+K+DST)Primary rank signal
Total VORSum of VOR for all 19 drafted playersSecondary tiebreaker
Bye ConflictsWeeks where 3+ starters share a bye week−1 grade tier each
Bench DepthAverage bench player projected pointsInformational

Grades A+ → D are assigned by finishing rank (1st = A+, 2nd = A, 3rd = A, etc.) then adjusted down for each bye-week concentration penalty.

Four Engineering Decisions That Matter

Why the simulator behaves the way it does — each choice reflects a real fantasy football principle with a measurable effect on draft outcomes.

🔢
Ranking Methodology

VOR Over Raw Points

A QB scoring 350 pts and a RB scoring 250 pts aren't comparable. VOR subtracts the replacement-level baseline per position (QB13, RB25, WR25, TE13 in a 12-team league). After VOR adjustment, the RB often ranks higher because the RB1→RB25 drop-off is twice as steep as QB1→QB13. This is why elite RBs routinely go before elite QBs in real drafts.

🤖
AI Design

9 Archetypes, Not One Optimal Bot

Real drafts have diverse strategies. Zero RB exploits waiver-wire RB value; Robust RB locks in scarce positional value early; TE Premium bets on the extreme scarcity cliff at tight end. By modeling 9 real strategies, the simulator creates realistic positional runs, reaches, and value falls that a single-strategy AI would never produce. Draft variance requires behavioral diversity.

🎯
Behavioral Realism

±8% Jitter Creates Realistic Variance

Perfect AI play is boring and unrealistic. Real drafters have biases, sleeper picks, and team preferences. The val *= (0.92 + Math.random() * 0.16) jitter causes occasional reaches (3–5 picks early) and steals (player falls unexpectedly), creating the variance that makes each draft unique and strategically meaningful.

📡
Live Data Integration

SportsData.io Projections with Fallback

On load, the simulator fetches live 2026 season projections and ADP from SportsData.io via two API endpoints (player projections + DST). If the API fails or returns insufficient data (<60 players), it silently falls back to hardcoded 2026 projections — a graceful degradation pattern that keeps the demo functional regardless of external API availability.

Core Algorithms

The Python implementations behind VOR ranking, AI strategy weighting, snake order generation, and post-draft grading.

🧮 VOR Calculation Engine (Python)
# ── Value Over Replacement (VOR) Calculation ──────────────────────
# VOR normalizes fantasy points across positions by subtracting
# the "replacement level" — the best player available on waivers.

def compute_vor(players: list[dict], league_size: int = 12) -> list[dict]:
    """
    Compute VOR for each player.
    Replacement level = the (N+1)th player at each position,
    where N = starting slots × league_size.
    """
    # Replacement thresholds: how many starters exist league-wide
    # QB1 × 12 = 12 starters → replacement = QB13
    # RB1-RB2 × 12 = 24 starters → replacement = RB25
    starters_per_team = {"QB": 1, "RB": 2, "WR": 2, "TE": 1, "K": 1, "DST": 1}
    replacement_rank = {
        pos: (slots * league_size) + 1
        for pos, slots in starters_per_team.items()
    }
    # e.g. {"QB": 13, "RB": 25, "WR": 25, "TE": 13, "K": 13, "DST": 13}

    from collections import defaultdict
    by_pos = defaultdict(list)
    for p in players:
        by_pos[p["pos"]].append(p)
    for pos in by_pos:
        by_pos[pos].sort(key=lambda x: x["projected_pts"], reverse=True)

    replacement_pts = {}
    for pos, rank in replacement_rank.items():
        idx = min(rank - 1, len(by_pos[pos]) - 1)
        replacement_pts[pos] = by_pos[pos][idx]["projected_pts"] if idx >= 0 else 0

    for p in players:
        p["vor"] = round(p["projected_pts"] - replacement_pts[p["pos"]], 1)

    players.sort(key=lambda x: x["vor"], reverse=True)
    return players

# Example: QB with 350 pts, replacement QB13 = 260 pts → VOR = 90
#          RB with 280 pts, replacement RB25 = 120 pts → VOR = 160
#          The RB ranks HIGHER despite fewer raw points — scarcity wins.
🤖 AI Draft Strategy Engine (Python)
# ── AI Draft Strategy: Weighted VOR with Archetype Biases ─────────

STRATEGIES = {
    "Robust RB":    {"QB": 0.50, "RB": 1.55, "WR": 0.85, "TE": 0.60, "K": 0.30, "DST": 0.30},
    "Zero RB":      {"QB": 0.70, "RB": 0.45, "WR": 1.50, "TE": 1.15, "K": 0.30, "DST": 0.30},
    "Hero RB":      {"QB": 0.60, "RB": 1.10, "WR": 1.35, "TE": 0.75, "K": 0.30, "DST": 0.30},
    "TE Premium":   {"QB": 0.65, "RB": 1.00, "WR": 1.00, "TE": 1.70, "K": 0.30, "DST": 0.30},
    "Late QB":      {"QB": 0.25, "RB": 1.25, "WR": 1.25, "TE": 0.90, "K": 0.30, "DST": 0.30},
    "Balanced BPA": {"QB": 1.00, "RB": 1.00, "WR": 1.00, "TE": 1.00, "K": 0.50, "DST": 0.50},
}

def ai_pick(available: list, team_roster: list, strategy: dict) -> dict:
    """Select best player using strategy-weighted VOR."""
    weights = strategy
    pos_counts = {p["pos"]: pos_counts.get(p["pos"], 0) + 1 for p in team_roster}
    pos_needs = {"QB": 3, "RB": 5, "WR": 5, "TE": 2, "K": 2, "DST": 2}
    best, best_score = None, float("-inf")

    for player in available[:60]:  # scan top 60 by VOR
        score = player["vor"] * weights.get(player["pos"], 1.0)

        # Need bonus: +15% if roster slot open
        if pos_counts.get(player["pos"], 0) < pos_needs[player["pos"]]:
            score *= 1.15
        else:
            score *= 0.45  # heavy penalty if position full

        # Scarcity check: positional urgency
        remaining = sum(1 for p in available[:40] if p["pos"] == player["pos"])
        if remaining <= 3:
            score *= 1.35
        elif remaining <= 6:
            score *= 1.12

        # Behavioral jitter: ±8% randomization
        import random
        score *= (0.92 + random.random() * 0.16)

        if score > best_score:
            best, best_score = player, score

    return best
🐍 Snake Draft Order Generator (Python)
# ── Snake Draft Order ────────────────────────────────────────────
# In a snake draft, odd rounds go 1→12, even rounds go 12→1.
# This creates the "snake" pattern that ensures fairness.

def build_draft_queue(num_teams: int = 12, num_rounds: int = 19) -> list[dict]:
    """
    Returns a flat list of {round, pick_in_round, team_idx}
    in snake order across all rounds.
    """
    queue = []
    for r in range(num_rounds):
        order = list(range(num_teams))
        if r % 2 == 1:          # even rounds (0-indexed) reverse
            order = order[::-1]
        for pick_pos, team_idx in enumerate(order):
            queue.append({
                "round":         r,
                "pick_in_round": pick_pos,
                "team_idx":      team_idx,
                "overall_pick":  r * num_teams + pick_pos + 1,
            })
    return queue

# Example output (12-team, first 2 rounds):
# Round 1: teams 0,1,2,...,11 (picks 1–12)
# Round 2: teams 11,10,9,...,0 (picks 13–24)
# Team 0 gets picks 1 and 24 (gap = 23)
# Team 11 gets picks 12 and 13 (back-to-back)
📊 Post-Draft Grade Analysis (Python)
# ── Post-Draft Grading Algorithm ──────────────────────────────────
# Grades each team on four dimensions then assigns A+ through D.

def grade_team(roster: list, all_teams: list, league_size: int = 12) -> dict:
    by_pos = {}
    for p in roster:
        by_pos.setdefault(p["pos"], []).append(p)
    for pos in by_pos:
        by_pos[pos].sort(key=lambda x: x["projected_pts"], reverse=True)

    # 1. Starter Projected Points
    starter_pts = 0
    if by_pos.get("QB"):  starter_pts += by_pos["QB"][0]["projected_pts"]
    for i in range(min(2, len(by_pos.get("RB", [])))):
        starter_pts += by_pos["RB"][i]["projected_pts"]
    for i in range(min(2, len(by_pos.get("WR", [])))):
        starter_pts += by_pos["WR"][i]["projected_pts"]
    if by_pos.get("TE"):  starter_pts += by_pos["TE"][0]["projected_pts"]
    # FLEX: best remaining RB/WR/TE
    flex = sorted(
        [p for pos in ["RB","WR","TE"] for p in by_pos.get(pos,[])[2:]],
        key=lambda x: x["projected_pts"], reverse=True
    )
    if flex: starter_pts += flex[0]["projected_pts"]
    if by_pos.get("K"):   starter_pts += by_pos["K"][0]["projected_pts"]
    if by_pos.get("DST"): starter_pts += by_pos["DST"][0]["projected_pts"]

    # 2. Bye Week Concentration Penalty
    bye_counts = {}
    for p in roster:
        bye_counts[p["bye"]] = bye_counts.get(p["bye"], 0) + 1
    bye_penalty = sum(1 for count in bye_counts.values() if count >= 3)

    # 3. Rank among all teams, assign grade
    all_starter_pts = sorted(
        [compute_starter_pts(t) for t in all_teams], reverse=True
    )
    rank = all_starter_pts.index(starter_pts) + 1
    grade_map = ["A+","A","A","A-","B+","B","B","B-","C+","C","C-","D"]
    grade_idx = min(rank - 1 + bye_penalty, len(grade_map) - 1)

    return {
        "grade":        grade_map[grade_idx],
        "rank":         rank,
        "starter_pts":  round(starter_pts),
        "bye_conflicts": bye_penalty,
        "total_vor":    round(sum(p["vor"] for p in roster)),
    }

🏈 Built for the Portfolio

This demo is part of the 7-Ideations Framework — a portfolio approach that walks from narrative to live demo to classroom to production code. Every pick in the simulator reflects a real algorithmic decision: VOR ranking, strategy-weighted AI, scarcity detection, and post-draft analytics.

The most modern resume for the most modern city on earth.