Skip to content

Commit 69aa924

Browse files
Merge branch 'Main' into feat/gamified-level
2 parents 79dc4e6 + 97c41ab commit 69aa924

11 files changed

Lines changed: 1248 additions & 28 deletions

File tree

‎navbar.js‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,19 @@
6666
`;
6767
const homeBtn = `
6868
<a class="btn ${isHome ? "btn-primary active" : "btn-ghost"} btn-sm" href="${homeHref}">
69-
<span class="mobile-nav-icon"><i class="fas fa-home"></i></span>
69+
<span class="mobile-nav-icon"><i class="fas fa-home" aria-hidden="true"></i></span>
7070
Home
7171
</a>`;
7272

7373
const learnBtn = `
7474
<a class="btn ${isLearn ? "btn-primary active" : "btn-ghost"} btn-sm" href="${learnHref}">
75-
<span class="mobile-nav-icon"><i class="fas fa-graduation-cap"></i></span>
75+
<span class="mobile-nav-icon"><i class="fas fa-graduation-cap" aria-hidden="true"></i></span>
7676
Learn
7777
</a>`;
7878

7979
const contributorsBtn = `
8080
<a class="btn ${isContributors ? "btn-primary active" : "btn-ghost"} btn-sm" href="${contributorsHref}">
81-
<span class="mobile-nav-icon"><i class="fas fa-users"></i></span>
81+
<span class="mobile-nav-icon"><i class="fas fa-users" aria-hidden="true"></i></span>
8282
Contributors
8383
</a>`;
8484

@@ -96,7 +96,7 @@
9696
safeStorage.getItem("customCursorEnabled") !== "false";
9797
const cursorBtn = `
9898
<button class="btn btn-ghost btn-sm" id="cursorToggleNav" aria-label="Toggle custom cursor (currently ${customCursorEnabled ? "Custom" : "Default"})">
99-
<span class="mobile-nav-icon"><i class="fas ${customCursorEnabled ? "fa-circle-notch" : "fa-mouse-pointer"}"></i></span>
99+
<span class="mobile-nav-icon"><i class="fas ${customCursorEnabled ? "fa-circle-notch" : "fa-mouse-pointer"}" aria-hidden="true"></i></span>
100100
Cursor: ${customCursorEnabled ? "Custom" : "Default"}
101101
</button>
102102
`;
@@ -139,7 +139,7 @@
139139
<div class="mobile-menu-header">
140140
<span class="mobile-menu-title">MENU</span>
141141
<button class="mobile-menu-close" id="mobileMenuClose" type="button">
142-
<i class="fas fa-times"></i>
142+
<i class="fas fa-times" aria-hidden="true"></i>
143143
</button>
144144
</div>
145145
${navButtonsHTML}
@@ -248,6 +248,14 @@
248248
dropdownMenu.classList.remove("show");
249249
}
250250
});
251+
252+
document.addEventListener("keydown", (e) => {
253+
if (e.key === "Escape" && dropdownMenu.classList.contains("show")) {
254+
dropdownToggle.setAttribute("aria-expanded", "false");
255+
dropdownMenu.classList.remove("show");
256+
dropdownToggle.focus();
257+
}
258+
});
251259
}
252260

253261
const logoutBtn = document.getElementById("logoutBtn");
@@ -272,7 +280,7 @@
272280
// Update all cursor toggle buttons on the page (both standard and mobile drawer might have it)
273281
document.querySelectorAll("#cursorToggleNav").forEach((btn) => {
274282
btn.innerHTML = `
275-
<span class="mobile-nav-icon"><i class="fas ${nextState ? "fa-circle-notch" : "fa-mouse-pointer"}"></i></span>
283+
<span class="mobile-nav-icon"><i class="fas ${nextState ? "fa-circle-notch" : "fa-mouse-pointer"}" aria-hidden="true"></i></span>
276284
Cursor: ${nextState ? "Custom" : "Default"}
277285
`;
278286
btn.setAttribute(

‎projects.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,5 +1910,13 @@
19101910
"techStack": ["html", "css", "javascript"],
19111911
"difficulty": "intermediate",
19121912
"projectDesc": "A responsive Kanban Board application featuring drag-and-drop task management, task creation, editing, deletion, local storage persistence, and dark/light theme support."
1913+
},
1914+
{
1915+
"projectNo": 210,
1916+
"projectName": "Cricket Scorecard",
1917+
"projectPath": "./public/Cricket-Scorecard/index.html",
1918+
"techStack": ["html", "css", "javascript"],
1919+
"difficulty": "advanced",
1920+
"projectDesc": "A tool to view the schedule of upcoming matches and watch live scorecard."
19131921
}
19141922
]

‎public/2048_game/index.html‎

Lines changed: 126 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
<head>
55
<meta charset="UTF-8" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<meta name="description" content="2048 — A beautiful, feature-rich tile puzzle game." />
7+
<meta name="description" content="2048 — A beautiful, feature-rich tile puzzle game. Slide tiles, merge numbers, and reach the 2048 tile!" />
88
<title>2048</title>
99
<link rel="preconnect" href="https://fonts.googleapis.com" />
1010
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@700;800;900&display=swap" rel="stylesheet" />
1111
<link rel="stylesheet" href="style.css" />
12+
<style id="theme-style"></style>
1213
</head>
1314

1415
<body class="light">
15-
<a href="/" class="project-back-button" style="position:fixed;left:18px;top:18px;padding:8px 16px;border-radius:8px;background:#1e293b;color:#3b82f6;border:1px solid #3b82f6;text-decoration:none;z-index:9999;font-weight:700;font-family:Inter, system-ui, sans-serif;transition:all 0.2s ease;" onmouseover="this.style.background='#3b82f6';this.style.color='#fff'" onmouseout="this.style.background='#1e293b';this.style.color='#3b82f6'">← Back to Home</a>
16+
1617

1718

1819
<div id="g">
@@ -49,19 +50,31 @@ <h1 class="logo" aria-label="2048">2048</h1>
4950
<div class="btns">
5051
<button class="btn" id="nb" aria-label="New Game">New Game</button>
5152
<button class="btn sm" id="ub" aria-label="Undo last move">↩ Undo</button>
53+
<button class="btn sm" id="pausebtn" aria-label="Pause game" style="display:none">⏸ Pause</button>
54+
<button class="btn sm" id="hintbtn" aria-label="Hint">💡 Hint</button>
5255
</div>
5356
<div class="top-icons">
5457
<button class="icon-btn" id="stbtn" title="View Stats" aria-label="View Stats">📊</button>
5558
<button class="icon-btn" id="sdbtn" title="Toggle Sound" aria-label="Toggle Sound">🔊</button>
5659
<button class="icon-btn" id="tb" title="Toggle Theme" aria-label="Toggle Theme">☀</button>
60+
<button class="icon-btn" id="howto-btn" title="How to Play" aria-label="How to Play">❓</button>
5761
</div>
5862
</div>
5963

6064
<!-- Mode selector -->
6165
<div class="mode-row" role="group" aria-label="Game mode">
62-
<button class="mode-btn active" data-mode="classic">Classic</button>
63-
<button class="mode-btn" data-mode="timed">⏱ 60s</button>
64-
<button class="mode-btn" data-mode="zen">Zen 5×5</button>
66+
<div class="theme-row" role="group" aria-label="Tile theme">
67+
<span class="theme-label">Theme:</span>
68+
<button class="theme-btn active" data-theme="classic">Classic</button>
69+
<button class="theme-btn" data-theme="ocean">🌊 Ocean</button>
70+
<button class="theme-btn" data-theme="neon">⚡ Neon</button>
71+
<button class="theme-btn" data-theme="retro">🎮 Retro</button>
72+
</div>
73+
<div class="mode-btns-row">
74+
<button class="mode-btn active" data-mode="classic">Classic</button>
75+
<button class="mode-btn" data-mode="timed">⏱ 60s</button>
76+
<button class="mode-btn" data-mode="zen">Zen 5×5</button>
77+
</div>
6578
</div>
6679

6780
<!-- Timer bar (timed mode only) -->
@@ -86,14 +99,7 @@ <h1 class="logo" aria-label="2048">2048</h1>
8699
<div class="tiles-layer" id="tl" aria-hidden="true"></div>
87100

88101
<!-- Game-over / win overlay -->
89-
<div
90-
id="ov"
91-
class="ov"
92-
style="display:none"
93-
role="dialog"
94-
aria-modal="true"
95-
aria-label="Game result">
96-
</div>
102+
<div id="ov" style="display:none" role="dialog" aria-modal="true" aria-label="Game result"></div>
97103

98104
<!-- Achievement pop-up -->
99105
<div class="achievement" id="ach" role="alert">
@@ -117,6 +123,11 @@ <h2 class="stats-title">Your Stats</h2>
117123
<div class="stat-card"><div class="lbl">Best Tile</div><div class="val" id="st-tile">2</div></div>
118124
</div>
119125

126+
<div class="graph-section">
127+
<div class="ach-list-title">Last 10 Games</div>
128+
<canvas id="score-graph" style="width:100%;height:80px;"></canvas>
129+
</div>
130+
120131
<div class="ach-list">
121132
<div class="ach-list-title">Achievements</div>
122133
<div id="ach-badges"></div>
@@ -128,9 +139,111 @@ <h2 class="stats-title">Your Stats</h2>
128139

129140
</div><!-- /.wrap -->
130141

142+
<p class="hint">Arrow keys · WASD · Swipe to play · <button id="howto-inline-btn" class="hint-link-btn" aria-label="How to Play">How to Play ❓</button></p>
131143
</div><!-- /#inner -->
132144
</div><!-- /#g -->
133145

146+
<!-- ===== How to Play Modal ===== -->
147+
<div id="howto-modal" class="howto-overlay" role="dialog" aria-modal="true" aria-labelledby="howto-title" style="display:none">
148+
<div class="howto-card">
149+
<button class="howto-close" id="howto-close" aria-label="Close How to Play">✕</button>
150+
151+
<div class="howto-header">
152+
<span class="howto-icon">🎮</span>
153+
<h2 class="howto-title" id="howto-title">How to Play 2048</h2>
154+
</div>
155+
156+
<!-- Goal -->
157+
<div class="howto-section">
158+
<h3 class="howto-section-title">🏆 Goal</h3>
159+
<p class="howto-text">
160+
Slide the tiles on the grid. When two tiles with the <strong>same number</strong> collide,
161+
they <strong>merge into one</strong>. Reach the <strong class="howto-highlight">2048</strong> tile to win!
162+
</p>
163+
</div>
164+
165+
<!-- Controls -->
166+
<div class="howto-section">
167+
<h3 class="howto-section-title">🕹️ Controls</h3>
168+
169+
<div class="howto-controls-grid">
170+
<!-- Keyboard -->
171+
<div class="howto-control-block">
172+
<p class="howto-control-label">⌨️ Keyboard</p>
173+
<div class="howto-keys">
174+
<div class="key-row">
175+
<kbd class="key">↑</kbd>
176+
</div>
177+
<div class="key-row">
178+
<kbd class="key">←</kbd>
179+
<kbd class="key">↓</kbd>
180+
<kbd class="key">→</kbd>
181+
</div>
182+
<p class="key-note">Arrow Keys or WASD</p>
183+
</div>
184+
</div>
185+
186+
<!-- Swipe -->
187+
<div class="howto-control-block">
188+
<p class="howto-control-label">📱 Mobile</p>
189+
<div class="howto-swipe">
190+
<div class="swipe-icon" aria-hidden="true">
191+
<svg width="52" height="52" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
192+
<circle cx="24" cy="24" r="10" stroke="rgba(255,255,255,0.6)" stroke-width="2" fill="none"/>
193+
<line x1="24" y1="4" x2="24" y2="14" stroke="rgba(255,255,255,0.8)" stroke-width="2.5" stroke-linecap="round"/>
194+
<line x1="24" y1="34" x2="24" y2="44" stroke="rgba(255,255,255,0.8)" stroke-width="2.5" stroke-linecap="round"/>
195+
<line x1="4" y1="24" x2="14" y2="24" stroke="rgba(255,255,255,0.8)" stroke-width="2.5" stroke-linecap="round"/>
196+
<line x1="34" y1="24" x2="44" y2="24" stroke="rgba(255,255,255,0.8)" stroke-width="2.5" stroke-linecap="round"/>
197+
<polygon points="24,4 21,10 27,10" fill="rgba(255,255,255,0.8)"/>
198+
<polygon points="24,44 21,38 27,38" fill="rgba(255,255,255,0.8)"/>
199+
<polygon points="4,24 10,21 10,27" fill="rgba(255,255,255,0.8)"/>
200+
<polygon points="44,24 38,21 38,27" fill="rgba(255,255,255,0.8)"/>
201+
</svg>
202+
</div>
203+
<p class="key-note">Swipe in any direction</p>
204+
</div>
205+
</div>
206+
</div>
207+
</div>
208+
209+
<!-- Pro Tips -->
210+
<div class="howto-section">
211+
<h3 class="howto-section-title">💡 Pro Tips</h3>
212+
<ul class="howto-tips">
213+
<li>Keep your <strong>highest tile</strong> in a corner.</li>
214+
<li>Build a <strong>snake chain</strong> of descending values.</li>
215+
<li>Use <strong>↩ Undo</strong> to recover from a bad move.</li>
216+
<li>Try <strong>💡 Hint</strong> when you are stuck!</li>
217+
</ul>
218+
</div>
219+
220+
<!-- Game Modes -->
221+
<div class="howto-section">
222+
<h3 class="howto-section-title">⚙️ Game Modes</h3>
223+
<div class="howto-modes">
224+
<div class="howto-mode-card">
225+
<span class="mode-emoji">🎯</span>
226+
<strong>Classic</strong>
227+
<span>4×4 grid, no time limit</span>
228+
</div>
229+
<div class="howto-mode-card">
230+
<span class="mode-emoji">⏱️</span>
231+
<strong>Timed 60s</strong>
232+
<span>Race against the clock</span>
233+
</div>
234+
<div class="howto-mode-card">
235+
<span class="mode-emoji">🧘</span>
236+
<strong>Zen 5×5</strong>
237+
<span>Bigger grid, relaxed play</span>
238+
</div>
239+
</div>
240+
</div>
241+
242+
<button class="btn howto-start-btn" id="howto-close2">Got it — Let's Play! 🚀</button>
243+
</div>
244+
</div>
245+
<!-- ===== / How to Play Modal ===== -->
246+
134247
<script src="script.js"></script>
135248
</body>
136249

‎public/2048_game/script.js‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ let stats = { best: 0, games: 0, wins: 0, bestTile: 2, earned: [] };
8888
function loadStats() {
8989
try {
9090
const s = localStorage.getItem('2048stats');
91-
if (s) stats = JSON.parse(s);
91+
if (s) {
92+
const parsed = JSON.parse(s);
93+
stats = { ...stats, ...parsed };
94+
if (!stats.earned) stats.earned = [];
95+
if (!stats.scoreHistory) stats.scoreHistory = [];
96+
}
9297
} catch (_) {}
9398
}
9499

@@ -275,24 +280,20 @@ function applyGridDimensions() {
275280

276281
function slideRow(row) {
277282
const arr = row.filter((x) => x);
278-
let pts = 0,
279-
didMerge = false;
283+
let pts = 0;
280284
for (let i = 0; i < arr.length - 1; i++) {
281-
if (arr[i] === arr[i + 1] && !didMerge) {
285+
if (arr[i] === arr[i + 1]) {
282286
arr[i] *= 2;
283287
pts += arr[i];
284288
arr.splice(i + 1, 1);
285-
didMerge = true;
286-
} else {
287-
didMerge = false;
288289
}
289290
}
290291
while (arr.length < N) arr.push(0);
291292
return { row: arr, pts };
292293
}
293294

294295
function rotateBoard(b, turns) {
295-
let out = b;
296+
let out = b.map(row => [...row]); // ensure deep copy to prevent mutation
296297
for (let t = 0; t < turns; t++) {
297298
const tmp = Array.from({ length: N }, () => Array(N).fill(0));
298299
for (let r = 0; r < N; r++) for (let c = 0; c < N; c++) tmp[c][N - 1 - r] = out[r][c];
@@ -917,7 +918,9 @@ document.addEventListener('visibilitychange', () => {
917918
paused = false;
918919
startTimer();
919920

920-
if (tfill) tfill.classList.remove('paused');
921+
922+
document.getElementById('tfill').classList.remove('paused');
923+
921924

922925
showToast('Timer resumed');
923926
}

0 commit comments

Comments
 (0)