I would love to get some feedback on my implementation of "Simon Says." I have not followed the original in that I have created a new sequence each time, rather and adding a new item to the pattern.
I'm particularly interested in the overall structure of my JS/JQuery - did I make good choices for the methods, is there enough separation of concerns etc. Are there any obvious conceptual errors?
I'm trying to cross the expanse from experienced beginner to "intermediate developer".
My code is at codepen: https://codepen.io/robin-andrews/full/BPPRWm/
$( document ).ready( function() {
var Simon = {
// Game constants
cacheDom: function() {}, // to be implemented
flashSpeed: 400,
score : 0,
level: 1,
// Init whole game
init: function() {
this.bindControls();
},
newRound : function(){
this.unbindPads();
this.bindControls();
},
bindControls: function() {
// currently only start button needs event
$( '#start-button' ).on( 'click', this.playerRound.bind( this ) );
},
unbindControls: function() {
$( '#start-button' ).off( 'click' );
},
bindPads: function() {
$( '#play-area' ).on( 'click', '.pad', this.checkSequence.bind( this ) ); // pass object
},
unbindPads: function() {
$( '#play-area' ).off( 'click' );
},
playerRound: function() {
this.unbindControls();
$( '#info' ).text( 'Copy the sequence using mouse clicks' );
this.computerSequence = this.generateSequence( this.level );
this.remainingSequence = this.computerSequence.slice( 0 );
this.displaySequence( this.computerSequence );
this.bindPads();
},
// Generate random sequence for pads
generateSequence: function( length ) {
var seq = [];
for ( var i = 0; i < length; i++ ) {
seq.push( Math.floor( Math.random() * 4 ) + 1 ); // returns a random integer from 1 to 10]
}
return seq;
},
// Display sequence to player
displaySequence: function( seq ) {
var self = this;
if ( seq.length >= 1 ) {
var current_list_item = seq[ 0 ];
$( '#pad' + current_list_item ).css( 'opacity', '1' );
$( '#pad' + current_list_item ).animate( {
'opacity': '0.4'
}, self.flashSpeed, function() {
self.displaySequence( seq.slice( 1 ) );
} );
return;
}
},
checkSequence: function( e ) {
var boxNum = $( e.target ).attr( 'id' ).slice( -1 );
if ( boxNum != this.remainingSequence[ 0 ] ) {
this.playerLoses();
return; //?
}
this.remainingSequence = this.remainingSequence.slice( 1 );
if ( this.remainingSequence.length < 1 ) {
this.playerWins();
}
},
playerWins : function(){
$( '#info' ).text( 'You won. Press play to continue.' );
this.score += this.level;
$( '#score' ).text( 'Score: ' + this.score);
this.level += 1;
this.newRound();
},
playerLoses: function() {
$( '#info' ).text( 'You lost. Press play to play again.' );
this.level = 1;
this.score = 0;
$( '#score' ).text( 'Score: ' + this.score);
this.newRound();
}
};
Simon.init();
} );
// To do
// increae speed with level?
// Track highest score?
* {
box-sizing: border-box;
}
body {
margin: 0;
background-color: grey;
font-family: "Lucida Console", Monaco, monospace;
}
#wrapper{
width: 600px;
height: 400px;
margin: auto;
position: relative;
}
#control-area{
width: 200px;
height: 400px;
background-color: black;
float: left;
}
#start-button{
display: block;
width: 100px;
margin: 20px auto;
background-color: white;
border: none;
border-radius: 30px;
padding: 5px;
top: 200px;
font-size: inherit;
font-family:inherit;
outline: none;
}
#info{
background-color: white;
width: 150px;
height: 90px;
padding: 5px;
margin: 20px auto;
border-radius: 5px;
}
#score{
background-color: white;
width: 100px;
padding: 5px;
margin: 20px auto;
border-radius: 5px;
}
#play-area{
width: 400px;
height: 400px;
float: right;
}
.pad{
width: 200px;
height: 200px;
float: left;
opacity: 0.4;
}
#pad1{
background: #66ff00;
}
#pad2{
background: red;
}
#pad3{
background: yellow;
}
#pad4{
background: blue;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Robin Says</title>
<link rel="stylesheet" href="style.css">
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="script.js"></script>
</head>
<body>
<div id="wrapper">
<h1 id="title">Robin Says</h1>
<div id="control-area">
<p id="info">
Press start to play
</p>
<div id="score">Score: 0</div>
<button id="start-button">Start</button>
</div>
<div id="play-area">
<div id="pad1" class="pad"></div><!-- Could use data attributes -->
<div id="pad2" class="pad"></div>
<div id="pad3" class="pad"></div>
<div id="pad4" class="pad"></div>
</div>
</div>
</body>
</html>