Photo(c) Ugur Akdemir

Free Code Camp: Build a Simon Game

The user stories in this project that we have to accomplish are:

  1. I am presented with a random series of button presses.
  2. Each time I input a series of button presses correctly, I see the same series of button presses but with an additional step.
  3. I hear a sound that corresponds to each button both when the series of button presses plays, and when I personally press a button.
  4. If I press the wrong button, I am notified that I have done so, and that series of button presses starts again to remind me of the pattern so I can try again.
  5. I can see how many steps are in the current series of button presses.
  6. If I want to restart, I can hit a button to do so, and the game will return to a single step.
  7. I can play in strict mode where if I get a button press wrong, it notifies me that I have done so, and the game restarts at a new random series of button presses.
  8. I can win the game by getting a series of 20 steps correct. I am notified of my victory, then the game starts over.

They also gave sources for the sounds that we can use in this program:

So let’s start!

markup

<div class="end">
<p>It's the end! You won!</p>
</div>

<div>
<div class="container">
  <div type="button" id="0" class="green button"></div>
  <div type="button" id="1" class="red button"></div>
  <div type="button" id="2" class="yellow button"></div>
  <div type="button" id="3" class="blue button"></div>
  <div class="switch">
    <div>
      <form action="">
        <input class="field" type="text" value = "" readonly/>
        <button type="button" class="start">Start</button>
        <button type="button" class="reset">Reset</button>
        <button type="button" class="strict">Strict</button>
      </form>
    </div>
  </div>
</div>
  
  <footer>
    <p>by <a href="http://eiringonzales.com/" target="_blank">Eirin Gonzales</a></p>
  </footer>
</div>

The div with the class end will only appear if you win the game. It will remain hidden throughout the game.

Styles

.end {
  z-index: 10;
  position: relative;
  top: 100px;
  left: 25%;
  color: #000;
  background-color: white;
  padding: 20px;
  width: 50%;
  text-align: center;
  opacity: 1;
  font-size: 25px;
  display:none;
}

body {
  background-color: #000;
  font-family: 'Unica One', sans-serif;
}

.container {
  width: 450px;
  height: 450px;
  position: fixed;
  top: 50%;
  left: 51%;
  transform: translate(-50%, -50%);
}

.container div {
  margin: 0;
  padding: 0;
}

div.button,
.switch {
  width: 200px;
  height: 200px;
}

.button {
  border: 10px solid #222426;
  float: left;
  opacity: 0.5;
  cursor: pointer;
}

.lit {
  opacity: 1;
}

.clickable {
  pointer-events: auto;
  cursor: pointer;
}

.unclickable {
  pointer-events: none;
}

button.strict.active {
  background-color: red;
}

.green {
  background-color: green;
  border-radius: 100% 0px 0px 0px;
  border:10px solid #2a2a2b;
}

.red {
  background-color: red;
  border-radius: 0px 100% 0px 0px;
  border:10px solid #2a2a2b;
}

.blue {
  background-color: blue;
  border-radius: 0px 0px 100% 0px;
  border:10px solid #2a2a2b;
}

.yellow {
  background-color: yellow;
  border-radius: 0px 0px 0px 100%;
  border:10px solid #2a2a2b;
}

.switch {
  background-color: #fff;
  position: absolute;
  left: 110px;
  top: 110px;
  border-radius: 50%;
  border:10px solid #2a2a2b;
  display: table;
}

.switch div {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}

.switch input {
  display: block;
  margin: 0 auto;
  
  background-color: #280000;
  border: 1px solid;
  padding: 8px 5px 5px 5px;
  width: 40%;
  margin-bottom: 10px;
}

.switch input {
  color: #f20000;
  font-size: 20px;
  vertical-align: middle;
  text-align: right;
}

.switch button {
  background-color: #280000;
  color: #fff;
  padding: 7px;
  border: none;
  letter-spacing: 1px;
  transition: 0.2s;
}

.switch button:hover {
  background-color: #fff;
  color: #f20000;
}

footer {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  text-align: center;
  font-size: 20px;
  color: grey ;
}

footer a,
footer a:visited {
  color: #fff;
  text-decoration: none;
  transition: 0.3s;
}

footer a:hover {
  color: grey;
  letter-spacing: 1px;
}

I suggest giving the buttons a specific width and height to achieve the same look.

Script

var chain = 1;
var isStrict = false;

var colors = ["green", "red", "yellow", "blue"];
var chainofColors = [];
var player = [];

var greenSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound1.mp3");
var redSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound2.mp3");
var yellowSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound3.mp3");
var blueSound = new Audio("https://s3.amazonaws.com/freecodecamp/simonSound4.mp3");
var errorSound = new Audio("https://www.freesfx.co.uk/rx2/mp3s/10/12671_1442589611.mp3");

The chain is used to track down how many button presses are playing. It’ll also be displayed in the game so the user knows.

The isStrict is for the strict mode. It’s default to false.

The arrays are used to store the random button presses the machine will throw and the button presses of the user.

Those sounds will play as the user press a particular button.

