Changes
This commit is contained in:
parent
1dfa8d6617
commit
12606925b7
1 changed files with 66 additions and 73 deletions
|
|
@ -2,7 +2,7 @@ import React, { useState, useCallback, useEffect } from 'react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { CheckCircle, XCircle, Info, ChevronDown, ChevronUp, RotateCcw, Shuffle, Lightbulb } from 'lucide-react';
|
import { CheckCircle, ChevronDown, ChevronUp, Shuffle, Lightbulb } from 'lucide-react';
|
||||||
import SocialShare from '@/components/SocialShare';
|
import SocialShare from '@/components/SocialShare';
|
||||||
|
|
||||||
interface Domino {
|
interface Domino {
|
||||||
|
|
@ -22,6 +22,7 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
const [showInstructions, setShowInstructions] = useState(false);
|
const [showInstructions, setShowInstructions] = useState(false);
|
||||||
const [moveCount, setMoveCount] = useState(0);
|
const [moveCount, setMoveCount] = useState(0);
|
||||||
const [showHint, setShowHint] = useState(false);
|
const [showHint, setShowHint] = useState(false);
|
||||||
|
const [hoverPlacement, setHoverPlacement] = useState<{ row: number; col: number; isHorizontal: boolean } | null>(null);
|
||||||
|
|
||||||
const BOARD_ROWS = 8;
|
const BOARD_ROWS = 8;
|
||||||
const BOARD_COLS = 9; // 8 + 1 extra square on top row
|
const BOARD_COLS = 9; // 8 + 1 extra square on top row
|
||||||
|
|
@ -70,6 +71,7 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
setDominoes(newDominoes);
|
setDominoes(newDominoes);
|
||||||
setSelectedDomino(null);
|
setSelectedDomino(null);
|
||||||
setMoveCount(0);
|
setMoveCount(0);
|
||||||
|
setHoverPlacement(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -99,7 +101,7 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if we can place a domino at a position
|
// Check if we can place a domino at a position
|
||||||
const canPlaceDomino = (row: number, col: number, isHorizontal: boolean, excludeDominoId: number): boolean => {
|
const canPlaceDomino = useCallback((row: number, col: number, isHorizontal: boolean, excludeDominoId: number): boolean => {
|
||||||
const cell1Row = row;
|
const cell1Row = row;
|
||||||
const cell1Col = col;
|
const cell1Col = col;
|
||||||
const cell2Row = isHorizontal ? row : row + 1;
|
const cell2Row = isHorizontal ? row : row + 1;
|
||||||
|
|
@ -109,35 +111,16 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
|
|
||||||
const occupied = getOccupiedCells(excludeDominoId);
|
const occupied = getOccupiedCells(excludeDominoId);
|
||||||
return !occupied.has(`${cell1Row},${cell1Col}`) && !occupied.has(`${cell2Row},${cell2Col}`);
|
return !occupied.has(`${cell1Row},${cell1Col}`) && !occupied.has(`${cell2Row},${cell2Col}`);
|
||||||
};
|
|
||||||
|
|
||||||
// Get valid placements for the selected domino
|
|
||||||
const getValidPlacements = useCallback((dominoId: number): { row: number; col: number; isHorizontal: boolean }[] => {
|
|
||||||
const placements: { row: number; col: number; isHorizontal: boolean }[] = [];
|
|
||||||
|
|
||||||
for (let row = 0; row < BOARD_ROWS; row++) {
|
|
||||||
const maxCol = row === 0 ? BOARD_COLS : 8;
|
|
||||||
for (let col = 0; col < maxCol; col++) {
|
|
||||||
// Check horizontal placement
|
|
||||||
if (canPlaceDomino(row, col, true, dominoId)) {
|
|
||||||
placements.push({ row, col, isHorizontal: true });
|
|
||||||
}
|
|
||||||
// Check vertical placement
|
|
||||||
if (canPlaceDomino(row, col, false, dominoId)) {
|
|
||||||
placements.push({ row, col, isHorizontal: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return placements;
|
|
||||||
}, [getOccupiedCells]);
|
}, [getOccupiedCells]);
|
||||||
|
|
||||||
// Handle clicking on a domino
|
// Handle clicking on a domino
|
||||||
const handleDominoClick = (dominoId: number) => {
|
const handleDominoClick = (dominoId: number) => {
|
||||||
if (selectedDomino === dominoId) {
|
if (selectedDomino === dominoId) {
|
||||||
setSelectedDomino(null);
|
setSelectedDomino(null);
|
||||||
|
setHoverPlacement(null);
|
||||||
} else {
|
} else {
|
||||||
setSelectedDomino(dominoId);
|
setSelectedDomino(dominoId);
|
||||||
|
setHoverPlacement(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -151,15 +134,35 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
: d
|
: d
|
||||||
));
|
));
|
||||||
setSelectedDomino(null);
|
setSelectedDomino(null);
|
||||||
|
setHoverPlacement(null);
|
||||||
setMoveCount(prev => prev + 1);
|
setMoveCount(prev => prev + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle cell hover
|
||||||
|
const handleCellHover = (row: number, col: number) => {
|
||||||
|
if (selectedDomino === null) return;
|
||||||
|
|
||||||
|
// Check which placements are valid for this cell
|
||||||
|
const canH = canPlaceDomino(row, col, true, selectedDomino);
|
||||||
|
const canV = canPlaceDomino(row, col, false, selectedDomino);
|
||||||
|
|
||||||
|
if (canH && canV) {
|
||||||
|
// Prefer horizontal
|
||||||
|
setHoverPlacement({ row, col, isHorizontal: true });
|
||||||
|
} else if (canH) {
|
||||||
|
setHoverPlacement({ row, col, isHorizontal: true });
|
||||||
|
} else if (canV) {
|
||||||
|
setHoverPlacement({ row, col, isHorizontal: false });
|
||||||
|
} else {
|
||||||
|
setHoverPlacement(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check if puzzle is solved (all dominoes horizontal)
|
// Check if puzzle is solved (all dominoes horizontal)
|
||||||
const isSolved = dominoes.length > 0 && dominoes.every(d => d.isHorizontal);
|
const isSolved = dominoes.length > 0 && dominoes.every(d => d.isHorizontal);
|
||||||
|
|
||||||
// Get domino color based on orientation
|
// Get domino color based on orientation
|
||||||
const getDominoColor = (isHorizontal: boolean, isSelected: boolean): string => {
|
const getDominoColor = (isHorizontal: boolean): string => {
|
||||||
if (isSelected) return 'bg-amber-400 border-amber-600';
|
|
||||||
if (isHorizontal) return 'bg-emerald-500 border-emerald-700';
|
if (isHorizontal) return 'bg-emerald-500 border-emerald-700';
|
||||||
return 'bg-rose-400 border-rose-600';
|
return 'bg-rose-400 border-rose-600';
|
||||||
};
|
};
|
||||||
|
|
@ -167,19 +170,13 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
// Render the board
|
// Render the board
|
||||||
const renderBoard = () => {
|
const renderBoard = () => {
|
||||||
const cells: JSX.Element[] = [];
|
const cells: JSX.Element[] = [];
|
||||||
const validPlacements = selectedDomino !== null ? getValidPlacements(selectedDomino) : [];
|
const occupied = getOccupiedCells(selectedDomino ?? undefined);
|
||||||
const occupied = getOccupiedCells();
|
|
||||||
|
|
||||||
// Render cells
|
// Render cells
|
||||||
for (let row = 0; row < BOARD_ROWS; row++) {
|
for (let row = 0; row < BOARD_ROWS; row++) {
|
||||||
const maxCol = row === 0 ? BOARD_COLS : 8;
|
const maxCol = row === 0 ? BOARD_COLS : 8;
|
||||||
for (let col = 0; col < maxCol; col++) {
|
for (let col = 0; col < maxCol; col++) {
|
||||||
const isOccupied = occupied.has(`${row},${col}`);
|
|
||||||
const isExtraSquare = row === 0 && col === 8;
|
const isExtraSquare = row === 0 && col === 8;
|
||||||
|
|
||||||
// Check if this is a valid horizontal placement start
|
|
||||||
const validHorizontal = validPlacements.find(p => p.row === row && p.col === col && p.isHorizontal);
|
|
||||||
const validVertical = validPlacements.find(p => p.row === row && p.col === col && !p.isHorizontal);
|
|
||||||
|
|
||||||
const cellStyle: React.CSSProperties = {
|
const cellStyle: React.CSSProperties = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|
@ -194,59 +191,31 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
key={`cell-${row}-${col}`}
|
key={`cell-${row}-${col}`}
|
||||||
className={`border border-border/50 ${
|
className={`border border-border/50 ${
|
||||||
isExtraSquare ? 'bg-primary/20' : (row + col) % 2 === 0 ? 'bg-muted/30' : 'bg-muted/60'
|
isExtraSquare ? 'bg-primary/20' : (row + col) % 2 === 0 ? 'bg-muted/30' : 'bg-muted/60'
|
||||||
}`}
|
} ${selectedDomino !== null ? 'cursor-pointer' : ''}`}
|
||||||
style={cellStyle}
|
style={cellStyle}
|
||||||
|
onMouseEnter={() => handleCellHover(row, col)}
|
||||||
|
onMouseLeave={() => setHoverPlacement(null)}
|
||||||
|
onClick={() => {
|
||||||
|
if (hoverPlacement && hoverPlacement.row === row && hoverPlacement.col === col) {
|
||||||
|
handlePlacementClick(hoverPlacement.row, hoverPlacement.col, hoverPlacement.isHorizontal);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render valid placement indicators
|
|
||||||
if (validHorizontal && !isOccupied) {
|
|
||||||
cells.push(
|
|
||||||
<div
|
|
||||||
key={`valid-h-${row}-${col}`}
|
|
||||||
className="absolute bg-emerald-300/60 border-2 border-emerald-500 border-dashed rounded cursor-pointer hover:bg-emerald-400/70 transition-colors z-10"
|
|
||||||
style={{
|
|
||||||
left: col * 50 + 2,
|
|
||||||
top: row * 50 + 2,
|
|
||||||
width: 96,
|
|
||||||
height: 46,
|
|
||||||
}}
|
|
||||||
onClick={() => handlePlacementClick(row, col, true)}
|
|
||||||
title="Place horizontally"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (validVertical && !isOccupied) {
|
|
||||||
cells.push(
|
|
||||||
<div
|
|
||||||
key={`valid-v-${row}-${col}`}
|
|
||||||
className="absolute bg-blue-300/60 border-2 border-blue-500 border-dashed rounded cursor-pointer hover:bg-blue-400/70 transition-colors z-10"
|
|
||||||
style={{
|
|
||||||
left: col * 50 + 2,
|
|
||||||
top: row * 50 + 2,
|
|
||||||
width: 46,
|
|
||||||
height: 96,
|
|
||||||
}}
|
|
||||||
onClick={() => handlePlacementClick(row, col, false)}
|
|
||||||
title="Place vertically"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render dominoes
|
// Render dominoes (except selected one)
|
||||||
dominoes.forEach(d => {
|
dominoes.forEach(d => {
|
||||||
const isSelected = selectedDomino === d.id;
|
if (d.id === selectedDomino) return; // Hide selected domino
|
||||||
|
|
||||||
const width = d.isHorizontal ? 96 : 46;
|
const width = d.isHorizontal ? 96 : 46;
|
||||||
const height = d.isHorizontal ? 46 : 96;
|
const height = d.isHorizontal ? 46 : 96;
|
||||||
|
|
||||||
cells.push(
|
cells.push(
|
||||||
<div
|
<div
|
||||||
key={`domino-${d.id}`}
|
key={`domino-${d.id}`}
|
||||||
className={`absolute rounded border-2 cursor-pointer transition-all z-20 ${getDominoColor(d.isHorizontal, isSelected)} ${
|
className={`absolute rounded border-2 cursor-pointer transition-all z-20 ${getDominoColor(d.isHorizontal)} hover:brightness-110`}
|
||||||
isSelected ? 'ring-2 ring-amber-300 ring-offset-2 scale-105' : 'hover:brightness-110'
|
|
||||||
}`}
|
|
||||||
style={{
|
style={{
|
||||||
left: d.col * 50 + 2,
|
left: d.col * 50 + 2,
|
||||||
top: d.row * 50 + 2,
|
top: d.row * 50 + 2,
|
||||||
|
|
@ -256,12 +225,36 @@ const DominoRetilingPuzzle: React.FC<DominoRetilingPuzzleProps> = ({ showSocialS
|
||||||
onClick={() => handleDominoClick(d.id)}
|
onClick={() => handleDominoClick(d.id)}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
<div className={`w-3 h-3 rounded-full ${isSelected ? 'bg-amber-700' : d.isHorizontal ? 'bg-emerald-700' : 'bg-rose-700'}`} />
|
<div className={`w-3 h-3 rounded-full ${d.isHorizontal ? 'bg-emerald-700' : 'bg-rose-700'}`} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Render hover preview domino
|
||||||
|
if (hoverPlacement && selectedDomino !== null) {
|
||||||
|
const width = hoverPlacement.isHorizontal ? 96 : 46;
|
||||||
|
const height = hoverPlacement.isHorizontal ? 46 : 96;
|
||||||
|
|
||||||
|
cells.push(
|
||||||
|
<div
|
||||||
|
key="hover-preview"
|
||||||
|
className="absolute rounded border-2 cursor-pointer z-30 bg-amber-400 border-amber-600 opacity-80 hover:opacity-100 transition-opacity"
|
||||||
|
style={{
|
||||||
|
left: hoverPlacement.col * 50 + 2,
|
||||||
|
top: hoverPlacement.row * 50 + 2,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<div className="w-3 h-3 rounded-full bg-amber-700" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return cells;
|
return cells;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue