add page for in-browser Snake game
Hunter
Wednesday, July 23rd, 2025 at 4:45 PM
1 +<html>
2 +<head>
3 + <title>SNAKE</title>
4 + <style>
5 + body {
6 + background-color: white;
7 + font-family: Arial, sans-serif;
8 + text-align: center;
9 + margin: 0;
10 + padding: 20px;
11 + }
12 +
13 + h1 {
14 + margin: 20px 0;
15 + }
16 +
17 + #gameBoard {
18 + display: inline-block;
19 + border: 2px solid #333;
20 + background-color: white;
21 + font-size: 16px;
22 + line-height: 1;
23 + font-family: monospace;
24 + }
25 +
26 + .row {
27 + display: block;
28 + margin: 0;
29 + padding: 0;
30 + }
31 +
32 + #score, #highScore {
33 + font-size: 20px;
34 + margin: 10px 0;
35 + }
36 +
37 + #gameOver, #startHint {
38 + font-size: 24px;
39 + color: red;
40 + margin: 20px 0;
41 + }
42 +
43 + #startHint {
44 + color: #333;
45 + }
46 +
47 + #mobileControls {
48 + display: none;
49 + margin: 20px 0;
50 + }
51 +
52 + .control-pad {
53 + display: inline-block;
54 + position: relative;
55 + width: 144px;
56 + height: 144px;
57 + }
58 +
59 + .control-btn {
60 + position: absolute;
61 + width: 48px;
62 + height: 48px;
63 + font-size: 48px;
64 + cursor: pointer;
65 + user-select: none;
66 + touch-action: manipulation;
67 + line-height: 48px;
68 + text-align: center;
69 + display: flex;
70 + align-items: center;
71 + justify-content: center;
72 + }
73 +
74 + .control-btn:active {
75 + opacity: 0.6;
76 + }
77 +
78 + #upBtn { top: 0; left: 48px; }
79 + #downBtn { bottom: 0; left: 48px; }
80 + #leftBtn { top: 48px; left: 0; }
81 + #rightBtn { top: 48px; right: 0; }
82 + #centerBtn { top: 48px; left: 48px; display: none; }
83 +
84 + #mobileGameOver, #mobileStartHint {
85 + font-size: 20px;
86 + color: red;
87 + margin-top: 20px;
88 + display: none;
89 + }
90 +
91 + #mobileStartHint {
92 + color: #333;
93 + }
94 +
95 + @media (pointer: none), (pointer: coarse) {
96 + #mobileControls {
97 + display: block;
98 + }
99 +
100 + #gameOver {
101 + display: none !important;
102 + }
103 +
104 + #startHint {
105 + display: none !important;
106 + }
107 + }
108 + </style>
109 +</head>
110 +<body>
111 + <h1>SNAKE</h1>
112 + <div id="gameBoard"></div>
113 + <div id="score">Score: 0</div>
114 + <div id="highScore">High Score: 0</div>
115 + <div id="gameOver"></div>
116 + <div id="startHint">Press Space to start!</div>
117 +
118 + <div id="mobileControls">
119 + <div class="control-pad">
120 + <div class="control-btn" id="upBtn">🔼</div>
121 + <div class="control-btn" id="leftBtn">◀️</div>
122 + <div class="control-btn" id="rightBtn">▶️</div>
123 + <div class="control-btn" id="downBtn">🔽</div>
124 + <div class="control-btn" id="centerBtn">❇️</div>
125 + </div>
126 + <div id="mobileGameOver"></div>
127 + <div id="mobileStartHint">Press ❇️ to start!</div>
128 + </div>
129 +
130 + <script>
131 + const BOARD_SIZE = 20;
132 + const EMPTY = '⬜';
133 + const SNAKE = '🟩';
134 + const APPLE = '🟥';
135 +
136 + let board = [];
137 + let snake = [{x: 10, y: 10}];
138 + let direction = {x: 0, y: -1};
139 + let nextDirection = {x: 0, y: -1};
140 + let apple = {x: 5, y: 5};
141 + let score = 0;
142 + let highScore = 0;
143 + let gameRunning = false;
144 + let gameStarted = false;
145 + let gameSpeed = 200;
146 + let gameInterval;
147 +
148 + function initBoard() {
149 + for (let y = 0; y < BOARD_SIZE; y++) {
150 + board[y] = [];
151 + for (let x = 0; x < BOARD_SIZE; x++) {
152 + board[y][x] = EMPTY;
153 + }
154 + }
155 + }
156 +
157 + function placeApple() {
158 + let newApple;
159 + do {
160 + newApple = {
161 + x: Math.floor(Math.random() * BOARD_SIZE),
162 + y: Math.floor(Math.random() * BOARD_SIZE)
163 + };
164 + } while (snake.some(segment => segment.x === newApple.x && segment.y === newApple.y));
165 +
166 + apple = newApple;
167 + }
168 +
169 + function updateBoard() {
170 + // Clear board
171 + for (let y = 0; y < BOARD_SIZE; y++) {
172 + for (let x = 0; x < BOARD_SIZE; x++) {
173 + board[y][x] = EMPTY;
174 + }
175 + }
176 +
177 + // Place snake
178 + snake.forEach(segment => {
179 + if (segment.x >= 0 && segment.x < BOARD_SIZE && segment.y >= 0 && segment.y < BOARD_SIZE) {
180 + board[segment.y][segment.x] = SNAKE;
181 + }
182 + });
183 +
184 + // Place apple
185 + board[apple.y][apple.x] = APPLE;
186 + }
187 +
188 + function renderBoard() {
189 + const gameBoard = document.getElementById('gameBoard');
190 + gameBoard.innerHTML = '';
191 +
192 + for (let y = 0; y < BOARD_SIZE; y++) {
193 + const row = document.createElement('div');
194 + row.className = 'row';
195 + for (let x = 0; x < BOARD_SIZE; x++) {
196 + row.innerHTML += board[y][x];
197 + }
198 + gameBoard.appendChild(row);
199 + }
200 + }
201 +
202 + function moveSnake() {
203 + if (!gameRunning) return;
204 +
205 + // Use the next direction for movement
206 + direction = nextDirection;
207 +
208 + const head = {
209 + x: snake[0].x + direction.x,
210 + y: snake[0].y + direction.y
211 + };
212 +
213 + // Check wall collision
214 + if (head.x < 0 || head.x >= BOARD_SIZE || head.y < 0 || head.y >= BOARD_SIZE) {
215 + gameOver();
216 + return;
217 + }
218 +
219 + // Check self collision
220 + if (snake.some(segment => segment.x === head.x && segment.y === head.y)) {
221 + gameOver();
222 + return;
223 + }
224 +
225 + snake.unshift(head);
226 +
227 + // Check apple collision
228 + if (head.x === apple.x && head.y === apple.y) {
229 + score += 10;
230 + document.getElementById('score').textContent = `Score: ${score}`;
231 + updateHighScore();
232 +
233 + // Increase speed
234 + gameSpeed = Math.max(50, 200 - Math.floor(score / 1));
235 + clearInterval(gameInterval);
236 + gameInterval = setInterval(gameLoop, gameSpeed);
237 +
238 + placeApple();
239 + } else {
240 + snake.pop();
241 + }
242 + }
243 +
244 + function gameOver() {
245 + gameRunning = false;
246 + document.getElementById('gameOver').innerHTML = 'Game Over!<br>Press Space to restart';
247 +
248 + // Show restart button and mobile message
249 + const centerBtn = document.getElementById('centerBtn');
250 + const mobileGameOver = document.getElementById('mobileGameOver');
251 + if (centerBtn) {
252 + centerBtn.textContent = '🔄';
253 + centerBtn.style.display = 'block';
254 + }
255 + if (mobileGameOver) {
256 + mobileGameOver.innerHTML = 'Game Over!<br>Press 🔄 to restart';
257 + mobileGameOver.style.display = 'block';
258 + }
259 +
260 + clearInterval(gameInterval);
261 + }
262 +
263 + function startGame() {
264 + // Reset game state first
265 + snake = [{x: 10, y: 10}];
266 + direction = {x: 0, y: -1};
267 + nextDirection = {x: 0, y: -1};
268 + score = 0;
269 + gameSpeed = 200;
270 + document.getElementById('score').textContent = `Score: ${score}`;
271 +
272 + // Only place new apple if restarting (not first time)
273 + if (score > 0 || gameStarted) {
274 + placeApple();
275 + }
276 +
277 + gameRunning = true;
278 + gameStarted = true;
279 +
280 + // Hide all hint messages
281 + document.getElementById('startHint').style.display = 'none';
282 + document.getElementById('mobileStartHint').style.display = 'none';
283 + document.getElementById('gameOver').textContent = '';
284 +
285 + const mobileGameOver = document.getElementById('mobileGameOver');
286 + if (mobileGameOver) {
287 + mobileGameOver.style.display = 'none';
288 + }
289 +
290 + // Hide center button
291 + const centerBtn = document.getElementById('centerBtn');
292 + if (centerBtn) {
293 + centerBtn.style.display = 'none';
294 + }
295 +
296 + updateBoard();
297 + renderBoard();
298 +
299 + // Start game loop
300 + clearInterval(gameInterval);
301 + gameInterval = setInterval(gameLoop, gameSpeed);
302 + }
303 +
304 + function loadHighScore() {
305 + const saved = localStorage.getItem('snakeHighScore');
306 + highScore = saved ? parseInt(saved) : 0;
307 + document.getElementById('highScore').textContent = `High Score: ${highScore}`;
308 + }
309 +
310 + function saveHighScore() {
311 + localStorage.setItem('snakeHighScore', highScore.toString());
312 + }
313 +
314 + function updateHighScore() {
315 + if (score > highScore) {
316 + highScore = score;
317 + document.getElementById('highScore').textContent = `High Score: ${highScore}`;
318 + saveHighScore();
319 + }
320 + }
321 +
322 + function gameLoop() {
323 + moveSnake();
324 + updateBoard();
325 + renderBoard();
326 + }
327 +
328 + function setDirection(newDirection) {
329 + if (!gameRunning) return;
330 +
331 + // Allow any direction when snake is only 1 segment long
332 + if (snake.length === 1) {
333 + nextDirection = newDirection;
334 + return;
335 + }
336 +
337 + // Prevent reversing into self by checking current direction
338 + if (newDirection.x !== 0 && direction.x !== -newDirection.x) {
339 + nextDirection = newDirection;
340 + } else if (newDirection.y !== 0 && direction.y !== -newDirection.y) {
341 + nextDirection = newDirection;
342 + }
343 + }
344 +
345 + document.addEventListener('keydown', (event) => {
346 + if (event.code === 'Space') {
347 + if (!gameRunning) {
348 + startGame();
349 + }
350 + return;
351 + }
352 +
353 + if (!gameRunning) return;
354 +
355 + switch(event.code) {
356 + case 'ArrowUp':
357 + setDirection({x: 0, y: -1});
358 + break;
359 + case 'ArrowDown':
360 + setDirection({x: 0, y: 1});
361 + break;
362 + case 'ArrowLeft':
363 + setDirection({x: -1, y: 0});
364 + break;
365 + case 'ArrowRight':
366 + setDirection({x: 1, y: 0});
367 + break;
368 + }
369 + });
370 +
371 + // Mobile controls
372 + document.getElementById('upBtn').addEventListener('click', () => {
373 + if (gameRunning) setDirection({x: 0, y: -1});
374 + });
375 + document.getElementById('downBtn').addEventListener('click', () => {
376 + if (gameRunning) setDirection({x: 0, y: 1});
377 + });
378 + document.getElementById('leftBtn').addEventListener('click', () => {
379 + if (gameRunning) setDirection({x: -1, y: 0});
380 + });
381 + document.getElementById('rightBtn').addEventListener('click', () => {
382 + if (gameRunning) setDirection({x: 1, y: 0});
383 + });
384 + document.getElementById('centerBtn').addEventListener('click', () => {
385 + if (!gameRunning) {
386 + startGame();
387 + }
388 + });
389 +
390 + // Initialize game
391 + loadHighScore();
392 + initBoard();
393 + updateBoard();
394 + renderBoard();
395 +
396 + // Show initial start hints and button
397 + document.getElementById('startHint').style.display = 'block';
398 + document.getElementById('mobileStartHint').style.display = 'block';
399 + document.getElementById('centerBtn').style.display = 'block';
400 + document.getElementById('centerBtn').textContent = '❇️';
401 + </script>
402 +</body>
403 +</html>