打造AI贪吃蛇
Posted 肥学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了打造AI贪吃蛇相关的知识,希望对你有一定的参考价值。
目录标题
演示
自动贪吃蛇
技术栈
bottom 属性规定元素的底部边缘。该属性定义了定位元素下外边距边界与其包含块下边界之间的偏移。
注释:如果 “position” 属性的值为 “static”,那么设置 “bottom” 属性不会产生任何效果。
对于 static 元素,为 auto;对于长度值,则为相应的绝对长度;对于百分比数值,为指定值;否则为 auto。
对于相对定义元素,如果 bottom 和 top 都是 auto,其计算值则都是 0;如果其中之一为auto,则取另一个值的相反数;如果二者都不是 auto,bottom 将取 top 值的相反数。
默认值: auto
继承性: no
版本: CSS2
javascript 语法: object.style.bottom="50px"
user-select 属性规定是否能选取元素的文本。
在 web 浏览器中,如果您在文本上双击,文本会被选取或高亮显示。此属性用于阻止这种行为。
user-select: auto|none|text|all;
auto 默认。如果浏览器允许,则可以选择文本。
none 防止文本选取。
text 文本可被用户选取。
all 单击选取文本,而不是双击。
源码
样式设置
canvas
position: absolute;
width: 100vh;
height: 100vh;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
user-select: none;
background: #000;
cursor: pointer;
构建食物对象
var food =
x: 0,
y: 0,
// add random food
add: function add()
var emptyNodes = [];
for (var x = 0; x < map.width; ++x)
for (var y = 0; y < map.height; ++y)
if (!map.collision(x, y)) emptyNodes.push(
x: x,
y: y
);
if (emptyNodes.length)
var p = emptyNodes[Math.floor(Math.random() * emptyNodes.length)];
this.x = p.x;
this.y = p.y;
;
构建贪吃蛇对象
var snake =
body: [],
head:
x: 0,
y: 0
,
removeTail: function removeTail()
var p = this.body.shift();
map.setSnake(p.x, p.y, 0);
,
addHead: function addHead(x, y)
this.head.x = x;
this.head.y = y;
this.body.push(
x: x,
y: y
);
map.setSnake(x, y, 1);
,
move: function move(dir)
var next = map.getNext(this.head.x, this.head.y, dir);
this.addHead(next.x, next.y);
if (next.x === food.x && next.y === food.y)
food.add();
else this.removeTail();
,
// snake IA
nextDirection: function nextDirection()
var x = this.head.x;
var y = this.head.y;
var pathNumber = map.tour(x, y);
var distanceToFood = map.distance(pathNumber, map.tour(food.x, food.y));
var distanceToTail = map.distance(pathNumber, map.tour(snake.body[0].x, snake.body[0].y));
var cuttingAmountAvailable = distanceToTail - 4;
var numEmptySquaresOnBoard = map.size - snake.body.length - 1;
if (distanceToFood < distanceToTail) cuttingAmountAvailable -= 1;
var cuttingAmountDesired = distanceToFood;
if (cuttingAmountDesired < cuttingAmountAvailable) cuttingAmountAvailable = cuttingAmountDesired;
if (cuttingAmountAvailable < 0) cuttingAmountAvailable = 0;
var canGoRight = !map.collision(x + 1, y);
var canGoLeft = !map.collision(x - 1, y);
var canGoDown = !map.collision(x, y + 1);
var canGoUp = !map.collision(x, y - 1);
var bestDir = -1;
var bestDist = -1;
var dist = 0;
if (canGoRight)
dist = map.distance(pathNumber, map.tour(x + 1, y));
if (dist <= cuttingAmountAvailable && dist > bestDist)
bestDir = map.Right;
bestDist = dist;
if (canGoLeft)
dist = map.distance(pathNumber, map.tour(x - 1, y));
if (dist <= cuttingAmountAvailable && dist > bestDist)
bestDir = map.Left;
bestDist = dist;
if (canGoDown)
dist = map.distance(pathNumber, map.tour(x, y + 1));
if (dist <= cuttingAmountAvailable && dist > bestDist)
bestDir = map.Down;
bestDist = dist;
if (canGoUp)
dist = map.distance(pathNumber, map.tour(x, y - 1));
if (dist <= cuttingAmountAvailable && dist > bestDist)
bestDir = map.Up;
bestDist = dist;
if (bestDist >= 0) return bestDir;
if (canGoUp) return map.Up;
if (canGoLeft) return map.Left;
if (canGoDown) return map.Down;
if (canGoRight) return map.Right;
return map.Right;
;
构建自动贪吃
var map =
// init map
init: function init(width, height)
var _this = this;
this.width = width;
this.height = height;
this.size = width * height;
this.scale = Math.min(canvasWidth, canvasHeight) / Math.max(this.width, this.height);
// Hamiltonian Cycle
// flags
var _array2D = this.array2D(width, height, true);
var _array2D2 = _slicedToArray(_array2D, 2);
this.tour = _array2D2[0];
this.setTour = _array2D2[1];
var _array2D3 = this.array2D(width / 2, height / 2);
var _array2D4 = _slicedToArray(_array2D3, 2);
this.isVisited = _array2D4[0];
this.setVisited = _array2D4[1];
var _array2D5 = this.array2D(width / 2, height / 2);
var _array2D6 = _slicedToArray(_array2D5, 2);
this.canGoRight = _array2D6[0];
this.setGoRight = _array2D6[1];
var _array2D7 = this.array2D(width / 2, height / 2);
var _array2D8 = _slicedToArray(_array2D7, 2);
this.canGoDown = _array2D8[0];
this.setGoDown = _array2D8[1];
var _array2D9 = this.array2D(width, height);
var _array2D10 = _slicedToArray(_array2D9, 2);
this.isSnake = _array2D10[0];
this.setSnake = _array2D10[1];
this.canGoLeft = function (x, y)
if (x === 0) return false;
return _this.canGoRight(x - 1, y);
;
this.canGoUp = function (x, y)
if (y === 0) return false;
return _this.canGoDown(x, y - 1);
;
,
// directions
Left: 1,
Up: 2,
Right: 3,
Down: 4,
// flat 2D array
array2D: function array2D(width, height, protect)
var data = new Uint16Array(width * height);
return [function (x, y)
return data[x + width * y];
, protect ? function (x, y, value)
var i = x + width * y;
if (!data[i]) data[i] = value;
: function (x, y, value)
data[x + width * y] = value;
];
,
// test snake collision
collision: function collision(x, y)
if (x < 0 || x >= this.width) return true;
if (y < 0 || y >= this.height) return true;
return this.isSnake(x, y) !== 0;
,
// path distance
distance: function distance(a, b)
if (a < b) return b - a - 1;else return b - a - 1 + this.size;
,
// Hamiltonian Cycle
generate_r: function generate_r(fromx, fromy, x, y)
if (x < 0 || y < 0 || x >= this.width / 2 || y >= this.height / 2) return;
if (this.isVisited(x, y)) return;
this.setVisited(x, y, 1);
if (fromx !== -1)
if (fromx < x) this.setGoRight(fromx, fromy, 1);else if (fromx > x) this.setGoRight(x, y, 1);else if (fromy < y) this.setGoDown(fromx, fromy, 1);else if (fromy > y) this.setGoDown(x, y, 1);
for (var i = 0; i < 2; i++)
var r = Math.floor(Math.random() * 4);
switch (r)
case 0:
this.generate_r(x, y, x - 1, y);
break;
case 1:
this.generate_r(x, y, x + 1, y);
break;
case 2:
this.generate_r(x, y, x, y - 1);
break;
case 3:
this.generate_r(x, y, x, y + 1);
break;
this.generate_r(x, y, x - 1, y);
this.generate_r(x, y, x + 1, y);
this.generate_r(x, y, x, y + 1);
this.generate_r(x, y, x, y - 1);
,
// find next direction in cycle
findNextDir: function findNextDir(x, y, dir)
if (dir === this.Right)
if (this.canGoUp(x, y)) return this.Up;
if (this.canGoRight(x, y)) return this.Right;
if (this.canGoDown(x, y)) return this.Down;
return this.Left;
else if (dir === this.Down)
if (this.canGoRight(x, y)) return this.Right;
if (this.canGoDown(x, y)) return this.Down;
if (this.canGoLeft(x, y)) return this.Left;
return this.Up;
else if (dir === this.Left)
if (this.canGoDown(x, y)) return this.Down;
if (this.canGoLeft(x, y)) return this.Left;
if (this.canGoUp(x, y)) return this.Up;
return this.Right;
else if (dir === 让AI学会玩贪吃蛇