const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
HINT_BUTTON.style.visibility = "hidden";
hintUsed = true;
const VIRTUAL_KEYS_CHILDREN = Array.from(
VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHILDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = blanks.join("");
const SECRET_WORD_STRING = secretWordArray.join("");
BLANKS_TAG.textContent = secretWord;
HINT_BUTTON.style.visibility = "hidden";
NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRINGsecretWord) BLANKS_TAG.classList.add("correct");
else BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
NEW_WORD_BUTTON.style.visibility = "hidden";
BLANKS_TAG.classList.remove("correct", "incorrect");
VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
secretWord = words[Math.floor(Math.random() * words.length)];
trials = MAX_TRIALS;
blanks = Array(secretWord.length).fill("_");
secretWordArray = secretWord.split("");
hintUsed = false;
BLANKS_TAG.textContent = blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (trials === 0 || !blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
words = data.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === VIRTUAL_KEYS) {
play(event);
} else if (TARGET === HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 15px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p id="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
HINT_BUTTON.style.visibility = "hidden";
hintUsed = true;
const VIRTUAL_KEYS_CHILDREN = Array.from(
VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHILDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = blanks.join("");
const SECRET_WORD_STRING = secretWordArray.join("");
BLANKS_TAG.textContent = secretWord;
HINT_BUTTON.style.visibility = "hidden";
NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
else BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
NEW_WORD_BUTTON.style.visibility = "hidden";
BLANKS_TAG.classList.remove("correct", "incorrect");
VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
secretWord = words[Math.floor(Math.random() * words.length)];
trials = MAX_TRIALS;
blanks = Array(secretWord.length).fill("_");
secretWordArray = secretWord.split("");
hintUsed = false;
BLANKS_TAG.textContent = blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (trials === 0 || !blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
words = data.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === VIRTUAL_KEYS) {
play(event);
} else if (TARGET === HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 15px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p id="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
HINT_BUTTON.style.visibility = "hidden";
hintUsed = true;
const VIRTUAL_KEYS_CHILDREN = Array.from(
VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHILDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = blanks.join("");
BLANKS_TAG.textContent = secretWord;
NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === secretWord) BLANKS_TAG.classList.add("correct");
else BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
NEW_WORD_BUTTON.style.visibility = "hidden";
BLANKS_TAG.classList.remove("correct", "incorrect");
VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
secretWord = words[Math.floor(Math.random() * words.length)];
trials = MAX_TRIALS;
blanks = Array(secretWord.length).fill("_");
secretWordArray = secretWord.split("");
hintUsed = false;
BLANKS_TAG.textContent = blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (trials === 0 || !blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
words = data.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === VIRTUAL_KEYS) {
play(event);
} else if (TARGET === HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 15px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p id="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;words;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
HINT_BUTTON.style.visibility = "hidden";
hintUsed = true;
const VIRTUAL_KEYS_CHIDRENVIRTUAL_KEYS_CHILDREN = Array.from(
VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHIDRENVIRTUAL_KEYS_CHILDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = blanks.join("");
const SECRET_WORD_STRING = secretWordArray.join("");
BLANKS_TAG.textContent = secretWord;
HINT_BUTTON.style.visibility = "hidden";
NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
else BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
NEW_WORD_BUTTON.style.visibility = "hidden";
BLANKS_TAG.classList.remove("correct", "incorrect");
VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
secretWord = wordsArray[Mathwords[Math.floor(Math.random() * wordsArraywords.length)];
trials = MAX_TRIALS;
blanks = Array(secretWord.length).fill("_");
secretWordArray = secretWord.split("");
hintUsed = false;
BLANKS_TAG.textContent = blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (trials === 0 || !blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
wordsArraywords = data.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === VIRTUAL_KEYS) {
play(event);
} else if (TARGET === HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 15px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p id="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
HINT_BUTTON.style.visibility = "hidden";
hintUsed = true;
const VIRTUAL_KEYS_CHIDREN = Array.from(
VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHIDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = blanks.join("");
const SECRET_WORD_STRING = secretWordArray.join("");
BLANKS_TAG.textContent = secretWord;
HINT_BUTTON.style.visibility = "hidden";
NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
else BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
NEW_WORD_BUTTON.style.visibility = "hidden";
BLANKS_TAG.classList.remove("correct", "incorrect");
VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
secretWord = wordsArray[Math.floor(Math.random() * wordsArray.length)];
trials = MAX_TRIALS;
blanks = Array(secretWord.length).fill("_");
secretWordArray = secretWord.split("");
hintUsed = false;
BLANKS_TAG.textContent = blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (trials === 0 || !blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
wordsArray = data.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === VIRTUAL_KEYS) {
play(event);
} else if (TARGET === HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 15px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p id="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let words;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
HINT_BUTTON.style.visibility = "hidden";
hintUsed = true;
const VIRTUAL_KEYS_CHILDREN = Array.from(
VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHILDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHILDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = blanks.join("");
const SECRET_WORD_STRING = secretWordArray.join("");
BLANKS_TAG.textContent = secretWord;
HINT_BUTTON.style.visibility = "hidden";
NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
else BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
NEW_WORD_BUTTON.style.visibility = "hidden";
BLANKS_TAG.classList.remove("correct", "incorrect");
VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
secretWord = words[Math.floor(Math.random() * words.length)];
trials = MAX_TRIALS;
blanks = Array(secretWord.length).fill("_");
secretWordArray = secretWord.split("");
hintUsed = false;
BLANKS_TAG.textContent = blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (trials === 0 || !blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
words = data.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === VIRTUAL_KEYS) {
play(event);
} else if (TARGET === HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 15px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p id="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const gameDataBLANKS_TAG = {
secretWord: "",
MAX_TRIALS: 6,
trials: 0,
blanks: [],
secretWordArray: [],
hintUsed: false,
words: "",
WORDS_ARRAY: [],
BLANKS_TAG: document.querySelectorgetElementById(".current"current-word"),;
const VIRTUAL_KEYS: = document.querySelectorgetElementById(".virtual"virtual-keys"),;
const HINT_BUTTON: = document.querySelectorgetElementById(".hint"hint-button"),;
const NEW_WORD_BUTTON: = document.querySelectorgetElementById(".new"new-word-button"),;
const HANGMAN_IMAGE: = document.querySelectorgetElementById("img""hangman-image"),
};
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
gameData.HANGMAN_IMAGE.src = `img/${
gameData.MAX_TRIALS - gameData.trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = gameData.BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of gameData.secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
gameData.blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = gameData.blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
gameData.BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
gameData.trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
gameData.HINT_BUTTON.style.visibility = "hidden";
gameData.hintUsed = true;
const VIRTUAL_KEYS_CHIDREN = Array.from(
gameData.VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHIDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !gameData.secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = gameData.blanks.join("");
const SECRET_WORD_STRING = gameData.secretWordArray.join("");
gameData.BLANKS_TAG.textContent = gameData.secretWord;
gameData.HINT_BUTTON.style.visibility = "hidden";
gameData.NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRING) gameData.BLANKS_TAG.classList.add("correct");
else gameData.BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
gameData.NEW_WORD_BUTTON.style.visibility = "hidden";
gameData.BLANKS_TAG.classList.remove("correct", "incorrect");
gameData.VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
gameData.secretWord = gameData.WORDS_ARRAY[
MathwordsArray[Math.floor(Math.random() * gameData.WORDS_ARRAYwordsArray.length)];
gameData.trials = gameData.MAX_TRIALS;
gameData.blanks = Array(gameData.secretWord.length).fill("_");
gameData.secretWordArray = gameData.secretWord.split("");
gameData.hintUsed = false;
gameData.BLANKS_TAG.textContent = gameData.blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (gameData.trials === 0 || !gameData.blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (gameData.trials === 1 && !gameData.hintUsed) gameData.HINT_BUTTON.style.visibility = "visible";
if (!gameData.trials || !gameData.blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
gameData.words = data;
gameData.WORDS_ARRAYwordsArray = gameData.wordsdata.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === gameData.NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === gameData.VIRTUAL_KEYS) {
play(event);
} else if (TARGET === gameData.HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 015px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p class="currentid="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const gameData = {
secretWord: "",
MAX_TRIALS: 6,
trials: 0,
blanks: [],
secretWordArray: [],
hintUsed: false,
words: "",
WORDS_ARRAY: [],
BLANKS_TAG: document.querySelector(".current-word"),
VIRTUAL_KEYS: document.querySelector(".virtual-keys"),
HINT_BUTTON: document.querySelector(".hint-button"),
NEW_WORD_BUTTON: document.querySelector(".new-word-button"),
HANGMAN_IMAGE: document.querySelector("img"),
};
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
gameData.HANGMAN_IMAGE.src = `img/${
gameData.MAX_TRIALS - gameData.trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = gameData.BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of gameData.secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
gameData.blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = gameData.blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
gameData.BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
gameData.trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
gameData.HINT_BUTTON.style.visibility = "hidden";
gameData.hintUsed = true;
const VIRTUAL_KEYS_CHIDREN = Array.from(
gameData.VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHIDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !gameData.secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = gameData.blanks.join("");
const SECRET_WORD_STRING = gameData.secretWordArray.join("");
gameData.BLANKS_TAG.textContent = gameData.secretWord;
gameData.HINT_BUTTON.style.visibility = "hidden";
gameData.NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRING) gameData.BLANKS_TAG.classList.add("correct");
else gameData.BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
gameData.NEW_WORD_BUTTON.style.visibility = "hidden";
gameData.BLANKS_TAG.classList.remove("correct", "incorrect");
gameData.VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
gameData.secretWord = gameData.WORDS_ARRAY[
Math.floor(Math.random() * gameData.WORDS_ARRAY.length)];
gameData.trials = gameData.MAX_TRIALS;
gameData.blanks = Array(gameData.secretWord.length).fill("_");
gameData.secretWordArray = gameData.secretWord.split("");
gameData.hintUsed = false;
gameData.BLANKS_TAG.textContent = gameData.blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (gameData.trials === 0 || !gameData.blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (gameData.trials === 1 && !gameData.hintUsed) gameData.HINT_BUTTON.style.visibility = "visible";
if (!gameData.trials || !gameData.blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
gameData.words = data;
gameData.WORDS_ARRAY = gameData.words.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === gameData.NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === gameData.VIRTUAL_KEYS) {
play(event);
} else if (TARGET === gameData.HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 0 auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button">Hint?</button>
<img/>
<button type="button" class="btn new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p class="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
const BLANKS_TAG = document.getElementById("current-word");
const VIRTUAL_KEYS = document.getElementById("virtual-keys");
const HINT_BUTTON = document.getElementById("hint-button");
const NEW_WORD_BUTTON = document.getElementById("new-word-button");
const HANGMAN_IMAGE = document.getElementById("hangman-image");
const MAX_TRIALS = 6;
let trials = MAX_TRIALS;
let hintUsed;
let blanks;
let wordsArray;
let secretWord;
let secretWordArray;
/**
* Updates the hangman image based on the number of attempts remaining.
*/
function updateHangmanImage() {
HANGMAN_IMAGE.src = `img/${MAX_TRIALS - trials}.png`;
}
/**
* Updates the blanks with the correctly guessed letter and marks the
* corresponding span element as correct.
* @param {string} LETTER - The letter to guess.
* @returns {boolean} Returns true if it's a good guess, false otherwise.
*/
function guessAndUpdateBlanks(LETTER) {
const SPANS = BLANKS_TAG.querySelectorAll("span");
let isGoodGuess = false;
let lastCorrectSpan = null;
for (const [I, CHAR] of secretWordArray.entries()) {
if (CHAR === LETTER) {
isGoodGuess = true;
blanks[I] = LETTER;
lastCorrectSpan = SPANS[I];
}
}
if (lastCorrectSpan) lastCorrectSpan.classList.remove("correct");
if (isGoodGuess) return true;
else return false;
}
/**
* Replaces the guessed letters in the string of blanks and updates the hangman image if the guess is incorrect.
* @param {boolean} IS_GOOD_GUESS - Indicates if the guess is correct.
* @param {string} LETTER - The guessed letter.
*/
function replaceGuessedLetters(IS_GOOD_GUESS, LETTER) {
if (IS_GOOD_GUESS) {
const BLANKS_STRING = blanks.join(" ");
const UPDATED_BLANKS_STRING = BLANKS_STRING.replace(
new RegExp(LETTER, "gi"),
`<span class="correct">${LETTER}</span>`
);
BLANKS_TAG.innerHTML = UPDATED_BLANKS_STRING;
} else {
trials--;
updateHangmanImage();
}
}
/**
* Provides a hint by disabling a number of letters that are not in the secret word.
*/
function hint() {
HINT_BUTTON.style.visibility = "hidden";
hintUsed = true;
const VIRTUAL_KEYS_CHIDREN = Array.from(
VIRTUAL_KEYS.querySelectorAll(".btn:not(.disabled)")
);
const MAX_LETTERS_TO_SHOW = Math.floor(Math.random() * 6) + 1;
const INDEXES = [];
while (INDEXES.length < MAX_LETTERS_TO_SHOW) {
const RANDOM_INDEX = Math.floor(
Math.random() * VIRTUAL_KEYS_CHIDREN.length
);
const BUTTON = VIRTUAL_KEYS_CHIDREN[RANDOM_INDEX];
const LETTER = BUTTON.getAttribute("data-value");
if (!INDEXES.includes(RANDOM_INDEX) && !secretWordArray.includes(LETTER)) {
BUTTON.classList.add("disabled");
INDEXES.push(RANDOM_INDEX);
}
}
}
/**
* Checks the game result and displays the secret word accordingly.
*/
function checkGameResult() {
const BLANKS_STRING = blanks.join("");
const SECRET_WORD_STRING = secretWordArray.join("");
BLANKS_TAG.textContent = secretWord;
HINT_BUTTON.style.visibility = "hidden";
NEW_WORD_BUTTON.style.visibility = "visible";
if (BLANKS_STRING === SECRET_WORD_STRING) BLANKS_TAG.classList.add("correct");
else BLANKS_TAG.classList.add("incorrect");
}
/**
* Initialize the game and choose a secret word at random.
*/
function initializeGame() {
NEW_WORD_BUTTON.style.visibility = "hidden";
BLANKS_TAG.classList.remove("correct", "incorrect");
VIRTUAL_KEYS.querySelectorAll(".btn").forEach((BUTTON) => {
BUTTON.classList.remove("disabled");
});
secretWord = wordsArray[Math.floor(Math.random() * wordsArray.length)];
trials = MAX_TRIALS;
blanks = Array(secretWord.length).fill("_");
secretWordArray = secretWord.split("");
hintUsed = false;
BLANKS_TAG.textContent = blanks.join(" ");
updateHangmanImage();
}
/**
* Manages the game event when a virtual key is clicked.
* @param {Event} event - The click event object.
*/
function play(event) {
if (trials === 0 || !blanks.includes("_")) return;
const BUTTON = event.target;
const LETTER = BUTTON.getAttribute("data-value");
const IS_GOOD_GUESS = guessAndUpdateBlanks(LETTER);
replaceGuessedLetters(IS_GOOD_GUESS, LETTER);
BUTTON.classList.add("disabled");
if (trials === 1 && !hintUsed) HINT_BUTTON.style.visibility = "visible";
if (!trials || !blanks.includes("_")) checkGameResult();
}
fetch("./words.txt")
.then((response) => response.text())
.then((data) => {
wordsArray = data.split(" ");
initializeGame();
})
.catch((error) => {
console.error(`Error reading word file: ${error}`);
});
document.addEventListener("click", (event) => {
const TARGET = event.target;
if (TARGET === NEW_WORD_BUTTON) {
initializeGame();
} else if (TARGET.parentNode === VIRTUAL_KEYS) {
play(event);
} else if (TARGET === HINT_BUTTON) {
hint();
}
});
body {
max-width: 100vw;
max-height: 100vh;
}
header {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
text-align: center;
}
.game-stage {
min-height: 350px;
max-height: 350px;
display: flex;
align-items: center;
justify-content: center;
}
.game-current-word {
min-height: 50px;
max-height: 50px;
display: flex;
justify-content: center;
font-size: 22px;
font-weight: bold;
}
.virtual-keyboard {
min-height: 80px;
max-height: 80px;
max-width: 100vw;
}
.virtual-keys {
max-width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 15px auto;
}
.btn {
background: none;
border: 0;
background-color: #0066cc;
box-shadow: 2px 2px 1px #036;
color: #f3ffff;
cursor: pointer;
font: inherit;
font-weight: bold;
line-height: normal;
overflow: visible;
width: 35px;
height: 35px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 0 10px;
margin: 5px;
}
.hint-button,
.new-word-button {
width: 40px;
font-size: 12px;
visibility: hidden;
}
.disabled {
background-color: #fff;
color: #000;
pointer-events: none;
cursor: not-allowed;
}
.correct {
color: green;
font-size: 25px;
}
.incorrect {
color: red;
font-size: 25px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hangman game</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="assets/normalize.css">
<link rel="stylesheet" type="text/css" href="assets/style.css" />
<script src="assets/index.js" defer></script>
</head>
<body>
<header><h1>Hangman game</h1></header>
<section class="game-stage">
<button type="button" class="btn hint-button" id="hint-button">Hint?</button>
<img id="hangman-image"/>
<button type="button" class="btn new-word-button" id="new-word-button">New Word</button>
</section>
<section class="game-current-word">
<p id="current-word"></p>
</section>
<section class="virtual-keyboard">
<div class="virtual-keys" id="virtual-keys">
<button type="button" class="btn" data-value="a">A</button>
<button type="button" class="btn" data-value="b">B</button>
<button type="button" class="btn" data-value="c">C</button>
<button type="button" class="btn" data-value="d">D</button>
<button type="button" class="btn" data-value="e">E</button>
<button type="button" class="btn" data-value="f">F</button>
<button type="button" class="btn" data-value="g">G</button>
<button type="button" class="btn" data-value="h">H</button>
<button type="button" class="btn" data-value="i">I</button>
<button type="button" class="btn" data-value="j">J</button>
<button type="button" class="btn" data-value="k">K</button>
<button type="button" class="btn" data-value="l">L</button>
<button type="button" class="btn" data-value="m">M</button>
<button type="button" class="btn" data-value="n">N</button>
<button type="button" class="btn" data-value="o">O</button>
<button type="button" class="btn" data-value="p">P</button>
<button type="button" class="btn" data-value="q">Q</button>
<button type="button" class="btn" data-value="r">R</button>
<button type="button" class="btn" data-value="s">S</button>
<button type="button" class="btn" data-value="t">T</button>
<button type="button" class="btn" data-value="u">U</button>
<button type="button" class="btn" data-value="v">V</button>
<button type="button" class="btn" data-value="w">W</button>
<button type="button" class="btn" data-value="x">X</button>
<button type="button" class="btn" data-value="y">Y</button>
<button type="button" class="btn" data-value="z">Z</button>
</div>
</section>
</body>
</html>
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
lang-css