import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Slider } from '@/components/ui/slider'; import { Play, RotateCcw, Info, ChevronDown, ChevronUp, ArrowLeft, ArrowRight, Skull, AlertTriangle, BookOpen, Zap, Eye, Grid3X3, TrendingUp, Pause, Volume2, VolumeX, Target, Trophy } from 'lucide-react'; import SocialShare from '@/components/SocialShare'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; interface ErdosDiscrepancyPuzzleProps { showSocialShare?: boolean; } type GamePhase = 'intro' | 'writing' | 'captor-reveal' | 'walking' | 'fallen' | 'survived'; type Direction = 1 | -1 | null; interface DiscrepancyResult { d: number; k: number; sum: number; positions: number[]; } const ErdosDiscrepancyPuzzle: React.FC = ({ showSocialShare = false }) => { // Core game state const [sequence, setSequence] = useState([]); const [discrepancyBound, setDiscrepancyBound] = useState(1); const [gamePhase, setGamePhase] = useState('intro'); const [showRules, setShowRules] = useState(false); // Walk visualization state const [selectedD, setSelectedD] = useState(1); const [walkPosition, setWalkPosition] = useState(0); const [walkStep, setWalkStep] = useState(0); const [isWalking, setIsWalking] = useState(false); const [walkSpeed, setWalkSpeed] = useState(500); // Prisoner's Walk story mode const [captorChosenD, setCaptorChosenD] = useState(null); const [prisonerPosition, setPrisonerPosition] = useState(0); const [prisonerStep, setPrisonerStep] = useState(0); // Heatmap state const [hoveredCell, setHoveredCell] = useState<{d: number, k: number} | null>(null); const [highlightedSubsequence, setHighlightedSubsequence] = useState([]); // Best score tracking const [bestLength, setBestLength] = useState(null); // Load best score from localStorage useEffect(() => { const saved = localStorage.getItem(`erdos-best-length-c${discrepancyBound}`); if (saved) { setBestLength(parseInt(saved)); } }, [discrepancyBound]); // Calculate all discrepancies for current sequence const allDiscrepancies = useMemo((): DiscrepancyResult[] => { if (sequence.length === 0) return []; const results: DiscrepancyResult[] = []; const n = sequence.length; for (let d = 1; d <= n; d++) { let sum = 0; const positions: number[] = []; for (let k = 1; k * d <= n; k++) { const pos = k * d - 1; // 0-indexed if (sequence[pos] !== null) { sum += sequence[pos]!; positions.push(k * d); results.push({ d, k, sum, positions: [...positions] }); } } } return results; }, [sequence]); // Find maximum discrepancy const maxDiscrepancy = useMemo(() => { if (allDiscrepancies.length === 0) return { value: 0, result: null }; let maxAbs = 0; let maxResult: DiscrepancyResult | null = null; for (const result of allDiscrepancies) { if (Math.abs(result.sum) > maxAbs) { maxAbs = Math.abs(result.sum); maxResult = result; } } return { value: maxAbs, result: maxResult }; }, [allDiscrepancies]); // Check if current sequence violates bound const hasViolation = maxDiscrepancy.value > discrepancyBound; // Find worst step size for captor const findWorstD = useCallback((): number => { let worstD = 1; let worstDiscrepancy = 0; for (const result of allDiscrepancies) { if (Math.abs(result.sum) > worstDiscrepancy) { worstDiscrepancy = Math.abs(result.sum); worstD = result.d; } } return worstD; }, [allDiscrepancies]); // Get discrepancy for specific d,k const getDiscrepancy = (d: number, k: number): number | null => { const result = allDiscrepancies.find(r => r.d === d && r.k === k); return result ? result.sum : null; }; // Toggle sequence value const togglePosition = (index: number) => { if (gamePhase !== 'writing') return; setSequence(prev => { const newSeq = [...prev]; if (newSeq[index] === null) { newSeq[index] = 1; } else if (newSeq[index] === 1) { newSeq[index] = -1; } else { newSeq[index] = 1; } return newSeq; }); }; // Add new position to sequence const addPosition = () => { if (gamePhase !== 'writing') return; setSequence(prev => [...prev, null]); }; // Start the game const startGame = () => { setSequence([null, null, null, null, null]); setGamePhase('writing'); setWalkPosition(0); setWalkStep(0); setPrisonerPosition(0); setPrisonerStep(0); setCaptorChosenD(null); setHighlightedSubsequence([]); }; // Reset the game const resetGame = () => { setSequence([]); setGamePhase('intro'); setWalkPosition(0); setWalkStep(0); setIsWalking(false); setPrisonerPosition(0); setPrisonerStep(0); setCaptorChosenD(null); setHighlightedSubsequence([]); }; // Handle captor reveal (Prisoner's Walk mode) const revealCaptor = () => { const worstD = findWorstD(); setCaptorChosenD(worstD); setGamePhase('captor-reveal'); setPrisonerPosition(0); setPrisonerStep(0); }; // Start prisoner walking const startPrisonerWalk = () => { if (captorChosenD === null) return; setGamePhase('walking'); setPrisonerPosition(0); setPrisonerStep(0); }; // Prisoner walk animation useEffect(() => { if (gamePhase !== 'walking' || captorChosenD === null) return; const interval = setInterval(() => { setPrisonerStep(prev => { const nextStep = prev + 1; const posIndex = nextStep * captorChosenD - 1; if (posIndex >= sequence.length || sequence[posIndex] === null) { // Sequence ended without falling setGamePhase('survived'); clearInterval(interval); // Save best score const validLength = sequence.filter(s => s !== null).length; if (!bestLength || validLength > bestLength) { setBestLength(validLength); localStorage.setItem(`erdos-best-length-c${discrepancyBound}`, validLength.toString()); } return prev; } const newPosition = prisonerPosition + sequence[posIndex]!; setPrisonerPosition(newPosition); if (Math.abs(newPosition) > discrepancyBound) { setGamePhase('fallen'); clearInterval(interval); return nextStep; } return nextStep; }); }, walkSpeed); return () => clearInterval(interval); }, [gamePhase, captorChosenD, sequence, prisonerPosition, discrepancyBound, walkSpeed, bestLength]); // Manual walk control const walkSubsequence = () => { if (isWalking) { setIsWalking(false); return; } setWalkPosition(0); setWalkStep(0); setIsWalking(true); }; // Walk animation effect useEffect(() => { if (!isWalking) return; const interval = setInterval(() => { setWalkStep(prev => { const nextStep = prev + 1; const posIndex = nextStep * selectedD - 1; if (posIndex >= sequence.length || sequence[posIndex] === null) { setIsWalking(false); return prev; } setWalkPosition(current => current + sequence[posIndex]!); return nextStep; }); }, walkSpeed); return () => clearInterval(interval); }, [isWalking, selectedD, sequence, walkSpeed]); // Get color for heatmap cell const getHeatmapColor = (discrepancy: number | null): string => { if (discrepancy === null) return 'bg-muted/30'; const absVal = Math.abs(discrepancy); if (absVal > discrepancyBound) { return 'bg-destructive/80 text-destructive-foreground'; } if (absVal === discrepancyBound) { return 'bg-amber-500/70 text-white'; } if (absVal === 0) { return 'bg-emerald-500/50 text-emerald-900 dark:text-emerald-100'; } const intensity = absVal / discrepancyBound; if (intensity < 0.5) { return 'bg-emerald-400/40 text-emerald-900 dark:text-emerald-100'; } return 'bg-amber-400/50 text-amber-900 dark:text-amber-100'; }; // Max valid k for given d const maxK = (d: number) => Math.floor(sequence.length / d); // Highlighted positions for selected d const selectedPositions = useMemo(() => { const positions: number[] = []; for (let k = 1; k * selectedD <= sequence.length; k++) { positions.push(k * selectedD); } return positions; }, [selectedD, sequence.length]); // Get fill status of sequence const filledCount = sequence.filter(s => s !== null).length; const allFilled = filledCount === sequence.length && sequence.length > 0; // Known optimal sequences const optimalSequences: Record = { 1: [1, 1, -1, 1, -1, -1, -1, 1, 1, 1, -1], // One of the optimal 11-length sequences for C=1 }; const loadOptimalSequence = () => { const optimal = optimalSequences[discrepancyBound]; if (optimal) { setSequence([...optimal]); setGamePhase('writing'); } }; return (
{/* Header */}

The Erdős Discrepancy Problem

Can you write instructions to survive the tunnel forever?

{/* Rules Panel */} {showRules && (

The Prisoner's Dilemma

You're trapped in a tunnel with a deadly cliff {discrepancyBound} step{discrepancyBound > 1 ? 's' : ''} to your LEFT and a pit of vipers {discrepancyBound} step{discrepancyBound > 1 ? 's' : ''} to your RIGHT.

Write a list of instructions: each is either LEFT or RIGHT.

The evil captor then picks a step size d and you follow every d-th instruction.

The Impossible Task

C = 1: Maximum possible sequence length is 11

C = 2: Maximum possible sequence length is 1,160

Terence Tao proved in 2015: No infinite sequence can survive!

)} {/* Game Phase: Intro */} {gamePhase === 'intro' && (

The Prisoner's Walk

You wake up in a dark tunnel. There's a cliff to your left and vipers to your right. Your captor demands you write a sequence of LEFT/RIGHT instructions... but they get to choose which instructions you follow.

1 setDiscrepancyBound(v[0])} min={1} max={3} step={1} className="flex-1" /> 3

C=1: Max 11 steps | C=2: Max 1,160 steps | C=3: Unknown (very large)

{discrepancyBound === 1 && ( )}
{bestLength && (

Your best: {bestLength} steps with C={discrepancyBound}

)}
)} {/* Main Game UI */} {gamePhase !== 'intro' && (
{/* Status Bar */}
Length: {sequence.length} Filled: {filledCount}/{sequence.length} Max Discrepancy: {maxDiscrepancy.value} {maxDiscrepancy.result && ` (d=${maxDiscrepancy.result.d}, k=${maxDiscrepancy.result.k})`} Bound: C = {discrepancyBound} {bestLength && ( Best: {bestLength} )}
{/* Violation Alert */} {hasViolation && gamePhase === 'writing' && ( Bound Exceeded! The subsequence with d={maxDiscrepancy.result?.d}, k={maxDiscrepancy.result?.k} has discrepancy {maxDiscrepancy.value}, which exceeds your bound of {discrepancyBound}. )} Sequence Walk Heatmap {/* Sequence Builder Tab */} Write Your Instructions {gamePhase === 'writing' && ( Click to toggle )} {/* Sequence Grid */}
{sequence.map((val, idx) => { const pos = idx + 1; const isHighlighted = highlightedSubsequence.includes(pos) || selectedPositions.includes(pos); const isViolationPart = maxDiscrepancy.result?.positions.includes(pos) && hasViolation; return ( ); })} {gamePhase === 'writing' && ( )}
{/* Controls */}
{gamePhase === 'writing' && ( <> )}
{/* Walk Visualization Tab */} Walk Visualization {/* Step size selector */}
{Array.from({ length: Math.min(sequence.length, 8) }, (_, i) => i + 1).map(d => ( ))}
setWalkSpeed(1000 - v[0])} min={0} max={900} step={100} className="w-24" />
{/* Number line visualization */}
{/* Danger zones */}
CLIFF
VIPERS
{/* Safe zone markers */}
{Array.from({ length: discrepancyBound * 2 + 1 }, (_, i) => i - discrepancyBound).map(pos => (
{pos}
))}
{/* Walker position */}
👤
{/* Walk info */}
Position: {walkPosition} Steps taken: {walkStep} Following: positions {selectedPositions.slice(0, 5).join(', ')}{selectedPositions.length > 5 ? '...' : ''}
{/* Walk controls */}
{/* Heatmap Tab */} {/* This is actually handled in the walk tab above */} Discrepancy Heatmap

Each cell shows the sum of values at positions d, 2d, 3d, ..., kd. Red cells exceed the bound.

{Array.from({ length: Math.min(12, sequence.length) }, (_, i) => i + 1).map(k => ( ))} {Array.from({ length: Math.min(8, sequence.length) }, (_, i) => i + 1).map(d => ( {Array.from({ length: Math.min(12, sequence.length) }, (_, i) => i + 1).map(k => { const discrepancy = getDiscrepancy(d, k); const isValid = d * k <= sequence.length; return ( ); })} ))}
d \ k{k}
{d} { if (isValid) { setHoveredCell({ d, k }); const result = allDiscrepancies.find(r => r.d === d && r.k === k); if (result) { setHighlightedSubsequence(result.positions); } } }} onMouseLeave={() => { setHoveredCell(null); setHighlightedSubsequence([]); }} > {isValid && discrepancy !== null ? discrepancy : ''}
{hoveredCell && (
d={hoveredCell.d}, k={hoveredCell.k}: Looking at positions{' '} {Array.from({ length: hoveredCell.k }, (_, i) => (i + 1) * hoveredCell.d).join(', ')}
)}
{/* Captor Reveal Phase */} {gamePhase === 'captor-reveal' && captorChosenD !== null && (

The Captor Speaks...

"Interesting sequence... Let me see... I choose d = {captorChosenD}!"

You will follow every {captorChosenD === 1 ? '' : captorChosenD === 2 ? '2nd' : captorChosenD === 3 ? '3rd' : `${captorChosenD}th`} instruction: positions {Array.from({ length: Math.floor(sequence.length / captorChosenD) }, (_, i) => (i + 1) * captorChosenD).slice(0, 8).join(', ')}...

)} {/* Walking Phase */} {gamePhase === 'walking' && captorChosenD !== null && (

Walking with d = {captorChosenD}...

Step {prisonerStep} | Position: {prisonerPosition}

{/* Tunnel visualization */}
🏔️
🐍
🚶
)} {/* Fallen Phase */} {gamePhase === 'fallen' && ( You Fell! After {prisonerStep} steps with d={captorChosenD}, you ended up at position {prisonerPosition}. The captor found your weakness! Your sequence lasted {sequence.length} instructions.
)} {/* Survived Phase (temporary - sequence ended) */} {gamePhase === 'survived' && ( Sequence Complete! Your {sequence.length}-instruction sequence survived d={captorChosenD}! But remember: no sequence can survive forever. Try making it longer!
)}
)} {/* Acknowledgement */}

Credits & History

This puzzle is based on the Erdős Discrepancy Problem, posed by Paul Erdős in the 1930s (with a $500 prize!). The "Prisoner's Walk" framing comes from various mathematical expositions, popularized by James Grime's singingbanana video.

2014: Boris Konev & Alexei Lisitsa proved C=2 has maximum length 1,160 using SAT solvers (generating a 13GB proof!). 2015: Terence Tao proved that NO infinite sequence can have bounded discrepancy, settling the problem completely.

{/* Social Share */} {showSocialShare !== false && ( )}
); }; export default ErdosDiscrepancyPuzzle;