From 8f033c3355754de18aaaf16233329461ff0a7ab3 Mon Sep 17 00:00:00 2001 From: liztanyl Date: Sat, 15 Jan 2022 10:40:31 +0800 Subject: [PATCH 1/6] base from match game, ends when timer hits 00:00 --- index.html | 20 +-- script.js | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++- styles.css | 71 ++++++++++- 3 files changed, 428 insertions(+), 11 deletions(-) diff --git a/index.html b/index.html index 4771b50..6ad2a55 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,15 @@ - - Timer - - + + + + Liz's Card Match Game (Stopwatch version) + - -

Timer!

- - - + +

Liz's Card Match Game

+

(Stopwatch version)

+ + + diff --git a/script.js b/script.js index e2d0297..555f5eb 100644 --- a/script.js +++ b/script.js @@ -1 +1,347 @@ -// Please implement exercise logic here +// ----- GLOBAL VARIABLES ----------------------- +const boardSize = 4; +const board = []; + +let deck; +let firstCard = null; +let firstCardElement; + +// For gameplay +let canClick = true; +let gameCompleted = false; + +// For timer +let milliseconds = 180000; // 3 minutes (1 min = 60 000ms) +const delayInMilliseconds = 100; // 0.1 second +let timerStarted = false; +let timerRef; + +// For game information +const gameInfoContainer = document.createElement('div'); +const gameInfo = document.createElement('div'); +const timerContainer = document.createElement('div'); +const timer = document.createElement('div'); + +// ----- HELPER FUNCTIONS ----------------------- +// Get a random index ranging from 0 (inclusive) to max (exclusive). +const getRandomIndex = (max) => Math.floor(Math.random() * max); + +// Create deck +const makeDeck = () => { + const newDeck = []; + const suits = ['hearts', 'diamonds', 'clubs', 'spades']; + const suitSymbols = ['♥️', '♦️', '♣️', '♠️']; + const cardName = [ + 'A', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + 'J', + 'Q', + 'K', + ]; + const cardRank = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; + + // Loop over the suits array + for (let suitIndex = 0; suitIndex < suits.length; suitIndex += 1) { + // Store the current suit in a variable + const currentSuit = suits[suitIndex]; + + for (let i = 0; i < 13; i += 1) { + // Set suit color + let suitColor = 'black'; + if (currentSuit === 'hearts' || currentSuit === 'diamonds') { + suitColor = 'red'; + } + + // Create a new card with the current name, suit, and rank + const card = { + name: cardName[i], + suit: currentSuit, + symbol: suitSymbols[suitIndex], + color: suitColor, + rank: cardRank[i], + }; + + // Add the new card to the deck + newDeck.push(card); + newDeck.push(card); + } + } + + // Return the completed card deck + return newDeck; +}; + +// Shuffle cards +const shuffleCards = (cards) => { + // Loop over the card deck array once + for (let currentIndex = 0; currentIndex < cards.length; currentIndex += 1) { + // Select a random index in the deck + const randomIndex = getRandomIndex(cards.length); + // Select the card that corresponds to randomIndex + const randomCard = cards[randomIndex]; + // Select the card that corresponds to currentIndex + const currentCard = cards[currentIndex]; + // Swap positions of randomCard and currentCard in the deck + cards[currentIndex] = randomCard; + cards[randomIndex] = currentCard; + } + // Return the shuffled deck + return cards; +}; + +// Format open cards +const formatOpenCard = (cardDiv, card) => { + cardDiv.innerText = `${card.name}${card.symbol}`; + if (card.symbol === '♥️' || card.symbol === '♦️') { + cardDiv.classList.add('red'); + } + cardDiv.classList.add('open-card'); +}; + +// Format timer +const formatTimer = (ms) => { + // Show min:sec + // calculate minutes + let min = Math.floor((ms / 1000 / 60) % 60); + // calculate seconds + let sec = Math.floor((ms / 1000) % 60); + + // add leading 0 + if (min < 10) { + min = '0' + min; + } + if (sec < 10) { + sec = '0' + sec; + } + return `${min}:${sec}`; +}; + +const startTimer = () => { + timerRef = setInterval(() => { + if (milliseconds <= 0) { + clearInterval(timerRef); + updateGameInfo(`Time's up! You lose.`); + canClick = false; + } + + timer.innerHTML = formatTimer(milliseconds); + milliseconds -= delayInMilliseconds; + }, delayInMilliseconds); +}; + +const stopTimer = () => { + clearInterval(timerRef); + updateGameInfo( + `Congrats, you matched all the cards!
Refresh the page to play again.` + ); + canClick = false; +}; + +const areAllCardsOpen = () => { + const numOfOpenCards = document.querySelectorAll('.open-card'); + if (numOfOpenCards.length === 16) { + return true; + } + return false; +}; + +// ----- GAMEPLAY LOGIC ------------------------- + +// What happens when user clicks on a square +const openCard = (cardElement, row, column) => { + // Start timer on first ever card clicked + if (timerStarted === false) { + startTimer(); + timerStarted = true; + } + + // Store the clicked card + const clickedCard = board[row][column]; + + // If this card is already open (user has already clicked this square) + // Or setTimeout is running + if (cardElement.innerText !== '' || canClick === false) { + return; + } + + // First turn + if (firstCard === null) { + // Set the firstCard to the card that was clicked + firstCard = clickedCard; + + // "Turn the card over" by showing the card name in the square + formatOpenCard(cardElement, clickedCard); + + // Hold on to this first in case second card doesn't match + firstCardElement = cardElement; + + // Update game info + updateGameInfo(`Great, now find its match!`); + } + + // Second turn + else { + canClick = false; + + // If it's a match + if ( + clickedCard.name === firstCard.name && + clickedCard.suit === firstCard.suit + ) { + // "Turn the card over" by showing the card name in the square + + formatOpenCard(cardElement, clickedCard); + + // Check if all cards are open + if (areAllCardsOpen() === true) { + stopTimer(); + return; + } + + // If not all cards have been open, update game info + else { + updateGameInfo(`Noice, it's a match!`); + if (milliseconds >= 2100) { + setTimeout(() => { + updateGameInfo(`Click a card to continue.`); + }, 2000); + } + + canClick = true; + } + } + + // If it's not a match + else { + // "Open cards" by showing the card name in the square and adding the relevant classes + + formatOpenCard(cardElement, clickedCard); + + // "Turn cards over" after a set time + setTimeout(() => { + // "Turn cards over" by removing card name in square + cardElement.innerText = ``; + firstCardElement.innerText = ``; + + cardElement.classList.remove('open-card', 'red', 'black'); + firstCardElement.classList.remove('open-card', 'red', 'black'); + + if (milliseconds >= 2000) { + updateGameInfo(`Click to open a card.`); + } + canClick = true; + }, 1500); + + // Update game info + updateGameInfo(`Sorry, those didn't match. Try again!`); + } + + // Reset the cards + firstCard = null; + } +}; + +// ----- GAME INITIALISATION -------------------- + +// Create container for timer +const createTimerContainer = () => { + timerContainer.classList.add('timer-container'); + timerContainer.innerHTML = `

You have 3 minutes to match all card pairs, starting from when you open your first card.

`; + document.body.appendChild(timerContainer); + + timer.classList.add('timer'); + timer.innerHTML = formatTimer(milliseconds); + timerContainer.appendChild(timer); +}; + +// Create container for game info +const createGameInfoContainer = () => { + gameInfoContainer.classList.add('game-info-container'); + gameInfo.classList.add('game-info'); + + gameInfo.innerHTML = `Click on the squares to match cards.`; + + gameInfoContainer.appendChild(gameInfo); + document.body.appendChild(gameInfoContainer); +}; + +const updateGameInfo = (msgText) => { + gameInfo.innerHTML = msgText; + gameInfoContainer.appendChild(gameInfo); +}; + +// Create container for board elements +const createBoardContainer = (board) => { + // Create main container + const boardContainer = document.createElement('div'); + boardContainer.classList.add('board-container'); + + // Create the board grid with 2 loops ------ + // First for row and second for column + for (let i = 0; i < board.length; i += 1) { + // Create variable to hold cards in this row + const row = board[i]; + + // Create div for the row + const rowDiv = document.createElement('div'); + rowDiv.classList.add('row'); + + // Start second loop -------- + // to create the columns (cards / squares) in the row + for (let j = 0; j < row.length; j += 1) { + // Create the square (card) + const square = document.createElement('div'); + square.classList.add('square'); + + // Add event listener to the square + square.addEventListener('click', (e) => { + openCard(e.currentTarget, i, j); + }); + + // Append the square to the row + rowDiv.appendChild(square); + } + + // Append row to the board + boardContainer.appendChild(rowDiv); + } + document.body.appendChild(boardContainer); +}; + +// Game initialisation +const initGame = () => { + // Prepare the deck ---------- + // Create a deck with twice the number of cards + let doubleDeck = makeDeck(); + + // Select enough to make a smaller deck + let deckSubset = doubleDeck.slice(0, boardSize * boardSize); + + // Shuffle the cards + deck = shuffleCards(deckSubset); + + // Deal cards to the board data structure (nested array) ----- + for (let i = 0; i < boardSize; i += 1) { + // Create the array for each row + board.push([]); + + // Deal the cards per row + for (let j = 0; j < boardSize; j += 1) { + board[i].push(deck.pop()); + } + } + + createTimerContainer(); + createGameInfoContainer(); + createBoardContainer(board); +}; + +initGame(); diff --git a/styles.css b/styles.css index 04e7110..7132be1 100644 --- a/styles.css +++ b/styles.css @@ -1,3 +1,72 @@ body { - background-color: pink; + background-color: lavenderblush; + font-family: monospace; + text-align: center; +} + +h1 { + color: darkmagenta; +} + +h2 { + color: darkmagenta; +} + +.timer-container { + color: darkmagenta; + font-size: 1.2em; + background-color: plum; + border: 2px solid mediumorchid; + width: 50vw; + padding: 1em; + margin: 2em auto; +} + +.timer-container p { + margin-top: 0.5em; +} + +.timer { + font-size: 1.5em; +} + +.game-info-container { + color: darkmagenta; + font-size: 1.2em; + background-color: plum; + border: 2px solid mediumorchid; + height: 2.5em; + width: 50vw; + padding: 1em; + margin: 2em auto; +} + +.board-container { + background-color: plum; + border: 2px solid mediumorchid; + width: 50vw; + padding: 1em; + margin: 2em auto; +} + +.square { + padding: 10px; + margin: 10px; + background-color: darkmagenta; + display: inline-block; + height: 15px; + width: 15px; + vertical-align: top; + text-align: center; +} + +.open-card { + background-color: lavenderblush; +} + +.red { + color: crimson; +} +.black { + color: darkslategrey; } From a8b709118c05ec02d7dc53934ef366041cd93080 Mon Sep 17 00:00:00 2001 From: liztanyl Date: Sat, 15 Jan 2022 11:05:06 +0800 Subject: [PATCH 2/6] add working stopwatch, not linked to game yet --- script.js | 100 +++++++++++++++++++++++++++++++++++------------------ styles.css | 25 ++++++++++++-- 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/script.js b/script.js index 555f5eb..41f53b7 100644 --- a/script.js +++ b/script.js @@ -8,19 +8,24 @@ let firstCardElement; // For gameplay let canClick = true; -let gameCompleted = false; +// let gameCompleted = false; -// For timer -let milliseconds = 180000; // 3 minutes (1 min = 60 000ms) +// For stopwatch +let milliseconds = 0; const delayInMilliseconds = 100; // 0.1 second -let timerStarted = false; -let timerRef; +const maxMilliseconds = 180000; // 3 minutes (1 min = 60 000ms) +let stopwatchStarted = false; +let stopwatchRef; + +const stopwatch = document.createElement('div'); +const startBtn = document.createElement('button'); +const stopBtn = document.createElement('button'); +const resetBtn = document.createElement('button'); // For game information const gameInfoContainer = document.createElement('div'); const gameInfo = document.createElement('div'); -const timerContainer = document.createElement('div'); -const timer = document.createElement('div'); +const stopwatchContainer = document.createElement('div'); // ----- HELPER FUNCTIONS ----------------------- // Get a random index ranging from 0 (inclusive) to max (exclusive). @@ -106,8 +111,8 @@ const formatOpenCard = (cardDiv, card) => { cardDiv.classList.add('open-card'); }; -// Format timer -const formatTimer = (ms) => { +// Format stopwatch +const formatStopwatch = (ms) => { // Show min:sec // calculate minutes let min = Math.floor((ms / 1000 / 60) % 60); @@ -124,25 +129,37 @@ const formatTimer = (ms) => { return `${min}:${sec}`; }; -const startTimer = () => { - timerRef = setInterval(() => { - if (milliseconds <= 0) { - clearInterval(timerRef); +const startStopwatch = () => { + stopwatchRef = setInterval(() => { + if (milliseconds >= maxMilliseconds) { + clearInterval(stopwatchRef); updateGameInfo(`Time's up! You lose.`); canClick = false; } - timer.innerHTML = formatTimer(milliseconds); - milliseconds -= delayInMilliseconds; + stopwatch.innerHTML = formatStopwatch(milliseconds); + milliseconds += delayInMilliseconds; }, delayInMilliseconds); + startBtn.disabled = true; + stopBtn.disabled = false; }; -const stopTimer = () => { - clearInterval(timerRef); +const stopStopwatch = () => { + clearInterval(stopwatchRef); updateGameInfo( `Congrats, you matched all the cards!
Refresh the page to play again.` ); canClick = false; + startBtn.disabled = false; + stopBtn.disabled = true; +}; + +const resetStopwatch = () => { + clearInterval(stopwatchRef); + milliseconds = 0; + stopwatch.innerHTML = formatStopwatch(milliseconds); + startBtn.disabled = false; + stopBtn.disabled = true; }; const areAllCardsOpen = () => { @@ -157,11 +174,11 @@ const areAllCardsOpen = () => { // What happens when user clicks on a square const openCard = (cardElement, row, column) => { - // Start timer on first ever card clicked - if (timerStarted === false) { - startTimer(); - timerStarted = true; - } + // // Start timer on first ever card clicked + // if (timerStarted === false) { + // startTimer(); + // timerStarted = true; + // } // Store the clicked card const clickedCard = board[row][column]; @@ -202,7 +219,7 @@ const openCard = (cardElement, row, column) => { // Check if all cards are open if (areAllCardsOpen() === true) { - stopTimer(); + stopStopwatch(); return; } @@ -251,15 +268,32 @@ const openCard = (cardElement, row, column) => { // ----- GAME INITIALISATION -------------------- -// Create container for timer -const createTimerContainer = () => { - timerContainer.classList.add('timer-container'); - timerContainer.innerHTML = `

You have 3 minutes to match all card pairs, starting from when you open your first card.

`; - document.body.appendChild(timerContainer); - - timer.classList.add('timer'); - timer.innerHTML = formatTimer(milliseconds); - timerContainer.appendChild(timer); +// Create container for stopwatch +const createStopwatchContainer = () => { + // Format the container + stopwatchContainer.classList.add('stopwatch-container'); + stopwatchContainer.innerHTML = `

Time how long it takes for you to match all the cards. You can only flip cards over when the stopwatch is running.

`; + document.body.appendChild(stopwatchContainer); + + // Format the stopwatch + stopwatch.classList.add('stopwatch'); + stopwatch.innerHTML = formatStopwatch(milliseconds); + stopwatchContainer.appendChild(stopwatch); + + // Format the buttons + startBtn.innerText = 'Start'; + stopBtn.innerText = 'Stop'; + resetBtn.innerText = 'Reset'; + stopwatchContainer.appendChild(startBtn); + stopwatchContainer.appendChild(stopBtn); + stopwatchContainer.appendChild(resetBtn); + + stopBtn.disabled = true; + + // Add event listeners to buttons + startBtn.addEventListener('click', startStopwatch); + stopBtn.addEventListener('click', stopStopwatch); + resetBtn.addEventListener('click', resetStopwatch); }; // Create container for game info @@ -339,7 +373,7 @@ const initGame = () => { } } - createTimerContainer(); + createStopwatchContainer(); createGameInfoContainer(); createBoardContainer(board); }; diff --git a/styles.css b/styles.css index 7132be1..2c93164 100644 --- a/styles.css +++ b/styles.css @@ -12,7 +12,7 @@ h2 { color: darkmagenta; } -.timer-container { +.stopwatch-container { color: darkmagenta; font-size: 1.2em; background-color: plum; @@ -22,11 +22,30 @@ h2 { margin: 2em auto; } -.timer-container p { +.stopwatch-container p { margin-top: 0.5em; } +.stopwatch-container button { + font-family: monospace; + font-size: 1rem; + letter-spacing: 2px; + font-weight: bold; + text-transform: uppercase; + padding: 0.5rem; + margin: 0.5rem; + color: white; + background-color: orchid; + border-radius: 10%; + border-width: 2px; + border-style: solid; + border-color: lavender purple purple lavender; +} + +.stopwatch-container button:disabled { + background-color: lightgrey; +} -.timer { +.stopwatch { font-size: 1.5em; } From 49767fd9920ec33d46550d9054dbab3ac30c2d12 Mon Sep 17 00:00:00 2001 From: liztanyl Date: Sat, 15 Jan 2022 11:55:54 +0800 Subject: [PATCH 3/6] reset btn resets game in addition to stopwatch --- script.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/script.js b/script.js index 41f53b7..741eac7 100644 --- a/script.js +++ b/script.js @@ -7,13 +7,13 @@ let firstCard = null; let firstCardElement; // For gameplay -let canClick = true; +let canClick = false; // let gameCompleted = false; // For stopwatch let milliseconds = 0; const delayInMilliseconds = 100; // 0.1 second -const maxMilliseconds = 180000; // 3 minutes (1 min = 60 000ms) +const maxMilliseconds = 60000; // 3 minutes (1 min = 60 000ms) let stopwatchStarted = false; let stopwatchRef; @@ -130,6 +130,10 @@ const formatStopwatch = (ms) => { }; const startStopwatch = () => { + canClick = true; + startBtn.disabled = true; + stopBtn.disabled = false; + stopwatchRef = setInterval(() => { if (milliseconds >= maxMilliseconds) { clearInterval(stopwatchRef); @@ -140,8 +144,6 @@ const startStopwatch = () => { stopwatch.innerHTML = formatStopwatch(milliseconds); milliseconds += delayInMilliseconds; }, delayInMilliseconds); - startBtn.disabled = true; - stopBtn.disabled = false; }; const stopStopwatch = () => { @@ -160,6 +162,14 @@ const resetStopwatch = () => { stopwatch.innerHTML = formatStopwatch(milliseconds); startBtn.disabled = false; stopBtn.disabled = true; + + // Reset game + board.length = 0; + const bodyDivs = document.querySelectorAll('body > div'); + for (let i = 0; i < bodyDivs.length; i += 1) { + document.body.removeChild(bodyDivs[i]); + } + initGame(); }; const areAllCardsOpen = () => { @@ -174,12 +184,6 @@ const areAllCardsOpen = () => { // What happens when user clicks on a square const openCard = (cardElement, row, column) => { - // // Start timer on first ever card clicked - // if (timerStarted === false) { - // startTimer(); - // timerStarted = true; - // } - // Store the clicked card const clickedCard = board[row][column]; @@ -226,7 +230,7 @@ const openCard = (cardElement, row, column) => { // If not all cards have been open, update game info else { updateGameInfo(`Noice, it's a match!`); - if (milliseconds >= 2100) { + if (milliseconds <= 2100) { setTimeout(() => { updateGameInfo(`Click a card to continue.`); }, 2000); From 12b0aed02bbee36671c0afb8b79253da42f732a7 Mon Sep 17 00:00:00 2001 From: liztanyl Date: Sat, 15 Jan 2022 12:54:47 +0800 Subject: [PATCH 4/6] polish reset function --- script.js | 87 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/script.js b/script.js index 741eac7..127f9ac 100644 --- a/script.js +++ b/script.js @@ -8,12 +8,11 @@ let firstCardElement; // For gameplay let canClick = false; -// let gameCompleted = false; // For stopwatch let milliseconds = 0; const delayInMilliseconds = 100; // 0.1 second -const maxMilliseconds = 60000; // 3 minutes (1 min = 60 000ms) +const maxMilliseconds = 180000; // 3 minutes (1 min = 60 000ms) let stopwatchStarted = false; let stopwatchRef; @@ -26,6 +25,8 @@ const resetBtn = document.createElement('button'); const gameInfoContainer = document.createElement('div'); const gameInfo = document.createElement('div'); const stopwatchContainer = document.createElement('div'); +let timeoutMsgMatch; +let timeoutMsgNoMatch; // ----- HELPER FUNCTIONS ----------------------- // Get a random index ranging from 0 (inclusive) to max (exclusive). @@ -111,6 +112,32 @@ const formatOpenCard = (cardDiv, card) => { cardDiv.classList.add('open-card'); }; +// Close all cards that are open +const closeAllOpenCards = () => { + let openCardsList = document.querySelectorAll('div.open-card'); + for (let i = 0; i < openCardsList.length; i += 1) { + openCardsList[i].classList.remove('open-card', 'red', 'black'); + openCardsList[i].innerText = ``; + console.log(openCardsList[i]); + } +}; + +// Checks if all cards are open (game complete) +// Returns true || false +const areAllCardsOpen = () => { + const numOfOpenCards = document.querySelectorAll('.open-card'); + if (numOfOpenCards.length === 16) { + return true; + } + return false; +}; + +// Update game info +const updateGameInfo = (msgText) => { + gameInfo.innerHTML = msgText; + gameInfoContainer.appendChild(gameInfo); +}; + // Format stopwatch const formatStopwatch = (ms) => { // Show min:sec @@ -137,8 +164,18 @@ const startStopwatch = () => { stopwatchRef = setInterval(() => { if (milliseconds >= maxMilliseconds) { clearInterval(stopwatchRef); - updateGameInfo(`Time's up! You lose.`); + clearTimeout(timeoutMsgMatch); + clearTimeout(timeoutMsgNoMatch); + + updateGameInfo(`Time's up! You lose.
Hit reset to try again.`); + + // Find all open cards and remove the open-card class + closeAllOpenCards(); + canClick = false; + startBtn.disabled = true; + stopBtn.disabled = true; + resetBtn.disabled = false; } stopwatch.innerHTML = formatStopwatch(milliseconds); @@ -148,12 +185,17 @@ const startStopwatch = () => { const stopStopwatch = () => { clearInterval(stopwatchRef); - updateGameInfo( - `Congrats, you matched all the cards!
Refresh the page to play again.` - ); + canClick = false; startBtn.disabled = false; stopBtn.disabled = true; + + if (areAllCardsOpen() === true) { + updateGameInfo( + `Congrats, you matched all the cards!
Click reset to play again.` + ); + startBtn.disabled = true; + } }; const resetStopwatch = () => { @@ -162,9 +204,15 @@ const resetStopwatch = () => { stopwatch.innerHTML = formatStopwatch(milliseconds); startBtn.disabled = false; stopBtn.disabled = true; + canClick = false; // Reset game + resetGame(); +}; + +const resetGame = () => { board.length = 0; + firstCard = null; const bodyDivs = document.querySelectorAll('body > div'); for (let i = 0; i < bodyDivs.length; i += 1) { document.body.removeChild(bodyDivs[i]); @@ -172,14 +220,6 @@ const resetStopwatch = () => { initGame(); }; -const areAllCardsOpen = () => { - const numOfOpenCards = document.querySelectorAll('.open-card'); - if (numOfOpenCards.length === 16) { - return true; - } - return false; -}; - // ----- GAMEPLAY LOGIC ------------------------- // What happens when user clicks on a square @@ -231,7 +271,7 @@ const openCard = (cardElement, row, column) => { else { updateGameInfo(`Noice, it's a match!`); if (milliseconds <= 2100) { - setTimeout(() => { + timeoutMsgMatch = setTimeout(() => { updateGameInfo(`Click a card to continue.`); }, 2000); } @@ -246,8 +286,13 @@ const openCard = (cardElement, row, column) => { formatOpenCard(cardElement, clickedCard); + stopBtn.disabled = true; + resetBtn.disabled = true; // "Turn cards over" after a set time - setTimeout(() => { + timeoutMsgNoMatch = setTimeout(() => { + stopBtn.disabled = false; + resetBtn.disabled = false; + // "Turn cards over" by removing card name in square cardElement.innerText = ``; firstCardElement.innerText = ``; @@ -255,11 +300,8 @@ const openCard = (cardElement, row, column) => { cardElement.classList.remove('open-card', 'red', 'black'); firstCardElement.classList.remove('open-card', 'red', 'black'); - if (milliseconds >= 2000) { - updateGameInfo(`Click to open a card.`); - } canClick = true; - }, 1500); + }, 2000); // Update game info updateGameInfo(`Sorry, those didn't match. Try again!`); @@ -311,11 +353,6 @@ const createGameInfoContainer = () => { document.body.appendChild(gameInfoContainer); }; -const updateGameInfo = (msgText) => { - gameInfo.innerHTML = msgText; - gameInfoContainer.appendChild(gameInfo); -}; - // Create container for board elements const createBoardContainer = (board) => { // Create main container From 082f90e1716237049a16718b32b0327f63a67c71 Mon Sep 17 00:00:00 2001 From: liztanyl Date: Sat, 15 Jan 2022 13:32:21 +0800 Subject: [PATCH 5/6] add game rules, styles. done with comfortable --- script.js | 39 +++++++++++++++++++++++++++++++-------- styles.css | 30 +++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/script.js b/script.js index 127f9ac..cccf318 100644 --- a/script.js +++ b/script.js @@ -25,6 +25,7 @@ const resetBtn = document.createElement('button'); const gameInfoContainer = document.createElement('div'); const gameInfo = document.createElement('div'); const stopwatchContainer = document.createElement('div'); +const gameRulesDiv = document.createElement('div'); let timeoutMsgMatch; let timeoutMsgNoMatch; @@ -118,7 +119,6 @@ const closeAllOpenCards = () => { for (let i = 0; i < openCardsList.length; i += 1) { openCardsList[i].classList.remove('open-card', 'red', 'black'); openCardsList[i].innerText = ``; - console.log(openCardsList[i]); } }; @@ -163,19 +163,21 @@ const startStopwatch = () => { stopwatchRef = setInterval(() => { if (milliseconds >= maxMilliseconds) { + // Clear all intervals and timeouts clearInterval(stopwatchRef); clearTimeout(timeoutMsgMatch); clearTimeout(timeoutMsgNoMatch); - updateGameInfo(`Time's up! You lose.
Hit reset to try again.`); - // Find all open cards and remove the open-card class closeAllOpenCards(); + // Discontinue gameplay, only allow to Reset canClick = false; startBtn.disabled = true; stopBtn.disabled = true; resetBtn.disabled = false; + + updateGameInfo(`Time's up! You lose.
Hit reset to try again.`); } stopwatch.innerHTML = formatStopwatch(milliseconds); @@ -269,11 +271,11 @@ const openCard = (cardElement, row, column) => { // If not all cards have been open, update game info else { - updateGameInfo(`Noice, it's a match!`); + updateGameInfo(`Noice, it's a match!
Pick your next card.`); if (milliseconds <= 2100) { timeoutMsgMatch = setTimeout(() => { updateGameInfo(`Click a card to continue.`); - }, 2000); + }, 1500); } canClick = true; @@ -301,10 +303,10 @@ const openCard = (cardElement, row, column) => { firstCardElement.classList.remove('open-card', 'red', 'black'); canClick = true; - }, 2000); + }, 1500); // Update game info - updateGameInfo(`Sorry, those didn't match. Try again!`); + updateGameInfo(`Sorry, those didn't match.
Try again!`); } // Reset the cards @@ -316,9 +318,30 @@ const openCard = (cardElement, row, column) => { // Create container for stopwatch const createStopwatchContainer = () => { + // Format game instructions + stopwatchContainer.innerHTML = ``; + + let gameRules = [ + `Time how long it takes for you to match all the cards.`, + `You can only flip cards over when the stopwatch is running.`, + ` You have a maximum of 3 minutes to play.`, + ]; + + let header = document.createElement('h3'); + header.innerText = `How to play`; + + let ul = document.createElement('ul'); + for (let i = 0; i < gameRules.length; i += 1) { + let li = document.createElement('li'); + li.innerText = gameRules[i]; + ul.appendChild(li); + } + + stopwatchContainer.appendChild(header); + stopwatchContainer.appendChild(ul); + // Format the container stopwatchContainer.classList.add('stopwatch-container'); - stopwatchContainer.innerHTML = `

Time how long it takes for you to match all the cards. You can only flip cards over when the stopwatch is running.

`; document.body.appendChild(stopwatchContainer); // Format the stopwatch diff --git a/styles.css b/styles.css index 2c93164..3925580 100644 --- a/styles.css +++ b/styles.css @@ -12,6 +12,16 @@ h2 { color: darkmagenta; } +h3 { + text-align: left; + margin: 0.5em; +} + +li { + text-align: left; + margin-bottom: 0.5em; +} + .stopwatch-container { color: darkmagenta; font-size: 1.2em; @@ -22,9 +32,6 @@ h2 { margin: 2em auto; } -.stopwatch-container p { - margin-top: 0.5em; -} .stopwatch-container button { font-family: monospace; font-size: 1rem; @@ -34,19 +41,28 @@ h2 { padding: 0.5rem; margin: 0.5rem; color: white; - background-color: orchid; - border-radius: 10%; + background-color: mediumorchid; + border-radius: 5em; border-width: 2px; border-style: solid; - border-color: lavender purple purple lavender; + border-color: darkorchid; } .stopwatch-container button:disabled { - background-color: lightgrey; + color: thistle; + background-color: lavender; + border-color: thistle; } .stopwatch { font-size: 1.5em; + background-color: lavenderblush; + width: 10em; + margin: auto; + padding: 0.5em; + border-radius: 5em; + border: 2px solid mediumorchid; + box-sizing: border-box; } .game-info-container { From 76cbe06c975ef0310ae6ff06191e4e8cf4167f82 Mon Sep 17 00:00:00 2001 From: liztanyl Date: Mon, 17 Jan 2022 10:34:00 +0800 Subject: [PATCH 6/6] separate global variables to globals.js --- globals.js | 30 ++++++++++++++++++++++++++++++ index.html | 1 + script.js | 35 ++++------------------------------- 3 files changed, 35 insertions(+), 31 deletions(-) create mode 100644 globals.js diff --git a/globals.js b/globals.js new file mode 100644 index 0000000..3c8b474 --- /dev/null +++ b/globals.js @@ -0,0 +1,30 @@ +// ----- GLOBAL VARIABLES ----------------------- +const boardSize = 4; +const board = []; + +let deck; +let firstCard = null; +let firstCardElement; + +// For gameplay +let canClick = false; + +// For stopwatch +let milliseconds = 0; +const delayInMilliseconds = 100; // 0.1 second +const maxMilliseconds = 180000; // 3 minutes (1 min = 60 000ms) +let stopwatchStarted = false; +let stopwatchRef; + +const stopwatch = document.createElement('div'); +const startBtn = document.createElement('button'); +const stopBtn = document.createElement('button'); +const resetBtn = document.createElement('button'); + +// For game information +const stopwatchContainer = document.createElement('div'); +const gameRulesDiv = document.createElement('div'); +const gameInfoContainer = document.createElement('div'); +const gameInfo = document.createElement('div'); +let timeoutMsgMatch; +let timeoutMsgNoMatch; diff --git a/index.html b/index.html index 6ad2a55..40acce4 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@

Liz's Card Match Game

(Stopwatch version)

+ diff --git a/script.js b/script.js index cccf318..bd8843c 100644 --- a/script.js +++ b/script.js @@ -1,34 +1,3 @@ -// ----- GLOBAL VARIABLES ----------------------- -const boardSize = 4; -const board = []; - -let deck; -let firstCard = null; -let firstCardElement; - -// For gameplay -let canClick = false; - -// For stopwatch -let milliseconds = 0; -const delayInMilliseconds = 100; // 0.1 second -const maxMilliseconds = 180000; // 3 minutes (1 min = 60 000ms) -let stopwatchStarted = false; -let stopwatchRef; - -const stopwatch = document.createElement('div'); -const startBtn = document.createElement('button'); -const stopBtn = document.createElement('button'); -const resetBtn = document.createElement('button'); - -// For game information -const gameInfoContainer = document.createElement('div'); -const gameInfo = document.createElement('div'); -const stopwatchContainer = document.createElement('div'); -const gameRulesDiv = document.createElement('div'); -let timeoutMsgMatch; -let timeoutMsgNoMatch; - // ----- HELPER FUNCTIONS ----------------------- // Get a random index ranging from 0 (inclusive) to max (exclusive). const getRandomIndex = (max) => Math.floor(Math.random() * max); @@ -156,6 +125,7 @@ const formatStopwatch = (ms) => { return `${min}:${sec}`; }; +// Stopwatch functions: start the stopwatch const startStopwatch = () => { canClick = true; startBtn.disabled = true; @@ -185,6 +155,7 @@ const startStopwatch = () => { }, delayInMilliseconds); }; +// Stopwatch fuctions: stop the stopwatch const stopStopwatch = () => { clearInterval(stopwatchRef); @@ -200,6 +171,7 @@ const stopStopwatch = () => { } }; +// Stopwatch functions: reset the stopwatch const resetStopwatch = () => { clearInterval(stopwatchRef); milliseconds = 0; @@ -212,6 +184,7 @@ const resetStopwatch = () => { resetGame(); }; +// Reset game const resetGame = () => { board.length = 0; firstCard = null;