Visual edit in Lovable

This commit is contained in:
gpt-engineer-app[bot] 2025-09-05 06:15:52 +00:00
parent e9d524df70
commit 095c7ccbf7

View file

@ -8,36 +8,42 @@ import { Switch } from '@/components/ui/switch';
import { Slider } from '@/components/ui/slider';
import { Dice1, Dice2, Dice3, Dice4, Dice5, Dice6, RotateCcw, Settings, Play } from 'lucide-react';
import SocialShare from '@/components/SocialShare';
interface CrapsGameProps {
showSocialShare?: boolean;
}
const getDiceIcon = (value: number) => {
switch (value) {
case 1: return <Dice1 className="w-12 h-12" />;
case 2: return <Dice2 className="w-12 h-12" />;
case 3: return <Dice3 className="w-12 h-12" />;
case 4: return <Dice4 className="w-12 h-12" />;
case 5: return <Dice5 className="w-12 h-12" />;
case 6: return <Dice6 className="w-12 h-12" />;
default: return <Dice1 className="w-12 h-12" />;
case 1:
return <Dice1 className="w-12 h-12" />;
case 2:
return <Dice2 className="w-12 h-12" />;
case 3:
return <Dice3 className="w-12 h-12" />;
case 4:
return <Dice4 className="w-12 h-12" />;
case 5:
return <Dice5 className="w-12 h-12" />;
case 6:
return <Dice6 className="w-12 h-12" />;
default:
return <Dice1 className="w-12 h-12" />;
}
};
const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
const CrapsGame: React.FC<CrapsGameProps> = ({
showSocialShare = false
}) => {
const [dice1, setDice1] = useState<number | null>(null);
const [dice2, setDice2] = useState<number | null>(null);
const [isRolling, setIsRolling] = useState(false);
const [gameResult, setGameResult] = useState<'win' | 'lose' | 'continue' | null>(null);
const [rollCount, setRollCount] = useState(0);
// Custom rules state
const [customWinSums, setCustomWinSums] = useState<number[]>([7, 11]);
const [customLoseSums, setCustomLoseSums] = useState<number[]>([2, 3, 12]);
const [isCustomRules, setIsCustomRules] = useState(false);
const [isRulesModalOpen, setIsRulesModalOpen] = useState(false);
// Simulation state
const [simulationRounds, setSimulationRounds] = useState([10]);
const [isSimulating, setIsSimulating] = useState(false);
@ -55,25 +61,22 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
totalTurns: number;
completedGames: number;
} | null>(null);
const rollDice = () => {
setIsRolling(true);
setGameResult(null);
// Simulate rolling animation
setTimeout(() => {
const newDice1 = Math.floor(Math.random() * 6) + 1;
const newDice2 = Math.floor(Math.random() * 6) + 1;
const sum = newDice1 + newDice2;
setDice1(newDice1);
setDice2(newDice2);
setRollCount(prev => prev + 1);
// Determine game result based on current rules
const winSums = isCustomRules ? customWinSums : [7, 11];
const loseSums = isCustomRules ? customLoseSums : [2, 3, 12];
if (loseSums.includes(sum)) {
setGameResult('lose');
} else if (winSums.includes(sum)) {
@ -81,29 +84,36 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
} else {
setGameResult('continue');
}
setIsRolling(false);
}, 1000);
};
const simulateGame = () => {
const winSums = isCustomRules ? customWinSums : [7, 11];
const loseSums = isCustomRules ? customLoseSums : [2, 3, 12];
let turns = 1;
let point: number | null = null;
while (true) {
const die1 = Math.floor(Math.random() * 6) + 1;
const die2 = Math.floor(Math.random() * 6) + 1;
const sum = die1 + die2;
if (point === null) {
// First roll (come out roll)
if (loseSums.includes(sum)) {
return { result: 'lose', turns, die1, die2, sum };
return {
result: 'lose',
turns,
die1,
die2,
sum
};
} else if (winSums.includes(sum)) {
return { result: 'win', turns, die1, die2, sum };
return {
result: 'win',
turns,
die1,
die2,
sum
};
} else {
point = sum;
turns++;
@ -111,50 +121,63 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
} else {
// Subsequent rolls
if (sum === point) {
return { result: 'win', turns, die1, die2, sum };
return {
result: 'win',
turns,
die1,
die2,
sum
};
} else if (sum === 7) {
return { result: 'lose', turns, die1, die2, sum };
return {
result: 'lose',
turns,
die1,
die2,
sum
};
} else {
turns++;
}
}
}
};
const simulateGameSlow = async (gameNumber: number) => {
const winSums = isCustomRules ? customWinSums : [7, 11];
const loseSums = isCustomRules ? customLoseSums : [2, 3, 12];
let turns = 1;
let point: number | null = null;
while (true) {
// Show rolling animation
setIsRolling(true);
await new Promise(resolve => setTimeout(resolve, 500));
const die1 = Math.floor(Math.random() * 6) + 1;
const die2 = Math.floor(Math.random() * 6) + 1;
const sum = die1 + die2;
// Update dice display
setDice1(die1);
setDice2(die2);
setIsRolling(false);
// Wait to show the result
await new Promise(resolve => setTimeout(resolve, 800));
if (point === null) {
// First roll (come out roll)
if (loseSums.includes(sum)) {
setGameResult('lose');
await new Promise(resolve => setTimeout(resolve, 1000));
return { result: 'lose', turns };
return {
result: 'lose',
turns
};
} else if (winSums.includes(sum)) {
setGameResult('win');
await new Promise(resolve => setTimeout(resolve, 1000));
return { result: 'win', turns };
return {
result: 'win',
turns
};
} else {
setGameResult('continue');
point = sum;
@ -166,11 +189,17 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
if (sum === point) {
setGameResult('win');
await new Promise(resolve => setTimeout(resolve, 1000));
return { result: 'win', turns };
return {
result: 'win',
turns
};
} else if (sum === 7) {
setGameResult('lose');
await new Promise(resolve => setTimeout(resolve, 1000));
return { result: 'lose', turns };
return {
result: 'lose',
turns
};
} else {
setGameResult('continue');
turns++;
@ -179,7 +208,6 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
}
}
};
const runSimulation = async () => {
setIsSimulating(true);
setCurrentSimulationGame(0);
@ -188,20 +216,23 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
let wins = 0;
let losses = 0;
let totalTurns = 0;
if (slowMode) {
// Initialize live stats for slow mode
setLiveSimulationStats({ wins: 0, losses: 0, totalTurns: 0, completedGames: 0 });
setLiveSimulationStats({
wins: 0,
losses: 0,
totalTurns: 0,
completedGames: 0
});
// Slow mode: simulate visually
for (let i = 0; i < numGames; i++) {
setCurrentSimulationGame(i + 1);
setGameResult(null);
const game = await simulateGameSlow(i + 1);
if (game.result === 'win') wins++;
else losses++;
if (game.result === 'win') wins++;else losses++;
totalTurns += game.turns;
// Update live stats
setLiveSimulationStats({
wins,
@ -209,7 +240,7 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
totalTurns,
completedGames: i + 1
});
// Brief pause between games
await new Promise(resolve => setTimeout(resolve, 500));
}
@ -217,10 +248,9 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
// Fast mode: simulate in background
for (let i = 0; i < numGames; i++) {
const game = simulateGame();
if (game.result === 'win') wins++;
else losses++;
if (game.result === 'win') wins++;else losses++;
totalTurns += game.turns;
// Add a small delay every 10 games to show progress
if (i % 10 === 0) {
setCurrentSimulationGame(i + 1);
@ -228,7 +258,6 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
}
}
}
setSimulationResults({
wins,
losses,
@ -239,66 +268,52 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
setLiveSimulationStats(null);
setIsSimulating(false);
};
const resetGame = () => {
setDice1(null);
setDice2(null);
setGameResult(null);
setSimulationResults(null);
};
const sum = dice1 && dice2 ? dice1 + dice2 : null;
const getResultMessage = () => {
if (!gameResult) return null;
switch (gameResult) {
case 'win':
return (
<div className="text-center space-y-2">
return <div className="text-center space-y-2">
<Badge variant="default" className="bg-green-600 text-white text-lg px-4 py-2">
🎉 You Win! 🎉
</Badge>
<p className="text-sm text-muted-foreground">
Rolling {sum} on the first throw wins immediately!
</p>
</div>
);
</div>;
case 'lose':
return (
<div className="text-center space-y-2">
return <div className="text-center space-y-2">
<Badge variant="destructive" className="text-lg px-4 py-2">
💸 You Lose! 💸
</Badge>
<p className="text-sm text-muted-foreground">
Rolling {sum} on the first throw loses immediately!
</p>
</div>
);
</div>;
case 'continue':
return (
<div className="text-center space-y-2">
return <div className="text-center space-y-2">
<Badge variant="secondary" className="text-lg px-4 py-2">
🎲 Game Continues 🎲
</Badge>
<p className="text-sm text-muted-foreground">
Your point is {sum}. Keep playing to win or lose!
</p>
</div>
);
</div>;
default:
return null;
}
};
return (
<div className="max-w-4xl mx-auto p-6 space-y-6">
return <div className="max-w-4xl mx-auto p-6 space-y-6">
<Card>
<CardHeader className="text-center">
<CardTitle className="text-3xl font-bold text-foreground">Craps: An Exploration</CardTitle>
<CardDescription className="text-lg">
Roll two dice and see what happens on the opening throw of craps!
</CardDescription>
<CardDescription className="text-lg">In the game of Craps, we roll two dice, and note the sum S. In the traditional game, if S is 2, 3, or 12, you lose immediately; if the S is 7 or 11, you win immediately, and the game continues otherwise.</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Simulation Controls */}
@ -312,52 +327,27 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
</div>
<div className="flex items-center space-x-2">
<label htmlFor="slow-mode" className="text-sm font-medium">Slow Mode</label>
<Switch
id="slow-mode"
checked={slowMode}
onCheckedChange={setSlowMode}
/>
<Switch id="slow-mode" checked={slowMode} onCheckedChange={setSlowMode} />
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Number of games: {simulationRounds[0]}</label>
<Slider
value={simulationRounds}
onValueChange={setSimulationRounds}
max={25}
min={1}
step={1}
className="w-full"
/>
<Slider value={simulationRounds} onValueChange={setSimulationRounds} max={25} min={1} step={1} className="w-full" />
</div>
<Button
onClick={runSimulation}
disabled={isSimulating}
className="w-full gap-2"
>
<Button onClick={runSimulation} disabled={isSimulating} className="w-full gap-2">
<Play className="w-4 h-4" />
{isSimulating
? (slowMode
? `Simulating Game ${currentSimulationGame}/${simulationRounds[0]}...`
: 'Simulating...'
)
: 'Simulate'
}
{isSimulating ? slowMode ? `Simulating Game ${currentSimulationGame}/${simulationRounds[0]}...` : 'Simulating...' : 'Simulate'}
</Button>
{(simulationResults || liveSimulationStats) && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4 p-4 bg-muted/50 rounded-lg">
{(simulationResults || liveSimulationStats) && <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4 p-4 bg-muted/50 rounded-lg">
<div className="text-center">
<div className="text-2xl font-bold text-green-600">
{liveSimulationStats ? liveSimulationStats.wins : simulationResults!.wins}
</div>
<div className="text-sm text-muted-foreground">Wins</div>
<div className="text-xs">
{liveSimulationStats
? `(${((liveSimulationStats.wins / Math.max(liveSimulationStats.completedGames, 1)) * 100).toFixed(1)}%)`
: `(${((simulationResults!.wins / simulationResults!.totalGames) * 100).toFixed(1)}%)`
}
{liveSimulationStats ? `(${(liveSimulationStats.wins / Math.max(liveSimulationStats.completedGames, 1) * 100).toFixed(1)}%)` : `(${(simulationResults!.wins / simulationResults!.totalGames * 100).toFixed(1)}%)`}
</div>
</div>
<div className="text-center">
@ -366,26 +356,19 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
</div>
<div className="text-sm text-muted-foreground">Losses</div>
<div className="text-xs">
{liveSimulationStats
? `(${((liveSimulationStats.losses / Math.max(liveSimulationStats.completedGames, 1)) * 100).toFixed(1)}%)`
: `(${((simulationResults!.losses / simulationResults!.totalGames) * 100).toFixed(1)}%)`
}
{liveSimulationStats ? `(${(liveSimulationStats.losses / Math.max(liveSimulationStats.completedGames, 1) * 100).toFixed(1)}%)` : `(${(simulationResults!.losses / simulationResults!.totalGames * 100).toFixed(1)}%)`}
</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-blue-600">
{liveSimulationStats
? (liveSimulationStats.totalTurns / Math.max(liveSimulationStats.completedGames, 1)).toFixed(1)
: simulationResults!.averageTurns.toFixed(1)
}
{liveSimulationStats ? (liveSimulationStats.totalTurns / Math.max(liveSimulationStats.completedGames, 1)).toFixed(1) : simulationResults!.averageTurns.toFixed(1)}
</div>
<div className="text-sm text-muted-foreground">Avg Turns</div>
<div className="text-xs">
{liveSimulationStats ? `(${liveSimulationStats.completedGames} games)` : 'per game'}
</div>
</div>
</div>
)}
</div>}
</CardContent>
</Card>
@ -413,66 +396,53 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
<div>
<h3 className="font-semibold mb-4 text-red-600">Loss Sums</h3>
<div className="grid grid-cols-3 gap-2">
{Array.from({ length: 11 }, (_, i) => i + 2).map((sum) => (
<div key={`loss-${sum}`} className="flex items-center space-x-2">
<Checkbox
id={`loss-${sum}`}
checked={customLoseSums.includes(sum)}
onCheckedChange={(checked) => {
if (checked) {
setCustomLoseSums([...customLoseSums.filter(s => s !== sum), sum]);
setCustomWinSums(customWinSums.filter(s => s !== sum));
} else {
setCustomLoseSums(customLoseSums.filter(s => s !== sum));
}
}}
/>
{Array.from({
length: 11
}, (_, i) => i + 2).map(sum => <div key={`loss-${sum}`} className="flex items-center space-x-2">
<Checkbox id={`loss-${sum}`} checked={customLoseSums.includes(sum)} onCheckedChange={checked => {
if (checked) {
setCustomLoseSums([...customLoseSums.filter(s => s !== sum), sum]);
setCustomWinSums(customWinSums.filter(s => s !== sum));
} else {
setCustomLoseSums(customLoseSums.filter(s => s !== sum));
}
}} />
<label htmlFor={`loss-${sum}`} className="text-sm">{sum}</label>
</div>
))}
</div>)}
</div>
</div>
<div>
<h3 className="font-semibold mb-4 text-green-600">Win Sums</h3>
<div className="grid grid-cols-3 gap-2">
{Array.from({ length: 11 }, (_, i) => i + 2).map((sum) => (
<div key={`win-${sum}`} className="flex items-center space-x-2">
<Checkbox
id={`win-${sum}`}
checked={customWinSums.includes(sum)}
onCheckedChange={(checked) => {
if (checked) {
setCustomWinSums([...customWinSums.filter(s => s !== sum), sum]);
setCustomLoseSums(customLoseSums.filter(s => s !== sum));
} else {
setCustomWinSums(customWinSums.filter(s => s !== sum));
}
}}
/>
{Array.from({
length: 11
}, (_, i) => i + 2).map(sum => <div key={`win-${sum}`} className="flex items-center space-x-2">
<Checkbox id={`win-${sum}`} checked={customWinSums.includes(sum)} onCheckedChange={checked => {
if (checked) {
setCustomWinSums([...customWinSums.filter(s => s !== sum), sum]);
setCustomLoseSums(customLoseSums.filter(s => s !== sum));
} else {
setCustomWinSums(customWinSums.filter(s => s !== sum));
}
}} />
<label htmlFor={`win-${sum}`} className="text-sm">{sum}</label>
</div>
))}
</div>)}
</div>
</div>
</div>
<div className="flex justify-between gap-4">
<Button
variant="outline"
onClick={() => {
setCustomWinSums([7, 11]);
setCustomLoseSums([2, 3, 12]);
setIsCustomRules(false);
}}
>
<Button variant="outline" onClick={() => {
setCustomWinSums([7, 11]);
setCustomLoseSums([2, 3, 12]);
setIsCustomRules(false);
}}>
Reset to Classic
</Button>
<Button
onClick={() => {
setIsCustomRules(true);
setIsRulesModalOpen(false);
resetGame();
}}
>
<Button onClick={() => {
setIsCustomRules(true);
setIsRulesModalOpen(false);
resetGame();
}}>
Apply Custom Rules
</Button>
</div>
@ -483,11 +453,11 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
<div className="text-center">
<Badge variant="destructive" className="mb-2">Immediate Loss</Badge>
<p>{isCustomRules ? (customLoseSums.length > 0 ? customLoseSums.join(', ') : 'None') : 'Rolling 2, 3, or 12'}</p>
<p>{isCustomRules ? customLoseSums.length > 0 ? customLoseSums.join(', ') : 'None' : 'Rolling 2, 3, or 12'}</p>
</div>
<div className="text-center">
<Badge variant="default" className="mb-2 bg-green-600">Immediate Win</Badge>
<p>{isCustomRules ? (customWinSums.length > 0 ? customWinSums.join(', ') : 'None') : 'Rolling 7 or 11'}</p>
<p>{isCustomRules ? customWinSums.length > 0 ? customWinSums.join(', ') : 'None' : 'Rolling 7 or 11'}</p>
</div>
<div className="text-center">
<Badge variant="secondary" className="mb-2">Game Continues</Badge>
@ -525,11 +495,7 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
<div className="text-center space-y-2">
<div className={`p-4 rounded-lg border-2 min-w-[80px] ${sum ? 'border-primary bg-primary/10' : 'border-muted'}`}>
{sum ? (
<div className="text-3xl font-bold text-primary">{sum}</div>
) : (
<div className="w-12 h-12 bg-muted rounded border-2 border-dashed mx-auto"></div>
)}
{sum ? <div className="text-3xl font-bold text-primary">{sum}</div> : <div className="w-12 h-12 bg-muted rounded border-2 border-dashed mx-auto"></div>}
</div>
<p className="text-sm text-muted-foreground">Total</p>
</div>
@ -545,18 +511,10 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
{/* Controls */}
<div className="flex justify-center gap-4">
<Button
onClick={rollDice}
disabled={isRolling}
className="px-8 py-3 text-lg"
>
<Button onClick={rollDice} disabled={isRolling} className="px-8 py-3 text-lg">
{isRolling ? 'Rolling...' : 'Roll Dice'}
</Button>
<Button
variant="outline"
onClick={resetGame}
className="px-8 py-3"
>
<Button variant="outline" onClick={resetGame} className="px-8 py-3">
<RotateCcw className="w-4 h-4 mr-2" />
Reset
</Button>
@ -599,17 +557,9 @@ const CrapsGame: React.FC<CrapsGameProps> = ({ showSocialShare = false }) => {
</CardContent>
</Card>
{showSocialShare && (
<div className="flex justify-center">
<SocialShare
title="Craps: First Throw"
description="Learn about probability through the classic casino game of craps!"
url={`${window.location.origin}/craps-game`}
/>
</div>
)}
</div>
);
{showSocialShare && <div className="flex justify-center">
<SocialShare title="Craps: First Throw" description="Learn about probability through the classic casino game of craps!" url={`${window.location.origin}/craps-game`} />
</div>}
</div>;
};
export default CrapsGame;