Simon® [FCC]

In this example below you will see how to do a Simon® [FCC] with some HTML / CSS and Javascript

This was created as part of the Front-End Developer while doing the Front-End Developer curriculum on Free Code Camp.

Thumbnail
This awesome code was written by JTKnox91, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright JTKnox91 ©
  • HTML
  • CSS
  • JavaScript
    <body><div class="app-container">
  <svg width="500" height="500">
    <defs>
      <path class="arc" d="M 0 200 A 200 200 0 0 1 200 0 V 100 A 100 100 0 0 0 100 200 H 0" stroke-width="2" stroke="black" />
      <use id="arc-shadow" xlink:href="#arc" fill="black">
    </defs>
    
    <!-- outer circle -->
    <circle cx="250" cy="250" r="220" fill="#444444" />
    
    <!-- Arc Shaped game buttons -->
    <path class="arc shadow" d="M 50 250 A 200 200 0 0 1 250 50 V 150 A 100 100 0 0 0 150 250 H 50" />
    <path id="green" class="arc green game-button" d="M 50 250 A 200 200 0 0 1 250 50 V 150 A 100 100 0 0 0 150 250 H 50" />
      
    <path class="arc shadow" d="M 250 50 A 200 200 0 0 1 450 250 H 350 A 100 100 0 0 0 250 150 V 50" />
    <path id="red" class="arc red game-button" d="M 250 50 A 200 200 0 0 1 450 250 H 350 A 100 100 0 0 0 250 150 V 50" />
    
    <path class="arc shadow" d="M 450 250 A 200 200 0 0 1 250 450 V 350 A 100 100 0 0 0 350 250 H 450" />
    <path id="blue" class="arc blue game-button" d="M 450 250 A 200 200 0 0 1 250 450 V 350 A 100 100 0 0 0 350 250 H 450" />  
    
    <path class="arc shadow" d="M 250 450 A 200 200 0 0 1 50 250 H 150 A 100 100 0 0 0 250 350 V 450" />
    <path id="yellow" class="arc yellow game-button" d="M 250 450 A 200 200 0 0 1 50 250 H 150 A 100 100 0 0 0 250 350 V 450" />
      
    <!-- Inner Circle -->
    <circle cx="250" cy="250" r="90" stroke="black" fill="#cccccc" transform="translate(0, 4)" />
    
    <text class="title" x="250" y="210">Simon®</text>
      
    <!-- Start/Stop/Mode Buttons -->
    <circle class="button shadow" cx="220" cy="240" r="15" />  
    <circle id="start" class="button green" cx="220" cy="240" r="15" />
      
    <circle class="button shadow" cx="280" cy="240" r="15" />  
    <circle id="strict" class="button red" cx="280" cy="240" r="15" />
    
    <!-- Start/Stop/Mode Labels -->
    <text class="label" x="220" y="275">Play!</text>
    <text class="label" x="280" y="275">Hard-Mode</text>
    
    <!-- Rounds Counter -->
    <rect class="counter-box" x="220" y="285" width="60" height="35" rx="5" ry="5"/>
    <text id="counter" class="counter" x="250" y="315">00</text>
      
  </svg>
  
    <audio id="audio-green" src="https://s3.amazonaws.com/freecodecamp/simonSound1.mp3" preload></audio>
    <audio id="audio-red" src="https://s3.amazonaws.com/freecodecamp/simonSound2.mp3" preload></audio>
    <audio id="audio-blue" src="https://s3.amazonaws.com/freecodecamp/simonSound3.mp3" preload></audio>
    <audio id="audio-yellow" src="https://s3.amazonaws.com/freecodecamp/simonSound4.mp3" preload></audio>
    <audio id="audio-error" src="http://www.soundjig.com/mp3/soundfx/beeps/beep2.mp3" preload></audio>
    
</div>
  
<footer id="footer">
  Designed and Coded by <a id="footer" href="https://codepen.io/JTKnox91/full/ALdpgG/">John T. Knox</a><br />
  Sources: http://www.soundjig.com
</footer>
  
</body>

