当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 兰顿蚂蚁html版

兰顿蚂蚁html版

2020年10月24日  | 移动技术网IT编程  | 我要评论
1024快乐!用html+js模拟兰顿蚂蚁(Langton‘s Ant)。地图和蚂蚁是分离的,地图自动扩充,地图能拖动缩放,,bug未知兰顿蚂蚁,是于1986年,由克里斯·兰顿提出来的,属于细胞自动机的一种。平面上的正方形格子被填上黑色或白色。在其中一格正方形内有一只“蚂蚁”。蚂蚁的头部朝向为:上下左右其中一方。蚂蚁的移动规则十分简单:若蚂蚁在黑格,右转90度,将该格改为白格,并向前移一格;若蚂蚁在白格,左转90度,将该格改为黑格,并向前移一格。规则虽然简单,蚂蚁的行为却十分复.

1024快乐!

 用html+js模拟兰顿蚂蚁(Langton‘s Ant)。地图和蚂蚁是分离的,地图自动扩充,地图能拖动缩放,可以添加干扰,,bug未知

兰顿蚂蚁,是于1986年,由克里斯·兰顿提出来的,属于细胞自动机的一种。

平面上的正方形格子被填上黑色或白色。在其中一格正方形内有一只“蚂蚁”。
蚂蚁的头部朝向为:上下左右其中一方。

蚂蚁的移动规则十分简单:
若蚂蚁在黑格,右转90度,将该格改为白格,并向前移一格;
若蚂蚁在白格,左转90度,将该格改为黑格,并向前移一格。

规则虽然简单,蚂蚁的行为却十分复杂。刚刚开始时留下的路线都会有接近对称,像是会重复,但不论起始状态如何,蚂蚁经过漫长的混乱活动后,会开辟出一条规则的“高速公路”。

<!-- ######################################################################### -->
<button onclick="nextSteps(1)">下一步</button>
<button onclick="nextSteps(10)">下10步</button>
<button onclick="nextSteps(100)">下100步</button>
<button onclick="nextSteps(1000)">下1000步</button>
<button onclick="nextSteps()">下N步</button>
<input type='number' min=1 max=10000 value=100 id="stepsCount">

<button onclick="randomDots()">干扰点</button>

<button onclick="nextStepsStart()">开始</button>
<button onclick="nextStepsStop()">停止</button>
步数:<span id='stepsTotal'>0</span>

