From 5adaf9d4c9515b5b6fe3527bfec35f02d344bcdf Mon Sep 17 00:00:00 2001 From: tabbiho Date: Sun, 23 Jan 2022 23:39:15 +0800 Subject: [PATCH 1/3] Match Game Countdown Timer completed --- package-lock.json | 2 +- script.js | 329 +++++++++++++++++++++++++++++++++++++++++++++- styles.css | 30 +++++ 3 files changed, 359 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a892a0..bf21e0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "timer-swe1", + "name": "timer-bootcamp", "version": "1.0.0", "lockfileVersion": 1, "requires": true, diff --git a/script.js b/script.js index e2d0297..57f4cdd 100644 --- a/script.js +++ b/script.js @@ -1 +1,328 @@ -// Please implement exercise logic here +const boardSize = 4; // dictates the number of rows & columns to the grid +let board = []; // an array of all cards used in the game +const totalBoardArea = boardSize * boardSize; // total number of cards in the game +let firstCard = null; // set to null to indicate that no first card has been picked yet +let firstCardElement; // variable to hold the first card while user picks second card +let deck; // hold all 52 cards for "popping" into the board array +let paused = false; // pause while setTimeout runs so function variables do not get overwritten +let gameOver = false; // to indicate when game is over (i.e. times up or won) +const timeGiven = 180000; // time given for game 3minutes (3mins x 60seconds x 1000milliseconds) +let cardsGuessed = 0; // variable to hold number of cards correctly matched +let clear; // global variable to hold setTimeout function for access across functions +let gameOverFunction; // global variable to hold setTimeout function for access across functions +let timerFunction; +let timeLeft; +let boardEl; // global variable to hold card elements for access across functions +let playerName; // holds player name +let numOfWins = 0; // tracks number of wins + +// create nameBox (will hold nameField, nameBox, numofWinsBox and resetButton) +const nameBox = document.createElement('div'); +const nameField = document.createElement('input'); +nameField.placeholder = 'Enter name here'; +nameBox.appendChild(nameField); +const nameSubmit = document.createElement('button'); +nameSubmit.innerText = 'Submit'; +nameBox.appendChild(nameSubmit); +const numOfWinsBox = document.createElement('div'); +numOfWinsBox.setAttribute('style', 'margin-bottom: 5px'); +numOfWinsBox.innerText = 'Number of Wins: 0'; +const resetButton = document.createElement('button'); +resetButton.innerText = 'Reset current game'; + +const timerBox = document.createElement('div'); +timerBox.className = 'timer'; + +const pad = (num, size) => { + let newNum = num.toString(); + while (newNum.length < size) newNum = `0${num}`; + return newNum; +}; + +const timerStart = () => { + if (timeLeft > 0) { + timeLeft -= 1000; + const minuteDisplay = Math.floor(timeLeft / 1000 / 60); + const secondDisplay = pad(Math.floor((timeLeft / 1000) % 60), 2); + timerBox.innerText = `Time Left: ${minuteDisplay}:${secondDisplay}`; + } else { + clearInterval(timerFunction); + } +}; + +const timerReset = () => { + const minuteDisplay = Math.floor(timeGiven / 1000 / 60); + const secondDisplay = pad(Math.floor((timeGiven / 1000) % 60), 2); + timerBox.innerText = `Time Left: ${minuteDisplay}:${secondDisplay}`; + timeLeft = timeGiven; +}; + +// create outputBox (for display of game messages) +const outputBox = document.createElement('div'); + +// output message function +const output = (message) => { + outputBox.innerText = message; +}; + +// clearing of temporary game messages function +const clearOutput = () => { + outputBox.innerText = ''; + resetButton.disabled = false; +}; + +// setTimeout function for clearing of outputBox +const clearFunction = () => { + clear = setTimeout(clearOutput, 3000); +}; + +// create winning message box +const winBox = document.createElement('div'); + +// setting 3 minutes game +const timesUp = () => { + if (gameOver === false) { + output('Your time is up! (3 minute game time)'); + gameOver = true; + resetButton.disabled = false; + } +}; + +// name submit button function to trigger start of game and appending of game elements +const captureName = () => { + playerName = nameField.value; + nameField.remove(); + nameSubmit.remove(); + nameBox.className = 'name'; + nameBox.innerHTML = `Welcome to Match Game, ${playerName}!

