Visual edit in Lovable

Edited UI in Lovable
This commit is contained in:
gpt-engineer-app[bot] 2025-11-19 02:59:44 +00:00
parent 4e4c667fa9
commit 1c6b1641e4

View file

@ -4,19 +4,19 @@ import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import SocialShare from './SocialShare'; import SocialShare from './SocialShare';
import { Gift, Play, RotateCw, Zap } from 'lucide-react'; import { Gift, Play, RotateCw, Zap } from 'lucide-react';
interface PresentsPuzzleProps { interface PresentsPuzzleProps {
showSocialShare?: boolean; showSocialShare?: boolean;
shareUrl?: string; shareUrl?: string;
} }
interface SimulationResult { interface SimulationResult {
alice: number; alice: number;
bob: number; bob: number;
ties: number; ties: number;
} }
const PresentsPuzzle = ({
const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzleProps) => { showSocialShare = false,
shareUrl
}: PresentsPuzzleProps) => {
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());
@ -24,7 +24,11 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
const [bobFound, setBobFound] = useState(0); const [bobFound, setBobFound] = useState(0);
const [isRunning, setIsRunning] = useState(false); const [isRunning, setIsRunning] = useState(false);
const [winner, setWinner] = useState<'alice' | 'bob' | 'tie' | null>(null); const [winner, setWinner] = useState<'alice' | 'bob' | 'tie' | null>(null);
const [simulationResults, setSimulationResults] = useState<SimulationResult>({ alice: 0, bob: 0, ties: 0 }); const [simulationResults, setSimulationResults] = useState<SimulationResult>({
alice: 0,
bob: 0,
ties: 0
});
const [hasSimulated, setHasSimulated] = useState(false); const [hasSimulated, setHasSimulated] = useState(false);
// Generate random present positions // Generate random present positions
@ -38,13 +42,19 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
// Get Alice's opening order: 1, 2, 3, ..., 100 // Get Alice's opening order: 1, 2, 3, ..., 100
const getAliceOrder = (): number[] => { const getAliceOrder = (): number[] => {
return Array.from({ length: 100 }, (_, i) => i + 1); return Array.from({
length: 100
}, (_, i) => i + 1);
}; };
// Get Bob's opening order: 1, 3, 5, ..., 99, 2, 4, 6, ..., 100 // Get Bob's opening order: 1, 3, 5, ..., 99, 2, 4, 6, ..., 100
const getBobOrder = (): number[] => { const getBobOrder = (): number[] => {
const odds = Array.from({ length: 50 }, (_, i) => i * 2 + 1); const odds = Array.from({
const evens = Array.from({ length: 50 }, (_, i) => (i + 1) * 2); length: 50
}, (_, i) => i * 2 + 1);
const evens = Array.from({
length: 50
}, (_, i) => (i + 1) * 2);
return [...odds, ...evens]; return [...odds, ...evens];
}; };
@ -52,14 +62,11 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
const simulateRound = (presents: Set<number>): 'alice' | 'bob' | 'tie' => { const simulateRound = (presents: Set<number>): 'alice' | 'bob' | 'tie' => {
const aliceOrder = getAliceOrder(); const aliceOrder = getAliceOrder();
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 < 100; 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++;
@ -70,7 +77,6 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
if (aliceCount === 26) return 'alice'; if (aliceCount === 26) return 'alice';
if (bobCount === 26) return 'bob'; if (bobCount === 26) return 'bob';
} }
return 'tie'; // Default, though this should never happen return 'tie'; // Default, though this should never happen
}; };
@ -82,34 +88,26 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
setBobOpened(new Set()); setBobOpened(new Set());
setAliceFound(0); setAliceFound(0);
setBobFound(0); setBobFound(0);
const presents = generatePresents(); const presents = generatePresents();
setPresentBoxes(presents); setPresentBoxes(presents);
const aliceOrder = getAliceOrder(); const aliceOrder = getAliceOrder();
const bobOrder = getBobOrder(); const bobOrder = getBobOrder();
let aliceCount = 0; let aliceCount = 0;
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 < 100 && !currentWinner; i++) {
await new Promise(resolve => setTimeout(resolve, 50)); // Animation delay await new Promise(resolve => setTimeout(resolve, 50)); // Animation delay
const aliceBox = aliceOrder[i]; const aliceBox = aliceOrder[i];
const bobBox = bobOrder[i]; const bobBox = bobOrder[i];
setAliceOpened(prev => new Set(prev).add(aliceBox)); setAliceOpened(prev => new Set(prev).add(aliceBox));
setBobOpened(prev => new Set(prev).add(bobBox)); setBobOpened(prev => new Set(prev).add(bobBox));
const aliceFoundPresent = presents.has(aliceBox); const aliceFoundPresent = presents.has(aliceBox);
const bobFoundPresent = presents.has(bobBox); const bobFoundPresent = presents.has(bobBox);
if (aliceFoundPresent) { if (aliceFoundPresent) {
aliceCount++; aliceCount++;
setAliceFound(aliceCount); setAliceFound(aliceCount);
} }
if (bobFoundPresent) { if (bobFoundPresent) {
bobCount++; bobCount++;
setBobFound(bobCount); setBobFound(bobCount);
@ -124,7 +122,6 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
currentWinner = 'bob'; currentWinner = 'bob';
} }
} }
setWinner(currentWinner); setWinner(currentWinner);
setIsRunning(false); setIsRunning(false);
}; };
@ -142,8 +139,11 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
// Run 100 simulations // Run 100 simulations
const runMultipleSimulations = () => { const runMultipleSimulations = () => {
const results: SimulationResult = { alice: 0, bob: 0, ties: 0 }; const results: SimulationResult = {
alice: 0,
bob: 0,
ties: 0
};
for (let i = 0; i < 100; i++) { for (let i = 0; i < 100; i++) {
const presents = generatePresents(); const presents = generatePresents();
const winner = simulateRound(presents); const winner = simulateRound(presents);
@ -155,20 +155,13 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
results.ties++; results.ties++;
} }
} }
setSimulationResults(results); setSimulationResults(results);
setHasSimulated(true); setHasSimulated(true);
}; };
// Render a grid of boxes // Render a grid of boxes
const renderGrid = ( const renderGrid = (title: string, openedBoxes: Set<number>, found: number, color: string) => {
title: string, return <div className="space-y-3">
openedBoxes: Set<number>,
found: number,
color: string
) => {
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">{title}</h3> <h3 className="font-semibold text-lg">{title}</h3>
<Badge variant="secondary" className="text-sm"> <Badge variant="secondary" className="text-sm">
@ -177,41 +170,28 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
</Badge> </Badge>
</div> </div>
<div className="grid grid-cols-10 gap-1 p-2 bg-muted/30 rounded-lg"> <div className="grid grid-cols-10 gap-1 p-2 bg-muted/30 rounded-lg">
{Array.from({ length: 100 }, (_, i) => i + 1).map((boxNum) => { {Array.from({
length: 100
}, (_, 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);
const showPresent = isOpened && hasPresent; const showPresent = isOpened && hasPresent;
return <div key={boxNum} className={`
return (
<div
key={boxNum}
className={`
aspect-square rounded border flex items-center justify-center text-xs font-medium aspect-square rounded border flex items-center justify-center text-xs font-medium
transition-all duration-200 transition-all duration-200
${isOpened ${isOpened ? showPresent ? 'bg-[#808000] text-white border-[#606000] shadow-sm' : 'bg-muted border-border text-muted-foreground' : 'bg-gray-400 border-gray-500 text-gray-700'}
? showPresent `} title={`Box ${boxNum}${hasPresent ? ' (Present!)' : ''}`}>
? 'bg-[#808000] text-white border-[#606000] shadow-sm'
: 'bg-muted border-border text-muted-foreground'
: 'bg-gray-400 border-gray-500 text-gray-700'
}
`}
title={`Box ${boxNum}${hasPresent ? ' (Present!)' : ''}`}
>
{showPresent ? <Gift className="w-3 h-3" /> : boxNum} {showPresent ? <Gift className="w-3 h-3" /> : boxNum}
</div> </div>;
);
})} })}
</div> </div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
{title === 'Alice' && 'Opens in order: 1, 2, 3, 4, 5, ...'} {title === 'Alice' && 'Opens in order: 1, 2, 3, 4, 5, ...'}
{title === 'Bob' && 'Opens odds first, then evens: 1, 3, 5, ..., 2, 4, 6, ...'} {title === 'Bob' && 'Opens odds first, then evens: 1, 3, 5, ..., 2, 4, 6, ...'}
</div> </div>
</div> </div>;
);
}; };
return <div className="space-y-6">
return (
<div className="space-y-6">
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
@ -242,37 +222,23 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
</div> </div>
{/* Winner Display */} {/* Winner Display */}
{winner && ( {winner && <Card className="bg-primary/10 border-primary/20">
<Card className="bg-primary/10 border-primary/20">
<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' {winner === 'tie' ? "🤝 It's a tie! Both found all 26 presents at the same time!" : `🎉 ${winner === 'alice' ? 'Alice' : 'Bob'} found all 26 presents first!`}
? "🤝 It's a tie! Both found all 26 presents at the same time!"
: `🎉 ${winner === 'alice' ? 'Alice' : 'Bob'} found all 26 presents first!`
}
</div> </div>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>}
)}
{/* Control Buttons */} {/* Control Buttons */}
<div className="flex gap-3 justify-center flex-wrap"> <div className="flex gap-3 justify-center flex-wrap">
<Button <Button onClick={startSimulation} disabled={isRunning} className="gap-2">
onClick={startSimulation}
disabled={isRunning}
className="gap-2"
>
<Play className="w-4 h-4" /> <Play className="w-4 h-4" />
Start Start
</Button> </Button>
<Button <Button onClick={resetSimulation} variant="outline" disabled={isRunning} className="gap-2">
onClick={resetSimulation}
variant="outline"
disabled={isRunning}
className="gap-2"
>
<RotateCw className="w-4 h-4" /> <RotateCw className="w-4 h-4" />
Reset Reset
</Button> </Button>
@ -281,20 +247,14 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
{/* Simulate 100 */} {/* Simulate 100 */}
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-center"> <div className="flex justify-center">
<Button <Button onClick={runMultipleSimulations} variant="secondary" className="gap-2" size="lg">
onClick={runMultipleSimulations}
variant="secondary"
className="gap-2"
size="lg"
>
<Zap className="w-4 h-4" /> <Zap className="w-4 h-4" />
Simulate 100 Rounds Simulate 100 Rounds
</Button> </Button>
</div> </div>
{/* Results */} {/* Results */}
{hasSimulated && ( {hasSimulated && <Card className="bg-accent/10 border-accent/20">
<Card className="bg-accent/10 border-accent/20">
<CardHeader> <CardHeader>
<CardTitle className="text-lg">Results from 100 Simulations</CardTitle> <CardTitle className="text-lg">Results from 100 Simulations</CardTitle>
</CardHeader> </CardHeader>
@ -306,7 +266,7 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
</div> </div>
<div className="text-sm font-medium">Alice Wins</div> <div className="text-sm font-medium">Alice Wins</div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
{((simulationResults.alice / 100) * 100).toFixed(1)}% {(simulationResults.alice / 100 * 100).toFixed(1)}%
</div> </div>
</div> </div>
<div className="text-center space-y-2"> <div className="text-center space-y-2">
@ -315,7 +275,7 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
</div> </div>
<div className="text-sm font-medium">Bob Wins</div> <div className="text-sm font-medium">Bob Wins</div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
{((simulationResults.bob / 100) * 100).toFixed(1)}% {(simulationResults.bob / 100 * 100).toFixed(1)}%
</div> </div>
</div> </div>
<div className="text-center space-y-2"> <div className="text-center space-y-2">
@ -324,26 +284,18 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
</div> </div>
<div className="text-sm font-medium">Ties</div> <div className="text-sm font-medium">Ties</div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
{((simulationResults.ties / 100) * 100).toFixed(1)}% {(simulationResults.ties / 100 * 100).toFixed(1)}%
</div> </div>
</div> </div>
</div> </div>
<div className="mt-4 pt-4 border-t border-border"> <div className="mt-4 pt-4 border-t border-border">
<p className="text-sm text-muted-foreground text-center"> <p className="text-sm text-muted-foreground text-center">
{simulationResults.alice > simulationResults.bob && simulationResults.alice > simulationResults.ties {simulationResults.alice > simulationResults.bob && simulationResults.alice > simulationResults.ties ? '💡 Alice tends to win more often!' : simulationResults.bob > simulationResults.alice && simulationResults.bob > simulationResults.ties ? '💡 Bob tends to win more often!' : simulationResults.ties > simulationResults.alice && simulationResults.ties > simulationResults.bob ? '💡 Ties are most common!' : '💡 The results are quite balanced!'}
? '💡 Alice tends to win more often!'
: simulationResults.bob > simulationResults.alice && simulationResults.bob > simulationResults.ties
? '💡 Bob tends to win more often!'
: simulationResults.ties > simulationResults.alice && simulationResults.ties > simulationResults.bob
? '💡 Ties are most common!'
: '💡 The results are quite balanced!'
}
</p> </p>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>}
)}
</div> </div>
{/* Insight */} {/* Insight */}
@ -361,24 +313,14 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
{/* Acknowledgement */} {/* Acknowledgement */}
<Card className="bg-muted/50"> <Card className="bg-muted/50">
<CardContent className="pt-6"> <CardContent className="pt-6">
<h4 className="font-semibold mb-2 text-sm">🙏 Acknowledgement</h4>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
This simulation was inspired by{' '} This simulation was inspired by{' '}
<a <a href="https://x.com/littmath/status/1990807150101475653" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
href="https://x.com/littmath/status/1990807150101475653"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
this tweet this tweet
</a> </a>
{' '}by Daniel Litt, which in turn is a variation of{' '} {' '}by Daniel Litt, which in turn is a variation of{' '}
<a <a href="https://gilkalai.wordpress.com/2024/09/03/test-your-intuition-56-fifteen-boxes-puzzle/" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
href="https://gilkalai.wordpress.com/2024/09/03/test-your-intuition-56-fifteen-boxes-puzzle/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
this puzzle this puzzle
</a> </a>
{' '}by Gil Kalai. {' '}by Gil Kalai.
@ -388,15 +330,7 @@ const PresentsPuzzle = ({ showSocialShare = false, shareUrl }: PresentsPuzzlePro
</CardContent> </CardContent>
</Card> </Card>
{showSocialShare && ( {showSocialShare && <SocialShare url={shareUrl || window.location.href} title="The Presents Puzzle" description="A probability puzzle about search strategies and expected value" />}
<SocialShare </div>;
url={shareUrl || window.location.href}
title="The Presents Puzzle"
description="A probability puzzle about search strategies and expected value"
/>
)}
</div>
);
}; };
export default PresentsPuzzle; export default PresentsPuzzle;