- 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:
		
							parent
							
								
									566f55ed12
								
							
						
					
					
						commit
						a2211030a0
					
				
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -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; | ||||
|      | ||||
|  |  | |||
|  | @ -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 = " " | ||||
|             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 = " " | ||||
|       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(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -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"); | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue