Skip to content

Instantly share code, notes, and snippets.

@emmanuellyautomated
Created December 25, 2017 19:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emmanuellyautomated/5a5bff3e712d2abca3f19f9a44f92728 to your computer and use it in GitHub Desktop.
Save emmanuellyautomated/5a5bff3e712d2abca3f19f9a44f92728 to your computer and use it in GitHub Desktop.
A reactive game of hangman
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<title>Hangman</title>
</head>
<body>
<div id="scoreboard"></div>
<div id="game"></div>
</body>
<script>
/*******
* MODEL
* -----
* Constructors of objects that hold data defining the state of the system
*/
function HangmanState(words) {
this.wins = 0;
this.losses = 0;
this.score = 0;
this.words = words.map(function(word) { return Array(word.length).fill('-'); });
this.render = function() {
document.querySelector("#scoreboard").innerHTML = hangmanTemplate(this);
};
}
function GameState(numWrongGuesses, word) {
this.wrongGuessesLeft = numWrongGuesses;
this.word = Array(word.length).fill('-');
this.wasWon = !this.word.includes('-');
this.render = function() {
document.querySelector("#game").innerHTML = gameTemplate(this);
};
}
/******
* VIEW
* ----
* Templates to display data held in models
*/
var hangmanTemplate = function(state) {
return "<table>" +
"<tr>" +
"<th>Wins</th>" +
"<th>Losses</th>" +
"<th>Score</th>" +
"</tr>" +
"<tr>" +
"<td align='center'>" + state.wins + "</td>" +
"<td align='center'>" + state.losses + "</td>" +
"<td align='center'>" + state.score + "</td>" +
"</tr>" +
"</table>";
};
var gameTemplate = function(state) {
return "<h1>Hangman</h1>" +
"<table>" +
"<tr>" +
"<th align='right'>Guesses Left</th>" +
"<td>" + state.wrongGuessesLeft + "</td>" +
"</tr>" +
"<tr>" +
"<th align='right'>Word</th>" +
"<td>" + state.word + "</td>" +
"</tr>" +
"<tr>" +
"<th align='right'>Game Won?!</th>" +
"<td>" + state.wasWon + "</td>" +
"</tr>" +
"</table>";
};
/************
* CONTROLLER
* ----------
* Constructors of objects that valdiate user input, modify models accordingly, and
* render approprate views of the data
*/
function Game(numWrongGuesses, word) {
this.state = new GameState(numWrongGuesses, word);
this.isPlaying = false;
this.wasWon = function() { return this.state.wasWon; };
this.validKeyPressed = function(key) {
choices = "abcdefghijklmnopqrstuvwxyz".split("");
return choices.includes(key);
};
this.over = function() {
noGuessesLeft = this.state.wrongGuessesLeft === 0;
guessedAll = !this.state.word.includes('-');
return noGuessesLeft || guessedAll;
};
this.play = function() {
if (!this.over()) { this.isPlaying = true; }
this.state.render();
};
this.pause = function() {
if (!this.over()) { this.isPlaying = false; }
this.state.render();
};
//-- PRIVATE ------------------------------------>>>
var that = this;
var answer = word.split("");
var revealLetters = function(userGuess) {
for (var i=0; i<answer.length; i++) {
if (userGuess == answer[i]) { that.state.word[i] = userGuess; }
}
};
var revealLettersOrNot = function(userGuess) {
if (answer.includes(userGuess)) {
revealLetters(userGuess);
} else {
that.state.wrongGuessesLeft--;
}
};
// Event Handlers
var processInput = function(event) {
userGuess = event.key;
if (that.validKeyPressed(userGuess) && that.isPlaying && !that.over()) {
revealLettersOrNot(userGuess);
that.state.render();
}
};
//----------------------------------------------->>>
document.addEventListener("keyup", processInput);
}
function Hangman(numWrongGuesses, words) {
this.state = new HangmanState(words);
this.games = Array.from(new Set(words)).map(
function(word) { return new Game(numWrongGuesses, word); }
);
this.game = this.games[0];
this.selectPreviousGame = function() {
this.game.pause();
this.game = this.games[this.games.indexOf(this.game) - 1] || this.games[this.games.length - 1];
this.game.play();
};
this.selectNextGame = function() {
this.game.pause();
this.game = this.games[this.games.indexOf(this.game) + 1] || this.games[0];
this.game.play();
};
this.countWins = function() {
gamesWonOrNot = this.games.map(function(game) { return game.wasWon && game.over(); });
return gamesWonOrNot.reduce(function(a, b){ return b ? a + 1 : a; }, 0);
};
this.countLosses = function() {
gamesLostOrNot = this.games.map(function(game) { return !game.wasWon && game.over(); });
return gamesLostOrNot.reduce(function(a, b){ return b ? a + 1 : a; }, 0);
};
this.calculateScore = function() {
score = 0;
this.games.forEach(function(game, index) {
guessesLeft = game.state.wrongGuessesLeft;
letterCount = game.state.word.reduce(function(tally, char) { return char != "-" ? tally + 1 : tally; }, 0);
score += (guessesLeft * letterCount);
});
return score;
};
//-- PRIVATE ------------------------------------>>>
var that = this;
var updateScoreboard = function() {
that.state.score = that.calculateScore();
if (that.game.over() && that.game.wasWon) {
that.state.wins = that.countWins();
} else if (that.game.over() && !that.game.wasWon) {
that.state.losses = that.countLosses();
}
that.state.render();
};
var allowGameBrowsing = function() {
switch (keyPressed) {
case "Enter":
if (!that.game.isPlaying) {
that.game.play();
} else {
that.game.pause();
}
break;
case "ArrowLeft":
that.selectPreviousGame();
break;
case "ArrowRight":
that.selectNextGame();
break;
}
};
// Event Handlers
var processInput = function(event) {
keyPressed = event.key;
updateScoreboard();
allowGameBrowsing();
};
//----------------------------------------------->>>
document.addEventListener("keyup", processInput);
document.onload = this.state.render(); this.game.play();
}
var words = [
"pineapple",
"mango",
"papaya",
"guava",
"lychee",
"tamarind",
"pitaya",
"cherimoya",
"soursop",
"mangosteen"
];
var hangman = new Hangman(6, words);
//== LEGACY =====================================>>>
var oldGameTemplate = function(state) {
return "<h1>Hangman</h1>" +
"<p>Guess what word I'm thinking of</p>" +
"<p>Guesses Left: " + state.wrongGuessesLeft + "</p>" +
"<p>Word: " + state.word + "</p>" +
"<p>Game Won?!: " + state.wasWon + "</p>";
};
var oldHangmanTemplate = function(state) {
return "<p>Wins: " + state.wins + "</p>" +
"<p>Losses: " + state.losses + "</p>";
};
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment