diff --git a/apps/web/src/app/games/memory-quiz/page.tsx b/apps/web/src/app/games/memory-quiz/page.tsx new file mode 100644 index 00000000..90d6bcb3 --- /dev/null +++ b/apps/web/src/app/games/memory-quiz/page.tsx @@ -0,0 +1,691 @@ +'use client' + +import { useState, useEffect, useRef } from 'react' +import Link from 'next/link' +import { css } from '../../../styled-system/css' +import { grid } from '../../../styled-system/patterns' +import { ServerSorobanSVG } from '../../../components/ServerSorobanSVG' + +interface GameConfig { + cardCount: number + displayTime: number // in seconds + numberRange: { min: number; max: number } +} + +interface GameStats { + totalCards: number + correct: number + incorrect: number + averageTime: number + accuracy: number +} + +interface Card { + id: number + number: number + userInput: string + isCorrect?: boolean +} + +const DIFFICULTY_CONFIGS: Record = { + beginner: { + cardCount: 3, + displayTime: 3.0, + numberRange: { min: 1, max: 9 } + }, + intermediate: { + cardCount: 5, + displayTime: 2.0, + numberRange: { min: 10, max: 99 } + }, + advanced: { + cardCount: 8, + displayTime: 1.5, + numberRange: { min: 100, max: 999 } + } +} + +export default function MemoryQuizPage() { + const [gameState, setGameState] = useState<'menu' | 'playing' | 'input' | 'results'>('menu') + const [difficulty, setDifficulty] = useState('beginner') + const [currentCards, setCurrentCards] = useState([]) + const [currentCardIndex, setCurrentCardIndex] = useState(0) + const [gameStats, setGameStats] = useState({ + totalCards: 0, + correct: 0, + incorrect: 0, + averageTime: 0, + accuracy: 0 + }) + const [startTime, setStartTime] = useState(null) + const [inputTimes, setInputTimes] = useState([]) + + const inputRefs = useRef<(HTMLInputElement | null)[]>([]) + + // Generate random numbers for the current difficulty + const generateCards = (config: GameConfig): Card[] => { + const cards: Card[] = [] + for (let i = 0; i < config.cardCount; i++) { + const number = Math.floor(Math.random() * (config.numberRange.max - config.numberRange.min + 1)) + config.numberRange.min + cards.push({ + id: i, + number, + userInput: '' + }) + } + return cards + } + + const startGame = (selectedDifficulty: keyof typeof DIFFICULTY_CONFIGS) => { + const config = DIFFICULTY_CONFIGS[selectedDifficulty] + const cards = generateCards(config) + + setDifficulty(selectedDifficulty) + setCurrentCards(cards) + setCurrentCardIndex(0) + setGameState('playing') + setStartTime(new Date()) + setInputTimes([]) + } + + const handleCardTimeout = () => { + if (currentCardIndex < currentCards.length - 1) { + setCurrentCardIndex(prev => prev + 1) + } else { + // All cards shown, move to input phase + setGameState('input') + // Focus first input + setTimeout(() => { + inputRefs.current[0]?.focus() + }, 100) + } + } + + const handleInputChange = (cardIndex: number, value: string) => { + setCurrentCards(prev => + prev.map((card, index) => + index === cardIndex ? { ...card, userInput: value } : card + ) + ) + } + + const handleInputKeyDown = (e: React.KeyboardEvent, cardIndex: number) => { + if (e.key === 'Enter' && cardIndex < currentCards.length - 1) { + inputRefs.current[cardIndex + 1]?.focus() + } + } + + const submitAnswers = () => { + const inputEndTime = new Date() + const totalInputTime = startTime ? (inputEndTime.getTime() - startTime.getTime()) / 1000 : 0 + + // Calculate results + const results = currentCards.map(card => ({ + ...card, + isCorrect: parseInt(card.userInput) === card.number + })) + + const correct = results.filter(card => card.isCorrect).length + const accuracy = (correct / results.length) * 100 + + setCurrentCards(results) + setGameStats({ + totalCards: results.length, + correct, + incorrect: results.length - correct, + averageTime: totalInputTime / results.length, + accuracy + }) + setGameState('results') + } + + const resetGame = () => { + setGameState('menu') + setCurrentCards([]) + setCurrentCardIndex(0) + setStartTime(null) + setInputTimes([]) + } + + // Auto-advance cards during display + useEffect(() => { + if (gameState === 'playing') { + const config = DIFFICULTY_CONFIGS[difficulty] + const timer = setTimeout(handleCardTimeout, config.displayTime * 1000) + return () => clearTimeout(timer) + } + }, [gameState, currentCardIndex, difficulty]) + + if (gameState === 'menu') { + return ( +
+
+ {/* Header */} +
+ + ← Back to Games + +

+ 🧠 Speed Memory Quiz +

+

+ Cards will flash briefly - memorize the abacus patterns and input the numbers you remember +

+
+ + {/* Difficulty Selection */} +
+

+ Choose Your Difficulty +

+ +
+ {Object.entries(DIFFICULTY_CONFIGS).map(([level, config]) => ( + + ))} +
+
+ + {/* Instructions */} +
+

+ How to Play: +

+
    +
  1. 1. Watch as abacus cards flash on screen
  2. +
  3. 2. Memorize each number shown
  4. +
  5. 3. Input all the numbers you remember
  6. +
  7. 4. See how well you did!
  8. +
+
+
+
+ ) + } + + if (gameState === 'playing') { + const config = DIFFICULTY_CONFIGS[difficulty] + const currentCard = currentCards[currentCardIndex] + const progress = ((currentCardIndex + 1) / currentCards.length) * 100 + + return ( +
+
+ {/* Progress Bar */} +
+
+
+
+

+ Card {currentCardIndex + 1} of {currentCards.length} +

+
+ + {/* Current Card */} +
+

+ Memorize this number: +

+
+ +
+
+ {currentCard.number} +
+
+ + {/* Timer indicator */} +
+ Next card in {config.displayTime} seconds... +
+
+
+ ) + } + + if (gameState === 'input') { + return ( +
+
+
+

+ Enter the numbers you remember: +

+ +
+ {currentCards.map((card, index) => ( +
+ + { inputRefs.current[index] = el }} + type="number" + value={card.userInput} + onChange={(e) => handleInputChange(index, e.target.value)} + onKeyDown={(e) => handleInputKeyDown(e, index)} + className={css({ + w: 'full', + px: '4', + py: '3', + border: '2px solid', + borderColor: 'gray.300', + rounded: 'lg', + fontSize: 'lg', + textAlign: 'center', + _focus: { + outline: 'none', + borderColor: 'blue.500', + ring: '2px', + ringColor: 'blue.200' + } + })} + placeholder="?" + /> +
+ ))} +
+ +
+ +
+
+
+
+ ) + } + + if (gameState === 'results') { + return ( +
+
+ {/* Results Header */} +
+

+ Quiz Complete! + {gameStats.accuracy >= 80 ? ' 🎉' : gameStats.accuracy >= 60 ? ' 👍' : ' 💪'} +

+ + {/* Score Summary */} +
+
= 80 ? 'green.600' : gameStats.accuracy >= 60 ? 'blue.600' : 'orange.600' + })}> + {gameStats.accuracy.toFixed(1)}% +
+
+ {gameStats.correct} of {gameStats.totalCards} correct +
+
+
+ + {/* Detailed Results */} +
+

