import React, { useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { RefreshCw, RotateCcw, BookOpen, Trophy, CheckCircle, ArrowLeft, ExternalLink } from 'lucide-react'; import SocialShare from './SocialShare'; import { useSearchParams, useNavigate } from 'react-router-dom'; interface Person { id: number; plate: number; isSelected: boolean; isPaired: boolean; pairId: number | null; pairColor: string | null; } interface GameState { people: Person[]; moves: number; isComplete: boolean; gameStarted: boolean; } interface PlateSwapPuzzleProps { showSocialShare?: boolean; } const PlateSwapPuzzle: React.FC = ({ showSocialShare = false }) => { const [searchParams] = useSearchParams(); const navigate = useNavigate(); const isFromPuzzles = searchParams.get('from') === 'puzzles'; const [gameState, setGameState] = useState(() => { const people: Person[] = []; for (let i = 1; i <= 16; i++) { people.push({ id: i, plate: i === 16 ? 1 : i + 1, // 1 has 2's plate, 2 has 3's plate, ..., 16 has 1's plate isSelected: false, isPaired: false, pairId: null, pairColor: null }); } return { people, moves: 0, isComplete: false, gameStarted: true }; }); const [showRules, setShowRules] = useState(false); const [showVictory, setShowVictory] = useState(false); const resetGame = () => { const people: Person[] = []; for (let i = 1; i <= 16; i++) { people.push({ id: i, plate: i === 16 ? 1 : i + 1, // 1 has 2's plate, 2 has 3's plate, ..., 16 has 1's plate isSelected: false, isPaired: false, pairId: null, pairColor: null }); } setGameState({ people, moves: 0, isComplete: false, gameStarted: true }); setShowVictory(false); }; const pairColors = [ '#ef4444', // red '#3b82f6', // blue '#f59e0b', // amber '#8b5cf6', // violet '#ec4899', // pink '#06b6d4', // cyan '#84cc16', // lime '#f97316' // orange ]; const handlePersonClick = (personId: number) => { if (!gameState.gameStarted || gameState.isComplete) return; const person = gameState.people.find(p => p.id === personId); if (!person || person.plate === person.id) return; // Can't select people who already have their plate setGameState(prev => { const updatedPeople = [...prev.people]; const clickedPerson = updatedPeople.find(p => p.id === personId)!; // If this person is already paired, unselect them if (clickedPerson.isPaired) { const pairId = clickedPerson.pairId!; const pairedPerson = updatedPeople.find(p => p.id === pairId)!; clickedPerson.isSelected = false; clickedPerson.isPaired = false; clickedPerson.pairId = null; clickedPerson.pairColor = null; pairedPerson.isSelected = false; pairedPerson.isPaired = false; pairedPerson.pairId = null; pairedPerson.pairColor = null; return { ...prev, people: updatedPeople }; } // If this person is selected, unselect them if (clickedPerson.isSelected) { clickedPerson.isSelected = false; return { ...prev, people: updatedPeople }; } // Check if there's already a selected person const selectedPerson = updatedPeople.find(p => p.isSelected && !p.isPaired); if (selectedPerson) { // Find the next available color const usedColors = updatedPeople.filter(p => p.pairColor !== null).map(p => p.pairColor); const availableColor = pairColors.find(color => !usedColors.includes(color)) || pairColors[0]; // Pair them up selectedPerson.isSelected = false; selectedPerson.isPaired = true; selectedPerson.pairId = personId; selectedPerson.pairColor = availableColor; clickedPerson.isSelected = false; clickedPerson.isPaired = true; clickedPerson.pairId = selectedPerson.id; clickedPerson.pairColor = availableColor; } else { // Select this person clickedPerson.isSelected = true; } return { ...prev, people: updatedPeople }; }); }; const handleSwap = () => { if (!gameState.gameStarted || gameState.isComplete) return; const pairedPeople = gameState.people.filter(p => p.isPaired); if (pairedPeople.length === 0) return; setGameState(prev => { const updatedPeople = [...prev.people]; // Process each paired person and swap with their pair const processedPairs = new Set(); pairedPeople.forEach(person => { if (processedPairs.has(person.id)) return; // Skip if already processed const person1 = updatedPeople.find(p => p.id === person.id)!; const person2 = updatedPeople.find(p => p.id === person.pairId!)!; // Swap plates const tempPlate = person1.plate; person1.plate = person2.plate; person2.plate = tempPlate; // Reset pairing state person1.isSelected = false; person1.isPaired = false; person1.pairId = null; person1.pairColor = null; person2.isSelected = false; person2.isPaired = false; person2.pairId = null; person2.pairColor = null; // Mark both as processed processedPairs.add(person1.id); processedPairs.add(person2.id); }); // Check if puzzle is complete const isComplete = updatedPeople.every(p => p.plate === p.id); if (isComplete) { setShowVictory(true); } return { ...prev, people: updatedPeople, moves: prev.moves + 1, isComplete }; }); }; const canSwap = () => { const pairedPeople = gameState.people.filter(p => p.isPaired); const peopleWithoutCorrectPlate = gameState.people.filter(p => p.plate !== p.id); // Check if all people without correct plates are paired const allIncorrectPeoplePaired = peopleWithoutCorrectPlate.every(p => p.isPaired); return pairedPeople.length >= 2 && pairedPeople.length % 2 === 0 && allIncorrectPeoplePaired; }; const renderPerson = (person: Person) => { const isCorrect = person.plate === person.id; const isSelected = person.isSelected; const isPaired = person.isPaired; return (
handlePersonClick(person.id)} > {/* Plate number */}
{person.plate}
); }; const renderTable = () => { const people = gameState.people; if (people.length === 0) return null; // Calculate positions for a circular table const radius = 240; const centerX = 300; const centerY = 300; const outerRadius = 310; // Outer radius for seat numbers return (
{/* Seat numbers on outer boundary */} {people.map((person, index) => { const angle = (index * 360) / 16 - 90; // Start from top const x = centerX + outerRadius * Math.cos((angle * Math.PI) / 180); const y = centerY + outerRadius * Math.sin((angle * Math.PI) / 180); return (
{person.id}
); })} {/* Plates */} {people.map((person, index) => { const angle = (index * 360) / 16 - 90; // Start from top const x = centerX + radius * Math.cos((angle * Math.PI) / 180); const y = centerY + radius * Math.sin((angle * Math.PI) / 180); return (
{renderPerson(person)}
); })}
); }; return (
{/* Back to Puzzles button - only show when coming from puzzles */} {isFromPuzzles && (
)} The Plate Swap Puzzle Arrange the plates so each person has their own plate.
Pair people up and swap their plates!
{/* Game Controls */}
Moves: {gameState.moves}
{/* Game Board */}
{renderTable()}
{/* Instructions */}

{gameState.isComplete ? ( "Congratulations! All plates are in their correct positions!" ) : ( "Click on people to pair them up. All people without correct plates must be paired before swapping." )}

{/* Swap Button */} {!gameState.isComplete && (
)} {/* Legend */}

Legend

Person with wrong plate
Person with correct plate
Selected for pairing
Paired up (color-coded)
{/* Rules Dialog */} The Plate Swap Puzzle Rules

Objective

Arrange the plates so that each person has their own plate (Person 1 has Plate 1, Person 2 has Plate 2, etc.).

Initial Setup

Each person starts with the next person's plate: Person 1 has Plate 2, Person 2 has Plate 3, ..., Person 16 has Plate 1.

How to Play

  1. Pair people up: Click on two people to select them for pairing.
  2. Swap plates: Click the SWAP button to exchange the plates of all paired people.
  3. Repeat: Continue pairing and swapping until everyone has their correct plate.

Rules

  • People who already have their correct plate cannot be selected.
  • You must pair an even number of people to swap.
  • The SWAP button is only enabled when everyone is properly paired.
  • Try to solve the puzzle in as few moves as possible!

Strategy Tips

  • Look for cycles in the plate arrangement
  • Try to fix multiple people at once with strategic swaps
  • Plan your moves ahead to minimize the total number of swaps
  • Remember that each swap affects all paired people simultaneously
{/* Victory Dialog */} Puzzle Complete!
Congratulations!

You solved the puzzle in {gameState.moves} moves!

{gameState.moves > 2 && (

Can you solve it in fewer moves?

)}
{/* Attribution */}

I learned about this puzzle from{" "} Akash Kumar {" "}over some very fun discussions! He in turn learned of this puzzle from{" "} Jaikumar Radhakrishnan .

{/* Social Share Section */} {showSocialShare && ( )}
); }; export default PlateSwapPuzzle;