slots / index.html
Karmastudios's picture
written in react i want a 3 reel slot machine that uses this logic Terms to understand
68ca7b4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crane Slot Machine</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/animejs/lib/anime.iife.min.js"></script>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Luckiest+Guy&display=swap" rel="stylesheet">
<style>
.crane-bg {
background-image: url('https://images.unsplash.com/photo-1605106702734-205df224ecce?q=80&w=2070');
background-size: cover;
background-position: center;
}
.slot-machine {
background: linear-gradient(135deg, #ff0000 0%, #990000 100%);
box-shadow: 0 0 30px rgba(255, 215, 0, 0.7);
}
.reel {
background: linear-gradient(180deg, #f8f8f8 0%, #e0e0e0 100%);
box-shadow: inset 0 0 10px rgba(0,0,0,0.5);
}
.symbol {
font-family: 'Luckiest Guy', cursive;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
.bonus-game {
background: rgba(0,0,0,0.8);
backdrop-filter: blur(5px);
}
.crane-claw {
transition: all 0.3s ease;
}
.win-animation {
animation: pulse 0.5s infinite alternate;
}
@keyframes pulse {
from { transform: scale(1); }
to { transform: scale(1.05); }
}
</style>
</head>
<body class="crane-bg min-h-screen flex items-center justify-center p-4">
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef } = React;
const SlotMachine = () => {
const [reels, setReels] = useState([[],[],[]]);
const [spinning, setSpinning] = useState(false);
const [balance, setBalance] = useState(1000);
const [bet, setBet] = useState(10);
const [winAmount, setWinAmount] = useState(0);
const [bonusGame, setBonusGame] = useState(false);
const [bonusPrize, setBonusPrize] = useState(null);
const [walletConnected, setWalletConnected] = useState(false);
const [walletAddress, setWalletAddress] = useState('');
const [provider, setProvider] = useState(null);
const reelSymbols = ['πŸ’', 'πŸ‹', '🍊', 'πŸ‡', 'πŸ””', '7️⃣', 'πŸ’°', '🎁'];
const reelStops = 64;
const bonusItems = [
{ id: 1, value: 50, position: { x: 20, y: 70 } },
{ id: 2, value: 100, position: { x: 50, y: 70 } },
{ id: 3, value: 200, position: { x: 80, y: 70 } },
{ id: 4, value: 'JACKPOT', position: { x: 35, y: 60 } },
{ id: 5, value: 75, position: { x: 65, y: 60 } }
];
const clawPosition = useRef({ x: 50, y: 10 });
const clawGrabbing = useRef(false);
const clawDropping = useRef(false);
// Initialize reels
useEffect(() => {
const initialReels = [[], [], []];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < reelStops; j++) {
initialReels[i].push(reelSymbols[Math.floor(Math.random() * reelSymbols.length)]);
}
}
setReels(initialReels);
}, []);
const connectWallet = async () => {
if (window.ethereum) {
try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send("eth_requestAccounts", []);
const signer = provider.getSigner();
const address = await signer.getAddress();
setProvider(provider);
setWalletAddress(`${address.substring(0, 6)}...${address.substring(address.length - 4)}`);
setWalletConnected(true);
} catch (error) {
console.error("Error connecting wallet:", error);
}
} else {
alert("Please install MetaMask or another Ethereum wallet!");
}
};
const spin = () => {
if (spinning || balance < bet) return;
setSpinning(true);
setWinAmount(0);
setBalance(prev => prev - bet);
// Simulate spinning animation
const spinDuration = 2000;
const spinInterval = 100;
const spins = spinDuration / spinInterval;
let spinCount = 0;
const spinIntervalId = setInterval(() => {
const newReels = [...reels];
for (let i = 0; i < 3; i++) {
const randomIndex = Math.floor(Math.random() * reelStops);
newReels[i] = [...newReels[i].slice(randomIndex), ...newReels[i].slice(0, randomIndex)];
}
setReels(newReels);
spinCount++;
if (spinCount >= spins) {
clearInterval(spinIntervalId);
checkWin();
}
}, spinInterval);
};
const checkWin = () => {
// Get the middle symbols of each reel
const results = [
reels[0][Math.floor(reelStops / 2)],
reels[1][Math.floor(reelStops / 2)],
reels[2][Math.floor(reelStops / 2)]
];
// Simple win logic - adjust as needed
let win = 0;
// Check for three matching symbols
if (results[0] === results[1] && results[1] === results[2]) {
switch(results[0]) {
case 'πŸ’': win = bet * 5; break;
case 'πŸ‹': win = bet * 10; break;
case '🍊': win = bet * 15; break;
case 'πŸ‡': win = bet * 20; break;
case 'πŸ””': win = bet * 25; break;
case '7️⃣': win = bet * 50; break;
case 'πŸ’°': win = bet * 100; break;
case '🎁':
win = bet * 5;
// Trigger bonus game
setTimeout(() => {
setBonusGame(true);
}, 1500);
break;
}
}
// Check for two matching symbols
else if (results[0] === results[1] || results[1] === results[2] || results[0] === results[2]) {
win = bet * 2;
}
setWinAmount(win);
if (win > 0) {
setBalance(prev => prev + win);
}
setSpinning(false);
};
const moveClaw = (direction) => {
if (clawGrabbing.current || clawDropping.current) return;
switch(direction) {
case 'left':
if (clawPosition.current.x > 10) clawPosition.current.x -= 5;
break;
case 'right':
if (clawPosition.current.x < 90) clawPosition.current.x += 5;
break;
case 'down':
if (!clawGrabbing.current && clawPosition.current.y < 80) {
clawGrabbing.current = true;
let y = clawPosition.current.y;
const dropInterval = setInterval(() => {
y += 2;
clawPosition.current.y = y;
if (y >= 70) {
clearInterval(dropInterval);
grabPrize();
}
}, 50);
}
break;
}
};
const grabPrize = () => {
// Find which prize is under the claw
const clawX = clawPosition.current.x;
const clawY = clawPosition.current.y;
const prize = bonusItems.find(item => {
return Math.abs(item.position.x - clawX) < 10 &&
Math.abs(item.position.y - clawY) < 10;
});
if (prize) {
setBonusPrize(prize);
// Animate claw going back up with prize
clawDropping.current = true;
let y = clawPosition.current.y;
const riseInterval = setInterval(() => {
y -= 2;
clawPosition.current.y = y;
if (y <= 10) {
clearInterval(riseInterval);
clawGrabbing.current = false;
clawDropping.current = false;
// Award prize
if (prize.value === 'JACKPOT') {
setWinAmount(bet * 500);
setBalance(prev => prev + bet * 500);
} else {
setWinAmount(prize.value);
setBalance(prev => prev + prize.value);
}
// Close bonus game after delay
setTimeout(() => {
setBonusGame(false);
setBonusPrize(null);
clawPosition.current = { x: 50, y: 10 };
}, 2000);
}
}, 50);
} else {
// No prize grabbed, return to top
clawGrabbing.current = false;
let y = clawPosition.current.y;
const riseInterval = setInterval(() => {
y -= 2;
clawPosition.current.y = y;
if (y <= 10) {
clearInterval(riseInterval);
}
}, 50);
}
};
return (
<div className="relative w-full max-w-3xl">
{/* Main Slot Machine */}
<div className={`slot-machine rounded-2xl p-6 shadow-2xl ${bonusGame ? 'opacity-30' : ''}`}>
<div className="flex justify-between items-center mb-6">
<div className="bg-black bg-opacity-70 text-yellow-400 px-4 py-2 rounded-lg">
<span className="font-bold">BALANCE: </span>
<span className="text-white">{balance}</span>
</div>
{walletConnected ? (
<div className="bg-green-700 text-white px-4 py-2 rounded-lg flex items-center">
<i data-feather="check-circle" className="mr-2"></i>
{walletAddress}
</div>
) : (
<button
onClick={connectWallet}
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center"
>
<i data-feather="wallet" className="mr-2"></i>
Connect Wallet
</button>
)}
</div>
{/* Reels */}
<div className="flex justify-center mb-6">
{[0, 1, 2].map((reelIndex) => (
<div key={reelIndex} className="reel mx-1 w-24 h-48 rounded-lg overflow-hidden relative">
<div className={`absolute w-full transition-transform duration-100 ${spinning ? 'animate-spin' : ''}`}>
{reels[reelIndex]?.map((symbol, index) => (
<div
key={index}
className="symbol flex items-center justify-center text-4xl h-16 border-b border-gray-300"
>
{symbol}
</div>
))}
</div>
</div>
))}
</div>
{/* Controls */}
<div className="flex justify-between items-center">
<div className="bg-black bg-opacity-70 text-white px-4 py-2 rounded-lg">
<div className="flex items-center">
<span className="mr-2">BET:</span>
<button
onClick={() => setBet(Math.max(1, bet - 1))}
className="bg-gray-600 hover:bg-gray-700 w-8 h-8 rounded-l flex items-center justify-center"
>
-
</button>
<div className="bg-gray-800 w-12 text-center">{bet}</div>
<button
onClick={() => setBet(bet + 1)}
className="bg-gray-600 hover:bg-gray-700 w-8 h-8 rounded-r flex items-center justify-center"
>
+
</button>
</div>
</div>
<button
onClick={spin}
disabled={spinning || balance < bet}
className={`spin-button bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-8 rounded-full text-xl flex items-center ${spinning ? 'opacity-50' : ''}`}
>
<i data-feather="rotate-cw" className="mr-2"></i>
SPIN
</button>
<div className={`bg-black bg-opacity-70 text-green-400 px-4 py-2 rounded-lg transition-opacity ${winAmount > 0 ? 'opacity-100 win-animation' : 'opacity-0'}`}>
WIN: {winAmount}
</div>
</div>
</div>
{/* Bonus Game */}
{bonusGame && (
<div className="bonus-game absolute inset-0 flex flex-col items-center justify-center rounded-2xl p-6">
<h2 className="text-yellow-400 text-3xl font-bold mb-4">BONUS GAME!</h2>
<p className="text-white mb-6">Use the controls to grab a prize!</p>
<div className="relative w-full h-64 bg-gray-900 rounded-lg mb-4">
{/* Crane Claw */}
<div
className="crane-claw absolute w-8 h-8 bg-red-600 rounded-full z-10"
style={{
left: `${clawPosition.current.x}%`,
top: `${clawPosition.current.y}%`,
transform: 'translate(-50%, -50%)'
}}
>
<div className="absolute w-1 h-12 bg-gray-700 -top-12 left-1/2 transform -translate-x-1/2"></div>
</div>
{/* Bonus Items */}
{bonusItems.map(item => (
<div
key={item.id}
className={`absolute w-12 h-12 flex items-center justify-center text-2xl rounded-lg ${bonusPrize?.id === item.id ? 'bg-yellow-400' : 'bg-blue-500'}`}
style={{
left: `${item.position.x}%`,
top: `${item.position.y}%`,
transform: 'translate(-50%, -50%)'
}}
>
{item.value === 'JACKPOT' ? 'πŸ’°' : item.value}
</div>
))}
</div>
{/* Crane Controls */}
<div className="flex space-x-4">
<button
onClick={() => moveClaw('left')}
className="bg-gray-700 hover:bg-gray-600 text-white w-12 h-12 rounded-full flex items-center justify-center"
>
<i data-feather="arrow-left"></i>
</button>
<button
onClick={() => moveClaw('down')}
className="bg-red-600 hover:bg-red-700 text-white w-12 h-12 rounded-full flex items-center justify-center"
>
<i data-feather="arrow-down"></i>
</button>
<button
onClick={() => moveClaw('right')}
className="bg-gray-700 hover:bg-gray-600 text-white w-12 h-12 rounded-full flex items-center justify-center"
>
<i data-feather="arrow-right"></i>
</button>
</div>
</div>
)}
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<SlotMachine />);
// Initialize feather icons
feather.replace();
</script>
</body>
</html>
This implementation includes:
1. A 3-reel slot machine with animated spinning reels
2. Crane machine theme with appropriate styling
3. Bonus game triggered by landing on the gift symbol
4. Wallet integration using MetaMask/Web3
5. Bet adjustment controls
6. Win animations and balance tracking
7. Responsive design using TailwindCSS
8. Interactive crane controls for the bonus game
The slot machine features:
- 8 different symbols with varying payouts
- Random reel stops generation
- Win detection logic (both for regular spins and bonus game)
- Balance tracking with bet adjustments
- Ethereum wallet integration for cryptocurrency transactions
The bonus game allows players to control a crane to grab prizes when triggered. The game includes a jackpot and several other prize values.
Note: For a production environment, you would want to:
1. Implement proper server-side RNG for fairness
2. Add more sophisticated win detection logic
3. Include proper wallet transaction handling
4. Add sound effects
5. Implement proper security measures for wallet integration