Skip to content

Commit 4885370

Browse files
Merge pull request steam-bell-92#496 from siri-004/feature/snake-game-difficulty-levels
feature: added difficulty levels to snake game🐍
2 parents 80450af + 36aa503 commit 4885370

1 file changed

Lines changed: 130 additions & 18 deletions

File tree

web-app/js/projects/snake.js

Lines changed: 130 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1-
function getsnakeGameHTML() {
1+
function getSnakeGameHTML() {
22
return `
33
<div class="project-content">
44
<h2>🐍 Classic Snake Game</h2>
55
<div class="snake-container">
6+
<div class="controls-top">
7+
<div class="difficulty-box">
8+
<label for="difficultySelect">Difficulty: </label>
9+
<select id="difficultySelect">
10+
<option value="easy">Easy (1x Score)</option>
11+
<option value="medium" selected>Medium (2x Score)</option>
12+
<option value="hard">Hard (3x Score)</option>
13+
</select>
14+
</div>
15+
</div>
16+
617
<div class="game-area">
718
<div id="canvas-wrapper" class="ui-canvas-frame">
819
<canvas id="snakeCanvas" width="600" height="400"></canvas>
@@ -19,6 +30,10 @@ function getsnakeGameHTML() {
1930
<span>Score</span>
2031
<div id="score">0</div>
2132
</div>
33+
<div class="score-card high-score-card">
34+
<span>Best</span>
35+
<div id="best-score">0</div>
36+
</div>
2237
</div>
2338
</div>
2439
@@ -40,6 +55,31 @@ function getsnakeGameHTML() {
4055
padding: 20px;
4156
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
4257
}
58+
.controls-top {
59+
margin-bottom: 15px;
60+
margin-right: 140px; /* Aligns with canvas center offset */
61+
}
62+
.difficulty-box select {
63+
padding: 6px 10px;
64+
border-radius: 6px;
65+
border: 1px solid var(--accent-border, #ccc);
66+
background-color: #ffffff !important; /* Explicitly forces white background */
67+
color: #222222 !important; /* Explicitly forces dark gray/black text */
68+
font-size: 14px;
69+
font-family: inherit;
70+
font-weight: 500;
71+
cursor: pointer;
72+
outline: none;
73+
}
74+
.difficulty-box select {
75+
padding: 4px 8px;
76+
border-radius: 4px;
77+
border: 1px solid #aaa;
78+
background-color: #ffffff;
79+
color: #222222;
80+
font-weight: normal;
81+
cursor: pointer;
82+
}
4383
.game-area {
4484
display: flex;
4585
align-items: flex-start;
@@ -52,16 +92,16 @@ function getsnakeGameHTML() {
5292
}
5393
#canvas-wrapper {
5494
position: relative;
55-
border: 4px solid var(--success-color);
95+
border: 4px solid var(--success-color, #2ecc71);
5696
border-radius: 12px;
5797
overflow: hidden;
5898
box-shadow: 0 15px 35px rgba(0,0,0,0.3);
5999
}
60100
#snakeCanvas {
61-
background-color: var(--panel-color);
101+
background-color: var(--panel-color, #222);
62102
background-image:
63-
linear-gradient(var(--border-color) 1px, transparent 1px),
64-
linear-gradient(90deg, var(--border-color) 1px, transparent 1px);
103+
linear-gradient(var(--border-color, rgba(255,255,255,0.05)) 1px, transparent 1px),
104+
linear-gradient(90deg, var(--border-color, rgba(255,255,255,0.05)) 1px, transparent 1px);
65105
background-size: 20px 20px;
66106
display: block;
67107
}
@@ -71,14 +111,18 @@ function getsnakeGameHTML() {
71111
gap: 15px;
72112
}
73113
.score-card {
74-
background: var(--accent-soft);
75-
border: 1px solid var(--accent-border);
76-
color: var(--text-color);
114+
background: var(--accent-soft, #fafafa);
115+
border: 1px solid var(--accent-border, #ddd);
116+
color: var(--text-color, #333);
77117
padding: 10px 25px;
78118
border-radius: 10px;
79119
text-align: center;
80120
min-width: 120px;
81-
box-shadow: var(--shadow);
121+
box-shadow: var(--shadow, 0 4px 6px rgba(0,0,0,0.1));
122+
}
123+
.high-score-card {
124+
border-color: #f1c40f;
125+
background: rgba(241, 196, 15, 0.05);
82126
}
83127
.score-card span {
84128
font-size: 12px;
@@ -92,15 +136,15 @@ function getsnakeGameHTML() {
92136
.button-group {
93137
display: flex;
94138
justify-content: center;
95-
gap: 30px; /* Space between buttons fixed */
139+
gap: 30px;
96140
margin-top: 10px;
97-
margin-right: 140px; /* Offset to align with canvas center */
141+
margin-right: 140px;
98142
}
99143
.instruction-box {
100144
margin-top: 20px;
101145
margin-right: 140px;
102146
text-align: center;
103-
color: var(--text-secondary);
147+
color: var(--text-secondary, #666);
104148
font-size: 15px;
105149
}
106150
#game-over-overlay {
@@ -109,31 +153,40 @@ function getsnakeGameHTML() {
109153
left: 0;
110154
width: 100%;
111155
height: 100%;
112-
background: var(--overlay-color);
156+
background: var(--overlay-color, rgba(0, 0, 0, 0.75));
113157
display: flex;
114158
flex-direction: column;
115159
justify-content: center;
116160
align-items: center;
117161
z-index: 10;
118-
color: var(--success-color);
162+
color: var(--success-color, #2ecc71);
119163
}
120164
#game-over-overlay h1 { font-size: 3rem; margin-bottom: 10px; }
121165
.hidden { display: none !important; }
122166
.btn-primary {
123167
padding: 12px 25px;
168+
cursor: pointer;
124169
}
125170
</style>
126171
`;
127172
}
128173

129-
// --- GAME LOGIC ---
174+
// --- GAME LOGIC STATE ---
130175
let direction = { x: 0, y: 0 };
131-
let speed = 7;
176+
let speed = 9; // System baseline operational configuration state
177+
let scoreMultiplier = 2; // Baseline multiplier scalar state
132178
let score = 0;
133179
let lastPaintTime = 0;
134180
let snakeArr = [{ x: 13, y: 10 }];
135181
let food = { x: 6, y: 7 };
136182

183+
// Profile configuration mapping matrices
184+
const CONFIG_DIFFICULTY = {
185+
easy: { speed: 5, multiplier: 1 },
186+
medium: { speed: 9, multiplier: 2 },
187+
hard: { speed: 14, multiplier: 3 }
188+
};
189+
137190
function main(ctime) {
138191
window.requestAnimationFrame(main);
139192
if ((ctime - lastPaintTime) / 1000 < 1 / speed) {
@@ -154,11 +207,44 @@ function isCollide(snake) {
154207
return false;
155208
}
156209

210+
function applyDifficultySettings() {
211+
const selector = document.getElementById('difficultySelect');
212+
if (selector) {
213+
const value = selector.value;
214+
speed = CONFIG_DIFFICULTY[value].speed;
215+
scoreMultiplier = CONFIG_DIFFICULTY[value].multiplier;
216+
}
217+
}
218+
219+
function updateBestScoreUI() {
220+
const bestScore = localStorage.getItem('snakeBestScore') || 0;
221+
const bestScoreElement = document.getElementById('best-score');
222+
if (bestScoreElement) {
223+
bestScoreElement.innerHTML = bestScore;
224+
}
225+
}
226+
227+
function checkAndSaveHighScore() {
228+
const currentBest = parseInt(localStorage.getItem('snakeBestScore') || '0', 10);
229+
if (score > currentBest) {
230+
localStorage.setItem('snakeBestScore', score);
231+
updateBestScoreUI();
232+
}
233+
}
234+
157235
function gameEngine() {
158236
if (isCollide(snakeArr)) {
159237
direction = { x: 0, y: 0 };
160238
document.getElementById('final-score').innerHTML = score;
161239
document.getElementById('game-over-overlay').classList.remove('hidden');
240+
241+
// Execute persistent local evaluations
242+
checkAndSaveHighScore();
243+
244+
// Restore controls visibility states
245+
const selector = document.getElementById('difficultySelect');
246+
if (selector) selector.disabled = false;
247+
162248
snakeArr = [{ x: 13, y: 10 }];
163249
score = 0;
164250
document.getElementById('score').innerHTML = score;
@@ -167,7 +253,7 @@ function gameEngine() {
167253

168254
// Eating food
169255
if (snakeArr[0].y === food.y && snakeArr[0].x === food.x) {
170-
score += 1;
256+
score += (1 * scoreMultiplier); // Scaled multiplier calculations
171257
document.getElementById('score').innerHTML = score;
172258
snakeArr.unshift({ x: snakeArr[0].x + direction.x, y: snakeArr[0].y + direction.y });
173259
let a = 2, b = 16;
@@ -199,11 +285,25 @@ function gameEngine() {
199285
ctx.fillRect(food.x * 20, food.y * 20, 18, 18);
200286
}
201287

202-
// IMPORTANT: Function to initialize listeners after HTML is loaded
288+
// Initialize execution listeners
203289
function initSnakeGame() {
204290
window.requestAnimationFrame(main);
291+
292+
// Set initial historic rendering metrics
293+
updateBestScoreUI();
294+
295+
// Map difficulty listener parameters
296+
const selector = document.getElementById('difficultySelect');
297+
if (selector) {
298+
selector.addEventListener('change', applyDifficultySettings);
299+
}
300+
// Initialize standard values configuration parameters
301+
applyDifficultySettings();
205302

206303
document.getElementById('startGameBtn').addEventListener('click', () => {
304+
applyDifficultySettings();
305+
// Prevent changing parameter matrices on an active engine run
306+
if (selector) selector.disabled = true;
207307
direction = { x: 1, y: 0 }; // Start moving right
208308
});
209309

@@ -213,13 +313,21 @@ function initSnakeGame() {
213313

214314
document.getElementById('overlayRestartBtn').addEventListener('click', () => {
215315
document.getElementById('game-over-overlay').classList.add('hidden');
316+
if (selector) selector.disabled = false;
216317
direction = { x: 0, y: 0 };
217318
snakeArr = [{ x: 13, y: 10 }];
218319
score = 0;
219320
document.getElementById('score').innerHTML = score;
220321
});
221322

222323
window.addEventListener('keydown', e => {
324+
// Change difficulty selection dynamic evaluations if arrow key registers
325+
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
326+
if (selector && !selector.disabled) {
327+
selector.disabled = true;
328+
}
329+
}
330+
223331
switch (e.key) {
224332
case "ArrowUp": if (direction.y !== 1) { direction.x = 0; direction.y = -1; } break;
225333
case "ArrowDown": if (direction.y !== -1) { direction.x = 0; direction.y = 1; } break;
@@ -228,3 +336,7 @@ function initSnakeGame() {
228336
}
229337
});
230338
}
339+
340+
// Global scope assignments
341+
window.getSnakeGameHTML = getSnakeGameHTML;
342+
window.initSnakeGame = initSnakeGame;

0 commit comments

Comments
 (0)