/** * Algorithm Pixel Game - Visual Learning Tool * Interactive visual guide to understand and memorize algorithms */ (function() { 'use strict'; function defineComponent() { if (typeof React === 'undefined' || typeof React.Component === 'undefined') { console.log('React not available yet for AlgorithmPixelGame, retrying in 100ms...'); setTimeout(defineComponent, 100); return; } console.log('React available, defining AlgorithmPixelGame component...'); const { useState, useEffect, useRef, useCallback } = React; // Pixel art style constants const COLORS = { primary: '#4A90E2', secondary: '#7B68EE', success: '#50C878', danger: '#FF6B6B', warning: '#FFD93D', background: '#2C3E50', grid: '#34495E', text: '#ECF0F1', highlight: '#FFD700' }; // Algorithm problems with visual step-by-step guides const PROBLEMS = [ { id: 'valid-parentheses', title: 'Valid Parentheses', difficulty: 'Medium', dataStructure: 'Stack', why: 'Stack is perfect because we need to match the MOST RECENT opening bracket (Last In First Out - LIFO)', concept: 'Think of it like stacking plates - you remove the top plate first. Opening brackets go on the stack, closing brackets remove the most recent opening bracket.', example: { input: '()[]{}', solution: true }, steps: [ { step: 1, action: 'Start with an empty stack', explanation: 'We use a stack to keep track of opening brackets', visual: { stack: [], currentChar: '(', index: 0, highlight: [0] } }, { step: 2, action: 'See opening bracket "(" - push to stack', explanation: 'Opening brackets go on the stack (like stacking plates)', visual: { stack: ['('], currentChar: ')', index: 1, highlight: [1] } }, { step: 3, action: 'See closing bracket ")" - check if it matches top of stack', explanation: 'Closing bracket ")" matches opening "(" - pop from stack', visual: { stack: [], currentChar: '[', index: 2, highlight: [2] } }, { step: 4, action: 'See opening bracket "[" - push to stack', explanation: 'Another opening bracket goes on the stack', visual: { stack: ['['], currentChar: ']', index: 3, highlight: [3] } }, { step: 5, action: 'See closing bracket "]" - matches top "[", pop from stack', explanation: 'The brackets match, so we remove it from the stack', visual: { stack: [], currentChar: '{', index: 4, highlight: [4] } }, { step: 6, action: 'See opening bracket "{" - push to stack', explanation: 'Final opening bracket goes on the stack', visual: { stack: ['{'], currentChar: '}', index: 5, highlight: [5] } }, { step: 7, action: 'See closing bracket "}" - matches top "{", pop from stack', explanation: 'Final match! Stack is now empty', visual: { stack: [], currentChar: null, index: 6, highlight: [] } }, { step: 8, action: 'Stack is empty - all brackets matched!', explanation: 'If stack is empty at the end, the string is valid', visual: { stack: [], currentChar: null, index: 6, highlight: [] } } ] }, { id: 'three-sum', title: '3Sum', difficulty: 'Medium', dataStructure: 'Array & Two Pointers', why: 'After sorting, we can use two pointers to efficiently find pairs that sum to a target. Fix one number, then search for the other two.', concept: 'Like finding two people whose heights add up to a target - sort everyone by height, then use two pointers to search efficiently.', example: { nums: [-1, 0, 1, 2, -1, -4], solution: [[-1, -1, 2], [-1, 0, 1]] }, steps: [ { step: 1, action: 'Sort the array: [-4, -1, -1, 0, 1, 2]', explanation: 'Sorting allows us to use two pointers efficiently', visual: { array: [-4, -1, -1, 0, 1, 2], i: 0, left: 1, right: 5, highlight: [0] } }, { step: 2, action: 'Fix i=0 (value -4), use two pointers left=1, right=5', explanation: 'We fix one number and search for two others that sum to 0', visual: { array: [-4, -1, -1, 0, 1, 2], i: 0, left: 1, right: 5, highlight: [0, 1, 5] } }, { step: 3, action: 'Sum = -4 + (-1) + 2 = -3. Too small, move left pointer right', explanation: 'When sum < 0, we need larger numbers, so move left pointer right', visual: { array: [-4, -1, -1, 0, 1, 2], i: 0, left: 2, right: 5, highlight: [0, 2, 5] } }, { step: 4, action: 'Continue searching... No valid triplets starting with -4', explanation: 'We skip to next i when pointers cross', visual: { array: [-4, -1, -1, 0, 1, 2], i: 1, left: 2, right: 5, highlight: [1, 2, 5] } }, { step: 5, action: 'Fix i=1 (value -1), left=2, right=5. Sum = -1 + (-1) + 2 = 0 ✓', explanation: 'Found a valid triplet! Add to result and move both pointers', visual: { array: [-4, -1, -1, 0, 1, 2], i: 1, left: 3, right: 4, highlight: [1, 2, 5], found: [[-1, -1, 2]] } }, { step: 6, action: 'Continue with i=1, left=3, right=4. Sum = -1 + 0 + 1 = 0 ✓', explanation: 'Another valid triplet found!', visual: { array: [-4, -1, -1, 0, 1, 2], i: 1, left: 4, right: 3, highlight: [1, 3, 4], found: [[-1, -1, 2], [-1, 0, 1]] } } ] }, { id: 'longest-palindrome', title: 'Longest Palindromic Substring', difficulty: 'Medium', dataStructure: 'String & Expand Around Centers', why: 'Instead of checking all substrings (O(n³)), we check each possible center and expand outward (O(n²)). Much more efficient!', concept: 'Like ripples in water - start from a center point and expand outward. Check both odd-length (center at char) and even-length (center between chars) palindromes.', example: { input: 'babad', solution: 'bab' }, steps: [ { step: 1, action: 'Start at position 0: "b"', explanation: 'Check odd-length palindrome centered at "b"', visual: { string: 'babad', center: 0, left: 0, right: 0, highlight: [0] } }, { step: 2, action: 'Expand: left=-1 (out of bounds), right=1. Stop. Length = 1', explanation: 'Cannot expand further, palindrome is just "b"', visual: { string: 'babad', center: 1, left: 1, right: 1, highlight: [1] } }, { step: 3, action: 'Center at position 1: "a". Expand: left=0 ("b"), right=2 ("b")', explanation: 'Characters match! Continue expanding', visual: { string: 'babad', center: 1, left: 0, right: 2, highlight: [0, 1, 2] } }, { step: 4, action: 'Expand further: left=-1, right=3. Stop. Found "bab" (length 3)', explanation: 'This is the longest palindrome found so far!', visual: { string: 'babad', center: 1, left: -1, right: 3, highlight: [0, 1, 2], longest: 'bab' } }, { step: 5, action: 'Check even-length: center between 1-2. "a" != "b", no palindrome', explanation: 'Even-length palindromes have center between two characters', visual: { string: 'babad', center: 1.5, left: 1, right: 2, highlight: [1, 2] } }, { step: 6, action: 'Continue checking other centers... Final answer: "bab"', explanation: 'We check all possible centers and keep the longest palindrome', visual: { string: 'babad', longest: 'bab', highlight: [0, 1, 2] } } ] } ]; function AlgorithmPixelGame() { const [currentProblem, setCurrentProblem] = useState(0); const [currentStep, setCurrentStep] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const [showWhy, setShowWhy] = useState(false); const [showConcept, setShowConcept] = useState(false); const [autoPlay, setAutoPlay] = useState(false); const [speed, setSpeed] = useState(1500); // milliseconds const autoPlayRef = useRef(null); const problem = PROBLEMS[currentProblem]; const totalSteps = problem.steps.length; // Reset state when problem changes useEffect(() => { setCurrentStep(0); setIsPlaying(false); setAutoPlay(false); setShowWhy(false); setShowConcept(false); }, [currentProblem]); // Auto-play functionality useEffect(() => { if (autoPlay && isPlaying && currentStep < totalSteps - 1) { autoPlayRef.current = setTimeout(() => { setCurrentStep(prev => Math.min(prev + 1, totalSteps - 1)); }, speed); } else if (currentStep >= totalSteps - 1) { setIsPlaying(false); setAutoPlay(false); } return () => { if (autoPlayRef.current) { clearTimeout(autoPlayRef.current); } }; }, [autoPlay, isPlaying, currentStep, totalSteps, speed]); const nextStep = () => { if (currentStep < totalSteps - 1) { setCurrentStep(currentStep + 1); } }; const prevStep = () => { if (currentStep > 0) { setCurrentStep(currentStep - 1); } }; const reset = () => { setCurrentStep(0); setIsPlaying(false); setAutoPlay(false); }; const startPlaythrough = () => { setCurrentStep(0); setIsPlaying(true); }; const toggleAutoPlay = () => { if (!isPlaying) { startPlaythrough(); } setAutoPlay(!autoPlay); }; // Next problem const nextProblem = () => { if (currentProblem < PROBLEMS.length - 1) { setCurrentProblem(currentProblem + 1); setCurrentStep(0); setIsPlaying(false); setAutoPlay(false); } }; // Previous problem const prevProblem = () => { if (currentProblem > 0) { setCurrentProblem(currentProblem - 1); setCurrentStep(0); setIsPlaying(false); setAutoPlay(false); } }; const currentStepData = problem.steps[currentStep]; const visual = currentStepData.visual; // Render visual elements based on problem type const renderVisualization = () => { if (problem.id === 'valid-parentheses') { return (