From 4f0b035e46ba63ee1e9d6252136e59000cdcfc68 Mon Sep 17 00:00:00 2001 From: JwelSrivastava <175561856+JwelSrivastava@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:51:43 +0530 Subject: [PATCH 1/3] Add Shuffle Board Power-Up Feature - One-time use power-up that reshuffles unmatched cards - Smooth bounce animation with audio feedback - 2-move penalty affects star rating - Responsive button layout with full text visibility - Works on all difficulty levels - Prevents conflicts during card interactions --- index.html | 1 + src/css/styles.css | 115 ++++++++++++++++++++++++++++++++++++++++++++- src/js/app.js | 76 ++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index c8d26cdc..fa8b0b52 100644 --- a/index.html +++ b/index.html @@ -75,6 +75,7 @@

Vault‑Tec Memory

+
Tip: Try to remember positions — the Pip‑Boy rewards patience.
diff --git a/src/css/styles.css b/src/css/styles.css index 38b8bfda..5a128e2c 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -226,6 +226,33 @@ h1.title { display: flex; gap: 0.6rem; margin-top: 1rem; + flex-wrap: wrap; +} + +.controls .btn { + flex: 1; + min-width: 120px; + white-space: nowrap; + overflow: visible; + text-overflow: clip; + font-size: 0.85rem; +} + +/* Shuffle button styling */ +#shuffleBoard { + min-width: 120px; + text-align: center; +} + +#shuffleBoard:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +#shuffleBoard:not(:disabled):hover { + background: linear-gradient(90deg, rgba(255, 210, 63, 0.1), rgba(255, 210, 63, 0.05)); + border-color: rgba(255, 210, 63, 0.3); + color: var(--vault-yellow); } .btn.ghost#playDemo { @@ -573,6 +600,30 @@ h1.title { } } +/* Shuffle animation for cards */ +.card.shuffling { + animation: shuffleMove 0.8s ease-in-out; + z-index: 10; +} + +@keyframes shuffleMove { + 0% { + transform: translateY(0) scale(1) rotateZ(0deg); + } + 25% { + transform: translateY(-20px) scale(0.9) rotateZ(-5deg); + } + 50% { + transform: translateY(-30px) scale(0.8) rotateZ(5deg); + } + 75% { + transform: translateY(-20px) scale(0.9) rotateZ(-2deg); + } + 100% { + transform: translateY(0) scale(1) rotateZ(0deg); + } +} + .modal-overlay { position: fixed; inset: 0; @@ -728,7 +779,54 @@ h1.title { padding: 1rem 0; } .star { width: 34px; height: 28px; } + + /* Better button responsiveness for medium screens */ + .controls { + gap: 0.4rem; + flex-wrap: wrap; + } + .controls .btn { + font-size: 0.8rem; + padding: 0.5rem 0.6rem; + min-width: 100px; + flex: 1 1 auto; + } +} + +@media (max-width: 600px) { + .controls { + flex-direction: column; + gap: 0.5rem; + } + .controls .btn { + width: 100%; + flex: none; + font-size: 0.9rem; + padding: 0.6rem; + text-align: center; + min-width: auto; + white-space: normal; + overflow: visible; + } +} +/* Specific breakpoint for button text visibility */ +@media (max-width: 380px) { + .controls { + flex-direction: column; + gap: 0.5rem; + } + .controls .btn { + width: 100%; + flex: none; + min-width: auto; + font-size: 0.9rem; + padding: 0.6rem; + white-space: normal; + overflow: visible; + text-overflow: clip; + } } + @media (max-width: 480px) { .deck { grid-template-columns: repeat(3, 1fr); @@ -742,9 +840,15 @@ h1.title { } .controls { flex-direction: column; + gap: 0.5rem; } - .btn { + .controls .btn { width: 100%; + flex: none; + font-size: 0.9rem; + padding: 0.6rem; + text-align: center; + white-space: normal; } .footer { padding: 1rem; @@ -756,6 +860,15 @@ h1.title { gap: 0.3rem; } } + +/* Ensure buttons don't get too small on narrow screens */ +@media (max-width: 320px) { + .controls .btn { + font-size: 0.8rem; + padding: 0.5rem; + min-height: 40px; + } +} /* Responsive layout adjustments */ @media (max-width: 1400px) { .app { diff --git a/src/js/app.js b/src/js/app.js index 51c7c38f..e7d18475 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -29,6 +29,7 @@ const restartBtn = document.getElementById('restart'); const restartBottom = document.getElementById('restart-bottom'); const hintBtn = document.getElementById('hint'); + const shuffleBoardBtn = document.getElementById('shuffleBoard'); const modal = document.getElementById('modalOverlay'); const playAgainBtn = document.getElementById('playAgain'); const closeModalBtn = document.getElementById('closeModal'); @@ -73,6 +74,7 @@ let moveLimitEnabled = false; let moveLimitMax = 0; let remainingMoves = 0; + let shuffleUsed = false; // toggle listener moveLimitToggle.addEventListener('change', () => { @@ -299,10 +301,14 @@ matched = 0; moves = 0; starCount = 3; + shuffleUsed = false; updateUI(); started = false; // enable hint based on difficulty hintBtn.style.display = difficulties[selectedDifficulty].hideHint ? 'none' : ''; + // enable shuffle button for new game + shuffleBoardBtn.disabled = false; + shuffleBoardBtn.textContent = 'Shuffle Board'; // ensure cards are interactive Array.from(deckEl.querySelectorAll('.card')).forEach(c => { c.removeAttribute('aria-disabled'); c.classList.remove('matched', 'is-flip'); }); locked = false; @@ -496,6 +502,76 @@ setTimeout(() => { c1.classList.remove('is-flip'); c2.classList.remove('is-flip'); playTone(440, 0.08); }, 900); }); + // shuffle board power-up + shuffleBoardBtn.addEventListener('click', () => { + if (shuffleUsed || locked) return; + + // Get all unmatched cards + const unmatchedCards = Array.from(deckEl.querySelectorAll('.card:not(.matched)')); + if (unmatchedCards.length < 2) return; + + // Prevent interactions during shuffle + locked = true; + shuffleUsed = true; + shuffleBoardBtn.disabled = true; + shuffleBoardBtn.textContent = 'Used'; + + // Flip all unmatched cards face down first + unmatchedCards.forEach(card => { + if (card.classList.contains('is-flip')) { + card.classList.remove('is-flip'); + } + }); + + // Clear opened cards array + opened = []; + + // Add shuffle animation to all unmatched cards + unmatchedCards.forEach((card, index) => { + setTimeout(() => { + card.classList.add('shuffling'); + playTone(300 + (index * 20), 0.05); // Rising tone sequence + }, index * 50); + }); + + // Shuffle the card positions after animation starts + setTimeout(() => { + // Get current data-src values of unmatched cards + const cardData = unmatchedCards.map(card => card.getAttribute('data-src')); + + // Shuffle the data array + const shuffledData = shuffle([...cardData]); + + // Reassign shuffled data to cards + unmatchedCards.forEach((card, index) => { + card.setAttribute('data-src', shuffledData[index]); + const altText = shuffledData[index].split('.').slice(0, -1).join('.'); + card.setAttribute('aria-label', `Card: ${altText}`); + + // Update the image in the card-front + const img = card.querySelector('.card-front img'); + if (img) { + img.src = `img/${shuffledData[index]}`; + img.alt = altText; + } + }); + + // Deduct points for using the power-up + moves += 2; + updateUI(); + + playTone(800, 0.15); // Success tone + }, 400); + + // Remove shuffle animation and unlock after animation completes + setTimeout(() => { + unmatchedCards.forEach(card => { + card.classList.remove('shuffling'); + }); + locked = false; + }, 800); + }); + // restart & modal handlers restartBtn.addEventListener('click', resetGame); restartBottom.addEventListener('click', resetGame); From 22f79a7fce57594aae1b165858efca78caf03e5c Mon Sep 17 00:00:00 2001 From: JwelSrivastava <175561856+JwelSrivastava@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:02:45 +0530 Subject: [PATCH 2/3] Trigger GitHub conflict resolution refresh From 9b931495a1cef88b024b7b3d766c5a46e4e1d757 Mon Sep 17 00:00:00 2001 From: JwelSrivastava <175561856+JwelSrivastava@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:04:42 +0530 Subject: [PATCH 3/3] Add PR update documentation - conflicts resolved --- PR_UPDATE.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 PR_UPDATE.md diff --git a/PR_UPDATE.md b/PR_UPDATE.md new file mode 100644 index 00000000..4c071267 --- /dev/null +++ b/PR_UPDATE.md @@ -0,0 +1,32 @@ +# 🔀 Shuffle Board Power-Up - PR Update + +## ✅ Conflict Resolution Complete + +All Git conflicts have been resolved and the PR is ready for merge. + +## 🎮 Feature Summary + +### Shuffle Board Power-Up +- **One-time use per game** - Button disables after use +- **Smart shuffling** - Only reshuffles unmatched cards, preserves completed pairs +- **Move penalty** - Adds 2 moves to score (affects star rating) +- **Smooth animation** - Bounce effect with staggered card timing +- **Audio feedback** - Rising tone sequence + success sound +- **Responsive design** - Full button text visibility on all screen sizes + +### Technical Implementation +- **HTML**: Added shuffle button to controls section +- **JavaScript**: Complete shuffle logic with state management +- **CSS**: Animation keyframes and responsive button layout +- **Accessibility**: Proper ARIA labels and keyboard support + +### Files Modified +- `index.html` - Added shuffle button +- `src/js/app.js` - Shuffle functionality and game logic +- `src/css/styles.css` - Animation and responsive styling + +## 🚀 Ready for Merge +- ✅ No conflicts +- ✅ All features tested +- ✅ Responsive design verified +- ✅ Clean commit history \ No newline at end of file