ai / index.html
GGSheng's picture
feat: deploy Gemma 4 to hf space
17e971c verified
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Take a Break - Snake Game</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
}
/* 背景动画粒子 */
.particles {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
overflow: hidden;
z-index: 0;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
animation: float 15s infinite;
}
@keyframes float {
0%, 100% {
transform: translateY(100vh) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100vh) rotate(720deg);
opacity: 0;
}
}
/* 主容器 */
.container {
position: relative;
z-index: 1;
text-align: center;
padding: 20px;
}
/* 标题区域 */
.header {
margin-bottom: 20px;
}
.header h1 {
color: #fff;
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
background: linear-gradient(90deg, #00ffff, #ff00ff, #ffff00);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from {
filter: drop-shadow(0 0 10px rgba(0, 255, 255, 0.5));
}
to {
filter: drop-shadow(0 0 20px rgba(255, 0, 255, 0.5));
}
}
.header p {
color: rgba(255, 255, 255, 0.7);
font-size: 1.1em;
}
/* 游戏区域 */
.game-wrapper {
display: inline-block;
background: rgba(0, 0, 0, 0.6);
border-radius: 20px;
padding: 20px;
box-shadow:
0 0 50px rgba(0, 255, 255, 0.2),
inset 0 0 50px rgba(0, 0, 0, 0.5);
border: 2px solid rgba(0, 255, 255, 0.3);
}
.score-board {
display: flex;
justify-content: space-around;
margin-bottom: 15px;
padding: 10px 15px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: #fff;
font-size: 1.1em;
font-weight: bold;
gap: 15px;
}
.score-item {
display: flex;
align-items: center;
gap: 10px;
}
.score-label {
color: rgba(255, 255, 255, 0.6);
font-size: 0.8em;
}
.score-value {
color: #00ffff;
font-size: 1.3em;
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
}
#gameCanvas {
border-radius: 10px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
background: #0a0a0a;
}
/* 控制按钮 */
.controls {
margin-top: 20px;
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 25px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-primary {
background: linear-gradient(135deg, #00ffff 0%, #0080ff 100%);
color: #000;
box-shadow: 0 4px 15px rgba(0, 255, 255, 0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(0, 255, 255, 0.6);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: #fff;
border: 2px solid rgba(255, 255, 255, 0.3);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.5);
}
/* 移动端控制 */
.mobile-controls {
display: none;
margin-top: 20px;
}
.d-pad {
display: grid;
grid-template-columns: repeat(3, 60px);
grid-template-rows: repeat(3, 60px);
gap: 5px;
justify-content: center;
}
.d-btn {
background: rgba(255, 255, 255, 0.1);
border: 2px solid rgba(0, 255, 255, 0.3);
border-radius: 10px;
color: #fff;
font-size: 1.5em;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.d-btn:hover, .d-btn:active {
background: rgba(0, 255, 255, 0.3);
border-color: #00ffff;
}
.d-btn:nth-child(2) { grid-column: 2; grid-row: 1; }
.d-btn:nth-child(4) { grid-column: 1; grid-row: 2; }
.d-btn:nth-child(5) { grid-column: 2; grid-row: 2; background: rgba(0, 255, 255, 0.2); }
.d-btn:nth-child(6) { grid-column: 3; grid-row: 2; }
.d-btn:nth-child(8) { grid-column: 2; grid-row: 3; }
/* 提示信息 */
.tips {
margin-top: 20px;
color: rgba(255, 255, 255, 0.5);
font-size: 0.9em;
}
.tips kbd {
background: rgba(255, 255, 255, 0.2);
padding: 2px 8px;
border-radius: 4px;
font-family: monospace;
}
/* 游戏结束覆盖层 */
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.9);
padding: 40px;
border-radius: 20px;
text-align: center;
display: none;
border: 2px solid #ff0066;
box-shadow: 0 0 50px rgba(255, 0, 102, 0.5);
}
.game-over h2 {
color: #ff0066;
font-size: 2em;
margin-bottom: 20px;
text-shadow: 0 0 20px rgba(255, 0, 102, 0.5);
}
.game-over .final-score {
color: #fff;
font-size: 1.5em;
margin-bottom: 20px;
}
/* 响应式 */
@media (max-width: 600px) {
.header h1 {
font-size: 1.8em;
}
#gameCanvas {
width: 100%;
max-width: 350px;
height: auto;
}
.mobile-controls {
display: block;
}
.tips {
display: none;
}
}
/* 登录链接 */
.login-link {
margin-top: 30px;
color: rgba(255, 255, 255, 0.5);
}
.login-link a {
color: #00ffff;
text-decoration: none;
border-bottom: 1px dashed #00ffff;
transition: all 0.3s;
}
.login-link a:hover {
color: #fff;
border-bottom-style: solid;
}
</style>
</head>
<body>
<!-- 背景粒子 -->
<div class="particles" id="particles"></div>
<div class="container">
<div class="header">
<h1>🐍 Snake Game</h1>
<p>Take a break and enjoy a game!</p>
</div>
<div class="game-wrapper">
<div class="score-board">
<div class="score-item">
<span class="score-label">SCORE</span>
<span class="score-value" id="score">0</span>
</div>
<div class="score-item" id="modeIndicator">
<span class="score-label" style="color: #ff00ff; text-shadow: 0 0 10px rgba(255, 0, 255, 0.5);">DEMO MODE</span>
</div>
<div class="score-item">
<span class="score-label">HIGH SCORE</span>
<span class="score-value" id="highScore">0</span>
</div>
</div>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div class="game-over" id="gameOver">
<h2>GAME OVER</h2>
<div class="final-score">Final Score: <span id="finalScore">0</span></div>
<button class="btn btn-primary" onclick="startGame()">PLAY AGAIN</button>
</div>
<div class="controls">
<button class="btn btn-primary" id="startBtn" onclick="startGame()">CLICK TO PLAY</button>
<button class="btn btn-secondary" onclick="pauseGame()" id="pauseBtn" disabled>PAUSE</button>
</div>
<!-- 移动端控制 -->
<div class="mobile-controls">
<div class="d-pad">
<div class="d-btn" onclick="changeDirection('up')"></div>
<div class="d-btn" onclick="changeDirection('left')"></div>
<div class="d-btn"></div>
<div class="d-btn" onclick="changeDirection('right')"></div>
<div class="d-btn" onclick="changeDirection('down')"></div>
</div>
</div>
<div class="tips">
Use <kbd></kbd> <kbd></kbd> <kbd></kbd> <kbd></kbd> or <kbd>W</kbd> <kbd>S</kbd> <kbd>A</kbd> <kbd>D</kbd> to control
</div>
</div>
<div class="login-link">
Need to manage your server? <a href="/login">Login here</a>
</div>
</div>
<script>
// 游戏配置
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let snake = [];
let food = {};
let direction = 'right';
let nextDirection = 'right';
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
let gameLoop = null;
let isPaused = false;
let isGameOver = false;
let gameSpeed = 100;
let isDemoMode = true; // 演示模式标志
let isUserMode = false; // 用户交互模式标志
// 初始化最高分显示
document.getElementById('highScore').textContent = highScore;
// 创建背景粒子
function createParticles() {
const container = document.getElementById('particles');
for (let i = 0; i < 50; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 15 + 's';
particle.style.animationDuration = (10 + Math.random() * 10) + 's';
container.appendChild(particle);
}
}
// 初始化游戏
function initGame() {
snake = [
{ x: 5, y: 10 },
{ x: 4, y: 10 },
{ x: 3, y: 10 }
];
direction = 'right';
nextDirection = 'right';
score = 0;
isGameOver = false;
isPaused = false;
gameSpeed = 100;
document.getElementById('score').textContent = score;
document.getElementById('gameOver').style.display = 'none';
if (isUserMode) {
document.getElementById('startBtn').textContent = 'RESTART';
} else {
document.getElementById('startBtn').textContent = 'CLICK TO PLAY';
}
spawnFood();
}
// AI自动寻路(演示模式)
function getAIDirection() {
const head = snake[0];
let bestDirection = direction;
let minDistance = Infinity;
const directions = ['up', 'down', 'left', 'right'];
const opposites = { 'up': 'down', 'down': 'up', 'left': 'right', 'right': 'left' };
for (const dir of directions) {
// 避免180度转向
if (opposites[dir] === direction) continue;
let nextX = head.x;
let nextY = head.y;
switch(dir) {
case 'up': nextY--; break;
case 'down': nextY++; break;
case 'left': nextX--; break;
case 'right': nextX++; break;
}
// 检查是否会撞墙或撞到自己
if (nextX < 0 || nextX >= tileCount || nextY < 0 || nextY >= tileCount) continue;
if (isSnakeAt(nextX, nextY)) continue;
// 计算到食物的距离
const distance = Math.abs(nextX - food.x) + Math.abs(nextY - food.y);
if (distance < minDistance) {
minDistance = distance;
bestDirection = dir;
}
}
return bestDirection;
}
// 生成食物
function spawnFood() {
do {
food = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
} while (isSnakeAt(food.x, food.y));
}
// 检查蛇是否在指定位置
function isSnakeAt(x, y) {
return snake.some(segment => segment.x === x && segment.y === y);
}
// 绘制游戏
function draw() {
// 清空画布
ctx.fillStyle = '#0a0a0a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格
ctx.strokeStyle = 'rgba(0, 255, 255, 0.05)';
ctx.lineWidth = 1;
for (let i = 0; i <= tileCount; i++) {
ctx.beginPath();
ctx.moveTo(i * gridSize, 0);
ctx.lineTo(i * gridSize, canvas.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i * gridSize);
ctx.lineTo(canvas.width, i * gridSize);
ctx.stroke();
}
// 绘制食物(发光效果)
const foodX = food.x * gridSize;
const foodY = food.y * gridSize;
// 食物光晕
const gradient = ctx.createRadialGradient(
foodX + gridSize/2, foodY + gridSize/2, 0,
foodX + gridSize/2, foodY + gridSize/2, gridSize
);
gradient.addColorStop(0, 'rgba(255, 0, 102, 0.8)');
gradient.addColorStop(1, 'rgba(255, 0, 102, 0)');
ctx.fillStyle = gradient;
ctx.fillRect(foodX - gridSize/2, foodY - gridSize/2, gridSize * 2, gridSize * 2);
// 食物本体
ctx.fillStyle = '#ff0066';
ctx.shadowColor = '#ff0066';
ctx.shadowBlur = 20;
ctx.fillRect(foodX + 2, foodY + 2, gridSize - 4, gridSize - 4);
ctx.shadowBlur = 0;
// 绘制蛇
snake.forEach((segment, index) => {
const x = segment.x * gridSize;
const y = segment.y * gridSize;
if (index === 0) {
// 蛇头
ctx.fillStyle = '#00ffff';
ctx.shadowColor = '#00ffff';
ctx.shadowBlur = 15;
} else {
// 蛇身渐变
const ratio = index / snake.length;
const r = Math.floor(0 * (1 - ratio) + 0 * ratio);
const g = Math.floor(255 * (1 - ratio) + 100 * ratio);
const b = Math.floor(255 * (1 - ratio) + 200 * ratio);
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
ctx.shadowColor = ctx.fillStyle;
ctx.shadowBlur = 10;
}
ctx.fillRect(x + 1, y + 1, gridSize - 2, gridSize - 2);
// 蛇眼
if (index === 0) {
ctx.fillStyle = '#000';
ctx.shadowBlur = 0;
const eyeSize = 3;
let eye1X, eye1Y, eye2X, eye2Y;
switch(direction) {
case 'up':
eye1X = x + 5; eye1Y = y + 5;
eye2X = x + 14; eye2Y = y + 5;
break;
case 'down':
eye1X = x + 5; eye1Y = y + 12;
eye2X = x + 14; eye2Y = y + 12;
break;
case 'left':
eye1X = x + 5; eye1Y = y + 5;
eye2X = x + 5; eye2Y = y + 12;
break;
case 'right':
eye1X = x + 12; eye1Y = y + 5;
eye2X = x + 12; eye2Y = y + 12;
break;
}
ctx.fillRect(eye1X, eye1Y, eyeSize, eyeSize);
ctx.fillRect(eye2X, eye2Y, eyeSize, eyeSize);
}
});
ctx.shadowBlur = 0;
}
// 更新游戏状态
function update() {
if (isPaused || isGameOver) return;
// 演示模式下AI自动控制
if (isDemoMode && !isUserMode) {
nextDirection = getAIDirection();
}
direction = nextDirection;
const head = { ...snake[0] };
switch(direction) {
case 'up': head.y--; break;
case 'down': head.y++; break;
case 'left': head.x--; break;
case 'right': head.x++; break;
}
// 检查碰撞
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
if (isDemoMode && !isUserMode) {
// 演示模式下自动重新开始
initGame();
return;
}
gameOver();
return;
}
if (isSnakeAt(head.x, head.y)) {
if (isDemoMode && !isUserMode) {
// 演示模式下自动重新开始
initGame();
return;
}
gameOver();
return;
}
snake.unshift(head);
// 检查是否吃到食物
if (head.x === food.x && head.y === food.y) {
score += 10;
document.getElementById('score').textContent = score;
// 更新最高分
if (score > highScore) {
highScore = score;
localStorage.setItem('snakeHighScore', highScore);
document.getElementById('highScore').textContent = highScore;
}
// 加速
if (gameSpeed > 50) {
gameSpeed -= 2;
clearInterval(gameLoop);
gameLoop = setInterval(gameStep, gameSpeed);
}
spawnFood();
} else {
snake.pop();
}
}
// 游戏步进
function gameStep() {
update();
draw();
}
// 游戏结束
function gameOver() {
isGameOver = true;
clearInterval(gameLoop);
document.getElementById('finalScore').textContent = score;
document.getElementById('gameOver').style.display = 'block';
}
// 切换到用户模式
function switchToUserMode() {
if (!isUserMode) {
isUserMode = true;
isDemoMode = false;
document.getElementById('startBtn').textContent = 'RESTART';
document.getElementById('pauseBtn').disabled = false;
// 更新模式指示器
const modeIndicator = document.getElementById('modeIndicator');
if (modeIndicator) {
modeIndicator.innerHTML = '<span class="score-label" style="color: #00ff00; text-shadow: 0 0 10px rgba(0, 255, 0, 0.5);">USER MODE</span>';
}
// 显示提示
showModeIndicator('USER MODE');
}
}
// 显示模式指示器
function showModeIndicator(text) {
const indicator = document.createElement('div');
indicator.style.cssText = `
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 255, 255, 0.9);
color: #000;
padding: 15px 30px;
border-radius: 10px;
font-weight: bold;
font-size: 1.2em;
z-index: 100;
animation: fadeInOut 1.5s ease-in-out;
`;
indicator.textContent = text;
document.querySelector('.game-wrapper').appendChild(indicator);
setTimeout(() => indicator.remove(), 1500);
}
// 添加淡入淡出动画
const style = document.createElement('style');
style.textContent = `
@keyframes fadeInOut {
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
50% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
}
`;
document.head.appendChild(style);
// 开始游戏
function startGame() {
clearInterval(gameLoop);
switchToUserMode();
initGame();
draw();
gameLoop = setInterval(gameStep, gameSpeed);
}
// 暂停游戏
function pauseGame() {
if (isGameOver) return;
isPaused = !isPaused;
event.target.textContent = isPaused ? 'RESUME' : 'PAUSE';
}
// 改变方向
function changeDirection(newDirection) {
// 第一次用户操作时切换到用户模式
switchToUserMode();
const opposites = {
'up': 'down',
'down': 'up',
'left': 'right',
'right': 'left'
};
if (opposites[newDirection] !== direction) {
nextDirection = newDirection;
}
}
// 键盘控制
document.addEventListener('keydown', (e) => {
switch(e.key) {
case 'ArrowUp':
case 'w':
case 'W':
e.preventDefault();
changeDirection('up');
break;
case 'ArrowDown':
case 's':
case 'S':
e.preventDefault();
changeDirection('down');
break;
case 'ArrowLeft':
case 'a':
case 'A':
e.preventDefault();
changeDirection('left');
break;
case 'ArrowRight':
case 'd':
case 'D':
e.preventDefault();
changeDirection('right');
break;
case ' ':
e.preventDefault();
pauseGame();
break;
}
});
// 触摸控制(防止页面滚动)
document.addEventListener('touchmove', (e) => {
e.preventDefault();
}, { passive: false });
// 点击画布切换到用户模式
canvas.addEventListener('click', () => {
switchToUserMode();
});
// 点击移动端控制按钮切换到用户模式
document.querySelectorAll('.d-btn').forEach(btn => {
btn.addEventListener('click', () => {
switchToUserMode();
});
});
// 初始化
createParticles();
initGame();
draw();
gameLoop = setInterval(gameStep, gameSpeed);
</script>
</body>
</html>