自动贪吃蛇.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><HTML> <HEAD> <TITLE> 自动贪吃蛇游戏</TITLE> <META NAME="Generator" CONTENT="EditPlus"> <META NAME="Author" CONTENT=""> <META NAME="Keywords" CONTENT=""> <META NAME="Description" CONTENT=""> <style> html, body {overflow: hidden;touch-action: none;content-zooming: none;position: absolute;margin: 0;padding: 0;width: 100%;height: 100%;background: #111;}canvas {position: absolute;width: 100vh;height: 100vh;margin: auto;top: 0;bottom: 0;left: 0;right: 0;user-select: none;background: #000;cursor: pointer;} </style> </HEAD> <BODY> <canvas id="c"></canvas> <script> var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();// Adapted from https://johnflux.com/2015/05/02/nokia-6110-part-3-algorithms/{// init mapvar init = function init() {snake.body.length = 0;map.init(10, 10);map.generate_r(-1, -1, 0, 0);map.generateTourNumber();snake.addHead(0, 0);snake.addHead(1, 0);snake.addHead(2, 0);food.add();if (!running) {running = true;run();}};// main loopvar run = function run() {if (snake.body.length < map.size) requestAnimationFrame(run);else running = false;if (fullcpgrid || frame % 4 === 0) {snake.move(snake.nextDirection());ctx.clearRect(0, 0, canvasWidth, canvasHeight);map.draw();}};var map = {// init mapinit: 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// flagsvar _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);};},// directionsLeft: 1,Up: 2,Right: 3,Down: 4,// flat 2D arrayarray2D: 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 collisioncollision: 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 distancedistance: function distance(a, b) {if (a < b) return b - a - 1;else return b - a - 1 this.size;},// Hamiltonian Cyclegenerate_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 cyclefindNextDir: 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 CyclegenerateTourNumber: 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 nodegetNext: 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 mapdraw: 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();}}};// food objectvar food = {x: 0,y: 0,// add random foodadd: 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;}}};// the snakevar 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 IAnextDirection: 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;}};// init canvasvar canvas = document.getElementById("c");var ctx = canvas.getContext('2d');var canvasWidth = canvas.width = 600;var canvasHeight = canvas.height = 600;canvas.onclick = init;var running = false;var frame = 0;var fullcpgrid = window.location.href.indexOf("fullcpgrid") > -1;init();} </script> </BODY></HTML>
评论