`; + nameBox.appendChild(numOfWinsBox); + nameBox.appendChild(resetButton); + nameBox.appendChild(timerBox); + document.body.appendChild(boardEl); + document.body.appendChild(outputBox); + document.body.appendChild(winBox); + gameOverFunction = setTimeout(timesUp, timeGiven); + timerReset(); + timerFunction = setInterval(timerStart, 1000); +}; +nameSubmit.addEventListener('click', captureName); + +// square click function +const squareClick = (cardElement, column, row) => { + if (paused === false && gameOver === false) { + resetButton.disabled = true; + console.log(cardElement); + + console.log('FIRST CARD DOM ELEMENT', firstCard); + + console.log('BOARD CLICKED CARD', board[column][row]); + + const clickedCard = board[column][row]; + + // the user already clicked on this square + if (cardElement.innerText !== '') { + return; + } + + // first turn + if (firstCard === null) { + console.log('first turn'); + firstCard = clickedCard; + // turn this card over + cardElement.innerText = firstCard.name; + // hold onto this for later when it may not match + firstCardElement = cardElement; + + // second turn + } else { + console.log('second turn'); + cardElement.innerText = clickedCard.name; + // matched + if ( + clickedCard.name === firstCard.name + && clickedCard.suit === firstCard.suit + ) { + console.log('match'); + if (outputBox.innerText === '') { + output("It's a match!"); + clearFunction(); + } else { + clearTimeout(clear); + clearFunction(); + } + cardsGuessed += 2; + if (cardsGuessed === totalBoardArea) { + numOfWins += 1; + gameOver = true; + winBox.innerText = 'Congratulations! You won!'; + setTimeout(() => { winBox.innerText = ''; }, 5000); + numOfWinsBox.innerText = `Number of Wins: ${numOfWins}`; + clearTimeout(gameOverFunction); + clearTimeout(timerFunction); + } + } + // did not match + else { + console.log('NOT a match'); + paused = true; + // turn this card back over + setTimeout(() => { firstCardElement.innerText = ''; + cardElement.innerText = ''; paused = false; resetButton.disabled = false; }, 3000); + } + + // reset the first card + firstCard = null; + } + } +}; + +// create all the board elements that will go on the screen +// return the built board +const buildBoardElements = (brd) => { + // create the element that everything will go inside of + const boardElement = document.createElement('div'); + + // give it a class for CSS purposes + boardElement.classList.add('board'); + + // use the board data structure we passed in to create the correct size board + for (let i = 0; i < brd.length; i += 1) { + // make a var for just this row of cards + const row = brd[i]; + + // make an element for this row of cards + const rowElement = document.createElement('div'); + rowElement.classList.add('row'); + + // make all the squares for this row + for (let j = 0; j < row.length; j += 1) { + // create the square element + const square = document.createElement('div'); + + // set a class for CSS purposes + square.classList.add('square'); + + // set the click event + // eslint-disable-next-line + square.addEventListener('click', (event) => { + // we will want to pass in the card element so + // that we can change how it looks on screen, i.e., + // "turn the card over" + squareClick(event.currentTarget, i, j); + }); + + rowElement.appendChild(square); + } + boardElement.appendChild(rowElement); + } + + return boardElement; +}; + +const makeDeck = () => { + // create the empty deck at the beginning + const newDeck = []; + const suits = ['hearts', 'diamonds', 'clubs', 'spades']; + + for (let suitIndex = 0; suitIndex < suits.length; suitIndex += 1) { + // make a variable of the current suit + const currentSuit = suits[suitIndex]; + console.log(`current suit: ${currentSuit}`); + + // loop to create all cards in this suit + // rank 1-13 + for (let rankCounter = 1; rankCounter <= 13; rankCounter += 1) { + // Convert rankCounter to string + let cardName = `${rankCounter}`; + + // 1, 11, 12 ,13 + if (cardName === '1') { + cardName = 'ace'; + } else if (cardName === '11') { + cardName = 'jack'; + } else if (cardName === '12') { + cardName = 'queen'; + } else if (cardName === '13') { + cardName = 'king'; + } + + // make a single card object variable + const card = { + name: cardName, + suit: currentSuit, + rank: rankCounter, + }; + + console.log(`rank: ${rankCounter}`); + + // add the card to the deck + newDeck.push(card); // add double the cards to the deck + newDeck.push(card); + } + } + + return newDeck; +}; + +const getRandomIndex = (max) => Math.floor(Math.random() * max); + +const shuffleCards = (cardDeck) => { + let currentIndex = 0; + while (currentIndex < cardDeck.length) { + const randomIndex = getRandomIndex(cardDeck.length); + const randomCard = cardDeck[randomIndex]; + const currentCard = cardDeck[currentIndex]; + cardDeck[currentIndex] = randomCard; + cardDeck[randomIndex] = currentCard; + currentIndex += 1; + } + return cardDeck; +}; + +// initialise function +const initGame = () => { + const doubleDeck = makeDeck(); + const deckSubset = doubleDeck.slice(0, boardSize * boardSize); + deck = shuffleCards(deckSubset); + + for (let i = 0; i < boardSize; i += 1) { + board.push([]); + for (let j = 0; j < boardSize; j += 1) { + board[i].push(deck.pop()); + } + } + boardEl = buildBoardElements(board); + + document.body.appendChild(nameBox); +}; + +// reset function +const reset = () => { + outputBox.innerText = ''; // clear outputBox + gameOverFunction = setTimeout(timesUp, timeGiven); // reset 3-minute timer + timerReset(); + timerFunction = setInterval(timerStart, 1000); + firstCard = null; // reset firstCard to null + winBox.innerText = ''; // clear winBox + gameOver = false; // switch gameOver from true to false + board = []; // empty out previous games' cards + const squaresArray = document.getElementsByClassName('square'); // retrieve all squares' element addresses + for (let i = 0; i < squaresArray.length; i += 1) { // clear all squares' innerText + squaresArray[i].innerText = ''; + } + + // generate a new set of board elements + const doubleDeck = makeDeck(); + const deckSubset = doubleDeck.slice(0, boardSize * boardSize); + deck = shuffleCards(deckSubset); + for (let i = 0; i < boardSize; i += 1) { + board.push([]); + for (let j = 0; j < boardSize; j += 1) { + board[i].push(deck.pop()); + } + } + boardEl = buildBoardElements(board); +}; +resetButton.addEventListener('click', reset); + +initGame(); diff --git a/styles.css b/styles.css index 04e7110..983eff0 100644 --- a/styles.css +++ b/styles.css @@ -1,3 +1,33 @@ body { background-color: pink; } + +body { + background-color: salmon; +} + +.square { + padding: 10px; + margin: 10px; + background-color: white; + display: inline-block; + height: 40px; + width: 20px; + vertical-align: top; + border-radius: 10%; + font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif; + font-size: 20px; +} + +.name { + font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif; +} + +.timer { + background-color: beige; + width: 100px; + margin-left: 10px; + border-style: inset; + padding: 2px; + display: inline-block; +} From 670eb73bf8005b35f717ab6c428eb4a698f773e0 Mon Sep 17 00:00:00 2001 From: tabbiho Date: Sun, 23 Jan 2022 23:41:54 +0800 Subject: [PATCH 2/3] Match Game Stopwatch completed --- script.js | 55 +++++++++++++++++++++++++++++++++++++++++++----------- styles.css | 4 ++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/script.js b/script.js index 57f4cdd..e164289 100644 --- a/script.js +++ b/script.js @@ -28,7 +28,11 @@ const numOfWinsBox = document.createElement('div'); numOfWinsBox.setAttribute('style', 'margin-bottom: 5px'); numOfWinsBox.innerText = 'Number of Wins: 0'; const resetButton = document.createElement('button'); -resetButton.innerText = 'Reset current game'; +resetButton.innerText = 'Reset Game'; +resetButton.className = 'button'; +const pauseButton = document.createElement('button'); +pauseButton.innerText = 'Pause Game'; +pauseButton.className = 'button'; const timerBox = document.createElement('div'); timerBox.className = 'timer'; @@ -55,6 +59,7 @@ const timerReset = () => { const secondDisplay = pad(Math.floor((timeGiven / 1000) % 60), 2); timerBox.innerText = `Time Left: ${minuteDisplay}:${secondDisplay}`; timeLeft = timeGiven; + pauseButton.innerText = 'Pause Game'; }; // create outputBox (for display of game messages) @@ -65,10 +70,40 @@ const output = (message) => { outputBox.innerText = message; }; +// setting 3 minutes game +const timesUp = () => { + if (gameOver === false) { + output('Your time is up! (3 minute game time)'); + gameOver = true; + resetButton.disabled = false; + pauseButton.disabled = true; + } +}; + +const timerPause = () => { + if (gameOver === false) { + if (paused === false) { + clearInterval(timerFunction); + clearInterval(gameOverFunction); + paused = true; + pauseButton.innerText = 'Resume Game'; + } else if (paused === true) { + timerFunction = setInterval(timerStart, 1000); + gameOverFunction = setTimeout(timesUp, timeLeft); + paused = false; + pauseButton.innerText = 'Pause Game'; + } + } else { + pauseButton.disabled = true; + pauseButton.innerText = '<-- Click here instead'; + } +}; + // clearing of temporary game messages function const clearOutput = () => { outputBox.innerText = ''; resetButton.disabled = false; + pauseButton.disabled = false; }; // setTimeout function for clearing of outputBox @@ -79,15 +114,6 @@ const clearFunction = () => { // create winning message box const winBox = document.createElement('div'); -// setting 3 minutes game -const timesUp = () => { - if (gameOver === false) { - output('Your time is up! (3 minute game time)'); - gameOver = true; - resetButton.disabled = false; - } -}; - // name submit button function to trigger start of game and appending of game elements const captureName = () => { playerName = nameField.value; @@ -97,6 +123,7 @@ const captureName = () => { nameBox.innerHTML = `Welcome to Match Game, ${playerName}!

