Moonfanz commited on
Commit
97691ee
·
verified ·
1 Parent(s): b64dd12

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +399 -18
index.html CHANGED
@@ -1,19 +1,400 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>贪吃蛇(对齐时无法转向)</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <style>
10
+ body {
11
+ font-family: 'Press Start 2P', cursive;
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ min-height: 100vh;
16
+ background-color: #1a1a1a; /* 深色背景 */
17
+ color: #ffffff;
18
+ flex-direction: column; /* 垂直排列 */
19
+ padding: 20px;
20
+ box-sizing: border-box;
21
+ }
22
+ #gameCanvas {
23
+ background-color: #000000; /* 黑色画布 */
24
+ border: 5px solid #33ff33; /* 亮绿色边框 */
25
+ display: block; /* 移除默认的内联间距 */
26
+ max-width: 100%; /* 确保画布在小屏幕上不会溢出 */
27
+ height: auto; /* 保持宽高比 */
28
+ aspect-ratio: 1 / 1; /* 使画布保持正方形 */
29
+ image-rendering: pixelated; /* 像素化渲染,保持复古风格 */
30
+ box-shadow: 0 0 20px #33ff33; /* 添加辉光效果 */
31
+ margin-bottom: 20px; /* 画布下方间距 */
32
+ }
33
+ .game-controls {
34
+ text-align: center;
35
+ margin-top: 15px;
36
+ }
37
+ .game-button {
38
+ font-family: 'Press Start 2P', cursive;
39
+ background-color: #33ff33; /* 亮绿色 */
40
+ color: #000000; /* 黑色文字 */
41
+ border: 2px solid #000000;
42
+ padding: 10px 20px;
43
+ margin: 5px;
44
+ cursor: pointer;
45
+ text-transform: uppercase;
46
+ transition: background-color 0.3s, transform 0.1s;
47
+ box-shadow: 3px 3px 0 #118811; /* 添加阴影 */
48
+ }
49
+ .game-button:hover {
50
+ background-color: #11cc11; /* 深一点的绿色 */
51
+ }
52
+ .game-button:active {
53
+ transform: translate(2px, 2px);
54
+ box-shadow: 1px 1px 0 #118811;
55
+ }
56
+ #messageBox {
57
+ position: absolute;
58
+ top: 50%;
59
+ left: 50%;
60
+ transform: translate(-50%, -50%);
61
+ background-color: rgba(0, 0, 0, 0.8);
62
+ color: #ff3333; /* 红色消息 */
63
+ padding: 30px;
64
+ border: 3px solid #ff3333;
65
+ border-radius: 10px; /* 圆角 */
66
+ display: none; /* 默认隐藏 */
67
+ text-align: center;
68
+ z-index: 10;
69
+ box-shadow: 0 0 15px #ff3333;
70
+ }
71
+ #messageText {
72
+ margin-bottom: 15px;
73
+ }
74
+ /* 响应式设计:小屏幕 */
75
+ @media (max-width: 600px) {
76
+ body {
77
+ padding: 10px;
78
+ }
79
+ #gameCanvas {
80
+ margin-bottom: 15px;
81
+ }
82
+ .game-button {
83
+ padding: 8px 15px;
84
+ font-size: 0.8em;
85
+ }
86
+ #messageBox {
87
+ padding: 20px;
88
+ font-size: 0.9em;
89
+ }
90
+ }
91
+ </style>
92
+ </head>
93
+ <body>
94
+
95
+ <h1 class="text-2xl mb-4 text-center">贪吃蛇</h1>
96
+ <div id="score" class="text-lg mb-2">分数: 0</div>
97
+
98
+ <canvas id="gameCanvas"></canvas>
99
+
100
+ <div class="game-controls">
101
+ <button id="resetButton" class="game-button">重新开始</button>
102
+ </div>
103
+
104
+ <div id="messageBox">
105
+ <div id="messageText">游戏结束!</div>
106
+ <button id="closeMessageButton" class="game-button" style="background-color: #ff3333; color: #000;">关闭</button>
107
+ </div>
108
+
109
+ <script>
110
+ const canvas = document.getElementById('gameCanvas');
111
+ const ctx = canvas.getContext('2d');
112
+ const scoreElement = document.getElementById('score');
113
+ const resetButton = document.getElementById('resetButton');
114
+ const messageBox = document.getElementById('messageBox');
115
+ const messageText = document.getElementById('messageText');
116
+ const closeMessageButton = document.getElementById('closeMessageButton');
117
+
118
+ // --- 游戏设置 ---
119
+ const grid = 20; // 每个格子的大小(像素)
120
+ const canvasSize = 400; // 画布大小(像素) - 确保是 grid 的倍数
121
+ canvas.width = canvasSize;
122
+ canvas.height = canvasSize;
123
+
124
+ // --- 游戏状态变量 ---
125
+ let snake; // 蛇的数组,每个元素是 {x, y}
126
+ let food; // 食物的位置 {x, y}
127
+ let score; // 分数
128
+ let direction; // 当前移动方向: 'up', 'down', 'left', 'right'
129
+ let nextDirection; // 玩家请求的下一个方向
130
+ let bufferedDirection; // 当与食物对齐时,缓存的下一个方向
131
+ let isAligned; // 蛇头是否与食物在同一行或同一列
132
+ let gameLoopTimeout; // 游戏循环的 Timeout ID
133
+ let isGameOver; // 游戏是否结束
134
+
135
+ // --- 初始化游戏 ---
136
+ function initGame() {
137
+ clearTimeout(gameLoopTimeout); // 清除之前的循环
138
+ hideMessage(); // 隐藏消息框
139
+ isGameOver = false;
140
+
141
+ // 初始化蛇的位置和方向
142
+ snake = [
143
+ { x: Math.floor(canvasSize / grid / 2) * grid, y: Math.floor(canvasSize / grid / 2) * grid } // 初始在中间
144
+ ];
145
+ direction = 'right'; // 初始向右移动
146
+ nextDirection = null; // 初始没有预定方向
147
+ bufferedDirection = null; // 初始没有缓存方向
148
+ isAligned = false; // 初始未对齐
149
+
150
+ // 初始化分数
151
+ score = 0;
152
+ scoreElement.textContent = `分数: ${score}`;
153
+
154
+ // 放置第一个食物
155
+ placeFood();
156
+
157
+ // 开始游戏循环
158
+ gameLoop();
159
+ }
160
+
161
+ // --- 放置食物 ---
162
+ function placeFood() {
163
+ let newFoodPosition;
164
+ do {
165
+ newFoodPosition = {
166
+ x: Math.floor(Math.random() * (canvasSize / grid)) * grid,
167
+ y: Math.floor(Math.random() * (canvasSize / grid)) * grid
168
+ };
169
+ } while (isFoodOnSnake(newFoodPosition)); // 确保食物不生成在蛇身上
170
+ food = newFoodPosition;
171
+ }
172
+
173
+ // --- 检查食物是否在蛇身上 ---
174
+ function isFoodOnSnake(position) {
175
+ for (let i = 0; i < snake.length; i++) {
176
+ if (snake[i].x === position.x && snake[i].y === position.y) {
177
+ return true;
178
+ }
179
+ }
180
+ return false;
181
+ }
182
+
183
+ // --- 游戏循环 ---
184
+ function gameLoop() {
185
+ if (isGameOver) {
186
+ return; // 如果游戏结束,停止循环
187
+ }
188
+
189
+ // 设置游戏速度(数字越小越快)
190
+ const speed = 150; // 毫秒
191
+
192
+ gameLoopTimeout = setTimeout(() => {
193
+ // 清除画布
194
+ ctx.clearRect(0, 0, canvasSize, canvasSize);
195
+
196
+ // 更新状态(移动,检查碰撞等)
197
+ update();
198
+
199
+ // 绘制游戏元素
200
+ draw();
201
+
202
+ // 继续下一帧
203
+ requestAnimationFrame(gameLoop); // 使用 requestAnimationFrame 替代 setTimeout 以获得更平滑的动画
204
+ }, speed);
205
+ }
206
+
207
+ // --- 更新游戏状态 ---
208
+ function update() {
209
+ // 检查蛇头是否与食物对齐
210
+ const head = snake[0];
211
+ isAligned = (head.x === food.x || head.y === food.y);
212
+
213
+ // --- 处理方向改变 ---
214
+ if (isAligned) {
215
+ // 如果对齐,并且玩家请求了新方向,则缓存它
216
+ if (nextDirection) {
217
+ // 检查缓存的方向是否与当前方向相反
218
+ if (!isOppositeDirection(nextDirection, direction)) {
219
+ bufferedDirection = nextDirection;
220
+ }
221
+ nextDirection = null; // 清除立即生效的请求
222
+ }
223
+ // 蛇继续按当前方向移动 (direction 不变)
224
+ } else {
225
+ // 如果不对齐
226
+ // 1. 检查是否有缓存的方向
227
+ if (bufferedDirection) {
228
+ // 检查缓存的方向是否与当前方向相反
229
+ if (!isOppositeDirection(bufferedDirection, direction)) {
230
+ direction = bufferedDirection; // 应用缓存的方向
231
+ }
232
+ bufferedDirection = null; // 清除缓存
233
+ nextDirection = null; // 也清除,避免冲突
234
+ }
235
+ // 2. 如果没有缓存,检查是否有新的请求方向
236
+ else if (nextDirection) {
237
+ // 检查请求的方向是否与当前方向相反
238
+ if (!isOppositeDirection(nextDirection, direction)) {
239
+ direction = nextDirection; // 应用请求的方向
240
+ }
241
+ nextDirection = null; // 清除请求
242
+ }
243
+ }
244
+
245
+ // --- 计算蛇头的新位置 ---
246
+ let newHead = { x: head.x, y: head.y };
247
+ switch (direction) {
248
+ case 'up': newHead.y -= grid; break;
249
+ case 'down': newHead.y += grid; break;
250
+ case 'left': newHead.x -= grid; break;
251
+ case 'right': newHead.x += grid; break;
252
+ }
253
+
254
+ // --- 检查碰撞 ---
255
+ // 1. 撞墙
256
+ if (newHead.x < 0 || newHead.x >= canvasSize || newHead.y < 0 || newHead.y >= canvasSize) {
257
+ gameOver("撞到墙了!");
258
+ return;
259
+ }
260
+ // 2. 撞自己
261
+ for (let i = 1; i < snake.length; i++) {
262
+ if (newHead.x === snake[i].x && newHead.y === snake[i].y) {
263
+ gameOver("撞到自己了!");
264
+ return;
265
+ }
266
+ }
267
+
268
+ // --- 移动蛇 ---
269
+ snake.unshift(newHead); // 在前面添加新头
270
+
271
+ // --- 检查是否“吃到”食物 ---
272
+ // 在这个版本中,蛇不会真的吃掉食物,只是触发食物重新生成
273
+ if (newHead.x === food.x && newHead.y === food.y) {
274
+ score++; // 增加分数
275
+ scoreElement.textContent = `分数: ${score}`;
276
+ placeFood(); // 重新放置食物
277
+ // 注意:蛇的尾巴不会移除,所以蛇会一直增长
278
+ } else {
279
+ // 如果没有吃到食物,移除尾巴 (正常移动)
280
+ // 如果想让蛇一直增长,注释掉下面这行
281
+ // snake.pop();
282
+ }
283
+
284
+ // 如果想让蛇一直增长,即使没碰到食物,就把 snake.pop() 移到上面 else 之外
285
+ // 如果只想在碰到食物时增长,保持现状即可
286
+
287
+ // --- 特殊规则:如果没碰到食物,移除尾巴 ---
288
+ // (这使得游戏更像传统贪吃蛇,但食物无法被“吃掉”来增长)
289
+ if (!(newHead.x === food.x && newHead.y === food.y)) {
290
+ snake.pop();
291
+ }
292
+
293
+
294
+ }
295
+
296
+ // --- 绘制游戏元素 ---
297
+ function draw() {
298
+ // 绘制蛇
299
+ ctx.fillStyle = '#33ff33'; // 蛇的颜色 (亮绿色)
300
+ snake.forEach(segment => {
301
+ ctx.fillRect(segment.x, segment.y, grid, grid);
302
+ ctx.strokeStyle = '#000000'; // 黑色边框,增加清晰度
303
+ ctx.strokeRect(segment.x, segment.y, grid, grid);
304
+ });
305
+
306
+ // 绘制食物
307
+ ctx.fillStyle = '#ff3333'; // 食物颜色 (红色)
308
+ ctx.fillRect(food.x, food.y, grid, grid);
309
+ ctx.strokeStyle = '#ffffff'; // 白色边框
310
+ ctx.strokeRect(food.x, food.y, grid, grid);
311
+
312
+ // 如果对齐,给蛇头或食物加点提示?(可选)
313
+ if (isAligned) {
314
+ ctx.fillStyle = 'rgba(255, 255, 0, 0.3)'; // 半透明黄色高亮
315
+ // 高亮蛇头和食物所在的行列
316
+ // ctx.fillRect(snake[0].x, 0, grid, canvasSize); // 高亮列
317
+ // ctx.fillRect(0, snake[0].y, canvasSize, grid); // 高亮行
318
+ // 或者只高亮蛇头
319
+ ctx.fillStyle = 'yellow';
320
+ ctx.fillRect(snake[0].x, snake[0].y, grid, grid);
321
+ }
322
+ }
323
+
324
+ // --- 检查是否是相反方向 ---
325
+ function isOppositeDirection(dir1, dir2) {
326
+ return (dir1 === 'up' && dir2 === 'down') ||
327
+ (dir1 === 'down' && dir2 === 'up') ||
328
+ (dir1 === 'left' && dir2 === 'right') ||
329
+ (dir1 === 'right' && dir2 === 'left');
330
+ }
331
+
332
+
333
+ // --- 处理键盘输入 ---
334
+ document.addEventListener('keydown', (e) => {
335
+ if (isGameOver) return; // 游戏结束时忽略输入
336
+
337
+ let requestedDir = null;
338
+ switch (e.key) {
339
+ case 'ArrowUp':
340
+ case 'w': // 添加 WASD 控制
341
+ requestedDir = 'up';
342
+ break;
343
+ case 'ArrowDown':
344
+ case 's':
345
+ requestedDir = 'down';
346
+ break;
347
+ case 'ArrowLeft':
348
+ case 'a':
349
+ requestedDir = 'left';
350
+ break;
351
+ case 'ArrowRight':
352
+ case 'd':
353
+ requestedDir = 'right';
354
+ break;
355
+ }
356
+
357
+ // 只有当请求的方向不是当前方向的直接反方向时,才设置 nextDirection
358
+ // 同时,也检查不能是 nextDirection 的反方向(防止快速连续按键导致180度转弯)
359
+ const effectiveCurrentDirection = bufferedDirection || direction; // 考虑缓存的方向
360
+ if (requestedDir &&
361
+ !isOppositeDirection(requestedDir, effectiveCurrentDirection) &&
362
+ (!nextDirection || !isOppositeDirection(requestedDir, nextDirection)))
363
+ {
364
+ nextDirection = requestedDir;
365
+ }
366
+ // 阻���方向键滚动页面
367
+ if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
368
+ e.preventDefault();
369
+ }
370
+ });
371
+
372
+ // --- 游戏结束处理 ---
373
+ function gameOver(reason) {
374
+ isGameOver = true;
375
+ clearTimeout(gameLoopTimeout); // 停止游戏循环
376
+ showMessage(`游戏结束! ${reason}<br>最终分数: ${score}`);
377
+ }
378
+
379
+ // --- 显示消息框 ---
380
+ function showMessage(msg) {
381
+ messageText.innerHTML = msg; // 使用 innerHTML 以支持 <br>
382
+ messageBox.style.display = 'block';
383
+ }
384
+
385
+ // --- 隐藏消息框 ---
386
+ function hideMessage() {
387
+ messageBox.style.display = 'none';
388
+ }
389
+
390
+ // --- 事件监听器 ---
391
+ resetButton.addEventListener('click', initGame);
392
+ closeMessageButton.addEventListener('click', hideMessage);
393
+
394
+ // --- 初始启动游戏 ---
395
+ initGame();
396
+
397
+ </script>
398
+
399
+ </body>
400
  </html>