第一版

<!-- 一個簡易的貪吃蛇專案 -->
<!-- CSS:
調整畫布大小,確保畫布在視口中居中。
設置遊戲區域的背景顏色和其他視覺效果。
HTML:
建立一個畫布元素用來顯示遊戲。
增加一些按鈕或訊息區來控制遊戲和顯示分數。
JavaScript:
透過 JavaScript 來控制遊戲邏輯,包括蛇的移動、吃食物和碰撞檢測。 -->
<!DOCTYPE html>
<html>
<head>

	<title>貪吃蛇</title>
	<style>

		body {
			margin: 0;
			padding: 0;
			background-color: #db9f9f;
			display: flex;
			align-items: center;
			justify-content: center;
			height: 100vh;
		}
    </style>

</head>
<body>

    <canvas id="game-canvas" width="400" height="400"></canvas>
    <div id="score-board">
        <p>Score: <span id="score">0</span></p>
        <button id="start-btn">Start</button>

    </div>

    <script>
        // 設定遊戲參數
        const canvas = document.getElementById('game-canvas');
        const ctx = canvas.getContext('2d');
        const width = canvas.width;
        const height = canvas.height;
        const cellSize = 20;
        const gridSize = 20;
        const speed = 10;
        const snakeLength = 3;
        const foodCount = 1;
        const scoreBoard = document.getElementById('score-board');
        const score = document.getElementById('score');
        const startBtn = document.getElementById('start-btn');
        let gameOver = false;
        let scoreCount = 0;
        let snake = [];
        let food = [];
        let direction = 'right';

        // 設定遊戲初始狀態
        function init() {
            gameOver = false;
            scoreCount = 0;
            snake = [];
            food = [];
            direction = 'right';
            for (let i = 0; i < snakeLength; i++) {
                snake.push({ x: i * gridSize, y: 0 });
            }
            placeFood();
            draw();
        }

        // 產生食物
        function placeFood() {
            let x = Math.floor(Math.random() * (width - cellSize) / gridSize) * gridSize;
            let y = Math.floor(Math.random() * (height - cellSize) / gridSize) * gridSize;
            food = [{ x: x, y: y }];
        }

        // 繪製遊戲畫面
        function draw() {
            ctx.clearRect(0, 0, width, height);
            // 繪製格子
            for (let x = 0; x < width; x += gridSize) {
                for (let y = 0; y < height; y += gridSize) {
                    ctx.fillStyle = (x + y) % 2 === 0? '#fff' : '#ddd';
                    ctx.fillRect(x, y, gridSize);
                }
            }
            // 繪製蛇
            for (let i = 0; i < snake.length; i++) {
                ctx.fillStyle = i === 0? '#000' : '#fff';
                ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
            }
            // 繪製食物
            ctx.fillStyle = '#f00';
            ctx.fillRect(food[0].x, food[0].y, cellSize);
            // 繪製分數
            score.textContent = scoreCount;
        }

        // 移動蛇
        function moveSnake() {
            // 取得蛇頭的位置
            let head = { x: snake[0].x, y: snake[0].y };
            // 根據方向移動蛇頭
            switch (direction) {
                case 'up':
                    head.y -= gridSize;
                    break;
                case 'down':
                    head.y += gridSize;
                    break;
                case 'left':
                    head.x -= gridSize;
                    break;
                case 'right':
                    head.x += gridSize;
                    break;
            }
            // 判斷蛇是否吃到食物
            if (head.x === food[0].x && head.y === food[0].y) {
                scoreCount++;
                placeFood();
            } else {
                // 移動蛇身
                snake.pop();
            }
            snake.unshift(head);
        }

        // 控制蛇移動
        function controlSnake() {
            if (gameOver) {
                return;
            }
            // 取得蛇頭的位置
            let head = snake[0];
            // 判斷蛇是否碰撞到邊界
            if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height) {
                gameOver = true;
                scoreBoard.style.display = 'none';
                alert('Game Over!');
                return;
            }
            // 判斷蛇是否吃到自己
            for (let i = 1; i < snake.length; i++) {
                if (head.x === snake[i].x && head.y === snake[i].y) {
                    gameOver = true;
                    scoreBoard.style.display = 'none';
                    alert('Game Over!');
                    return;
                }
            }
            // 移動蛇
            moveSnake();
            draw();
            // 判斷蛇是否吃到食物
            if (head.x === food[0].x && head.y === food[0].y) {
                scoreCount++;
                placeFood();
            }
            // 設定下一個移動方向
            switch (direction) {
                case 'up':
                    direction = 'left';
                    break;
                case 'left':
                    direction = 'down';
                    break;
                case 'down':
                    direction = 'right';
                    break;
                case 'right':
                    direction = 'up';
                    break;
            }
            // 設定遊戲速度
            setTimeout(controlSnake, speed);
        }

        // 開始遊戲
        startBtn.addEventListener('click', function () {
            scoreBoard.style.display = 'block';
            init();
            controlSnake();
            startBtn.style.display = 'none';
            
        });
 
    </script>

