├── 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 |
--------------------------------------------------------------------------------