// Global variables let targetWords = [] let dictionary = [] let targetWord = "" const WORD_LENGTH = 5 const FLIP_ANIMATION_DURATION = 500 const DANCE_ANIMATION_DURATION = 500 const keyboard = document.querySelector("[data-keyboard]") const alertContainer = document.querySelector("[data-alert-container]") const guessGrid = document.querySelector("[data-guess-grid]") // Initialize the game by loading JSON data async function initializeGame() { try { // Load both JSON files const [targetWordsResponse, dictionaryResponse] = await Promise.all([ fetch('targetWords.json'), fetch('dictionary.json') ]) // Check if responses are ok if (!targetWordsResponse.ok) { throw new Error(`Failed to load targetWords.json: ${targetWordsResponse.status}`) } if (!dictionaryResponse.ok) { throw new Error(`Failed to load dictionary.json: ${dictionaryResponse.status}`) } // Parse JSON data targetWords = await targetWordsResponse.json() dictionary = await dictionaryResponse.json() // Validate data if (!Array.isArray(targetWords) || targetWords.length === 0) { throw new Error('Target words must be a non-empty array') } if (!Array.isArray(dictionary) || dictionary.length === 0) { throw new Error('Dictionary must be a non-empty array') } // Select random target word targetWord = targetWords[Math.floor(Math.random() * targetWords.length)] // Start the game startInteraction() console.log('Game initialized successfully!') console.log(`Target word selected from ${targetWords.length} possible words`) console.log(`Dictionary loaded with ${dictionary.length} words`) } catch (error) { console.error('Failed to initialize game:', error) showAlert(`Error loading game data: ${error.message}`, 5000) } } // Alternative initialization with date-based word selection (uncomment if needed) /* async function initializeGameWithDate() { try { const [targetWordsResponse, dictionaryResponse] = await Promise.all([ fetch('targetWords.json'), fetch('dictionary.json') ]) if (!targetWordsResponse.ok || !dictionaryResponse.ok) { throw new Error('Failed to load game data files') } targetWords = await targetWordsResponse.json() dictionary = await dictionaryResponse.json() // Date-based word selection const offsetFromDate = new Date(2022, 0, 1) const msOffset = Date.now() - offsetFromDate const dayOffset = Math.floor(msOffset / 1000 / 60 / 60 / 24) targetWord = targetWords[dayOffset % targetWords.length] startInteraction() } catch (error) { console.error('Failed to initialize game:', error) showAlert(`Error loading game data: ${error.message}`, 5000) } } */ function startInteraction() { document.addEventListener("click", handleMouseClick) document.addEventListener("keydown", handleKeyPress) } function stopInteraction() { document.removeEventListener("click", handleMouseClick) document.removeEventListener("keydown", handleKeyPress) } function handleMouseClick(e) { if (e.target.closest("[data-key]")) { pressKey(e.target.dataset.key) return } if (e.target.closest("[data-enter]")) { submitGuess() return } if (e.target.closest("[data-delete]")) { deleteKey() return } } function handleKeyPress(e) { if (e.key === "Enter") { submitGuess() return } if (e.key === "Backspace" || e.key === "Delete") { deleteKey() return } if (e.key.match(/^[a-z]$/)) { pressKey(e.key) return } } function pressKey(key) { const activeTiles = getActiveTiles() if (activeTiles.length >= WORD_LENGTH) return const nextTile = guessGrid.querySelector(":not([data-letter])") nextTile.dataset.letter = key.toLowerCase() nextTile.textContent = key nextTile.dataset.state = "active" } function deleteKey() { const activeTiles = getActiveTiles() const lastTile = activeTiles[activeTiles.length - 1] if (lastTile == null) return lastTile.textContent = "" delete lastTile.dataset.state delete lastTile.dataset.letter } function submitGuess() { const activeTiles = [...getActiveTiles()] if (activeTiles.length !== WORD_LENGTH) { showAlert("Not enough letters, Sara") shakeTiles(activeTiles) return } const guess = activeTiles.reduce((word, tile) => { return word + tile.dataset.letter }, "") if (!dictionary.includes(guess)) { showAlert("Not in dictionary") shakeTiles(activeTiles) return } stopInteraction() activeTiles.forEach((...params) => flipTile(...params, guess)) } function flipTile(tile, index, array, guess) { const letter = tile.dataset.letter const key = keyboard.querySelector(`[data-key="${letter}"i]`) setTimeout(() => { tile.classList.add("flip") }, (index * FLIP_ANIMATION_DURATION) / 2) tile.addEventListener( "transitionend", () => { tile.classList.remove("flip") if (targetWord[index] === letter) { tile.dataset.state = "correct" key.classList.add("correct") } else if (targetWord.includes(letter)) { tile.dataset.state = "wrong-position" key.classList.add("wrong-position") } else { tile.dataset.state = "wrong" key.classList.add("wrong") } if (index === array.length - 1) { tile.addEventListener( "transitionend", () => { startInteraction() checkWinLose(guess, array) }, { once: true } ) } }, { once: true } ) } function getActiveTiles() { return guessGrid.querySelectorAll('[data-state="active"]') } function showAlert(message, duration = 1000) { const alert = document.createElement("div") alert.textContent = message alert.classList.add("alert") alertContainer.prepend(alert) if (duration == null) return setTimeout(() => { alert.classList.add("hide") alert.addEventListener("transitionend", () => { alert.remove() }) }, duration) } function shakeTiles(tiles) { tiles.forEach(tile => { tile.classList.add("shake") tile.addEventListener( "animationend", () => { tile.classList.remove("shake") }, { once: true } ) }) } // The CELEBRATION SOUND EFFECT function playCelebrationSound() { const audio = new Audio('celebration.mp3') audio.volume = 0.5 // Adjust volume as needed audio.play().catch(error => { console.error('Error playing celebration sound:', error) }) } // IT'S RIGHT HERE YOU BLIND MOFO" function checkWinLose(guess, tiles) { if (guess === targetWord) { playCelebrationSound() showAlert("Congratulations, You win!", 5000) danceTiles(tiles) celebrateWithConfetti() setTimeout(() => { showWordDefinition(targetWord, true) },3000) stopInteraction() return } const remainingTiles = guessGrid.querySelectorAll(":not([data-letter])") if (remainingTiles.length === 0) { showAlert(targetWord.toUpperCase(), null) showWordDefinition(targetWord, false) stopInteraction() } } // Function to fetch and display word definition async function showWordDefinition(word, isWin = true) { try { console.log(`Fetching definition for: ${word}`) const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${word}`) if (!response.ok) { throw new Error(`API request failed: ${response.status}`) } const data = await response.json() // Extract the first definition const firstMeaning = data[0]?.meanings?.[0] const definition = firstMeaning?.definitions?.[0]?.definition const partOfSpeech = firstMeaning?.partOfSpeech const example = firstMeaning?.definitions?.[0]?.example if (definition) { // Create definition display let definitionText = `šŸ“– **${word.toUpperCase()}** (${partOfSpeech || 'word'})\n${definition}` if (example) { definitionText += `\n\nšŸ’” Example: "${example}"` } // Show definition in a styled alert showDefinitionAlert(definitionText, isWin) } else { throw new Error('No definition found') } } catch (error) { console.error('Error fetching definition:', error) // Fallback message const fallbackMsg = `šŸ“– **${word.toUpperCase()}**\nDefinition not available at the moment.` showDefinitionAlert(fallbackMsg, isWin) } } // Special alert function for definitions function showDefinitionAlert(message, isWin = true) { const alert = document.createElement("div") alert.innerHTML = message.replace(/\n/g, '
').replace(/\*\*(.*?)\*\*/g, '$1') alert.classList.add("alert", "definition-alert") if (isWin) { alert.classList.add("win-definition") } else { alert.classList.add("lose-definition") } alertContainer.prepend(alert) // Auto-hide after 8 seconds (longer for reading) setTimeout(() => { alert.classList.add("hide") alert.addEventListener("transitionend", () => { alert.remove() }) }, 8000) // Click to dismiss alert.addEventListener('click', () => { alert.classList.add("hide") alert.addEventListener("transitionend", () => { alert.remove() }) }) } // Confetti celebration function function celebrateWithConfetti() { console.log('Confetti celebration triggered!') // Check if confetti library is loaded if (typeof confetti === 'undefined') { console.error('Confetti library not loaded! Make sure to include the script tag.') return } try { // Initial burst from the center confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } }) // Side cannons effect setTimeout(() => { confetti({ particleCount: 50, angle: 60, spread: 55, origin: { x: 0 } }) confetti({ particleCount: 50, angle: 120, spread: 55, origin: { x: 1 } }) }, 200) // Stars effect setTimeout(() => { confetti({ particleCount: 30, spread: 360, startVelocity: 30, decay: 0.9, scalar: 1.2, shapes: ['star'], colors: ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7'] }) }, 400) // Final cascade setTimeout(() => { confetti({ particleCount: 200, spread: 100, origin: { y: 0.4 }, colors: ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7', '#dda0dd'] }) }, 600) console.log('Confetti animation started successfully!') } catch (error) { console.error('Error triggering confetti:', error) } } function danceTiles(tiles) { tiles.forEach((tile, index) => { setTimeout(() => { tile.classList.add("dance") tile.addEventListener( "animationend", () => { tile.classList.remove("dance") }, { once: true } ) }, (index * DANCE_ANIMATION_DURATION) / 5) }) } /*==================== DARK LIGHT THEME ====================*/ const themeButton = document.getElementById('theme-button') const darkTheme = 'dark-theme' const iconTheme = 'bx-sun' // Previously selected topic (if user selected) const selectedTheme = localStorage.getItem('selected-theme') const selectedIcon = localStorage.getItem('selected-icon') // We obtain the current theme that the interface has by validating the dark-theme class const getCurrentTheme = () => document.body.classList.contains(darkTheme) ? 'dark' : 'light' const getCurrentIcon = () => themeButton.classList.contains(iconTheme) ? 'bx-moon' : 'bx-sun' // We validate if the user previously chose a topic if (selectedTheme) { // If the validation is fulfilled, we ask what the issue was to know if we activated or deactivated the dark document.body.classList[selectedTheme === 'dark' ? 'add' : 'remove'](darkTheme) themeButton.classList[selectedIcon === 'bx-moon' ? 'add' : 'remove'](iconTheme) } // Activate / deactivate the theme manually with the button themeButton.addEventListener('click', () => { // Add or remove the dark / icon theme document.body.classList.toggle(darkTheme) themeButton.classList.toggle(iconTheme) // We save the theme and the current icon that the user chose localStorage.setItem('selected-theme', getCurrentTheme()) localStorage.setItem('selected-icon', getCurrentIcon()) }) // Initialize the game when the page loads document.addEventListener('DOMContentLoaded', initializeGame)