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