Create Simon Game in Javascript
When I started Free Code Camp Course last year I knew this day would finally come. The day of my last front-end development course challenge. I thought about creating this game for quite a long while. Honestly, I had all concepts figured out before I even started coding, but in the end the challenge turned out to be much more difficult then I presumed.
Simon Game - breakdown
Simon Game is a simple game which goal is for user to repeat the pattern showed by the program. With each round a new step is added to the pattern making, the game much more difficult with every round. Additionally, the program should play a different sound for each field that is activated. On top Of that you have to enable user to switch between strict mode, which restarts a game whenever user selects wrong field in a pattern, and normal mode which only repeats the pattern subsequent of the wrong button being pressed. The provided example constituted of 4 different fields with different color for each field and I followed with the same idea.Simon Game - design
I genuinely didn’t like the design Free Code Camp example so I decided to change it. After some googling I came across a mobile game for iPhone called Circles. It’s design appeared to me - minimalistic and intuitive.Following that design I began working on my version of Simon Game in JS.
Simon Game - HTML & CSS
As usual I used Bootstrap for responsiveness of the game, and added my own styles using CSS (with LESS preprocessor). I also took advantage of Animate.css to add animation.I used 3 rows to place circles in correct order and adjust their positions to make them to look like they’re aligned in a circle.
<div class="row">
<div class="col-md-12">
<div class="gamefield">
<div class="btn-sett" data-toggle="modal" data-target="#myModal">
<h5>Settings</h5>
</div>
<div class="top-row">
<div id="blue" class="gamebutton" onClick="addToPlayer(this.id)"></div>
</div>
<div class="middle-row">
<div id="green" class="gamebutton" onClick="addToPlayer(this.id)"></div>
<div id="gameNumber">
<h2 id="clickNumber">0</h2>
</div>
<div id="red" class="gamebutton" onClick="addToPlayer(this.id)"></div>
</div>
<div class="bottom-row">
<div id="dark" class="gamebutton" onClick="addToPlayer(this.id)"></div>
<div></div>
</div>
</div>
</div>
</div>
I also added a Settings button to activate Bootstrap modal in which buttons for changing game mode and resetting the game are stored.
@import 'https://fonts.googleapis.com/css?family=Muli';
@rich-black: #071013;
@kelly-green: #29BF12;
@tiffany-blue: #08BDBD;
@red: #F21B3F;
@dark: #FF9914;
To generate colors for the design I used a great online tool called Coolors, after few space bar clicks I had a new set of colors, which I stored in LESS variables for futher use. I also added a font from Google Fonts.
The rest of LESS code
body {
background-color: @rich-black;
color: white;
font-family: 'Muli', sans-serif;
}
.gamefield {
max-width: 480px;
margin: 20px auto;
}
.align {
display: flex;
justify-content: center;
}
.top-row {
.align;
}
.middle-row {
display: flex;
justify-content: space-around;
align-items: center;
margin: 15px auto;
}
.bottom-row {
.align;
}
@buttonWidth: 90px;
.gamebutton {
width: @buttonWidth;
height: @buttonWidth;
border-radius: 400px;
}
.border(@color) {
border: 4px solid @color;
margin: 20px;
&:active {
// DO NOT USE HOVER (better mobile performance)
background-color: @color;
}
}
.onhover(@color) {
background-color: @color;
}
#blue {
.border(@tiffany-blue);
}
#green {
.border(@kelly-green);
}
#red {
.border(@red);
}
#dark {
.border(@dark);
}
#blue.hover {
.onhover(@tiffany-blue);
}
#green.hover {
.onhover(@kelly-green);
}
#red.hover {
.onhover(@red);
}
#dark.hover {
.onhover(@dark);
}
.btn-sett {
font-size: 2.7em;
text-align: left;
transition: 500ms;
width: 70px;
margin-right: 340px;
&:hover {
color: @kelly-green;
}
}
.modal-title {
.black;
}
.black {
color: @rich-black;
text-align: center;
}
#strict {
margin: 5px auto;
text-align: center;
}
When it comes to CSS I had a problem with showing which of the elements are in a pattern. As you see later in JavaScript code I resolved this by adding .hover
class to element and then removing it. But the circle had to be lit in right color, that’s why I used #id.hover
CSS selector. CSS code added to that selector will only apply to elements which have both an id id
and a class hover
at the same time.
Simon Game - JavaScript Time
As I said at the beginning of this article - I’ve been thinking a lot about this last project before I’ve started it. I had all the basic functions laid out quite good, but still came across some serious problems.Ok. Let’s start with the basic stuff. The first thing I did was creating an JavaScript Object with all the necessary properties stored for the game to work.
var game = {
count: 0,
possibilities: ['#green', '#blue', '#red', '#dark'],
currentGame: [],
player: [],
sound: {
blue: new Audio('https://s3.amazonaws.com/freecodecamp/simonSound1.mp3'),
red: new Audio('https://s3.amazonaws.com/freecodecamp/simonSound2.mp3'),
dark: new Audio('https://s3.amazonaws.com/freecodecamp/simonSound3.mp3'),
green: new Audio('https://s3.amazonaws.com/freecodecamp/simonSound4.mp3'),
},
strict: false,
};
The code is pretty self-explanatory, we need to show possibilities that computer can choose from to plan the current game (which is a pattern it will repeat), we also need an array to store player’s input, some sounds, round count and strict boolean for switching game modes.
The game starts when newGame()
function is called. This function’s only task is simply calling another function - clearGame()
.
function newGame() {
clearGame();
}
function clearGame() {
game.currentGame = [];
game.count = 0;
addCount();
}
function addCount() {
game.count;
$('#clickNumber').addClass('animated fadeOutDown');
setTimeout(function () {
$('#clickNumber')
.removeClass('fadeOutDown')
.html(game.count)
.addClass('fadeInDown');
}, 200);
generateMove();
}
clearGame()
is called simply to make sure that the game starts with parameters we want it to start with (that is - game.count=0
and game.currentGame = []
). After the game object properties are cleared it’s time to add our first move to the pattern by using addCount()
.
This function adds 1 to count total, and changes the number displayed in the middle using before mentioned Animate.css animations. To display animations correctly it’s necessary to time those events by using setTimeout()
method.
The next step is generating a move to make.
function generateMove() {
game.currentGame.push(game.possibilities[Math.floor(Math.random() * 4)]);
showMoves();
}
This function is very basic, but also crucial to the whole game. All we need to do is randomly select a move from a pool of possibilities using Math.floor()
and Math.Random()
. The generated move should be pushed into currentGame
array (as we only want to add the last move to the pattern). With every new round this function will add 1 step to the pattern.
Simon Game - Problems
My problems started with `showMoves()` function. I wanted to call a `for` loop that for every item in `game.currentGame` would add and then remove class `.hover` with a slight delay between those actions. The delay would allow user to clearly see which fields were selected. Unfortunately for loops run without waiting for the `setTimeout` method to be executed.Well that’s life I guess - not every idea is a good one. I tried a lot of different configurations - calling functions from outside the showMoves()
function, setting different setTimeouts that would be multiplied with each round of a loop and bunch of different things. The code became unclear, worked in a way I couldn’t understand and caused a handful of other problems.
I forgot about another method that helps with timed events in JavaScript - the setInterval()
method.
function showMoves() {
var i = 0;
var moves = setInterval(function () {
playGame(game.currentGame[i]);
i;
if (i >= game.currentGame.length) {
clearInterval(moves);
}
}, 600);
clearPlayer();
}
function playGame(field) {
$(field).addClass('hover');
sound(field);
setTimeout(function () {
$(field).removeClass('hover');
}, 300);
}
As you can see this function is structured a little bit like a for
loop except it respects setTimeout
method in playGame()
function. The functions stops the interval after all of currentGame
elements were shown.
Simon Game - checking for player moves
Now the game works - it displays all the items in an array in a correct way, and is capable of adding new step to the pattern with each round. All that left was to let player repeat the pattern.function clearPlayer() {
game.player = [];
}
function addToPlayer(id) {
var field = "#" id
console.log(field);
game.player.push(field);
playerTurn(field);
}
function playerTurn(x) {
if (game.player[game.player.length - 1] !== game.currentGame[game.player.length - 1]) {
if(game.strict){
alert('Try again! ...From scratch!');
newGame();
} else {
alert('Wrong move! Try again!');
showMoves();
}
} else {
console.log('Good Move!');
sound(x);
var check = game.player.length === game.currentGame.length;
if (check) {
if(game.count == 20){
alert('You won! Congrats.');
} else {
alert('Next round!');
nextLevel();
}
}
}
}
First thing to do is make sure the player haven’t inputed anything in current round by calling clearPlayer()
. Then we wait for the player to press the correct circle and test it using playerTurn(x)
function. If the move is incorrect the game will display the pattern again, if it’s correct it will check whether this round is completed or not.
The function takes different action based on game settings by checking game.strict
boolean.
Simon Game - Conclusion
Tic Tac Toe game was a more demanding project. Mostly because creating a perfect tic tac toe game involves using a mini-max algorithm. On the other hand Simon Game is a great opportunity to practice timed events in JavaScript and I had a lot of fun coding it.Check out this project on CodePen and take a look at more of my blog posts.
As always if you have any questions/suggestions - comment below or message me on Twitter - @mrkaluzny.