Outside of the jQuery DSL I've hardly ever written any JavaScript. I never bothered learning JavaScript until a couple of months ago. After a slow start I got stuck in and created this game today.
I usually program in Ruby, and this is proving to be an obstacle because I'm thinking in ruby while writing JS.
The code may come as a shocker to experienced JS programmers. My greatest challenge is to separate presentation from logic. I haven't figured that out yet. This is a case of knowing the syntax without knowing the language well.
I'm interested in comments about structure. I know this is hardly maintainable code. It's obvious. What is not obvious (to me) is how to make it maintainable.
I'm only adding the JS code here, but the entire game is included in the snippet below the code.
(function() {
/*
* Screens
*/
var titleScreen = document.getElementById('title-screen');
var gameScreen = document.getElementById('game-screen');
var nameScreen = document.getElementById('name-screen');
/*
* Player name input
*/
var name = document.getElementById('player-name');
name.addEventListener('keyup', function() {
if (name.value.length > 0 && name.value.length <= 8) {
hideNameError();
}
else {
showNameError();
}
});
/*
* Buttons
*/
// Sends player to name screen
var newGameButton = document.getElementById('btn__new-game');
newGameButton.addEventListener('click', function() {
toNameScreen();
});
// Quits the game at any moment
var quitToTitleScreenButton = document.getElementById('quit-to-title-screen');
quitToTitleScreenButton.addEventListener('click', function() {
var quit = confirm("Are you sure you want to quit?");
if (quit) {
toTitleScreen();
}
});
// Cancels game on name screen
var cancelNewGameButton = document.getElementById('cancel-new-game');
cancelNewGameButton.addEventListener('click', function(event) {
event.preventDefault();
toTitleScreen();
});
// Starts game
var startGameButton = document.getElementById('start-game');
startGameButton.addEventListener('click', function(event) {
event.preventDefault();
if (!name.value || name.value.length > 8) {
showNameError();
return false;
}
else {
var game = new Game(name.value);
// Change name labels to show player name
var nameLabels = document.getElementsByClassName('name-label');
for (var i = 0; i < nameLabels.length; i++) {
nameLabels[i].innerHTML = name.value;
}
// Reset name input and transition to game screen
name.value = '';
toGameScreen();
// Prime game play buttons
var playButtons = document.getElementsByClassName('play-button');
for (var i = 0; i < playButtons.length; i++) {
var element = playButtons[i];
element.addEventListener('click', function() {
var cpuChoice = game.computerChoice();
var playerChoice = this.id;
var result = game.resolve(playerChoice, cpuChoice);
refreshScreen(result, game, playerChoice, cpuChoice);
});
}
}
});
/*
* Show hide error if name is invalid
*/
function showNameError() {
document.getElementById('error').style.display = 'block';
}
function hideNameError() {
document.getElementById('error').style.display = 'none';
}
/*
* Screen transition functions
*/
function toTitleScreen() {
gameScreen.style.display = 'none';
nameScreen.style.display = 'none';
titleScreen.style.display = 'block';
}
function toGameScreen() {
titleScreen.style.display = 'none';
nameScreen.style.display = 'none';
gameScreen.style.display = 'block';
}
function toNameScreen() {
titleScreen.style.display = 'none';
gameScreen.style.display = 'none';
nameScreen.style.display = 'block';
}
/*
* Updates game screen after each game
*/
function refreshScreen(result, game, player, cpu) {
// Hide messages
var messages = document.getElementsByClassName('player-choices__result');
for (var i = 0; i < messages.length; i++) {
messages[i].style.display = 'none';
}
// Show player and computer choices
document.getElementById('choice-computer').innerHTML = cpu.capitalize();
document.getElementById('choice-player').innerHTML = player.capitalize();
// Update scores and show message
switch(result) {
case 'playerWin':
document.getElementById('player-score').innerHTML = game.playerScore;
document.getElementById('result-win').style.display = 'block';
break;
case 'computerWin':
document.getElementById('computer-score').innerHTML = game.computerScore;
document.getElementById('result-loss').style.display = 'block';
break;
case 'tie':
document.getElementById('ties-score').innerHTML = game.ties;
document.getElementById('result-tie').style.display = 'block';
break;
}
}
/*
* Game object
*/
var possibleChoices = ['rock', 'paper', 'scissors'];
function Game(player) {
this.player = player;
this.computerScore = 0;
this.playerScore = 0;
this.ties = 0;
}
Game.prototype.resolve = function(playerChoice, cpuChoice) {
switch(playerChoice) {
case 'rock':
if (cpuChoice === 'rock') {
this.ties += 1;
return 'tie';
}
else if (cpuChoice == 'paper') {
this.computerScore += 1;
return 'computerWin';
}
else if (cpuChoice == 'scissors') {
this.playerScore += 1;
return 'playerWin';
}
case 'paper':
if (cpuChoice === 'rock') {
this.playerScore += 1;
return 'playerWin';
}
else if (cpuChoice == 'paper') {
this.ties += 1;
return 'tie';
}
else if (cpuChoice == 'scissors') {
this.computerScore += 1;
return 'computerWin';
}
case 'scissors':
if (cpuChoice === 'rock') {
this.computerScore += 1;
return 'computerWin';
}
else if (cpuChoice == 'paper') {
this.playerScore += 1;
return 'playerWin';
}
else if (cpuChoice == 'scissors') {
this.ties += 1;
return 'tie';
}
}
};
Game.prototype.computerChoice = function() {
return possibleChoices[Math.floor(Math.random() * possibleChoices.length)];
};
String.prototype.capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
}
})();
(function() {
/*
* Screens
*/
var titleScreen = document.getElementById('title-screen');
var gameScreen = document.getElementById('game-screen');
var nameScreen = document.getElementById('name-screen');
/*
* Player name input
*/
var name = document.getElementById('player-name');
name.addEventListener('keyup', function() {
if (name.value.length > 0 && name.value.length <= 8) {
hideNameError();
}
else {
showNameError();
}
});
/*
* Buttons
*/
// Sends player to name screen
var newGameButton = document.getElementById('btn__new-game');
newGameButton.addEventListener('click', function() {
toNameScreen();
});
// Quits the game at any moment
var quitToTitleScreenButton = document.getElementById('quit-to-title-screen');
quitToTitleScreenButton.addEventListener('click', function() {
var quit = confirm("Are you sure you want to quit?");
if (quit) {
toTitleScreen();
}
});
// Cancels game on name screen
var cancelNewGameButton = document.getElementById('cancel-new-game');
cancelNewGameButton.addEventListener('click', function(event) {
event.preventDefault();
toTitleScreen();
});
// Starts game
var startGameButton = document.getElementById('start-game');
startGameButton.addEventListener('click', function(event) {
event.preventDefault();
if (!name.value || name.value.length > 8) {
showNameError();
return false;
}
else {
var game = new Game(name.value);
// Change name labels to show player name
var nameLabels = document.getElementsByClassName('name-label');
for (var i = 0; i < nameLabels.length; i++) {
nameLabels[i].innerHTML = name.value;
}
// Reset name input and transition to game screen
name.value = '';
toGameScreen();
// Prime game play buttons
var playButtons = document.getElementsByClassName('play-button');
for (var i = 0; i < playButtons.length; i++) {
var element = playButtons[i];
element.addEventListener('click', function() {
var cpuChoice = game.computerChoice();
var playerChoice = this.id;
var result = game.resolve(playerChoice, cpuChoice);
refreshScreen(result, game, playerChoice, cpuChoice);
});
}
}
});
/*
* Show hide error if name is invalid
*/
function showNameError() {
document.getElementById('error').style.display = 'block';
}
function hideNameError() {
document.getElementById('error').style.display = 'none';
}
/*
* Screen transition functions
*/
function toTitleScreen() {
gameScreen.style.display = 'none';
nameScreen.style.display = 'none';
titleScreen.style.display = 'block';
}
function toGameScreen() {
titleScreen.style.display = 'none';
nameScreen.style.display = 'none';
gameScreen.style.display = 'block';
}
function toNameScreen() {
titleScreen.style.display = 'none';
gameScreen.style.display = 'none';
nameScreen.style.display = 'block';
}
/*
* Updates game screen after each game
*/
function refreshScreen(result, game, player, cpu) {
// Hide messages
var messages = document.getElementsByClassName('player-choices__result');
for (var i = 0; i < messages.length; i++) {
messages[i].style.display = 'none';
}
// Show player and computer choices
document.getElementById('choice-computer').innerHTML = cpu.capitalize();
document.getElementById('choice-player').innerHTML = player.capitalize();
// Update scores and show message
switch(result) {
case 'playerWin':
document.getElementById('player-score').innerHTML = game.playerScore;
document.getElementById('result-win').style.display = 'block';
break;
case 'computerWin':
document.getElementById('computer-score').innerHTML = game.computerScore;
document.getElementById('result-loss').style.display = 'block';
break;
case 'tie':
document.getElementById('ties-score').innerHTML = game.ties;
document.getElementById('result-tie').style.display = 'block';
break;
}
}
/*
* Game object
*/
var possibleChoices = ['rock', 'paper', 'scissors'];
function Game(player) {
this.player = player;
this.computerScore = 0;
this.playerScore = 0;
this.ties = 0;
}
Game.prototype.resolve = function(playerChoice, cpuChoice) {
switch(playerChoice) {
case 'rock':
if (cpuChoice === 'rock') {
this.ties += 1;
return 'tie';
}
else if (cpuChoice == 'paper') {
this.computerScore += 1;
return 'computerWin';
}
else if (cpuChoice == 'scissors') {
this.playerScore += 1;
return 'playerWin';
}
case 'paper':
if (cpuChoice === 'rock') {
this.playerScore += 1;
return 'playerWin';
}
else if (cpuChoice == 'paper') {
this.ties += 1;
return 'tie';
}
else if (cpuChoice == 'scissors') {
this.computerScore += 1;
return 'computerWin';
}
case 'scissors':
if (cpuChoice === 'rock') {
this.computerScore += 1;
return 'computerWin';
}
else if (cpuChoice == 'paper') {
this.playerScore += 1;
return 'playerWin';
}
else if (cpuChoice == 'scissors') {
this.ties += 1;
return 'tie';
}
}
};
Game.prototype.computerChoice = function() {
return possibleChoices[Math.floor(Math.random() * possibleChoices.length)];
};
String.prototype.capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
}
})();
@import url(http://fonts.googleapis.com/css?family=Sigmar+One);
* {
box-sizing: border-box;
font-family: 'Sigmar One', sans-serif;
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
.frame {
border: 12px solid blue;
height: 100%;
}
/*
* Title screen
*/
#title-screen {
left: 50%;
position: fixed;
text-align: center;
top: 50%;
transform: translate(-50%, -50%);
}
#game-title {
color: #fff;
font-size: 4em;
line-height: 1.2em;
margin: 0 0 1em;
text-shadow:
2px 3px 0 red,
-2px -3px 0 green,
-2px 3px 0 blue,
2px -2px 0 blue,
6px 6px 0 black;
}
#btn__new-game {
background-color: #fff;
border: 2px solid #000;
bottom: 10%;
font-family: 'Sigmar One';
font-size: 2em;
position: relative;
padding: 10px 20px;
}
/*
* END Title screen
*/
/*
* New game splash screen
*/
#game-screen {
display: none;
text-align: center;
}
.game-screen__title {
font-size: 2.5em;
margin: 0 0 .5em;
}
.btn {
background-color: #fff;
border: 3px solid #333;
display: inline-block;
font-size: 1.1em;
margin: .3em 0;
padding: 12px 20px;
width: 200px;
}
/* Total running score */
.game-score {
border-collapse: collapse;
margin: 1em 0;
width: 100%;
}
.game-score__label {
color: #666;
width: 33.3%;
}
.game-score__score {
font-size: 1.5em;
}
/* Player choices and result message */
.player-choices {
border-collapse: collapse;
margin: 2em 0;
text-align: center;
width: 100%;
}
.player-choices__results {
vertical-align: middle;
}
.player-choices__result {
display: none;
font-size: 2em;
}
#result-win {
color: green;
}
#result-loss {
color: red;
}
.player-choices__label {
color: #777;
font-size: 1em;
width: 30%;
}
.player-choices__choice {
font-size: 2em;
}
/* Game Over Menu */
#game-over-menu {
display: none;
margin: 1em 0;
}
.game-over-menu__btn {
font-size: .8em;
padding: 4px 8px;
width: 120px;
}
/* Quick to title screen button, always visible */
#quit-to-title-screen {
bottom: 30px;
padding: 5px 8px;
position: absolute;
right: 30px;
}
/*
* END New game splash screen
*/
/*
* Name screen
*/
#name-screen {
display: none;
left: 50%;
position: fixed;
text-align: center;
top: 50%;
transform: translate(-50%, -50%);
}
#player-name-label {
font-size: 2em;
}
#player-name {
box-sizing: border-box;
display: block;
font-size: 1.5em;
margin: 1em 0;
padding: 20px;
width: 100%;
}
#error {
display: none;
color: red;
margin: 1em;
}
<!DOCTYPE html>
<html>
<head>
<title>Rock Paper Scissors</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<main class="frame">
<section id="title-screen">
<h1 id="game-title">Rock Paper Scissors</h1>
<button id="btn__new-game">Play Game</button>
</section>
<section id="name-screen">
<form>
<label for="player-name" id="player-name-label">What's your name?</label>
<input type="text" name="playerName" id="player-name" />
<p id="error">Enter a name between 1 and 8 chars long!</p>
<button class="btn" type="submit" id="start-game">Start Game</button>
<button class="btn" type="cancel" id="cancel-new-game">Cancel</button>
</form>
</section>
<section id="game-screen">
<table class="game-score">
<thead>
<tr>
<th class="game-score__label">Computer</th>
<th class="game-score__label">Tie</th>
<th class="game-score__label name-label">Player</th>
</tr>
</thead>
<tbody>
<tr>
<td class="game-score__score" id="computer-score">0</td>
<td class="game-score__score" id="ties-score">0</td>
<td class="game-score__score" id="player-score">0</td>
</tr>
</tbody>
</table>
<h1 class="game-screen__title">Choose</h1>
<div class="choices">
<button class="btn play-button" id="rock">Rock</button>
<br />
<button class="btn play-button" id="paper">Paper</button>
<br />
<button class="btn play-button" id="scissors">Scissors</button>
</div>
<table class="player-choices">
<tbody>
<tr>
<th class="player-choices__label">Computer</th>
<th class="player-choices__results" rowspan="2">
<h2 class="player-choices__result" id="result-win">YOU WIN!</h2>
<h2 class="player-choices__result" id="result-loss">YOU LOSE!</h2>
<h2 class="player-choices__result" id="result-tie">IT'S A TIE!</h2>
</th>
<th class="player-choices__label name-label">Player</th>
</tr>
<tr>
<td class="player-choices__choice" id="choice-computer">?</td>
<td class="player-choices__choice" id="choice-player">?</td>
</tr>
</tbody>
</table>
<div id="game-over-menu">
<button class="game-over-menu__btn" id="play-new-game">Play Again</button>
<button class="game-over-menu__btn" id="quit-game">Quit Game</button>
</div>
<button id="quit-to-title-screen">Quit</button>
</section>
</main>
<script type="text/javascript" src="main.js"></script>
</body>
</html>