├── README.md ├── Dockerfile ├── Makefile ├── components ├── errMsg.js ├── inputField.js ├── checkButton.js ├── gameover.js └── tries.js ├── app.js ├── utils ├── countOccurence.js └── crypt.js ├── index.html ├── .gitignore └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # sha256le 2 | 3 | Wordle sha256 version 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM caddy:2.4.6-alpine 2 | 3 | WORKDIR /usr/share/caddy 4 | 5 | COPY index.html . 6 | COPY app.js . 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: image ctn-run 2 | 3 | PORT ?= 2931 4 | 5 | image: 6 | docker build . -t sha256le 7 | 8 | ctn-run: 9 | docker run -p=$(PORT):80 sha256le 10 | -------------------------------------------------------------------------------- /components/errMsg.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const addErrMsg = (parentNode) => { 4 | // Error message if needed 5 | const errMsg = document.createElement("div") 6 | errMsg.id = "err" 7 | errMsg.style.color = "red" 8 | errMsg.style.fontSize = "0.75em" 9 | errMsg.style.textAlign = "left" 10 | errMsg.innerText = "" 11 | 12 | parentNode.appendChild(errMsg) 13 | 14 | return errMsg 15 | } 16 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | let guess = "" 4 | let nbTry = 0 5 | let secretSha256 = "" 6 | 7 | const main = async () => { 8 | secretSha256 = await digestMessage(Math.random().toString()) 9 | const app = document.getElementById("app") 10 | 11 | const inputField = addInputField(app) 12 | addCheckButton(app, onValidInput, onInvalidInput) 13 | addErrMsg(app) 14 | addTriesNode(app) 15 | } 16 | 17 | window.addEventListener("load", main) 18 | -------------------------------------------------------------------------------- /utils/countOccurence.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const countOccurence = (word, character) => { 4 | return word.split("").filter(c => c === character).length 5 | } 6 | 7 | const countOccurenceGoodPlaceAfter = (start, guess, secret, character) => { 8 | let nb = 0 9 | 10 | for (let i = start; i < secret.length; i++) { 11 | if (guess[i] === character && secret[i] === character) { 12 | nb++ 13 | } 14 | } 15 | 16 | return nb 17 | } 18 | -------------------------------------------------------------------------------- /utils/crypt.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | // Function from https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest 4 | async function digestMessage(message) { 5 | const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array 6 | const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message 7 | const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array 8 | const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string 9 | return hashHex; 10 | } 11 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sha256le 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | Source code available on GitHub 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | .vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /components/inputField.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const addInputField = (parentNode) => { 4 | // Text box to input sha256 5 | const inputField = document.createElement("input") 6 | inputField.type = "text" 7 | inputField.id = "input" 8 | inputField.maxLength = 64 9 | inputField.autofocus = "autofocus" 10 | inputField.size = 64 11 | inputField.style.fontFamily = "monospace" 12 | inputField.placeholder = "sha256" 13 | inputField.addEventListener("input", (_) => { 14 | const inputField = document.getElementById("input") 15 | guess = inputField.value.toLowerCase() 16 | }) 17 | // Check guess sha256 when pressing Enter 18 | inputField.addEventListener("keyup", (e) => { 19 | if (e.keyCode != 13) { 20 | return 21 | } 22 | 23 | const checkButton = document.getElementById("checkButton") 24 | checkButton.click() 25 | }) 26 | 27 | parentNode.appendChild(inputField) 28 | 29 | return inputField 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alban 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/checkButton.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const addCheckButton = (parentNode, onValidInput, onInvalidInput) => { 4 | // Button to check the input 5 | const checkButton = document.createElement("button") 6 | checkButton.id = "checkButton" 7 | checkButton.innerHTML = "Check" 8 | checkButton.addEventListener("click", () => { 9 | if (isValidInput(guess)) { 10 | onValidInput(guess) 11 | } else { 12 | onInvalidInput(guess) 13 | } 14 | }) 15 | 16 | parentNode.appendChild(checkButton) 17 | 18 | return addCheckButton 19 | } 20 | 21 | const isValidInput = (input) => { 22 | return input.length == 64 23 | } 24 | 25 | const onValidInput = (guess) => { 26 | nbTry++ 27 | validInput() 28 | addTry(guess) 29 | isGameOver(guess) 30 | } 31 | 32 | const validInput = () => { 33 | const inputField = document.getElementById("input") 34 | inputField.style.borderColor = null 35 | 36 | const errMsg = document.getElementById("err") 37 | errMsg.innerHTML = "" 38 | } 39 | 40 | const onInvalidInput = (_) => { 41 | unvalidInput() 42 | } 43 | 44 | const unvalidInput = () => { 45 | const inputField = document.getElementById("input") 46 | inputField.style.borderColor = "red" 47 | 48 | const errMsg = document.getElementById("err") 49 | errMsg.innerHTML = "The sha256 must be 64 caracters long" 50 | } 51 | -------------------------------------------------------------------------------- /components/gameover.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const isGameOver = (guess) => { 4 | if (guess != secretSha256) { 5 | return 6 | } 7 | 8 | const app = document.getElementById("app") 9 | 10 | app.insertBefore(document.createElement("br"), app.firstChild) 11 | app.insertBefore(document.createElement("br"), app.firstChild) 12 | 13 | // Add reset button 14 | const reset = document.createElement("button") 15 | reset.innerHTML = "Reset" 16 | reset.addEventListener("click", () => { 17 | window.location.reload() 18 | }) 19 | app.insertBefore(reset, app.firstChild) 20 | 21 | app.insertBefore(document.createElement("br"), app.firstChild) 22 | 23 | // Add score 24 | const score = document.createElement("div") 25 | score.innerHTML = `You guessed the sha256 in ${nbTry} tries.` 26 | app.insertBefore(score, app.firstChild) 27 | 28 | app.insertBefore(document.createElement("br"), app.firstChild) 29 | 30 | // Add congratulation text 31 | const congrats = document.createElement("div") 32 | congrats.innerHTML = "🥳 You guess the sha256 ! Congratulation ! 🎉" 33 | congrats.style.fontSize = "2em" 34 | app.insertBefore(congrats, app.firstChild) 35 | 36 | // Disable the check button and input field 37 | const checkButton = document.getElementById("checkButton") 38 | checkButton.disabled = true 39 | const inputField = document.getElementById("input") 40 | inputField.disabled = true 41 | } 42 | -------------------------------------------------------------------------------- /components/tries.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const addTry = (guess) => { 4 | const colors = compare(guess, secretSha256) 5 | 6 | const line = document.createElement("div") 7 | for (let i = 0; i < secretSha256.length; i++) { 8 | const characterBox = document.createElement("span") 9 | characterBox.innerHTML = guess[i] 10 | characterBox.style.fontFamily = "monospace" 11 | characterBox.style.backgroundColor = colors[i] 12 | characterBox.style.margin = "0.1em" 13 | 14 | line.appendChild(characterBox) 15 | } 16 | const triesNode = document.getElementById("triesNode") 17 | triesNode.appendChild(line) 18 | } 19 | 20 | const addTriesNode = (parentNode) => { 21 | // Tries 22 | const tries = document.createElement("div") 23 | tries.id = "triesNode" 24 | parentNode.appendChild(tries) 25 | return tries 26 | } 27 | 28 | const compare = (guess, secret) => { 29 | let colors = [] // Return background color or characters (gray → not present, yellow → wrong place, green → good place) 30 | 31 | for (let i = 0; i < secret.length; i++){ 32 | const guessCharacter = guess[i] 33 | const secretCharacter = secret[i] 34 | 35 | // The character is at the good place 36 | if (guessCharacter === secretCharacter) { 37 | colors.push("limegreen") 38 | continue 39 | } else if ( 40 | countOccurence(guess.substr(0, i), guessCharacter) < 41 | ( 42 | countOccurence(secret, guessCharacter) - 43 | countOccurenceGoodPlaceAfter( 44 | i + 1, 45 | guess, 46 | secret, 47 | guessCharacter, 48 | ) 49 | ) 50 | ) { 51 | colors.push("yellow") 52 | } else { 53 | colors.push("lightgray") 54 | } 55 | 56 | } 57 | 58 | return colors 59 | } 60 | --------------------------------------------------------------------------------