interactives/src/components/ParityBitsGame.tsx
gpt-engineer-app[bot] 352b2342ed Fix parity bits calculation
The parity bits are now correctly calculated and displayed, reflecting the parity of their respective rows and columns.
2025-08-22 17:26:29 +00:00

274 lines
No EOL
9.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useCallback } from 'react';
import { Button } from '@/components/ui/button';
import { Slider } from '@/components/ui/slider';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { RotateCcw, Info } from 'lucide-react';
interface ParityBitsGameProps {
showSocialShare?: boolean;
}
const ParityBitsGame = ({ showSocialShare = true }: ParityBitsGameProps) => {
const [gridSize, setGridSize] = useState(3);
const [grid, setGrid] = useState<boolean[][]>(() =>
Array(gridSize).fill(null).map(() => Array(gridSize).fill(false))
);
const [parityBitsAdded, setParityBitsAdded] = useState(false);
const [parityGrid, setParityGrid] = useState<boolean[][]>([]);
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');
// Reset game when grid size changes
const handleGridSizeChange = useCallback((newSize: number[]) => {
const size = newSize[0];
setGridSize(size);
setGrid(Array(size).fill(null).map(() => Array(size).fill(false)));
setParityBitsAdded(false);
setParityGrid([]);
setFlippedCell(null);
setErrorDetected(null);
setPhase('setup');
}, []);
// Toggle a cell in the main grid
const toggleCell = (row: number, col: number) => {
if (phase !== 'setup') return;
const newGrid = [...grid];
newGrid[row][col] = !newGrid[row][col];
setGrid(newGrid);
};
// Calculate parity for a row or column
const calculateParity = (bits: boolean[]): boolean => {
return bits.filter(bit => bit).length % 2 === 1;
};
// Add parity bits to create the extended grid
const addParityBits = () => {
const extended = Array(gridSize + 1).fill(null).map(() => Array(gridSize + 1).fill(false));
// Copy original grid
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
extended[i][j] = grid[i][j];
}
}
// Calculate row parities
for (let i = 0; i < gridSize; i++) {
extended[i][gridSize] = calculateParity(grid[i]);
}
// Calculate column parities
for (let j = 0; j < gridSize; j++) {
const column = grid.map(row => row[j]);
extended[gridSize][j] = calculateParity(column);
}
// Calculate overall parity
const allBits = grid.flat();
const rowParities = Array(gridSize).fill(null).map((_, i) => calculateParity(grid[i]));
extended[gridSize][gridSize] = calculateParity([...allBits, ...rowParities]);
setParityGrid(extended);
setParityBitsAdded(true);
setPhase('parity');
};
// Flip a bit in the parity grid
const flipBit = (row: number, col: number) => {
if (phase !== 'parity' || row >= gridSize || col >= gridSize) return;
const newGrid = [...parityGrid];
newGrid[row][col] = !newGrid[row][col];
setParityGrid(newGrid);
setFlippedCell({row, col});
setPhase('flip');
};
// Identify the flipped bit using parity check
const identifyFlip = () => {
if (!parityBitsAdded) return;
let errorRow = -1;
let errorCol = -1;
// 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;
}
}
// 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;
}
}
if (errorRow >= 0 && errorCol >= 0) {
setErrorDetected({row: errorRow, col: errorCol});
}
setPhase('detect');
};
// Reset the game
const resetGame = () => {
setGrid(Array(gridSize).fill(null).map(() => Array(gridSize).fill(false)));
setParityBitsAdded(false);
setParityGrid([]);
setFlippedCell(null);
setErrorDetected(null);
setPhase('setup');
};
const renderGrid = () => {
const currentGrid = parityBitsAdded ? parityGrid : grid;
const size = parityBitsAdded ? gridSize + 1 : gridSize;
return (
<div className="grid gap-1 justify-center" style={{gridTemplateColumns: `repeat(${size}, 1fr)`}}>
{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 (
<button
key={`${row}-${col}`}
className={`
w-12 h-12 border-2 transition-all duration-200
${currentGrid[row]?.[col] ? 'bg-foreground' : 'bg-background'}
${isParityCell ? 'border-primary' : 'border-foreground'}
${isFlipped ? 'ring-4 ring-destructive' : ''}
${isError ? 'ring-4 ring-destructive bg-destructive/20' : ''}
${isErrorRow || isErrorCol ? 'ring-2 ring-warning' : ''}
${phase === 'setup' && !isParityCell ? 'hover:bg-accent cursor-pointer' : ''}
${phase === 'parity' && !isParityCell ? 'hover:bg-accent cursor-pointer' : ''}
${isParityCell ? 'cursor-not-allowed' : ''}
`}
onClick={() => {
if (phase === 'setup') toggleCell(row, col);
else if (phase === 'parity') flipBit(row, col);
}}
disabled={isParityCell}
/>
);
})
)}
</div>
);
};
return (
<div className="container mx-auto p-6 max-w-4xl">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Info className="h-5 w-5" />
Parity Bits Error Detection
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* Instructions */}
<Alert>
<AlertDescription>
{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.'}
</AlertDescription>
</Alert>
{/* Controls */}
<div className="flex flex-col sm:flex-row items-center gap-4">
<div className="flex items-center gap-2">
<label htmlFor="grid-size" className="text-sm font-medium whitespace-nowrap">
Grid Size: {gridSize}×{gridSize}
</label>
<Slider
id="grid-size"
min={2}
max={10}
step={1}
value={[gridSize]}
onValueChange={handleGridSizeChange}
className="w-24"
disabled={phase !== 'setup'}
/>
</div>
<div className="flex gap-2">
<Button
onClick={addParityBits}
disabled={phase !== 'setup'}
variant={phase === 'setup' ? 'default' : 'outline'}
>
Add Parity Bits
</Button>
<Button
onClick={identifyFlip}
disabled={phase !== 'flip'}
variant={phase === 'flip' ? 'default' : 'outline'}
>
Identify Flip
</Button>
<Button
onClick={resetGame}
variant="outline"
size="icon"
>
<RotateCcw className="h-4 w-4" />
</Button>
</div>
</div>
{/* Grid */}
<div className="flex justify-center">
{renderGrid()}
</div>
{/* Status Information */}
{parityBitsAdded && (
<div className="text-center space-y-2">
<p className="text-sm text-muted-foreground">
Parity cells have colored borders. Each row and column should have an even number of black squares.
</p>
{flippedCell && (
<p className="text-sm text-destructive">
Flipped bit at position ({flippedCell.row + 1}, {flippedCell.col + 1})
</p>
)}
{errorDetected && (
<p className="text-sm text-destructive font-medium">
Error detected at position ({errorDetected.row + 1}, {errorDetected.col + 1})!
</p>
)}
</div>
)}
{/* Legend */}
<div className="text-xs text-muted-foreground text-center space-y-1">
<p><strong>How it works:</strong> Parity bits ensure even parity in each row/column.</p>
<p>When a bit flips, the affected row and column will have odd parity, pinpointing the error.</p>
</div>
</CardContent>
</Card>
</div>
);
};
export default ParityBitsGame;