</body>

第二版(補注釋)

<!DOCTYPE html>
<html>
<head>
    <title>貪吃蛇</title>
    <style>
        body {
            /* 設置邊距和填充為0 */
            margin: 0;
            padding: 0;
            /* 設置背景顏色 */
            background-color: #db9f9f;
            /* 使用flex布局,使內容居中 */
            display: flex;
            align-items: center;
            justify-content: center;
            /* 設置高度為視窗高度 */
            height: 100vh;
        }
    </style>
</head>
<body>

    <!-- 定義遊戲畫布,設置寬高為400像素 -->
    <canvas id="game-canvas" width="400" height="400"></canvas>
    <!-- 分數顯示區域,包含分數顯示和開始按鈕 -->
    <div id="score-board">
        <p>Score: <span id="score">0</span></p>
        <button id="start-btn">Start</button>
    </div>

    <script>
        // 設定遊戲參數
        // 獲取畫布元素和繪圖上下文
        const canvas = document.getElementById('game-canvas');
        const ctx = canvas.getContext('2d');
        // 獲取畫布的寬度和高度
        const width = canvas.width;
        const height = canvas.height;
        // 設置單元格大小和網格大小
        const cellSize = 20;
        const gridSize = 20;
        // 設置遊戲速度(毫秒)
        const speed = 100;
        // 設置初始蛇的長度
        const snakeLength = 3;
        // 設置食物的數量
        const foodCount = 1;
        // 獲取計分板和分數顯示元素
        const scoreBoard = document.getElementById('score-board');
        const score = document.getElementById('score');
        // 獲取開始按鈕元素
        const startBtn = document.getElementById('start-btn');
        // 遊戲是否結束的標誌
        let gameOver = false;
        // 分數計數
        let scoreCount = 0;
        // 蛇的數組
        let snake = [];
        // 食物的數組
        let food = [];
        // 蛇的移動方向
        let direction = 'right';
        // 遊戲循環的間隔ID
        let intervalId = null;

        // 設定遊戲初始狀態
        // 初始化遊戲狀態的函數
        function init() {
            gameOver = false; // 設置遊戲是否結束的標誌
            scoreCount = 0; // 初始化得分
            snake = []; // 初始化蛇的身體
            food = []; // 初始化食物的位置
            direction = 'right'; // 初始化蛇的移動方向
            for (let i = 0; i < snakeLength; i++) {
                snake.push({ x: i * gridSize, y: 0 }); // 初始化蛇的身體位置
            }
            placeFood(); // 放置食物
            draw(); // 繪製遊戲畫面
        }

        // 產生食物
        // 放置食物的函數
        // 在遊戲區域內隨機生成食物的位置
        function placeFood() {
            let x = Math.floor(Math.random() * (width / gridSize)) * gridSize;
            let y = Math.floor(Math.random() * (height / gridSize)) * gridSize;
            food = [{ x: x, y: y }];
        }

        // 繪製遊戲畫面
        // 繪製函數,用於清除畫布上的內容
        function draw() {
            ctx.clearRect(0, 0, width, height);
            ctx.clearRect(0, 0, width, height);
            // 繪製格子
            // 繪製棋盤格的函數
            // 使用雙重循環遍歷每個網格單元,根據其位置決定填充顏色
            // 如果 (x + y) 是偶數,則填充白色,否則填充淺灰色
            for (let x = 0; x < width; x += gridSize) {
                for (let y = 0; y < height; y += gridSize) {
                    ctx.fillStyle = (x + y) % 2 === 0 ? '#fff' : '#ddd';
                    ctx.fillRect(x, y, gridSize, gridSize);
                }
            }
            // 繪製蛇
            for (let i = 0; i < snake.length; i++) {
                ctx.fillStyle = i === 0 ? '#000' : '#fff';
                ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
            }
            // 繪製食物
            ctx.fillStyle = '#f00';
            ctx.fillRect(food[0].x, food[0].y, cellSize, cellSize);
            // 繪製分數
            score.textContent = scoreCount;
        }

        // 移動蛇
        function moveSnake() {
            // 取得蛇頭的位置
            let head = { x: snake[0].x, y: snake[0].y };
            // 根據方向移動蛇頭
            switch (direction) {
                case 'up':
                    head.y -= gridSize;
                    break;
                case 'down':
                    head.y += gridSize;
                    break;
                case 'left':
                    head.x -= gridSize;
                    break;
                case 'right':
                    head.x += gridSize;
                    break;
            }
            // 判斷蛇是否吃到食物
            if (head.x === food[0].x && head.y === food[0].y) {
                scoreCount++;
                placeFood();
            } else {
                // 移動蛇身
                snake.pop();
            }
            snake.unshift(head);
        }

        // 控制蛇移動
        function controlSnake() {
            if (gameOver) {
                clearInterval(intervalId);
                return;
            }
            moveSnake();
            draw();
            let head = snake[0];
            // 判斷蛇是否碰撞到邊界或自己
            if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height || snake.slice(1).some(s => s.x === head.x && s.y === head.y)) {
                gameOver = true;
                scoreBoard.style.display = 'none';
                alert('Game Over!');
            }
        }

        // 開始遊戲
        startBtn.addEventListener('click', function () {
            scoreBoard.style.display = 'block';
            init();
            intervalId = setInterval(controlSnake, speed);
        });

        // 監聽鍵盤事件來改變方向
        document.addEventListener('keydown', function (event) {
            switch (event.key) {
                case 'ArrowUp':
                    if (direction !== 'down') direction = 'up';
                    break;
                case 'ArrowDown':
                    if (direction !== 'up') direction = 'down';
                    break;
                case 'ArrowLeft':
                    if (direction !== 'right') direction = 'left';
                    break;
                case 'ArrowRight':
                    if (direction !== 'left') direction = 'right';
                    break;
            }
        });

    </script>
    
