Spaces:
Sleeping
Sleeping
Fix level progression and word validation issues
Browse files- Add validation to prevent selecting all-caps words that appear in passages
- Implement proper 2-round requirement before level advancement
- Update UI to show progress feedback (e.g., "1/2 rounds completed")
- Fix bug where words like "VOLUME" were selected when visible in text
- src/aiService.js +9 -3
- src/app.js +11 -3
- src/clozeGameEngine.js +16 -3
src/aiService.js
CHANGED
|
@@ -428,7 +428,7 @@ Return as JSON: {"passage1": {...}, "passage2": {...}}`
|
|
| 428 |
parsed.passage2.words = parsed.passage2.words.filter(word => word && word.trim() !== '');
|
| 429 |
|
| 430 |
// Filter problematic words and validate word lengths based on level
|
| 431 |
-
const validateWords = (words) => {
|
| 432 |
const problematicWords = ['negro', 'retard', 'retarded', 'nigger', 'chinaman', 'jap', 'gypsy', 'savage', 'primitive', 'heathen'];
|
| 433 |
return words.filter(word => {
|
| 434 |
const cleanWord = word.replace(/[^a-zA-Z]/g, '');
|
|
@@ -437,6 +437,12 @@ Return as JSON: {"passage1": {...}, "passage2": {...}}`
|
|
| 437 |
// Skip problematic words
|
| 438 |
if (problematicWords.includes(lowerWord)) return false;
|
| 439 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
// Check length constraints
|
| 441 |
if (level <= 2) {
|
| 442 |
return cleanWord.length >= 4 && cleanWord.length <= 7;
|
|
@@ -451,8 +457,8 @@ Return as JSON: {"passage1": {...}, "passage2": {...}}`
|
|
| 451 |
const originalP1Count = parsed.passage1.words.length;
|
| 452 |
const originalP2Count = parsed.passage2.words.length;
|
| 453 |
|
| 454 |
-
parsed.passage1.words = validateWords(parsed.passage1.words);
|
| 455 |
-
parsed.passage2.words = validateWords(parsed.passage2.words);
|
| 456 |
|
| 457 |
console.log(`β
Level ${level} batch validation: P1 ${parsed.passage1.words.length}/${originalP1Count}, P2 ${parsed.passage2.words.length}/${originalP2Count} words passed`);
|
| 458 |
|
|
|
|
| 428 |
parsed.passage2.words = parsed.passage2.words.filter(word => word && word.trim() !== '');
|
| 429 |
|
| 430 |
// Filter problematic words and validate word lengths based on level
|
| 431 |
+
const validateWords = (words, passageText) => {
|
| 432 |
const problematicWords = ['negro', 'retard', 'retarded', 'nigger', 'chinaman', 'jap', 'gypsy', 'savage', 'primitive', 'heathen'];
|
| 433 |
return words.filter(word => {
|
| 434 |
const cleanWord = word.replace(/[^a-zA-Z]/g, '');
|
|
|
|
| 437 |
// Skip problematic words
|
| 438 |
if (problematicWords.includes(lowerWord)) return false;
|
| 439 |
|
| 440 |
+
// Check if word appears in all caps in the passage (like "VOLUME")
|
| 441 |
+
if (passageText.includes(word.toUpperCase()) && word === word.toUpperCase()) {
|
| 442 |
+
console.log(`Skipping all-caps word: ${word}`);
|
| 443 |
+
return false;
|
| 444 |
+
}
|
| 445 |
+
|
| 446 |
// Check length constraints
|
| 447 |
if (level <= 2) {
|
| 448 |
return cleanWord.length >= 4 && cleanWord.length <= 7;
|
|
|
|
| 457 |
const originalP1Count = parsed.passage1.words.length;
|
| 458 |
const originalP2Count = parsed.passage2.words.length;
|
| 459 |
|
| 460 |
+
parsed.passage1.words = validateWords(parsed.passage1.words, passage1);
|
| 461 |
+
parsed.passage2.words = validateWords(parsed.passage2.words, passage2);
|
| 462 |
|
| 463 |
console.log(`β
Level ${level} batch validation: P1 ${parsed.passage1.words.length}/${originalP1Count}, P2 ${parsed.passage2.words.length}/${originalP2Count} words passed`);
|
| 464 |
|
src/app.js
CHANGED
|
@@ -69,9 +69,11 @@ class App {
|
|
| 69 |
<strong>${roundData.title}</strong> by ${roundData.author}
|
| 70 |
`;
|
| 71 |
|
| 72 |
-
// Show level information
|
| 73 |
const blanksCount = roundData.blanks.length;
|
| 74 |
-
|
|
|
|
|
|
|
| 75 |
|
| 76 |
// Show contextualization from AI agent
|
| 77 |
this.elements.contextualization.innerHTML = `
|
|
@@ -160,7 +162,13 @@ class App {
|
|
| 160 |
}
|
| 161 |
|
| 162 |
if (results.passed) {
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
this.elements.result.className = 'mt-4 text-center font-semibold text-green-600';
|
| 165 |
} else {
|
| 166 |
if (this.game.currentLevel >= 3) {
|
|
|
|
| 69 |
<strong>${roundData.title}</strong> by ${roundData.author}
|
| 70 |
`;
|
| 71 |
|
| 72 |
+
// Show level information
|
| 73 |
const blanksCount = roundData.blanks.length;
|
| 74 |
+
const levelInfo = `Level ${this.game.currentLevel} β’ ${blanksCount} blank${blanksCount > 1 ? 's' : ''}`;
|
| 75 |
+
|
| 76 |
+
this.elements.roundInfo.innerHTML = levelInfo;
|
| 77 |
|
| 78 |
// Show contextualization from AI agent
|
| 79 |
this.elements.contextualization.innerHTML = `
|
|
|
|
| 162 |
}
|
| 163 |
|
| 164 |
if (results.passed) {
|
| 165 |
+
// Check if this completes the requirements for level advancement
|
| 166 |
+
const roundsCompleted = this.game.roundsPassedAtCurrentLevel + 1; // +1 for this round
|
| 167 |
+
if (roundsCompleted >= 2) {
|
| 168 |
+
message += ` - Excellent! Advancing to Level ${this.game.currentLevel + 1}! π`;
|
| 169 |
+
} else {
|
| 170 |
+
message += ` - Great job! ${roundsCompleted}/2 rounds completed for Level ${this.game.currentLevel + 1}`;
|
| 171 |
+
}
|
| 172 |
this.elements.result.className = 'mt-4 text-center font-semibold text-green-600';
|
| 173 |
} else {
|
| 174 |
if (this.game.currentLevel >= 3) {
|
src/clozeGameEngine.js
CHANGED
|
@@ -25,6 +25,9 @@ class ClozeGame {
|
|
| 25 |
this.currentBooks = []; // Array of two books per round
|
| 26 |
this.passages = []; // Array of two passages per round
|
| 27 |
this.currentPassageIndex = 0; // 0 for first passage, 1 for second
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
| 29 |
|
| 30 |
async initialize() {
|
|
@@ -749,11 +752,21 @@ class ClozeGame {
|
|
| 749 |
// Always increment round counter
|
| 750 |
this.currentRound++;
|
| 751 |
|
| 752 |
-
//
|
| 753 |
if (roundPassed) {
|
| 754 |
-
this.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 755 |
}
|
| 756 |
-
// If failed, stay at same level
|
| 757 |
|
| 758 |
// Clear chat conversations for new round
|
| 759 |
this.chatService.clearConversations();
|
|
|
|
| 25 |
this.currentBooks = []; // Array of two books per round
|
| 26 |
this.passages = []; // Array of two passages per round
|
| 27 |
this.currentPassageIndex = 0; // 0 for first passage, 1 for second
|
| 28 |
+
|
| 29 |
+
// Level progression tracking
|
| 30 |
+
this.roundsPassedAtCurrentLevel = 0; // Track successful rounds at current level
|
| 31 |
}
|
| 32 |
|
| 33 |
async initialize() {
|
|
|
|
| 752 |
// Always increment round counter
|
| 753 |
this.currentRound++;
|
| 754 |
|
| 755 |
+
// Track successful rounds and advance level after 2 successful rounds
|
| 756 |
if (roundPassed) {
|
| 757 |
+
this.roundsPassedAtCurrentLevel++;
|
| 758 |
+
console.log(`Round passed! Total rounds passed at level ${this.currentLevel}: ${this.roundsPassedAtCurrentLevel}`);
|
| 759 |
+
|
| 760 |
+
// Advance level after 2 successful rounds
|
| 761 |
+
if (this.roundsPassedAtCurrentLevel >= 2) {
|
| 762 |
+
this.currentLevel++;
|
| 763 |
+
this.roundsPassedAtCurrentLevel = 0; // Reset counter for new level
|
| 764 |
+
console.log(`Advancing to level ${this.currentLevel} after 2 successful rounds`);
|
| 765 |
+
}
|
| 766 |
+
} else {
|
| 767 |
+
// Failed round - do not reset the counter, user must accumulate 2 passes
|
| 768 |
+
console.log(`Round failed. Still need ${2 - this.roundsPassedAtCurrentLevel} more passed round(s) to advance from level ${this.currentLevel}`);
|
| 769 |
}
|
|
|
|
| 770 |
|
| 771 |
// Clear chat conversations for new round
|
| 772 |
this.chatService.clearConversations();
|