Mini Scrabble - Edizione Principessa
Giocatore 1 0

SCRABBLE

EDIZIONE PER BAMBINI
Giocatore 2 0
Benvenuto! Posiziona una parola al centro ★
LETTERE DEL GIOCATORE 1
` : text; msgBox.className = `p-3 rounded-xl text-center font-bold text-sm sm:text-base shadow-sm flex flex-col items-center gap-1 transition-all duration-500 `; if (type === 'error') msgBox.classList.add('bg-red-100', 'text-red-600'); else if (type === 'warning') msgBox.classList.add('bg-orange-100', 'text-orange-700'); else if (type === 'success') msgBox.classList.add('bg-green-100', 'text-green-700'); else if (type === 'funfact') msgBox.classList.add('bg-yellow-50', 'text-pink-600', 'ring-4', 'ring-yellow-200'); else msgBox.classList.add('bg-white', 'text-pink-500'); if (showActions && !state.aiLoading) { actions.classList.remove('hidden'); state.lastUnknownWord = word; } else { actions.classList.add('hidden'); state.lastUnknownWord = null; } lucide.createIcons(); } // --- INTERACTION --- function handleHandClick(index) { if (state.selectedHandIndex === index) state.selectedHandIndex = null; else state.selectedHandIndex = index; render(); } function handleGridClick(r, c) { if (state.grid[r][c]) return; // Posiziona una lettera if (state.selectedHandIndex !== null) { const player = state.players[state.currentPlayerIdx]; const letterObj = player.hand[state.selectedHandIndex]; // Nessuna sovrascrittura temporanea if (state.tempPlacements.some(t => t.r === r && t.c === c)) return; state.tempPlacements.push({ r, c, ...letterObj }); player.hand.splice(state.selectedHandIndex, 1); state.selectedHandIndex = null; render(); } // Riprendi una lettera else { const tempIdx = state.tempPlacements.findIndex(t => t.r === r && t.c === c); if (tempIdx !== -1) { const item = state.tempPlacements[tempIdx]; state.players[state.currentPlayerIdx].hand.push({ letter: item.letter, score: item.score, id: item.id }); state.tempPlacements.splice(tempIdx, 1); render(); } } } function recallLetters() { if (state.tempPlacements.length === 0) return; const player = state.players[state.currentPlayerIdx]; state.tempPlacements.forEach(p => { player.hand.push({ letter: p.letter, score: p.score, id: p.id }); }); state.tempPlacements = []; state.selectedHandIndex = null; render(); } // --- CONVALIDA --- async function validateTurn(force = false, forcedWord = null) { if (state.tempPlacements.length === 0) return updateMessage("Non hai posizionato nulla!", "error"); // 1. Allineamento const rows = state.tempPlacements.map(t => t.r); const cols = state.tempPlacements.map(t => t.c); const isRow = new Set(rows).size === 1; const isCol = new Set(cols).size === 1; if (state.tempPlacements.length > 1 && !isRow && !isCol) return updateMessage("Le lettere devono essere allineate.", "error"); // 2. Connessione const isGridEmpty = state.grid[4][4] === null; if (isGridEmpty) { if (!state.tempPlacements.some(t => t.r === 4 && t.c === 4)) return updateMessage("La prima parola deve passare per la stella centrale ★", "error"); } else { const isConnected = state.tempPlacements.some(t => { const neighbors = [{r:t.r-1,c:t.c},{r:t.r+1,c:t.c},{r:t.r,c:t.c-1},{r:t.r,c:t.c+1}]; return neighbors.some(n => n.r >= 0 && n.r = 0 && n.c 1) ? wordH : ((wordV.length > 1) ? wordV : state.tempPlacements[0].letter); } else { const first = state.tempPlacements[0]; word = isRow ? getFullWordAt(first.r, first.c, 0, 1) : getFullWordAt(first.r, first.c, 1, 0); } if(forcedWord) word = forcedWord; // 4. Dizionario const isValid = FULL_DICTIONARY.includes(word); if (!isValid && !force) { if (word.length { let score = p.score; const special = SPECIAL_CELLS[`${p.r},${p.c}`]; if (special === 'DL') score *= 2; if (special === 'TW') wordMultiplier *= 3; if (special === 'DW') wordMultiplier *= 2; turnScore += score; }); if (state.tempPlacements.length === 7) { turnScore += 10; updateMessage("BINGO! +10 punti!", "success"); } turnScore *= wordMultiplier; // Applica state.tempPlacements.forEach(p => state.grid[p.r][p.c] = { letter: p.letter, score: p.score }); state.players[state.currentPlayerIdx].score += turnScore; // Pesca const needed = 7 - state.players[state.currentPlayerIdx].hand.length; if (needed > 0) { const drawn = drawLetters(needed, state.players[state.currentPlayerIdx].hand); state.players[state.currentPlayerIdx].hand.push(...drawn); } state.tempPlacements = []; state.selectedHandIndex = null; // Definizione divertente (Avvia l'animazione di caricamento) getFunDefinition(word); // Cambia giocatore state.currentPlayerIdx = state.currentPlayerIdx === 0 ? 1 : 0; render(); } function forceValidate() { if(state.lastUnknownWord) validateTurn(true, state.lastUnknownWord); } // --- LOGICA IA --- async function checkWordWithAI() { if(!state.lastUnknownWord) return; const word = state.lastUnknownWord; state.aiLoading = true; updateMessage("Chiedo al Mago... ✨", "info"); render(); // Mostra il caricatore const prompt = `"${word}" è una parola italiana valida ed esistente, adatta a un bambino di 7 anni? Rispondi solo SÌ o NO.`; const result = await callGeminiFast(prompt); state.aiLoading = false; if (result && result.trim().toUpperCase().includes("SÌ")) { validateTurn(true, word); } else { updateMessage(`Il Mago dice che "${word}" non esiste!`, "error"); render(); } } async function getFunDefinition(word) { state.aiLoading = true; updateMessage("Il Mago sta cercando... ⚡", "info"); render(); // Prompt semplificato const prompt = `Spiega la parola "${word}" a una bambina di 7 anni. Una frase divertente. Emoji finale.`; // Utilizzo della versione rapida const def = await callGeminiFast(prompt); state.aiLoading = false; if(def) updateMessage(def, "funfact"); else updateMessage(`Brava! "${word}" è una parola fantastica! 🌟`, "success"); render(); } // --- LOGICA DEI SUGGERIMENTI (SMART) --- // Verifica se posizionare una parola in una determinata posizione è valido (limiti, conflitti, parole incrociate) function isValidPlacement(word, r, c, isHorizontal, handLetters) { let tempHand = [...handLetters]; let lettersNeeded = 0; // 1. Verifica principale (limiti + conflitti + disponibilità delle lettere) for (let i = 0; i = GRID_SIZE || cc = GRID_SIZE) return false; let cell = state.grid[cr][cc]; if (cell) { if (cell.letter !== word[i]) return false; } else { let idx = tempHand.indexOf(word[i]); if (idx === -1) return false; tempHand.splice(idx, 1); lettersNeeded++; } } if (lettersNeeded === 0) return false; // Non deve usare solo lettere del tabellone // 2. Verifica delle parole incrociate for (let i = 0; i = 0 && nc >= 0 && state.grid[nr][nc]) { perpWord = state.grid[nr][nc].letter + perpWord; pBefore++; } else break; } // Scansione in avanti while (true) { let nr = isHorizontal ? cr + pAfter : cr; let nc = isHorizontal ? cc : cc + pAfter; if (nr 1) { if (!FULL_DICTIONARY.includes(perpWord)) return false; } } return true; } // Sistema di suggerimenti intelligente function giveSmartHint() { const player = state.players[state.currentPlayerIdx]; const hand = player.hand.map(l => l.letter); const isGridEmpty = state.grid[4][4] === null; let found = null; const shuffledDict = [...FULL_DICTIONARY].sort(() => Math.random() - 0.5); if (isGridEmpty) { for (let word of shuffledDict) { let tempHand = [...hand]; let possible = true; for(let char of word) { let idx = tempHand.indexOf(char); if(idx !== -1) tempHand.splice(idx, 1); else { possible = false; break; } } if(!possible) continue; // Prova a posizionarla in modo che tocchi il centro for(let i=0; i w.includes(anchor)); for (let word of candidates) { for (let i = 0; i ${letter} ${score} `; } function render() { const p1 = state.players[0]; const p2 = state.players[1]; const currentP = state.players[state.currentPlayerIdx]; const isP1 = state.currentPlayerIdx === 0; // Stili dei punteggi e dell'intestazione document.getElementById('p1-score').textContent = p1.score; document.getElementById('p2-score').textContent = p2.score; const p1Box = document.getElementById('p1-box'); const p2Box = document.getElementById('p2-box'); if(isP1) { p1Box.className = "flex flex-col items-center p-2 px-4 rounded-xl transition-all duration-300 bg-pink-100 ring-4 ring-pink-300 scale-110 shadow-md z-10"; p2Box.className = "flex flex-col items-center p-2 px-4 rounded-xl transition-all duration-300 opacity-40 grayscale scale-90"; } else { p2Box.className = "flex flex-col items-center p-2 px-4 rounded-xl transition-all duration-300 bg-blue-100 ring-4 ring-blue-300 scale-110 shadow-md z-10"; p1Box.className = "flex flex-col items-center p-2 px-4 rounded-xl transition-all duration-300 opacity-40 grayscale scale-90"; } // Cavalletto dell'avversario const otherP = isP1 ? p2 : p1; document.getElementById('opponent-rack').innerHTML = otherP.hand.map(l => `
${l.letter}
` ).join(''); // Tabellone const boardEl = document.getElementById('board'); boardEl.innerHTML = ''; for(let r=0; r t.r === r && t.c === c); const content = cellData || tempData; const special = SPECIAL_CELLS[`${r},${c}`]; let bgClass = "bg-white"; if(special === 'TW') bgClass = "bg-red-200 text-red-600"; else if(special === 'DL') bgClass = "bg-blue-200 text-blue-600"; else if(special === 'DW') bgClass = "bg-pink-300 text-pink-700"; else if(special === 'CENTER') bgClass = "bg-pink-400 text-white"; if(content) bgClass = "bg-amber-100"; cell.className = `w-full h-full aspect-square border-2 border-pink-200 rounded-md flex items-center justify-center relative ${bgClass} ${(!content && state.selectedHandIndex !== null) ? 'cursor-pointer hover:bg-pink-100 animate-pulse' : ''}`; cell.onclick = () => handleGridClick(r, c); if(!content && special) { cell.innerHTML = `${CELL_LABELS[special]}`; } if(content) { cell.innerHTML = renderTile(content.letter, content.score, !!tempData, false); } boardEl.appendChild(cell); } } // Stato del pulsante di convalida const valBtn = document.getElementById('btn-validate'); const canValidate = state.tempPlacements.length > 0; if(canValidate) { valBtn.className = `flex-1 py-3 rounded-2xl shadow-md font-bold text-white text-lg flex items-center justify-center gap-2 transition-all active:scale-95 duration-300 ${isP1 ? 'bg-gradient-to-r from-pink-400 to-pink-500 shadow-pink-200' : 'bg-gradient-to-r from-blue-400 to-blue-500 shadow-blue-200'}`; } else { valBtn.className = "flex-1 py-3 rounded-2xl shadow-md font-bold text-white text-lg flex items-center justify-center gap-2 transition-all active:scale-95 duration-300 bg-gray-300 cursor-not-allowed"; } // Cavalletto del giocatore const playerArea = document.getElementById('player-area'); const rackLabel = document.getElementById('rack-label'); const indicator = document.getElementById('turn-indicator'); playerArea.className = `fixed bottom-0 w-full bg-white border-t-4 p-4 pb-6 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)] transition-colors duration-500 ${isP1 ? 'border-pink-200' : 'border-blue-200'}`; rackLabel.textContent = `LETTERE DI ${currentP.name}`; rackLabel.className = `text-xs font-bold uppercase tracking-widest transition-colors duration-300 ${isP1 ? 'text-pink-500' : 'text-blue-500'}`; indicator.className = `w-3 h-3 rounded-full shadow-sm transition-colors duration-300 ${isP1 ? 'bg-pink-500' : 'bg-blue-500'}`; const handEl = document.getElementById('player-hand'); handEl.innerHTML = currentP.hand.map((tile, i) => `
${renderTile(tile.letter, tile.score, false, state.selectedHandIndex === i)}
`).join('') + Array(7 - currentP.hand.length).fill('
').join(''); // Aggiorna le icone lucide.createIcons(); } // Inizializzazione startNewGame();