$(".start").on("click", function() {
  chain = 1;
  player = [];
  chainOfColors = [];
  
  $(".start").addClass("unclickable").removeClass("clickable");
  $(".button").addClass("clickable").removeClass("unclickable");
  $(".reset").addClass("clickable").removeClass("unclickable");
  $(".strict").addClass("unclickable").removeClass("clickable");

  $(".field").val(chain);

  startGame();
});

When the user clicks the start button, we’re going to reset some things first. We’re going to make some buttons clickable and unclickable.

Then, we’re going to display what number of button presses the user is. And finally, we’ll start the game.

function generateNumber() {
  return Math.floor(Math.random() * 3);
}

function playSound(colorNumber) {
  switch(colorNumber) {
    case 0:
      greenSound.play(); break;
    case 1:
      redSound.play(); break;
    case 2:
      yellowSound.play(); break; 
    case 3:
      blueSound.play(); break;
    default:
      console.log(colorNumber);
      errorSound.play();
  }
}

function playButtonPresses() {
  var i = 0;
  var id = setInterval(function() {
    if (i < chainOfColors.length) {
      playSound(chainOfColors[i]);
      pressButton(chainOfColors[i]);
    } else {
      clearInterval(id);
    };
    i++;
  }, 1500);
}

function pressButton(btnNumber) {
  $("." + colors[btnNumber]).addClass("lit").delay(500).queue(function() {
    $("." + colors[btnNumber]).removeClass("lit").dequeue();
  });
}

function startGame() {
  if (chain < 21) {
    var colorBtn = generateNumber();
    chainOfColors.push(colorBtn);
    $(".field").val(chain);
    playButtonPresses();
  } else {
    // alert("It's the end! You won!");
    $(".end").fadeIn("fast").delay(4000).queue(function() {
      $(".end").fadeOut("slow").dequeue();
    });
    resetGame();
  }
}

function resetGame() {
  isStrict = false;
  
  $(".start").addClass("clickable").removeClass("unclickable");
  $(".button").addClass("unclickable").removeClass("clickable");
  $(".reset").addClass("unclickable").removeClass("clickable");
  $(".strict").removeClass("unclickable").addClass("clickable").removeClass("active");
  
  chain = 0;
  chainOfColors = [];

  $(".field").val("");
}

If the button presses are less than 21, it means that the user is still playing.

  1. Generate a random number from 0 to 3 using the function generateNumber(). The numbers 0-3 corresponds to the color of the button.
  2. Store the number which represents the color of the button in the chainOfColors and display the chain number.
  3.  Call function, playButtonPresses(), that sets an interval here so we can completely play the random button presses before the user presses any button.
    1. Play the sound that corresponds to the color of the button.
    2. Emphasize the button that is generated by making it brighter.
  4. When i exceeds the length of chainOfColors, we’ll clear the interval and wait for the user’s button presses.

If the game is already over, we’re going to show a banner and reset the game. We’ll just set the things back.

The next chunk of code will be when the user presses the button.

function checkButtonPresses() {
  for (var i = 0; i < chainOfColors.length; i++) {
    if (player[i] !== chainOfColors[i]) {
      return false;
    }
  }
  return true;
}

$(".button").on("click", function() {
  var id = Number($(this).attr("id"));
  pressButton(id);
  player.push(id);

  var status = function() {
    for (var click in player) {
      if(player[click] !== chainOfColors[click]) {
        return false;
      }
    }
    return true;
  };
  
  var result = status();
  
  if (result) {
    playSound(id);
    
    if (player.length === chainOfColors.length) {
      var state = checkButtonPresses();
      
      if (state) {
        chain++;
        setTimeout(function() {
          startGame();
        }, 1000);
      } 
      player = [];
    }
    
  } else {
    player = [];
    errorSound.play();
    
    if (isStrict) {
      chainOfColors = [];
      $(".field").text("!!");
      chain = 1;
      setTimeout(function() {
        startGame();
      }, 1000);
    } else {
      playButtonPresses();
    }
    
  }
});

When the user clicks on a button:

  1. Get the value of the id of that particular button and add it to the player’s or user’s array. And emphasize the button press by calling pressButton().
  2. Check if each player’s button press is the same with the random button press. We need to do this so we can identify immediately if there’s error.
  3. If they are the same,
    1. Play the sound for that particular button.
    2. If the length of the user’s array is the same with the chainOfColors, then call checkButtonPresses() to see if the user pressed the right sequence of buttons.
    3. If the user has pressed the right buttons, increment chain and call startGame() after a second. Afterwards, clear the contents of the player array because we will always check the user’s button presses from the start.
  4. If not,
    1. Play the sound for error.
    2. Reset the player’s array.
    3. Check if the player is in strict mode.
    4. If it is, reset the said variables and start the game again after a second.
    5. If the user is not in strict mode, call playButtonPresses() to repeat the last series of buttons.
$(".reset").on("click", function(){
  resetGame();
});

$(".strict").click(function() {
  $(this).toggleClass("active");
  isStrict = true;
});

If the player clicks on the reset, reset the game.

If the player clicks on the strict button, the game will be in strict mode.

 

I think the sound for error is not available anymore, so add your own.

You can check my whole code here.

And that’s it. That’s how we build a simon game using jQuery.

I hope you understand it.

Still, I recommend customizing and experimenting it to achieve your desired result.

Good luck!

 

Love,
Eirin

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s