diff --git a/src/components/ParityBitsGame.tsx b/src/components/ParityBitsGame.tsx index 322d10c..8da54fb 100644 --- a/src/components/ParityBitsGame.tsx +++ b/src/components/ParityBitsGame.tsx @@ -19,6 +19,23 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => { const [flippedCell, setFlippedCell] = useState<{row: number, col: number} | null>(null); const [errorDetected, setErrorDetected] = useState<{row: number, col: number} | null>(null); const [phase, setPhase] = useState<'setup' | 'parity' | 'flip' | 'detect'>('setup'); + const [animationState, setAnimationState] = useState<{ + isAnimating: boolean; + currentRow: number; + currentCol: number; + showRowParity: boolean; + showColParity: boolean; + rowParities: boolean[]; + colParities: boolean[]; + }>({ + isAnimating: false, + currentRow: -1, + currentCol: -1, + showRowParity: false, + showColParity: false, + rowParities: [], + colParities: [] + }); // Reset game when grid size changes const handleGridSizeChange = useCallback((newSize: number[]) => { @@ -89,34 +106,74 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => { setPhase('flip'); }; - // Identify the flipped bit using parity check - const identifyFlip = () => { + // Identify the flipped bit using parity check with animation + const identifyFlip = async () => { if (!parityBitsAdded) return; - let errorRow = -1; - let errorCol = -1; + // Calculate all parities first + const rowParities: boolean[] = []; + const colParities: boolean[] = []; - // Check row parities for (let i = 0; i < gridSize; i++) { const rowBits = parityGrid[i].slice(0, gridSize); const expectedParity = calculateParity(rowBits); - if (expectedParity !== parityGrid[i][gridSize]) { - errorRow = i; - } + rowParities[i] = expectedParity !== parityGrid[i][gridSize]; } - // Check column parities for (let j = 0; j < gridSize; j++) { const colBits = parityGrid.slice(0, gridSize).map(row => row[j]); const expectedParity = calculateParity(colBits); - if (expectedParity !== parityGrid[gridSize][j]) { - errorCol = j; - } + colParities[j] = expectedParity !== parityGrid[gridSize][j]; } + setAnimationState({ + isAnimating: true, + currentRow: -1, + currentCol: -1, + showRowParity: false, + showColParity: false, + rowParities, + colParities + }); + + // Animate through rows + for (let i = 0; i < gridSize; i++) { + setAnimationState(prev => ({...prev, currentRow: i})); + await new Promise(resolve => setTimeout(resolve, 800)); + } + + // Show row parities + setAnimationState(prev => ({...prev, currentRow: -1, showRowParity: true})); + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Animate through columns + for (let j = 0; j < gridSize; j++) { + setAnimationState(prev => ({...prev, currentCol: j})); + await new Promise(resolve => setTimeout(resolve, 800)); + } + + // Show column parities + setAnimationState(prev => ({...prev, currentCol: -1, showColParity: true})); + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Find and highlight the error + const errorRow = rowParities.findIndex(parity => parity); + const errorCol = colParities.findIndex(parity => parity); + if (errorRow >= 0 && errorCol >= 0) { setErrorDetected({row: errorRow, col: errorCol}); } + + setAnimationState({ + isAnimating: false, + currentRow: -1, + currentCol: -1, + showRowParity: false, + showColParity: false, + rowParities: [], + colParities: [] + }); + setPhase('detect'); }; @@ -128,6 +185,15 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => { setFlippedCell(null); setErrorDetected(null); setPhase('setup'); + setAnimationState({ + isAnimating: false, + currentRow: -1, + currentCol: -1, + showRowParity: false, + showColParity: false, + rowParities: [], + colParities: [] + }); }; const renderGrid = () => { @@ -135,37 +201,77 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => { const size = parityBitsAdded ? gridSize + 1 : gridSize; return ( -
- {Array(size).fill(null).map((_, row) => - Array(size).fill(null).map((_, col) => { - const isParityCell = parityBitsAdded && (row === gridSize || col === gridSize); - const isFlipped = flippedCell?.row === row && flippedCell?.col === col; - const isError = errorDetected?.row === row && errorDetected?.col === col; - const isErrorRow = errorDetected && row === errorDetected.row; - const isErrorCol = errorDetected && col === errorDetected.col; - - return ( -
+ + {/* Row parity labels */} + {(animationState.showRowParity || animationState.isAnimating) && ( +
+ {Array(gridSize).fill(null).map((_, row) => { + const hasOddParity = animationState.rowParities[row]; + return ( +
+ + {hasOddParity ? 'odd' : 'even'} + +
+ ); + })} +
{/* Empty space for parity cell */} +
+ )} + + + {/* Column parity labels */} + {(animationState.showColParity || animationState.isAnimating) && ( +
+ {Array(gridSize).fill(null).map((_, col) => { + const hasOddParity = animationState.colParities[col]; + return ( +
+ + {hasOddParity ? 'odd' : 'even'} + +
+ ); + })} +
{/* Empty space for parity cell */} +
)} ); @@ -187,7 +293,7 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => { {phase === 'setup' && 'Set your grid by clicking squares to make them black. Then add parity bits.'} {phase === 'parity' && 'Parity bits added! Now flip exactly one bit in the main grid (not parity cells).'} {phase === 'flip' && 'Bit flipped! Click "Identify Flip" to see error detection in action.'} - {phase === 'detect' && 'Error detected! The highlighted cell shows where the bit was flipped.'} + {phase === 'detect' && 'Animation complete! Rows and columns with odd parity were highlighted, revealing the error location in green.'} @@ -220,10 +326,10 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {