Skip to Content
🎲 Welcome to RandBox - Powerful JavaScript Random Data Generation Library! Learn More
📦 Some ExamplesreactDiceGame - Dice Game

DiceGame - Dice Game

DiceGame is a Canvas-based 3D dice game component that provides a realistic throwing experience and multiple game modes. It supports custom dice count, number of sides, and rich game rule configuration.

📦 Import

import { DiceGame } from '@randbox/react'; import type { DiceGameProps, DiceGameResult } from '@randbox/react';

🚀 Basic Usage

import React from 'react'; import { DiceGame } from '@randbox/react'; function BasicDiceGame() { const handleResult = (result) => { console.log('Dice result:', result); alert(`Dice result: ${result.results.join(', ')}, Total: ${result.total}`); }; return ( <DiceGame diceCount={2} onResult={handleResult} /> ); }

🎯 Advanced Usage

Multiple Game Modes

function MultiModeDiceGame() { const [gameMode, setGameMode] = useState('simple'); const [targetSum, setTargetSum] = useState(7); const handleResult = (result) => { console.log(`${result.gameMode} mode result:`, result); switch (result.gameMode) { case 'sum': alert(`Target sum: ${targetSum}, Actual sum: ${result.sum}, ${result.isWin ? 'Success' : 'Failed'}!`); break; case 'bigSmall': alert(`Roll result: ${result.sum > 7 ? 'Big' : 'Small'} (${result.sum}), ${result.description}`); break; case 'even_odd': alert(`Roll result: ${result.sum % 2 === 0 ? 'Even' : 'Odd'} (${result.sum}), ${result.description}`); break; default: alert(result.message); } }; return ( <div style={{ padding: '20px' }}> <div style={{ marginBottom: '20px' }}> <label>Game Mode: </label> <select value={gameMode} onChange={(e) => setGameMode(e.target.value)}> <option value="simple">Simple Mode</option> <option value="sum">Sum Mode</option> <option value="bigSmall">Big/Small Mode</option> <option value="even_odd">Even/Odd Mode</option> <option value="guess">Guess Mode</option> <option value="specific">Specific Value Mode</option> </select> {gameMode === 'sum' && ( <div style={{ marginTop: '10px' }}> <label>Target Sum: </label> <input type="number" value={targetSum} min="2" max="12" onChange={(e) => setTargetSum(parseInt(e.target.value))} /> </div> )} </div> <DiceGame diceCount={2} gameMode={gameMode} targetSum={targetSum} onResult={handleResult} /> </div> ); }

Multi-Dice Game

function MultiDiceGame() { const [diceCount, setDiceCount] = useState(3); const [sides, setSides] = useState(6); const [results, setResults] = useState([]); const handleResult = (result) => { const newResult = { ...result, timestamp: new Date().toLocaleTimeString() }; setResults(prev => [newResult, ...prev.slice(0, 9)]); }; const totalPossibleSum = diceCount * sides; const averageSum = diceCount * (sides + 1) / 2; return ( <div style={{ padding: '20px' }}> <div style={{ marginBottom: '20px', display: 'flex', gap: '20px' }}> <div> <label>Dice Count: </label> <select value={diceCount} onChange={(e) => setDiceCount(parseInt(e.target.value))}> {[1, 2, 3, 4, 5, 6].map(n => ( <option key={n} value={n}>{n} dice</option> ))} </select> </div> <div> <label>Dice Sides: </label> <select value={sides} onChange={(e) => setSides(parseInt(e.target.value))}> <option value={4}>4-sided</option> <option value={6}>6-sided</option> <option value={8}>8-sided</option> <option value={10}>10-sided</option> <option value={12}>12-sided</option> <option value={20}>20-sided</option> </select> </div> </div> <div style={{ marginBottom: '20px', padding: '10px', backgroundColor: '#f0f8ff' }}> <div>Configuration: {diceCount} {sides}-sided dice</div> <div>Possible sum range: {diceCount} - {totalPossibleSum}</div> <div>Expected average: {averageSum.toFixed(1)}</div> </div> <DiceGame diceCount={diceCount} sides={sides} gameMode="simple" onResult={handleResult} /> {/* History */} {results.length > 0 && ( <div style={{ marginTop: '20px' }}> <h3>Roll History:</h3> <div style={{ maxHeight: '300px', overflowY: 'auto' }}> {results.map((result, index) => ( <div key={index} style={{ padding: '8px', margin: '5px 0', backgroundColor: '#f8f9fa', borderRadius: '5px', display: 'flex', justifyContent: 'space-between' }} > <span>{result.results.join(' + ')} = {result.sum}</span> <span style={{ color: '#666', fontSize: '0.9em' }}>{result.timestamp}</span> </div> ))} </div> </div> )} </div> ); }

