Spaces:
Running
Running
written in react i want a 3 reel slot machine that uses this logic Terms to understand
68ca7b4
verified
| <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 |