Skip to Content
🎲 Welcome to RandBox - Powerful JavaScript Random Data Generation Library! Learn More
📦 Some ExamplesreactRockPaperScissors - Rock Paper Scissors Game

RockPaperScissors - Rock Paper Scissors Game

RockPaperScissors is a classic rock paper scissors game component that provides multiple AI strategies, game statistics and custom options. It supports traditional three-choice mode as well as extended five-choice mode.

📦 Import

import { RockPaperScissors } from '@randbox/react'; import type { RockPaperScissorsProps, RPSResult, RPSStats } from '@randbox/react';

🚀 Basic Usage

import React from 'react'; import { RockPaperScissors } from '@randbox/react'; function BasicRockPaperScissors() { const handleResult = (result) => { console.log('Game result:', result); alert(`You played ${result.emoji.player}, Computer played ${result.emoji.computer}\n${result.message}`); }; return ( <RockPaperScissors onResult={handleResult} /> ); }

🎯 Advanced Usage

Multiple AI Strategy Modes

function StrategyRockPaperScissors() { const [strategy, setStrategy] = useState('random'); const [gameHistory, setGameHistory] = useState([]); const strategies = { random: 'Random Strategy', counter: 'Counter Strategy', pattern: 'Pattern Recognition' }; const handleResult = (result) => { setGameHistory(prev => [result, ...prev.slice(0, 9)]); let message = result.message; if (strategy === 'counter') { message += '\n(AI is analyzing your move patterns)'; } else if (strategy === 'pattern') { message += '\n(AI is predicting based on historical patterns)'; } setTimeout(() => alert(message), 500); }; return ( <div style={{ padding: '20px' }}> <div style={{ marginBottom: '20px' }}> <label>AI Strategy: </label> <select value={strategy} onChange={(e) => setStrategy(e.target.value)}> {Object.entries(strategies).map(([key, name]) => ( <option key={key} value={key}>{name}</option> ))} </select> </div> <RockPaperScissors strategy={strategy} showStats={true} onResult={handleResult} /> {/* Recent Game History */} {gameHistory.length > 0 && ( <div style={{ marginTop: '20px' }}> <h3>Recent Battle History:</h3> {gameHistory.map((result, index) => ( <div key={index} style={{ padding: '10px', margin: '5px 0', backgroundColor: result.result === 'win' ? '#d4edda' : result.result === 'lose' ? '#f8d7da' : '#fff3cd', borderRadius: '5px', display: 'flex', justifyContent: 'space-between' }} > <span>Round {result.round}</span> <span>{result.emoji.player} vs {result.emoji.computer}</span> <span style={{ fontWeight: 'bold' }}> {result.result === 'win' ? 'Win' : result.result === 'lose' ? 'Lose' : 'Tie'} </span> </div> ))} </div> )} </div> ); }

Extended Five-Choice Mode