<br>
<canvas id="out" width="800px" height="600px" style="border:2px solid #777;"></canvas>
<script>
	
	
	
	function CellMap(canvasId = 'out', initMapWidth = 50, initMapHeight = 50, mapWidthMax = 100000, mapHeightMax = 100000) {
		
		this.canvasId = canvasId;
		
		this.map = null;
		this.mapWidth = initMapWidth;
		this.mapHeight = initMapHeight;
		this.mapWidthMax = mapWidthMax;
		this.mapHeightMax = mapHeightMax;
		
		this.canvas = null;
		this.cctx = null;
		
		this.mapImageZoomMin = 0.5;
		this.mapImageZoomMax = 10;
		
		this.cellBaseWidth = 8;
		this.mapImageZoom = 1;
		this.mapImageOffsetX = 0;
		this.mapImageOffsetY = 0;
		this.mapImageWidth = null;
		this.mapImageHeight = null;
		
		// ------------------------------------
		this.init = function() {
			this.initMap();
			this.initCanvas();
			this.initVMap();
			
		}
		this.initMap = function() {
			this.map = Array(this.mapHeight);
			for (var i = 0, len = this.mapHeight; i < len; i++) {
				this.map[i] = Array(this.mapWidth);
			}
		}
		this.initCanvas = function() {
			this.canvas = document.getElementById(this.canvasId);
			this.canvasWidth = this.canvas.width;
			this.canvasHeight = this.canvas.height;
			//console.log(this.canvas, this.canvasWidth, this.canvasHeight);
			this.cctx = this.canvas.getContext("2d");
			this.cctx.strokeStyle = '#aaa';
			this.cctx.fillStyle = '#888';
			this.cctx.lineWidth = 1;
			this.cctx.translate(0.5,0.5);
		}
		// 支持缩放和拖动地图
		this.bindCanvasEvent = function() {
			var that = this;
			var down = false;
			var x1, x2, y1, y2;
			this.canvas.onmousedown = function(evt) { // 触发鼠标按下
				down = true;
				x1 = evt.offsetX;
				y1 = evt.offsetY;
			}
			this.canvas.onmouseup = function(evt) { // 触发鼠标抬起
				down = false;
				x2 = evt.offsetX;
				y2 = evt.offsetY;
				that.moveMapImageDisplayArea(x2 - x1, y2 - y1);
			}
			this.canvas.onmousemove = function(evt) { // 触发鼠标拖动
				if (!down) {
					return;
				}
				var x = evt.offsetX;
				var y = evt.offsetY;
				that.moveMapImageDisplayArea(x - x1, y - y1);
				x1 = x;
				y1 = y;
			}
			this.canvas.onmouseenter = function(evt) { // 触发鼠标进入
				down = false;
			}
			this.canvas.onmouseleave = function(evt) { // 触发鼠标离开
				down = false;
			}
			this.canvas.onmousewheel = function(evt) { // 触发鼠标滚轮
				var wheelDelta = evt.wheelDelta;
				that.zoomMapImageDisplayArea(wheelDelta, evt.offsetX, evt.offsetY);
			}
			// 手机端拖动
			this.canvas.ontouchstart = function(evt) { // 触发鼠标按下
				down = true;
				x1 = evt.touches[0].screenX;
				y1 = evt.touches[0].screenY;
				evt.preventDefault();
			}
			this.canvas.ontouchend = function(evt) { // 触发鼠标抬起
				down = false;
				x2 = evt.touches[0].screenX;
				y2 = evt.touches[0].screenY;
				that.moveMapImageDisplayArea(x2 - x1, y2 - y1);
				evt.preventDefault();
			}
			this.canvas.ontouchmove = function(evt) { // 触发鼠标拖动
				if (!down) {
					return;
				}
				var x = evt.touches[0].screenX;
				var y = evt.touches[0].screenY;
				that.moveMapImageDisplayArea(x - x1, y - y1);
				x1 = x;
				y1 = y;
				evt.preventDefault();
			}
		}
		
		// ------------------------------------
		// 移动地图
		this.moveMapImageDisplayArea = function(offsetX, offsetY) {
			//console.log(offsetX, offsetY);
			this.setMapImageDisplayArea(null, this.mapImageOffsetX - offsetX, this.mapImageOffsetY - offsetY);
			this.drawLines();
			this.drawCells();
		}
		// 缩放地图
		this.zoomMapImageDisplayArea = function(wheelDelta, pointerX, pointerY) {
			var newZoom = this.mapImageZoom;
			if (wheelDelta > 0) {
				newZoom += this.mapImageZoom * 0.1;
			} else {
				newZoom -= this.mapImageZoom * 0.1;
			}
			if (newZoom < this.mapImageZoomMin || newZoom > this.mapImageZoomMax) {
				return;
			}
			// 光标处点位置保持不变
			var xx = (this.mapImageOffsetX + pointerX) / this.mapImageZoom * newZoom - pointerX;
			var yy = (this.mapImageOffsetY + pointerY) / this.mapImageZoom * newZoom - pointerY;
			this.setMapImageDisplayArea(newZoom, xx, yy);
			this.drawLines();
			this.drawCells();
		}
		// 设置地图要显示的位置
		this.setMapImageDisplayArea = function(mapImageZoom = undefined, mapImageOffsetX = undefined, mapImageOffsetY = undefined) {
			
			if (mapImageZoom) {
				if (mapImageZoom < this.mapImageZoomMin) {
					mapImageZoom = this.mapImageZoomMin;
				}
				if (mapImageZoom > this.mapImageZoomMax) {
					mapImageZoom = this.mapImageZoomMax;
				}
				this.mapImageZoom = mapImageZoom;
			}
			if (mapImageOffsetX)
				this.mapImageOffsetX = mapImageOffsetX;
			if (mapImageOffsetY)
				this.mapImageOffsetY = mapImageOffsetY;
			
			// 地图图片大小
			this.mapImageWidth = this.mapWidth * this.cellBaseWidth * this.mapImageZoom;
			this.mapImageHeight = this.mapHeight * this.cellBaseWidth * this.mapImageZoom;
			
			// 展示到canvas上时,对应的cell或线的索引的位置
			var x1 = Math.floor(this.mapImageOffsetX / this.cellBaseWidth / this.mapImageZoom);
			var x2 = Math.ceil((this.mapImageOffsetX + this.canvasWidth) / this.cellBaseWidth / this.mapImageZoom);
			var y1 = Math.floor(this.mapImageOffsetY / this.cellBaseWidth / this.mapImageZoom);
			var y2 = Math.ceil((this.mapImageOffsetY + this.canvasHeight) / this.cellBaseWidth / this.mapImageZoom);
			
			//console.log(x1, x2, y1, y2);
			
			this.mapX1 = x1;
			this.mapX2 = x2;
			this.mapY1 = y1;
			this.mapY2 = y2;
			
			// cell的长宽
			this.cellImageBaseWidth = 1 * this.cellBaseWidth * this.mapImageZoom;
		}
		
		// 绘制网格
		this.drawLines = function() {
			var [x1, x2, y1, y2] = [this.mapX1, this.mapX2, this.mapY1, this.mapY2];
			// 边线位置
			var xFirstLinePos = x1 >= 0 ? 0 : (-this.mapImageOffsetX);
			var xLastLinePos = x2 < this.mapWidth ? this.canvasWidth : (this.mapImageWidth - this.mapImageOffsetX);
			var yFirstLinePos = y1 >= 0 ? 0 : (-this.mapImageOffsetY);
			var yLastLinePos = y2 < this.mapHeight ? this.canvasHeight : (this.mapImageHeight - this.mapImageOffsetY);
			//console.log('firstlast', xFirstLinePos, xLastLinePos, yFirstLinePos, yLastLinePos);
			
			//console.log('this.mapWidth', this.mapWidth);
			//console.log('this.mapHeight', this.mapHeight);
			
			var ctx = this.cctx;
			
			ctx.fillStyle = '#ffffff';
			ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
			
			ctx.beginPath();
			for (var i = x1; i <= x2; i++) {
				if (i < 0 || i > this.mapWidth) { // 超界线不绘制
					continue;
				}
				// 实际线位置,需要矫正偏移量
				var pos = i * this.cellBaseWidth * this.mapImageZoom;
				pos = pos - this.mapImageOffsetX;
				pos = Math.floor(pos);
				ctx.moveTo(pos, yFirstLinePos);
				ctx.lineTo(pos, yLastLinePos);
			}
			for (var i = y1; i <= y2; i++) {
				if (i < 0 || i > this.mapHeight) {
					continue;
				}
				var pos = i * this.cellBaseWidth * this.mapImageZoom;
				pos = pos - this.mapImageOffsetY;
				pos = Math.floor(pos);
				ctx.moveTo(xFirstLinePos, pos);
				ctx.lineTo(xLastLinePos, pos);
			}
			ctx.strokeStyle = '#aaa';
			ctx.stroke();
		}
		// 绘制格子状态
		this.drawCells = function() {
			var [x1, x2, y1, y2] = [this.mapX1, this.mapX2, this.mapY1, this.mapY2];
			
			var ctx = this.cctx;
			ctx.fillStyle = '#888';
			var pww = this.cellImageBaseWidth - 2;
			for (var i = x1; i <= x2; i++) {
				if (i < 0 || i >= this.mapWidth)
					continue;
				var px1 = i * this.cellBaseWidth * this.mapImageZoom - this.mapImageOffsetX;
				px1 = Math.floor(px1);
				for (var j = y1; j <= y2; j++) {
					if (j < 0 || j >= this.mapHeight)
						continue;
					if (this.map[j][i] === 1) {
						var py1 = j * this.cellBaseWidth * this.mapImageZoom - this.mapImageOffsetY;
						py1 = Math.floor(py1);
						ctx.fillRect(px1 + 1, py1 + 1, pww, pww);
					}
				}
			}
		}
		// 绘制格子状态
		this.drawCell = function(x, y, val = undefined) {
			//return;
			var [x1, x2, y1, y2] = [this.mapX1, this.mapX2, this.mapY1, this.mapY2];
			if (x >= x1 && x <= x2 && y >= y1 && y <= y2) {
				var pww = this.cellImageBaseWidth - 2;
				var px1 = x * this.cellBaseWidth * this.mapImageZoom - this.mapImageOffsetX;
				px1 = Math.floor(px1);
				var py1 = y * this.cellBaseWidth * this.mapImageZoom - this.mapImageOffsetY;
				py1 = Math.floor(py1);
				if (val === 1)
					this.cctx.fillStyle = '#888';
				else
					this.cctx.fillStyle = '#ffffff';
				this.cctx.fillRect(px1 + 1, py1 + 1, pww, pww);
			}
		}
		// 干扰点
		this.setRandomDots = function() {
			for (var i = 0, len = this.mapHeight; i < len; i++) {
				var j = Math.floor(Math.random() * 1000) % this.mapWidth;
				if (this.map[i][j] === 1)
					this.map[i][j] = 0;
				else
					this.map[i][j] = 1;
			}
			this.drawLines();
			this.drawCells();
		}
		
		// ------------------------------------
		// 扩充地图
		this.grownVMap = function(vmapX, vmapY) {
			if (vmapX > this.vmapX1 && vmapX < this.vmapX2 && vmapY > this.vmapY1 && vmapY < this.vmapY2) {
				return;
			}
			
			// 扩充方向
			var gx = 0;
			if (vmapX <= this.vmapX1) {
				gx = 1;
			} else if (vmapX >= this.vmapX2) {
				gx = 2;
			}
			var gy = 0;
			if (vmapY <= this.vmapY1) {
				gy = 1;
			} else if (vmapY >= this.vmapY2) {
				gy = 2;
			}
			// 扩充后地图大小
			var mapWidthNew = gx === 0 ? this.mapWidth : Math.floor(this.mapWidth * 1.3);
			var mapHeightNew = gy === 0 ? this.mapHeight : Math.floor(this.mapHeight * 1.3);
			if (mapWidthNew > mapWidthMax) {
				mapWidthNew = this.mapWidthMax;
			}
			if (mapHeightNew > mapHeightMax) {
				mapHeightNew = this.mapHeightMax;
			}
			//console.log('grownVMap ...', this.mapWidth, this.mapHeight, ' to ', mapWidthNew, mapHeightNew);
			//console.log('grownVMap ...gx gy', gx, gy);
			
			// 新旧对应的位置
			var tx = 0, tx2 = 0, ty = 0, ty2 = 2;
			if (gx === 1) {
				tx = mapWidthNew - this.mapWidth; // 前面的是扩充的
				tx2 = mapWidthNew;
			} else {
				tx = 0;
				tx2 = this.mapWidth; // 后面没了,或还有扩充的
			}
			if (gy == 1) {
				ty = mapHeightNew - this.mapHeight;
				ty2 = mapHeightNew;
			} else {
				ty = 0;
				ty2 = this.mapHeight;
			}
			// 新的地图
			var map2 = Array(mapHeightNew);
			for (var i = 0, len = mapHeightNew; i < len; i++) {
				if (i >= ty && i < ty2) { // 纵向范围内,从原来的计算
					if (gx === 1) {
						var arr1 = Array(mapWidthNew - this.mapWidth);
						var arr2 = this.map[i - ty]
						map2[i] = arr1.concat(arr2); // 前扩
					} else if (gx === 2) {
						var arr1 = Array(mapWidthNew - this.mapWidth);
						var arr2 = this.map[i - ty]
						map2[i] = arr2.concat(arr1); // 后扩
					} else {
						map2[i] = this.map[i - ty]; // 直接用原来的
					}
				} else {
					map2[i] = Array(mapWidthNew); // 纵向范围外
				}
			}
			
			// 地图更新
			this.mapWidth = mapWidthNew;
			this.mapHeight = mapHeightNew;
			this.map = map2;
			//console.log('grownVMap ', this.map);
			
			// 调整绘制偏移量
			if (gx === 1) {
				this.mapImageOffsetX = this.mapImageOffsetX + tx * this.cellBaseWidth * this.mapImageZoom;
			}
			if (gy === 1) {
				this.mapImageOffsetY = this.mapImageOffsetY + ty * this.cellBaseWidth * this.mapImageZoom;
			}
			
			// 绘制更新
			this.setMapImageDisplayArea();
			this.drawLines();
			this.drawCells();
			
			// 虚拟地图更新
			if (gx === 1) {
				this.vmapX0 = this.vmapX0 + tx;
			}
			if (gy === 1) {
				this.vmapY0 = this.vmapY0 + ty;
			}
			this.initVMap(this.vmapX0, this.vmapY0);
		}
		this.initVMap = function(vmapX0 = undefined, vmapY0 = undefined) {
			this.vmapX0 = vmapX0 ? vmapX0 : (this.mapWidth >> 1);
			this.vmapY0 = vmapY0 ? vmapY0 : (this.mapHeight >> 1);
			this.vmapX1 = -this.vmapX0;
			this.vmapX2 = this.mapWidth - this.vmapX0;
			this.vmapY1 = -this.vmapY0;
			this.vmapY2 = this.mapHeight - this.vmapY0;
			//console.log('initVMap',
			//		'vmapX0,vmapY0=', this.vmapX0, this.vmapY0,
			//		'vmapX1,vmapX2=', this.vmapX1, this.vmapX2,
			//		'vmapY1,vmapY2=', this.vmapY1, this.vmapY2);
		}
		this.mapx = function(vmapX) {
			return vmapX - this.vmapX0;
		}
		this.mapy = function(vmapY) {
			return vmapY - this.vmapY0;
		}
		// 虚拟地图的坐标点的值
		this.vmapPoint = function(vmapX, vmapY, val = undefined) {
			this.grownVMap(vmapX, vmapY);
			
			var mapx = vmapX + this.vmapX0;
			var mapy = vmapY + this.vmapY0;
			
			if (mapx < 0 || mapx >= this.mapWidth)
				return;
			if (mapy < 0 || mapy >= this.mapHeight)
				return;
			
			if (val === undefined) {
				return this.map[mapy][mapx];
			} else {
				//console.log('set cell', mapx, mapy, val);
				this.map[mapy][mapx] = val;
				this.drawCell(mapx, mapy, val);
			}
		}
	}
	
	function LangtonsAnt(canvasId = 'out', initMapWidth = 50, initMapHeight = 50, mapWidthMax = 100000, mapHeightMax = 100000) {
		CellMap.call(this, canvasId, initMapWidth, initMapHeight, mapWidthMax, mapHeightMax);
		
		this.stepsTotal = 0;
		
		this.antX = 0;
		this.antY = 0;
		this.antDirection = 1;
		
		this.super = {};
		this.super.init = this.init;
		
		this.init = function() {
			this.initMap(true);
			this.initCanvas();
			this.initVMap();
			//this.super.init.call(this);
			this.antX = 0;
			this.antY = 0;
			this.vmapPoint(this.antX, this.antY, 1);
		}
		
		this.nextStep = function() {
			var now = this.vmapPoint(this.antX, this.antY);
			if (now === undefined || now === 0) {
				this.antDirection += 1;
				if (this.antDirection === 5)
					this.antDirection = 1;
				this.vmapPoint(this.antX, this.antY, 1);
			} else {
				this.antDirection -= 1;
				if (this.antDirection === 0)
					this.antDirection = 4;
				this.vmapPoint(this.antX, this.antY, 0);
			}
			if (this.antDirection === 1) {
				this.antY --;
			} else if (this.antDirection === 2) {
				this.antX ++;
			} else if (this.antDirection === 3) {
				this.antY ++;
			} else if (this.antDirection === 4) {
				this.antX --;
			}
			
			this.stepsTotal++;
			//console.log('ant', this.antX, this.antY, now, this.stepsTotal);
			
		}
		this.nextSteps = function(cnt) {
			//console.log("nextSteps:", cnt);
			for (var i = 0; i < cnt; i++) {
				this.nextStep();
			}
		}
	}
	
	var ltant = new LangtonsAnt('out', 10, 10, 10000, 10000);
	ltant.mapImageZoom = 1;
	console.log(ltant);
	ltant.init();
	ltant.bindCanvasEvent();
	ltant.setMapImageDisplayArea();
	ltant.drawLines();
	ltant.drawCells();
	
	function nextSteps(cnt) {
		nextStepsStop();
		if (cnt === undefined) {
			cnt = document.getElementById("stepsCount").value;
			cnt = parseInt(cnt);
		}
		ltant.nextSteps(cnt);
		document.getElementById("stepsTotal").innerHTML = ltant.stepsTotal;
	}
	function nextStepsStart() {
		if (window.interval != null)
			return;
		window.interval = setInterval(function() {
			ltant.nextSteps(1);
			document.getElementById("stepsTotal").innerHTML = ltant.stepsTotal;
		}, 100);
	}
	function nextStepsStop() {
		if (window.interval == null)
			return;
		clearInterval(window.interval);
		window.interval = null;
	}
	function randomDots() {
		nextStepsStop();
		ltant.setRandomDots();
	}
</script>

<pre>
	
  兰顿蚂蚁,是于1986年,由克里斯·兰顿提出来的,属于细胞自动机的一种。

  平面上的正方形格子被填上黑色或白色。在其中一格正方形内有一只“蚂蚁”。
  蚂蚁的头部朝向为:上下左右其中一方。

  蚂蚁的移动规则十分简单:
  若蚂蚁在黑格,右转90度,将该格改为白格,并向前移一格;
  若蚂蚁在白格,左转90度,将该格改为黑格,并向前移一格。

  规则虽然简单,蚂蚁的行为却十分复杂。刚刚开始时留下的路线都会有接近对称,像是会重复,但不论起始状态如何,蚂蚁经过漫长的混乱活动后,会开辟出一条规则的“高速公路”。
  
</pre>
<!-- ######################################################################### -->

 

本文地址:https://blog.csdn.net/superzlc/article/details/109257190

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网