From 8b14d92ae4a9ff251ba21e0204681c43f440303a Mon Sep 17 00:00:00 2001 From: root Date: Thu, 2 Dec 2021 23:47:28 +0800 Subject: [PATCH 1/4] added timer, reset button, and start button --- script.js | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++++ styles.css | 69 +++++++++++- 2 files changed, 380 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index e2d0297..e52a163 100644 --- a/script.js +++ b/script.js @@ -1 +1,313 @@ // Please implement exercise logic here + +// Global Variables +// boardSize has to be an even number +const boardSize = 4; +const board = []; +let firstCard = null; +let firstCardElement; +let deck; +const gameInfo = document.createElement("div"); +gameInfo.id = "gameInfo"; +gameInfo.classList.add("gluten"); +let currentScore = 0; +const maxScore = boardSize * 2; +let canCLick = false; +// const gameTime = 180; // time in seconds +let timeLeft = 180; +let timeInterval; + +const cardRankEmojiMap = { + 1: "🅰", + 2: "2️⃣", + 3: "3️⃣", + 4: "4️⃣", + 5: "5️⃣", + 6: "6️⃣", + 7: "7️⃣", + 8: "8️⃣", + 9: "9️⃣", + 10: "🔟", + 11: "👑👶", + 12: "👑👩", + 13: "👑👨", +}; + +const cardNameMap = { + 1: "A", + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + 10: 10, + 11: "J", + 12: "Q", + 13: "K", +}; + +// HTML Element Creation +// Win Message +const winMessage = document.createElement("div"); +winMessage.id = "winMessage"; +winMessage.classList.add("gluten"); +winMessage.innerText = "You've Won!\n✨✨✨✨✨"; +// i can't actually figure out how to make this look more like an annoying popup message +// but whatever + +// Time Div +const timeSection = document.createElement("div"); +timeSection.id - "timeSection"; +timeSection.classList.add("timeSection"); + +// Timer Display +const timeDisplay = document.createElement("h1"); +timeDisplay.id = "timeDisplay"; +timeDisplay.classList.add("gluten", "timeDisplay"); +timeDisplay.innerText = "3:00"; +timeSection.appendChild(timeDisplay); + +// Start Button +const startButton = document.createElement("button"); +startButton.id = "startButton"; +startButton.classList.add("button"); +startButton.innerText = "Start"; +startButton.addEventListener("click", () => { + startRound(); +}); +timeSection.appendChild(startButton); + +// Reset Button +const resetButton = document.createElement("button"); +resetButton.id = "resetButton"; +resetButton.classList.add("button"); +resetButton.innerText = "Reset"; +resetButton.addEventListener("click", () => { + resetRound(); +}); +timeSection.appendChild(resetButton); + +document.body.appendChild(timeSection); + +const refreshTimer = function () { + if (timeLeft > 0) { + timeLeft -= 1; + const secondsDisplay = ("0" + (timeLeft % 60)).slice(-2); + const minutesDisplay = Math.floor(timeLeft / 60); + + document.getElementById( + "timeDisplay" + ).innerText = `${minutesDisplay}:${secondsDisplay}`; + } else { + document.getElementById( + "gameInfo" + ).innerText = `Time's UP!\nYou did not manage to match all the cards. Your score was ${currentScore}.\n\nReset the game to play again.`; + canCLick = false; + } +}; + +// Gameplay Logic +const squareClick = (cardElement, column, row) => { + // 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; + canCLick = true; + + // second turn + } else { + console.log("second turn"); + if ( + clickedCard.name === firstCard.name && + clickedCard.suit === firstCard.suit + ) { + currentScore++; + gameInfo.innerText = `A Match! Continue opening your cards.\nYour current score is ${currentScore}. `; + setTimeout(() => { + gameInfo.innerText = '"Pick a card, any (closed) card..."'; + }, 1000); + cardElement.innerText = clickedCard.name; + canCLick = true; + } else { + // show card to user for 3 seconds before turning over + gameInfo.innerText = + "Not a Match - Cards will be turned over again in 3 seconds!"; + cardElement.innerText = clickedCard.name; + canCLick = false; + setTimeout(() => { + // console.log("waiting 3 seconds..."); + // turn both cards back over + firstCardElement.innerText = ""; + cardElement.innerText = ""; + gameInfo.innerText = "Pick a card, any (closed) card..."; + canCLick = true; + }, 1000); + } + + // reset the first card + firstCard = null; + } + if (currentScore == maxScore) { + gameInfo.innerText = "Well done! You've completed the game."; + document.body.appendChild(winMessage); + setTimeout(() => { + const elementsToRemove = document.getElementById("winMessage"); + elementsToRemove.remove(); + }, 5000); + // add code to reset board here + // create (or unhide) a reset button + // on reset clicked, re-initialize most global variables + // can probably just create another function and listener + } +}; + +const startRound = function () { + canCLick = true; + timeInterval = setInterval(() => { + refreshTimer(); + }, 1000); + // trigger this with a button? +}; + +const resetRound = function () { + firstCard = null; + canCLick = false; + + board.length = 0; + deck.length = 0; + currentScore = 0; + timeLeft = 180; + clearInterval(timeInterval); + document.getElementById("timeDisplay").innerText = "3:00"; + + document.getElementById("boardElement").remove(); + initGame(); +}; + +// Game Initialization Logic +// create all the board elements that will go on the screen +// return the built board +const buildBoardElements = (board) => { + // 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"); + boardElement.id = "boardElement"; + + // use the board data structure we passed in to create the correct size board + for (let i = 0; i < board.length; i += 1) { + // make a var for just this row of cards + const row = board[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" + if (canCLick) { + squareClick(event.currentTarget, i, j); + } + }); + + rowElement.appendChild(square); + } + boardElement.appendChild(rowElement); + } + + return boardElement; +}; + +const makeDeck = function () { + const cardDeck = []; + const suits = ["hearts", "diamonds", "clubs", "spades"]; + let suitIndex = 0; + while (suitIndex < suits.length) { + var currentSuit = suits[suitIndex]; + let rankCounter = 1; + while (rankCounter <= 13) { + const cardName = cardNameMap[rankCounter]; + const cardRankEmojiTemp = cardRankEmojiMap[rankCounter]; + + const card = { + name: cardName, + suit: currentSuit, + rank: rankCounter, + cardRankEmoji: cardRankEmojiTemp, + }; + cardDeck.push(card); + cardDeck.push(card); + rankCounter += 1; + } + suitIndex += 1; + } + return cardDeck; +}; + +const shuffleCards = function (array) { + // based on Durstenfeld shuffle + // https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array + for (let i = array.length - 1; i > 0; i -= 1) { + const j = Math.floor(Math.random() * (i + 1)); + const temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + return array; +}; + +const initGame = () => { + // create this special deck by getting the doubled cards and + // making a smaller array that is ( boardSize squared ) number of cards + let doubleDeck = makeDeck(); + let deckSubset = doubleDeck.slice(0, boardSize * boardSize); + deck = shuffleCards(deckSubset); + + // deal the cards out to the board data structure + for (let i = 0; i < boardSize; i += 1) { + board.push([]); + for (let j = 0; j < boardSize; j += 1) { + board[i].push(deck.pop()); + } + } + + const boardEl = buildBoardElements(board); + document.body.appendChild(boardEl); + + gameInfo.innerText = "Welcome to Match Game - Click Start to begin~"; + gameInfo.classList.add("game-info"); + document.body.appendChild(gameInfo); +}; + +// Main Function +initGame(); diff --git a/styles.css b/styles.css index 04e7110..df3fe32 100644 --- a/styles.css +++ b/styles.css @@ -1,3 +1,70 @@ body { - background-color: pink; + background-color: yellowgreen; + font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande", + "Lucida Sans Unicode", Geneva, Verdana, sans-serif; + text-align: center; +} + +.board { + background-color: thistle; + border-radius: 10px; + width: fit-content; + margin: auto; +} + +.square { + padding: 10px; + margin: 10px; + background-color: white; + display: inline-block; + height: 30px; + width: 30px; + vertical-align: top; + border-radius: 5px; + text-align: middle; + font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande", + "Lucida Sans Unicode", Geneva, Verdana, sans-serif; +} + +.game-info { + padding: 10px; + margin: 10px auto auto auto; + width: fit-content; + background-color: grey; + border-radius: 10px; + border: pink; + text-align: center; + font-size: 24px; +} + +.winMessage { + width: auto; + background-color: black; + color: white; + text-align: center; + border-radius: 6px; + font-size: 48px; +} + +.timeSection { + /* background-color: aquamarine; */ + text-align: center; +} + +.timeDisplay { + width: auto; + margin: auto; + /* border: 10px solid; + border-color: antiquewhite; */ + text-align: center; + font-size: 48px; +} + +.button { + margin: 0 0 10px 0; + font-size: 24px; +} + +.gluten { + font-family: "Gluten", cursive; } From ced8ae88c06ab376979cdfb1729726787625d4dc Mon Sep 17 00:00:00 2001 From: root Date: Thu, 2 Dec 2021 23:48:16 +0800 Subject: [PATCH 2/4] added font updates in HTML --- index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 4771b50..d18cbe5 100644 --- a/index.html +++ b/index.html @@ -3,10 +3,16 @@ Timer + + + -