/*Downloaded from https://www.codeseek.co/JTKnox91/simonandxae-fcc-vyYexQ */
    body {
  background-color: tan;
}

.app-container {
  margin: 0 auto;
  width: 500px;
  height: 500px;
}

.arc {
  stroke-width: 2px;
  stroke: black;
  transform-origin: 50% 50%;
  transform: scale(.95, .95);
}
.arc:hover {
  cursor: pointer;
}
.arc:active {
  transform-origin: 50% 50%;
  transform: translate(0, 10px) scale(.95, .95);
}
.shadow {
  fill: black;
}

.green {
  fill: #0b0;
}
.green.active {
  fill: #0f0;
}

.red {
  fill: #b00 
}
.red.active {
  fill: #f00;
}

.blue {
  fill: #00b;
}
.blue.active {
  fill: #00f;
}

.yellow {
  fill: #bb0;
}
.yellow.active {
  fill: #ff0;
}

.arc.shadow {
  transform-origin: 50% 50%;
  transform: translate(0, 10px) scale(.95, .95);
}

svg text {
  text-anchor: middle;
}

.title {
  font-size: 24px;
  font-weight: bold;
  text-decoration: underline;
}

.label {
  font-size: 12px;
  font-weight: bold;
}

.button {
  stroke: black;
  stroke-width: 2px
}
.button:hover {
  cursor: pointer;
}
.button.active, .button:active {
  transform-origin: 50% 50%;
  transform: translate(0, 6px)
}
.button.shadow {
  stroke: black;
  transform-origin: 50% 50%;
  transform: translate(0, 6px)
}

.counter-box {
  fill: black;
}
.counter {
  font-family: 'Share Tech Mono', monospace;
  font-size: 40px;
  fill: red;
}

#footer {
  width: 100%;
  text-align: center;
  font-size: 10px;
  color: black;
}


/*Downloaded from https://www.codeseek.co/JTKnox91/simonandxae-fcc-vyYexQ */
    $(function () {
  
  /*
  ** GAME LOGIC
  */
  
  var Game = function () {
    this.pattern = [ this.randomColor() ];
    this.isPlayerTurn = false;
    this.strict = false;
    this.position = 0;
    this.maxLength = 10; //per spec set this to 20, but less can be nice for testing.
    this.status; 
    /*
      'correct': keep guessing 
      'next': correct and end of sequence
      'restart': incorrect retry sequence
      'finished': guess correctly for all 20 rounds
    */
  };
  
  //generate 1 of 4 colors randomly
  Game.prototype.randomColor = function () {
    var r = Math.random() * 4;
    if (r < 1) {
      return "green";
    } else if (r < 2) {
      return "red";
    } else if (r < 3) {
      return "blue";
    } else {
      return "yellow";
    }
  };
  
  //add a random color to the current pattern
  Game.prototype.incPattern = function () {
    this.pattern.push(this.randomColor());
    this.position = 0;
  }
  
  //clear the current pattern
  Game.prototype.clearPattern = function () {
    this.pattern = [ this.randomColor() ];
  }
  
  Game.prototype.checkColor = function (color) {
    //if its currently the players turn
    if (this.isPlayerTurn) {
      //if color matches current color
      if (color === this.pattern[this.position]) {
        //if end of pattern
        if (this.position === this.pattern.length-1) {
          //if last pattern            
          if (this.pattern.length === this.maxLength) {
            //status finished
            this.status = "finished";
          } else {
            //status next
            this.status = "next";
            //inc pattern, position = 0
            this.incPattern();
          }
        //else
        } else {
          //statis correct
          this.status = "correct";
          //position++
          this.position++;
        }
      //else
      } else {
        //status restart
        this.status = "restart";
        this.position = 0;
        //if strict
        if (this.strict === true) {
          //new pattern, position = 0;
          this.clearPattern();          
        }
        //if regular, change nothing  
      }
    } else {
      this.status = "not ready";
    }     
  };
  
  //toggle between player turn and game turn
  Game.prototype.playerTurn = function (boolean) {
    this.isPlayerTurn = boolean;
  }
  
  //toggle hard-mode on and off
  Game.prototype.hardMode = function (boolean) {
    this.strict = boolean;
  }
  
  Game.prototype.patternLength = function () {
    return this.pattern.length;
  }
  
  /*
  ** DOM REFERENCES / APP GLOBALS
  */
  
  var $counter = $("#counter");
  var $start = $("#start");
  var $strict = $("#strict");
  
  var game;
  
  var colorMap = {
    "red": $("#red"),
    "green": $("#green"),
    "blue": $("#blue"),
    "yellow": $("#yellow")
  };
  
  var soundMap = {
    "red": $("#audio-red").get(0),
    "green": $("#audio-green").get(0),
    "blue": $("#audio-blue").get(0),
    "yellow": $("#audio-yellow").get(0),
    "error": $("#audio-error").get(0)
  };
  
  /*
  ** DOM READ/WRITES
  */
  
  //function set counter
  //set the current value for the pattern length counter
  var setCounter = function (n) {
    var buffer = n < 10 ? "0" : "";
    $counter.html(buffer + n);
  };
  
  //sets a message in the counter...3 chars max
  var setCounterString = function (message) {
    $counter.html(message);
  };
  
  var refreshCounter = function () {
    setCounter(game.patternLength());
  };
  
  //takes a string color and callback as argument
  //lights up arc and plays sound
  //after timeout //unlights note and runs callback
  //if pause if true, will add a delay befor ecallback
  var playColor = function (color, callback, buffer, duration) {
    buffer = buffer || 0;
    duration = duration || 500;
    callback = callback || function () {};
    
    var $color = colorMap[color];
    var $audio = soundMap[color];
 
    $color.addClass("active");
    $audio.currentTime = 0;
    $audio.play();

    setTimeout(function () {
      $color.removeClass("active");
      //this time out creates some space between notes
      setTimeout(callback, buffer);
    }, duration);
  };
  
  //takes an array of colors and a callback
  //individiaully lights up each color
  //after lighting last color runs callback   
  var playAllColors = function (colors, callback) {
    var i = 0
    var last = colors.length -1;
    var next = function () {
      if (i === last) {
        playColor(colors[i], callback, 0);
      } else {
        playColor(colors[i++], next, 500);
      }
    };
    next();
    
  };
  
  /*
  ** HELPER FUNCTION
  ** Some DOM Manipulation;
  */
  
  //function player for computer
  var playPattern = function () {
    //toggle off player turn
    game.playerTurn(false);
    //play all colors
    playAllColors(game.pattern, function () {
      //toggle player turn on  
      game.playerTurn(true);
    });

  };

  
  /*
  ** EVENT LISTENERS;
  */
  
  //on start button
  $start.click(function (e) {
    //default as easy mode
    $strict.removeClass("active");
    //instantiate game
    game = new Game();
    //write rounds
    refreshCounter();
    //play computer turn    
    playPattern();
  });

  
  //on strict button
  $strict.click(function (e) {    
    //toggle active class for strict button
    //toggle strict setting for game
    if ( $strict.hasClass("active") ) {
      $strict.removeClass("active");
      game.hardMode(false);
    } else {
      $strict.addClass("active");
      game.hardMode(true);
    }
  });

  //on arc press
  $(".game-button").click(function (e) {
    var color = $(this).attr("id");
    //try to make a move with that color
    game.checkColor(color);
    //if success (but not last color)
    if (game.status === "correct") {
      playColor(color);
      
    } else if (game.status === "finished") {
      game.playerTurn(false);
      setCounterString("FIN");
      playColor(color);
      
    } else if (game.status === "next") {
      game.playerTurn(false);
      //play happy sound, maybe?
      setCounterString("GO!");
      playColor(color, function() {
        setTimeout(refreshCounter, 0);
        setTimeout(playPattern, 1000);        
      });
  
    } else if (game.status === "restart") {
      game.playerTurn(false);
      soundMap.error.play();
      setCounterString("ERR");
      setTimeout(refreshCounter, 1000);
      setTimeout(playPattern, 2000);

    } else if (game.status === "not ready") {
      console.error("not player turn yet");
    } else {
      console.error("unknown game status:", game.status);
    }  
    
  });

});


Comments