<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>贪吃蛇小游戏</title>
    <style>
        body{
            margin: 0;
            padding: 0;
        }
        #main{
            margin: 100px;
        }
        .btn{
            width: 100px;
            height: 40px;
        }
        .gtitle{
            font-size: 25px;
            font-weight: bold;
        }
        #gnum{
            color: red;
        }
    </style>
</head>
<body>
    <div id="main">
        <h1>贪吃蛇</h1>
        <input class="btn" type="button" value="开始游戏" id="begin">
        <input class="btn" type="button" value="结束游戏" id="pause">
        <span class="gtitle">第<span id="gnum">1</span>关</span>

    </div>
    <script>
        let main = document.getElementById('main');
        var showcanvas = false; // 是否开启画布的格子
        function Map(atom, xnum, ynum){
            this.atom = atom;
            this.xnum = xnum;
            this.ynum = ynum;

            this.canvas = null;

            // 创建画布的方法
            this.create = function(){
                this.canvas = document.createElement('div');
                this.canvas.style.cssText = "position: relative; top: 40px; border: 1px solid darkred;background: #FAFAFA";
                this.canvas.style.width = this.atom * this.xnum + 'px'; // 画布的宽
                this.canvas.style.height = this.atom * this.ynum + 'px';// 画布的高
                main.appendChild(this.canvas);

                if(showcanvas){
                    for(var y = 0; y < ynum; y++){
                        for(var x = 0; x < xnum; x++){
                            var a = document.createElement('div');
                            a.style.cssText = "border: 1px solid yellow;";
                            a.style.width = this.atom + 'px';
                            a.style.height = this.atom + 'px';
                            a.style.backgroundColor = "green";
                            this.canvas.appendChild(a);
                            a.style.position = 'absolute';
                            a.style.left = x * this.atom + 'px';
                            a.style.top = y* this.atom + 'px';
                        }
                    }
                }

            };
        };

        function Food(map){
            this.width = map.atom;
            this.height = map.atom;
            this.bgcolor = `rgb(${Math.floor(Math.random() * 200)},${Math.floor(Math.random() * 200)},${Math.floor(Math.random() * 200)})`
            this.x = Math.floor(Math.random() * map.xnum);
            this.y = Math.floor(Math.random() * map.ynum);

            this.flag = document.createElement('div');
            this.flag.style.width = this.width + 'px';
            this.flag.style.height = this.height + 'px';

            this.flag.style.borderRadius = '50%';
            this.flag.style.position = 'absolute';
            this.flag.style.backgroundColor = this.bgcolor;
            this.flag.style.left = this.x * this.width + 'px';
            this.flag.style.top = this.y * this.height + 'px';
            map.canvas.appendChild(this.flag);
        }

        function Snake(map){
            this.width = map.atom;
            this.height =  map.atom;

            this.direction = 'right';

            this.body = [
                {x: 2, y: 0}, // 蛇头, 第一点
                {x: 1, y: 0},
                {x: 0, y: 0}  // 蛇尾, 第三点
            ];

            // 显示蛇
            this.display = function(){
                for(let i=0; i<this.body.length; i++){
                    if(this.body[i].x != null){ // 当吃到食物时,x == null,不能新建, 不然会在0,0处新建一个
                        var s = document.createElement('div');
                        // 将节点保存到一个状态变量中,以便以后删除使用
                        this.body[i].flag = s;

                        // 设置蛇的样式
                        s.style.width = this.width + 'px';
                        s.style.height = this.height + 'px';
                        s.style.backgroundColor = `rgb(${Math.floor(Math.random() * 200)},${Math.floor(Math.random() * 200)},${Math.floor(Math.random() * 200)})`
                        s.style.borderRadius = '50%';

                        // 设置位置
                        s.style.position = 'absolute';
                        s.style.left = this.body[i].x * this.width + 'px';
                        s.style.top = this.body[i].y * this.height + 'px';
                        map.canvas.appendChild(s);
                    }
                }
            };

            // 让蛇运动起来
            this.run = function(){

                for(let i = this.body.length-1; i>0; i--){
                    this.body[i].x = this.body[i-1].x;
                    this.body[i].y = this.body[i-1].y;
                }

                switch (this.direction) {
                    case "left": 
                            this.body[0].x -= 1;
                        break;
                    case "right": 
                            this.body[0].x += 1;
                        break;
                    case "up": 
                            this.body[0].y -= 1;
                        break;
                    case "down": 
                            this.body[0].y += 1;
                        break;
                    default:
                        break;
                }
                // 判断蛇头吃到食物,xy和食物的xy重合
                if(this.body[0].x == food.x && this.body[0].y == food.y){
                    // 蛇加一节,根据最后节定
                    this.body.push({x: null, y: null, flag: null});
                    // 判断一个设置级别
                    if(this.body.length > l.slength){
                        l.set();
                    }
                    map.canvas.removeChild(food.flag)
                    food = new Food(map);
                }

                // 判断是否出界,蛇头
                if(this.body[0].x < 0 || this.body[0].x > map.xnum - 1 || this.body[0].y < 0 || this.body[0].y > map.ynum -1 ){
                    clearInterval(timer);
                    alert("游戏结束");
                    restart(map, this);
                    return false;
                }
                // 判断是否和自己重合
                for(let i=4; i<this.body.length; i++){
                    if(this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y){
                        clearInterval(timer);
                        alert("游戏结束");
                        restart(map, this);
                    }
                }
                for(let i=0; i<this.body.length; i++){
                    if(this.body[i].flag != null){ // 当吃到食物,flag等null,且不能删除
                        map.canvas.removeChild(this.body[i].flag);
                    }
                }
                
                this.display();
            };
        }

        // 重新开始游戏
        function restart(map,snake){
            for(let i=0; i < snake.body.length; i++){
                map.canvas.removeChild(snake.body[i].flag);
            }
            snake.body = [
                {x:2, y:0},
                {x:1, y:0},
                {x:0, y:0}
            ];
            snake.direction = "right";
            snake.display();
            map.canvas.removeChild(food.flag);
            food = new Food(map);
        }

        // 设置级别对象
        function Level(){
            this.num = 1; // 第几级别
            this.speek = 300; // 毫秒, 每开一关, 数量减少20, 速度加快
            this.slength = 8; // 每关的长度判断

            this.set = function(){
                this.num++;
                if(this.speek <= 50){
                    this.speek = 50;
                }else{
                    this.speek -= 50;
                }
                this.slength += 3; 

                this.display();

                start(); // 重新开始, 速度加快
            }

            this.display = function(){
                document.getElementById('gnum').innerHTML = this.num;
            }
        }
        var l = new Level();
        l.display();
        // 创建地图对象
        var map = new Map(20, 40, 20);
        map.create();

        // 构造食物对象
        var food = new Food(map);

        //构造蛇对象
        let snake = new Snake(map)
        snake.display();

        window.onkeydown = function(e){
            let event = e || window.event;
            switch (event.keyCode) {
                case 38:
                    if(snake.direction != "down"){
                        snake.direction = "up";
                    }
                    break;
                case 40:
                    if(snake.direction != "up"){
                        snake.direction = "down";
                    }
                    break;
                case 37:
                    if(snake.direction != "right"){
                        snake.direction = "left";
                    }   
                    break;
                case 39:
                    if(snake.direction != "left"){
                        snake.direction = "right";
                    }
                    break;
                default:
                    break;
            }
        }

        var timer;
        function start(){
            clearInterval(timer);
            timer = setInterval(function(){
                snake.run();
            },l.speek);
        }
        document.getElementById('begin').onclick = function(){
            start();
        }
        document.getElementById('pause').onclick = function(){
            clearInterval(timer);
        }
    </script>
</body>
</html>