Timer!

+

Timer!

From d30a71b1338812fae595d88b8153720e009062e8 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 3 Dec 2021 22:41:13 +0800 Subject: [PATCH 3/4] added auto-reset after winning and timing our, improved styling --- index.html | 4 ++-- script.js | 30 +++++++++++++----------------- styles.css | 47 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/index.html b/index.html index d18cbe5..fc120e1 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - Timer + Match Game Timer @@ -12,7 +12,7 @@ -

Timer!

+

Match Game - Timer!

diff --git a/script.js b/script.js index e52a163..e7e13f0 100644 --- a/script.js +++ b/script.js @@ -1,4 +1,4 @@ -// Please implement exercise logic here +// This thing mainly uses JS to generate and add HTML elements, as opposed to directly creating HTML elements in the index file // Global Variables // boardSize has to be an even number @@ -53,8 +53,8 @@ const cardNameMap = { // Win Message const winMessage = document.createElement("div"); winMessage.id = "winMessage"; -winMessage.classList.add("gluten"); -winMessage.innerText = "You've Won!\n✨✨✨✨✨"; +winMessage.classList.add("gluten", "winMessage"); +winMessage.innerText = "You've Won!\nCongratulations!"; // i can't actually figure out how to make this look more like an annoying popup message // but whatever @@ -73,7 +73,7 @@ timeSection.appendChild(timeDisplay); // Start Button const startButton = document.createElement("button"); startButton.id = "startButton"; -startButton.classList.add("button"); +startButton.classList.add("gluten", "button"); startButton.innerText = "Start"; startButton.addEventListener("click", () => { startRound(); @@ -83,7 +83,7 @@ timeSection.appendChild(startButton); // Reset Button const resetButton = document.createElement("button"); resetButton.id = "resetButton"; -resetButton.classList.add("button"); +resetButton.classList.add("gluten", "button"); resetButton.innerText = "Reset"; resetButton.addEventListener("click", () => { resetRound(); @@ -106,6 +106,9 @@ const refreshTimer = function () { "gameInfo" ).innerText = `Time's UP!\nYou did not manage to match all the cards. Your score was ${currentScore}.\n\nReset the game to play again.`; canCLick = false; + setInterval(() => { + resetRound(); + }); } }; @@ -123,7 +126,6 @@ const squareClick = (cardElement, column, row) => { // first turn if (firstCard === null) { - console.log("first turn"); firstCard = clickedCard; // turn this card over cardElement.innerText = firstCard.name; @@ -133,7 +135,6 @@ const squareClick = (cardElement, column, row) => { // second turn } else { - console.log("second turn"); if ( clickedCard.name === firstCard.name && clickedCard.suit === firstCard.suit @@ -146,13 +147,12 @@ const squareClick = (cardElement, column, row) => { cardElement.innerText = clickedCard.name; canCLick = true; } else { - // show card to user for 3 seconds before turning over + // show card to user for 1 second before turning over gameInfo.innerText = - "Not a Match - Cards will be turned over again in 3 seconds!"; + "Not a Match - Cards will be turned over again in 1 second!"; cardElement.innerText = clickedCard.name; canCLick = false; setTimeout(() => { - // console.log("waiting 3 seconds..."); // turn both cards back over firstCardElement.innerText = ""; cardElement.innerText = ""; @@ -160,21 +160,18 @@ const squareClick = (cardElement, column, row) => { canCLick = true; }, 1000); } - // reset the first card firstCard = null; } + if (currentScore == maxScore) { gameInfo.innerText = "Well done! You've completed the game."; document.body.appendChild(winMessage); setTimeout(() => { const elementsToRemove = document.getElementById("winMessage"); elementsToRemove.remove(); + resetRound(); }, 5000); - // add code to reset board here - // create (or unhide) a reset button - // on reset clicked, re-initialize most global variables - // can probably just create another function and listener } }; @@ -183,7 +180,6 @@ const startRound = function () { timeInterval = setInterval(() => { refreshTimer(); }, 1000); - // trigger this with a button? }; const resetRound = function () { @@ -227,7 +223,7 @@ const buildBoardElements = (board) => { const square = document.createElement("div"); // set a class for CSS purposes - square.classList.add("square"); + square.classList.add("square", "gluten"); // set the click event // eslint-disable-next-line diff --git a/styles.css b/styles.css index df3fe32..f1c44c5 100644 --- a/styles.css +++ b/styles.css @@ -1,12 +1,12 @@ body { - background-color: yellowgreen; + background-color: #fefbd8; font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande", "Lucida Sans Unicode", Geneva, Verdana, sans-serif; text-align: center; } .board { - background-color: thistle; + background-color: #d5f4e6; border-radius: 10px; width: fit-content; margin: auto; @@ -19,27 +19,31 @@ body { display: inline-block; height: 30px; width: 30px; - vertical-align: top; + vertical-align: middle; border-radius: 5px; - text-align: middle; - font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande", - "Lucida Sans Unicode", Geneva, Verdana, sans-serif; + text-align: match-parent; +} + +.square:hover { + background-color: #618685; } .game-info { padding: 10px; margin: 10px auto auto auto; width: fit-content; - background-color: grey; + background-color: #80ced6; border-radius: 10px; - border: pink; + border: 2px solid black; text-align: center; font-size: 24px; } .winMessage { width: auto; - background-color: black; + margin: 20px; + padding: 40px; + background-color: #618685; color: white; text-align: center; border-radius: 6px; @@ -52,17 +56,34 @@ body { } .timeDisplay { - width: auto; + width: fit-content; margin: auto; - /* border: 10px solid; - border-color: antiquewhite; */ text-align: center; font-size: 48px; } .button { - margin: 0 0 10px 0; + margin: 0 5px 10px 5px; font-size: 24px; + + display: inline-block; + outline: 0; + cursor: pointer; + border-radius: 6px; + border: 2px solid #618685; + color: #618685; + background: 0 0; + padding: 8px; + box-shadow: rgba(0, 0, 0, 0.07) 0px 2px 4px 0px, + rgba(0, 0, 0, 0.05) 0px 1px 1.5px 0px; + font-weight: 800; + font-size: 16px; + height: 42px; +} + +.button:hover { + background-color: #618685; + color: #fff; } .gluten { From 0d4ce9198799e8ed17672633d32559358c26bbb1 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 4 Dec 2021 01:51:41 +0800 Subject: [PATCH 4/4] added stopwatch with start and pause as a link in match gaame --- index.html | 5 +++ stopwatch_index.html | 49 ++++++++++++++++++++++++++++ stopwatch_script.js | 56 ++++++++++++++++++++++++++++++++ stopwatch_styles.css | 77 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 stopwatch_index.html create mode 100644 stopwatch_script.js create mode 100644 stopwatch_styles.css diff --git a/index.html b/index.html index fc120e1..5578129 100644 --- a/index.html +++ b/index.html @@ -15,5 +15,10 @@

Match Game - Timer!

+ + + diff --git a/stopwatch_index.html b/stopwatch_index.html new file mode 100644 index 0000000..9514147 --- /dev/null +++ b/stopwatch_index.html @@ -0,0 +1,49 @@ + + + + Stopwatch + + + + + + + +

Stopwatch

+ +
+
+

Insert Lap Data

+
+ +
+

00 : 00 : 00

+ +
+
+ + +
+
+ + +
+
+
+
+ + + + + + + diff --git a/stopwatch_script.js b/stopwatch_script.js new file mode 100644 index 0000000..ff13885 --- /dev/null +++ b/stopwatch_script.js @@ -0,0 +1,56 @@ +// laps are saved as values within a 1D array +// elapsed time must start from 0a nd count up +let currentElapsedTime = 0; +let stopped = true; +let timerInterval; + +const showTime = function (element, elapsedTime) { + let tempTime = elapsedTime; + + let seconds = tempTime % 1000; // get the remainder of 1000 ms which can go to second + tempTime = Math.floor(tempTime / 1000); //to get seconds + let minutes = tempTime % 60; // get the remainder of 60s which can go to minutes + tempTime = Math.floor(tempTime / 60); // to get minutes + let hours = tempTime % 60; // get the remainder of 60mins which can go to hours + + let timeStr = ""; + timeStr += hours.toString().padStart(2, "0") + " : "; + timeStr += minutes.toString().padStart(2, "0") + " : "; + timeStr += seconds.toString().padStart(2, "0"); + element.innerHTML = timeStr; + return element.innerHTML; +}; + +const startButtonFunction = function () { + stopped = false; + timerInterval = setInterval(() => { + currentElapsedTime += 1; + showTime(document.getElementById("elapsedTime"), currentElapsedTime); + }, 1000); +}; + +const stopButtonFunction = function () { + clearInterval(timerInterval); +}; + +const startButton = document.getElementById("startButton"); +const stopButton = document.getElementById("stopButton"); +const resetButton = document.getElementById("resetButton"); +const lapButton = document.getElementById("lapButton"); + +// Setting Event Listeners +window.onload = function () { + startButton.addEventListener("click", () => { + startButtonFunction(); + }); + stopButton.addEventListener("click", () => { + // call a fuction to stop the countdown + stopButtonFunction(); + }); + resetButton.addEventListener("click", () => { + // call a function to set elapsed time and lap data to zero + }); + lapButton.addEventListener("click", () => { + // call a fuction to lap and append the current lap time to the lap data div + }); +}; diff --git a/stopwatch_styles.css b/stopwatch_styles.css new file mode 100644 index 0000000..3186bd5 --- /dev/null +++ b/stopwatch_styles.css @@ -0,0 +1,77 @@ +body { + background-color: #36395a; + color: #fff; + font-family: "Concert One", cursive; + text-align: center; +} + +.timeSection { + display: block; + width: auto; + background-color: #5a5e9a; + text-align: center; +} + +.timeDisplay { + width: fit-content; + height: fit-content; + margin: auto; + text-align: center; + font-size: 72px; + display: inline-block; +} + +.functionButtons { + display: inline-block; + width: auto; + background-color: #2cc8f7; + text-align: center; + vertical-align: middle; +} + +.button { + margin: auto; + padding: 10px; + font-size: 24px; + text-align: center; + width: 150px; + + display: inline-block; + outline: 0; + cursor: pointer; + border-radius: 6px; + border: 2px solid #618685; + color: #618685; + background: 0 0; + padding: 8px; + box-shadow: rgba(0, 0, 0, 0.07) 0px 2px 4px 0px, + rgba(0, 0, 0, 0.05) 0px 1px 1.5px 0px; + font-family: "Concert One", cursive; + font-weight: 800; + font-size: 16px; + height: 42px; +} + +.button:hover { + background-color: #618685; + color: #fff; +} + +.concert { + font-family: "Concert One", cursive; +} + +.center { + width: auto; + margin: auto; +} + +.leftSide { + background-color: pink; + display: inline-block; +} + +.rightSide { + background-color: powderblue; + display: inline-block; +}