Competitive Dice Game

function CompetitiveDiceGame() { const [playerScore, setPlayerScore] = useState(0); const [computerScore, setComputerScore] = useState(0); const [round, setRound] = useState(1); const [gameHistory, setGameHistory] = useState([]); const [isGameOver, setIsGameOver] = useState(false); const maxRounds = 5; const winningScore = 3; const handleResult = (result) => { const playerSum = result.sum; // Computer roll const computerDice = Array.from({ length: 2 }, () => Math.floor(Math.random() * 6) + 1); const computerSum = computerDice.reduce((a, b) => a + b, 0); const roundResult = { round, player: { dice: result.results, sum: playerSum }, computer: { dice: computerDice, sum: computerSum }, winner: playerSum > computerSum ? 'player' : computerSum > playerSum ? 'computer' : 'tie' }; setGameHistory(prev => [...prev, roundResult]); // Update scores if (roundResult.winner === 'player') { setPlayerScore(prev => prev + 1); } else if (roundResult.winner === 'computer') { setComputerScore(prev => prev + 1); } // Check game end conditions const newPlayerScore = roundResult.winner === 'player' ? playerScore + 1 : playerScore; const newComputerScore = roundResult.winner === 'computer' ? computerScore + 1 : computerScore; if (newPlayerScore >= winningScore || newComputerScore >= winningScore || round >= maxRounds) { setIsGameOver(true); } else { setRound(prev => prev + 1); } // Show result setTimeout(() => { alert( `Round ${round} result:\n` + `Player: ${playerSum} vs Computer: ${computerSum}\n` + `${roundResult.winner === 'tie' ? 'Tie!' : `${roundResult.winner === 'player' ? 'Player' : 'Computer'} wins!`}` ); }, 1000); }; const resetGame = () => { setPlayerScore(0); setComputerScore(0); setRound(1); setGameHistory([]); setIsGameOver(false); }; const gameWinner = playerScore >= winningScore ? 'player' : computerScore >= winningScore ? 'computer' : 'ongoing'; return ( <div style={{ padding: '20px' }}> {/* Score Panel */} <div style={{ display: 'flex', justifyContent: 'space-around', marginBottom: '20px', padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '10px' }}> <div style={{ textAlign: 'center' }}> <h3>Player</h3> <div style={{ fontSize: '2em', color: '#007bff' }}>{playerScore}</div> </div> <div style={{ textAlign: 'center' }}> <h3>Round {round}</h3> <div style={{ fontSize: '1.2em' }}> {isGameOver ? 'Game Over' : `In Progress (Best of ${maxRounds})`} </div> </div> <div style={{ textAlign: 'center' }}> <h3>Computer</h3> <div style={{ fontSize: '2em', color: '#dc3545' }}>{computerScore}</div> </div> </div> {/* Game Result */} {isGameOver && ( <div style={{ textAlign: 'center', padding: '20px', backgroundColor: gameWinner === 'player' ? '#d4edda' : '#f8d7da', borderRadius: '10px', marginBottom: '20px' }}> <h2> {gameWinner === 'player' ? '🎉 Congratulations! You Win!' : '😢 Sorry! You Lose!'} </h2> <button onClick={resetGame} style={{ padding: '10px 20px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginTop: '10px' }} > Play Again </button> </div> )} {/* Dice Game Area */} {!isGameOver && ( <div style={{ textAlign: 'center', marginBottom: '20px' }}> <h4>Please roll your dice</h4> <DiceGame diceCount={2} gameMode="simple" onResult={handleResult} disabled={isGameOver} /> </div> )} {/* Battle History */} {gameHistory.length > 0 && ( <div> <h3>Battle History:</h3> {gameHistory.map((record, index) => ( <div key={index} style={{ padding: '10px', margin: '5px 0', backgroundColor: record.winner === 'player' ? '#d4edda' : record.winner === 'computer' ? '#f8d7da' : '#fff3cd', borderRadius: '5px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }} > <span>Round {record.round}</span> <span> Player: {record.player.dice.join('+')}={record.player.sum} vs Computer: {record.computer.dice.join('+')}={record.computer.sum} </span> <span style={{ fontWeight: 'bold' }}> {record.winner === 'tie' ? 'Tie' : record.winner === 'player' ? 'Player Wins' : 'Computer Wins'} </span> </div> ))} </div> )} </div> ); }

Statistical Analysis Dice Game

function StatisticalDiceGame() { const [rolls, setRolls] = useState([]); const [diceCount, setDiceCount] = useState(2); const [autoRoll, setAutoRoll] = useState(false); const handleResult = (result) => { setRolls(prev => [...prev, result]); }; // Calculate statistics const stats = useMemo(() => { if (rolls.length === 0) return null; const sums = rolls.map(r => r.sum); const average = sums.reduce((a, b) => a + b, 0) / sums.length; const frequency = {}; sums.forEach(sum => { frequency[sum] = (frequency[sum] || 0) + 1; }); const mostCommon = Object.entries(frequency) .sort(([,a], [,b]) => b - a)[0]; return { totalRolls: rolls.length, average: average.toFixed(2), min: Math.min(...sums), max: Math.max(...sums), mostCommon: mostCommon ? `${mostCommon[0]} (${mostCommon[1]} times)` : 'None', frequency }; }, [rolls]); // Auto roll useEffect(() => { let interval; if (autoRoll && rolls.length < 100) { interval = setInterval(() => { // Trigger roll... this needs to simulate rolling const simulatedResult = { results: Array.from({ length: diceCount }, () => Math.floor(Math.random() * 6) + 1), sum: 0, gameMode: 'simple', message: 'Auto roll' }; simulatedResult.sum = simulatedResult.results.reduce((a, b) => a + b, 0); setRolls(prev => [...prev, simulatedResult]); }, 500); } return () => clearInterval(interval); }, [autoRoll, rolls.length, diceCount]); const clearStats = () => setRolls([]); return ( <div style={{ padding: '20px' }}> <div style={{ marginBottom: '20px', display: 'flex', gap: '20px', alignItems: 'center' }}> <div> <label>Dice Count: </label> <select value={diceCount} onChange={(e) => setDiceCount(parseInt(e.target.value))}> {[1, 2, 3, 4].map(n => ( <option key={n} value={n}>{n} dice</option> ))} </select> </div> <label> <input type="checkbox" checked={autoRoll} onChange={(e) => setAutoRoll(e.target.checked)} disabled={rolls.length >= 100} /> Auto Roll ({rolls.length}/100) </label> <button onClick={clearStats} style={{ padding: '8px 16px', backgroundColor: '#dc3545', color: 'white', border: 'none', borderRadius: '4px' }} > Clear Stats </button> </div> <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}> {/* Dice Game Area */} <div> <DiceGame diceCount={diceCount} gameMode="simple" onResult={handleResult} disabled={autoRoll} /> </div> {/* Statistics Panel */} {stats && ( <div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '10px' }}> <h3>Statistics</h3> <div style={{ display: 'grid', gap: '10px' }}> <div>Total Rolls: {stats.totalRolls}</div> <div>Average: {stats.average}</div> <div>Minimum: {stats.min}</div> <div>Maximum: {stats.max}</div> <div>Most Common: {stats.mostCommon}</div> </div> <h4 style={{ marginTop: '20px' }}>Frequency Distribution:</h4> <div style={{ fontSize: '0.9em' }}> {Object.entries(stats.frequency) .sort(([a], [b]) => parseInt(a) - parseInt(b)) .map(([sum, count]) => ( <div key={sum} style={{ marginBottom: '5px' }}> <div style={{ display: 'flex', justifyContent: 'space-between' }}> <span>{sum}:</span> <span>{count} times ({((count / stats.totalRolls) * 100).toFixed(1)}%)</span> </div> <div style={{ height: '8px', backgroundColor: '#e9ecef', borderRadius: '4px', overflow: 'hidden' }}> <div style={{ height: '100%', width: `${(count / stats.totalRolls) * 100}%`, backgroundColor: '#007bff' }} /> </div> </div> ))} </div> </div> )} </div> </div> ); }

📋 API Reference

DiceGameProps

PropertyTypeDefaultDescription
diceCountnumber2Number of dice
sidesnumber6Number of sides per die
gameMode'simple' | 'sum' | 'bigSmall' | 'guess' | 'even_odd' | 'specific''simple'Game mode
targetSumnumber7Target sum (used in sum mode)
onResult(result: DiceGameResult) => voidundefinedRoll result callback

Inherited BaseGameProps

PropertyTypeDefaultDescription
classNamestring''CSS class name
styleReact.CSSProperties{}Inline styles
disabledbooleanfalseWhether disabled
onGameStart() => voidundefinedGame start callback
onGameEnd(result: DiceGameResult) => voidundefinedGame end callback

DiceGameResult

interface DiceGameResult { results: number[]; // Each die's value total: number; // Total sum (same as sum) gameMode: string; // Game mode isWin?: boolean; // Whether won (in certain modes) message: string; // Result description message values: number[]; // Die values array (same as results) sum: number; // Sum of values description: string; // Detailed description }

Game Mode Descriptions

ModeDescriptionWin Condition
simpleSimple modeNo specific condition, just display result
sumSum modeRoll total equals target value
bigSmallBig/Small modeTotal > 7 is “Big”, ≤ 7 is “Small”
guessGuess modePlayer must predict result beforehand
even_oddEven/Odd modeTotal is even or odd
specificSpecific value modeRoll specific combination

🎨 Style Customization

Container Styles

.dice-game-container { border: 2px solid #28a745; border-radius: 15px; box-shadow: 0 8px 20px rgba(40, 167, 69, 0.2); background: linear-gradient(145deg, #f8fff8, #e8f5e8); } .dice-game-container:hover { transform: translateY(-2px); transition: transform 0.3s ease; }

Rolling Animation

.dice-rolling { animation: diceRoll 1s ease-in-out; } @keyframes diceRoll { 0%, 100% { transform: rotate(0deg) scale(1); } 25% { transform: rotate(90deg) scale(1.1); } 50% { transform: rotate(180deg) scale(1.2); } 75% { transform: rotate(270deg) scale(1.1); } }

🔧 Advanced Features

Custom Rolling Rules

function CustomRulesDiceGame() { const checkSpecialCombinations = (results) => { const sorted = [...results].sort(); // Straight check const isStraight = sorted.every((val, i) => i === 0 || val === sorted[i-1] + 1); // Pairs check const pairs = {}; results.forEach(val => pairs[val] = (pairs[val] || 0) + 1); const pairCounts = Object.values(pairs); if (isStraight) return { type: 'straight', message: 'Straight!', bonus: 50 }; if (pairCounts.includes(3)) return { type: 'triple', message: 'Three of a kind!', bonus: 30 }; if (pairCounts.includes(2)) return { type: 'pair', message: 'Pair!', bonus: 10 }; return { type: 'normal', message: 'Normal roll', bonus: 0 }; }; return ( <DiceGame diceCount={3} gameMode="simple" onResult={(result) => { const special = checkSpecialCombinations(result.results); alert(`${result.message}\n${special.message}\nBonus Score: ${special.bonus}`); }} /> ); }

Multiplayer Game Mode

function MultiPlayerDiceGame() { const [players] = useState(['Player 1', 'Player 2', 'Player 3']); const [currentPlayer, setCurrentPlayer] = useState(0); const [scores, setScores] = useState({}); const [round, setRound] = useState(1); const handleResult = (result) => { const player = players[currentPlayer]; setScores(prev => ({ ...prev, [player]: (prev[player] || 0) + result.sum })); // Switch to next player const nextPlayer = (currentPlayer + 1) % players.length; if (nextPlayer === 0) { setRound(prev => prev + 1); } setCurrentPlayer(nextPlayer); }; return ( <div style={{ padding: '20px' }}> <div style={{ marginBottom: '20px' }}> <h3>Round {round} - {players[currentPlayer]}'s Turn</h3> <div style={{ display: 'flex', gap: '20px' }}> {players.map(player => ( <div key={player} style={{ padding: '10px', backgroundColor: player === players[currentPlayer] ? '#fff3cd' : '#f8f9fa', borderRadius: '5px' }} > {player}: {scores[player] || 0} points </div> ))} </div> </div> <DiceGame diceCount={2} gameMode="simple" onResult={handleResult} /> </div> ); }

🎯 Best Practices

1. Game Mode Selection

// Choose appropriate game mode based on user group const gameModeConfig = { children: { mode: 'simple', diceCount: 1 }, casual: { mode: 'bigSmall', diceCount: 2 }, competitive: { mode: 'sum', diceCount: 3, targetSum: 10 } };

2. Performance Optimization

// Use React.memo to optimize re-renders const OptimizedDiceGame = React.memo(({ diceCount, ...props }) => { return <DiceGame diceCount={diceCount} {...props} />; }); // Cache complex calculations const MemoizedDiceGame = () => { const gameConfig = useMemo(() => ({ diceCount: 3, sides: 6, gameMode: 'sum' }), []); return <DiceGame {...gameConfig} />; };

3. Error Handling

function SafeDiceGame() { const [error, setError] = useState(null); const handleError = (error) => { setError(error.message); console.error('Dice game error:', error); }; return ( <div> {error && ( <div style={{ color: 'red', marginBottom: '10px' }}> Error: {error} </div> )} <DiceGame diceCount={2} onResult={(result) => { setError(null); console.log('Roll successful:', result); }} /> </div> ); }

🐛 Common Issues

Q: Are dice rolls truly random?

A: Yes, the component uses RandBox’s Mersenne Twister algorithm, providing high-quality random number generation.

Q: Can I customize the number of dice sides?

A: Yes, through the sides property you can set 4-sided, 6-sided, 8-sided, 10-sided, 12-sided, 20-sided and other types of dice.

Q: How to implement special rolling rules?

A: You can implement custom logic in the onResult callback to check roll results and implement special rules.

Q: How many dice can be rolled simultaneously?

A: Theoretically no limit, but recommend no more than 6 to ensure good user experience and performance.

Last updated on: