Co-creation-projects/YYHDBL-HelloCodeAgentCli/.helloagents/notes/note_20251219_171820_21.md
User input: 用户想创建一个贪吃蛇游戏的 MVP 版本,用 HTML、CSS、JS
Patch:
*** Begin Patch
*** Add File: testDemo/snake.js
// Snake Game MVP - JavaScript Implementation
document.addEventListener('DOMContentLoaded', function() {
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const highScoreElement = document.getElementById('highScore');
const speedElement = document.getElementById('speed');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resetBtn = document.getElementById('resetBtn');
// Game constants
const GRID_SIZE = 20;
const GRID_WIDTH = canvas.width / GRID_SIZE;
const GRID_HEIGHT = canvas.height / GRID_SIZE;
// Game state
let snake = [];
let food = {};
let direction = 'right';
let nextDirection = 'right';
let gameSpeed = 150; // ms
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
let gameRunning = false;
let gameLoop;
let foodsEaten = 0;
// Initialize game
function init() {
// Initialize snake
snake = [
{x: 5, y: 10},
{x: 4, y: 10},
{x: 3, y: 10}
];
// Generate first food
generateFood();
// Reset score and speed
score = 0;
foodsEaten = 0;
updateScore();
updateSpeedDisplay();
// Draw initial state
draw();
}
// Generate food at random position
function generateFood() {
let newFood;
let foodOnSnake;
do {
foodOnSnake = false;
newFood = {
x: Math.floor(Math.random() * GRID_WIDTH),
y: Math.floor(Math.random() * GRID_HEIGHT)
};
// Check if food is on snake
for (let segment of snake) {
if (segment.x === newFood.x && segment.y === newFood.y) {
foodOnSnake = true;
break;
}
}
} while (foodOnSnake);
food = newFood;
}
// Draw game elements
function draw() {
// Clear canvas
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw snake
ctx.fillStyle = '#00ff88';
for (let i = 0; i < snake.length; i++) {
const segment = snake[i];
// Head is slightly brighter
if (i === 0) {
ctx.fillStyle = '#00ff88';
} else {
ctx.fillStyle = '#00cc6a';
}
ctx.fillRect(
segment.x * GRID_SIZE,
segment.y * GRID_SIZE,
GRID_SIZE - 1,
GRID_SIZE - 1
);
}
// Draw food
ctx.fillStyle = '#ff5555';
ctx.beginPath();
ctx.arc(
food.x * GRID_SIZE + GRID_SIZE / 2,
food.y * GRID_SIZE + GRID_SIZE / 2,
GRID_SIZE / 2 - 1,
0,
Math.PI * 2
);
ctx.fill();
// Draw grid (optional, for debugging)
// drawGrid();
}
// Draw grid lines (for debugging)
function drawGrid() {
ctx.strokeStyle = '#333';
ctx.lineWidth = 0.5;
// Vertical lines
for (let x = 0; x <= canvas.width; x += GRID_SIZE) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
// Horizontal lines
for (let y = 0; y <= canvas.height; y += GRID_SIZE) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
}
// Update game state
function update() {
// Update direction
direction = nextDirection;
// Calculate new head position
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;
}
// Check wall collision
if (
head.x < 0 ||
head.x >= GRID_WIDTH ||
head.y < 0 ||
head.y >= GRID_HEIGHT
) {
gameOver();
return;
}
// Check self collision
for (let segment of snake) {
if (head.x === segment.x && head.y === segment.y) {
gameOver();
return;
}
}
// Add new head
snake.unshift(head);
// Check food collision
if (head.x === food.x && head.y === food.y) {
// Increase score
score += 10;
foodsEaten++;
// Update high score if needed
if (score > highScore) {
highScore = score;
localStorage.setItem('snakeHighScore', highScore);
}
// Increase speed every 5 foods
if (foodsEaten % 5 === 0 && gameSpeed > 50) {
gameSpeed -= 20;
updateSpeedDisplay();
clearInterval(gameLoop);
if (gameRunning) {
gameLoop = setInterval(gameStep, gameSpeed);
}
}
// Generate new food
generateFood();
updateScore();
} else {
// Remove tail if no food eaten
snake.pop();
}
// Draw updated state
draw();
}
// Game step (called by interval)
function gameStep() {
if (gameRunning) {
update();
}
}
// Update score display
function updateScore() {
scoreElement.textContent = score;
highScoreElement.textContent = highScore;
}
// Update speed display
function updateSpeedDisplay() {
if (gameSpeed >= 130) {
speedElement.textContent = 'Slow';
} else if (gameSpeed >= 90) {
speedElement.textContent = 'Normal';
} else if (gameSpeed >= 70) {
speedElement.textContent = 'Fast';
} else if (gameSpeed >= 50) {
speedElement.textContent = 'Very Fast';
} else {
speedElement.textContent = 'Extreme';
}
}
// Game over
function gameOver() {
gameRunning = false;
clearInterval(gameLoop);
// Show game over overlay
const gameOverDiv = document.createElement('div');
gameOverDiv.className = 'game-over';
gameOverDiv.innerHTML = `
<h2>Game Over!</h2>
<p>Final Score: ${score}</p>
<p>High Score: ${highScore}</p>
<button class="btn btn-primary" onclick="this.parentElement.remove();">OK</button>
`;
// Add to game board
const gameBoard = document.querySelector('.game-board');
gameBoard.appendChild(gameOverDiv);
// Update button states
startBtn.disabled = false;
pauseBtn.disabled = true;
}
// Start game
function startGame() {
if (!gameRunning) {
gameRunning = true;
gameLoop = setInterval(gameStep, gameSpeed);
// Update button states
startBtn.disabled = true;
pauseBtn.disabled = false;
}
}
// Pause game
function pauseGame() {
if (gameRunning) {
gameRunning = false;
clearInterval(gameLoop);
// Update button states
startBtn.disabled = false;
pauseBtn.disabled = true;
}
}
// Reset game
function resetGame() {
// Clear any game over overlay
const gameOverDiv = document.querySelector('.game-over');
if (gameOverDiv) {
gameOverDiv.remove();
}
// Stop game if running
if (gameRunning) {
clearInterval(gameLoop);
gameRunning = false;
}
// Reinitialize
init();
// Update button states
startBtn.disabled = false;
pauseBtn.disabled = true;
}
// Keyboard controls
document.addEventListener('keydown', function(e) {
switch (e.key) {
case 'ArrowUp':
case 'w':
case 'W':
if (direction !== 'down') nextDirection = 'up';
break;
case 'ArrowDown':
case 's':
case 'S':
if (direction !== 'up') nextDirection = 'down';
break;
case 'ArrowLeft':
case 'a':
case 'A':
if (direction !== 'right') nextDirection = 'left';
break;
case 'ArrowRight':
case 'd':
case 'D':
if (direction !== 'left') nextDirection = 'right';
break;
case ' ':
if (gameRunning) {
pauseGame();
} else {
startGame();
}
break;
}
});
// Button event listeners
startBtn.addEventListener('click', startGame);
pauseBtn.addEventListener('click', pauseGame);
resetBtn.addEventListener('click', resetGame);
// Initialize game on load
init();
// Update button states
pauseBtn.disabled = true;
});
*** End Patch
Files: