Add Craps game interactive
This commit is contained in:
parent
f72073358b
commit
36d3b20bff
3 changed files with 277 additions and 0 deletions
257
src/components/CrapsGame.tsx
Normal file
257
src/components/CrapsGame.tsx
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Dice1, Dice2, Dice3, Dice4, Dice5, Dice6, RotateCcw } 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" />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 craps rules
|
||||||
|
if (sum === 2 || sum === 3 || sum === 12) {
|
||||||
|
setGameResult('lose');
|
||||||
|
} else if (sum === 7 || sum === 11) {
|
||||||
|
setGameResult('win');
|
||||||
|
} else {
|
||||||
|
setGameResult('continue');
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsRolling(false);
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetGame = () => {
|
||||||
|
setDice1(null);
|
||||||
|
setDice2(null);
|
||||||
|
setGameResult(null);
|
||||||
|
setRollCount(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
case 'lose':
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
case 'continue':
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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: First Throw</CardTitle>
|
||||||
|
<CardDescription className="text-lg">
|
||||||
|
Roll two dice and see what happens on the opening throw of craps!
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-6">
|
||||||
|
{/* Rules */}
|
||||||
|
<Card className="bg-muted/50">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl">Rules</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
<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>Rolling 2, 3, or 12</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<Badge variant="default" className="mb-2 bg-green-600">Immediate Win</Badge>
|
||||||
|
<p>Rolling 7 or 11</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<Badge variant="secondary" className="mb-2">Game Continues</Badge>
|
||||||
|
<p>Rolling 4, 5, 6, 8, 9, or 10</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Dice Display */}
|
||||||
|
<div className="flex justify-center items-center gap-8">
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<div className={`p-4 rounded-lg border-2 ${isRolling ? 'animate-spin' : ''} ${dice1 ? 'border-primary' : 'border-muted'}`}>
|
||||||
|
{dice1 ? getDiceIcon(dice1) : <div className="w-12 h-12 bg-muted rounded border-2 border-dashed"></div>}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">Die 1</p>
|
||||||
|
{dice1 && <p className="font-bold text-lg">{dice1}</p>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-2xl font-bold">+</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<div className={`p-4 rounded-lg border-2 ${isRolling ? 'animate-spin' : ''} ${dice2 ? 'border-primary' : 'border-muted'}`}>
|
||||||
|
{dice2 ? getDiceIcon(dice2) : <div className="w-12 h-12 bg-muted rounded border-2 border-dashed"></div>}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">Die 2</p>
|
||||||
|
{dice2 && <p className="font-bold text-lg">{dice2}</p>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-2xl font-bold">=</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">Total</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Result */}
|
||||||
|
{getResultMessage()}
|
||||||
|
|
||||||
|
{/* Statistics */}
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-sm text-muted-foreground">Rolls: {rollCount}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Controls */}
|
||||||
|
<div className="flex justify-center gap-4">
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<RotateCcw className="w-4 h-4 mr-2" />
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Educational Information */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl">About Craps</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Craps is a classic casino dice game that demonstrates interesting probability concepts.
|
||||||
|
The first throw (called the "come out roll") immediately determines the outcome in certain cases:
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold mb-2">Losing Numbers (Craps)</h4>
|
||||||
|
<ul className="space-y-1">
|
||||||
|
<li>• 2 (Snake Eyes): 1/36 probability</li>
|
||||||
|
<li>• 3: 2/36 probability</li>
|
||||||
|
<li>• 12 (Boxcars): 1/36 probability</li>
|
||||||
|
<li><strong>Total losing probability: 4/36 = 11.1%</strong></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold mb-2">Winning Numbers (Natural)</h4>
|
||||||
|
<ul className="space-y-1">
|
||||||
|
<li>• 7: 6/36 probability</li>
|
||||||
|
<li>• 11: 2/36 probability</li>
|
||||||
|
<li><strong>Total winning probability: 8/36 = 22.2%</strong></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
If you roll 4, 5, 6, 8, 9, or 10, that becomes your "point" and the game continues with different rules.
|
||||||
|
</p>
|
||||||
|
</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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CrapsGame;
|
||||||
|
|
@ -12,6 +12,7 @@ import GoldCoinGame from '@/components/GoldCoinGame';
|
||||||
import GuessingGame from '@/components/GuessingGame';
|
import GuessingGame from '@/components/GuessingGame';
|
||||||
import SubtractionGame from '@/components/SubtractionGame';
|
import SubtractionGame from '@/components/SubtractionGame';
|
||||||
import ParityBitsGame from '@/components/ParityBitsGame';
|
import ParityBitsGame from '@/components/ParityBitsGame';
|
||||||
|
import CrapsGame from '@/components/CrapsGame';
|
||||||
|
|
||||||
interface Game {
|
interface Game {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -22,6 +23,13 @@ interface Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
const games: Game[] = [
|
const games: Game[] = [
|
||||||
|
{
|
||||||
|
id: 'craps-game',
|
||||||
|
title: 'Craps: First Throw',
|
||||||
|
description: 'Experience the classic casino dice game! Roll two dice and learn about probability as you discover the rules of the opening throw.',
|
||||||
|
tags: ['probability', 'dice', 'casino', 'mathematics', 'statistics', 'gambling'],
|
||||||
|
component: CrapsGame
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'parity-magic',
|
id: 'parity-magic',
|
||||||
title: 'Parity Magic',
|
title: 'Parity Magic',
|
||||||
|
|
|
||||||
12
src/pages/CrapsGamePage.tsx
Normal file
12
src/pages/CrapsGamePage.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Layout from "@/components/Layout";
|
||||||
|
import CrapsGame from "@/components/CrapsGame";
|
||||||
|
|
||||||
|
const CrapsGamePage = () => {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<CrapsGame showSocialShare={true} />
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CrapsGamePage;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue