Simon Game

Using CSS transformations and animations

by vishalvasani

HTML

<button>Start</button>
<div id="level">Simon level: <span>0</span></div>
<div id="pie">
  <div id="1" class="piece"></div>
  <div id="2" class="piece"></div>
  <div id="3" class="piece"></div>
  <div id="4" class="piece"></div>
</div>
<div id="record"></div>

CSS

* {
  -webkit-user-select: none;
  user-select: none;
}

body {
  text-align: center;
  background-color: antiquewhite;
}

body > * {
  margin: 1vw 0;
  color: #2b2b2b;
  font-family: "Roboto Condensed";
}

button {
  font-size: 4vw;
  border-radius: 2vw;
  border: 0.5vw solid #ccc;
  padding: 1vw 2.5vw;
  cursor: pointer;
  text-transform: uppercase;
}

button:focus {
  outline: none;
}

#level {
  font-size: 3.8vw;
}

#record {
  font-size: 2.8vw;
}

#pie {
  width: 70vw;
  height: 70vw;
  margin: 2vw auto;
  padding: 1.5vw;
  background-color: #ddd;
  border-radius: 50%;
  /* Removing Gaps Between Inline child elements */
  font-size: 0;
}

#pie.avoid-tap {
  pointer-events: none;
}


/* Landscape (default is portrait) */

body.landscape > * {
  margin: 1vh 0;
}

body.landscape #pie {
  width: 70vh;
  height: 70vh;
  margin: 2vh auto;
  padding: 1.5vh;
}

body.landscape button {
  font-size: 4vh;
  border-radius: 2vh;
  border: 0.5vh solid #ccc;
  padding: 1vh 2.5vh;
}

body.landscape #level {
  font-size: 3.8vh;
}

body.landscape #record {
  font-size: 2.8vh;
}

.piece {
  width: 50%;
  height: 50%;
  display: inline-flex;
  cursor: pointer;
}

button:active,
.piece:active {
  transform: scale(0.9);
}


/* green */

.piece:nth-child(1) {
  border-radius: 100% 0 0 0;
  background-color: #9BC53D;
}

.piece:nth-child(1):active {
  transform-origin: bottom right;
}


/* blue */

.piece:nth-child(2) {
  border-radius: 0 100% 0 0;
  background-color: #00A8E8;
}

.piece:nth-child(2):active {
  transform-origin: bottom left;
}


/* orange */

.piece:nth-child(3) {
  border-radius: 0 0 0 100%;
  background-color: #FF9F1C;
}

.piece:nth-child(3):active {
  transform-origin: top right;
}


/* red */

.piece:nth-child(4) {
  border-radius: 0 0 100% 0;
  background-color: #E71D36;
}

.piece:nth-child(4):active {
  transform-origin: top left;
}


/* black */

.piece.fail {
  background-color: #50514F;
  content: "sdf";
}

.piece.blink {
  animation: 1s blink ease-in-out...

JavaScript

const $button_ = document.querySelector('button');
const $level_ = document.querySelector('#level>span');
const $pie_ = document.querySelector('#pie');
const $$pieces_ = document.querySelectorAll('.piece');
const $record_ = document.querySelector('#record');
let pieces_ = [];

// No taps allow before start the game
$pie_.classList.add('avoid-tap');

/**
 * Generate next piece (number)
 * 1: green, 2: blue, 3: orange, 4: red
 */
function get_random_piece() { // generate a random number between 4 and 1
  return Math.floor(Math.random() * (4)) + 1;
}

/**
 * Show the sequence you should follow
 */
function show_sequence() {
  let i = 0,
    e;

  $pie_.classList.add('avoid-tap');

  (function blink() {
    e = document.getElementById(pieces_[i]);

    e.classList.remove('blink'); // remove class
    void e.offsetWidth; // "restart" -> triggering reflow
    e.classList.add('blink'); // show animation

    i++;

    if (i < pieces_.length) { // if there are more pieces
      setTimeout(blink, 1000); // wait 1s before show next one
    } else {
      setTimeout(function() { // wait 1s before allow user to tap 
        $pie_.classList.remove('avoid-tap');
      }, 1000);
    }
  })();
}

/**
 * Check sequence
 */
let check_sequence = function() {
  let i = 0;

  return function(piece) {
    if (typeof(piece) === 'undefined') { // reset
      i = 0;
    } else { // play
      if (piece === pieces_[i]) { // success
        i++;
        // Keep playing
        if (i >= pieces_.length) { // we have guessed all
          $level_.textContent = pieces_.length; // set level before add
          pieces_.push(get_random_piece()); // add one more
          show_sequence();
          i = 0; // check from the beggining
        }
      } else {
   
        $level_.textContent = '☹ you failed';
        document.getElementById(piece).classList.add('fail');
        $pie_.classList.add('avoid-tap');
        i = 0;
        // Save user level if there is an improvement
        if...