您的位置:首页 > 大数据 > 人工智能

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

2015-05-24 22:42 281 查看
写了几天,搞定了贪吃蛇自动寻路……目前在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);
})()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息