</body>
</html>

第三版

<!DOCTYPE html>
<html>
<head>
    <title>貪吃蛇</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            background-color: #db9f9f;
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }
    </style>
</head>
<body>
    <canvas id="game-canvas" width="400" height="400"></canvas>
    <div id="score-board">
        <p>Score: <span id="score">0</span></p>
        <button id="start-btn">Start</button>
    </div>

    <script>
        const canvas = document.getElementById('game-canvas');
        const ctx = canvas.getContext('2d');
        const width = canvas.width;
        const height = canvas.height;
        const cellSize = 20;
        const gridSize = 20;
        const speed = 100;
        const snakeLength = 3;
        const foodCount = 1;
        const scoreBoard = document.getElementById('score-board');
        const score = document.getElementById('score');
        const startBtn = document.getElementById('start-btn');
        let gameOver = false;
        let scoreCount = 0;
        let snake = [];
        let food = [];
        let direction = 'right';
        let intervalId = null;

        function init() {
            gameOver = false;
            scoreCount = 0;
            snake = [];
            food = [];
            direction = 'right';

            // 將蛇初始化為不重疊
            for (let i = 0; i < snakeLength; i++) {
                snake.push({ x: gridSize * (snakeLength - i - 1), y: 0 });
            }
            placeFood();
            draw();
        }

        function placeFood() {
            let x = Math.floor(Math.random() * (width / gridSize)) * gridSize;
            let y = Math.floor(Math.random() * (height / gridSize)) * gridSize;
            food = [{ x: x, y: y }];
        }

        function draw() {
            ctx.clearRect(0, 0, width, height);

            for (let x = 0; x < width; x += gridSize) {
                for (let y = 0; y < height; y += gridSize) {
                    ctx.fillStyle = (x + y) % 2 === 0 ? '#fff' : '#ddd';
                    ctx.fillRect(x, y, gridSize, gridSize);
                }
            }

            for (let i = 0; i < snake.length; i++) {
                ctx.fillStyle = i === 0 ? '#000' : '#fff';
                ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
            }

            ctx.fillStyle = '#f00';
            ctx.fillRect(food[0].x, food[0].y, cellSize, cellSize);

            score.textContent = scoreCount;
        }

        function moveSnake() {
            let head = { x: snake[0].x, y: snake[0].y };

            switch (direction) {
                case 'up':
                    head.y -= gridSize;
                    break;
                case 'down':
                    head.y += gridSize;
                    break;
                case 'left':
                    head.x -= gridSize;
                    break;
                case 'right':
                    head.x += gridSize;
                    break;
            }

            if (head.x === food[0].x && head.y === food[0].y) {
                scoreCount++;
                placeFood();
                addBody();
            } else {
                snake.pop();
            }

            snake.unshift(head);
        }

        function controlSnake() {
            if (gameOver) {
                clearInterval(intervalId);
                return;
            }

            moveSnake();
            draw();
            let head = snake[0];

            if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height || snake.slice(1).some(s => s.x === head.x && s.y === head.y)) {
                gameOver = true;
                scoreBoard.style.display = 'none';
                alert('Game Over!');
            }
        }
            //當蛇吃到食物時,身長會增加一格
        function addBody() {
            // 在蛇尾部新增一格,與蛇尾部的最後一格位置相同
            let tail = snake[snake.length - 1];
            snake.push({ x: tail.x, y: tail.y });
        }

        startBtn.addEventListener('click', function () {
            scoreBoard.style.display = 'block';
            init();
            intervalId = setInterval(controlSnake, speed);
        });

        document.addEventListener('keydown', function (event) {
            switch (event.key) {
                case 'ArrowUp':
                    if (direction !== 'down') direction = 'up';
                    break;
                case 'ArrowDown':
                    if (direction !== 'up') direction = 'down';
                    break;
                case 'ArrowLeft':
                    if (direction !== 'right') direction = 'left';
                    break;
                case 'ArrowRight':
                    if (direction !== 'left') direction = 'right';
                    break;
            }
        });

    </script>
    
