Added some quality of life features
All checks were successful
/ publish (push) Successful in 1m25s

- Now preload the letters on successive rows
- Hid the keyboard during intermissions
- Added a give-up easter egg when tapping the enter six times
- Reshuffled the letters
This commit is contained in:
Leon Mika 2025-10-30 21:50:58 +11:00
parent 566f55ed12
commit a2211030a0
6 changed files with 478 additions and 400 deletions

File diff suppressed because one or more lines are too long

View file

@ -48,7 +48,17 @@ export default class extends Controller {
keyElement.classList.value = "";
}
}
onShow() {
let keyboard = this.element;
keyboard.style.display = "block";
}
onHide() {
let keyboard = this.element;
keyboard.style.display = "none";
}
colorizeKeys(ev) {
let hits = ev.detail.hits;

View file

@ -1,256 +1,310 @@
import { Controller } from "https://unpkg.com/@hotwired/stimulus@v3.2.2/dist/stimulus.js"
import {Controller} from "https://unpkg.com/@hotwired/stimulus@v3.2.2/dist/stimulus.js"
import { GUESS_RESULT, MARKERS, GameController } from "../models/gamecontroller.js";
import { WordSource } from "../models/words.js";
import {GameController, GUESS_RESULT, MARKERS} from "../models/gamecontroller.js";
import {WordSource} from "../models/words.js";
const CONSECUTIVE_ENTERS_TO_GIVE_UP = 6;
export default class extends Controller {
static targets = ["row", "playfield", "topMessage", "nextPuzzleButtons", "loader"];
static outlets = ["overlay"];
static targets = ["row", "playfield", "topMessage", "nextPuzzleButtons", "loader"];
static outlets = ["overlay"];
async connect() {
this._wordSource = new WordSource();
this._gameController = new GameController(this._wordSource);
async connect() {
this._wordSource = new WordSource();
this._gameController = new GameController(this._wordSource);
this._consectiveEnters = 0;
await this._gameController.start();
await this._gameController.start();
this._buildPlayfield();
this._buildPlayfield();
this.loaderTarget.classList.add("hide");
}
tappedKey(key) {
this._addLetter(key);
}
_addLetter(letter) {
if (this._activeRowIndex < 0) {
return;
} else if (this._activeLetter >= this._gameController.wordLength()) {
return;
this.loaderTarget.classList.add("hide");
}
let rowElem = this.rowTargets[this._activeRowIndex];
let colElem = rowElem.querySelectorAll("span")[this._activeLetter];
colElem.innerText = letter.toUpperCase();
colElem.classList.remove("hint");
this._activeLetter += 1;
}
enterGuess() {
if (this._activeLetter >= this._gameController.wordLength()) {
let rowElem = this.rowTargets[this._activeRowIndex];
this._verifyGuess(rowElem);
}
}
loadDef(ev) {
ev.preventDefault()
let word = this._gameController.currentWord();
window.open(`https://www.ecosia.org/search?q=define+${word}`, "_blank");
}
tappedBackspace() {
if (this._activeLetter == 0) {
return;
tappedKey(key) {
this._consectiveEnters = 0;
this._addLetter(key);
}
this._activeLetter -= 1;
let rowElem = this.rowTargets[this._activeRowIndex];
let colElem = rowElem.querySelectorAll("span")[this._activeLetter];
_addLetter(letter) {
if (this._activeRowIndex < 0) {
return;
} else if (this._activeLetter >= this._gameController.wordLength()) {
return;
}
let colHint = colElem.dataset["hint"];
if (colHint) {
colElem.classList.add("hint");
colElem.innerText = colHint;
} else {
colElem.innerText = "";
let rowElem = this.rowTargets[this._activeRowIndex];
this._addLetterAtRow(rowElem, letter);
}
}
async _verifyGuess(rowElem) {
let guessedWord = Array.from(rowElem.querySelectorAll("span")).map((x) => x.innerText).join("");
let results = this._gameController.submitGuess(guessedWord);
_addLetterAtRow(rowElem, letter) {
let colElem = rowElem.querySelectorAll("span")[this._activeLetter];
switch (results.guessResult) {
case GUESS_RESULT.FOUL:
this.overlayOutlet.showMessage("Not in dictionary");
colElem.innerText = letter.toUpperCase();
colElem.classList.remove("hint");
let newRow = this._buildPlayfieldRow(this._gameController.wordLength());
this._showHints(newRow);
rowElem.replaceWith(newRow);
this._activeLetter = 0;
this._activeLetter += 1;
}
window.dispatchEvent(new CustomEvent("guessResults", {
detail: results
}));
break;
case GUESS_RESULT.MISS:
await this._colorizeRow(rowElem, results, true);
this._broadcastGuessResults(results);
enterGuess() {
if (this._activeLetter >= this._gameController.wordLength()) {
let rowElem = this.rowTargets[this._activeRowIndex];
this._verifyGuess(rowElem);
this._consectiveEnters = 0;
} else {
this._consectiveEnters += 1;
if (this._consectiveEnters >= CONSECUTIVE_ENTERS_TO_GIVE_UP) {
this._giveUp();
this._consectiveEnters = 0;
}
}
}
this._activeRowIndex += 1;
if (this._activeRowIndex >= this._gameController.guesses()) {
this._revealAnswer();
} else {
loadDef(ev) {
ev.preventDefault()
let word = this._gameController.currentWord();
window.open(`https://www.ecosia.org/search?q=define+${word}`, "_blank");
}
_giveUp() {
if (confirm("Are you sure you want to reveal the answer?")) {
this._gameController.giveUp();
this._revealAnswer();
}
}
tappedBackspace() {
this._consectiveEnters = 0;
if (this._activeLetter == 0) {
return;
}
this._activeLetter -= 1;
let rowElem = this.rowTargets[this._activeRowIndex];
let colElem = rowElem.querySelectorAll("span")[this._activeLetter];
let colHint = colElem.dataset["hint"];
if (colHint) {
colElem.classList.add("hint");
colElem.innerText = colHint;
} else {
colElem.innerText = "";
}
}
async _verifyGuess(rowElem) {
let guessedWord = Array.from(rowElem.querySelectorAll("span")).map((x) => x.innerText).join("");
let results = this._gameController.submitGuess(guessedWord);
switch (results.guessResult) {
case GUESS_RESULT.FOUL:
this.overlayOutlet.showMessage("Not in dictionary");
let newRow = this._buildPlayfieldRow(this._gameController.wordLength());
this._showHints(newRow);
rowElem.replaceWith(newRow);
this._activeLetter = 0;
if (this._activeRowIndex > 0) {
this._prefillFirstFewLetters(newRow);
}
window.dispatchEvent(new CustomEvent("guessResults", {
detail: results
}));
break;
case GUESS_RESULT.MISS:
await this._colorizeRow(rowElem, results, true);
this._broadcastGuessResults(results);
this._activeRowIndex += 1;
if (this._activeRowIndex >= this._gameController.guesses()) {
this._revealAnswer();
} else {
this._activeLetter = 0;
this._showHints(this.rowTargets[this._activeRowIndex], results.markers);
this._prefillFirstFewLetters(this.rowTargets[this._activeRowIndex]);
}
break;
case GUESS_RESULT.WIN:
await this._colorizeRow(rowElem, results, true);
this._broadcastGuessResults(results);
this._showWin();
break;
}
}
async loadNextPuzzle(ev) {
ev.preventDefault();
this.loaderTarget.classList.remove("hide");
if (await this._gameController.nextWord()) {
this._buildPlayfield();
} else {
this.overlayOutlet.showMessage("No more words available.");
}
this.loaderTarget.classList.add("hide");
}
_showWin() {
this.topMessageTarget.innerText = "Hooray! You did it.";
this.nextPuzzleButtonsTarget.classList.remove("hide");
this._setKeyboardVisibility(false);
}
_revealAnswer() {
this.topMessageTarget.innerText = this._gameController.currentWord().toUpperCase();
this.nextPuzzleButtonsTarget.classList.remove("hide");
this._setKeyboardVisibility(false);
}
_buildPlayfield() {
let rows = this._gameController.guesses();
let wordLength = this._gameController.wordLength();
let {currentGuesses, wasWin, wasGiveUp} = this._gameController.currentState();
this._activeRowIndex = currentGuesses.length;
this._activeLetter = 0;
this._showHints(this.rowTargets[this._activeRowIndex], results.markers);
}
break;
case GUESS_RESULT.WIN:
await this._colorizeRow(rowElem, results, true);
this._broadcastGuessResults(results);
this._showWin();
break;
}
}
window.dispatchEvent(new CustomEvent("resetKeyColors"));
async loadNextPuzzle(ev) {
ev.preventDefault();
let newRows = [];
let lastGuessResults = null;
for (let r = 0; r < rows; r++) {
let currentGuess = null;
let currentGuessResults = null;
this.loaderTarget.classList.remove("hide");
if (r < currentGuesses.length) {
currentGuess = currentGuesses[r];
currentGuessResults = this._gameController.checkGuess(currentGuess);
}
if (await this._gameController.nextWord()) {
this._buildPlayfield();
} else {
this.overlayOutlet.showMessage("No more words available.");
newRows.push(this._buildPlayfieldRow(wordLength, currentGuess, currentGuessResults));
if (currentGuessResults) {
lastGuessResults = currentGuessResults;
}
}
if (lastGuessResults != null) {
this._broadcastGuessResults(lastGuessResults);
}
if (wasWin) {
this._showWin();
} else if ((currentGuesses.length >= rows) || wasGiveUp) {
// User has already used up all their guesses so just show the results;
this._revealAnswer();
} else {
this._showHints(newRows[currentGuesses.length]);
if (currentGuesses.length > 0) {
this._prefillFirstFewLetters(newRows[currentGuesses.length]);
}
this.topMessageTarget.innerHTML = "&nbsp;"
this.nextPuzzleButtonsTarget.classList.add("hide");
this._setKeyboardVisibility(true);
}
this.playfieldTarget.replaceChildren.apply(this.playfieldTarget, newRows);
}
this.loaderTarget.classList.add("hide");
}
_buildPlayfieldRow(wordLength, currentGuess, currentGuessResults) {
let divElem = document.createElement("div");
divElem.classList.add("row");
divElem.setAttribute("data-playfield-target", "row");
_showWin() {
this.topMessageTarget.innerText = "Hooray! You did it.";
this.nextPuzzleButtonsTarget.classList.remove("hide");
}
for (let c = 0; c < wordLength; c++) {
let letterSpan = document.createElement("span");
if (currentGuess) {
letterSpan.innerText = currentGuess[c].toUpperCase();
}
divElem.appendChild(letterSpan);
}
_revealAnswer() {
this.topMessageTarget.innerText = this._gameController.currentWord().toUpperCase();
this.nextPuzzleButtonsTarget.classList.remove("hide");
}
if (currentGuess) {
this._colorizeRow(divElem, currentGuessResults, false);
}
_buildPlayfield() {
let rows = this._gameController.guesses();
let wordLength = this._gameController.wordLength();
let {currentGuesses, wasWin} = this._gameController.currentState();
this._activeRowIndex = currentGuesses.length;
this._activeLetter = 0;
window.dispatchEvent(new CustomEvent("resetKeyColors"));
let newRows = [];
let lastGuessResults = null;
for (let r = 0; r < rows; r++) {
let currentGuess = null;
let currentGuessResults = null;
if (r < currentGuesses.length) {
currentGuess = currentGuesses[r];
currentGuessResults = this._gameController.checkGuess(currentGuess);
}
newRows.push(this._buildPlayfieldRow(wordLength, currentGuess, currentGuessResults));
if (currentGuessResults) {
lastGuessResults = currentGuessResults;
}
return divElem;
}
if (lastGuessResults != null) {
this._broadcastGuessResults(lastGuessResults);
async _colorizeRow(row, results, delayed) {
let markers = results.markers;
if (delayed) {
row.classList.add("animated-colors");
}
for (let i = 0; i < this._gameController.wordLength(); i++) {
this._colorizeCell(row, results, i);
}
if (delayed) {
return new Promise(resolve => {
row.children[row.children.length - 1].addEventListener("transitionend", () => {
resolve();
});
});
}
return Promise.resolve();
}
if (wasWin) {
this._showWin();
} else if (currentGuesses.length >= rows) {
// User has already used up all their guesses so just show the results;
this._revealAnswer();
} else {
this._showHints(newRows[currentGuesses.length]);
this.topMessageTarget.innerHTML = "&nbsp;"
this.nextPuzzleButtonsTarget.classList.add("hide");
_broadcastGuessResults(results) {
window.dispatchEvent(new CustomEvent("guessResults", {
detail: results
}));
}
this.playfieldTarget.replaceChildren.apply(this.playfieldTarget, newRows);
}
_buildPlayfieldRow(wordLength, currentGuess, currentGuessResults) {
let divElem = document.createElement("div");
divElem.classList.add("row");
divElem.setAttribute("data-playfield-target", "row");
for (let c = 0; c < wordLength; c++) {
let letterSpan = document.createElement("span");
if (currentGuess) {
letterSpan.innerText = currentGuess[c].toUpperCase();
}
divElem.appendChild(letterSpan);
_colorizeCell(row, results, cellIndex) {
let markers = results.markers;
switch (markers[cellIndex]) {
case MARKERS.RIGHT_POS:
row.children[cellIndex].classList.add("right-pos");
break;
case MARKERS.RIGHT_CHAR:
row.children[cellIndex].classList.add("right-char");
break;
case MARKERS.MISS:
row.children[cellIndex].classList.add("miss");
break;
}
}
if (currentGuess) {
this._colorizeRow(divElem, currentGuessResults, false);
_showHints(row) {
let hint = this._gameController.showHint();
for (let i = 0; i < this._gameController.wordLength(); i++) {
if (hint[i]) {
let colElem = row.children[i];
colElem.classList.add("hint");
colElem.innerText = hint[i].toUpperCase();
colElem.dataset["hint"] = hint[i].toUpperCase();
}
}
}
return divElem;
}
_prefillFirstFewLetters(row) {
let hint = this._gameController.showHint();
async _colorizeRow(row, results, delayed) {
let markers = results.markers;
if (delayed) {
row.classList.add("animated-colors");
for (let i = 0; i < this._gameController.wordLength(); i++) {
if (hint[i]) {
this._addLetterAtRow(row, hint[i]);
} else {
return;
}
}
}
for (let i = 0; i < this._gameController.wordLength(); i++) {
this._colorizeCell(row, results, i);
_setKeyboardVisibility(visible) {
if (visible) {
window.dispatchEvent(new CustomEvent("showKeyboard"));
} else {
window.dispatchEvent(new CustomEvent("hideKeyboard"));
}
}
if (delayed) {
return new Promise(resolve => {
row.children[row.children.length - 1].addEventListener("transitionend", () => {
resolve();
});
});
}
return Promise.resolve();
}
_broadcastGuessResults(results) {
window.dispatchEvent(new CustomEvent("guessResults", {
detail: results
}));
}
_colorizeCell(row, results, cellIndex) {
let markers = results.markers;
switch (markers[cellIndex]) {
case MARKERS.RIGHT_POS:
row.children[cellIndex].classList.add("right-pos");
break;
case MARKERS.RIGHT_CHAR:
row.children[cellIndex].classList.add("right-char");
break;
case MARKERS.MISS:
row.children[cellIndex].classList.add("miss");
break;
}
}
_showHints(row) {
let hint = this._gameController.showHint();
for (let i = 0; i < this._gameController.wordLength(); i++) {
if (hint[i]) {
let colElem = row.children[i];
colElem.classList.add("hint");
colElem.innerText = hint[i].toUpperCase();
colElem.dataset["hint"] = hint[i].toUpperCase();
}
}
}
}

View file

@ -1,7 +1,7 @@
export const GUESS_RESULT = {
MISS: 'm',
WIN: 'w',
FOUL: 'f'
MISS: 'm',
WIN: 'w',
FOUL: 'f'
};
export const MARKERS = {
@ -12,207 +12,217 @@ export const MARKERS = {
};
class ProgressionState {
getProgression() {
let prog = localStorage.getItem('progression');
if (prog) {
return JSON.parse(prog);
getProgression() {
let prog = localStorage.getItem('progression');
if (prog) {
return JSON.parse(prog);
}
this.clearProgression("");
return prog;
}
this.clearProgression("");
return prog;
}
clearProgression(shuffleId) {
let prog = {
shuffleId: shuffleId,
wordLength: 4,
wordIndex: {"4": 0, "5": 0, "6": 0, "7": 0},
currentGuesses: []
};
localStorage.setItem('progression', JSON.stringify(prog));
return prog;
}
clearProgression(shuffleId) {
let prog = {
shuffleId: shuffleId,
wordLength: 4,
wordIndex: {"4": 0, "5": 0, "6": 0, "7": 0},
currentGuesses: []
};
localStorage.setItem('progression', JSON.stringify(prog));
return prog;
}
saveProgression(prog) {
localStorage.setItem('progression', JSON.stringify(prog));
}
saveProgression(prog) {
localStorage.setItem('progression', JSON.stringify(prog));
}
}
export class GameController {
constructor(wordSource) {
this._wordSource = wordSource;
this._progressionState = new ProgressionState();
}
async start() {
let prog = this._progressionState.getProgression();
if (await this._wordSource.needToResetProgression(prog)) {
console.log("Clearing patten shuffle id")
prog = this._progressionState.clearProgression(await this._wordSource.getPattenShuffleID());
constructor(wordSource) {
this._wordSource = wordSource;
this._progressionState = new ProgressionState();
}
this._currentWord = await this._wordSource.getCurrentWord(prog);
this._guesses = 6;
async start() {
let prog = this._progressionState.getProgression();
console.log("The current word: " + this._currentWord);
}
wordLength() {
this._checkHasStarted();
return this._currentWord.length;
}
guesses() {
this._checkHasStarted();
return this._guesses;
}
currentState() {
let prog = this._progressionState.getProgression();
if (!prog || !prog.currentGuesses) {
return [];
}
return {
wasWin: prog.wasWin,
currentGuesses: prog.currentGuesses
};
}
currentWord() {
this._checkHasStarted();
return this._currentWord;
}
async nextWord() {
// Increment the progress
let prog = this._progressionState.getProgression();
prog.wordIndex[prog.wordLength + ""] += 1;
//prog.wordLength = (((Math.random() * 23) | 0) / 10 | 0) + 4;
prog.wordLength = ((Math.random() * 4) | 0) + 4;
prog.currentGuesses = [];
prog.wasWin = false;
this._progressionState.saveProgression(prog);
this._currentWord = await this._wordSource.getCurrentWord(prog);
//this._guesses = Math.max(this._currentWord.length, 5) + 1;
this._guesses = 6;
return true;
}
showHint() {
let hints = new Array(this._currentWord.length);
let priorGuesses = this._progressionState.getProgression().currentGuesses;
hints[0] = this._currentWord[0];
for (let i = 1; i < this._currentWord.length; i++) {
for (let guess of priorGuesses) {
if (guess[i] === this._currentWord[i]) {
hints[i] = this._currentWord[i];
if (await this._wordSource.needToResetProgression(prog)) {
console.log("Clearing patten shuffle id")
prog = this._progressionState.clearProgression(await this._wordSource.getPattenShuffleID());
}
}
this._currentWord = await this._wordSource.getCurrentWord(prog);
this._guesses = 6;
console.log("The current word: " + this._currentWord);
}
return hints;
}
submitGuess(guess) {
guess = guess.toLowerCase();
let results = this.checkGuess(guess);
// Add this guess to the progression state
if (results.guessResult !== GUESS_RESULT.FOUL) {
let prog = this._progressionState.getProgression();
prog.currentGuesses.push(guess);
prog.wasWin = results.guessResult === GUESS_RESULT.WIN;
this._progressionState.saveProgression(prog);
wordLength() {
this._checkHasStarted();
return this._currentWord.length;
}
return results;
}
checkGuess(guess) {
this._checkHasStarted();
guess = guess.toLowerCase();
if (guess.length != this._currentWord.length) {
throw Error(`Expected length to be ${this._currentWord.length} but was ${guess.length}`);
}
// Check correct placements
let guessResult;
let markers = new Array(guess.length);
let misses = {};
let hits = {};
if (!this._wordSource.isWord(guess)) {
hits = {};
for (let i = 0; i < guess.length; i++) {
hits[guess[i]] = MARKERS.ATTEMPTED;
}
return {
hits: hits,
guessResult: GUESS_RESULT.FOUL,
};
guesses() {
this._checkHasStarted();
return this._guesses;
}
for (let i = 0; i < guess.length; i++) {
if (guess[i] == this._currentWord[i]) {
markers[i] = MARKERS.RIGHT_POS;
hits[guess[i]] = MARKERS.RIGHT_POS;
} else {
if (this._currentWord[i] in misses) {
misses[this._currentWord[i]] += 1;
currentState() {
let prog = this._progressionState.getProgression();
if (!prog || !prog.currentGuesses) {
return {
currentGuesses: []
};
}
return {
wasWin: prog.wasWin,
currentGuesses: prog.currentGuesses,
wasGiveUp: prog.wasGiveUp,
};
}
currentWord() {
this._checkHasStarted();
return this._currentWord;
}
async nextWord() {
// Increment the progress
let prog = this._progressionState.getProgression();
prog.wordIndex[prog.wordLength + ""] += 1;
//prog.wordLength = (((Math.random() * 23) | 0) / 10 | 0) + 4;
prog.wordLength = ((Math.random() * 4) | 0) + 4;
prog.currentGuesses = [];
prog.wasWin = false;
prog.wasGiveUp = false;
this._progressionState.saveProgression(prog);
this._currentWord = await this._wordSource.getCurrentWord(prog);
//this._guesses = Math.max(this._currentWord.length, 5) + 1;
this._guesses = 6;
return true;
}
showHint() {
let hints = new Array(this._currentWord.length);
let priorGuesses = this._progressionState.getProgression().currentGuesses;
hints[0] = this._currentWord[0];
for (let i = 1; i < this._currentWord.length; i++) {
for (let guess of priorGuesses) {
if (guess[i] === this._currentWord[i]) {
hints[i] = this._currentWord[i];
}
}
}
return hints;
}
submitGuess(guess) {
guess = guess.toLowerCase();
let results = this.checkGuess(guess);
// Add this guess to the progression state
if (results.guessResult !== GUESS_RESULT.FOUL) {
let prog = this._progressionState.getProgression();
prog.currentGuesses.push(guess);
prog.wasWin = results.guessResult === GUESS_RESULT.WIN;
this._progressionState.saveProgression(prog);
}
return results;
}
giveUp() {
let prog = this._progressionState.getProgression();
prog.wasGiveUp = true;
this._progressionState.saveProgression(prog);
}
checkGuess(guess) {
this._checkHasStarted();
guess = guess.toLowerCase();
if (guess.length != this._currentWord.length) {
throw Error(`Expected length to be ${this._currentWord.length} but was ${guess.length}`);
}
// Check correct placements
let guessResult;
let markers = new Array(guess.length);
let misses = {};
let hits = {};
if (!this._wordSource.isWord(guess)) {
hits = {};
for (let i = 0; i < guess.length; i++) {
hits[guess[i]] = MARKERS.ATTEMPTED;
}
return {
hits: hits,
guessResult: GUESS_RESULT.FOUL,
};
}
for (let i = 0; i < guess.length; i++) {
if (guess[i] == this._currentWord[i]) {
markers[i] = MARKERS.RIGHT_POS;
hits[guess[i]] = MARKERS.RIGHT_POS;
} else {
if (this._currentWord[i] in misses) {
misses[this._currentWord[i]] += 1;
} else {
misses[this._currentWord[i]] = 1;
}
}
}
// Check words that are wrong placement but are in the word
// Distribute based on the words position
for (let i = 0; i < guess.length; i++) {
if (markers[i]) {
continue;
}
if (misses[guess[i]] && misses[guess[i]] > 0) {
misses[guess[i]] -= 1;
markers[i] = MARKERS.RIGHT_CHAR;
if (!hits[guess[i]]) {
hits[guess[i]] = MARKERS.RIGHT_CHAR;
}
} else {
if (!hits[guess[i]]) {
hits[guess[i]] = MARKERS.MISS;
}
markers[i] = MARKERS.MISS;
}
}
let isRight = markers.filter((x) => x == MARKERS.RIGHT_POS).length == this._currentWord.length;
if (isRight) {
guessResult = GUESS_RESULT.WIN;
} else {
misses[this._currentWord[i]] = 1;
guessResult = GUESS_RESULT.MISS;
}
}
}
// Check words that are wrong placement but are in the word
// Distribute based on the words position
for (let i = 0; i < guess.length; i++) {
if (markers[i]) {
continue;
}
if (misses[guess[i]] && misses[guess[i]] > 0) {
misses[guess[i]] -= 1;
markers[i] = MARKERS.RIGHT_CHAR;
if (!hits[guess[i]]) {
hits[guess[i]] = MARKERS.RIGHT_CHAR;
}
} else {
if (!hits[guess[i]]) {
hits[guess[i]] = MARKERS.MISS;
}
markers[i] = MARKERS.MISS;
}
}
let isRight = markers.filter((x) => x == MARKERS.RIGHT_POS).length == this._currentWord.length;
if (isRight) {
guessResult = GUESS_RESULT.WIN;
} else {
guessResult = GUESS_RESULT.MISS;
return {
guessResult: guessResult,
hits: hits,
markers: markers
};
}
return {
guessResult: guessResult,
hits: hits,
markers: markers
};
}
_checkHasStarted() {
if (!this._currentWord) {
throw new Error("call start() first");
_checkHasStarted() {
if (!this._currentWord) {
throw new Error("call start() first");
}
}
}
}

View file

@ -69,7 +69,8 @@ div.playfield div[data-playfield-target="playfield"] {
div.playfield div[data-playfield-target="nextPuzzleButtons"] {
display: flex;
justify-content: center;
gap: 0.8rem;
gap: 1rem;
margin-block-start: 1.2rem;
margin-block-end: 0.8rem;
}

View file

@ -30,10 +30,13 @@
</div>
<div class="keyboard" data-controller="keyboard"
data-keyboard-playfield-outlet=".playfield"
style="display: none;"
data-action="
keydown@window->keyboard#onKeyPress
guessResults@window->keyboard#colorizeKeys
resetKeyColors@window->keyboard#resetKeyColors
showKeyboard@window->keyboard#onShow
hideKeyboard@window->keyboard#onHide
">
<div>
<button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="q">q</button>