打造AI贪吃蛇
演示
自动贪吃蛇
技术栈
bottom 属性规定元素的底部边缘。该属性定义了定位元素下外边距边界与其包含块下边界之间的偏移。
注释:如果 “position” 属性的值为 “static”,那么设置 “bottom” 属性不会产生任何效果。
对于 static 元素,为 auto;对于长度值,则为相应的绝对长度;对于百分比数值,为指定值;否则为 auto。
对于相对定义元素,如果 bottom 和 top 都是 auto,其计算值则都是 0;如果其中之一为auto,则取另一个值的相反数;如果二者都不是 auto,bottom 将取 top 值的相反数。
默认值: auto
继承性: no
版本: CSS2
JavaScript 语法: object.style.bottom="50px"
- 1
- 2
- 3
- 4
user-select 属性规定是否能选取元素的文本。
在 web 浏览器中,如果您在文本上双击,文本会被选取或高亮显示。此属性用于阻止这种行为。
user-select: auto|none|text|all;
- 1
auto 默认。如果浏览器允许,则可以选择文本。
none 防止文本选取。
text 文本可被用户选取。
all 单击选取文本,而不是双击。
- 1
- 2
- 3
- 4
源码
样式设置
canvas {
position: absolute;
width: 100vh;
height: 100vh;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
user-select: none;
background: #000;
cursor: pointer;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
构建食物对象
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;
}
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
构建贪吃蛇对象
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;
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
构建自动贪吃
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 === this.Up) {
if (this.canGoLeft(x, y)) return this.Left;
if (this.canGoUp(x, y)) return this.Up;
if (this.canGoRight(x, y)) return this.Right;
return this.Down;
}
return -1; //Unreachable
},
// generate Hamiltonian Cycle
generateTourNumber: function generateTourNumber() {
var x = 0;
var y = 0;
var dir = this.canGoDown(x, y) ? this.Up : this.Left;
var number = 0;
do {
var nextDir = this.findNextDir(x, y, dir);
switch (dir) {
case this.Right:
this.setTour(x * 2, y * 2, number++);
if (nextDir === dir || nextDir === this.Down || nextDir === this.Left) this.setTour(x * 2 + 1, y * 2, number++);
if (nextDir === this.Down || nextDir === this.Left) this.setTour(x * 2 + 1, y * 2 + 1, number++);
if (nextDir === this.Left) this.setTour(x * 2, y * 2 + 1, number++);
break;
case this.Down:
this.setTour(x * 2 + 1, y * 2, number++);
if (nextDir === dir || nextDir === this.Left || nextDir === this.Up) this.setTour(x * 2 + 1, y * 2 + 1, number++);
if (nextDir === this.Left || nextDir === this.Up) this.setTour(x * 2, y * 2 + 1, number++);
if (nextDir === this.Up) this.setTour(x * 2, y * 2, number++);
break;
case this.Left:
this.setTour(x * 2 + 1, y * 2 + 1, number++);
if (nextDir === dir || nextDir === this.Up || nextDir === this.Right) this.setTour(x * 2, y * 2 + 1, number++);
if (nextDir === this.Up || nextDir === this.Right) this.setTour(x * 2, y * 2, number++);
if (nextDir === this.Right) this.setTour(x * 2 + 1, y * 2, number++);
break;
case this.Up:
this.setTour(x * 2, y * 2 + 1, number++);
if (nextDir === dir || nextDir === this.Right || nextDir === this.Down) this.setTour(x * 2, y * 2, number++);
if (nextDir === this.Right || nextDir === this.Down) this.setTour(x * 2 + 1, y * 2, number++);
if (nextDir === this.Down) this.setTour(x * 2 + 1, y * 2 + 1, number++);
break;
}
dir = nextDir;
switch (nextDir) {
case this.Right:
++x;
break;
case this.Left:
--x;
break;
case this.Down:
++y;
break;
case this.Up:
--y;
break;
}
} while (number !== this.size);
},
// get next node
getNext: function getNext(x, y, dir) {
switch (dir) {
case this.Left:
if (x) return {
x: x - 1,
y: y
};
break;
case this.Up:
if (y) return {
x: x,
y: y - 1
};
break;
case this.Right:
return {
x: x + 1,
y: y
};
break;
case this.Down:
return {
x: x,
y: y + 1
};
break;
}
return {
x: x,
y: y
};
},
// draw map
draw: function draw() {
ctx.beginPath();
ctx.strokeStyle = "#fff";
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.lineWidth = this.scale * 0.5;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = snake.body[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var b = _step.value;
ctx.lineTo(this.scale * 0.5 + b.x * this.scale, this.scale * 0.5 + b.y * this.scale);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
ctx.stroke();
if (snake.body.length < map.size - 1) {
ctx.beginPath();
ctx.fillStyle = "#f80";
ctx.arc(this.scale * 0.5 + food.x * this.scale, this.scale * 0.5 + food.y * this.scale, 0.4 * this.scale, 0, 2 * Math.PI);
ctx.fill();
}
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
点击直接资料领取
如果你在学习python或者Java哪怕是C遇到问题都可以来给我留言,因为在学习初期新手总会走很多弯路,这个时候如果没有有个人来帮一把的话很容易就放弃了。身边很多这样的例子许多人学着学着就转了专业换了方向,不仅是自身问题还是没有正确的学习。所以作为一个过来人我希望有问题给我留言,说不上是帮助就是顺手敲几行字的事情。
这里有python,Java学习资料还有有有趣好玩的编程项目,更有难寻的各种资源。反正看看也不亏。
文章来源: blog.csdn.net,作者:肥学,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/jiahuiandxuehui/article/details/125326338
- 点赞
- 收藏
- 关注作者
评论(0)