Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save linuxenko/100ccbedca95d6f4739c to your computer and use it in GitHub Desktop.

Select an option

Save linuxenko/100ccbedca95d6f4739c to your computer and use it in GitHub Desktop.
Build a Simon Game [freeCodeCamp [Advanced Projects]] (Challenge)

Build a Simon Game [freeCodeCamp [Advanced Projects]] (Challenge)

User Story: I am presented with a random series of button presses.

User Story: Each time I input a series of button presses correctly, I see the same series of button presses but with an additional step.

User Story: I hear a sound that corresponds to each button both when the series of button presses plays, and when I personally press a button.

User Story: 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.

User Story: I can see how many steps are in the current series of button presses.

User Story: If I want to restart, I can hit a button to do so, and the game will return to a single step.

User Story: 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.

User Story: I can win the game by getting a series of 20 steps correct. I am notified of my victory, then the game starts over.

Hint: Here are mp3s you can use for each button: https://s3.amazonaws.com/freecodecamp/simonSound1.mp3, https://s3.amazonaws.com/freecodecamp/simonSound2.mp3, https://s3.amazonaws.com/freecodecamp/simonSound3.mp3, https://s3.amazonaws.com/freecodecamp/simonSound4.mp3.

A Pen by Svetlana Linuxenko on CodePen.

License.

