Snake Game AI Coding Test
Snake Game
Use arrow keys to control the snake. Eat the red food to grow and earn points. Avoid walls and yourself!
Score: 0
High Score: 0
Game Over!
Use Arrow Keys (↑, ↓, ←, →) to control direction
Source Code
<div class="snake-game-wrapper">
<style>
.snake-game-wrapper {
max-width: 600px;
margin: 0 auto;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
text-align: center;
color: #333;
}
.game-header {
margin-bottom: 15px;
}
.instructions {
background: #f8f9fa;
padding: 12px;
border-radius: 8px;
margin-bottom: 15px;
font-size: 14px;
color: #666;
border: 1px solid #e9ecef;
}
.score-container {
display: flex;
justify-content: center;
gap: 30px;
margin-bottom: 15px;
font-weight: bold;
font-size: 18px;
}
.current-score {
color: #4CAF50;
}
.high-score {
color: #2196F3;
}
.game-container {
position: relative;
margin-bottom: 20px;
}
.game-canvas {
background-color: #1a1a1a;
border-radius: 8px;
border: 2px solid #333;
display: block;
margin: 0 auto;
}
.game-button {
background: #4CAF50;
color: white;
border: none;
padding: 12px 30px;
font-size: 16px;
font-weight: bold;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.game-button:hover {
background: #45a049;
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0,0,0,0.15);
}
.game-button:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.85);
color: white;
padding: 20px 30px;
border-radius: 8px;
font-size: 24px;
font-weight: bold;
display: none;
box-shadow: 0 8px 16px rgba(0,0,0,0.3);
}
.controls-info {
font-size: 13px;
color: #777;
margin-top: 15px;
}
</style>
<div class="game-header">
<h2>Snake Game</h2>
<div class="instructions">
Use arrow keys to control the snake. Eat the red food to grow and earn points. Avoid walls and yourself!
</div>
</div>
<div class="score-container">
<div class="current-score">Score: <span id="currentScore">0</span></div>
<div class="high-score">High Score: <span id="highScore">0</span></div>
</div>
<div class="game-container">
<canvas class="game-canvas" width="400" height="400"></canvas>
<div class="game-over" id="gameOverText">Game Over!</div>
</div>
<button class="game-button" id="startButton">Start Game</button>
<div class="controls-info">Use Arrow Keys (↑, ↓, ←, →) to control direction</div>
<script>
// Game state and configuration
const gameState = {
canvas: null,
ctx: null,
snake: [],
food: { x: 0, y: 0 },
direction: { x: 1, y: 0 },
nextDirection: { x: 1, y: 0 },
gridSize: 20,
gameLoopId: null,
score: 0,
highScore: 0,
isRunning: false,
speed: 100 // milliseconds per move
};
// Initialize game
function initGame() {
gameState.canvas = document.querySelector('.game-canvas');
gameState.ctx = gameState.canvas.getContext('2d');
// Load high score from localStorage
gameState.highScore = parseInt(localStorage.getItem('snakeHighScore')) || 0;
document.getElementById('highScore').textContent = gameState.highScore;
// Set up event listeners
document.addEventListener('keydown', handleKeyPress);
document.getElementById('startButton').addEventListener('click', startGame);
// Draw initial static state
drawStatic();
}
// Draw static elements (border, initial text)
function drawStatic() {
gameState.ctx.fillStyle = '#1a1a1a';
gameState.ctx.fillRect(0, 0, gameState.canvas.width, gameState.canvas.height);
// Draw grid pattern (subtle)
gameState.ctx.strokeStyle = '#222';
gameState.ctx.lineWidth = 0.5;
for (let x = 0; x <= gameState.canvas.width; x += gameState.gridSize) {
gameState.ctx.beginPath();
gameState.ctx.moveTo(x, 0);
gameState.ctx.lineTo(x, gameState.canvas.height);
gameState.ctx.stroke();
}
for (let y = 0; y <= gameState.canvas.height; y += gameState.gridSize) {
gameState.ctx.beginPath();
gameState.ctx.moveTo(0, y);
gameState.ctx.lineTo(gameState.canvas.width, y);
gameState.ctx.stroke();
}
// Initial instruction text
gameState.ctx.fillStyle = '#666';
gameState.ctx.font = '16px Arial';
gameState.ctx.textAlign = 'center';
gameState.ctx.fillText('Click "Start Game" to begin',
gameState.canvas.width/2,
gameState.canvas.height/2);
}
// Start or restart the game
function startGame() {
if (gameState.isRunning) {
clearInterval(gameState.gameLoopId);
}
// Reset game state
gameState.snake = [
{ x: 10, y: 10 },
{ x: 9, y: 10 },
{ x: 8, y: 10 }
];
gameState.direction = { x: 1, y: 0 };
gameState.nextDirection = { x: 1, y: 0 };
gameState.score = 0;
gameState.isRunning = true;
// Update UI
document.getElementById('currentScore').textContent = '0';
document.getElementById('gameOverText').style.display = 'none';
document.getElementById('startButton').textContent = 'Restart Game';
// Generate initial food
generateFood();
// Start game loop
gameState.gameLoopId = setInterval(gameLoop, gameState.speed);
// Draw initial frame
draw();
}
// Main game loop
function gameLoop() {
// Update direction from queued input
gameState.direction = { ...gameState.nextDirection };
// Calculate new head position
const head = { ...gameState.snake[0] };
head.x += gameState.direction.x;
head.y += gameState.direction.y;
// Check for collisions
if (isCollision(head)) {
gameOver();
return;
}
// Add new head
gameState.snake.unshift(head);
// Check if food is eaten
if (head.x === gameState.food.x && head.y === gameState.food.y) {
// Increase score
gameState.score += 10;
document.getElementById('currentScore').textContent = gameState.score;
// Update high score if needed
if (gameState.score > gameState.highScore) {
gameState.highScore = gameState.score;
document.getElementById('highScore').textContent = gameState.highScore;
localStorage.setItem('snakeHighScore', gameState.highScore.toString());
}
// Generate new food
generateFood();
} else {
// Remove tail if no food eaten
gameState.snake.pop();
}
// Draw updated game state
draw();
}
// Check for collisions
function isCollision(head) {
// Wall collision
if (head.x < 0 || head.x >= gameState.canvas.width / gameState.gridSize ||
head.y < 0 || head.y >= gameState.canvas.height / gameState.gridSize) {
return true;
}
// Self collision (skip head itself)
for (let i = 1; i < gameState.snake.length; i++) {
if (gameState.snake[i].x === head.x && gameState.snake[i].y === head.y) {
return true;
}
}
return false;
}
// Generate food at random location (not on snake)
function generateFood() {
let newFood;
let foodOnSnake;
do {
foodOnSnake = false;
newFood = {
x: Math.floor(Math.random() * (gameState.canvas.width / gameState.gridSize)),
y: Math.floor(Math.random() * (gameState.canvas.height / gameState.gridSize))
};
// Check if food would spawn on snake
for (const segment of gameState.snake) {
if (segment.x === newFood.x && segment.y === newFood.y) {
foodOnSnake = true;
break;
}
}
} while (foodOnSnake);
gameState.food = newFood;
}
// Draw everything on canvas
function draw() {
// Clear canvas
gameState.ctx.fillStyle = '#1a1a1a';
gameState.ctx.fillRect(0, 0, gameState.canvas.width, gameState.canvas.height);
// Draw grid (subtle)
gameState.ctx.strokeStyle = '#222';
gameState.ctx.lineWidth = 0.5;
for (let x = 0; x <= gameState.canvas.width; x += gameState.gridSize) {
gameState.ctx.beginPath();
gameState.ctx.moveTo(x, 0);
gameState.ctx.lineTo(x, gameState.canvas.height);
gameState.ctx.stroke();
}
for (let y = 0; y <= gameState.canvas.height; y += gameState.gridSize) {
gameState.ctx.beginPath();
gameState.ctx.moveTo(0, y);
gameState.ctx.lineTo(gameState.canvas.width, y);
gameState.ctx.stroke();
}
// Draw snake
gameState.snake.forEach((segment, index) => {
const x = segment.x * gameState.gridSize;
const y = segment.y * gameState.gridSize;
// Head is slightly brighter
if (index === 0) {
gameState.ctx.fillStyle = '#66BB6A';
} else {
gameState.ctx.fillStyle = '#4CAF50';
}
// Draw rounded segments
gameState.ctx.beginPath();
gameState.ctx.roundRect(x, y, gameState.gridSize, gameState.gridSize, 4);
gameState.ctx.fill();
// Add subtle border
gameState.ctx.strokeStyle = '#388E3C';
gameState.ctx.lineWidth = 1;
gameState.ctx.stroke();
});
// Draw food
const foodX = gameState.food.x * gameState.gridSize;
const foodY = gameState.food.y * gameState.gridSize;
gameState.ctx.fillStyle = '#f44336';
gameState.ctx.beginPath();
gameState.ctx.arc(foodX + gameState.gridSize/2,
foodY + gameState.gridSize/2,
gameState.gridSize/2 - 1, 0, Math.PI * 2);
gameState.ctx.fill();
// Add shine effect to food
gameState.ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
gameState.ctx.beginPath();
gameState.ctx.arc(foodX + gameState.gridSize/2 - 3,
foodY + gameState.gridSize/2 - 3,
gameState.gridSize/6, 0, Math.PI * 2);
gameState.ctx.fill();
}
// Handle keyboard input
function handleKeyPress(event) {
if (!gameState.isRunning) return;
// Prevent default behavior for arrow keys to avoid page scrolling
if ([37, 38, 39, 40].includes(event.keyCode)) {
event.preventDefault();
}
let newDirection = { ...gameState.nextDirection };
// Update direction based on key press, prevent opposite direction
switch (event.keyCode) {
case 37: // Left arrow
if (gameState.direction.x !== 1) newDirection = { x: -1, y: 0 };
break;
case 38: // Up arrow
if (gameState.direction.y !== 1) newDirection = { x: 0, y: -1 };
break;
case 39: // Right arrow
if (gameState.direction.x !== -1) newDirection = { x: 1, y: 0 };
break;
case 40: // Down arrow
if (gameState.direction.y !== -1) newDirection = { x: 0, y: 1 };
break;
}
// Only update if direction actually changed
if (newDirection.x !== gameState.nextDirection.x ||
newDirection.y !== gameState.nextDirection.y) {
gameState.nextDirection = newDirection;
}
}
// Handle game over
function gameOver() {
gameState.isRunning = false;
clearInterval(gameState.gameLoopId);
// Show game over text
const gameOverElement = document.getElementById('gameOverText');
gameOverElement.textContent = `Game Over! Score: ${gameState.score}`;
gameOverElement.style.display = 'block';
}
// Initialize game when page loads
window.addEventListener('DOMContentLoaded', initGame);
</script>
</div>
</div>
Source Prompt (Used on DeepSeek
Create a fully functional Snake game as a SINGLE DIV suitable for embedding in a Blogger post. Critical requirements:
STRUCTURE:
- Start with <div class="snake-game-wrapper"> and end with </div>
- NO <!DOCTYPE>, <html>, <head>, or <body> tags
- Include <style> tag inside the div with scoped CSS
- Include <script> tag inside the div
- All code must be self-contained with no external dependencies
FUNCTIONALITY:
- Canvas-based Snake game (400x400px recommended)
- Arrow keys control direction (prevent opposite-direction moves)
- Snake starts at center, length 3, moving right automatically on game start
- Food spawns randomly (never on snake body)
- Snake grows one segment per food eaten
- Score increments with each food
- High score persists using localStorage
- Game over on wall collision or self-collision
- Clear "Start Game" button to begin/restart
- Display current score and high score
GAMEPLAY REQUIREMENTS:
- Game must start moving immediately when Start is clicked (don't wait for arrow key)
- Movement must be smooth and continuous at ~100ms intervals
- Direction changes must queue properly (no skipped inputs)
- Game loop must stop cleanly on game over
STYLE:
- Container max-width: 600px, centered
- Dark canvas background (#1a1a1a)
- Green snake (#4CAF50), red food (#f44336)
- Clean, modern UI with good contrast
- Brief instructions above canvas
- Professional button styling
CODE QUALITY:
- Clear comments explaining game logic
- Defensive coding (check boundaries, prevent bugs)
- Use const/let appropriately
- Test that the game actually works before returning
The game must be playable immediately when embedded - no debugging required.
Comments