diff --git a/CLAUDE.md b/CLAUDE.md index 2ad2e72..e147b9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -751,4 +751,54 @@ python3 -m http.server 8000 4. **Verify content compatibility** - Make sure your content has the data your game needs 5. **Use global CSS classes** - Don't reinvent layout, build on existing structure -**Remember: Most bugs are simple syntax errors (especially template literals) or missing module registrations. Check these first!** 🎯 \ No newline at end of file +**Remember: Most bugs are simple syntax errors (especially template literals) or missing module registrations. Check these first!** 🎯 + +## 🤝 **Collaborative Development Best Practices** + +**Critical lesson learned from real debugging sessions:** + +### **✅ Always Test Before Committing** + +**❌ BAD WORKFLOW:** +1. Write code +2. Immediately commit +3. Discover it doesn't work +4. Debug on committed broken code + +**✅ GOOD WORKFLOW:** +1. Write code +2. **TEST THOROUGHLY** +3. If broken → debug cooperatively +4. When working → commit + +### **🔍 Cooperative Debugging Method** + +**When user reports: "ça marche pas" or "y'a pas de lettres":** + +1. **Get specific symptoms** - Don't assume, ask exactly what they see +2. **Add targeted debug logs** - Console.log the exact variables in question +3. **Test together** - Have user run and report console output +4. **Analyze together** - Look at debug output to find root cause +5. **Fix precisely** - Target the exact issue, don't rewrite everything + +**Real Example - Letter Discovery Issue:** +```javascript +// ❌ ASSUMPTION: "Letters not working, must rewrite everything" + +// ✅ ACTUAL DEBUG: +console.log('🔍 DEBUG this.content.letters:', this.content.letters); // undefined +console.log('🔍 DEBUG this.content.rawContent?.letters:', this.content.rawContent?.letters); // {U: Array(4), V: Array(4), T: Array(4)} + +// ✅ PRECISE FIX: Check both locations +const letters = this.content.letters || this.content.rawContent?.letters; +``` + +### **🎯 Key Principles** + +- **Communication > Code** - Clear problem description saves hours +- **Debug logs > Assumptions** - Add console.log to see actual data +- **Test early, test often** - Don't tunnel vision on untested code +- **Pair debugging** - Two brains spot issues faster than one +- **Patience > Speed** - Taking time to understand beats rushing broken fixes + +**"C'est mieux quand on prend notre temps en coop plutot que de tunnel vision !"** 🎯 \ No newline at end of file diff --git a/js/content/WTA1B1.js b/js/content/WTA1B1.js index 4c5b3d3..86227c2 100644 --- a/js/content/WTA1B1.js +++ b/js/content/WTA1B1.js @@ -298,6 +298,100 @@ window.ContentModules.WTA1B1 = { } }, + // === LETTERS DISCOVERY SYSTEM === + letters: { + "U": [ + { + word: "unhappy", + translation: "不开心的", + type: "adjective", + pronunciation: "ʌnhæpi", + example: "The cat looks unhappy." + }, + { + word: "umbrella", + translation: "雨伞", + type: "noun", + pronunciation: "ʌmbrɛlə", + example: "I need an umbrella when it rains." + }, + { + word: "up", + translation: "向上", + type: "adverb", + pronunciation: "ʌp", + example: "The bird flies up high." + }, + { + word: "under", + translation: "在...下面", + type: "preposition", + pronunciation: "ʌndər", + example: "The cat hides under the table." + } + ], + "V": [ + { + word: "violet", + translation: "紫色的", + type: "adjective", + pronunciation: "vaɪələt", + example: "She has a violet dress." + }, + { + word: "van", + translation: "面包车", + type: "noun", + pronunciation: "væn", + example: "The vet drives a white van." + }, + { + word: "vet", + translation: "兽医", + type: "noun", + pronunciation: "vɛt", + example: "The vet takes care of pets." + }, + { + word: "vest", + translation: "背心", + type: "noun", + pronunciation: "vɛst", + example: "He wears a warm vest." + } + ], + "T": [ + { + word: "tall", + translation: "高的", + type: "adjective", + pronunciation: "tɔl", + example: "The teacher is very tall." + }, + { + word: "turtle", + translation: "海龟", + type: "noun", + pronunciation: "tɜrtəl", + example: "The turtle moves slowly." + }, + { + word: "tent", + translation: "帐篷", + type: "noun", + pronunciation: "tɛnt", + example: "We sleep in a tent when camping." + }, + { + word: "tiger", + translation: "老虎", + type: "noun", + pronunciation: "taɪgər", + example: "The tiger is a big cat." + } + ] + }, + vocabulary: { "unhappy": { "user_language": "不开心的", diff --git a/js/core/content-game-compatibility.js b/js/core/content-game-compatibility.js index 0cadddc..0c6efee 100644 --- a/js/core/content-game-compatibility.js +++ b/js/core/content-game-compatibility.js @@ -13,7 +13,8 @@ class ContentGameCompatibility { 'chinese-study': 35, 'story-builder': 35, 'story-reader': 40, - 'word-storm': 15 + 'word-storm': 15, + 'letter-discovery': 60 }; } @@ -91,12 +92,15 @@ class ContentGameCompatibility { hasCorrections: this.hasContent(content, 'corrections'), hasComprehension: this.hasContent(content, 'comprehension'), hasMatching: this.hasContent(content, 'matching'), + hasLetters: this.hasContent(content, 'letters'), // Compteurs vocabularyCount: this.countItems(content, 'vocabulary'), sentenceCount: this.countItems(content, 'sentences'), dialogueCount: this.countItems(content, 'dialogues'), - grammarCount: this.countItems(content, 'grammar') + grammarCount: this.countItems(content, 'grammar'), + letterCount: this.countItems(content, 'letters'), + letterWordsCount: this.calculateAverageWordsPerLetter(content) }; } @@ -133,6 +137,9 @@ class ContentGameCompatibility { case 'word-storm': return this.calculateWordStormCompat(capabilities); + case 'letter-discovery': + return this.calculateLetterDiscoveryCompat(capabilities); + default: return { compatible: true, score: 50, reason: 'Jeu non spécifiquement analysé' }; } @@ -390,6 +397,36 @@ class ContentGameCompatibility { }; } + calculateLetterDiscoveryCompat(capabilities) { + let score = 0; + const reasons = []; + + // Letter Discovery requires predefined letters structure + if (capabilities.hasLetters) { + score += 80; + const letterCount = capabilities.letterCount || 'unknown'; + reasons.push(`Structure de lettres prédéfinie (${letterCount} lettres)`); + } else { + return { + compatible: false, + score: 0, + reason: 'Nécessite une structure de lettres prédéfinie (content.letters)' + }; + } + + // Bonus for well-structured letter content + if (capabilities.letterWordsCount && capabilities.letterWordsCount >= 3) { + score += 20; + reasons.push(`${capabilities.letterWordsCount} mots par lettre en moyenne`); + } + + return { + compatible: score >= 60, + score, + reason: score >= 60 ? `Compatible: ${reasons.join(', ')}` : 'Structure de lettres insuffisante' + }; + } + // === UTILITAIRES === hasContent(content, type) { @@ -458,6 +495,23 @@ class ContentGameCompatibility { return 0; } + calculateAverageWordsPerLetter(content) { + const letters = content.letters || content.rawContent?.letters || content.adaptedContent?.letters; + if (!letters || typeof letters !== 'object') return 0; + + let totalWords = 0; + let letterCount = 0; + + Object.values(letters).forEach(letterWords => { + if (Array.isArray(letterWords)) { + totalWords += letterWords.length; + letterCount++; + } + }); + + return letterCount > 0 ? Math.round(totalWords / letterCount) : 0; + } + getGameRequirements(gameType) { const requirements = { 'whack-a-mole': ['5+ mots de vocabulaire OU 3+ phrases', 'Contenu simple et répétitif'], @@ -469,7 +523,8 @@ class ContentGameCompatibility { 'chinese-study': ['Vocabulaire et phrases chinoises', 'Audio recommandé'], 'story-builder': ['Dialogues OU 5+ phrases', 'Vocabulaire varié'], 'story-reader': ['Textes à lire, dialogues recommandés', 'Contenu narratif'], - 'word-storm': ['3+ mots de vocabulaire', 'Prononciations recommandées'] + 'word-storm': ['3+ mots de vocabulaire', 'Prononciations recommandées'], + 'letter-discovery': ['Structure de lettres prédéfinie (content.letters)', 'Lettres avec mots associés'] }; return requirements[gameType] || ['Contenu de base']; @@ -484,7 +539,8 @@ class ContentGameCompatibility { 'adventure-reader': 'Aventure narrative nécessitant contenu riche', 'chinese-study': 'Optimisé pour apprentissage du chinois', 'story-builder': 'Construction narrative nécessitant éléments variés', - 'story-reader': 'Lecture d\'histoires nécessitant contenu narratif' + 'story-reader': 'Lecture d\'histoires nécessitant contenu narratif', + 'letter-discovery': 'Apprentissage par lettres nécessitant structure prédéfinie' }; return reasons[gameType] || 'Compatibilité non évaluée spécifiquement'; diff --git a/js/games/letter-discovery.js b/js/games/letter-discovery.js index c130b14..6147d52 100644 --- a/js/games/letter-discovery.js +++ b/js/games/letter-discovery.js @@ -199,6 +199,29 @@ class LetterDiscovery { 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 */ @@ -369,51 +392,33 @@ class LetterDiscovery { extractContent() { logSh('🔍 Letter Discovery - Extracting content...', 'INFO'); - // Check if content has letter structure - if (this.content.letters) { - this.letters = Object.keys(this.content.letters); - this.letterWords = this.content.letters; + // 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 { - // Fallback: Create letter structure from vocabulary - this.generateLetterStructure(); - } - - if (this.letters.length === 0) { - throw new Error('No letters found in content'); + this.showNoLettersMessage(); + return; } logSh(`🎯 Letter Discovery ready: ${this.letters.length} letters`, 'INFO'); } - generateLetterStructure() { - logSh('🔧 Generating letter structure from vocabulary...', 'INFO'); - - const letterMap = {}; - - if (this.content.vocabulary) { - Object.keys(this.content.vocabulary).forEach(word => { - const firstLetter = word.charAt(0).toUpperCase(); - if (!letterMap[firstLetter]) { - letterMap[firstLetter] = []; - } - - const wordData = this.content.vocabulary[word]; - letterMap[firstLetter].push({ - word: word, - translation: typeof wordData === 'string' ? wordData : wordData.translation || wordData.user_language, - pronunciation: wordData.pronunciation || wordData.prononciation, - type: wordData.type, - image: wordData.image, - audioFile: wordData.audioFile - }); - }); - } - - this.letters = Object.keys(letterMap).sort(); - this.letterWords = letterMap; - - logSh(`📝 Generated ${this.letters.length} letters from vocabulary`, 'INFO'); + showNoLettersMessage() { + this.container.innerHTML = ` +
❌ 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.
+ +