// Kruiswoord Pro Game Logic - Fixed Version with Tablet Support and Correct Hints class KruiswoordProGame { constructor() { this.currentLevel = 1; this.coins = 100; this.hints = 3; this.currentPuzzle = null; this.userAnswers = {}; this.selectedCell = null; this.gridElement = document.getElementById('crosswordGrid'); this.isTablet = window.innerWidth >= 768; this.initializeGame(); this.setupEventListeners(); this.loadPuzzle(this.currentLevel); // Handle window resize for tablet/desktop window.addEventListener('resize', () => { this.handleResize(); }); } handleResize() { const wasTablet = this.isTablet; this.isTablet = window.innerWidth >= 768; if (wasTablet !== this.isTablet && this.currentPuzzle) { this.renderGrid(); // Re-render for different screen size } } initializeGame() { this.updateUI(); this.setupTabletStyles(); } setupTabletStyles() { if (this.isTablet) { document.body.classList.add('tablet-mode'); } else { document.body.classList.remove('tablet-mode'); } } setupEventListeners() { // Menu controls document.getElementById('menuBtn').addEventListener('click', () => { document.getElementById('sideMenu').classList.add('active'); }); document.getElementById('closeMenu').addEventListener('click', () => { document.getElementById('sideMenu').classList.remove('active'); }); // Game controls document.getElementById('checkBtn').addEventListener('click', () => { this.checkAnswers(); }); document.getElementById('clearBtn').addEventListener('click', () => { this.clearGrid(); }); document.getElementById('revealLetterBtn').addEventListener('click', () => { this.showHintModal(); }); document.getElementById('skipBtn').addEventListener('click', () => { this.skipPuzzle(); }); // Hint modal document.getElementById('useHintBtn').addEventListener('click', () => { this.useHint(); }); document.getElementById('cancelHintBtn').addEventListener('click', () => { this.hideHintModal(); }); // Success modal document.getElementById('nextPuzzleBtn').addEventListener('click', () => { this.nextPuzzle(); }); document.getElementById('closeModalBtn').addEventListener('click', () => { this.hideSuccessModal(); }); // Menu items document.getElementById('dailyPuzzle').addEventListener('click', () => { this.loadDailyPuzzle(); }); document.getElementById('levelSelect').addEventListener('click', () => { this.showLevelSelect(); }); // Keyboard navigation document.addEventListener('keydown', (e) => { this.handleKeyboardInput(e); }); } // FIXED: Better Dutch puzzles with correct hint placement getDutchPuzzles() { return { 1: { grid: [ ['H', 'A', 'A', 'S'], ['#', '#', '#', '#'], ['V', 'L', 'I', 'E', 'G'], ['#', '#', '#', '#', '#'] ], words: [ { word: 'HAAS', clue: 'Snel dier →', startRow: 0, startCol: 0, direction: 'horizontal', answer: 'HAAS' }, { word: 'VLIEG', clue: 'Insect →', startRow: 2, startCol: 0, direction: 'horizontal', answer: 'VLIEG' } ], difficulty: 1 }, 2: { grid: [ ['B', 'O', 'M', 'E', 'N'], ['#', '#', '#', '#', '#'], ['H', 'O', 'N', 'D', 'E'], ['#', '#', '#', '#', '#'] ], words: [ { word: 'BOMEN', clue: 'Planten →', startRow: 0, startCol: 0, direction: 'horizontal', answer: 'BOMEN' }, { word: 'HONDE', clue: 'Huisdieren →', startRow: 2, startCol: 0, direction: 'horizontal', answer: 'HONDE' } ], difficulty: 2 }, 3: { grid: [ ['R', 'E', 'G', 'E', 'N'], ['#', '#', '#', '#', '#'], ['S', 'N', 'E', 'E', 'U'], ['#', '#', '#', '#', '#'] ], words: [ { word: 'REGEN', clue: 'Valt uit de lucht →', startRow: 0, startCol: 0, direction: 'horizontal', answer: 'REGEN' }, { word: 'SNEEU', clue: 'Witte vlokken →', startRow: 2, startCol: 0, direction: 'horizontal', answer: 'SNEEU' } ], difficulty: 3 }, // Tablet-optimized larger puzzles 4: { grid: [ ['H', 'A', 'A', 'S', '#', '#'], ['#', '#', '#', 'T', '#', '#'], ['V', 'L', 'I', 'E', 'G', '#'], ['#', '#', '#', 'S', '#', '#'], ['#', '#', '#', '#', '#', '#'], ['#', '#', '#', '#', '#', '#'] ], words: [ { word: 'HAAS', clue: 'Snel dier →', startRow: 0, startCol: 0, direction: 'horizontal', answer: 'HAAS' }, { word: 'VLIEG', clue: 'Insect →', startRow: 2, startCol: 0, direction: 'horizontal', answer: 'VLIEG' } ], difficulty: 2, tabletOptimized: true } }; } loadPuzzle(level) { const puzzles = this.getDutchPuzzles(); this.currentPuzzle = puzzles[level] || puzzles[1]; this.renderGrid(); this.userAnswers = {}; this.updateProgress(); } renderGrid() { if (!this.currentPuzzle) return; const grid = this.currentPuzzle.grid; const words = this.currentPuzzle.words; this.gridElement.innerHTML = ''; // Set grid dimensions const rows = grid.length; const cols = Math.max(...grid.map(row => row.length)); // FIXED: Tablet-responsive sizing const cellSize = this.isTablet ? (window.innerWidth > 1024 ? 60 : 50) : 45; this.gridElement.style.gridTemplateColumns = `repeat(${cols}, ${cellSize}px)`; this.gridElement.style.gridTemplateRows = `repeat(${rows}, ${cellSize}px)`; this.gridElement.style.gap = '2px'; this.gridElement.style.justifyContent = 'center'; // Create grid cells for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { const cellValue = grid[row] && grid[row][col] ? grid[row][col] : '#'; const cellElement = this.createCellElement(cellValue, row, col, words, cellSize); this.gridElement.appendChild(cellElement); } } this.attachCellListeners(); } createCellElement(cellValue, row, col, words, cellSize) { const cellDiv = document.createElement('div'); cellDiv.className = 'grid-cell'; cellDiv.dataset.row = row; cellDiv.dataset.col = col; // FIXED: Set consistent sizing cellDiv.style.width = `${cellSize}px`; cellDiv.style.height = `${cellSize}px`; if (cellValue === '#') { // Blocked cell cellDiv.classList.add('blocked-cell'); } else if (this.isClueCell(row, col, words)) { // Clue cell const clueData = this.getClueForPosition(row, col, words); cellDiv.classList.add('clue-cell', clueData.direction); // FIXED: Better clue text sizing for tablets const clueText = document.createElement('div'); clueText.className = 'clue-text'; clueText.textContent = clueData.clue; clueText.style.fontSize = this.isTablet ? '10px' : '8px'; const arrow = document.createElement('div'); arrow.className = `arrow arrow-${clueData.direction === 'horizontal' ? 'right' : 'down'}`; arrow.textContent = clueData.direction === 'horizontal' ? '→' : '↓'; arrow.style.fontSize = this.isTablet ? '16px' : '14px'; cellDiv.appendChild(clueText); cellDiv.appendChild(arrow); } else { // FIXED: Input cell - only create if it's part of a word if (this.isPartOfWord(row, col, words)) { cellDiv.classList.add('input-cell'); const input = document.createElement('input'); input.type = 'text'; input.maxLength = 1; input.dataset.row = row; input.dataset.col = col; // FIXED: Responsive font sizing input.style.fontSize = this.isTablet ? '24px' : '20px'; // Add existing answer if available const key = `${row}-${col}`; if (this.userAnswers[key]) { input.value = this.userAnswers[key]; } cellDiv.appendChild(input); } else { // Empty cell that's not part of any word cellDiv.classList.add('blocked-cell'); } } return cellDiv; } // FIXED: Check if cell is part of any word isPartOfWord(row, col, words) { for (const word of words) { if (word.direction === 'horizontal') { if (row === word.startRow && col >= word.startCol && col < word.startCol + word.answer.length) { return true; } } else if (word.direction === 'vertical') { if (col === word.startCol && row >= word.startRow && row < word.startRow + word.answer.length) { return true; } } } return false; } isClueCell(row, col, words) { return words.some(word => word.startRow === row && word.startCol === col); } getClueForPosition(row, col, words) { for (const word of words) { if (word.startRow === row && word.startCol === col) { return { clue: word.clue, direction: word.direction }; } } return null; } attachCellListeners() { const inputs = this.gridElement.querySelectorAll('input'); inputs.forEach(input => { input.addEventListener('input', (e) => { this.handleInput(e); }); input.addEventListener('focus', (e) => { this.selectCell(e.target); }); input.addEventListener('click', (e) => { this.selectCell(e.target); }); }); } handleInput(e) { const input = e.target; const value = input.value.toUpperCase(); input.value = value; const row = parseInt(input.dataset.row); const col = parseInt(input.dataset.col); const key = `${row}-${col}`; this.userAnswers[key] = value; // FIXED: Smart navigation to next cell in the same word if (value && value.length === 1) { this.moveToNextInWord(input, row, col); } this.updateProgress(); } // FIXED: Navigate to next cell in the same word, not just any next input moveToNextInWord(currentInput, row, col) { const currentWord = this.findWordContainingCell(row, col); if (!currentWord) { this.moveToNextCell(currentInput); return; } let nextRow = row; let nextCol = col; if (currentWord.direction === 'horizontal') { nextCol++; } else { nextRow++; } // Check if next position is still in the same word const nextInput = this.gridElement.querySelector(`input[data-row="${nextRow}"][data-col="${nextCol}"]`); if (nextInput && this.isPartOfWord(nextRow, nextCol, this.currentPuzzle.words)) { nextInput.focus(); nextInput.select(); } else { // End of word, move to next available input this.moveToNextCell(currentInput); } } findWordContainingCell(row, col) { for (const word of this.currentPuzzle.words) { if (word.direction === 'horizontal') { if (row === word.startRow && col >= word.startCol && col < word.startCol + word.answer.length) { return word; } } else if (word.direction === 'vertical') { if (col === word.startCol && row >= word.startRow && row < word.startRow + word.answer.length) { return word; } } } return null; } selectCell(input) { // Remove previous selection document.querySelectorAll('.grid-cell').forEach(cell => { cell.classList.remove('active'); }); // Add selection to current cell input.parentElement.classList.add('active'); this.selectedCell = input; } // FIXED: Better navigation that respects word boundaries moveToNextCell(currentInput) { const inputs = Array.from(this.gridElement.querySelectorAll('input')); const currentIndex = inputs.indexOf(currentInput); if (currentIndex !== -1 && currentIndex < inputs.length - 1) { const nextInput = inputs[currentIndex + 1]; nextInput.focus(); nextInput.select(); } } handleKeyboardInput(e) { if (!this.selectedCell) return; const inputs = Array.from(this.gridElement.querySelectorAll('input')); const currentIndex = inputs.indexOf(this.selectedCell); if (currentIndex === -1) return; let targetIndex = currentIndex; switch (e.key) { case 'ArrowUp': const currentRow = parseInt(this.selectedCell.dataset.row); const currentCol = parseInt(this.selectedCell.dataset.col); const targetInput = this.findInputAbove(currentRow, currentCol); if (targetInput) { targetInput.focus(); targetInput.select(); } break; case 'ArrowDown': const targetInputDown = this.findInputBelow(currentRow, currentCol); if (targetInputDown) { targetInputDown.focus(); targetInputDown.select(); } break; case 'ArrowLeft': if (currentIndex > 0) { targetIndex--; inputs[targetIndex].focus(); inputs[targetIndex].select(); } break; case 'ArrowRight': if (currentIndex < inputs.length - 1) { targetIndex++; inputs[targetIndex].focus(); inputs[targetIndex].select(); } break; case 'Backspace': this.selectedCell.value = ''; const row = parseInt(this.selectedCell.dataset.row); const col = parseInt(this.selectedCell.dataset.col); delete this.userAnswers[`${row}-${col}`]; this.updateProgress(); break; } } findInputAbove(row, col) { for (let r = row - 1; r >= 0; r--) { const input = this.gridElement.querySelector(`input[data-row="${r}"][data-col="${col}"]`); if (input) return input; } return null; } findInputBelow(row, col) { const maxRow = Math.max(...Array.from(this.gridElement.querySelectorAll('input')).map(input => parseInt(input.dataset.row))); for (let r = row + 1; r <= maxRow; r++) { const input = this.gridElement.querySelector(`input[data-row="${r}"][data-col="${col}"]`); if (input) return input; } return null; } checkAnswers() { let correct = 0; let total = 0; const inputs = this.gridElement.querySelectorAll('input'); inputs.forEach(input => { const row = parseInt(input.dataset.row); const col = parseInt(input.dataset.col); const key = `${row}-${col}`; const userAnswer = input.value.toUpperCase(); const correctAnswer = this.findCorrectAnswer(row, col); if (correctAnswer) { total++; if (userAnswer === correctAnswer) { correct++; input.parentElement.classList.add('correct'); input.parentElement.classList.remove('incorrect'); } else if (userAnswer) { input.parentElement.classList.add('incorrect'); input.parentElement.classList.remove('correct'); } } }); const percentage = total > 0 ? Math.round((correct / total) * 100) : 0; if (percentage === 100) { this.showSuccess(); } else { this.showFeedback(`Je hebt ${correct} van de ${total} letters correct (${percentage}%)`); } } findCorrectAnswer(row, col) { for (const word of this.currentPuzzle.words) { if (word.direction === 'horizontal') { if (word.startRow === row && col >= word.startCol && col < word.startCol + word.answer.length) { const letterIndex = col - word.startCol; return word.answer[letterIndex]; } } else if (word.direction === 'vertical') { if (word.startCol === col && row >= word.startRow && row < word.startRow + word.answer.length) { const letterIndex = row - word.startRow; return word.answer[letterIndex]; } } } return null; } clearGrid() { if (confirm('Weet je zeker dat je alle ingevulde letters wilt wissen?')) { const inputs = this.gridElement.querySelectorAll('input'); inputs.forEach(input => { input.value = ''; input.parentElement.classList.remove('correct', 'incorrect'); }); this.userAnswers = {}; this.updateProgress(); this.showFeedback('Raster gewist'); } } showHintModal() { if (this.hints <= 0) { this.showFeedback('Je hebt geen hints meer. Verdien hints door puzzels op te lossen!', 'error'); return; } document.getElementById('hintModal').classList.add('active'); } hideHintModal() { document.getElementById('hintModal').classList.remove('active'); } useHint() { if (this.hints <= 0) return; const inputs = this.gridElement.querySelectorAll('input'); const emptyInputs = Array.from(inputs).filter(input => !input.value); if (emptyInputs.length === 0) { this.showFeedback('Alle letters zijn al ingevuld!', 'info'); this.hideHintModal(); return; } // Randomly select an empty cell const randomInput = emptyInputs[Math.floor(Math.random() * emptyInputs.length)]; const row = parseInt(randomInput.dataset.row); const col = parseInt(randomInput.dataset.col); const correctAnswer = this.findCorrectAnswer(row, col); if (correctAnswer) { randomInput.value = correctAnswer; const key = `${row}-${col}`; this.userAnswers[key] = correctAnswer; this.hints--; this.updateUI(); this.updateProgress(); this.showFeedback('Letter onthuld!'); this.hideHintModal(); // Check if puzzle is complete this.checkIfComplete(); } } checkIfComplete() { const inputs = this.gridElement.querySelectorAll('input'); let allFilled = true; inputs.forEach(input => { if (!input.value) { allFilled = false; } }); if (allFilled) { setTimeout(() => this.checkAnswers(), 500); } } showSuccess() { // Award rewards this.coins += 50; this.hints += 1; this.updateUI(); document.getElementById('successModal').classList.add('active'); } hideSuccessModal() { document.getElementById('successModal').classList.remove('active'); } nextPuzzle() { this.hideSuccessModal(); this.currentLevel++; this.loadPuzzle(this.currentLevel); this.showFeedback(`Niveau ${this.currentLevel} geladen!`); } skipPuzzle() { if (confirm('Weet je zeker dat je deze puzzel wilt overslaan?')) { this.currentLevel++; this.loadPuzzle(this.currentLevel); this.showFeedback('Puzzel overgeslagen'); } } loadDailyPuzzle() { this.showFeedback('Dagelijkse puzzel wordt geladen...'); // Implement daily puzzle logic document.getElementById('sideMenu').classList.remove('active'); } showLevelSelect() { this.showFeedback('Niveau selectie komt binnenkort!'); document.getElementById('sideMenu').classList.remove('active'); } updateProgress() { const inputs = this.gridElement.querySelectorAll('input'); let filled = 0; let total = 0; inputs.forEach(input => { const row = parseInt(input.dataset.row); const col = parseInt(input.dataset.col); const correctAnswer = this.findCorrectAnswer(row, col); if (correctAnswer) { total++; if (input.value) { filled++; } } }); const progress = total > 0 ? Math.round((filled / total) * 100) : 0; document.getElementById('progressFill').style.width = `${progress}%`; document.getElementById('progressText').textContent = `${progress}% voltooid`; } updateUI() { document.getElementById('coinsCount').textContent = this.coins; document.querySelector('.hint-count').textContent = this.hints; document.querySelector('.level-number').textContent = `Niveau ${this.currentLevel}`; } showFeedback(message, type = 'info') { const feedback = document.createElement('div'); feedback.className = `feedback feedback-${type}`; feedback.textContent = message; feedback.style.cssText = ` position: fixed; top: 100px; right: 20px; padding: 15px 20px; border-radius: 8px; color: white; font-weight: 600; z-index: 1000; animation: slideIn 0.3s ease; max-width: 300px; word-wrap: break-word; `; switch (type) { case 'error': feedback.style.background = 'linear-gradient(135deg, #f56565, #e53e3e)'; break; case 'success': feedback.style.background = 'linear-gradient(135deg, #48bb78, #38a169)'; break; default: feedback.style.background = 'linear-gradient(135deg, #4299e1, #3182ce)'; } document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOut 0.3s ease'; setTimeout(() => { if (feedback.parentNode) { feedback.parentNode.removeChild(feedback); } }, 300); }, 3000); } } // Add CSS animations and tablet styles const style = document.createElement('style'); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } /* Tablet Mode Styles */ .tablet-mode .grid-container { max-width: 90vw; margin: 0 auto; } .tablet-mode .puzzle-section { padding: 30px; } @media (min-width: 768px) { .app-container { max-width: 100vw; } .game-main { padding: 30px; } .bottom-controls { max-width: 600px; margin: 0 auto; } } @media (min-width: 1024px) { .grid-container { transform: scale(1.1); transform-origin: center; } .puzzle-section { min-height: 70vh; } } `; document.head.appendChild(style); // Initialize game when DOM is loaded document.addEventListener('DOMContentLoaded', () => { const game = new KruiswoordProGame(); });