+ Review Your Answers +

+ +
+ {currentCards.map((card, index) => ( +
+
+
+ +
+
+
+ Card {index + 1} +
+
+ Correct: {card.number} +
+
+ Your answer: {card.userInput || '(empty)'} +
+
+ {card.isCorrect ? '✓ Correct' : '✗ Incorrect'} +
+
+
+
+ ))} +
+
+ + {/* Action Buttons */} +
+ + + + More Games + +
+
+
+ ) + } + + return null +} \ No newline at end of file diff --git a/apps/web/src/app/games/page.tsx b/apps/web/src/app/games/page.tsx new file mode 100644 index 00000000..9be5e661 --- /dev/null +++ b/apps/web/src/app/games/page.tsx @@ -0,0 +1,407 @@ +'use client' + +import Link from 'next/link' +import { css } from '../../styled-system/css' +import { grid } from '../../styled-system/patterns' + +export default function GamesPage() { + return ( +
+
+ {/* Hero Section */} +
+

+ 🎮 Soroban Games +

+

+ Master the soroban through interactive games and challenges +

+
+ + {/* Games Grid */} +
+ + {/* Speed Memory Quiz */} + +
+
+ 🧠 +
+

+ Speed Memory Quiz +

+

+ Flash cards appear briefly - memorize the abacus patterns and input the numbers you remember. Test your visual memory and speed recognition skills. +

+
+ + Memory Training + + + Beginner Friendly + +
+
+ + + {/* Matching Pairs Game */} +
+
+ 🃏 +
+

+ Matching Pairs + + Coming Soon + +

+

+ Match abacus patterns with their corresponding numbers in this memory-style card game. Perfect for building pattern recognition skills. +

+
+ + Pattern Matching + + + All Levels + +
+
+ + {/* Speed Complement Race */} +
+
+ 🏃 +
+

+ Speed Complement Race + + Coming Soon + +

+

+ Race against time to find complement pairs that add to 5 or 10. Multiple game modes including practice, sprint, and survival challenges. +

+
+ + Speed Challenge + + + Advanced + +
+
+ + {/* Card Sorting Challenge */} +
+
+ 🔢 +
+

+ Card Sorting Challenge + + Coming Soon + +

+

+ Drag and drop abacus cards to sort them from lowest to highest value. Develop number sense and comparison skills. +

+
+ + Sorting & Logic + + + Intermediate + +
+
+
+ + {/* Call to Action */} +
+

+ New to Soroban? +

+

+ Learn the basics with our comprehensive guide before diving into games +

+ + Start Learning → + +
+
+
+ ) +} \ No newline at end of file