</body>
</html>

最終失敗版

<!DOCTYPE html>
<html>
<head>
    <title>貪吃蛇</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            background-color: #db9f9f;
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }
    </style>
</head>
<body>
    <canvas id="game-canvas" width="400" height="400"></canvas>
    <div id="score-board">
        <p>Score: <span id="score">0</span></p>
        <button id="start-btn">Start</button>
    </div>

    <script>
        const canvas = document.getElementById('game-canvas');
        const ctx = canvas.getContext('2d');
        const width = canvas.width;
        const height = canvas.height;
        const cellSize = 20;
        const gridSize = 20;
        const speed = 100;
        const snakeLength = 3;
        const scoreBoard = document.getElementById('score-board');
        const score = document.getElementById('score');
        const startBtn = document.getElementById('start-btn');
        let gameOver = false;
        let scoreCount = 0;
        let snake = [];
        let food = [];
        let direction = 'right';
        let intervalId = null;

        function init() {
            gameOver = false;
            scoreCount = 0;
            snake = [];
            food = [];
            direction = 'right';

            for (let i = 0; i < snakeLength; i++) {
                snake.push({ x: gridSize * (snakeLength - i - 1), y: 0 });
            }
            placeFood();
            draw();
        }

        function placeFood() {
            let x, y, isFoodOnSnake;

            do {
                x = Math.floor(Math.random() * (width / gridSize)) * gridSize;
                y = Math.floor(Math.random() * (height / gridSize)) * gridSize;
                isFoodOnSnake = snake.some(s => s.x === x && s.y === y);
            } while (isFoodOnSnake);

            food = [{ x: x, y: y }];
        }

        function draw() {
            ctx.clearRect(0, 0, width, height);

            for (let x = 0; x < width; x += gridSize) {
                for (let y = 0; y < height; y += gridSize) {
                    ctx.fillStyle = (x + y) % 2 === 0 ? '#fff' : '#ddd';
                    ctx.fillRect(x, y, gridSize, gridSize);
                }
            }

            for (let i = 0; i < snake.length; i++) {
                ctx.fillStyle = i === 0 ? '#000' : '#fff';
                ctx.fillRect(snake[i].x, snake[i].y, cellSize, cellSize);
            }

            ctx.fillStyle = '#f00';
            ctx.fillRect(food[0].x, food[0].y, cellSize, cellSize);

            score.textContent = scoreCount;
        }

        function moveSnake() {
            let head = { x: snake[0].x, y: snake[0].y };

            switch (direction) {
                case 'up':
                    head.y -= gridSize;
                    break;
                case 'down':
                    head.y += gridSize;
                    break;
                case 'left':
                    head.x -= gridSize;
                    break;
                case 'right':
                    head.x += gridSize;
                    break;
            }

            if (head.x === food[0].x && head.y === food[0].y) {
                console.log('Food eaten!'); // Debug message
                scoreCount++;
                placeFood();
                addBody();
            } else {
                snake.pop(); // Remove tail
            }

            snake.unshift(head); // Add new head
            console.log('Snake:', snake); // Debug message
        }

        function controlSnake() {
            if (gameOver) {
                clearInterval(intervalId);
                return;
            }

            moveSnake();
            draw();
            let head = snake[0];

            if (head.x < 0 || head.x + cellSize > width || head.y < 0 || head.y + cellSize > height || snake.slice(1).some(s => s.x === head.x && s.y === head.y)) {
                gameOver = true;
                scoreBoard.style.display = 'none';
                alert('Game Over!');
            }
        }

        function addBody() {
            let tail = snake[snake.length - 1];
            snake.push({ x: tail.x, y: tail.y });
            console.log('Body added! Snake:', snake); // Debug message
        }

        startBtn.addEventListener('click', function () {
            scoreBoard.style.display = 'block';
            init();
            intervalId = setInterval(controlSnake, speed);
        });

        document.addEventListener('keydown', function (event) {
            switch (event.key) {
                case 'ArrowUp':
                    if (direction !== 'down') direction = 'up';
                    break;
                case 'ArrowDown':
                    if (direction !== 'up') direction = 'down';
                    break;
                case 'ArrowLeft':
                    if (direction !== 'right') direction = 'left';
                    break;
                case 'ArrowRight':
                    if (direction !== 'left') direction = 'right';
                    break;
            }
        });

    </script>
</body>
</html>