```html Snake Game
Score: 0
``` ```typescript // snake.ts content type Point = { x: number; y: number }; type Direction = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'; class SnakeGame { private canvas: HTMLCanvasElement; private ctx: CanvasRenderingContext2D; private snake: Point[]; private direction: Direction; private nextDirection: Direction; private food: Point; private score: number; private gameOver: boolean; private gridSize: number; private tileCount: number; private scoreElement: HTMLElement; constructor() { this.canvas = document.getElementById('gameCanvas') as HTMLCanvasElement; this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D; this.gridSize = 20; this.tileCount = this.canvas.width / this.gridSize; this.snake = [{ x: 10, y: 10 }]; this.direction = 'RIGHT'; this.nextDirection = 'RIGHT'; this.food = this.generateFood(); this.score = 0; this.gameOver = false; this.scoreElement = document.getElementById('score') as HTMLElement; this.setupEventListeners(); this.gameLoop(); } private setupEventListeners(): void { document.addEventListener('keydown', (e) => { switch (e.key) { case 'ArrowUp': if (this.direction !== 'DOWN') this.nextDirection = 'UP'; break; case 'ArrowDown': if (this.direction !== 'UP') this.nextDirection = 'DOWN'; break; case 'ArrowLeft': if (this.direction !== 'RIGHT') this.nextDirection = 'LEFT'; break; case 'ArrowRight': if (this.direction !== 'LEFT') this.nextDirection = 'RIGHT'; break; } }); } private generateFood(): Point { let food: Point; do { food = { x: Math.floor(Math.random() * this.tileCount), y: Math.floor(Math.random() * this.tileCount) }; } while (this.snake.some(segment => segment.x === food.x && segment.y === food.y)); return food; } private draw(): void { // Clear canvas this.ctx.fillStyle = '#fff'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Draw snake this.snake.forEach((segment, index) => { this.ctx.fillStyle = index === 0 ? '#4CAF50' : '#8BC34A'; this.ctx.fillRect(segment.x * this.gridSize, segment.y * this.gridSize, this.gridSize, this.gridSize); this.ctx.strokeStyle = '#fff'; this.ctx.strokeRect(segment.x * this.gridSize, segment.y * this.gridSize, this.gridSize, this.gridSize); }); // Draw food this.ctx.fillStyle = '#F44336'; this.ctx.fillRect(this.food.x * this.gridSize, this.food.y * this.gridSize, this.gridSize, this.gridSize); } private update(): void { if (this.gameOver) return; this.direction = this.nextDirection; // Calculate new head position const head = { ...this.snake[0] }; switch (this.direction) { case 'UP': head.y--; break; case 'DOWN': head.y++; break; case 'LEFT': head.x--; break; case 'RIGHT': head.x++; break; } // Check for collisions if ( head.x < 0 || head.x >= this.tileCount || head.y < 0 || head.y >= this.tileCount || this.snake.some(segment => segment.x === head.x && segment.y === head.y) ) { this.gameOver = true; alert('Game Over! Your score: ' + this.score); return; } // Add new head this.snake.unshift(head); // Check if food is eaten if (head.x === this.food.x && head.y === this.food.y) { this.score++; this.scoreElement.textContent = this.score.toString(); this.food = this.generateFood(); } else { // Remove tail if no food eaten this.snake.pop(); } } private gameLoop(): void { this.update(); this.draw(); setTimeout(() => this.gameLoop(), 100); } } // Start the game when the page loads window.addEventListener('load', () => { new SnakeGame(); }); ```