From 974bd3c39febcc120fd780cb297f0137fade5313 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Tue, 21 Jan 2025 22:13:56 +1100 Subject: [PATCH] Initial commit Have got guesses and colourisation working --- src/index.html | 69 ++++++++++++++++ src/scripts/main.js | 192 ++++++++++++++++++++++++++++++++++++++++++++ src/styles/main.css | 32 ++++++++ 3 files changed, 293 insertions(+) create mode 100644 src/index.html create mode 100644 src/scripts/main.js create mode 100644 src/styles/main.css diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..93de54e --- /dev/null +++ b/src/index.html @@ -0,0 +1,69 @@ + + + + + + + Wordle Clone + + +
+

Wordle Clone

+
+ +
+
+ +
+ +
+
+ + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + +
+
+ + +
+ + + + \ No newline at end of file diff --git a/src/scripts/main.js b/src/scripts/main.js new file mode 100644 index 0000000..523e8ab --- /dev/null +++ b/src/scripts/main.js @@ -0,0 +1,192 @@ +import { Application, Controller } from "https://unpkg.com/@hotwired/stimulus@v3.2.2/dist/stimulus.js" + +window.Stimulus = Application.start(); + +const MARKER_MISS = 'm'; +const MARKER_RIGHT_POS = 'g'; +const MARKER_RIGHT_CHAR = 'y'; + +class GameController { + constructor() { + this._currentWord = "DEERS"; + this._guesses = 5; + } + + wordLength() { + return this._currentWord.length; + } + + guesses() { + return this._guesses; + } + + checkGuess(guess) { + if (guess.length != this._currentWord.length) { + throw Error(`Expected length to be ${this._currentWord.length} but was ${guess.length}`); + } + + // Check correct placements + let markers = new Array(guess.length); + let misses = {}; + let hits = {}; + + for (let i = 0; i < guess.length; i++) { + if (guess[i] == this._currentWord[i]) { + markers[i] = MARKER_RIGHT_POS; + hits[guess[i]] = MARKER_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] = MARKER_RIGHT_CHAR; + + if (!hits[guess[i]]) { + hits[guess[i]] = MARKER_RIGHT_CHAR; + } + } else { + if (!hits[guess[i]]) { + hits[guess[i]] = MARKER_MISS; + } + markers[i] = MARKER_MISS; + } + } + + return { + hits: hits, + markers: markers + }; + } +} + + +Stimulus.register("playfield", class extends Controller { + static targets = ["row"]; + + connect() { + this._gameController = new GameController(); + + this._activeRowIndex = 0; + this._activeLetter = 0; + + this._buildPlayfield(this._gameController.guesses(), this._gameController.wordLength()); + } + + tappedKey(key) { + console.log(`Key ${key} was tapped via outliet`); + + this._addLetter(key); + } + + _addLetter(letter) { + let rowElem = this.rowTargets[this._activeRowIndex]; + let colElem = rowElem.querySelectorAll("span")[this._activeLetter]; + + colElem.innerText = letter.toUpperCase(); + + this._activeLetter += 1; + if (this._activeLetter >= this._gameController.wordLength()) { + this._colorizeRow(rowElem); + + this._activeRowIndex += 1; + this._activeLetter = 0; + } + } + + _buildPlayfield(rows, wordLength) { + let newRows = []; + for (let r = 0; r < rows; r++) { + 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"); + divElem.appendChild(letterSpan); + } + + newRows.push(divElem); + } + + this.element.replaceChildren.apply(this.element, newRows); + } + + _colorizeRow(row) { + let guessedWord = Array.from(row.querySelectorAll("span")).map((x) => x.innerText).join(""); + console.log("The guessed word is: " + guessedWord); + + let results = this._gameController.checkGuess(guessedWord); + let markers = results.markers; + + for (let i = 0; i < this._gameController.wordLength(); i++) { + switch (markers[i]) { + case MARKER_RIGHT_POS: + row.children[i].classList.add("right-pos"); + break; + case MARKER_RIGHT_CHAR: + row.children[i].classList.add("right-char"); + break; + case MARKER_MISS: + row.children[i].classList.add("miss"); + break; + } + } + + window.dispatchEvent(new CustomEvent("guessResults", { + detail: results + })); + } +}); + +Stimulus.register("keyboard", class extends Controller { + static targets = [ "key" ]; + static outlets = [ "playfield" ]; + + tappedKey(ev) { + ev.preventDefault(); + + let key = ev.target.dataset["key"]; + this.playfieldOutlet.tappedKey(key); + } + + colorizeKeys(ev) { + let hits = ev.detail.hits; + + for (let k in hits) { + let thisKey = k.toLowerCase(); + let marker = hits[k]; + + let keyElement = this.keyTargets.filter((e) => e.dataset.key == thisKey)[0]; + switch (marker) { + case MARKER_RIGHT_POS: + keyElement.classList.value = "right-pos"; + break; + case MARKER_RIGHT_CHAR: + if (!keyElement.classList.contains("right-pos")) { + keyElement.classList.add("right-char"); + } + break; + case MARKER_MISS: + if (keyElement.classList.length == 0) { + keyElement.classList.add("miss"); + } + break; + } + } + + } +}); + diff --git a/src/styles/main.css b/src/styles/main.css new file mode 100644 index 0000000..e984906 --- /dev/null +++ b/src/styles/main.css @@ -0,0 +1,32 @@ +button[data-keyboard-target="key"].right-pos { + background: green; +} + +button[data-keyboard-target="key"].right-char { + background: yellow; +} + +button[data-keyboard-target="key"].miss { + background: grey; +} + + +div.playfield div.row span { + display: inline-block; + border: solid thin gray; + + height: 1.1em; + width: 1.1em; +} + +div.playfield div.row span.right-pos { + background: green; +} + +div.playfield div.row span.right-char { + background: yellow; +} + +div.playfield div.row span.miss { + background: grey; +} \ No newline at end of file