From ac7e457e5a49ee563cde9f511ec2a9c8260e2aac Mon Sep 17 00:00:00 2001 From: Tommy Parnell Date: Fri, 23 Oct 2026 23:00:11 -0400 Subject: [PATCH] fix(gameplay): auto-erase tiles on invalid/duplicate submissions and show real-time already-found status --- src/components/WordBuilder.js | 8 +++++--- src/main.js | 10 ++++++++-- src/state/game-state.js | 6 ++---- src/styles/board.css | 11 +++++++++++ 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/components/WordBuilder.js b/src/components/WordBuilder.js index a3e7e1f..ed0d73c 100644 --- a/src/components/WordBuilder.js +++ b/src/components/WordBuilder.js @@ -13,6 +13,8 @@ export function createWordBuilder({ onTileRemoved, onTilesReordered, onSubmitWor const hasTiles = tileCount > 0; const tileObjects = state.activeTileIds.map(id => state.boardTiles.find(t => t.id === id)).filter(Boolean); const pts = tileCount === 4 ? 8 : tileCount === 3 ? 4 : tileCount === 2 ? 2 : 1; + const constructedWord = tileObjects.map(t => t.text.toLowerCase()).join(''); + const isAlreadyFound = hasTiles && state.foundWords.has(constructedWord); wrapper.innerHTML = `
@@ -35,10 +37,10 @@ export function createWordBuilder({ onTileRemoved, onTilesReordered, onSubmitWor -
diff --git a/src/main.js b/src/main.js index 2d4a2db..c71cd8f 100644 --- a/src/main.js +++ b/src/main.js @@ -174,8 +174,14 @@ async function initApp() { const result = gameState.submitWord(); if (!result.success) { - sound.playError(); - wordBuilder.triggerShake(); + if (result.reason !== 'empty') { + sound.playError(); + wordBuilder.triggerShake(); + // Automatically erase selected tiles after shake so player doesn't have to keep hitting backspace/x + setTimeout(() => { + gameState.clearActive(); + }, 400); + } return; } diff --git a/src/state/game-state.js b/src/state/game-state.js index c17a6f6..3265a3b 100644 --- a/src/state/game-state.js +++ b/src/state/game-state.js @@ -337,9 +337,7 @@ class GameStateManager { // Check if already found if (this.foundWords.has(constructedWord)) { this.notifyToast(`"${constructedWord.toUpperCase()}" already found!`, 'warning'); - this.activeTileIds = []; - this.notify(); - return { success: false, reason: 'duplicate' }; + return { success: false, reason: 'duplicate', word: constructedWord }; } // Look up in current puzzle's valid words list @@ -347,7 +345,7 @@ class GameStateManager { if (!match) { this.notifyToast(`"${constructedWord.toUpperCase()}" is not in the word list`, 'error'); - return { success: false, reason: 'invalid' }; + return { success: false, reason: 'invalid', word: constructedWord }; } // Valid word! diff --git a/src/styles/board.css b/src/styles/board.css index e2fc5b8..aef0559 100644 --- a/src/styles/board.css +++ b/src/styles/board.css @@ -186,6 +186,17 @@ margin-left: 2px; } +.btn-submit-tray.is-duplicate { + background: linear-gradient(135deg, #f59e0b, #d97706); + box-shadow: 0 2px 8px rgba(245, 158, 11, 0.35); +} + +.submit-pts.already-found { + background: rgba(0, 0, 0, 0.22); + color: #ffffff; + font-weight: 800; +} + @media (max-width: 420px) { .word-builder { padding: 3px 6px;