5
\$\begingroup\$

I've made a JavaScript, HTML and CSS version of the memory game 'Simon' from the 70s. The design of the board is not too impressive (I wanted to focus first on the JavaScript part) but I also appreciate feedback on it of course. If you want to see the game already in action, it's available here.

var computerMovements = [];
var answers = [];
var rounds = 0;
//strict mode allows one  mistake per round. false if 'relaxed' mode
var strict = true;
//in strict mode, there is no last chance
var lastChance = false;

var addColor = function(arr) {
  var colorsArray = ["green", "red", "yellow", "blue"];
  return arr.push(colorsArray[Math.floor(Math.random() * colorsArray.length)]);
};

var flashLights = function(arr) {
  var i = 0;

  var interval = setInterval(function() {
    $("#" + arr[i]).fadeTo("slow", 0).fadeTo("slow", 1);
    $("#sound-" + arr[i])[0].play();
    i++;
    if (i >= arr.length) {
      clearInterval(interval);
    }
  }, 1500);
};

var resetAnswers = function() {
  answers = [];
};

var updateRounds = function() {
  rounds++;
  $("#show-rounds").html(rounds);
};

var resetGame = function() {
  rounds = 0;
  computerMovements = [];
  if (strict === false) {
    lastChance = true;
  }
  resetAnswers();
};

var playerTurn = function() {
  //during the game we don't want the player to switch between strict and relaxed
  $("#mode").click(function() {
    return false;
  });

  //winning condition
  if (rounds === 20) {
    alert("You, you, you're good you!");
    resetGame();
  }

  updateRounds();
  addColor(computerMovements);
  flashLights(computerMovements);

  $(".button").off("click").on("click", function() {
    $("#sound-" + $(this).attr("id"))[0].play();
    answers.push($(this).attr("id"));

    for (var i = 0; i < answers.length; i++) {
      //correct answer
      if (JSON.stringify(computerMovements) === JSON.stringify(answers)) {
        resetAnswers();
        playerTurn();
        break;
      }

      //wrong answer
      if (answers[i] !== computerMovements[i]) {
        if (strict === false && lastChance === true) {
          lastChance = false;
          alert("You get one more chance...");
          resetAnswers();
          flashLights(computerMovements);
        } else if (
          answers[i] !== computerMovements[i] &&
          lastChance === false
        ) {
          alert("Epic fail!");
          resetGame();
          break;
        }
      }
    }
  });
};

$("#mode").click(function() {
  switch (strict) {
    case true:
      strict = false;
      lastChance = true;
      $("#mode").html("Mode: Relaxed");
      break;

    case false:
      strict = true;
      lastChance = false;
      $("#mode").html("Mode: Strict");
      break;
  }
});

$("#start").click(function() {
  playerTurn();
});
#container {
  background-color: gray;
  width: 160px;
  height: 160px;
  margin: 0 auto;
  text-align: center;
  text-align: justify;
}

#green {
  background-color: green;
  width: 70px;
  height: 70px;
  float: left;
}

#red {
  background-color: red;
  width: 70px;
  height: 70px;
  display: inline-block;
}

#yellow {
  background-color: yellow;
  height: 70px;
  width: 70px;
  clear: left;
  float: left;
}

#blue {
  background-color: blue;
  width: 70px;
  height: 70px;
  float: left;
}

#green:active {
  background-color: lightgreen;
}

#red:active {
  background-color: pink;
}

#yellow:active {
  background-color: lightyellow;
}

#blue:active {
  background-color: lightblue;
}

.box {
  margin: 0 auto;
  width: 45px;
  border: 1px solid black;
  text-align: center;
}

#mode:hover {
  cursor: pointer;
}

#start:hover {
  cursor: pointer;
}

#show-rounds {}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!doctype html>

<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>Simon says...</title>
  <link rel="stylesheet" href="css/main.css">
</head>

<body>
  <audio preload="auto" id="sound-green"> <source src="https://s3.amazonaws.com/freecodecamp/simonSound1.mp3"></source></audio>
  <audio preload="auto" id="sound-red"><source src="https://s3.amazonaws.com/freecodecamp/simonSound2.mp3"></source></audio>
  <audio preload="auto" id="sound-yellow"><source src="https://s3.amazonaws.com/freecodecamp/simonSound3.mp3"></source></audio>
  <audio preload="auto" id="sound-blue"><source src="https://s3.amazonaws.com/freecodecamp/simonSound4.mp3"></source></audio>

  <div id='container'>
    <div class='button' id='green'> </div>
    <div class='button' id='red'> </div>
    <div class='button' id='yellow'> </div>
    <div class='button' id='blue'> </div>
  </div>
  <div class='box' id='show-rounds'>0</div>
  <div class='box' id='mode'>Mode: Strict</div>
  <div class='box' id='start'>Start</div>
  </div>
</body>

</html>

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

JavaScript

The code looks nice and short. I like the comments on the strict and lastChance variables since they explain the game to readers who are not familiar with the game.

Instead of var name = function(...) {}, it is more common to write function name(...) {}.

The function flashLights is always called with computerMovements as its argument. Therefore, you can remove the arr parameter and replace it with computerMovements.

The code below the //during the game comment does not do what the comment says; you can still change the mode during a game. To avoid this, you have to call $("#mode").off("click").

The code if (strict === false && lastChance === true) { can be written shorter, as if (!strict && lastChance) {.

Comparing two arrays by transforming both into JSON format is a nice trick. But that whole if block should be outside the for loop.

In the // wrong answer section, you have some redundant checks:

  • when lastChance is true, strict is guaranteed to be false, so you don't need to check this.
  • the condition before the Epic fail alert is completely redundant. The first part is already covered by the outside if statement, and the second part is coverted by the if ... then part.

The code of the function that changes the mode can be written a lot simpler:

$("#mode").click(function() {
  strict = !strict;
  lastChance = !strict;
  $("#mode").html(strict ? "Mode: Strict" : "Mode: Relaxed");
});

CSS

The last rule is empty and therefore can be removed.

HTML

The <!DOCTYPE declaration has to be at the beginning of the file.

The <script> element should be near the <link> element.

\$\endgroup\$
2
  • \$\begingroup\$ "The function flashLights is always called with computerMovements as its argument. Therefore, you can remove the arr parameter and replace it with computerMovements." - Are you suggesting to replace arguments with globals? Wouldn't a more decoupled more local code with less reliance on outside variables be easier to test, reuse and understand? \$\endgroup\$
    – le_m
    Commented Apr 30, 2017 at 2:41
  • \$\begingroup\$ @le_m Im general you're right. In this case though, the function has so many other dependencies (window, DOM) that it is already hard to test it automatically. Therefore I thought it wouldn't hurt. \$\endgroup\$ Commented Apr 30, 2017 at 6:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.