Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,20 @@ <h1 class="title">Vault‑Tec Memory</h1>
<button class="btn ghost" id="hint">Reveal 1 Pair</button>
</div>

<div class="hint" style="margin-top:1rem;padding:0.8rem;background:rgba(137, 166, 207, 0.1);border-radius:4px">
Tip: Try to remember positions — the Pip‑Boy rewards patience.
<div class="hint" style="margin-top:1rem;padding:0.8rem;background:rgba(137, 166, 207, 0.1);border-radius:4px">
      Tip: Try to remember positions — the Pip‑Boy rewards patience.
    </div>

        <div style="margin-top:1.5rem;padding-top:1.2rem;border-top:1px solid rgba(137, 166, 207, 0.2)">
      <div class="label" style="margin-bottom:0.8rem">Leaderboard</div>
      <div class="lb-list" id="leaderboardList" style="margin-bottom:1rem"></div>
      <div class="lb-controls" role="group" aria-label="Leaderboard actions" style="display:grid;grid-template-columns:repeat(2, 1fr);gap:0.5rem">
        <button class="btn ghost" id="openHighScores" title="View full high scores" aria-label="Open high scores">High Scores</button>
        <button class="btn ghost" id="exportLb" title="Export leaderboard as JSON" aria-label="Export leaderboard">Export</button>
        <button class="btn ghost" id="shareLb" title="Share leaderboard via link" aria-label="Share leaderboard">Share</button>
        <button class="btn ghost" id="clearLb" title="Clear leaderboard" aria-label="Clear leaderboard">Clear</button>
      </div>
</div>

<!-- Leaderboard (top-5 per difficulty) -->
<div style="margin-top:1.5rem;padding-top:1.2rem;border-top:1px solid rgba(137, 166, 207, 0.2)">
<div class="label" style="margin-bottom:0.8rem">Leaderboard</div>
<div class="lb-list" id="leaderboardList" style="margin-bottom:1rem"></div>
<div class="lb-controls" style="display:grid;grid-template-columns:repeat(2, 1fr);gap:0.5rem">
<button class="btn ghost" id="openHighScores">High Scores</button>
<button class="btn ghost" id="exportLb">Export</button>
<button class="btn ghost" id="shareLb">Share</button>
<button class="btn ghost" id="clearLb">Clear</button>
</div>
</div>
</div>

<div style="margin-top:1.5rem;padding:1rem;background:rgba(137, 166, 207, 0.08);border-radius:6px;font-size:0.85rem;color:#89a6cf;display:flex;gap:0.8rem;align-items:flex-start">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true" style="flex-shrink:0;margin-top:2px"><path d="M12 2v6" stroke="#ffd23f" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"></path><path d="M5 8c1-6 14-6 14 0" stroke="#89a6cf" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
Expand All @@ -103,6 +101,16 @@ <h1 class="title">Vault‑Tec Memory</h1>
<main class="game">
<div class="top-row" >
<div class="progress">Find all pairs — Good luck, Vault Dweller.</div>
<div id="difficultyControls" style="font-family:'Share Tech Mono',monospace;color:var(--muted);display:flex;gap:0.4rem;align-items:center">
<div style="color:var(--muted)">Difficulty:</div>
<button class="btn difficulty-btn" data-diff="easy">Easy</button>
<button class="btn difficulty-btn btn--active" data-diff="medium">Medium</button>
<button class="btn difficulty-btn" data-diff="hard">Hard</button>
</div>

<div style="display:flex;gap:0.5rem;align-items:center">
<button class="btn ghost" id="mute" aria-pressed="false">Mute</button>
<button class="btn ghost" id="playDemo">Play Demo</button>
<div class="top-btns" >

<div id="difficultyControls" style="font-family:'Share Tech Mono',monospace;color:var(--muted);display:flex;gap:0.4rem;align-items:center">
Expand All @@ -122,7 +130,6 @@ <h1 class="title">Vault‑Tec Memory</h1>

<section class="deck" id="deck" aria-live="polite" aria-label="Game cards"></section>

<!-- confetti canvas used by the win animation -->
<canvas id="confettiCanvas" aria-hidden="true"></canvas>

<div style="width:100%;display:flex;align-items:center;justify-content:center">
Expand Down Expand Up @@ -163,7 +170,6 @@ <h2 id="modalTitle"></h2>
</div>
</div>

<!-- High Scores Modal -->
<div class="modal-overlay" id="highScoresOverlay" role="dialog" aria-modal="true" aria-hidden="true">
<div class="modal-box" role="document">
<h2>High Scores</h2>
Expand Down
32 changes: 29 additions & 3 deletions src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
let moveLimitEnabled = false;
let moveLimitMax = 0;
let remainingMoves = 0;
// NEW: State for hint penalty
let hintUsed = false;

// toggle listener
moveLimitToggle.addEventListener('change', () => {
Expand Down Expand Up @@ -286,9 +288,18 @@
pairsEl.textContent = `${matched} / ${totalTiles}`;
// star logic per difficulty thresholds
const [threeThresh, twoThresh] = difficulties[selectedDifficulty].stars;
if (moves <= threeThresh) starCount = 3;
else if (moves <= twoThresh) starCount = 2;
else starCount = 1;
let newStarCount;
if (moves <= threeThresh) newStarCount = 3;
else if (moves <= twoThresh) newStarCount = 2;
else newStarCount = 1;

// Apply Hint Penalty: Cap stars at 2 if hint was used, effectively preventing 3 stars.
if (hintUsed && newStarCount === 3) {
newStarCount = 2;
}

starCount = newStarCount;

Array.from(starsEl.children).forEach((el, idx) => {
el.classList.toggle('star--lost', idx + 1 > starCount);
});
Expand All @@ -302,6 +313,8 @@
matched = 0;
moves = 0;
starCount = 3;
// NEW: Reset hint state
hintUsed = false;
updateUI();
started = false;
// enable hint based on difficulty
Expand Down Expand Up @@ -438,6 +451,11 @@
function showWinModal() {
modalTitle.textContent = 'CONGRATULATIONS, Vault Dweller';
modalMessage.textContent = 'You matched all pairs — Vault‑Tec is proud.';
// Add warning if hint was used
if (hintUsed) {
modalMessage.textContent += ' (Note: Hint used, score capped at 2 Stars.)';
}

modalStats.innerHTML = '';
const elapsed = timeTotal - timeLeft;
modalStats.appendChild(statCard('Time', formatTime(Math.floor(elapsed / 60), elapsed % 60)));
Expand Down Expand Up @@ -488,6 +506,14 @@

// hint
hintBtn.addEventListener('click', () => {
// Apply Hint Penalty
if (hintBtn.style.display !== 'none' && !locked) {
moves += 10; // Penalty: +10 moves
hintUsed = true; // Penalty: Mark hint as used
updateUI(); // Reflect the penalty in the score
playTone(150, 0.10); // Play a negative tone for penalty
}

const nodes = Array.from(deckEl.querySelectorAll('.card:not(.matched):not(.is-flip)'));
if (nodes.length < 2) return;
const grouped = {};
Expand Down