前几天搞定了贪吃蛇自动寻路算法……

来源:互联网 发布:unix网络编程源码下载 编辑:程序博客网 时间:2024/05/16 17:04

写了几天,搞定了贪吃蛇自动寻路……目前在20*20的格子上面可以稳定跑到100+分,200+的话……看运气吧o(╯□╰)o,总之算法还有很多可以修改的地方,而且UI部分还有部分没写的……
总结下:这次写的话主要算法用的是BFS,没有用A*算法,因为太复杂了,用JS实现感觉好难,所以暂时没有写……然后有两种策略,一种是吃到食物还能找到自己尾巴的话就直接去吃食物,如果发现找不到吃食物的路径OR吃了就会死那么就在头部附近找一个格子,格子满足两个条件:一个是到了这个格子之后可以找到去尾巴的路,第二个是这个格子离食物最远……其实策略主要是看了别人写的贪吃蛇算法搞出来的,可惜的是目前还是不能铺满整个格子orz……算法还得改啊哈哈哈o(╯□╰)o
代码如下【只放JS了】:

(function () {    /*全局变量*/    var g = {        //获得or设置盒子的attribute        attr : function (x, y, att, name) {            var d = document.getElementById("box_" + x + "_" + y);            if (d && name)                d.setAttribute(att, name);            else if (d)                return d.getAttribute(att);        },        //随机创建点        create : function (start, end) {            return Math.floor(Math.random() * (end - start) + start);        },        addHandler: function (ele, type, handler) {            if (ele.length === undefined) {                if (ele.addEventListener) {                    ele.addEventListener(type, handler, false);                }                else if (ele.attachEvent) {                    ele.attachEvent("on" + type, handler);                }                else {                    ele['on' + type] = handler;                }            }            else if (ele.length === 0) {                console.log("The element is null!");                return false;            }            else {                for (var i = 0; i < ele.length; i ++) {                    if (ele[i].addEventListener) {                        ele[i].addEventListener(type, handler, false);                    }                    else if (ele[i].attachEvent) {                        ele[i].attachEvent("on" + type, handler);                    }                    else {                        ele[i]['on' + type] = handler;                    }                }            }        },        removeHandler: function(element, type, handler) {            if (ele.length === undefined) {                if (ele.removeEventListener)                    ele.removeEventListener(type, handler, false);                else if (ele.detachEvent)                    ele.detachEvent("on" + type, handler);                else                    ele['on' + type] = null;            }            else if (ele.length === 0) {                return (console.log("the element is null"));            }            else {                for (var i = 0; i < ele.length; i ++) {                    if (ele[i].removeEventListener)                    ele[i].removeEventListener(type, handler, false);                else if (ele[i].detachEvent)                    ele[i].detachEvent("on" + type, handler);                else                    ele[i]['on' + type] = null;                }            }        },        //运动方向        direction : {            left : 37,            up : 38,            right : 39,            down : 40        },        //游戏设定        setting : {            size : 20,            speed : 500,            len : 3,            func : null,            direct : null        }    }    /*Game构造器*/    function Game (food) {        this.snake = new Snake(food);        this.food = food;        var that = this;        /*Snake构造器*/        function Snake (food) {            this.headX = 0;            this.headY = 0;            this.lastX = 0;            this.lastY = 0;            this.pos = [];            this.f = 0;            this.h = 0;            this.food = food;        };        Snake.prototype.init = function () {            this.headX = this.headY = this.lastX = this.lastY = 0;            this.pos = [];        }        /*创建蛇*/        Snake.prototype.create = function () {            var x = g.create(g.setting.len, g.setting.size / 2),            y = g.create(g.setting.len, g.setting.size / 2);            //获得蛇的坐标            for (var i = 0; i < g.setting.len; i ++) {                y --;                this.pos.push([x, y]);                if (i === 0) {                    g.attr(x, y, "class", "snake head");                    g.attr(x, y, "touchable", "0");                }                else if (i === g.setting.len - 1){                    g.attr(x, y, "class", "snake tail")                    g.attr(x, y, "touchable", "1");                }                else {                    g.attr(x, y, "class", "snake");                    g.attr(x, y, "touchable", "0");                }            }            this.headX = this.pos[0][0];            this.headY = this.pos[0][1];            this.lastX = this.pos[this.pos.length - 1][0];            this.lastY = this.pos[this.pos.length - 1][1];        };        /*移动*/        Snake.prototype.move = function () {            this.headX = this.pos[0][0];            this.headY = this.pos[0][1];            this.lastX = this.pos[this.pos.length - 1][0];            this.lastY = this.pos[this.pos.length - 1][1];            g.attr(this.lastX, this.lastY, "class", " ");            for (var i = this.pos.length - 1; i > 0; i --) {                this.pos[i][0] = this.pos[i - 1][0];                this.pos[i][1] = this.pos[i - 1][1];            }            if (command.length == 0)                command.push(g.setting.direct);            g.setting.direct = command.shift();            command = [];            switch (g.setting.direct) {                case g.direction.up : this.headY    -= 1; break;                case g.direction.down : this.headY  += 1; break;                case g.direction.left : this.headX  -= 1; break;                case g.direction.right : this.headX += 1; break;            }            this.pos[0][0] = this.headX;            this.pos[0][1] = this.headY;            this.lastX = this.pos[this.pos.length - 1][0];            this.lastY = this.pos[this.pos.length - 1][1];            for (var i = 0; i < this.pos.length; i ++) {                g.attr(this.pos[i][0], this.pos[i][1], "class", "snake");                g.attr(this.pos[i][0], this.pos[i][1], "touchable", "0");            }            g.attr(this.headX, this.headY, "class", "snake head");            g.attr(this.lastX, this.lastY, "class", "snake tail");            g.attr(this.lastX, this.lastY, "touchable", "1");            var inside = g.attr(this.headX, this.headY, "inside");            if (inside == "food") {                this.lastX = this.pos[this.pos.length - 1][0];                this.lastY = this.pos[this.pos.length - 1][1];                if (this.lastX == this.pos[this.pos.length - 2][0]) {                    if (this.lastY - this.pos[this.pos.length - 2][1] == 1)                        this.pos.push([this.lastX, this.lastY + 1]);                    else if (this.pos[this.pos.length - 2][1] - this.lastY == 1)                        this.pos.push([this.lastX, this.lastY - 1]);                }                else if (this.lastY == this.pos[this.pos.length - 2][1]) {                    if (this.lastX - this.pos[this.pos.length - 2][0] == 1)                        this.pos.push([this.lastX + 1, this.lastY])                    else if (this.pos[this.pos.length - 2][0] - this.lastX == 1)                        this.pos.push([this.lastX - 1, this.lastY]);                }                g.attr(this.lastX, this.lastY, "class", "snake");                g.attr(this.lastX, this.lastY, "touchable", "0");                this.lastX = this.pos[this.pos.length - 1][0];                this.lastY = this.pos[this.pos.length - 1][1];                g.attr(this.lastX, this.lastY, "class", "snake tail");                g.attr(this.lastX, this.lastY, "touchable", "1");                score ++;                this.food.create();                document.getElementById("score").innerHTML = score;                g.attr(this.headX, this.headY, "inside", " ");            };            //撞到自己或者墙,游戏结束            for (var i = 1; i < this.pos.length; i ++) {                if (this.headX == this.pos[i][0] && this.headY == this.pos[i][1]) {                    that.over();                }                else {                    continue;                }            }            if (this.headX > (g.setting.size - 1) || this.headX < 0 || this.headY > (g.setting.size - 1) || this.headY < 0) {                that.over();            }        };        Snake.prototype.AIMode = function() {            var head = [this.headX, this.headY],                tail = [this.lastX, this.lastY],                foodPos = this.food.pos,                headPath = this.existPath(head, foodPos);            if (headPath.length) {                var test = this.fakemove(this.pos, headPath);                if (test.length) {                    this.path(head, foodPos);                }                else {                    var fathest = this.farthest(head, foodPos, tail);                    if (fathest.length) {                        if (fathest[0] - head[0] > 0) {                            g.setting.direct = g.direction.right;                        }                        else if (fathest[0] - head[0] < 0) {                            g.setting.direct = g.direction.left;                        }                        else if (fathest[1] - head[1] > 0) {                            g.setting.direct = g.direction.down;                        }                        else if (fathest[1] - head[1] < 0) {                            g.setting.direct = g.direction.up;                        }                    }                    else {                        if (this.existPath(head, tail)) {                            this.path(head, tail);                        }                        else {                            this.path(head, foodPos);                        }                    }                }            }            else {                var fathest = this.farthest(head, foodPos, tail);                if (fathest.length) {                    if (fathest[0] - head[0] > 0) {                        g.setting.direct = g.direction.right;                    }                    else if (fathest[0] - head[0] < 0) {                        g.setting.direct = g.direction.left;                    }                    else if (fathest[1] - head[1] > 0) {                        g.setting.direct = g.direction.down;                    }                    else if (fathest[1] - head[1] < 0) {                        g.setting.direct = g.direction.up;                    };                }                else {                    if (this.existPath(head, tail).length) {                        this.path(head, tail);                    }                    else {                        this.struggle(head);                    }                }            }        };        /* 算法JS实现 */        Snake.prototype.path = function(sta, end) {            var startX = sta[0], startY = sta[1], distance = this.existPath(sta, end);            if (distance.length > 1) {                if (distance[1][0] - startX > 0) {                    g.setting.direct = g.direction.right;                }                else if (distance[1][0] - startX < 0) {                    g.setting.direct = g.direction.left;                }                else if (distance[1][1] - startY > 0) {                    g.setting.direct = g.direction.down;                }                else if (distance[1][1] - startY < 0) {                    g.setting.direct = g.direction.up;                }            }        }        /* 贪吃蛇头部附近找个距离食物最远的有效点 */        Snake.prototype.farthest = function(start, end, tail) {            var startX = start[0], startY = start[1], endX = end[0], endY = end[1], que = [], distance = [];            for (var i = -1; i <= 1; i ++) {                if (i === 0) {                    continue;                }                else {                    if (startX + i < g.setting.size                        && startX + i >= 0                        && g.attr(startX + i, startY, "touchable") === '1'                        && this.existPath([startX + i, startY], tail).length) {                        que.push([startX + i, startY])                    }                    if (startY + i < g.setting.size                        && startY + i >= 0                        && g.attr(startX, startY + i, "touchable") === '1'                        && this.existPath([startX, startY + i], tail).length) {                        que.push([startX, startY + i])                    }                }            }            for (var i = 0; i < que.length; i ++) {                var dis = Math.sqrt(Math.pow(endX - que[i][0], 2) + Math.pow(endY - que[i][1], 2));                distance.push([dis, que[i]]);            }            distance.sort(function(a, b) {                return b[0] - a[0];            })            if (distance.length) {                return distance[0][1];            }            else {                return distance;            }        }        /* 移动一条假蛇去吃食物以检查路径是否安全 */        Snake.prototype.fakemove = function(pos, path) {            var temp = [];            if (path.length >= pos.length) {                for (var i = path.length - 1; i >= path.length - pos.length; i --) {                    if (i === path.length - pos.length) {                        g.attr(path[i][0], path[i][1], "touchable", '1');                    }                    else {                        g.attr(path[i][0], path[i][1], "touchable", '0');                    }                }                for (var i = 0; i < pos.length; i ++) {                    g.attr(pos[i][0], pos[i][1], "touchable", "1");                }                temp = this.existPath(path[path.length - 1], path[path.length - pos.length]);            }            else {                if (path.length > 2) {                    for (var i = pos.length - 1; i >= pos.length - path.length; i --) {                        g.attr(pos[i][0], pos[i][1], "touchable", "1");                    }                    for (var i = 0; i < path.length; i ++) {                        g.attr(path[i][0], path[i][1], "touchable", "0");                    }                    temp = this.existPath(path[path.length - 1], pos[pos.length - path.length]);                }                else {                    g.attr(pos[pos.length - 1][0], pos[pos.length - 1][1], "touchable", "1");                    for (var i = 0; i < path.length; i ++) {                        g.attr(path[i][0], path[i][1], "touchable", "0");                    }                    temp = this.existPath(path[path.length - 1], pos[pos.length - 1]);                }            }            for (var i = path.length - 1; i >= 0; i --) {                g.attr(path[i][0], path[i][1], "touchable", "1");            }            for (var i = pos.length - 2; i >= 0; i --) {                g.attr(pos[i][0], pos[i][1], "touchable", "0");            }            g.attr(pos[pos.length - 1][0], pos[pos.length - 1][1], "touchable", "1");            return temp;        }        /* BFS算法实现寻找路径 */        Snake.prototype.existPath = function(start, end) {            var visited = [], que = [], parent = [];            var sta = start;            for (var i = 0; i < g.setting.size; i ++) {                visited[i] = [];                parent[i] = [];                for (var j = 0; j < g.setting.size; j ++) {                    visited[i][j] = false;                    parent[i][j] = [-1, -1];                }            }            var tempque = [];            tempque.push(start);            if (start) {                if (visited[start[0]])                    visited[start[0]][start[1]] = true;            }            while (tempque.length) {                start = tempque.shift();                var x = start[0], y = start[1];                for (var i = -1; i <= 1; i ++) {                    if (i === 0) {                        continue;                    }                    else {                        if (x + i < g.setting.size                            && x + i >= 0                            && (g.attr(x + i, y, "touchable") === '1')                            && !visited[x + i][y]) {                            tempque.push([x + i, y]);                            visited[x + i][y] = true;                            parent[x + i][y] = [x, y];                            if (x + i === end[0] && y === end[1]) {                                var temp = end;                                while (temp && temp[0] != -1) {                                    que.unshift(temp);                                    temp = parent[temp[0]][temp[1]];                                }                            }                        }                        if (y + i < g.setting.size                            && y + i >= 0                            && (g.attr(x, y + i, "touchable") === '1')                            && !visited[x][y + i]) {                            tempque.push([x, y + i]);                            visited[x][y + i] = true;                            parent[x][y + i] = [x, y];                            if (x === end[0] && y + i === end[1]) {                                var temp = end;                                while (temp && temp[0] != -1) {                                    que.unshift(temp);                                    temp = parent[temp[0]][temp[1]];                                }                            }                        }                    }                }            }            return que;        }        /* 实在没辙就只能就近做S形运动苟延残喘 */        Snake.prototype.struggle = function(head) {            var t = [-1, 1], x = head[0], y = head[1], que = [], die = 0;            switch(g.setting.direct) {                case g.direction.up:                    if (y - 1 < 0 || g.attr(x, y - 1, "class").indexOf("snake") != -1) {                        die = 1;                    }                    break;                case g.direction.down:                    if (y + 1 >= g.setting.size || g.attr(x, y + 1, "class").indexOf("snake") != -1) {                        die = 1;                    }                    break;                case g.direction.left:                    if (x - 1 < 0 || g.attr(x - 1, y, "class").indexOf("snake") != -1) {                        die = 1;                    }                    break;                case g.direction.right:                    if (x + 1 >= g.setting.size || g.attr(x + 1, y, "class").indexOf("snake") != -1) {                        die = 1;                    }                    break;            }            if (die) {                for (var i = 0; i < 2; i ++) {                    if (x + t[i] < g.setting.size                        && x + t[i] >= 0                        && g.attr(x + t[i], y, "class")                        && g.attr(x + t[i], y, "class").indexOf("snake") == -1) {                        que.push([x + t[i], y]);                    }                    if (y + t[i] < g.setting.size                        && y + t[i] >= 0                        && g.attr(x, y + t[i], "class")                        && g.attr(x, y + t[i], "class").indexOf("snake") == -1) {                        que.push([x, y + t[i]]);                    }                }                if (que.length) {                    if (que[0][0] - head[0] > 0) {                        g.setting.direct = g.direction.right;                    }                    else if (que[0][0] - head[0] < 0) {                        g.setting.direct = g.direction.left;                    }                    else if (que[0][1] - head[1] > 0) {                        g.setting.direct = g.direction.down;                    }                    else if (que[0][1] - head[1] < 0) {                        g.setting.direct = g.direction.up;                    }                }            }        }    };    /*创建格子*/    Game.prototype.pannel = function () {        var t = [];        t.push("<table>");        for (var i = 0; i < g.setting.size; i ++) {            t.push("<tr class = 'row' y = " + i + ">");            for (var j = 0; j < g.setting.size; j ++) {                t.push("<td id = 'box_" + j + "_" + i + "' inside = ' ' x = '" + j + "' y = '" + i + "' touchable=1></td>");            }            t.push("</tr>");        }        t.push("</table>");        document.getElementById("pannel").innerHTML = t.join("");    };    /*初始化游戏*/    Game.prototype.init = function () {        score = 0;        document.getElementById("score").innerHTML = score;        this.snake.init();        speed.disabled = start.disabled = false;        if (g.setting.func) {            window.clearInterval(g.setting.func);        }        for (var x = 0; x < g.setting.size; x ++) {            for (var y = 0; y < g.setting.size; y ++) {                g.attr(x, y, "class", " ");                g.attr(x, y, "inside", " ");            }        }    }    /*游戏开始*/    Game.prototype.start = function () {        this.food.create();        this.snake.create();        g.setting.direct = g.direction.down;        var that = this;        g.setting.func = window.setInterval(function() {            that.snake.AIMode();            that.snake.move();        }, g.setting.speed)        //蛇移动        //g.setting.func = window.setInterval(function() {        //  snake.move();        //}, g.setting.speed);    }    /*监听键盘*/    Game.prototype.listen = function (e) {        e = e || event;        command.push(Math.abs(e.keyCode - g.setting.direct) != 2 && e.keyCode > 36 && e.keyCode < 41 ? e.keyCode : g.setting.direct);    }    /*游戏结束*/    Game.prototype.over = function () {        alert("game over XD!");        this.init();    };    /*Food构造器*/    function Food () {        this.pos = [];    };        /*创建食物*/    Food.prototype.create = function () {        var that = this;        var x = g.create(0, g.setting.size),        y = g.create(0, g.setting.size);        //检查是否与蛇的位置重合        var c = g.attr(x, y, "class");        if (c.indexOf("snake") != -1)            return this.create();        else {            g.attr(x, y, "class", "food");            g.attr(x, y, "inside", "food");        }        this.pos = [x, y];    };    var command = [], gameAi, foodAi, speed = document.getElementById("gameSpeed"), start = document.getElementById("start"), score = 0;    foodAi = new Food();    gameAi = new Game(foodAi);    gameAi.pannel();    gameAi.init();    function gameStart() {        this.disabled = true;        g.setting.speed = speed.options[speed.selectedIndex].value;        speed.disabled = true;        gameAi.start();        //window.onkeydown = game.listen;    };    function switchChange() {        var cla = this.firstChild.className.split(" ");        for (var i = 0; i < cla.length; i ++) {            if (cla[i] == "switch-animate") {                cla.splice(i, 1);                break;            }        }        cla = cla.join(" ");        this.firstChild.className = cla;    }    function switchAnimate() {        var cla = this.firstChild.className.split(" ");        for (var i = 0; i < cla.length; i ++) {            if (cla[i] == "switch-off") {                cla.splice(i, 1, "switch-on");                break;            }            else if (cla[i] == "switch-on") {                cla[i] = "switch-off";                break;            }        }        cla.push("switch-animate");        cla = cla.join(" ");        this.firstChild.className = cla;    }    g.addHandler(start, "click", gameStart);    g.addHandler(document.getElementsByClassName("switch"), 'mousedown', switchChange);    g.addHandler(document.getElementsByClassName("switch"), 'mouseup', switchAnimate);})()
0 0
原创粉丝点击