Spaces:
Sleeping
Sleeping
| import { useState, useEffect } from 'react' | |
| import './App.css' | |
| // Define the types for the game | |
| type DisplayMode = 'numbers' | 'objects' | 'both'; | |
| interface AdditionQuestion { | |
| num1: number; | |
| num2: number; | |
| answer: number; | |
| options: number[]; | |
| object1: string; | |
| object2: string; | |
| } | |
| // Emoji pool | |
| const emojis = ['🍎', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', '🫐', '🍒', '🥝', '🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐸', '🐷', '⚽', '🏀', '🎾', '🏈', '🎱', '🎨', '🎭', '🎯', '🚗', '🚕', '🚓', '🚑', '🚁', '🚀']; | |
| function App() { | |
| const [currentQuestion, setCurrentQuestion] = useState<AdditionQuestion | null>(null); | |
| const [displayMode, setDisplayMode] = useState<DisplayMode>('numbers'); | |
| const [feedback, setFeedback] = useState(''); | |
| const [score, setScore] = useState(0); | |
| // Generate a random number between min and max (inclusive) | |
| const getRandomNumber = (min: number, max: number) => { | |
| return Math.floor(Math.random() * (max - min + 1)) + min; | |
| }; | |
| // Helper function to get an operand (0-5) with a reduced chance of it being 0 | |
| const getOperandWithReducedZero = (maxValue: number): number => { | |
| const CHANCE_OF_BEING_ZERO = 0.10; // 10% chance the operand is 0 | |
| if (maxValue < 0) maxValue = 0; // Ensure maxValue is not negative | |
| if (maxValue === 0) return 0; // If max is 0, operand must be 0 | |
| if (Math.random() < CHANCE_OF_BEING_ZERO) { | |
| return 0; | |
| } else { | |
| // Generate a number from 1 to maxValue | |
| return getRandomNumber(1, maxValue); | |
| } | |
| }; | |
| // Get a random emoji | |
| const getRandomEmoji = () => { | |
| return emojis[Math.floor(Math.random() * emojis.length)]; | |
| }; | |
| // Generate a new question | |
| const generateQuestion = () => { | |
| let num1, num2; | |
| const MAX_OPERAND_VALUE = 5; | |
| const MAX_SUM = 5; | |
| do { | |
| num1 = getOperandWithReducedZero(MAX_OPERAND_VALUE); | |
| num2 = getOperandWithReducedZero(MAX_OPERAND_VALUE); | |
| } while (num1 + num2 > MAX_SUM); | |
| const answer = num1 + num2; | |
| // Generate options, one of which is the correct answer | |
| const options = new Set<number>(); | |
| options.add(answer); | |
| while (options.size < 3) { | |
| const option = getRandomNumber(0, 10); // Options can be up to 10 | |
| if (option !== answer) { // Ensure options are not trivially easy if sum is 0 or 1 | |
| options.add(option); | |
| } | |
| } | |
| const object1 = getRandomEmoji(); | |
| let object2 = getRandomEmoji(); | |
| // Ensure objects are different if numbers are different, or if it's the same number, it's fine. | |
| // This logic primarily ensures visual distinction when counts are different. | |
| if (num1 > 0 && num2 > 0 && num1 !== num2) { // only ensure different emojis if counts are different and non-zero | |
| while (object2 === object1) { | |
| object2 = getRandomEmoji(); | |
| } | |
| } else if (num1 > 0 && num2 > 0 && num1 === num2) { // if numbers are same and non-zero, make sure emojis are same for clarity | |
| object2 = object1; | |
| } | |
| setCurrentQuestion({ | |
| num1, | |
| num2, | |
| answer, | |
| options: Array.from(options).sort(() => Math.random() - 0.5), // Shuffle options | |
| object1, | |
| object2, | |
| }); | |
| setFeedback(''); | |
| }; | |
| useEffect(() => { | |
| generateQuestion(); | |
| }, []); | |
| // Handle answer submission | |
| const handleAnswer = (selectedOption: number) => { | |
| if (currentQuestion && selectedOption === currentQuestion.answer) { | |
| setFeedback('Correct! 🎉'); | |
| setScore(score + 1); | |
| setTimeout(() => { | |
| generateQuestion(); | |
| }, 1500); | |
| } else if (currentQuestion) { // Added currentQuestion check here | |
| setFeedback('Try again! 🤔'); | |
| setTimeout(() => { | |
| setFeedback(''); | |
| }, 1000); | |
| } | |
| }; | |
| if (!currentQuestion) { | |
| return <div className="loading">Loading game...</div>; | |
| } | |
| const { num1, num2, answer, options, object1, object2 } = currentQuestion; | |
| // const sumObject = object1; // No longer needed as sum is not displayed with objects here | |
| return ( | |
| <div className="app-container"> | |
| <header className="game-header"> | |
| <h1>Addition Game</h1> | |
| <div className="display-mode-selector"> | |
| <button onClick={() => setDisplayMode('numbers')} className={displayMode === 'numbers' ? 'active' : ''}>Number</button> | |
| <button onClick={() => setDisplayMode('objects')} className={displayMode === 'objects' ? 'active' : ''}>Object</button> | |
| <button onClick={() => setDisplayMode('both')} className={displayMode === 'both' ? 'active' : ''}>Both</button> | |
| </div> | |
| <div className="score-display">Score: {score}</div> | |
| </header> | |
| <main className="game-area"> | |
| <div className="question-container-new"> | |
| {/* Operand 1 Column */} | |
| <div className="operand-column"> | |
| {(displayMode === 'numbers' || displayMode === 'both') && ( | |
| <div className="number-box"> | |
| <span className="number-text">{num1}</span> | |
| </div> | |
| )} | |
| {(displayMode === 'objects' || displayMode === 'both') && ( | |
| <div className="objects-display"> | |
| {Array(num1).fill(null).map((_, i) => ( | |
| <span key={`obj1-${i}`} className="item-object">{object1}</span> | |
| ))} | |
| </div> | |
| )} | |
| {/* If only objects mode and num1 is 0, show nothing or a placeholder if desired*/} | |
| {displayMode === 'objects' && num1 === 0 && <div className="objects-display" style={{minHeight: '4rem'}}></div>} | |
| </div> | |
| {/* Operator + Column */} | |
| <div className="operator-cell"> | |
| <span className="operator-symbol">+</span> | |
| </div> | |
| {/* Operand 2 Column */} | |
| <div className="operand-column"> | |
| {(displayMode === 'numbers' || displayMode === 'both') && ( | |
| <div className="number-box"> | |
| <span className="number-text">{num2}</span> | |
| </div> | |
| )} | |
| {(displayMode === 'objects' || displayMode === 'both') && ( | |
| <div className="objects-display"> | |
| {Array(num2).fill(null).map((_, i) => ( | |
| <span key={`obj2-${i}`} className="item-object">{object2}</span> | |
| ))} | |
| </div> | |
| )} | |
| {displayMode === 'objects' && num2 === 0 && <div className="objects-display" style={{minHeight: '4rem'}}></div>} | |
| </div> | |
| {/* Operator = Column */} | |
| <div className="operator-cell"> | |
| <span className="operator-symbol">=</span> | |
| </div> | |
| {/* Result Column - Now displays a question mark */} | |
| <div className="result-column"> | |
| <span className="answer-placeholder-new">?</span> | |
| </div> | |
| </div> | |
| <div className="options-container"> | |
| {options.map((option) => ( | |
| <button key={option} onClick={() => handleAnswer(option)} className="option-button"> | |
| {option} | |
| </button> | |
| ))} | |
| </div> | |
| {feedback && <div className="feedback-message">{feedback}</div>} | |
| </main> | |
| </div> | |
| ); | |
| } | |
| export default App; | |