This commit is contained in:
gpt-engineer-app[bot] 2025-11-19 04:05:25 +00:00
parent ca39141f80
commit 3641f01310

View file

@ -2,6 +2,7 @@ import { useState, useRef } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardDescription, 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 { Slider } from '@/components/ui/slider';
import SocialShare from './SocialShare'; import SocialShare from './SocialShare';
import { Gift, Play, RotateCw, Zap, Pause, ChevronRight, ChevronLeft, ChevronsRight, ChevronsLeft } from 'lucide-react'; import { Gift, Play, RotateCw, Zap, Pause, ChevronRight, ChevronLeft, ChevronsRight, ChevronsLeft } from 'lucide-react';
interface PresentsPuzzleProps { interface PresentsPuzzleProps {
@ -17,6 +18,8 @@ const PresentsPuzzle = ({
showSocialShare = false, showSocialShare = false,
shareUrl shareUrl
}: PresentsPuzzleProps) => { }: PresentsPuzzleProps) => {
const [numBoxes, setNumBoxes] = useState(100);
const [numPresents, setNumPresents] = useState(26);
const [presentBoxes, setPresentBoxes] = useState<Set<number>>(new Set()); const [presentBoxes, setPresentBoxes] = useState<Set<number>>(new Set());
const [aliceOpened, setAliceOpened] = useState<Set<number>>(new Set()); const [aliceOpened, setAliceOpened] = useState<Set<number>>(new Set());
const [bobOpened, setBobOpened] = useState<Set<number>>(new Set()); const [bobOpened, setBobOpened] = useState<Set<number>>(new Set());
@ -38,27 +41,27 @@ const PresentsPuzzle = ({
// Generate random present positions // Generate random present positions
const generatePresents = (): Set<number> => { const generatePresents = (): Set<number> => {
const presents = new Set<number>(); const presents = new Set<number>();
while (presents.size < 26) { while (presents.size < numPresents) {
presents.add(Math.floor(Math.random() * 100) + 1); presents.add(Math.floor(Math.random() * numBoxes) + 1);
} }
return presents; return presents;
}; };
// Get Alice's opening order: 1, 2, 3, ..., 100 // Get Alice's opening order: 1, 2, 3, ..., numBoxes
const getAliceOrder = (): number[] => { const getAliceOrder = (): number[] => {
return Array.from({ return Array.from({
length: 100 length: numBoxes
}, (_, i) => i + 1); }, (_, i) => i + 1);
}; };
// Get Bob's opening order: 1, 3, 5, ..., 99, 2, 4, 6, ..., 100 // Get Bob's opening order: 1, 3, 5, ..., odds, 2, 4, 6, ..., evens
const getBobOrder = (): number[] => { const getBobOrder = (): number[] => {
const odds = Array.from({ const odds = Array.from({
length: 50 length: Math.ceil(numBoxes / 2)
}, (_, i) => i * 2 + 1); }, (_, i) => i * 2 + 1).filter(n => n <= numBoxes);
const evens = Array.from({ const evens = Array.from({
length: 50 length: Math.floor(numBoxes / 2)
}, (_, i) => (i + 1) * 2); }, (_, i) => (i + 1) * 2).filter(n => n <= numBoxes);
return [...odds, ...evens]; return [...odds, ...evens];
}; };
@ -68,18 +71,18 @@ const PresentsPuzzle = ({
const bobOrder = getBobOrder(); const bobOrder = getBobOrder();
let aliceCount = 0; let aliceCount = 0;
let bobCount = 0; let bobCount = 0;
for (let i = 0; i < 100; i++) { for (let i = 0; i < numBoxes; i++) {
const aliceFoundPresent = presents.has(aliceOrder[i]); const aliceFoundPresent = presents.has(aliceOrder[i]);
const bobFoundPresent = presents.has(bobOrder[i]); const bobFoundPresent = presents.has(bobOrder[i]);
if (aliceFoundPresent) aliceCount++; if (aliceFoundPresent) aliceCount++;
if (bobFoundPresent) bobCount++; if (bobFoundPresent) bobCount++;
// Check if both reached 26 on the same turn (tie) // Check if both reached target on the same turn (tie)
if (aliceCount === 26 && bobCount === 26) return 'tie'; if (aliceCount === numPresents && bobCount === numPresents) return 'tie';
// Check individual winners // Check individual winners
if (aliceCount === 26) return 'alice'; if (aliceCount === numPresents) return 'alice';
if (bobCount === 26) return 'bob'; if (bobCount === numPresents) return 'bob';
} }
return 'tie'; // Default, though this should never happen return 'tie'; // Default, though this should never happen
}; };
@ -117,11 +120,11 @@ const PresentsPuzzle = ({
if (presents.has(aliceBox)) aliceCount++; if (presents.has(aliceBox)) aliceCount++;
if (presents.has(bobBox)) bobCount++; if (presents.has(bobBox)) bobCount++;
if (aliceCount === 26 && bobCount === 26) { if (aliceCount === numPresents && bobCount === numPresents) {
currentWinner = 'tie'; currentWinner = 'tie';
} else if (aliceCount === 26) { } else if (aliceCount === numPresents) {
currentWinner = 'alice'; currentWinner = 'alice';
} else if (bobCount === 26) { } else if (bobCount === numPresents) {
currentWinner = 'bob'; currentWinner = 'bob';
} }
} }
@ -160,7 +163,7 @@ const PresentsPuzzle = ({
let bobCount = 0; let bobCount = 0;
let currentWinner: 'alice' | 'bob' | 'tie' | null = null; let currentWinner: 'alice' | 'bob' | 'tie' | null = null;
for (let i = 0; i < 100 && !currentWinner; i++) { for (let i = 0; i < numBoxes && !currentWinner; i++) {
await new Promise(resolve => setTimeout(resolve, 50)); await new Promise(resolve => setTimeout(resolve, 50));
// Check pause after delay, before opening boxes // Check pause after delay, before opening boxes
@ -192,11 +195,11 @@ const PresentsPuzzle = ({
setBobFound(bobCount); setBobFound(bobCount);
setCurrentStep(i); setCurrentStep(i);
if (aliceCount === 26 && bobCount === 26) { if (aliceCount === numPresents && bobCount === numPresents) {
currentWinner = 'tie'; currentWinner = 'tie';
} else if (aliceCount === 26) { } else if (aliceCount === numPresents) {
currentWinner = 'alice'; currentWinner = 'alice';
} else if (bobCount === 26) { } else if (bobCount === numPresents) {
currentWinner = 'bob'; currentWinner = 'bob';
} }
} }
@ -211,7 +214,7 @@ const PresentsPuzzle = ({
let bobCount = bobFound; let bobCount = bobFound;
let currentWinner: 'alice' | 'bob' | 'tie' | null = winner; let currentWinner: 'alice' | 'bob' | 'tie' | null = winner;
for (let i = currentStep + 1; i < 100 && !currentWinner; i++) { for (let i = currentStep + 1; i < numBoxes && !currentWinner; i++) {
await new Promise(resolve => setTimeout(resolve, 50)); await new Promise(resolve => setTimeout(resolve, 50));
// Check pause after delay, before opening boxes // Check pause after delay, before opening boxes
@ -243,11 +246,11 @@ const PresentsPuzzle = ({
setBobFound(bobCount); setBobFound(bobCount);
setCurrentStep(i); setCurrentStep(i);
if (aliceCount === 26 && bobCount === 26) { if (aliceCount === numPresents && bobCount === numPresents) {
currentWinner = 'tie'; currentWinner = 'tie';
} else if (aliceCount === 26) { } else if (aliceCount === numPresents) {
currentWinner = 'alice'; currentWinner = 'alice';
} else if (bobCount === 26) { } else if (bobCount === numPresents) {
currentWinner = 'bob'; currentWinner = 'bob';
} }
} }
@ -262,7 +265,7 @@ const PresentsPuzzle = ({
}; };
const stepNext = () => { const stepNext = () => {
if (currentStep >= 99 || winner) return; if (currentStep >= numBoxes - 1 || winner) return;
updateStep(currentStep + 1); updateStep(currentStep + 1);
}; };
@ -274,21 +277,21 @@ const PresentsPuzzle = ({
const stepToEnd = () => { const stepToEnd = () => {
// Don't do anything if already at end or winner already determined // Don't do anything if already at end or winner already determined
if (currentStep >= 99 || winner !== null) return; if (currentStep >= numBoxes - 1 || winner !== null) return;
const { presents, aliceOrder: aOrder, bobOrder: bOrder } = initializeSimulation(); const { presents, aliceOrder: aOrder, bobOrder: bOrder } = initializeSimulation();
let aliceCount = 0; let aliceCount = 0;
let bobCount = 0; let bobCount = 0;
let winningStep = 99; // default to last step if no winner found let winningStep = numBoxes - 1; // default to last step if no winner found
// Find the step where someone first wins // Find the step where someone first wins
for (let i = 0; i < 100; i++) { for (let i = 0; i < numBoxes; i++) {
if (presents.has(aOrder[i])) aliceCount++; if (presents.has(aOrder[i])) aliceCount++;
if (presents.has(bOrder[i])) bobCount++; if (presents.has(bOrder[i])) bobCount++;
// Check if someone won on this step // Check if someone won on this step
if (aliceCount === 26 || bobCount === 26) { if (aliceCount === numPresents || bobCount === numPresents) {
winningStep = i; winningStep = i;
break; break;
} }
@ -347,19 +350,20 @@ const PresentsPuzzle = ({
// Render a grid of boxes // Render a grid of boxes
const renderGrid = (title: string, openedBoxes: Set<number>, found: number, color: string) => { const renderGrid = (title: string, openedBoxes: Set<number>, found: number, color: string) => {
const cols = Math.min(10, numBoxes);
return <div className="space-y-3"> return <div className="space-y-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="font-semibold text-lg"> <h3 className="font-semibold text-lg">
{title} <span className="text-sm text-muted-foreground">({openedBoxes.size}/100)</span> {title} <span className="text-sm text-muted-foreground">({openedBoxes.size}/{numBoxes})</span>
</h3> </h3>
<Badge variant="secondary" className="text-sm"> <Badge variant="secondary" className="text-sm">
<Gift className="w-3 h-3 mr-1" /> <Gift className="w-3 h-3 mr-1" />
{found}/26 {found}/{numPresents}
</Badge> </Badge>
</div> </div>
<div className="grid grid-cols-10 gap-1 p-2 bg-muted/30 rounded-lg"> <div className={`grid gap-1 p-2 bg-muted/30 rounded-lg`} style={{ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` }}>
{Array.from({ {Array.from({
length: 100 length: numBoxes
}, (_, i) => i + 1).map(boxNum => { }, (_, i) => i + 1).map(boxNum => {
const hasPresent = presentBoxes.has(boxNum); const hasPresent = presentBoxes.has(boxNum);
const isOpened = openedBoxes.has(boxNum); const isOpened = openedBoxes.has(boxNum);
@ -393,12 +397,42 @@ const PresentsPuzzle = ({
<CardContent className="space-y-6"> <CardContent className="space-y-6">
{/* Puzzle Statement */} {/* Puzzle Statement */}
<Card className="bg-muted/50"> <Card className="bg-muted/50">
<CardContent className="pt-6"> <CardContent className="pt-6 space-y-4">
<p className="text-sm leading-relaxed"> <p className="text-sm leading-relaxed">
Charlie puts <strong>26 presents</strong> in <strong>100 boxes</strong>, labeled 1 to 100. Charlie puts{' '}
<span className="inline-flex items-center gap-2 font-semibold">
<input
type="number"
value={numPresents}
onChange={(e) => {
const val = Math.max(1, Math.min(numBoxes, parseInt(e.target.value) || 1));
setNumPresents(val);
resetSimulation();
}}
className="w-16 px-2 py-0.5 text-center border rounded bg-background"
disabled={isRunning || currentStep > -1}
/>
presents
</span>
{' '}in{' '}
<span className="inline-flex items-center gap-2 font-semibold">
<input
type="number"
value={numBoxes}
onChange={(e) => {
const val = Math.max(numPresents, parseInt(e.target.value) || numPresents);
setNumBoxes(val);
resetSimulation();
}}
className="w-16 px-2 py-0.5 text-center border rounded bg-background"
disabled={isRunning || currentStep > -1}
/>
boxes
</span>
, labeled 1 to {numBoxes}.
Each second, Alice and Bob look in one box. Alice opens them in order (1, 2, 3, ), Each second, Alice and Bob look in one box. Alice opens them in order (1, 2, 3, ),
while Bob opens the odds first, then the evens (1, 3, 5, , 2, 4, 6, ). while Bob opens the odds first, then the evens (1, 3, 5, , 2, 4, 6, ).
<strong> Who is more likely to see all 26 presents first?</strong> <strong> Who is more likely to see all {numPresents} presents first?</strong>
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -414,7 +448,7 @@ const PresentsPuzzle = ({
<CardContent className="pt-6"> <CardContent className="pt-6">
<div className="text-center"> <div className="text-center">
<div className="text-2xl font-bold text-primary"> <div className="text-2xl font-bold text-primary">
{winner === 'tie' ? "🤝 It's a tie! Both found all 26 presents at the same time!" : `🎉 ${winner === 'alice' ? 'Alice' : 'Bob'} found all 26 presents first!`} {winner === 'tie' ? `🤝 It's a tie! Both found all ${numPresents} presents at the same time!` : `🎉 ${winner === 'alice' ? 'Alice' : 'Bob'} found all ${numPresents} presents first!`}
</div> </div>
</div> </div>
</CardContent> </CardContent>
@ -459,7 +493,7 @@ const PresentsPuzzle = ({
</Button> </Button>
<Button <Button
onClick={stepNext} onClick={stepNext}
disabled={isRunning || currentStep >= 99 || winner !== null} disabled={isRunning || currentStep >= numBoxes - 1 || winner !== null}
variant="outline" variant="outline"
className="gap-2" className="gap-2"
title="Step next" title="Step next"
@ -468,7 +502,7 @@ const PresentsPuzzle = ({
</Button> </Button>
<Button <Button
onClick={stepToEnd} onClick={stepToEnd}
disabled={isRunning || currentStep >= 99 || winner !== null} disabled={isRunning || currentStep >= numBoxes - 1 || winner !== null}
variant="outline" variant="outline"
className="gap-2" className="gap-2"
title="Step to end" title="Step to end"