// === LETTER DISCOVERY GAME ===
// Discover letters first, then explore words that start with each letter
class LetterDiscovery {
constructor({ container, content, onScoreUpdate, onGameEnd }) {
this.container = container;
this.content = content;
this.onScoreUpdate = onScoreUpdate;
this.onGameEnd = onGameEnd;
// Game state
this.currentPhase = 'letter-discovery'; // letter-discovery, word-exploration, practice
this.currentLetterIndex = 0;
this.discoveredLetters = [];
this.currentLetter = null;
this.currentWordIndex = 0;
this.discoveredWords = [];
this.score = 0;
this.lives = 3;
// Content processing
this.letters = [];
this.letterWords = {}; // Map letter -> words starting with that letter
// Practice system
this.practiceLevel = 1;
this.practiceRound = 0;
this.maxPracticeRounds = 8;
this.practiceCorrectAnswers = 0;
this.practiceErrors = 0;
this.currentPracticeItems = [];
this.injectCSS();
this.extractContent();
this.init();
}
injectCSS() {
if (document.getElementById('letter-discovery-styles')) return;
const styleSheet = document.createElement('style');
styleSheet.id = 'letter-discovery-styles';
styleSheet.textContent = `
.letter-discovery-wrapper {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
position: relative;
overflow-y: auto;
}
.letter-discovery-hud {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(255,255,255,0.1);
padding: 15px 20px;
border-radius: 15px;
backdrop-filter: blur(10px);
margin-bottom: 20px;
flex-wrap: wrap;
gap: 10px;
}
.hud-group {
display: flex;
align-items: center;
gap: 15px;
}
.hud-item {
color: white;
font-weight: bold;
font-size: 1.1em;
}
.phase-indicator {
background: rgba(255,255,255,0.2);
padding: 8px 16px;
border-radius: 20px;
font-size: 0.9em;
color: white;
backdrop-filter: blur(5px);
}
.letter-discovery-main {
background: rgba(255,255,255,0.1);
border-radius: 20px;
padding: 30px;
backdrop-filter: blur(10px);
min-height: 70vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.game-content {
width: 100%;
max-width: 900px;
text-align: center;
}
/* Letter Display Styles */
.letter-card {
background: rgba(255,255,255,0.95);
border-radius: 25px;
padding: 60px 40px;
margin: 30px auto;
max-width: 400px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
transform: scale(0.8);
animation: letterAppear 0.8s ease-out forwards;
}
@keyframes letterAppear {
to { transform: scale(1); }
}
.letter-display {
font-size: 8em;
font-weight: bold;
color: #667eea;
margin-bottom: 20px;
text-shadow: 0 4px 8px rgba(0,0,0,0.1);
font-family: 'Arial Black', Arial, sans-serif;
}
.letter-info {
font-size: 1.5em;
color: #333;
margin-bottom: 15px;
}
.letter-pronunciation {
font-size: 1.2em;
color: #666;
font-style: italic;
margin-bottom: 25px;
}
.letter-controls {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 30px;
}
/* Word Exploration Styles */
.word-exploration-header {
background: rgba(255,255,255,0.1);
padding: 20px;
border-radius: 15px;
margin-bottom: 30px;
backdrop-filter: blur(5px);
}
.exploring-letter {
font-size: 3em;
color: white;
margin-bottom: 10px;
font-weight: bold;
}
.word-progress {
color: rgba(255,255,255,0.8);
font-size: 1.1em;
}
.word-card {
background: rgba(255,255,255,0.95);
border-radius: 20px;
padding: 40px 30px;
margin: 25px auto;
max-width: 500px;
box-shadow: 0 15px 30px rgba(0,0,0,0.1);
transform: translateY(20px);
animation: wordSlideIn 0.6s ease-out forwards;
}
@keyframes wordSlideIn {
to { transform: translateY(0); }
}
.word-text {
font-size: 2.5em;
color: #667eea;
margin-bottom: 15px;
font-weight: bold;
}
.word-translation {
font-size: 1.3em;
color: #333;
margin-bottom: 10px;
}
.word-pronunciation {
font-size: 1.1em;
color: #666;
font-style: italic;
margin-bottom: 10px;
}
.word-type {
font-size: 0.9em;
color: #667eea;
background: rgba(102, 126, 234, 0.1);
padding: 4px 12px;
border-radius: 15px;
display: inline-block;
margin-bottom: 15px;
font-weight: 500;
}
.word-example {
font-size: 1em;
color: #555;
font-style: italic;
padding: 10px 15px;
background: rgba(0, 0, 0, 0.05);
border-left: 3px solid #667eea;
border-radius: 0 8px 8px 0;
margin-bottom: 15px;
}
/* Practice Challenge Styles */
.practice-challenge {
text-align: center;
margin-bottom: 30px;
}
.challenge-text {
font-size: 1.8em;
color: white;
margin-bottom: 25px;
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
.practice-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
max-width: 800px;
margin: 0 auto;
}
.practice-option {
background: rgba(255,255,255,0.9);
border: none;
border-radius: 15px;
padding: 20px;
font-size: 1.2em;
cursor: pointer;
transition: all 0.3s ease;
color: #333;
font-weight: 500;
}
.practice-option:hover {
background: rgba(255,255,255,1);
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
}
.practice-option.correct {
background: #4CAF50;
color: white;
animation: correctPulse 0.6s ease;
}
.practice-option.incorrect {
background: #F44336;
color: white;
animation: incorrectShake 0.6s ease;
}
@keyframes correctPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes incorrectShake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.practice-stats {
display: flex;
justify-content: space-around;
margin-top: 20px;
color: white;
font-size: 1.1em;
}
.stat-item {
text-align: center;
padding: 10px;
background: rgba(255,255,255,0.1);
border-radius: 10px;
backdrop-filter: blur(5px);
}
/* Control Buttons */
.discovery-btn {
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
border: none;
padding: 15px 30px;
border-radius: 25px;
font-size: 1.1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
margin: 0 10px;
}
.discovery-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0,0,0,0.3);
}
.discovery-btn:active {
transform: translateY(0);
}
.audio-btn {
background: none;
border: none;
font-size: 2em;
cursor: pointer;
color: #667eea;
margin-left: 15px;
transition: all 0.3s ease;
}
.audio-btn:hover {
transform: scale(1.2);
color: #764ba2;
}
/* Completion Message */
.completion-message {
text-align: center;
padding: 40px;
background: rgba(255,255,255,0.1);
border-radius: 20px;
backdrop-filter: blur(10px);
color: white;
}
.completion-title {
font-size: 2.5em;
margin-bottom: 20px;
color: #00ff88;
text-shadow: 0 2px 10px rgba(0,255,136,0.3);
}
.completion-stats {
font-size: 1.3em;
margin-bottom: 30px;
line-height: 1.6;
}
/* Responsive Design */
@media (max-width: 768px) {
.letter-discovery-wrapper {
padding: 15px;
}
.letter-display {
font-size: 6em;
}
.word-text {
font-size: 2em;
}
.challenge-text {
font-size: 1.5em;
}
.practice-grid {
grid-template-columns: 1fr;
}
}
`;
document.head.appendChild(styleSheet);
}
extractContent() {
logSh('🔍 Letter Discovery - Extracting content...', 'INFO');
// Check for letters in content or rawContent
const letters = this.content.letters || this.content.rawContent?.letters;
if (letters && Object.keys(letters).length > 0) {
this.letters = Object.keys(letters).sort();
this.letterWords = letters;
logSh(`📝 Found ${this.letters.length} letters with words`, 'INFO');
} else {
this.showNoLettersMessage();
return;
}
logSh(`🎯 Letter Discovery ready: ${this.letters.length} letters`, 'INFO');
}
showNoLettersMessage() {
this.container.innerHTML = `
🔤 Letter Discovery
❌ No letter structure found in this content.
This game requires content with a predefined letters system.
Try with content that includes letter-based learning material.
`;
}
init() {
this.container.innerHTML = `
Score: ${this.score}
Lives: ${this.lives}
Letter Discovery
Progress: 0/${this.letters.length}
`;
this.updateHUD();
}
start() {
this.showLetterCard();
}
updateHUD() {
const scoreDisplay = document.getElementById('score-display');
const livesDisplay = document.getElementById('lives-display');
const progressDisplay = document.getElementById('progress-display');
const phaseIndicator = document.getElementById('phase-indicator');
if (scoreDisplay) scoreDisplay.textContent = this.score;
if (livesDisplay) livesDisplay.textContent = this.lives;
if (this.currentPhase === 'letter-discovery') {
if (progressDisplay) progressDisplay.textContent = `${this.currentLetterIndex}/${this.letters.length}`;
if (phaseIndicator) phaseIndicator.textContent = 'Letter Discovery';
} else if (this.currentPhase === 'word-exploration') {
if (progressDisplay) progressDisplay.textContent = `${this.currentWordIndex}/${this.letterWords[this.currentLetter].length}`;
if (phaseIndicator) phaseIndicator.textContent = `Exploring Letter "${this.currentLetter}"`;
} else if (this.currentPhase === 'practice') {
if (progressDisplay) progressDisplay.textContent = `Round ${this.practiceRound + 1}/${this.maxPracticeRounds}`;
if (phaseIndicator) phaseIndicator.textContent = `Practice - Level ${this.practiceLevel}`;
}
}
showLetterCard() {
if (this.currentLetterIndex >= this.letters.length) {
this.showCompletion();
return;
}
const letter = this.letters[this.currentLetterIndex];
const gameContent = document.getElementById('game-content');
gameContent.innerHTML = `
${letter}
Letter "${letter}"
${this.getLetterPronunciation(letter)}
`;
// Store reference for button callbacks
window.currentLetterGame = this;
// Auto-play letter sound
setTimeout(() => this.playLetterSound(letter), 500);
}
getLetterPronunciation(letter) {
// Basic letter pronunciation guide
const pronunciations = {
'A': 'ay', 'B': 'bee', 'C': 'see', 'D': 'dee', 'E': 'ee',
'F': 'ef', 'G': 'gee', 'H': 'aych', 'I': 'eye', 'J': 'jay',
'K': 'kay', 'L': 'el', 'M': 'em', 'N': 'en', 'O': 'oh',
'P': 'pee', 'Q': 'cue', 'R': 'ar', 'S': 'ess', 'T': 'tee',
'U': 'you', 'V': 'vee', 'W': 'double-you', 'X': 'ex', 'Y': 'why', 'Z': 'zee'
};
return pronunciations[letter] || letter.toLowerCase();
}
playLetterSound(letter) {
if (window.SettingsManager && window.SettingsManager.speak) {
const speed = 0.8; // Slower for letters
window.SettingsManager.speak(letter, {
lang: this.content.language || 'en-US',
rate: speed
}).catch(error => {
console.warn('🔊 TTS failed for letter:', error);
});
}
}
discoverLetter() {
const letter = this.letters[this.currentLetterIndex];
this.discoveredLetters.push(letter);
this.score += 10;
this.onScoreUpdate(this.score);
// Start word exploration for this letter
this.currentLetter = letter;
this.currentPhase = 'word-exploration';
this.currentWordIndex = 0;
this.updateHUD();
this.showWordExploration();
}
showWordExploration() {
const words = this.letterWords[this.currentLetter];
if (!words || this.currentWordIndex >= words.length) {
// Finished exploring words for this letter
this.currentPhase = 'letter-discovery';
this.currentLetterIndex++;
this.updateHUD();
this.showLetterCard();
return;
}
const word = words[this.currentWordIndex];
const gameContent = document.getElementById('game-content');
gameContent.innerHTML = `
${word.word}
${word.translation}
${word.pronunciation ? `
[${word.pronunciation}]
` : ''}
${word.type ? `
${word.type}
` : ''}
${word.example ? `
"${word.example}"
` : ''}
`;
// Add word to discovered list
this.discoveredWords.push(word);
// Auto-play word sound
setTimeout(() => this.playWordSound(word.word), 500);
}
playWordSound(word) {
if (window.SettingsManager && window.SettingsManager.speak) {
const speed = 0.9;
window.SettingsManager.speak(word, {
lang: this.content.language || 'en-US',
rate: speed
}).catch(error => {
console.warn('🔊 TTS failed for word:', error);
});
}
}
nextWord() {
this.currentWordIndex++;
this.score += 5;
this.onScoreUpdate(this.score);
this.updateHUD();
this.showWordExploration();
}
showCompletion() {
const gameContent = document.getElementById('game-content');
const totalWords = Object.values(this.letterWords).reduce((sum, words) => sum + words.length, 0);
gameContent.innerHTML = `
🎉 All Letters Discovered!
Letters Discovered: ${this.discoveredLetters.length}
Words Learned: ${this.discoveredWords.length}
Final Score: ${this.score}
`;
}
startPractice() {
this.currentPhase = 'practice';
this.practiceLevel = 1;
this.practiceRound = 0;
this.practiceCorrectAnswers = 0;
this.practiceErrors = 0;
// Create mixed practice from all discovered words
this.currentPracticeItems = this.shuffleArray([...this.discoveredWords]);
this.updateHUD();
this.showPracticeChallenge();
}
showPracticeChallenge() {
if (this.practiceRound >= this.maxPracticeRounds) {
this.endPractice();
return;
}
const currentItem = this.currentPracticeItems[this.practiceRound % this.currentPracticeItems.length];
const gameContent = document.getElementById('game-content');
// Generate options (correct + 3 random)
const allWords = this.discoveredWords.filter(w => w.word !== currentItem.word);
const randomOptions = this.shuffleArray([...allWords]).slice(0, 3);
const options = this.shuffleArray([currentItem, ...randomOptions]);
gameContent.innerHTML = `
What does "${currentItem.word}" mean?
${options.map((option, index) => `
`).join('')}
Correct: ${this.practiceCorrectAnswers}
Errors: ${this.practiceErrors}
Round: ${this.practiceRound + 1}/${this.maxPracticeRounds}
`;
// Store correct answer for checking
this.currentCorrectAnswer = currentItem.word;
// Auto-play word
setTimeout(() => this.playWordSound(currentItem.word), 500);
}
selectPracticeAnswer(selectedIndex, selectedWord) {
const buttons = document.querySelectorAll('.practice-option');
const isCorrect = selectedWord === this.currentCorrectAnswer;
if (isCorrect) {
buttons[selectedIndex].classList.add('correct');
this.practiceCorrectAnswers++;
this.score += 10;
this.onScoreUpdate(this.score);
} else {
buttons[selectedIndex].classList.add('incorrect');
this.practiceErrors++;
// Show correct answer
buttons.forEach((btn, index) => {
if (btn.textContent.trim() === this.discoveredWords.find(w => w.word === this.currentCorrectAnswer)?.translation) {
btn.classList.add('correct');
}
});
}
setTimeout(() => {
this.practiceRound++;
this.updateHUD();
this.showPracticeChallenge();
}, 1500);
}
endPractice() {
const accuracy = Math.round((this.practiceCorrectAnswers / this.maxPracticeRounds) * 100);
const gameContent = document.getElementById('game-content');
gameContent.innerHTML = `
🏆 Practice Complete!
Accuracy: ${accuracy}%
Correct Answers: ${this.practiceCorrectAnswers}/${this.maxPracticeRounds}
Final Score: ${this.score}
`;
// End game
setTimeout(() => {
this.onGameEnd(this.score);
}, 3000);
}
shuffleArray(array) {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
}
restart() {
this.currentPhase = 'letter-discovery';
this.currentLetterIndex = 0;
this.discoveredLetters = [];
this.currentLetter = null;
this.currentWordIndex = 0;
this.discoveredWords = [];
this.score = 0;
this.lives = 3;
this.practiceLevel = 1;
this.practiceRound = 0;
this.practiceCorrectAnswers = 0;
this.practiceErrors = 0;
this.currentPracticeItems = [];
this.updateHUD();
this.start();
}
destroy() {
// Cleanup
if (window.currentLetterGame === this) {
delete window.currentLetterGame;
}
const styleSheet = document.getElementById('letter-discovery-styles');
if (styleSheet) {
styleSheet.remove();
}
}
}
// Register the game module
window.GameModules = window.GameModules || {};
window.GameModules.LetterDiscovery = LetterDiscovery;