├── .DS_Store ├── README.md ├── css ├── .DS_Store └── style.css ├── img ├── .DS_Store └── what-the-hex.png ├── js └── memory.js └── memory.html /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richie-edwards/memory-game/38da742ff2829dd2b0e2164261089063f31dd3f2/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Enjoy! This is a grid memory game memory that was created using javascript, html, and css. 3 | 4 | ## Rules and Game Components 5 | **Winning**: Successfully match all cards in the quickest time possible and with the least amount of errors (mis-matches). 6 | **Star Rating**: Try to finish the matching game with the most possible stars. A player loses a star every 3 failed attempts. 7 | **Moves**: Every match or failed match is counted as a move. 8 | **Timer**: The timer is shown in **seconds** and starts as soon as the first card is clicked. 9 | **Reset Game**: To reset the game and start fresh, simple click the reset icon. 10 | 11 | -------------------------------------------------------------------------------- /css/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richie-edwards/memory-game/38da742ff2829dd2b0e2164261089063f31dd3f2/css/.DS_Store -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | 5 | *, 6 | *::before, 7 | *::after { 8 | box-sizing: inherit; 9 | } 10 | 11 | html, 12 | body { 13 | width: 100%; 14 | height: 100%; 15 | margin: 0; 16 | padding: 0; 17 | } 18 | 19 | body { 20 | background: #ffffff url('../img/what-the-hex.png'); /* Background pattern from Subtle Patterns */ 21 | font-family: 'Coda', cursive; 22 | } 23 | 24 | .container { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-direction: column; 29 | } 30 | 31 | h1 { 32 | font-family: 'Open Sans', sans-serif; 33 | font-weight: 300; 34 | } 35 | 36 | /* 37 | * Styles for the deck of cards 38 | */ 39 | 40 | .deck { 41 | width: 660px; 42 | min-height: 680px; 43 | background: linear-gradient(160deg, #5A7DDF 0%, #ACDCD9 100%); 44 | padding: 32px; 45 | border-radius: 10px; 46 | box-shadow: 12px 15px 20px 0 rgba(46, 61, 73, 0.5); 47 | display: flex; 48 | flex-wrap: wrap; 49 | justify-content: space-between; 50 | align-items: center; 51 | margin: 0 0 3em; 52 | } 53 | 54 | .deck .card { 55 | height: 125px; 56 | width: 125px; 57 | background: #2e3d49; 58 | font-size: 0; 59 | color: #ffffff; 60 | border-radius: 8px; 61 | cursor: pointer; 62 | display: flex; 63 | justify-content: center; 64 | align-items: center; 65 | box-shadow: 5px 2px 20px 0 rgba(46, 61, 73, 0.5); 66 | } 67 | 68 | .deck .card.open { 69 | transform: rotateY(0); 70 | background: #02b3e4; 71 | cursor: default; 72 | } 73 | 74 | .deck .card.show { 75 | font-size: 33px; 76 | } 77 | 78 | .deck .card.match { 79 | cursor: default; 80 | background: #02ccba; 81 | font-size: 33px; 82 | } 83 | 84 | /* 85 | * Styles for the Score Panel 86 | */ 87 | 88 | .score-panel { 89 | text-align: left; 90 | width: 345px; 91 | margin-bottom: 10px; 92 | } 93 | 94 | .score-panel .stars { 95 | margin: 0; 96 | padding: 0; 97 | display: inline-block; 98 | margin: 0 5px 0 0; 99 | } 100 | 101 | .score-panel .stars li { 102 | list-style: none; 103 | display: inline-block; 104 | } 105 | 106 | .score-panel .restart { 107 | float: right; 108 | cursor: pointer; 109 | } 110 | 111 | #timer { 112 | margin-left: 10px; 113 | } 114 | 115 | .moves { 116 | margin-left: 10px; 117 | } 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richie-edwards/memory-game/38da742ff2829dd2b0e2164261089063f31dd3f2/img/.DS_Store -------------------------------------------------------------------------------- /img/what-the-hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richie-edwards/memory-game/38da742ff2829dd2b0e2164261089063f31dd3f2/img/what-the-hex.png -------------------------------------------------------------------------------- /js/memory.js: -------------------------------------------------------------------------------- 1 | 2 | let cardNodeList = document.querySelectorAll('.card'); 3 | let cards = [...cardNodeList]; 4 | let activeCards = []; 5 | let exposedCards = []; 6 | let pendingCard; 7 | let currentCard; 8 | let totalMoves = 0; 9 | let maxFailedMoves = 3; 10 | let failedMoves = 0; 11 | let stars = document.querySelectorAll('fa-star'); 12 | 13 | let resetButton = document.querySelector('.fa-repeat'); 14 | let intervalId; 15 | let clickCount = 0; 16 | 17 | document.body.onload = startGame(); 18 | 19 | // starts game and also used to reset 20 | function startGame(){ 21 | // game board 22 | const gameBoard = document.querySelector(".deck"); 23 | 24 | // shuffle cards 25 | shuffledCards = shuffle(cards); 26 | shuffledCards.forEach(function(card){ 27 | gameBoard.appendChild(card); 28 | }); 29 | 30 | resetTotalMoves(); 31 | resetTimer(); 32 | 33 | //fill empty stars 34 | let stars = getAllStars(); 35 | stars.forEach(function(element, index) { 36 | element.classList.remove('fa-star-o'); 37 | }); 38 | 39 | // cover cards 40 | cards.forEach(function(card){ 41 | coverCard(card); 42 | }); 43 | } 44 | 45 | // Shuffle function from http://stackoverflow.com/a/2450976 46 | function shuffle(array) { 47 | var currentIndex = array.length, temporaryValue, randomIndex; 48 | while (currentIndex !== 0) { 49 | randomIndex = Math.floor(Math.random() * currentIndex); 50 | currentIndex -= 1; 51 | temporaryValue = array[currentIndex]; 52 | array[currentIndex] = array[randomIndex]; 53 | array[randomIndex] = temporaryValue; 54 | } 55 | 56 | return array; 57 | } 58 | 59 | // wanted to win game automtically to test code 60 | function winGame() { 61 | for (var i = 0; i < cards.length; i++) { 62 | cards[i].click(); 63 | for (var j = i + 1; j < cards.length; j++) { 64 | 65 | } 66 | } 67 | } 68 | 69 | function showCard(card) { 70 | card.classList.add('open'); 71 | card.classList.add('show'); 72 | exposedCards.push(card); 73 | } 74 | 75 | function coverCard(card) { 76 | card.classList.remove('show'); 77 | card.classList.remove('open'); 78 | let cardIndex = exposedCards.indexOf(card); 79 | exposedCards.splice(cardIndex, 1); 80 | } 81 | 82 | function resetGame(){ 83 | //startGame(); 84 | location.reload(true); 85 | } 86 | 87 | function startTimer(){ 88 | let time = 0; 89 | intervalId = window.setInterval(function(){ 90 | time++; 91 | let timer = document.getElementById('timer') 92 | timer.innerText = time; 93 | }, 1000); 94 | 95 | } 96 | 97 | function stopTimer(){ 98 | clearInterval(intervalId); 99 | } 100 | 101 | function resetTimer(){ 102 | clearInterval(intervalId); 103 | document.getElementById('timer').innerText = 0; 104 | } 105 | 106 | function checkForMatches(card){ 107 | let myCard = card; 108 | if (pendingCard != undefined) { 109 | currentCard = myCard; 110 | 111 | //increase totalMoves by 1 112 | increaseMoves(); 113 | 114 | //add new card to activeCards array 115 | activeCards.push(currentCard); 116 | 117 | // get class of current card 118 | let typeOfPendingCard = pendingCard.getAttribute('title'); 119 | let typeOfCurrentCard = currentCard.getAttribute('title'); 120 | 121 | // are cards the same type? 122 | if (typeOfCurrentCard == typeOfPendingCard) { 123 | pendingCard.classList.add('match'); 124 | currentCard.classList.add('match'); 125 | activeCards.forEach(function(currentCard){ 126 | removeListener(currentCard, 'click', validateCard); 127 | }); 128 | } 129 | else{ 130 | failTurn(currentCard); 131 | } 132 | resetActiveAndPendingCards(); 133 | } 134 | else{ 135 | pendingCard = myCard; 136 | removeListener(pendingCard, 'click', validateCard); 137 | activeCards.push(pendingCard); 138 | } 139 | } 140 | 141 | function checkForWinOrLoss(){ 142 | let coveredCards = document.querySelectorAll('li.card:not(.show)'); 143 | if (coveredCards.length == 0) 144 | { 145 | let response = confirm("You win! \n# of Moves: " + totalMoves + " \nTotal Time: " + document.getElementById('timer').innerText + " seconds,\nStar Rating: " + getStarRating() + "\nWould you like to play again?"); 146 | if (response) { 147 | resetGame(); 148 | } 149 | else { 150 | stopTimer(); 151 | } 152 | } 153 | } 154 | 155 | function failTurn(card){ 156 | failedMoves++; 157 | activeCards.forEach(function(card){ 158 | window.setTimeout(function(){ 159 | coverCard(card) 160 | }, 1000); 161 | addListener(card, 'click', validateCard) 162 | }); 163 | // remove a star every 3rd failed move 164 | if (failedMoves % 3 == 0) { 165 | removeStar(); 166 | } 167 | } 168 | 169 | function removeStar() { 170 | let emptyStarClass = 'fa-star-o'; 171 | let stars = getAllStars(); 172 | let emptiedStars = getEmptiedStarsCount(); 173 | 174 | switch(emptiedStars) { 175 | case 0: 176 | stars[2].classList.add(emptyStarClass); 177 | break; 178 | case 1: 179 | stars[1].classList.add(emptyStarClass); 180 | break; 181 | case 2: 182 | stars[0].classList.add(emptyStarClass); 183 | break; 184 | } 185 | 186 | } 187 | 188 | function getAllStars() { 189 | let stars = document.querySelectorAll('.fa-star'); 190 | return stars; 191 | } 192 | 193 | function getEmptiedStarsCount() { 194 | let emptiedStars = document.querySelectorAll('.fa-star-o'); 195 | return emptiedStars.length; 196 | } 197 | 198 | 199 | function getStarRating() { 200 | let totalRating = getAllStars().length - getEmptiedStarsCount(); 201 | return totalRating; 202 | } 203 | 204 | function increaseMoves(){ 205 | totalMoves += 1; 206 | let totalMovesElement = getMovesElement(); 207 | totalMovesElement.innerText = totalMoves; 208 | } 209 | 210 | function resetTotalMoves(){ 211 | totalMoves = 0; 212 | failedMoves = 0; 213 | clickCount = 0; 214 | let totalMovesElement = getMovesElement(); 215 | totalMovesElement.innerText = totalMoves; 216 | } 217 | 218 | function getMovesElement(){ 219 | let totalMovesElement = document.querySelector('.moves'); 220 | return totalMovesElement; 221 | } 222 | 223 | function resetActiveAndPendingCards(){ 224 | activeCards = []; 225 | pendingCard = undefined; 226 | } 227 | 228 | // starts timer, keeps track of clicks and shows card 229 | let openCard = function(e){ 230 | clickCount++; 231 | if (clickCount == 1){ 232 | startTimer(); 233 | } 234 | showCard(e.target); 235 | } 236 | 237 | let validateCard = function(e){ 238 | checkForMatches(e.target); 239 | checkForWinOrLoss(); 240 | } 241 | 242 | // Event listeners 243 | function addListener(card, event, func) { 244 | card.addEventListener(event, validateCard); 245 | } 246 | 247 | function removeListener(card, event, func){ 248 | card.removeEventListener(event, func); 249 | } 250 | 251 | for (let i = 0; i < cards.length; i++){ 252 | cards[i].addEventListener('click', openCard); 253 | cards[i].addEventListener('click', validateCard); 254 | } 255 | 256 | resetButton.addEventListener('click', resetGame); 257 | -------------------------------------------------------------------------------- /memory.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |