// Hangman code ported from The Coding Artist
// https://codingartistweb.com/2022/05/hangman-game-with-javascript/
// Features added: User Chat using websockets, users guess the same word,
// if one user wins, the other users lose.
// Whoever guesses the word first is the winner.
document.write ( `
<h1> </h1>
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hangman</title>
</head>
<body>
<div class="container">
<div id="categoryContainer"></div>
<div id="charContainer" class="charContainer hide"></div>
<div id="userInput"></div>
<canvas id="canvas"></canvas>
<div id="newGameContainer" class="newGame hide">
<div id="resultMsg"></div>
<button id="newGameButton">New Game</button>
</div>
</div>
</body>
</html>
<div style="width:20vw; margin-top:20px; background-color:white; border: 1px solid black; margin:20; padding: 20px;">
<h3> Send A Message! </h3>
<INPUT style="width:10vw;" id=me >
<button onclick="sendchat();" class=ab-normbutton > Send </button>
</div>
<div style="width:20vw; background-color:#ffffcc; border: 1px solid black; margin:20; padding: 20px;">
<h3> Chat </h3>
<div id=txt style="max-height: 150px; overflow-y: scroll;"> </div>
</div>
<div style="width:20vw; background-color:#ffffcc; border: 1px solid black; margin:20; padding: 20px;">
<h3> Users </h3>
<div id=userlist style="display: block;/* or inline-block */
text-overflow: ellipsis;
word-wrap: break-word;
overflow: hidden;
max-height: 3.6em;
line-height: 1.8em;" id=userlist > </div>
</div>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: "Verdana", sans-serif;
}
.container {
font-size: 20px;
width: 90vw;
max-width: 34em;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
padding: 3em;
border-radius: 0.6em;
box-shadow: 0 1.2em 2.4em rgba(111, 85, 0, 0.25);
}
#categoryContainer {
text-align: center;
}
#categoryContainer div {
width: 100%;
display: flex;
justify-content: space-between;
margin: 1.2em 0 2.4em 0;
}
#categoryContainer button {
padding: 0.6em 1.2em;
border: 3px solid #000000;
background-color: #ffffff;
color: #000000;
border-radius: 0.3em;
text-transform: capitalize;
}
#categoryContainer button:disabled {
border: 3px solid #f52027;
color: #171616;
background-color: #ff5257;
}
#categoryContainer button.active {
background-color: #49f566;
border: 3px solid #00ff2a;
color: #000000;
}
.charContainer {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.6em;
}
#charContainer button {
height: 2.4em;
width: 2.4em;
border-radius: 0.3em;
background-color: #ffffff;
}
.newGame {
background-color: #ffffff;
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
border-radius: 0.6em;
}
h1 {
text-align: center;
padding-top: 200px
}
#userInput {
display: flex;
justify-content: center;
font-size: 1.8em;
margin: 0.6em 0 1.2em 0;
}
canvas {
display: block;
margin: auto;
border: 1px solid #000000;
}
.hide {
display: none;
}
#resultMsg h2 {
font-size: 1.8em;
text-align: center;
}
#resultMsg p {
font-size: 1.25em;
margin: 1em 0 2em 0;
}
#resultMsg span {
font-weight: 600;
}
#newGameButton {
font-size: 1.25em;
padding: 0.5em 1em;
background-color: #f4c531;
border: 3px solid #000000;
color: #000000;
border-radius: 0.2em;
}
.winMsg {
color: #39d78d;
}
.lossMsg {
color: #fe5152;
}
</style>
` );
//Chat
AB.socketStart();
document.getElementById('me').onkeydown = function(event) { if (event.keyCode == 13) sendchat(); };
function sendchat()
{
var txtInput = $("#me").val();
document.getElementById("me").value = ""
if (txtInput === "")
{
document.getElementById("me").value = ""
}
else
{
$("#txt").append ('<p>' + AB.myusername + ": " + txtInput + '\n' + '<p>');
var elem = document.getElementById('txt');elem.scrollTop = elem.scrollHeight;
var data =
{
userid: AB.myuserid,
username: AB.myusername,
txt: txtInput
};
AB.socketOut ( data );
}
}
function userlink ( userid, username )
{
if ( userid == "none" ) return ( "Anonymous" );
else return ( "<a target='_blank' href='https://ancientbrain.com/user.php?userid=" + userid + "'>" + username + "</a>" );
}
AB.socketIn = function(data)
{
var word = $(data).attr('word');
var userid = $(data).attr('userid');
var win = $(data).attr('win');
var loss = $(data).attr('loss');
console.log(data);
if (typeof userid !== 'undefined' && userid !== false)
{
var userhtml = userlink ( data.userid, data.username );
$("#txt").append ('<p>' + userhtml + ": " + data.txt + '\n' + '<p>');
}
else if (typeof loss !== 'undefined' && loss !== false)
{
console.log("You win against: " + data.username);
resultMsg.innerHTML = `<h2 class='win-msg'>You Win!!</h2><p>The word was <span>${word}</span></p>`;
//block all buttons
blocker();
}
else if (typeof win !== 'undefined' && win !== false)
{
console.log("You lost against: " + data.username);
resultMsg.innerHTML = `<h2 class='lose-msg'>You Lose!!</h2><p>The word was <span>${word}</span></p>`;
blocker();
}
else if (typeof word !== 'undefined' && word !== false)
{
var chosenWord = word;
let categoryButtons = document.querySelectorAll(".category");
//If categoryValue matches the button innerText then highlight the button
categoryButtons.forEach((button) => {
if (button.innerText.toLowerCase() === data.categoryValue) {
button.classList.add("active");
}
button.disabled = true;
});
//initially hide letters, clear previous word
charContainer.classList.remove("hide");
userInput.innerText = "";
//replace every letter with span containing dash
let displayHiddenWord = chosenWord.replace(/./g, '<span class="dashes">_</span>');
//Display each element as span
userInput.innerHTML = displayHiddenWord;
getWord(chosenWord);
data = null;
}
};
AB.socketUserlist = function ( a )
{
console.log ( "Got user list: " );
console.log ( JSON.stringify ( a, null, ' ' ) );
if ( a.length < 2 )
{
$("#userlist").html ( "<h3 style='color:red'> 0 users. </h3>");
return;
}
// else
var str = " <ol> ";
for (var i = 0; i < a.length; i++)
{
var userhtml = userlink ( a[i][0], a[i][1] );
str = str + " <li> " + userhtml ;
}
$("#userlist").html ( str + " </ol> " );
};
//Initial References
const charContainer = document.getElementById("charContainer");
const categoryContainer = document.getElementById("categoryContainer");
const userInput = document.getElementById("userInput");
const newGameContainer = document.getElementById("newGameContainer");
const newGameButton = document.getElementById("newGameButton");
const canvas = document.getElementById("canvas");
const resultMsg = document.getElementById("resultMsg");
//Options values for buttons
let category = {
food: [
"Pizza",
"Spaghetti",
"Sandwich",
"Cheeseburger",
"Doughnut",
"Steak",
"Hamburger",
"Noodles",
"Biscuit",
"Cheese",
"Chips",
"Soup",
"Sausage",
],
languages: [
"Irish",
"French",
"Japanese",
"Croatian",
"Lithuanian",
"Spanish",
"German",
"Portuguese",
"Latvian",
"Filipino",
"Russian",
"English",
"Polish",
],
sports: [
"Football",
"Basketball",
"Baseball",
"Archery",
"Badminton",
"Tennis",
"Volleyball",
"Swimming",
"Handball",
],
insects: [
"Spider",
"Beetle",
"Mantis",
"Stinkbug",
"Ladybird",
"Cockroach",
"Mosquito",
"Tarantula",
"Wasp",
"Butterfly",
"Dragonfly"
]
};
//count
let winCount = 0;
let count = 0;
let chosenWord = "";
let categoryValue = null;
function getCategoryValue(value)
{
categoryValue = value;
}
function getWord(word="")
{
if (word === "")
{
chosenWord = "";
}
else
{
chosenWord = word;
}
}
//Display Category buttons
const displayCategories = () => {
categoryContainer.innerHTML = "<h3>Please Select A Category</h3>";
let buttonCon = document.createElement("div");
for (let value in category) {
buttonCon.innerHTML += `<button class="category" onclick="generateWord('${value}')">${value}</button>`;
}
categoryContainer.appendChild(buttonCon);
};
//Block all the Buttons
const blocker = () => {
let categoryButtons = document.querySelectorAll(".category");
let charButtons = document.querySelectorAll(".char");
//disable all options
categoryButtons.forEach((button) => {
button.disabled = true;
});
//disable all letters
charButtons.forEach((button) => {
button.disabled.true;
});
newGameContainer.classList.remove("hide");
};
//Word Generator
const generateWord = (categoryValue) => {
let categoryButtons = document.querySelectorAll(".category");
//If optionValur matches the button innerText then highlight the button
categoryButtons.forEach((button) => {
if (button.innerText.toLowerCase() === categoryValue) {
button.classList.add("active");
}
button.disabled = true;
});
//initially hide letters, clear previous word
charContainer.classList.remove("hide");
userInput.innerText = "";
let categoryArray = category[categoryValue];
//choose random word
if (chosenWord === "")
{
chosenWord = categoryArray[Math.floor(Math.random() * categoryArray.length)];
}
chosenWord = chosenWord.toUpperCase();
//replace every letter with span containing dash
let displayHiddenWord = chosenWord.replace(/./g, '<span class="dashes">_</span>');
//Display each element as span
userInput.innerHTML = displayHiddenWord;
var data =
{
word: chosenWord,
categoryValue: categoryValue
};
AB.socketOut ( data ); // server gets this, and sends the data to all clients running this World
};
//Initial Function (Called when page loads/user presses new game)
const initializer = () => {
winCount = 0;
count = 0;
//Initially erase all content and hide letteres and new game button
userInput.innerHTML = "";
categoryContainer.innerHTML = "";
charContainer.classList.add("hide");
newGameContainer.classList.add("hide");
charContainer.innerHTML = "";
//For creating letter buttons
for (let i = 65; i < 91; i++) {
let button = document.createElement("button");
button.classList.add("char");
//Number to ASCII[A-Z]
button.innerText = String.fromCharCode(i);
//character button click
button.addEventListener("click", () => {
let charArray = chosenWord.split("");
let dashes = document.getElementsByClassName("dashes");
//if array contains clciked value replace the matched dash with letter else dram on canvas
if (charArray.includes(button.innerText)) {
charArray.forEach((char, index) => {
//if character in array is same as clicked button
if (char === button.innerText) {
//replace dash with letter
dashes[index].innerText = char;
//increment counter
winCount += 1;
//if winCount equals word lenfth
if (winCount == charArray.length) {
var data =
{
username: AB.myusername,
word: chosenWord,
win: "won"
};
AB.socketOut ( data ); // server gets this, and sends the data to all clients running this World
resultMsg.innerHTML = `<h2 class='win-msg'>You Win!!</h2><p>The word was <span>${chosenWord}</span></p>`;
//block all buttons
blocker();
}
}
});
} else {
//lose count
count += 1;
//for drawing man
drawMan(count);
//Count==6 because head,body,left arm, right arm,left leg,right leg
if (count == 6) {
var data =
{
username: AB.myusername,
word: chosenWord,
loss: "lost"
};
AB.socketOut ( data ); // server gets this, and sends the data to all clients running this World
resultMsg.innerHTML = `<h2 class='lose-msg'>You Lose!!</h2><p>The word was <span>${chosenWord}</span></p>`;
blocker();
}
}
//disable clicked button
button.disabled = true;
});
charContainer.append(button);
chosenWord = "";
}
displayCategories();
//Call to canvasCreator (for clearing previous canvas and creating initial canvas)
let { initialDrawing } = canvasCreator();
//initialDrawing would draw the frame
initialDrawing();
};
//Canvas
const canvasCreator = () => {
let context = canvas.getContext("2d");
context.beginPath();
context.strokeStyle = "#000";
context.lineWidth = 2;
//For drawing lines
const drawLine = (fromX, fromY, toX, toY) => {
context.moveTo(fromX, fromY);
context.lineTo(toX, toY);
context.stroke();
};
const head = () => {
context.beginPath();
context.arc(70, 30, 10, 0, Math.PI * 2, true);
context.stroke();
};
const body = () => {
drawLine(70, 40, 70, 80);
};
const leftArm = () => {
drawLine(70, 50, 50, 70);
};
const rightArm = () => {
drawLine(70, 50, 90, 70);
};
const leftLeg = () => {
drawLine(70, 80, 50, 110);
};
const rightLeg = () => {
drawLine(70, 80, 90, 110);
};
//initial frame
const initialDrawing = () => {
//clear canvas
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
//bottom line
drawLine(10, 130, 130, 130);
//left line
drawLine(10, 10, 10, 131);
//top line
drawLine(10, 10, 70, 10);
//small top line
drawLine(70, 10, 70, 20);
};
return { initialDrawing, head, body, leftArm, rightArm, leftLeg, rightLeg };
};
//draw the man
const drawMan = (count) => {
let { head, body, leftArm, rightArm, leftLeg, rightLeg } = canvasCreator();
switch (count) {
case 1:
head();
break;
case 2:
body();
break;
case 3:
leftArm();
break;
case 4:
rightArm();
break;
case 5:
leftLeg();
break;
case 6:
rightLeg();
break;
default:
break;
}
};
//New Game
newGameButton.addEventListener("click", initializer);
window.onload = initializer;