This is an attempt to build the Hangman game in plain JavaScript.
Note: These experiments are part of the CodingSummer21 activities and their goal is not to provide a perfect solution, but some solution simple to understand so that students can fork and improve.
GitHub: github.com/codingsummer21/hangman-js
The hangman.html
file has only a button to start the game, a div for the canvas, and three empty divs to display the word, buttons with the letters, and the result of the game.
Start Game
Upon clicking the start button the getRandomWord function is called.
function getRandomWord() {
fetch('https://random-word-api.herokuapp.com/word?number=1')
.then(response => response.json())
.then(data => {
word = data[0]
gameOver = false
wrong = []
correct = []
console.log(word)
displaySecret()
displayLetters()
document.getElementById("result").innerHTML = ' '
drawCanvas()
})
}
This function fetches a random word from an API and then initialises the game state:
word: this variable will hold the retrieved word
wrong: an empty array to hold selected letters not in the word
correct: an empty array to hold selected letters in the word
Finally, the function displays the secret word, displays buttons with the letters where user can click to select a letter, cleans up the game result message, and redraws the hangman on the canvas.
Function displaySecret
displays the secret word by showing the first and last letter, any letters that are already guessed by the user, and the rest of the letters as underscores.
function displaySecret() {
let secret = word.charAt(0)
for(let i = 1; i < word.length - 1; i++) {
if(correct.includes(word.charAt(i))) {
secret += ' ' + word.charAt(i) + ' '
}
else {
secret += ' _ '
}
}
secret += word.charAt(word.length-1)
document.getElementById("secret-word").innerHTML = '<h1>' + secret + '</h1>'
}
Function displayLetters
displays buttons for each letter. Letters already selected by the user are disabled. On game over all buttons are disabled. Upon clicking on a button, the playLetter function is called, passing the ASCII value of the selected character.
function displayLetters() {
let alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
let letters = ''
let counter = 0
alphabet.forEach(ch => {
let disabled = ''
counter++
if(gameOver || wrong.includes(ch) || correct.includes(ch)) {
disabled = 'disabled'
}
letters += "<button onclick='playLetter(" + ch.charCodeAt(0) + ")' "
+ disabled + ">" + ch + "</button> "
if(counter % 9 === 0) {
letters += "<br/>"
}
})
document.getElementById("letters").innerHTML = letters
}
This function uses a counter in order to avoid a long line of letters and adds a break for every group of nine (9) letters.
Play
Function playLetter
receives the ASCII value of the selected character (as a function parameter) and convert it to string. Then checks if the character is correct or wrong and pushes it in the respective array. Finally, it calls checkGameState function to check if the user solved the puzzle or reached the maximum efforts allowed.
function playLetter(ch) {
let letter = String.fromCharCode(ch)
if(word.includes(letter)) {
correct.push(letter)
}
else {
wrong.push(letter)
}
checkGameState()
displaySecret()
displayLetters()
}
Function checkGameState
first checks if the number of wrong letters is the maximum allowed (user loses), else checks if all secret word letters are in the correct array (user wins), and if one of the previous conditions applies, displays a respective message.
function checkGameState() {
if(wrong.length >= 6) {
gameOver = true
document.getElementById("result").innerHTML = "<h2>You Lost ...</h2>"
}
else {
for(let i = 1; i < word.length - 1; i++) {
let ch = word.charAt(i)
if(!correct.includes(ch)) {
return
}
}
gameOver = true
document.getElementById("result").innerHTML = "<h2>You Win!</h2>"
}
}
Last, function drawCanvas
draws the hangman on a canvas element. In the following code fragment drawing statements have been removed in order to keep it short. They can be found on GitHub.
function drawCanvas() {
let canvas = document.querySelector('canvas');
let context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
// Hangman base
let wrongLetters = wrong.length;
switch (wrongLetters) {
case 6:
// Left leg
case 5:
// Right leg
case 4:
// Left hand
case 3:
// Right hand
case 2:
// Body
case 1:
// Head
}
}
Notice that the switch cases have no break statement, so that all body elements will be drawn depending on the number of wrong letters.
Use this project
Clone or Fork/Clone the GitHub repository.
You may just double click on the html file and run in your browser. A better approach is to clone the project in your htdocs folder (if you are using a local web server) and run using localhost and the path to the project.
Ideas for improvement:
- Style the app with CSS
- Sometimes the random word is short. Repeat fetching until the word has more than a desired number of characters (for example, at least 6 characters)
- Add the ability to play using the keyboard instead of using the letter buttons
- Draw a better version of the hangman
- At the end of the game display the correct word and its definition (using an appropriate API)
- The API used to retrieve random words is free and cannot afford too many requests. Disable the Play button until the end of the game in order to limit the continuous usage.