Spaces:
Running
Running
Update index.html
Browse files- index.html +525 -1
index.html
CHANGED
|
@@ -570,4 +570,528 @@
|
|
| 570 |
|
| 571 |
// Vowel Teams (30 patterns)
|
| 572 |
{ pattern: "ai", pronunciation: "ay", category: "Vowels", words: ["rain", "train", "wait", "mail", "pain", "brain", "chain", "sail", "tail", "frail"] },
|
| 573 |
-
{ pattern: "ay", pronunciation: "ay",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 570 |
|
| 571 |
// Vowel Teams (30 patterns)
|
| 572 |
{ pattern: "ai", pronunciation: "ay", category: "Vowels", words: ["rain", "train", "wait", "mail", "pain", "brain", "chain", "sail", "tail", "frail"] },
|
| 573 |
+
{ pattern: "ay", pronunciation: "ay", category: "Vowels", words: ["day", "play", "say", "way", "may", "pay", "stay", "gray", "clay", "spray"] },
|
| 574 |
+
{ pattern: "ee", pronunciation: "ee", category: "Vowels", words: ["tree", "see", "sleep", "bee", "feet", "green", "sheep", "three", "queen", "sweet"] },
|
| 575 |
+
{ pattern: "ea", pronunciation: "ee", category: "Vowels", words: ["eat", "sea", "team", "leaf", "meal", "seat", "beach", "peach", "teach", "cheap"] },
|
| 576 |
+
{ pattern: "ie", pronunciation: "ee", category: "Vowels", words: ["piece", "chief", "field", "believe", "grief", "relief", "thief", "yield", "shield", "niece"] },
|
| 577 |
+
{ pattern: "y", pronunciation: "ee", category: "Vowels", words: ["happy", "baby", "funny", "sunny", "candy", "city", "puppy", "lady", "penny", "cherry"] },
|
| 578 |
+
{ pattern: "y", pronunciation: "eye", category: "Vowels", words: ["cry", "fly", "my", "dry", "sky", "try", "why", "shy", "spy", "ply"] },
|
| 579 |
+
{ pattern: "igh", pronunciation: "eye", category: "Vowels", words: ["night", "light", "high", "right", "fight", "bright", "flight", "sight", "tight", "might"] },
|
| 580 |
+
{ pattern: "oa", pronunciation: "oh", category: "Vowels", words: ["boat", "coat", "road", "goat", "soap", "toast", "float", "throat", "coach", "roast"] },
|
| 581 |
+
{ pattern: "oe", pronunciation: "oh", category: "Vowels", words: ["toe", "hoe", "foe", "doe", "woe", "aloe", "canoe", "oboe", "roe", "shoe"] },
|
| 582 |
+
{ pattern: "ow", pronunciation: "oh", category: "Vowels", words: ["snow", "grow", "show", "bow", "row", "blow", "flow", "slow", "throw", "arrow"] },
|
| 583 |
+
{ pattern: "ou", pronunciation: "ow", category: "Vowels", words: ["out", "cloud", "found", "house", "mouse", "shout", "proud", "round", "sound", "count"] },
|
| 584 |
+
{ pattern: "ow", pronunciation: "ow", category: "Vowels", words: ["cow", "town", "down", "how", "now", "brown", "crowd", "flower", "power", "tower"] },
|
| 585 |
+
{ pattern: "oi", pronunciation: "oy", category: "Vowels", words: ["coin", "boil", "soil", "oil", "noise", "voice", "choice", "spoil", "join", "point"] },
|
| 586 |
+
{ pattern: "oy", pronunciation: "oy", category: "Vowels", words: ["toy", "boy", "enjoy", "joy", "soy", "annoy", "destroy", "employ", "loyal", "royal"] },
|
| 587 |
+
{ pattern: "oo", pronunciation: "oo (long)", category: "Vowels", words: ["moon", "spoon", "tooth", "food", "room", "boot", "root", "pool", "school", "cool"] },
|
| 588 |
+
{ pattern: "oo", pronunciation: "oo (short)", category: "Vowels", words: ["book", "look", "foot", "good", "hood", "wood", "stood", "cook", "hook", "shook"] },
|
| 589 |
+
{ pattern: "ew", pronunciation: "yoo", category: "Vowels", words: ["few", "dew", "new", "chew", "news", "jewel", "nephew", "stew", "view", "review"] },
|
| 590 |
+
{ pattern: "ue", pronunciation: "yoo", category: "Vowels", words: ["blue", "true", "clue", "glue", "value", "rescue", "argue", "venue", "statue", "virtue"] },
|
| 591 |
+
{ pattern: "au", pronunciation: "aw", category: "Vowels", words: ["haul", "autumn", "launch", "cause", "pause", "audio", "author", "sauce", "haunt", "fault"] },
|
| 592 |
+
{ pattern: "aw", pronunciation: "aw", category: "Vowels", words: ["saw", "draw", "claw", "law", "paw", "straw", "jaw", "lawn", "dawn", "yawn"] },
|
| 593 |
+
{ pattern: "ei", pronunciation: "ay", category: "Vowels", words: ["veil", "eight", "weigh", "neighbor", "reign", "freight", "vein", "beige", "sleigh", "rein"] },
|
| 594 |
+
{ pattern: "ey", pronunciation: "ay", category: "Vowels", words: ["they", "prey", "grey", "convey", "survey", "obey", "disobey", "hey", "whey", "abeyance"] },
|
| 595 |
+
{ pattern: "ei", pronunciation: "ee", category: "Vowels", words: ["receive", "ceiling", "deceive", "perceive", "conceive", "receipt", "seize", "leisure", "weird", "either"] },
|
| 596 |
+
{ pattern: "ey", pronunciation: "ee", category: "Vowels", words: ["key", "monkey", "honey", "money", "donkey", "turkey", "valley", "journey", "chimney", "kidney"] },
|
| 597 |
+
{ pattern: "ie", pronunciation: "eye", category: "Vowels", words: ["pie", "tie", "die", "lie", "fried", "cried", "tried", "dried", "spied", "supplied"] },
|
| 598 |
+
{ pattern: "ea", pronunciation: "eh", category: "Vowels", words: ["bread", "head", "ready", "heavy", "weather", "feather", "leather", "sweater", "treasure", "pleasure"] },
|
| 599 |
+
{ pattern: "ou", pronunciation: "oo", category: "Vowels", words: ["soup", "group", "youth", "coupon", "routine", "souvenir", "you", "through", "wound", "route"] },
|
| 600 |
+
{ pattern: "ui", pronunciation: "oo", category: "Vowels", words: ["fruit", "juice", "suit", "bruise", "cruise", "suitcase", "recruit", "biscuit", "circuit", "pursuit"] },
|
| 601 |
+
{ pattern: "ew", pronunciation: "oo", category: "Vowels", words: ["blew", "grew", "flew", "crew", "chew", "drew", "threw", "screw", "brew", "jewel"] },
|
| 602 |
+
|
| 603 |
+
// Word Endings (15 patterns)
|
| 604 |
+
{ pattern: "tion", pronunciation: "shun", category: "Endings", words: ["action", "station", "nation", "education", "vacation", "celebration", "information", "communication", "population", "attention"] },
|
| 605 |
+
{ pattern: "sion", pronunciation: "zhun", category: "Endings", words: ["vision", "decision", "confusion", "television", "division", "conclusion", "explosion", "invasion", "occasion", "persuasion"] },
|
| 606 |
+
{ pattern: "sion", pronunciation: "shun", category: "Endings", words: ["mission", "passion", "session", "expression", "discussion", "permission", "profession", "impression", "depression", "compression"] },
|
| 607 |
+
{ pattern: "cian", pronunciation: "shun", category: "Endings", words: ["musician", "magician", "physician", "politician", "electrician", "mathematician", "technician", "beautician", "dietician", "statistician"] },
|
| 608 |
+
{ pattern: "ture", pronunciation: "chur", category: "Endings", words: ["picture", "future", "nature", "creature", "adventure", "furniture", "temperature", "signature", "mixture", "literature"] },
|
| 609 |
+
{ pattern: "le", pronunciation: "ul", category: "Endings", words: ["table", "candle", "little", "apple", "bubble", "puzzle", "turtle", "castle", "bottle", "middle"] },
|
| 610 |
+
{ pattern: "ous", pronunciation: "us", category: "Endings", words: ["famous", "nervous", "dangerous", "enormous", "generous", "humorous", "mysterious", "numerous", "precious", "serious"] },
|
| 611 |
+
{ pattern: "cious", pronunciation: "shus", category: "Endings", words: ["delicious", "suspicious", "precious", "conscious", "vicious", "gracious", "spacious", "judicious", "malicious", "auspicious"] },
|
| 612 |
+
{ pattern: "tious", pronunciation: "shus", category: "Endings", words: ["ambitious", "cautious", "nutritious", "fictitious", "infectious", "superstitious", "contentious", "ostentatious", "pretentious", "conscientious"] },
|
| 613 |
+
{ pattern: "cial", pronunciation: "shul", category: "Endings", words: ["special", "social", "official", "commercial", "financial", "racial", "crucial", "judicial", "beneficial", "superficial"] },
|
| 614 |
+
{ pattern: "tial", pronunciation: "shul", category: "Endings", words: ["partial", "essential", "initial", "potential", "residential", "confidential", "substantial", "sequential", "influential", "presidential"] },
|
| 615 |
+
{ pattern: "age", pronunciation: "ij", category: "Endings", words: ["cage", "page", "stage", "message", "village", "damage", "manage", "package", "passage", "voyage"] },
|
| 616 |
+
{ pattern: "age", pronunciation: "azh", category: "Endings", words: ["garage", "massage", "mirage", "corsage", "camouflage", "espionage", "collage", "montage", "sabotage", "badinage"] },
|
| 617 |
+
{ pattern: "ine", pronunciation: "in", category: "Endings", words: ["engine", "imagine", "medicine", "discipline", "determine", "famine", "routine", "doctrine", "genuine", "vaccine"] },
|
| 618 |
+
{ pattern: "ine", pronunciation: "ine", category: "Endings", words: ["pine", "fine", "line", "mine", "nine", "vine", "wine", "shine", "whine", "divine"] }
|
| 619 |
+
];
|
| 620 |
+
|
| 621 |
+
// Game state
|
| 622 |
+
let currentPattern = null;
|
| 623 |
+
let currentWord = "";
|
| 624 |
+
let selectedOption = null;
|
| 625 |
+
let score = 0;
|
| 626 |
+
let streak = 0;
|
| 627 |
+
let correctCount = 0;
|
| 628 |
+
let totalQuestions = 0;
|
| 629 |
+
let patternsMastered = new Set();
|
| 630 |
+
let timer = 30;
|
| 631 |
+
let timerInterval = null;
|
| 632 |
+
let gameActive = false;
|
| 633 |
+
let wordHistory = [];
|
| 634 |
+
let currentCategory = "all";
|
| 635 |
+
|
| 636 |
+
// DOM Elements
|
| 637 |
+
const wordDisplay = document.getElementById('wordDisplay');
|
| 638 |
+
const wordHint = document.getElementById('wordHint');
|
| 639 |
+
const patternOptions = document.getElementById('patternOptions');
|
| 640 |
+
const feedback = document.getElementById('feedback');
|
| 641 |
+
const submitBtn = document.getElementById('submitBtn');
|
| 642 |
+
const nextBtn = document.getElementById('nextBtn');
|
| 643 |
+
const hintBtn = document.getElementById('hintBtn');
|
| 644 |
+
const resetBtn = document.getElementById('resetBtn');
|
| 645 |
+
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
|
| 646 |
+
const exportBtn = document.getElementById('exportBtn');
|
| 647 |
+
const scoreEl = document.getElementById('score');
|
| 648 |
+
const streakEl = document.getElementById('streak');
|
| 649 |
+
const correctCountEl = document.getElementById('correctCount');
|
| 650 |
+
const totalQuestionsEl = document.getElementById('totalQuestions');
|
| 651 |
+
const accuracyEl = document.getElementById('accuracy');
|
| 652 |
+
const progressFill = document.getElementById('progressFill');
|
| 653 |
+
const progressText = document.getElementById('progressText');
|
| 654 |
+
const historyList = document.getElementById('historyList');
|
| 655 |
+
const historyCount = document.getElementById('historyCount');
|
| 656 |
+
const timerEl = document.getElementById('timer');
|
| 657 |
+
const completedWord = document.getElementById('completedWord');
|
| 658 |
+
const patternCategories = document.getElementById('patternCategories');
|
| 659 |
+
|
| 660 |
+
// Initialize game
|
| 661 |
+
function initGame() {
|
| 662 |
+
loadGameState();
|
| 663 |
+
setupEventListeners();
|
| 664 |
+
renderCategoryFilters();
|
| 665 |
+
generateNewWord();
|
| 666 |
+
updateStats();
|
| 667 |
+
updateHistory();
|
| 668 |
+
}
|
| 669 |
+
|
| 670 |
+
// Load game state from localStorage
|
| 671 |
+
function loadGameState() {
|
| 672 |
+
const savedState = JSON.parse(localStorage.getItem('wordBuilderState'));
|
| 673 |
+
if (savedState) {
|
| 674 |
+
score = savedState.score || 0;
|
| 675 |
+
streak = savedState.streak || 0;
|
| 676 |
+
correctCount = savedState.correctCount || 0;
|
| 677 |
+
totalQuestions = savedState.totalQuestions || 0;
|
| 678 |
+
patternsMastered = new Set(savedState.patternsMastered || []);
|
| 679 |
+
wordHistory = savedState.wordHistory || [];
|
| 680 |
+
currentCategory = savedState.currentCategory || "all";
|
| 681 |
+
}
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
// Save game state to localStorage
|
| 685 |
+
function saveGameState() {
|
| 686 |
+
const state = {
|
| 687 |
+
score,
|
| 688 |
+
streak,
|
| 689 |
+
correctCount,
|
| 690 |
+
totalQuestions,
|
| 691 |
+
patternsMastered: Array.from(patternsMastered),
|
| 692 |
+
wordHistory,
|
| 693 |
+
currentCategory,
|
| 694 |
+
timestamp: Date.now()
|
| 695 |
+
};
|
| 696 |
+
localStorage.setItem('wordBuilderState', JSON.stringify(state));
|
| 697 |
+
}
|
| 698 |
+
|
| 699 |
+
// Render category filters
|
| 700 |
+
function renderCategoryFilters() {
|
| 701 |
+
const categories = ['all', 'Consonant', 'Vowels', 'Endings'];
|
| 702 |
+
|
| 703 |
+
patternCategories.innerHTML = '';
|
| 704 |
+
categories.forEach(category => {
|
| 705 |
+
const count = category === 'all'
|
| 706 |
+
? wordBuilderData.length
|
| 707 |
+
: wordBuilderData.filter(p => p.category === category).length;
|
| 708 |
+
|
| 709 |
+
const tag = document.createElement('div');
|
| 710 |
+
tag.className = `category-tag ${currentCategory === category ? 'active' : ''}`;
|
| 711 |
+
tag.textContent = `${category} (${count})`;
|
| 712 |
+
tag.dataset.category = category;
|
| 713 |
+
|
| 714 |
+
tag.addEventListener('click', () => {
|
| 715 |
+
currentCategory = category;
|
| 716 |
+
renderCategoryFilters();
|
| 717 |
+
generateNewWord();
|
| 718 |
+
saveGameState();
|
| 719 |
+
});
|
| 720 |
+
|
| 721 |
+
patternCategories.appendChild(tag);
|
| 722 |
+
});
|
| 723 |
+
}
|
| 724 |
+
|
| 725 |
+
// Setup event listeners
|
| 726 |
+
function setupEventListeners() {
|
| 727 |
+
submitBtn.addEventListener('click', checkAnswer);
|
| 728 |
+
nextBtn.addEventListener('click', generateNewWord);
|
| 729 |
+
hintBtn.addEventListener('click', showHint);
|
| 730 |
+
resetBtn.addEventListener('click', resetGame);
|
| 731 |
+
clearHistoryBtn.addEventListener('click', clearHistory);
|
| 732 |
+
exportBtn.addEventListener('click', exportHistory);
|
| 733 |
+
|
| 734 |
+
// Keyboard shortcuts
|
| 735 |
+
document.addEventListener('keydown', (e) => {
|
| 736 |
+
if (e.key >= '1' && e.key <= '8') {
|
| 737 |
+
const index = parseInt(e.key) - 1;
|
| 738 |
+
const options = document.querySelectorAll('.pattern-option');
|
| 739 |
+
if (options[index]) {
|
| 740 |
+
options[index].click();
|
| 741 |
+
}
|
| 742 |
+
} else if (e.key === 'Enter') {
|
| 743 |
+
if (gameActive) {
|
| 744 |
+
checkAnswer();
|
| 745 |
+
} else {
|
| 746 |
+
generateNewWord();
|
| 747 |
+
}
|
| 748 |
+
} else if (e.key === ' ') {
|
| 749 |
+
e.preventDefault();
|
| 750 |
+
if (!gameActive) {
|
| 751 |
+
generateNewWord();
|
| 752 |
+
}
|
| 753 |
+
}
|
| 754 |
+
});
|
| 755 |
+
}
|
| 756 |
+
|
| 757 |
+
// Generate a new word with continuous blank line
|
| 758 |
+
function generateNewWord() {
|
| 759 |
+
// Stop any existing timer
|
| 760 |
+
if (timerInterval) clearInterval(timerInterval);
|
| 761 |
+
|
| 762 |
+
// Reset UI
|
| 763 |
+
feedback.style.display = 'none';
|
| 764 |
+
selectedOption = null;
|
| 765 |
+
completedWord.textContent = '';
|
| 766 |
+
|
| 767 |
+
// Filter patterns by category
|
| 768 |
+
let filteredPatterns = wordBuilderData;
|
| 769 |
+
if (currentCategory !== 'all') {
|
| 770 |
+
filteredPatterns = wordBuilderData.filter(p => p.category === currentCategory);
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
// Select random pattern
|
| 774 |
+
const randomIndex = Math.floor(Math.random() * filteredPatterns.length);
|
| 775 |
+
currentPattern = filteredPatterns[randomIndex];
|
| 776 |
+
|
| 777 |
+
// Select random word from pattern
|
| 778 |
+
const wordIndex = Math.floor(Math.random() * currentPattern.words.length);
|
| 779 |
+
currentWord = currentPattern.words[wordIndex];
|
| 780 |
+
|
| 781 |
+
// Find pattern in word (case insensitive)
|
| 782 |
+
const patternIndex = currentWord.toLowerCase().indexOf(currentPattern.pattern.toLowerCase());
|
| 783 |
+
|
| 784 |
+
if (patternIndex !== -1) {
|
| 785 |
+
const before = currentWord.substring(0, patternIndex);
|
| 786 |
+
const after = currentWord.substring(patternIndex + currentPattern.pattern.length);
|
| 787 |
+
|
| 788 |
+
// Display with continuous blank line
|
| 789 |
+
wordDisplay.innerHTML = `
|
| 790 |
+
${before ? `<span class="word-part">${before}</span>` : ''}
|
| 791 |
+
<div class="continuous-blank">
|
| 792 |
+
<div class="blank-line"></div>
|
| 793 |
+
</div>
|
| 794 |
+
${after ? `<span class="word-part">${after}</span>` : ''}
|
| 795 |
+
`;
|
| 796 |
+
|
| 797 |
+
// Update hint
|
| 798 |
+
wordHint.textContent = `Category: ${currentPattern.category} | Sound: "${currentPattern.pronunciation}"`;
|
| 799 |
+
} else {
|
| 800 |
+
// Fallback: show entire word as blank (shouldn't happen)
|
| 801 |
+
wordDisplay.innerHTML = `
|
| 802 |
+
<div class="continuous-blank" style="min-width: 250px;">
|
| 803 |
+
<div class="blank-line"></div>
|
| 804 |
+
</div>
|
| 805 |
+
`;
|
| 806 |
+
wordHint.textContent = "Find the phonics pattern that completes this word";
|
| 807 |
+
}
|
| 808 |
+
|
| 809 |
+
// Generate options
|
| 810 |
+
generateOptions();
|
| 811 |
+
|
| 812 |
+
// Start timer
|
| 813 |
+
timer = 30;
|
| 814 |
+
timerEl.textContent = `Time: ${timer}s`;
|
| 815 |
+
timerEl.style.color = "#ffd166";
|
| 816 |
+
gameActive = true;
|
| 817 |
+
|
| 818 |
+
timerInterval = setInterval(() => {
|
| 819 |
+
timer--;
|
| 820 |
+
timerEl.textContent = `Time: ${timer}s`;
|
| 821 |
+
|
| 822 |
+
if (timer <= 0) {
|
| 823 |
+
clearInterval(timerInterval);
|
| 824 |
+
gameActive = false;
|
| 825 |
+
feedback.textContent = "Time's up! The answer was: " + currentPattern.pattern;
|
| 826 |
+
feedback.className = "feedback incorrect";
|
| 827 |
+
feedback.style.display = "block";
|
| 828 |
+
addToHistory(false, false);
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
if (timer <= 10) {
|
| 832 |
+
timerEl.style.color = "#ff416c";
|
| 833 |
+
} else if (timer <= 20) {
|
| 834 |
+
timerEl.style.color = "#ffb347";
|
| 835 |
+
}
|
| 836 |
+
}, 1000);
|
| 837 |
+
|
| 838 |
+
// Update UI
|
| 839 |
+
patternOptions.querySelectorAll('.pattern-option').forEach(opt => {
|
| 840 |
+
opt.classList.remove('selected', 'wrong');
|
| 841 |
+
});
|
| 842 |
+
|
| 843 |
+
submitBtn.disabled = false;
|
| 844 |
+
submitBtn.innerHTML = '<i class="fas fa-check-circle"></i> Submit Answer';
|
| 845 |
+
}
|
| 846 |
+
|
| 847 |
+
// Generate pattern options
|
| 848 |
+
function generateOptions() {
|
| 849 |
+
// Clear existing options
|
| 850 |
+
patternOptions.innerHTML = '';
|
| 851 |
+
const options = [currentPattern.pattern];
|
| 852 |
+
|
| 853 |
+
// Get other patterns for wrong options
|
| 854 |
+
let allPatterns = [...new Set(wordBuilderData.map(p => p.pattern))];
|
| 855 |
+
|
| 856 |
+
// Filter by category if needed
|
| 857 |
+
if (currentCategory !== 'all') {
|
| 858 |
+
allPatterns = [...new Set(wordBuilderData
|
| 859 |
+
.filter(p => p.category === currentCategory)
|
| 860 |
+
.map(p => p.pattern))];
|
| 861 |
+
}
|
| 862 |
+
|
| 863 |
+
const wrongPatterns = allPatterns.filter(p => p !== currentPattern.pattern);
|
| 864 |
+
|
| 865 |
+
// Shuffle and select 7 wrong options (8 total)
|
| 866 |
+
shuffleArray(wrongPatterns);
|
| 867 |
+
for (let i = 0; i < 7; i++) {
|
| 868 |
+
if (wrongPatterns[i]) {
|
| 869 |
+
options.push(wrongPatterns[i]);
|
| 870 |
+
}
|
| 871 |
+
}
|
| 872 |
+
|
| 873 |
+
// Shuffle options
|
| 874 |
+
shuffleArray(options);
|
| 875 |
+
|
| 876 |
+
// Create option buttons
|
| 877 |
+
options.forEach((pattern, index) => {
|
| 878 |
+
const option = document.createElement('div');
|
| 879 |
+
option.className = 'pattern-option';
|
| 880 |
+
option.textContent = pattern;
|
| 881 |
+
option.dataset.pattern = pattern;
|
| 882 |
+
option.title = `Option ${index + 1} (Press ${index + 1})`;
|
| 883 |
+
|
| 884 |
+
option.addEventListener('click', () => {
|
| 885 |
+
if (!gameActive) return;
|
| 886 |
+
|
| 887 |
+
// Deselect all options
|
| 888 |
+
patternOptions.querySelectorAll('.pattern-option').forEach(opt => {
|
| 889 |
+
opt.classList.remove('selected');
|
| 890 |
+
});
|
| 891 |
+
|
| 892 |
+
// Select clicked option
|
| 893 |
+
option.classList.add('selected');
|
| 894 |
+
selectedOption = pattern;
|
| 895 |
+
});
|
| 896 |
+
|
| 897 |
+
patternOptions.appendChild(option);
|
| 898 |
+
});
|
| 899 |
+
}
|
| 900 |
+
|
| 901 |
+
// Check answer
|
| 902 |
+
function checkAnswer() {
|
| 903 |
+
if (!selectedOption || !gameActive) return;
|
| 904 |
+
|
| 905 |
+
clearInterval(timerInterval);
|
| 906 |
+
gameActive = false;
|
| 907 |
+
|
| 908 |
+
const isCorrect = selectedOption === currentPattern.pattern;
|
| 909 |
+
const timeBonus = Math.floor(timer / 5) * 10;
|
| 910 |
+
|
| 911 |
+
// Update score and streak
|
| 912 |
+
if (isCorrect) {
|
| 913 |
+
score += 10 + timeBonus;
|
| 914 |
+
streak++;
|
| 915 |
+
correctCount++;
|
| 916 |
+
patternsMastered.add(currentPattern.pattern);
|
| 917 |
+
|
| 918 |
+
feedback.textContent = `Correct! +${10 + timeBonus} points (${timeBonus} time bonus)`;
|
| 919 |
+
feedback.className = "feedback correct";
|
| 920 |
+
completedWord.textContent = `Complete word: ${currentWord}`;
|
| 921 |
+
} else {
|
| 922 |
+
streak = 0;
|
| 923 |
+
|
| 924 |
+
feedback.textContent = `Incorrect. The answer was: ${currentPattern.pattern}`;
|
| 925 |
+
feedback.className = "feedback incorrect";
|
| 926 |
+
|
| 927 |
+
// Highlight correct option
|
| 928 |
+
patternOptions.querySelectorAll('.pattern-option').forEach(opt => {
|
| 929 |
+
if (opt.dataset.pattern === currentPattern.pattern) {
|
| 930 |
+
opt.classList.add('selected');
|
| 931 |
+
} else if (opt.dataset.pattern === selectedOption) {
|
| 932 |
+
opt.classList.add('wrong');
|
| 933 |
+
}
|
| 934 |
+
});
|
| 935 |
+
}
|
| 936 |
+
|
| 937 |
+
totalQuestions++;
|
| 938 |
+
|
| 939 |
+
// Add to history
|
| 940 |
+
addToHistory(isCorrect, true);
|
| 941 |
+
|
| 942 |
+
// Update stats and save
|
| 943 |
+
updateStats();
|
| 944 |
+
saveGameState();
|
| 945 |
+
|
| 946 |
+
// Disable submit button
|
| 947 |
+
submitBtn.disabled = true;
|
| 948 |
+
submitBtn.innerHTML = '<i class="fas fa-check"></i> Answered';
|
| 949 |
+
}
|
| 950 |
+
|
| 951 |
+
// Show hint
|
| 952 |
+
function showHint() {
|
| 953 |
+
// Already showing hint in wordHint
|
| 954 |
+
// Could add more specific hint here
|
| 955 |
+
if (currentPattern) {
|
| 956 |
+
const examples = currentPattern.words.slice(0, 3).join(", ");
|
| 957 |
+
alert(`Hint: The pattern "${currentPattern.pattern}" makes the sound "${currentPattern.pronunciation}"\n\nExamples: ${examples}`);
|
| 958 |
+
}
|
| 959 |
+
}
|
| 960 |
+
|
| 961 |
+
// Reset game
|
| 962 |
+
function resetGame() {
|
| 963 |
+
if (confirm("Start a new game? Your progress will be saved, but current streak will reset.")) {
|
| 964 |
+
streak = 0;
|
| 965 |
+
generateNewWord();
|
| 966 |
+
updateStats();
|
| 967 |
+
saveGameState();
|
| 968 |
+
}
|
| 969 |
+
}
|
| 970 |
+
|
| 971 |
+
// Add word to history
|
| 972 |
+
function addToHistory(isCorrect, attempted) {
|
| 973 |
+
const historyItem = {
|
| 974 |
+
word: currentWord,
|
| 975 |
+
pattern: currentPattern.pattern,
|
| 976 |
+
correct: isCorrect,
|
| 977 |
+
attempted: attempted, // false for timeout
|
| 978 |
+
timestamp: new Date().toISOString(),
|
| 979 |
+
time: new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}),
|
| 980 |
+
date: new Date().toLocaleDateString(),
|
| 981 |
+
category: currentPattern.category
|
| 982 |
+
};
|
| 983 |
+
|
| 984 |
+
wordHistory.unshift(historyItem);
|
| 985 |
+
// Keep only last 100 items
|
| 986 |
+
if (wordHistory.length > 100) {
|
| 987 |
+
wordHistory = wordHistory.slice(0, 100);
|
| 988 |
+
}
|
| 989 |
+
updateHistory();
|
| 990 |
+
}
|
| 991 |
+
|
| 992 |
+
// Update history display
|
| 993 |
+
function updateHistory() {
|
| 994 |
+
historyList.innerHTML = '';
|
| 995 |
+
|
| 996 |
+
if (wordHistory.length === 0) {
|
| 997 |
+
historyList.innerHTML = '<div style="text-align: center; padding: 20px; opacity: 0.7;">No attempts yet</div>';
|
| 998 |
+
historyCount.textContent = '0';
|
| 999 |
+
return;
|
| 1000 |
+
}
|
| 1001 |
+
|
| 1002 |
+
wordHistory.forEach(item => {
|
| 1003 |
+
const div = document.createElement('div');
|
| 1004 |
+
div.className = `history-item ${item.correct ? 'correct' : 'incorrect'}`;
|
| 1005 |
+
|
| 1006 |
+
let statusIcon = item.correct ? '✓' : '✗';
|
| 1007 |
+
let statusText = item.correct ? 'Correct' : 'Incorrect';
|
| 1008 |
+
if (!item.attempted) {
|
| 1009 |
+
statusIcon = '⏰';
|
| 1010 |
+
statusText = 'Timeout';
|
| 1011 |
+
}
|
| 1012 |
+
|
| 1013 |
+
div.innerHTML = `
|
| 1014 |
+
<div class="history-word" title="${item.word}">${item.word}</div>
|
| 1015 |
+
<div class="history-pattern">${item.pattern}</div>
|
| 1016 |
+
<div class="history-result">
|
| 1017 |
+
<span>${statusIcon}</span>
|
| 1018 |
+
<span>${item.time}</span>
|
| 1019 |
+
</div>
|
| 1020 |
+
`;
|
| 1021 |
+
div.title = `${item.word} - ${item.pattern} - ${item.category} - ${statusText} at ${item.time}`;
|
| 1022 |
+
|
| 1023 |
+
historyList.appendChild(div);
|
| 1024 |
+
});
|
| 1025 |
+
|
| 1026 |
+
historyCount.textContent = wordHistory.length;
|
| 1027 |
+
}
|
| 1028 |
+
|
| 1029 |
+
// Clear history
|
| 1030 |
+
function clearHistory() {
|
| 1031 |
+
if (confirm("Clear all attempt history? This cannot be undone.")) {
|
| 1032 |
+
wordHistory = [];
|
| 1033 |
+
updateHistory();
|
| 1034 |
+
saveGameState();
|
| 1035 |
+
}
|
| 1036 |
+
}
|
| 1037 |
+
|
| 1038 |
+
// Export history
|
| 1039 |
+
function exportHistory() {
|
| 1040 |
+
if (wordHistory.length === 0) {
|
| 1041 |
+
alert("No history to export.");
|
| 1042 |
+
return;
|
| 1043 |
+
}
|
| 1044 |
+
|
| 1045 |
+
let csvContent = "Word,Pattern,Category,Result,Date,Time\n";
|
| 1046 |
+
|
| 1047 |
+
wordHistory.forEach(item => {
|
| 1048 |
+
const result = item.correct ? 'Correct' : (item.attempted ? 'Incorrect' : 'Timeout');
|
| 1049 |
+
csvContent += `"${item.word}","${item.pattern}","${item.category}",${result},"${item.date}","${item.time}"\n`;
|
| 1050 |
+
});
|
| 1051 |
+
|
| 1052 |
+
const blob = new Blob([csvContent], { type: 'text/csv' });
|
| 1053 |
+
const url = window.URL.createObjectURL(blob);
|
| 1054 |
+
const a = document.createElement('a');
|
| 1055 |
+
a.href = url;
|
| 1056 |
+
a.download = `phonics-history-${new Date().toISOString().slice(0,10)}.csv`;
|
| 1057 |
+
document.body.appendChild(a);
|
| 1058 |
+
a.click();
|
| 1059 |
+
document.body.removeChild(a);
|
| 1060 |
+
window.URL.revokeObjectURL(url);
|
| 1061 |
+
|
| 1062 |
+
alert(`Exported ${wordHistory.length} attempts to CSV file.`);
|
| 1063 |
+
}
|
| 1064 |
+
|
| 1065 |
+
// Update all stats
|
| 1066 |
+
function updateStats() {
|
| 1067 |
+
scoreEl.textContent = score;
|
| 1068 |
+
streakEl.textContent = streak;
|
| 1069 |
+
correctCountEl.textContent = correctCount;
|
| 1070 |
+
totalQuestionsEl.textContent = totalQuestions;
|
| 1071 |
+
|
| 1072 |
+
const accuracy = totalQuestions > 0 ? Math.round((correctCount / totalQuestions) * 100) : 0;
|
| 1073 |
+
accuracyEl.textContent = `${accuracy}%`;
|
| 1074 |
+
|
| 1075 |
+
const totalPatterns = currentCategory === 'all'
|
| 1076 |
+
? wordBuilderData.length
|
| 1077 |
+
: wordBuilderData.filter(p => p.category === currentCategory).length;
|
| 1078 |
+
|
| 1079 |
+
const progress = totalPatterns > 0 ? (patternsMastered.size / totalPatterns) * 100 : 0;
|
| 1080 |
+
progressFill.style.width = `${progress}%`;
|
| 1081 |
+
progressText.textContent = `${patternsMastered.size}/${totalPatterns} patterns (${Math.round(progress)}%)`;
|
| 1082 |
+
}
|
| 1083 |
+
|
| 1084 |
+
// Utility: Shuffle array
|
| 1085 |
+
function shuffleArray(array) {
|
| 1086 |
+
for (let i = array.length - 1; i > 0; i--) {
|
| 1087 |
+
const j = Math.floor(Math.random() * (i + 1));
|
| 1088 |
+
[array[i], array[j]] = [array[j], array[i]];
|
| 1089 |
+
}
|
| 1090 |
+
return array;
|
| 1091 |
+
}
|
| 1092 |
+
|
| 1093 |
+
// Initialize when page loads
|
| 1094 |
+
document.addEventListener('DOMContentLoaded', initGame);
|
| 1095 |
+
</script>
|
| 1096 |
+
</body>
|
| 1097 |
+
</html>
|