function ExtendedRockPaperScissors() { // Extended mode including Lizard and Spock const extendedChoices = ['rock', 'paper', 'scissors', 'lizard', 'spock']; const extendedEmojis = { rock: '🪨', paper: '📄', scissors: '✂️', lizard: '🦎', spock: '🖖' }; const [wins, setWins] = useState(0); const [losses, setLosses] = useState(0); const [ties, setTies] = useState(0); const handleResult = (result) => { // Update statistics if (result.result === 'win') setWins(prev => prev + 1); else if (result.result === 'lose') setLosses(prev => prev + 1); else setTies(prev => prev + 1); // Show extended rule explanations const rules = { 'rock-lizard': 'Rock crushes Lizard', 'rock-scissors': 'Rock crushes Scissors', 'paper-rock': 'Paper covers Rock', 'paper-spock': 'Paper disproves Spock', 'scissors-paper': 'Scissors cuts Paper', 'scissors-lizard': 'Scissors decapitates Lizard', 'lizard-spock': 'Lizard poisons Spock', 'lizard-paper': 'Lizard eats Paper', 'spock-scissors': 'Spock smashes Scissors', 'spock-rock': 'Spock vaporizes Rock' }; const combination = `${result.playerChoice}-${result.computerChoice}`; const rule = rules[combination]; alert( `You: ${extendedEmojis[result.playerChoice]} vs Computer: ${extendedEmojis[result.computerChoice]}\n` + `${result.message}${rule ? `\nRule: ${rule}` : ''}` ); }; const totalGames = wins + losses + ties; const winRate = totalGames > 0 ? ((wins / totalGames) * 100).toFixed(1) : '0.0'; return ( <div style={{ padding: '20px' }}> <div style={{ marginBottom: '20px', textAlign: 'center' }}> <h3>Big Bang Theory Rock Paper Scissors</h3> <div style={{ fontSize: '0.9em', color: '#666' }}> Extended version including Lizard🦎 and Spock🖖 </div> </div> {/* Statistics Panel */} <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '10px', marginBottom: '20px', textAlign: 'center' }}> <div style={{ padding: '10px', backgroundColor: '#d4edda', borderRadius: '5px' }}> <div>Wins</div> <div style={{ fontSize: '1.5em', fontWeight: 'bold' }}>{wins}</div> </div> <div style={{ padding: '10px', backgroundColor: '#f8d7da', borderRadius: '5px' }}> <div>Losses</div> <div style={{ fontSize: '1.5em', fontWeight: 'bold' }}>{losses}</div> </div> <div style={{ padding: '10px', backgroundColor: '#fff3cd', borderRadius: '5px' }}> <div>Ties</div> <div style={{ fontSize: '1.5em', fontWeight: 'bold' }}>{ties}</div> </div> <div style={{ padding: '10px', backgroundColor: '#d1ecf1', borderRadius: '5px' }}> <div>Win Rate</div> <div style={{ fontSize: '1.5em', fontWeight: 'bold' }}>{winRate}%</div> </div> </div> <RockPaperScissors choices={extendedChoices} emojis={extendedEmojis} strategy="pattern" onResult={handleResult} /> {/* Rule Explanations */} <div style={{ marginTop: '20px', fontSize: '0.8em', color: '#666' }}> <h4>Game Rules:</h4> <div style={{ columns: 2, columnGap: '20px' }}> <div>• Rock → Lizard, Scissors</div> <div>• Paper → Rock, Spock</div> <div>• Scissors → Paper, Lizard</div> <div>• Lizard → Spock, Paper</div> <div>• Spock → Scissors, Rock</div> </div> </div> </div> ); }

Tournament Mode

function TournamentRockPaperScissors() { const [tournament, setTournament] = useState({ currentRound: 1, maxRounds: 5, playerScore: 0, computerScore: 0, rounds: [] }); const [isGameOver, setIsGameOver] = useState(false); const handleResult = (result) => { const newRound = { round: tournament.currentRound, player: result.playerChoice, computer: result.computerChoice, result: result.result, playerEmoji: result.emoji.player, computerEmoji: result.emoji.computer }; setTournament(prev => { const newTournament = { ...prev, rounds: [...prev.rounds, newRound], currentRound: prev.currentRound + 1, playerScore: prev.playerScore + (result.result === 'win' ? 1 : 0), computerScore: prev.computerScore + (result.result === 'lose' ? 1 : 0) }; // Check if tournament is over if (newTournament.currentRound > newTournament.maxRounds) { setIsGameOver(true); } return newTournament; }); }; const resetTournament = () => { setTournament({ currentRound: 1, maxRounds: 5, playerScore: 0, computerScore: 0, rounds: [] }); setIsGameOver(false); }; const winner = tournament.playerScore > tournament.computerScore ? 'player' : tournament.computerScore > tournament.playerScore ? 'computer' : 'tie'; return ( <div style={{ padding: '20px' }}> {/* Tournament Status */} <div style={{ textAlign: 'center', marginBottom: '20px', padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '10px' }}> <h2>Tournament Mode</h2> {!isGameOver ? ( <div> <div style={{ fontSize: '1.2em', marginBottom: '10px' }}> Round {tournament.currentRound} / {tournament.maxRounds} </div> <div style={{ display: 'flex', justifyContent: 'center', gap: '40px' }}> <div> <div>Player</div> <div style={{ fontSize: '2em', color: '#007bff' }}>{tournament.playerScore}</div> </div> <div>VS</div> <div> <div>Computer</div> <div style={{ fontSize: '2em', color: '#dc3545' }}>{tournament.computerScore}</div> </div> </div> </div> ) : ( <div> <h3>Tournament Over!</h3> <div style={{ fontSize: '1.5em', margin: '10px 0' }}> {winner === 'player' ? '🎉 Congratulations! You Win!' : winner === 'computer' ? '😢 Sorry! You Lose!' : '🤝 It\'s a Tie!'} </div> <div>Final Score: {tournament.playerScore} - {tournament.computerScore}</div> <button onClick={resetTournament} style={{ marginTop: '15px', padding: '10px 20px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }} > Start New Tournament </button> </div> )} </div> {/* Game Area */} {!isGameOver && ( <RockPaperScissors strategy="counter" onResult={handleResult} /> )} {/* Match Records */} {tournament.rounds.length > 0 && ( <div style={{ marginTop: '20px' }}> <h3>Match Records:</h3> <div style={{ display: 'grid', gap: '8px' }}> {tournament.rounds.map((round) => ( <div key={round.round} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '10px', backgroundColor: round.result === 'win' ? '#d4edda' : round.result === 'lose' ? '#f8d7da' : '#fff3cd', borderRadius: '5px' }} > <span>Round {round.round}</span> <span style={{ fontSize: '1.2em' }}> {round.playerEmoji} vs {round.computerEmoji} </span> <span style={{ fontWeight: 'bold' }}> {round.result === 'win' ? 'Player Wins' : round.result === 'lose' ? 'Computer Wins' : 'Tie'} </span> </div> ))} </div> </div> )} </div> ); }

Real-Time Battle Mode

function RealTimeRockPaperScissors() { const [countdown, setCountdown] = useState(0); const [isCountingDown, setIsCountingDown] = useState(false); const [gameSpeed, setGameSpeed] = useState('normal'); const [autoPlay, setAutoPlay] = useState(false); const [streak, setStreak] = useState({ current: 0, best: 0 }); const speeds = { slow: { time: 5, label: 'Slow (5s)' }, normal: { time: 3, label: 'Normal (3s)' }, fast: { time: 1, label: 'Fast (1s)' } }; const handleResult = (result) => { // Update winning streak setStreak(prev => { const newCurrent = result.result === 'win' ? prev.current + 1 : 0; return { current: newCurrent, best: Math.max(prev.best, newCurrent) }; }); if (result.result === 'win' && streak.current + 1 > 3) { alert(`🔥 ${streak.current + 1} win streak!`); } }; const startCountdown = () => { setIsCountingDown(true); const time = speeds[gameSpeed].time; setCountdown(time); const interval = setInterval(() => { setCountdown(prev => { if (prev <= 1) { clearInterval(interval); setIsCountingDown(false); return 0; } return prev - 1; }); }, 1000); }; // Auto game useEffect(() => { let interval; if (autoPlay) { interval = setInterval(() => { if (!isCountingDown) { startCountdown(); } }, (speeds[gameSpeed].time + 2) * 1000); } return () => clearInterval(interval); }, [autoPlay, gameSpeed, isCountingDown]); return ( <div style={{ padding: '20px' }}> {/* Game Control Panel */} <div style={{ marginBottom: '20px', padding: '15px', backgroundColor: '#f8f9fa', borderRadius: '10px' }}> <div style={{ display: 'flex', gap: '20px', alignItems: 'center', marginBottom: '10px' }}> <div> <label>Game Speed: </label> <select value={gameSpeed} onChange={(e) => setGameSpeed(e.target.value)}> {Object.entries(speeds).map(([key, config]) => ( <option key={key} value={key}>{config.label}</option> ))} </select> </div> <label> <input type="checkbox" checked={autoPlay} onChange={(e) => setAutoPlay(e.target.checked)} /> Auto Mode </label> <button onClick={startCountdown} disabled={isCountingDown || autoPlay} style={{ padding: '8px 16px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '4px', cursor: isCountingDown || autoPlay ? 'not-allowed' : 'pointer' }} > Start Countdown </button> </div> {/* Streak Statistics */} <div style={{ display: 'flex', gap: '20px' }}> <div>Current Streak: <strong>{streak.current}</strong></div> <div>Best Streak: <strong>{streak.best}</strong></div> </div> </div> {/* Countdown Display */} {isCountingDown && ( <div style={{ textAlign: 'center', fontSize: '3em', color: countdown <= 1 ? '#dc3545' : '#007bff', marginBottom: '20px', animation: countdown <= 1 ? 'pulse 0.5s infinite' : 'none' }}> {countdown > 0 ? countdown : 'GO!'} </div> )} <RockPaperScissors strategy="random" disabled={isCountingDown} onResult={handleResult} /> <style jsx>{` @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } } `}</style> </div> ); }

📋 API Reference

RockPaperScissorsProps

PropertyTypeDefaultDescription
choicesstring[]['rock', 'paper', 'scissors']Array of available choices
emojisRecord<string, string>{rock: '🪨', paper: '📄', scissors: '✂️'}Emoji mapping for choices
showStatsbooleanfalseWhether to show statistics
strategy'random' | 'counter' | 'pattern''random'AI strategy mode
onResult(result: RPSResult) => voidundefinedGame result callback

Inherited BaseGameProps

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

RPSResult

interface RPSResult { playerChoice: string; // Player's choice computerChoice: string; // Computer's choice result: 'win' | 'lose' | 'tie'; // Game result message: string; // Result message emoji: { // Emoji symbols player: string; computer: string; }; round: number; // Round number }

RPSStats

interface RPSStats { totalGames: number; // Total number of games wins: number; // Number of wins losses: number; // Number of losses ties: number; // Number of ties winRate: string; // Win rate percentage }

🎨 Style Customization

Game Container Styles

.rps-container { border: 3px solid #6f42c1; border-radius: 20px; box-shadow: 0 10px 25px rgba(111, 66, 193, 0.2); background: linear-gradient(145deg, #f8f4ff, #efe8ff); } .rps-container:hover { transform: scale(1.02); transition: transform 0.3s ease; }

Choice Button Styles

.choice-button { background: linear-gradient(145deg, #ffffff, #e6e6e6); border: 2px solid #ddd; border-radius: 50%; width: 80px; height: 80px; font-size: 2em; cursor: pointer; transition: all 0.2s ease; } .choice-button:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0,0,0,0.2); } .choice-button:active { transform: translateY(0); box-shadow: 0 4px 10px rgba(0,0,0,0.1); }

🔧 Advanced Features

Custom Game Rules

function CustomRulesRPS() { // Implement custom win/lose logic const customRules = { rock: ['scissors', 'lizard'], paper: ['rock', 'spock'], scissors: ['paper', 'lizard'], lizard: ['spock', 'paper'], spock: ['scissors', 'rock'] }; const checkWinner = (player, computer) => { if (player === computer) return 'tie'; return customRules[player]?.includes(computer) ? 'win' : 'lose'; }; return ( <RockPaperScissors choices={['rock', 'paper', 'scissors', 'lizard', 'spock']} onResult={(result) => { const customResult = checkWinner(result.playerChoice, result.computerChoice); console.log('Custom rule result:', customResult); }} /> ); }

AI Learning Mode

function LearningAI() { const [playerHistory, setPlayerHistory] = useState([]); const [aiPrediction, setAiPrediction] = useState(null); const predictNextMove = (history) => { if (history.length < 3) return null; // Simple pattern recognition: find most common choice const frequency = {}; history.slice(-5).forEach(choice => { frequency[choice] = (frequency[choice] || 0) + 1; }); const mostCommon = Object.entries(frequency) .sort(([,a], [,b]) => b - a)[0]; // Predict player's next choice, then choose counter const counters = { rock: 'paper', paper: 'scissors', scissors: 'rock' }; return mostCommon ? counters[mostCommon[0]] : null; }; const handleResult = (result) => { setPlayerHistory(prev => [...prev, result.playerChoice].slice(-10)); // Update AI prediction const prediction = predictNextMove([...playerHistory, result.playerChoice]); setAiPrediction(prediction); }; return ( <div> {aiPrediction && ( <div style={{ marginBottom: '20px', padding: '10px', backgroundColor: '#fff3cd' }}> AI predicts you'll play: {aiPrediction} </div> )} <RockPaperScissors strategy="pattern" onResult={handleResult} /> <div style={{ marginTop: '20px', fontSize: '0.9em' }}> <div>Your move history: {playerHistory.join(' → ')}</div> </div> </div> ); }

🎯 Best Practices

1. Game Balance

// Ensure AI strategy balance const balancedStrategies = { beginner: { randomness: 0.8, pattern: 0.1, counter: 0.1 }, intermediate: { randomness: 0.5, pattern: 0.3, counter: 0.2 }, expert: { randomness: 0.2, pattern: 0.4, counter: 0.4 } };

2. User Experience Optimization

// Add visual feedback and animations const AnimatedRPS = () => { const [isAnimating, setIsAnimating] = useState(false); const handleGameStart = () => { setIsAnimating(true); }; const handleGameEnd = () => { setIsAnimating(false); }; return ( <RockPaperScissors className={isAnimating ? 'game-animating' : ''} onGameStart={handleGameStart} onGameEnd={handleGameEnd} /> ); };

3. Data Persistence

// Save game statistics to local storage const PersistentRPS = () => { const [stats, setStats] = useState(() => { const saved = localStorage.getItem('rps-stats'); return saved ? JSON.parse(saved) : { wins: 0, losses: 0, ties: 0 }; }); useEffect(() => { localStorage.setItem('rps-stats', JSON.stringify(stats)); }, [stats]); return ( <RockPaperScissors onResult={(result) => { setStats(prev => ({ ...prev, [result.result === 'win' ? 'wins' : result.result === 'lose' ? 'losses' : 'ties']: prev[result.result === 'win' ? 'wins' : result.result === 'lose' ? 'losses' : 'ties'] + 1 })); }} /> ); };

🐛 Common Issues

Q: How do AI strategies work?

A:

  • Random Strategy: Completely random choices
  • Counter Strategy: Analyzes player’s recent choices, selects counter options
  • Pattern Recognition: Identifies player’s move patterns and makes predictions

Q: Can I customize game rules?

A: Yes, you can implement any rules through choices and custom win/lose logic.

Q: Does it support multiplayer battles?

A: Current version mainly supports human vs AI battles, multiplayer features require additional state management.

Q: How to ensure fair gameplay?

A: The component uses RandBox to ensure randomness, AI strategies can be adjusted to maintain game balance.

Last updated on: