From 9efad14e16103ed4f0a1b1a3aa7421d1771fd022 Mon Sep 17 00:00:00 2001 From: Mathew Jolly <156569490+mathewjolly11@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:57:41 +0000 Subject: [PATCH 1/2] Add initial implementation of Mathew's Memory Game with README, HTML, CSS, JS, favicon, and assets --- Memory-Game-By-Mathew-Jolly-main/README.md | 41 ++ Memory-Game-By-Mathew-Jolly-main/favicon.ico | Bin 0 -> 15406 bytes Memory-Game-By-Mathew-Jolly-main/index.html | 274 ++++++++++++ Memory-Game-By-Mathew-Jolly-main/memory.jpeg | Bin 0 -> 8854 bytes Memory-Game-By-Mathew-Jolly-main/script.js | 423 +++++++++++++++++++ Memory-Game-By-Mathew-Jolly-main/style.css | 364 ++++++++++++++++ 6 files changed, 1102 insertions(+) create mode 100644 Memory-Game-By-Mathew-Jolly-main/README.md create mode 100644 Memory-Game-By-Mathew-Jolly-main/favicon.ico create mode 100644 Memory-Game-By-Mathew-Jolly-main/index.html create mode 100644 Memory-Game-By-Mathew-Jolly-main/memory.jpeg create mode 100644 Memory-Game-By-Mathew-Jolly-main/script.js create mode 100644 Memory-Game-By-Mathew-Jolly-main/style.css diff --git a/Memory-Game-By-Mathew-Jolly-main/README.md b/Memory-Game-By-Mathew-Jolly-main/README.md new file mode 100644 index 000000000..71b375307 --- /dev/null +++ b/Memory-Game-By-Mathew-Jolly-main/README.md @@ -0,0 +1,41 @@ +# Mathew's Memory Game + +# Mathew Jolly's Memory Quest + +A modern, responsive memory card game built with HTML, CSS, and JavaScript. + +## Features +- Multiple difficulty levels: Easy, Medium, Hard, Expert, Legend +- Beautiful, mobile-friendly UI +- Leaderboard for each level +- Timer and scoring system +- Admin-only leaderboard reset (with secret code) +- Playable on desktop and mobile devices + +## How to Play +1. Enter your name and click **Start Game**. +2. Choose your desired level. +3. Flip cards to find matching pairs. +4. Complete the board as fast as possible for a high score! + +## Levels +- **Easy**: 4 pairs +- **Medium**: 6 pairs +- **Hard**: 8 pairs +- **Expert**: 10 pairs +- **Legend**: 12 pairs + +## Leaderboard +- Each level has its own leaderboard. +- Scores are sorted by points and time. +- Admins can reset the leaderboard using a secret code. + +## Responsive Design +- Fully optimized for mobile and desktop. +- All UI elements scale and align for any device. + +## Developer +Made with ❤️ by [Mathew Jolly](https://mathewjolly.vercel.app) + +## License +This project is open source and free to use. diff --git a/Memory-Game-By-Mathew-Jolly-main/favicon.ico b/Memory-Game-By-Mathew-Jolly-main/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..50680563546a9197b4133e27387bc7a13df0636e GIT binary patch literal 15406 zcmeHNTPS^57~k&qoN>S3jpKe?8u4PJlt^w5A~i(K11~}-@}@j_@_;B_{9_`|P#Wx4z&0)^D%qM#5Tc^K-Rpl6gmJOWVEC*5XF>SGUl8=-7*Rx&_|`67J}M-3@Ty`1dwY9I zN=hPJEwxo?X(Ue%w*lvX1~}mA>Pm%$h1As4L~d?wU$$n8i;J|iwM9-&PG5qdr>BQd%FD|w z)*l-i%WRpLm`Dx|4&?9ePiVt_n30h|6%`e%!v8YeBiq{An0{PM`~J>-T3VVck7pVk z9cB0M!BEc5&g6dOypsO*_I9)V%CTy{LqkJl`JJ5|<^$Q;*)eWG|IyKr+^?MH-`20! z>*@Xdoz~XY2)-D&ZB6~Xy}e9db8|CAMn+m)|K8pnJwHD)-_zRq;kUx}c&D+(0RaKz z}5J_=xh^{4XgfVR{x87G&MPAn89o zJ~nEX@O5@}GP$mP~#LD9&{M{32z`xhr2Al(&1Dpe# z1Dpe#0~T`tXT(84LG0`oaYL>77Qm0#$K>QB4Gj&k*rV2-0eBHdy1u@q)6-MR%*-UM ztp(!U2L}iA_Vz~O8U9$!QLAi8!RS=H9!paGkAoY0@x}zIGDV>y~)MJh3P@g zfv>MG%U^(AXJSD;F0R3@Y{z*d6$^ zg1@%5Rx&encXzY*Lo4`^FEu|u&+y;g-m-UE`u=Fa{y6(Z-WLk?7;pz?LO2W30{+Iv zMtXdFq}SJ1!Z{kwvGI*SKANrLhwp`aKa{JhE0((l-s9{E-%q^PQnAJTCpi@vb!^6Weww9I_Bil>sfM=8n zd}FEI+!fEexw*-PKRrEBYHF(Lcyn|7NLyA`7V|4-XJ-jM8TNemq~>Iihk`MvD=RB3 z*9hMR)3NZgD=RB$Zf;H<-`Cg2a(*TM+*HQWd)Udes?B9O*4+O>w3&{R?$zezef%7S dbAWSzbAWSzbAWSzb3pSP5SNe_ZUM_I@F%QnkU; + + + + + + Memory Quest + + + + + + + + + + + + + + + + + + + + + + +
+

🧠 Memory Quest

+

Mathew's Memory Game

+ + + +
+ + +
+
+
+ 🎮 + + +
+
+ Score: 0 + Time: 00:00 +
+
+ +
+
+
+
+ + +
+

🏆 Leaderboard

+
+ + + + + +
+
    + + +
    + + + + + + + \ No newline at end of file diff --git a/Memory-Game-By-Mathew-Jolly-main/memory.jpeg b/Memory-Game-By-Mathew-Jolly-main/memory.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b30b6a46e9678e64c57f1639881aa80aa8c5f1dc GIT binary patch literal 8854 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 z s.style.display = "none"); + section.style.display = "flex"; +} + +function showLanding() { showSection(landing); } + +function startTimer() { + seconds = 0; + clearInterval(timerInterval); + timerInterval = setInterval(() => { + seconds++; + document.getElementById("time").textContent = formatTime(seconds); + }, 1000); +} + +function stopTimer() { clearInterval(timerInterval); } + +function formatTime(sec) { + const m = Math.floor(sec / 60), s = sec % 60; + return `${m.toString().padStart(2,"0")}:${s.toString().padStart(2,"0")}`; +} + +// Game Board +function createBoard(level) { + gameBoard.innerHTML = ""; + let pairs; + switch(level) { + case "easy": pairs = 4; break; + case "medium": pairs = 6; break; + case "hard": pairs = 8; break; + case "expert": pairs = 10; break; + case "legend": pairs = 12; break; + default: pairs = 4; + } + let symbols = ["🍎","🍌","🍇","🍒","🍉","🥝","🍋","🍑","🍓","🍍","🍈","🍏","🍐","🍊","🍔","🍕","🍦","🍩","🍪","🍫","🍿","🍭","🍬","🍯"].slice(0,pairs); + cards = [...symbols, ...symbols].sort(() => 0.5 - Math.random()); + // Add medium/expert/legend class for those levels + gameBoard.classList.remove("easy", "medium", "expert", "legend"); + if(level === "easy") gameBoard.classList.add("easy"); + if(level === "medium") gameBoard.classList.add("medium"); + if(level === "expert") gameBoard.classList.add("expert"); + if(level === "legend") gameBoard.classList.add("legend"); + // Force 4 columns for expert/legend on mobile + if ((level === "expert" || level === "legend") && window.innerWidth <= 600) { + gameBoard.style.gridTemplateColumns = `repeat(4, 1fr)`; + } else { + gameBoard.style.gridTemplateColumns = `repeat(${Math.ceil(Math.sqrt(pairs*2))}, 80px)`; + } + cards.forEach(sym => { + let cardClass = "card"; + if(level === "easy") cardClass += " easy"; + if(level === "medium") cardClass += " medium"; + if(level === "expert") cardClass += " expert"; + if(level === "legend") cardClass += " legend"; + const card = document.createElement("div"); + card.className = cardClass; + card.textContent = "?"; + card.dataset.symbol = sym; + card.addEventListener("click", ()=>flipCard(card)); + gameBoard.appendChild(card); + }); +} + +function flipCard(card) { + if(flipped.length===2 || card.classList.contains("flipped")) return; + card.classList.add("flipped"); + card.textContent = card.dataset.symbol; + flipped.push(card); + if(flipped.length===2) setTimeout(checkMatch, 800); +} + +function checkMatch() { + const currentLevel = levelSelect.value; + let basePoints; + + // Different base points per level (harder = more points) + switch(currentLevel) { + case "easy": basePoints = 5; break; + case "medium": basePoints = 8; break; + case "hard": basePoints = 12; break; + case "expert": basePoints = 15; break; + case "legend": basePoints = 20; break; + default: basePoints = 5; + } + + if(flipped[0].dataset.symbol===flipped[1].dataset.symbol){ + // Correct match - add base points + score += basePoints; + scoreDisplay.textContent = score; + flipped=[]; + + if(document.querySelectorAll(".card:not(.flipped)").length===0){ + stopTimer(); + + // Calculate final score with time bonus and penalties + const timeBonus = Math.max(0, 300 - seconds); // Bonus for finishing under 5 minutes + const penalty = wrongMatches * 2; // 2 points penalty per wrong match + const finalScore = score + timeBonus - penalty; + + // Calculate maximum possible score for this level + let maxPairs; + switch(currentLevel) { + case "easy": maxPairs = 4; break; + case "medium": maxPairs = 6; break; + case "hard": maxPairs = 8; break; + case "expert": maxPairs = 10; break; + case "legend": maxPairs = 12; break; + default: maxPairs = 4; + } + const maxScore = (maxPairs * basePoints) + 300; // Max match points + max time bonus + + // Show detailed score breakdown + Swal.fire({ + title:"🎉 You Won!", + html:` +
    + Match Points: ${score}
    + Time Bonus: ${timeBonus} pts
    + Wrong Matches: ${wrongMatches} (-${penalty} pts)
    + Time Taken: ${formatTime(seconds)}
    +
    + Final Score: ${finalScore} / ${maxScore} +
    + `, + icon:"success", + confirmButtonColor:"#00e0ff", + width: 400 + }).then(()=>showLeaderboard()); + + // Save the final score + saveScore(finalScore); + } + } else { + // Wrong match - add penalty + wrongMatches++; + flipped.forEach(c=>{ + c.classList.remove("flipped"); + c.textContent="?"; + }); + flipped=[]; + } +} + +// Leaderboard +function getLeaderboardKey(level) { + return `memoryLeaderboard_${level}`; +} + +function saveScore(finalScore){ + const level = levelSelect.value; + console.log('[SAVE] Level:', level, 'Name:', playerName, 'Final Score:', finalScore, 'Time:', seconds); + saveScoreOnline(level, playerName, finalScore, seconds).then(() => { + showLeaderboard(level); + }).catch(error => { + console.error('[SAVE] Error saving score:', error); + // Fallback to showing leaderboard anyway + showLeaderboard(level); + }); +} + +// Google Sheets integration +// Save score to Google Sheets +function saveScoreOnline(level, name, score, time) { + console.log('[SHEETS SAVE] Attempting to save:', { level, name, score, time }); + const url = `${SHEETS_URL}?action=save&level=${encodeURIComponent(level)}&name=${encodeURIComponent(name)}&score=${score}&time=${time}`; + return fetch(url, { method: 'GET' }) + .then(response => response.text()) + .then(data => { + console.log('[SHEETS SAVE] Success:', data); + return data; + }) + .catch(error => { + console.error('[SHEETS SAVE] Error:', error); + throw error; + }); +} + +// Fetch leaderboard from Google Sheets +async function fetchLeaderboard(level) { + console.log('[FETCH] Level:', level); + try { + const url = `${SHEETS_URL}?action=fetch&level=${encodeURIComponent(level)}`; + console.log('[FETCH] URL:', url); + const response = await fetch(url, { method: 'GET' }); + + console.log('[FETCH] Response status:', response.status); + const text = await response.text(); + console.log('[FETCH] Raw response:', text); + + let scores; + try { + scores = JSON.parse(text); + console.log('[FETCH] Parsed scores:', scores); + } catch (parseError) { + console.error('[FETCH] JSON parse error:', parseError); + return []; + } + + // Ensure scores is an array + if (!Array.isArray(scores)) { + console.error('[FETCH] Scores is not an array:', scores); + return []; + } + + console.log('[FETCH] Returning scores:', scores); + return scores; + } catch (error) { + console.error('[FETCH] Error:', error); + return []; + } +} + +// Reset leaderboard in Google Sheets +function resetLeaderboardOnline(level) { + console.log('[RESET] Level:', level); + const url = `${SHEETS_URL}?action=reset&level=${encodeURIComponent(level)}`; + return fetch(url, { method: 'GET' }) + .then(response => response.text()) + .then(data => { + console.log('[RESET] Success:', data); + return data; + }) + .catch(error => { + console.error('[RESET] Error:', error); + throw error; + }); +} + +function updateLeaderboard(){ + const level = levelSelect.value; + const key = getLeaderboardKey(level); + const data = JSON.parse(localStorage.getItem(key))||[]; + leaderboardList.innerHTML=""; + if(data.length===0) leaderboardList.innerHTML="
  1. No scores yet 😔
  2. "; + else data.forEach((p,i)=>{ + const li = document.createElement("li"); + li.textContent = `${i+1}. ${p.name} — ${p.score} pts`; + leaderboardList.appendChild(li); + }); +} + +function showLeaderboard(){ + // Fetch from Google Sheets and update UI + let level = levelSelect.value; + if (arguments.length > 0 && typeof arguments[0] === 'string') { + level = arguments[0]; + levelSelect.value = level; + } + console.log('[SHOW LEADERBOARD] Level:', level); + + // Show loading state + leaderboardList.innerHTML = "
  3. Loading scores...
  4. "; + + fetchLeaderboard(level).then(scores => { + console.log('[SHOW LEADERBOARD] Scores received:', scores); + leaderboardList.innerHTML = ""; + if(scores.length === 0) { + leaderboardList.innerHTML = "
  5. No scores yet 😔
  6. "; + } else { + scores.forEach((p, i) => { + const li = document.createElement("li"); + li.textContent = `${i+1}. ${p.name} — ${p.score} pts`; + leaderboardList.appendChild(li); + }); + } + }).catch(error => { + console.error('Error fetching leaderboard:', error); + leaderboardList.innerHTML = "
  7. Error loading scores 😔
  8. "; + }); + + showSection(leaderboard); + // Ensure tab button is active for correct level + tabButtons.forEach(b => { + if (b.dataset.level === level) b.classList.add('active'); + else b.classList.remove('active'); + }); +} + +// Reset Leaderboard with SweetAlert +function resetLeaderboard(){ + Swal.fire({ + title:"Admin Verification 🔒", + input:"password", + inputPlaceholder:"Enter secret code", + showCancelButton:true, + confirmButtonColor:"#3085d6", + cancelButtonColor:"#d33", + preConfirm:code=>{ + if(code!==ADMIN_SECRET) Swal.showValidationMessage("❌ Wrong code!"); else return true; + } + }).then(async result => { + if(result.isConfirmed){ + // Always get level from active tab + let activeTab = document.querySelector('.tab-btn.active'); + let level = activeTab ? activeTab.dataset.level : 'easy'; + levelSelect.value = level; + console.log('[RESET] Level:', level); + await resetLeaderboardOnline(level); // Reset in Firebase + await showLeaderboard(level); + Swal.fire({title:"✅ Leaderboard Reset!", icon:"success", timer:2000, showConfirmButton:false}); + } + }); +} + +// Leaderboard tab logic +const tabButtons = document.querySelectorAll('.tab-btn'); +tabButtons.forEach(btn => { + btn.addEventListener('click', function() { + const level = btn.dataset.level; + levelSelect.value = level; + showLeaderboard(level); // Always fetch correct leaderboard + }); +}); + +// Event Listeners +startBtn.onclick = ()=>{ + const nameInput = playerNameInput.value.trim(); + if (!nameInput) { + Swal.fire({ + title: "Name Required! 🎮", + text: "Please enter your name before starting the game", + icon: "warning", + confirmButtonColor: "#00e0ff", + confirmButtonText: "Got it!", + timer: 1000, + timerProgressBar: true + }).then(() => { + playerNameInput.focus(); + }); + return; + } + playerName = nameInput; + + // Show game start message for 2 seconds + Swal.fire({ + title: "Get Ready! 🎯", + text: `Good luck, ${playerName}! Match all the cards as fast as you can!`, + icon: "info", + confirmButtonColor: "#00e0ff", + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }).then(() => { + // Start the game after the alert + score = 0; + wrongMatches = 0; // Reset wrong matches counter + scoreDisplay.textContent = score; + showSection(game); + createBoard(levelSelect.value); + + // Show all cards for 2 seconds before starting + const allCards = document.querySelectorAll('.card'); + allCards.forEach(card => { + card.classList.add('flipped'); + card.textContent = card.dataset.symbol; + }); + + setTimeout(() => { + // Hide all cards after 1 second + allCards.forEach(card => { + card.classList.remove('flipped'); + card.textContent = '?'; + }); + // Start the timer after cards are hidden + startTimer(); + }, 1000); + }); +}; + +backBtn.onclick = ()=>{ stopTimer(); showLanding(); }; +levelSelect.onchange = ()=>createBoard(levelSelect.value); +if (showLeaderboardBtn) { + showLeaderboardBtn.onclick = () => { + // Set first tab as active and show its leaderboard + if(tabButtons.length) { + tabButtons.forEach(b => b.classList.remove('active')); + tabButtons[0].classList.add('active'); + levelSelect.value = tabButtons[0].dataset.level; + showLeaderboard(tabButtons[0].dataset.level); + } else { + showLeaderboard(); + } + }; +} + +// Add event listeners for leaderboard buttons +const resetBtn = document.getElementById("resetBtn"); +if (resetBtn) { + resetBtn.onclick = resetLeaderboard; +} + +const leaderboardBackBtn = document.getElementById("leaderboardBackBtn"); +if (leaderboardBackBtn) { + leaderboardBackBtn.onclick = showLanding; +} + +// Initialize - show landing page and set first tab as active +if(tabButtons.length) { + tabButtons[0].classList.add('active'); + levelSelect.value = tabButtons[0].dataset.level; +} +showSection(landing); diff --git a/Memory-Game-By-Mathew-Jolly-main/style.css b/Memory-Game-By-Mathew-Jolly-main/style.css new file mode 100644 index 000000000..02e5ba756 --- /dev/null +++ b/Memory-Game-By-Mathew-Jolly-main/style.css @@ -0,0 +1,364 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Poppins', sans-serif; + background: radial-gradient(circle at center, #0a0f1f, #1b2a47); + color: #fff; + text-align: center; + overflow-x: hidden; +} + +section { + display: none; + min-height: 100vh; + align-items: center; + justify-content: center; + flex-direction: column; + opacity: 0; + transition: opacity 0.5s ease; +} + +section.active { + display: flex; + opacity: 1; +} + +h1.title { + font-size: 3em; + color: #00e0ff; + text-shadow: 0 0 25px #00e0ff, 0 0 50px #00ff85; +} + +.tagline { + color: #b9e8ff; + margin-bottom: 20px; +} + +button { + background: linear-gradient(45deg, #00e0ff, #00ff85); + border: none; + padding: 12px 28px; + color: #111; + border-radius: 30px; + font-size: 1.1em; + margin: 10px; + cursor: pointer; + font-weight: bold; + box-shadow: 0 0 15px #00e0ff; + transition: transform 0.3s, box-shadow 0.3s; +} + +button:hover { + transform: scale(1.08); + box-shadow: 0 0 30px #00ffcc; +} + +input#playerName { + padding: 12px; + border: 2px solid #00e0ff; + border-radius: 25px; + width: 260px; + text-align: center; + outline: none; + background: rgba(0, 0, 0, 0.3); + color: #fff; +} + +.top-bar { + display: flex; + justify-content: space-around; + width: 85%; + margin: 20px auto; + flex-wrap: wrap; +} + +.top-bar-row { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 12px; + margin-bottom: 8px; +} + +#gameBoard { + display: grid; + justify-content: center; + gap: 12px; + margin-top: 30px; +} + +.card { + width: 90px; + height: 90px; + background: #162447; + border-radius: 15px; + display: flex; + align-items: center; + justify-content: center; + font-size: 2.2em; + cursor: pointer; + transition: transform 0.3s, background 0.3s; + box-shadow: 0 0 10px rgba(0, 224, 255, 0.3); +} + +.card.flipped { + background: #00e0ff; + color: #000; + transform: rotateY(180deg); +} + +.card.hidden { + opacity: 0; + transform: scale(0.8); +} +.card.fade-in { + animation: fadeIn 0.4s forwards ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: scale(0.8) rotateY(90deg); } + to { opacity: 1; transform: scale(1) rotateY(0deg); } +} + +#leaderboard table { + border-collapse: collapse; + width: 70%; + color: #fff; + margin-top: 20px; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 0 15px #00e0ff; +} + +#leaderboard th, #leaderboard td { + border: 1px solid #00e0ff; + padding: 12px; +} + +#leaderboard tr:nth-child(even) { + background-color: #10223b; +} + +footer { + margin: 20px; + font-size: 0.9em; + color: #88aaff; +} + +.landing-leaderboard { + background: rgba(255, 255, 255, 0.1); + border-radius: 12px; + padding: 15px; + margin-top: 20px; + width: 60%; + box-shadow: 0 0 15px #00e0ff33; +} + +.landing-leaderboard h2 { + color: #00e0ff; + margin-bottom: 10px; +} + +.landing-leaderboard li { + list-style: none; + margin: 5px 0; + color: #00ffcc; + font-weight: 500; +} + +#musicToggle { + position: fixed; + bottom: 25px; + right: 25px; + background: #00e0ff; + color: #111; + border: none; + border-radius: 25px; + padding: 12px 18px; + font-weight: bold; + cursor: pointer; + box-shadow: 0 0 15px #00e0ff; + transition: 0.3s; +} + +#musicToggle:hover { + transform: scale(1.1); +} + +#levelSelect { + padding: 10px 18px; + border-radius: 25px; + border: 2px solid #00e0ff; + background: #1e2a47; + color: #00e0ff; + font-size: 1.1em; + font-weight: 600; + outline: none; + transition: border-color 0.3s, box-shadow 0.3s; + box-shadow: 0 0 8px #00e0ff33; +} +#levelSelect:focus { + border-color: #00ff85; + box-shadow: 0 0 12px #00ff85; +} +#levelSelect option { + background: #243b55; + color: #00e0ff; + font-weight: 500; +} + +@media (max-width: 600px) { + body { + font-size: 0.95em; + padding: 12px; + margin: 0; + width: 100vw; + min-width: 0; + box-sizing: border-box; + } + section, .top-bar, .landing-leaderboard, #leaderboard, #gameBoard { + width: 100vw !important; + min-width: 0 !important; + max-width: 100vw !important; + box-sizing: border-box; + padding-left: 8px; + padding-right: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + .top-bar { + flex-direction: column; + gap: 10px; + align-items: center; + margin: 10px auto; + width: 100vw !important; + justify-content: center; + max-width: 400px; + } + #gameBoard { + grid-template-columns: repeat(3, 1fr) !important; + gap: 8px; + margin-top: 18px; + width: 100vw !important; + justify-content: center; + align-items: center; + max-width: 400px; + } + .card { + width: 22vw !important; + height: 22vw !important; + min-width: 0; + font-size: 1.2em; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; + max-width: 90px; + max-height: 90px; + } + h1.title, .main-title { + font-size: 1.5em; + margin-bottom: 10px; + text-align: center; + } + input#playerName { + width: 90vw; + font-size: 1em; + margin-bottom: 10px; + text-align: center; + display: block; + margin-left: auto; + margin-right: auto; + max-width: 350px; + } + button, .tab-btn, .reset-btn { + width: 90vw; + font-size: 1em; + padding: 10px 0; + margin: 8px auto; + min-width: 0; + display: block; + margin-left: auto; + margin-right: auto; + max-width: 350px; + } + .leaderboard-tabs { + flex-direction: column; + gap: 8px; + width: 100vw !important; + align-items: center; + justify-content: center; + display: flex; + max-width: 350px; + } + .tab-btn { + width: 100%; + padding: 10px 0; + font-size: 1em; + text-align: center; + } + .landing-leaderboard, #leaderboardList { + width: 98vw !important; + padding: 8px; + font-size: 0.95em; + min-width: 0; + text-align: center; + margin-left: auto; + margin-right: auto; + max-width: 350px; + } + #leaderboardList li { + font-size: 0.95em; + padding: 6px 8px; + text-align: center; + } + footer { + font-size: 0.85em; + padding: 8px 0; + width: 100vw !important; + min-width: 0; + text-align: center; + margin-left: auto; + margin-right: auto; + max-width: 400px; + } + #gameBoard.easy, #gameBoard.medium, #gameBoard.expert, #gameBoard.legend { + grid-template-columns: repeat(3, 1fr) !important; + padding-left: 8px !important; + padding-right: 8px !important; + box-sizing: border-box; + justify-content: center; + align-items: center; + } + .card.easy, .card.medium, .card.expert, .card.legend { + width: 22vw !important; + height: 22vw !important; + font-size: 1.2em; + margin: 0 auto; + max-width: 90px; + max-height: 90px; + } + #gameBoard.expert, #gameBoard.legend { + grid-template-columns: repeat(4, 1fr) !important; + padding-left: 8px !important; + padding-right: 8px !important; + box-sizing: border-box; + justify-content: center; + align-items: center; + } + .card.expert, .card.legend { + width: 17vw !important; + height: 17vw !important; + font-size: 1em; + margin: 0 auto; + max-width: 70px; + max-height: 70px; + } +} From fa54525a7d15aad22f571ae1d3cf658252d81a2d Mon Sep 17 00:00:00 2001 From: Mathew Jolly <156569490+mathewjolly11@users.noreply.github.com> Date: Tue, 28 Oct 2025 02:07:49 +0000 Subject: [PATCH 2/2] Update pull request template with detailed project description and finalized README for Mathew's Memory Game --- .github/pull_request_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 71e019332..522673abc 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ ### Description -(Describe your project over here.) +(Added and finalized project README and documentation for "Mathew's Memory Game". This PR documents the project features, gameplay, levels, leaderboard behavior (per-level leaderboards with online storage via Google Sheets), responsive UI, scoring rules, and developer credits. It also summarizes recent gameplay improvements (preview timer, SweetAlert prompts, admin reset flow, and level-specific scoring)..) ### Checklist @@ -12,4 +12,4 @@ ## Related Issues or Pull Requests number -(Write your answer here.) \ No newline at end of file +(N/A (add issue/PR number here if available)) \ No newline at end of file