`; nameBox.appendChild(numOfWinsBox); nameBox.appendChild(resetButton); + nameBox.appendChild(pauseButton); nameBox.appendChild(timerBox); document.body.appendChild(boardEl); document.body.appendChild(outputBox); @@ -111,6 +138,7 @@ nameSubmit.addEventListener('click', captureName); const squareClick = (cardElement, column, row) => { if (paused === false && gameOver === false) { resetButton.disabled = true; + pauseButton.disabled = true; console.log(cardElement); console.log('FIRST CARD DOM ELEMENT', firstCard); @@ -159,6 +187,7 @@ const squareClick = (cardElement, column, row) => { numOfWinsBox.innerText = `Number of Wins: ${numOfWins}`; clearTimeout(gameOverFunction); clearTimeout(timerFunction); + pauseButton.disabled = true; } } // did not match @@ -167,7 +196,7 @@ const squareClick = (cardElement, column, row) => { paused = true; // turn this card back over setTimeout(() => { firstCardElement.innerText = ''; - cardElement.innerText = ''; paused = false; resetButton.disabled = false; }, 3000); + cardElement.innerText = ''; paused = false; resetButton.disabled = false; pauseButton.disabled = false; }, 3000); } // reset the first card @@ -299,7 +328,10 @@ const initGame = () => { // reset function const reset = () => { outputBox.innerText = ''; // clear outputBox + clearInterval(gameOverFunction); + clearInterval(timerFunction); gameOverFunction = setTimeout(timesUp, timeGiven); // reset 3-minute timer + pauseButton.disabled = false; timerReset(); timerFunction = setInterval(timerStart, 1000); firstCard = null; // reset firstCard to null @@ -324,5 +356,6 @@ const reset = () => { boardEl = buildBoardElements(board); }; resetButton.addEventListener('click', reset); +pauseButton.addEventListener('click', timerPause); initGame(); diff --git a/styles.css b/styles.css index 983eff0..2aad788 100644 --- a/styles.css +++ b/styles.css @@ -31,3 +31,7 @@ body { padding: 2px; display: inline-block; } + +.button { + margin: 3px; +} From 5e36ecd8f0e7baa87009999a1a5149571f259dfd Mon Sep 17 00:00:00 2001 From: tabbiho Date: Sun, 23 Jan 2022 23:44:51 +0800 Subject: [PATCH 3/3] Stopwatch completed - toggle between scripts in index.html --- index.html | 2 +- script_stopwatch.js | 136 ++++++++++++++++++++++++++++++++++++++++++++ styles.css | 38 +++++++++++-- 3 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 script_stopwatch.js diff --git a/index.html b/index.html index 4771b50..e7e3c12 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,6 @@

Timer!

- + diff --git a/script_stopwatch.js b/script_stopwatch.js new file mode 100644 index 0000000..651ead2 --- /dev/null +++ b/script_stopwatch.js @@ -0,0 +1,136 @@ +const dataSplit = document.createElement('div'); +dataSplit.className = 'box'; +const dataSplitHeader = document.createElement('span'); +dataSplitHeader.className = 'header'; +dataSplitHeader.innerText = 'Split Time'; +dataSplit.appendChild(dataSplitHeader); +const dataSplitText = document.createElement('span'); +dataSplitText.className = 'text'; +dataSplit.appendChild(dataSplitText); + +const dataLap = document.createElement('div'); +dataLap.className = 'box'; +const dataLapHeader = document.createElement('span'); +dataLapHeader.className = 'header'; +dataLapHeader.innerText = 'Lap Time'; +dataLap.appendChild(dataLapHeader); +const dataLapText = document.createElement('span'); +dataLapText.className = 'text'; +dataLap.appendChild(dataLapText); + +const timeElapsed = document.createElement('div'); +timeElapsed.className = 'box'; +timeElapsed.setAttribute('style', 'height:50px; width: 200px'); +const timeElapsedHeader = document.createElement('span'); +timeElapsedHeader.className = 'header'; +timeElapsedHeader.innerText = 'Elapsed Time'; +timeElapsed.appendChild(timeElapsedHeader); +const timeElapsedText = document.createElement('span'); +timeElapsedText.className = 'text'; +timeElapsed.appendChild(timeElapsedText); + +const startButton = document.createElement('button'); +startButton.className = 'button'; +startButton.innerText = 'Start'; +startButton.setAttribute('style', 'position: absolute; top: 170px; left: 370px'); + +const stopButton = document.createElement('button'); +stopButton.className = 'button'; +stopButton.innerText = 'Stop'; +stopButton.setAttribute('style', 'position: absolute; top: 170px; left: 470px'); + +const resetButton = document.createElement('button'); +resetButton.className = 'button'; +resetButton.innerText = 'Reset'; +resetButton.setAttribute('style', 'position: absolute; top: 220px; left: 370px'); + +const splitButton = document.createElement('button'); +splitButton.className = 'button'; +splitButton.innerText = 'Split'; +splitButton.setAttribute('style', 'position: absolute; top: 220px; left: 470px'); + +document.body.appendChild(dataSplit); +document.body.appendChild(dataLap); +document.body.appendChild(timeElapsed); +document.body.appendChild(startButton); +document.body.appendChild(stopButton); +document.body.appendChild(resetButton); +document.body.appendChild(splitButton); + +// ==========LOGIC======================= + +let currentTime = 0; // in seconds +stopButton.disabled = true; +splitButton.disabled = true; +let timeFunction; +let splitTimes = [0]; +let lapTimes = []; + +const pad = (num, size) => { + let newNum = num.toString(); + while (newNum.length < size) newNum = `0${num}`; + return newNum; +}; + +const display = () => { + const hours = pad(Math.floor(currentTime / 60 / 60), 2); + const minutes = pad(Math.floor((currentTime / 60) % 60), 2); + const seconds = pad(currentTime % 60, 2); + timeElapsedText.innerText = `${hours}:${minutes}:${seconds}`; +}; +display(); + +const timeRun = () => { + currentTime += 1; + display(); +}; + +const timerStart = () => { + startButton.disabled = true; + stopButton.disabled = false; + splitButton.disabled = false; + timeFunction = setInterval(timeRun, 1000); +}; +startButton.addEventListener('click', timerStart); + +const timerStop = () => { + startButton.disabled = false; + stopButton.disabled = true; + clearInterval(timeFunction); +}; +stopButton.addEventListener('click', timerStop); + +const timerReset = () => { + currentTime = 0; + startButton.disabled = false; + stopButton.disabled = true; + splitButton.disabled = true; + clearInterval(timeFunction); + display(); + + splitTimes = [0]; + lapTimes = []; + dataSplitText.innerText = ''; + dataLapText.innerText = ''; +}; +resetButton.addEventListener('click', timerReset); + +const timerSplit = () => { + splitTimes.push(currentTime); + const currentLap = currentTime - splitTimes[splitTimes.length - 2]; + lapTimes.push(currentLap); + dataSplitText.innerText = ''; + dataLapText.innerText = ''; + for (let i = 0; i < lapTimes.length; i += 1) { + const splitHours = pad(Math.floor(splitTimes[i + 1] / 60 / 60), 2); + const splitMinutes = pad(Math.floor((splitTimes[i + 1] / 60) % 60), 2); + const splitSeconds = pad(splitTimes[i + 1] % 60, 2); + dataSplitText.innerHTML += `${i + 1}. ${splitHours}:${splitMinutes}:${splitSeconds}
`; + + const lapHours = pad(Math.floor(lapTimes[i] / 60 / 60), 2); + const lapMinutes = pad(Math.floor((lapTimes[i] / 60) % 60), 2); + const lapSeconds = pad(lapTimes[i] % 60, 2); + dataLapText.innerHTML += `${i + 1}. ${lapHours}:${lapMinutes}:${lapSeconds}
`; + } +}; +splitButton.addEventListener('click', timerSplit); diff --git a/styles.css b/styles.css index 2aad788..f701cc7 100644 --- a/styles.css +++ b/styles.css @@ -1,7 +1,3 @@ -body { - background-color: pink; -} - body { background-color: salmon; } @@ -19,8 +15,15 @@ body { font-size: 20px; } +.board { + width: 30%; + margin: 0 auto; +} + .name { font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif; + margin: 0 auto; + width: 30%; } .timer { @@ -35,3 +38,30 @@ body { .button { margin: 3px; } + +.box { + border-style: groove; + background-color: ivory; + height: 300px; + width: 150px; + margin-right: 10px; + display: inline-block; + vertical-align: top; + padding: 5px; +} + +.header { + text-decoration: underline; + font-family: Verdana, Geneva, Tahoma, sans-serif; + font-weight: bold; + padding: 10px; +} + +.button { + width: 90px; +} + +.text { + display: block; + padding: 10px; +}