<div class="simon-container" ng-app="simonApp">
<div class="simon-badge" ng-controller="simonCtrl">
<div class="board">
<div class="button" play-btn data-color="green"></div>
<div class="button" play-btn data-color="red"></div>
<div class="button" play-btn data-color="yellow"></div>
<div class="button" play-btn data-color="blue"></div>
<div class="center text-center">
<div class="title invisible" ng-class="{visible: gameVisible}">Simon Game</div>
<div class="display invisible" ng-class="{visible: gameVisible}">
{{display}}
</div>
<div class="controls invisible" ng-class="{visible: gameVisible}">
<div class="pull-left">
<div>GAME</div>
<div class="switchable" data-name="game" data-selected="off"></div>
</div>
<div class="pull-right">
<div>STRICT</div>
<div class="switchable" data-name="strict" data-selected="off"></div>
</div>
</div>
</div>
</div>
</div>
<div class="copy"><a href="http://www.linuxenko.pro">&copy; Svetlana Linuxenko</a></div>
</div>
playAlert.content['blue'] = 'https://s3.amazonaws.com/freecodecamp/simonSound1.mp3';
playAlert.content['green'] = 'https://s3.amazonaws.com/freecodecamp/simonSound2.mp3';
playAlert.content['red'] = 'https://s3.amazonaws.com/freecodecamp/simonSound3.mp3';
playAlert.content['yellow'] = 'https://s3.amazonaws.com/freecodecamp/simonSound4.mp3';
playAlert.content['rrr'] = 'https://cdn.rawgit.com/linuxenko/linuxenko.github.io/master/showcase/freecodecamp/alarm3.wav';
playAlert.content['win'] = 'https://cdn.rawgit.com/linuxenko/linuxenko.github.io/master/showcase/freecodecamp/win.wav';
var app = angular.module('simonApp', []);
app.directive('switchable', function() {
return {
restrict: 'C',
link : function(scope, element, attrs) {
element.on('click', function() {
if (attrs.selected === 'off') {
attrs.$set('selected', 'on');
} else {
attrs.$set('selected', 'off');
}
scope.control(attrs.name, attrs.selected);
});
},
template : '<span class="slide"></span>'
}
});
app.directive('playBtn', function() {
return {
restrict : 'A',
link : function(scope, element, attrs) {
function click(delay) {
if (scope.playing !== true) { return; }
playAlert(attrs.color);
element.addClass('animate');
setTimeout(function() {
element.removeClass('animate');
}, delay || 1000);
}
scope.buttons[attrs.color] = {
click : click
};
element.on('click', function() {
if (scope.feedback === true) {
scope.onClick(attrs.color);
click(300);
playAlert('s2');
}
});
}
}
});
app.controller('simonCtrl', function($scope, $q) {
$scope.gameVisible = true; // codepen trick
$scope.display = '-';
$scope.playing = false;
$scope.strict = false;
$scope.feedback = false;
$scope.level = 1;
$scope.buttons = { blue: {}, red : {}, yellow : {}, green : {}};
$scope.lastSequence = [];
function createSequence() {
var s = [];
var colors = Object.keys($scope.buttons);
var l = $scope.level < 4 ? $scope.level : 3 + Math.round(Math.random() * 3); // make easier ))
for (var i = 0; i < l; i++) {
s.push(colors[Math.round(Math.random() * 3)]);
}
return s;
}
function playColor(color) {
return $q(function(resolve) {
setTimeout(function() {
$scope.buttons[color].click();
resolve();
}, 2000);
});
}
function gameDelay() {
return $q(function(resolve) {
setTimeout(function() {
resolve();
}, 1000);
});
}
function playSequence(seq) {
return seq.reduce(function(p,c) {
return p.then(function() { return playColor(c); });
}, $q(function(resolve){resolve();}));
}
function startLevel(level) {
$scope.level = level;
$scope.display = '' + $scope.level;
$scope.lastSequence = createSequence();
$scope.feedback = false;
//$scope.$apply();
gameDelay().then(function() {
playAlert('win');
playSequence($scope.lastSequence).then(function() {
gameDelay().then(function() {
$scope.feedback = true;
});
});
});
}
function resetGame() {
$scope.display = '!';
$scope.level = 1;
$scope.feedback = false;
//$scope.$apply();
}
function isWrongAnswer(color) {
return $scope.lastSequence[0] !== color;
}
$scope.onClick = function(color) {
if (isWrongAnswer(color) === false) {
$scope.lastSequence.shift();
if ($scope.lastSequence.length === 0) {
startLevel($scope.level + 1);
}
} else {
$scope.feedback = false;
gameDelay().then(function() {
playAlert('rrr');
if ($scope.strict === true) {
resetGame();
startLevel(1);
} else {
startLevel($scope.level);
}
});
}
}
$scope.control = function(name, val) {
if (name === 'game') {
$scope.playing = val === 'on' ? true : false;
if (val === 'on') {
startLevel(1);
} else {
resetGame();
$scope.display = '-';
}
}
if (name === 'strict') {
$scope.strict = val === 'on' ? true : false;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script src="https://rawgit.com/azer/alert/master/dist/alert.js"></script>
@import url(https://fonts.googleapis.com/css?family=Roboto);
body,html,.simon-container {
width: 100%;
height: 100%;
font-family: 'Roboto', sans-serif;
}
.simon-badge {
margin: auto;
}
.simon-container {
display: flex;
flex-direction: column;
.copy {
display: flex;
align-items: flex-end;
justify-content: center;
padding: 10px 0px;
}
}
.board {
position: relative;
width: 472px;
height: 472px;
padding: 16px;
border-radius: 50%;
box-shadow: 0px 0px 10px #444;
background: #333;
.center {
position: absolute;
width: 236px;
height: 236px;
left: 118px;
top: 118px;
background: #fff;
border-radius: 50%;
border: 18px solid #333;
.display {
width: 80px;
height: 28px;
margin: 20px auto;
background: #333;
color: red;
font-weight: bold;
font-size: 18px;
line-height: 28px;
text-shadow: 0px 0px 1px #111;
}
.title {
font-size: 18px;
font-weight: bold;
margin-top: 24px;
}
.controls {
padding: 0px 28px;
}
}
.button {
float: left;
position: relative;
margin: 10px;
width: 200px;
height: 200px;
background: #eee;
cursor: pointer;
overflow: hidden;
transition: opacity 0.1s ease;
//border: 2px solid #924da3;
&.animate {
&:before {
content : ' ';
display: block;
width: 100%;
height: 100%;
position: absollute;
top: 0px;
left: 0px;
background: radial-gradient(#fff, transparent);
opacity: 1;
transition: opacity 0.3s ease;
}
}
&[data-color=green] {
background: radial-gradient(#47C300, #37C300);
}
&[data-color=red] {
background: radial-gradient(#fE424a, #DE4949);
}
&[data-color=blue] {
background: radial-gradient(#2695DC, #0695fC);
}
&[data-color=yellow] {
background: radial-gradient(#D6c934, #DED944);
}
&:nth-child(1) {
border-top-left-radius: 100%;
}
&:nth-child(2) {
border-top-right-radius: 100%;
}
&:nth-child(3) {
border-bottom-left-radius: 100%;
}
&:nth-child(4) {
border-bottom-right-radius: 100%;
}
}
}
.switchable {
display: inline-block;
font-size: 14px;
font-weight: normal;
.slide {
display: inline-block;
position: relative;
margin: 0px 4px;
top: 5px;
width: 60px;
height: 20px;
box-shadow: inset 0px 0px 6px #aaa;
cursor: pointer;
&:after {
content: 'Off';
text-transform: uppercase;
position: absolute;
height: 16px;
width: 24px;
top: 2px;
left: 34px;
font-size: 12px;
box-shadow: 0px 0px 4px #444;
border-radius: 2px;
font-weight: bold;
color: #eee;
font-size: 9px;
line-height: 16px;
background: linear-gradient(#cE424a, #c6424a);
}
}
&[data-selected="on"] {
.slide:after{
left: 0px;
content: 'On';
background: linear-gradient(#0695DC, #0095fC);
}
}
}
.invisible {
visibility: hidden;
}
.visible {
visibility: visible;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment