利用JS實(shí)現(xiàn)AI自動(dòng)玩貪吃蛇
演示
自動(dòng)貪吃蛇
技術(shù)棧
bottom 屬性規(guī)定元素的底部邊緣。該屬性定義了定位元素下外邊距邊界與其包含塊下邊界之間的偏移。
注釋:如果 “position” 屬性的值為 “static”,那么設(shè)置 “bottom” 屬性不會(huì)產(chǎn)生任何效果。
對(duì)于 static 元素,為 auto;對(duì)于長度值,則為相應(yīng)的絕對(duì)長度;對(duì)于百分比數(shù)值,為指定值;否則為 auto。
對(duì)于相對(duì)定義元素,如果 bottom 和 top 都是 auto,其計(jì)算值則都是 0;如果其中之一為auto,則取另一個(gè)值的相反數(shù);如果二者都不是 auto,bottom 將取 top 值的相反數(shù)。
- 默認(rèn)值: auto
- 繼承性: no
- 版本: CSS2
- JavaScript 語法: object.style.bottom="50px"
user-select 屬性規(guī)定是否能選取元素的文本。
在 web 瀏覽器中,如果您在文本上雙擊,文本會(huì)被選取或高亮顯示。此屬性用于阻止這種行為。
user-select: auto|none|text|all;
- auto 默認(rèn)。如果瀏覽器允許,則可以選擇文本。
- none 防止文本選取。
- text 文本可被用戶選取。
- all 單擊選取文本,而不是雙擊。
源碼
樣式設(shè)置
canvas { position: absolute; width: 100vh; height: 100vh; margin: auto; top: 0; bottom: 0; left: 0; right: 0; user-select: none; background: #000; cursor: pointer; }
構(gòu)建食物對(duì)象
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; } } };
構(gòu)建貪吃蛇對(duì)象
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; } };
構(gòu)建自動(dòng)貪吃
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(); } } };
到此這篇關(guān)于利用JS實(shí)現(xiàn)AI自動(dòng)玩貪吃蛇的文章就介紹到這了,更多相關(guān)JS自動(dòng)玩貪吃蛇內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mqtt.js?無法連接/錯(cuò)誤提示?WebSocket?connection?to?‘ws://xxxxx‘?
這篇文章主要介紹了mqtt.js?無法連接/錯(cuò)誤提示?WebSocket?connection?to?‘ws://xxxxx‘?failed:,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01JS實(shí)現(xiàn)點(diǎn)擊登錄彈出窗口同時(shí)背景色漸變動(dòng)畫效果
這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊登錄彈出窗口同時(shí)背景色漸變動(dòng)畫效果,涉及JavaScript基于鼠標(biāo)事件及時(shí)間函數(shù)定時(shí)觸發(fā)形成漸變動(dòng)畫的相關(guān)技巧,需要的朋友可以參考下2016-03-03javascript的漸進(jìn)增強(qiáng)與平穩(wěn)退化淺談
2013-11-11JavaScript實(shí)現(xiàn)元素滾動(dòng)條到達(dá)一定位置循環(huán)追加內(nèi)容
下面小編就為大家分享一篇JavaScript實(shí)現(xiàn)元素滾動(dòng)條到達(dá)一定位置循環(huán)追加內(nèi)容,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-12-12JS Testing Properties 判斷屬性是否在對(duì)象里的方法
下面小編就為大家?guī)硪黄狫S Testing Properties 判斷屬性是否在對(duì)象里的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10JavaScript CSS 通用循環(huán)滾動(dòng)條
核心是 position:relative;,才能讓其內(nèi)部的 ul 以絕對(duì)定位,通過改變 top 值實(shí)現(xiàn)向上移位置。2009-10-10