Refine parity bits identification

Update parity bits interactive to highlight flipped bit location in green, display row/column parity ("odd" in red), and briefly highlight all black cells upon clicking "Identify Flip".
This commit is contained in:
gpt-engineer-app[bot] 2025-08-22 17:30:30 +00:00
parent 352b2342ed
commit 0bb5653759

View file

@ -19,6 +19,23 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
const [flippedCell, setFlippedCell] = useState<{row: number, col: number} | null>(null); const [flippedCell, setFlippedCell] = useState<{row: number, col: number} | null>(null);
const [errorDetected, setErrorDetected] = 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 [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 // Reset game when grid size changes
const handleGridSizeChange = useCallback((newSize: number[]) => { const handleGridSizeChange = useCallback((newSize: number[]) => {
@ -89,34 +106,74 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
setPhase('flip'); setPhase('flip');
}; };
// Identify the flipped bit using parity check // Identify the flipped bit using parity check with animation
const identifyFlip = () => { const identifyFlip = async () => {
if (!parityBitsAdded) return; if (!parityBitsAdded) return;
let errorRow = -1; // Calculate all parities first
let errorCol = -1; const rowParities: boolean[] = [];
const colParities: boolean[] = [];
// Check row parities
for (let i = 0; i < gridSize; i++) { for (let i = 0; i < gridSize; i++) {
const rowBits = parityGrid[i].slice(0, gridSize); const rowBits = parityGrid[i].slice(0, gridSize);
const expectedParity = calculateParity(rowBits); const expectedParity = calculateParity(rowBits);
if (expectedParity !== parityGrid[i][gridSize]) { rowParities[i] = expectedParity !== parityGrid[i][gridSize];
errorRow = i;
}
} }
// Check column parities
for (let j = 0; j < gridSize; j++) { for (let j = 0; j < gridSize; j++) {
const colBits = parityGrid.slice(0, gridSize).map(row => row[j]); const colBits = parityGrid.slice(0, gridSize).map(row => row[j]);
const expectedParity = calculateParity(colBits); const expectedParity = calculateParity(colBits);
if (expectedParity !== parityGrid[gridSize][j]) { colParities[j] = expectedParity !== parityGrid[gridSize][j];
errorCol = 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) { if (errorRow >= 0 && errorCol >= 0) {
setErrorDetected({row: errorRow, col: errorCol}); setErrorDetected({row: errorRow, col: errorCol});
} }
setAnimationState({
isAnimating: false,
currentRow: -1,
currentCol: -1,
showRowParity: false,
showColParity: false,
rowParities: [],
colParities: []
});
setPhase('detect'); setPhase('detect');
}; };
@ -128,6 +185,15 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
setFlippedCell(null); setFlippedCell(null);
setErrorDetected(null); setErrorDetected(null);
setPhase('setup'); setPhase('setup');
setAnimationState({
isAnimating: false,
currentRow: -1,
currentCol: -1,
showRowParity: false,
showColParity: false,
rowParities: [],
colParities: []
});
}; };
const renderGrid = () => { const renderGrid = () => {
@ -135,14 +201,18 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
const size = parityBitsAdded ? gridSize + 1 : gridSize; const size = parityBitsAdded ? gridSize + 1 : gridSize;
return ( return (
<div className="grid gap-1 justify-center" style={{gridTemplateColumns: `repeat(${size}, 1fr)`}}> <div className="flex flex-col items-center gap-2">
{/* Main grid with row labels */}
<div className="flex items-center gap-2">
<div className="grid gap-1" style={{gridTemplateColumns: `repeat(${size}, 1fr)`}}>
{Array(size).fill(null).map((_, row) => {Array(size).fill(null).map((_, row) =>
Array(size).fill(null).map((_, col) => { Array(size).fill(null).map((_, col) => {
const isParityCell = parityBitsAdded && (row === gridSize || col === gridSize); const isParityCell = parityBitsAdded && (row === gridSize || col === gridSize);
const isFlipped = flippedCell?.row === row && flippedCell?.col === col; const isFlipped = flippedCell?.row === row && flippedCell?.col === col;
const isError = errorDetected?.row === row && errorDetected?.col === col; const isError = errorDetected?.row === row && errorDetected?.col === col;
const isErrorRow = errorDetected && row === errorDetected.row; const isCurrentRowBeingChecked = animationState.currentRow === row && !isParityCell;
const isErrorCol = errorDetected && col === errorDetected.col; const isCurrentColBeingChecked = animationState.currentCol === col && !isParityCell;
const shouldHighlightForAnimation = (isCurrentRowBeingChecked || isCurrentColBeingChecked) && currentGrid[row]?.[col];
return ( return (
<button <button
@ -152,8 +222,8 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
${currentGrid[row]?.[col] ? 'bg-foreground' : 'bg-background'} ${currentGrid[row]?.[col] ? 'bg-foreground' : 'bg-background'}
${isParityCell ? 'border-primary' : 'border-foreground'} ${isParityCell ? 'border-primary' : 'border-foreground'}
${isFlipped ? 'ring-4 ring-destructive' : ''} ${isFlipped ? 'ring-4 ring-destructive' : ''}
${isError ? 'ring-4 ring-destructive bg-destructive/20' : ''} ${isError ? 'ring-4 ring-green-500 bg-green-500/20' : ''}
${isErrorRow || isErrorCol ? 'ring-2 ring-warning' : ''} ${shouldHighlightForAnimation ? 'ring-4 ring-yellow-400 bg-yellow-400/30' : ''}
${phase === 'setup' && !isParityCell ? 'hover:bg-accent cursor-pointer' : ''} ${phase === 'setup' && !isParityCell ? 'hover:bg-accent cursor-pointer' : ''}
${phase === 'parity' && !isParityCell ? 'hover:bg-accent cursor-pointer' : ''} ${phase === 'parity' && !isParityCell ? 'hover:bg-accent cursor-pointer' : ''}
${isParityCell ? 'cursor-not-allowed' : ''} ${isParityCell ? 'cursor-not-allowed' : ''}
@ -168,6 +238,42 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
}) })
)} )}
</div> </div>
{/* Row parity labels */}
{(animationState.showRowParity || animationState.isAnimating) && (
<div className="flex flex-col gap-1">
{Array(gridSize).fill(null).map((_, row) => {
const hasOddParity = animationState.rowParities[row];
return (
<div key={`row-${row}`} className="w-12 h-12 flex items-center justify-center">
<span className={`text-xs font-medium ${hasOddParity ? 'text-red-500' : 'text-foreground'}`}>
{hasOddParity ? 'odd' : 'even'}
</span>
</div>
);
})}
<div className="w-12 h-12"></div> {/* Empty space for parity cell */}
</div>
)}
</div>
{/* Column parity labels */}
{(animationState.showColParity || animationState.isAnimating) && (
<div className="flex gap-1">
{Array(gridSize).fill(null).map((_, col) => {
const hasOddParity = animationState.colParities[col];
return (
<div key={`col-${col}`} className="w-12 h-6 flex items-center justify-center">
<span className={`text-xs font-medium ${hasOddParity ? 'text-red-500' : 'text-foreground'}`}>
{hasOddParity ? 'odd' : 'even'}
</span>
</div>
);
})}
<div className="w-12 h-6"></div> {/* Empty space for parity cell */}
</div>
)}
</div>
); );
}; };
@ -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 === '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 === '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 === '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.'}
</AlertDescription> </AlertDescription>
</Alert> </Alert>
@ -220,10 +326,10 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
<Button <Button
onClick={identifyFlip} onClick={identifyFlip}
disabled={phase !== 'flip'} disabled={phase !== 'flip' || animationState.isAnimating}
variant={phase === 'flip' ? 'default' : 'outline'} variant={phase === 'flip' ? 'default' : 'outline'}
> >
Identify Flip {animationState.isAnimating ? 'Checking...' : 'Identify Flip'}
</Button> </Button>
<Button <Button
@ -253,8 +359,8 @@ const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
</p> </p>
)} )}
{errorDetected && ( {errorDetected && (
<p className="text-sm text-destructive font-medium"> <p className="text-sm text-green-600 font-medium">
Error detected at position ({errorDetected.row + 1}, {errorDetected.col + 1})! Error detected at position ({errorDetected.row + 1}, {errorDetected.col + 1}) - highlighted in green!
</p> </p>
)} )}
</div> </div>