生物细胞繁衍介绍
康威生命游戏,又称康威生命棋,是英国数学家约翰•何顿•康威在1970年发明的细胞自动机。 它最初于1970年10月在《科学美国人》杂志上马丁•葛登能的“数学游戏”专栏出现。在游戏的进行中,杂乱无序的细胞会逐渐演化出各种精致、有形的结构;这些结构往往有很好的对称性,而且每一代都在变化形状。一些形状已经锁定,不会逐代变化。有时,一些已经成形的结构会因为一些无序细胞的“入侵”而被破坏。但是形状和秩序经常能从杂乱中产生出来。对于生成的形状和秩序我们称作 pattern。或者在这里,我们也把它称作 creature。“生命游戏”并不是通常意义上的游戏,它没有游戏玩家之间的竞争,也谈不上输赢,甚至可以说游戏的一开始就注定了结果。
开发生命游戏,让“仿真生物”生存于计算机上,在计算机上生存、死亡,从而模拟生命的演化,通过计算机的模拟,了解生命在一定规则下,开始条件对最终结果的影响,突发事件对最终结果的影响。
本游戏是小组共同开发的课程设计项目,实现了基础的康威生命游戏规则,能够模拟生命繁殖演化的基本过程,实现了必要的图形界面。
生物细胞繁衍生存模拟仿真实现
制作用户图形界面,使得游戏在运行时,用户能在图形界面上进行操作和直观的看到演化过程与结果。
按钮,重新开始、大小、剩余生命、进化次数,用来控制繁衍进程。
用户界面的要求:一个N*N的二维格子界面和对应重新开始、大小、剩余生命、进化次数,每一个格子代表一个生命,亮为生、暗为死,每一次格子的生与死都显示在屏幕上。
一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。用代码实现生命游戏中的规则,通过算法控制,计算格子在每一刻的生死状态。
在一个二维矩形世界中,每个方格里居住着一个活着的或死了的细胞,每个细胞有两种状态-存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。
一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量,模拟生命繁衍。
细胞过少:当周围低于2个(不包含2个)存活细胞时, 本单元活细胞死亡
稳定:当周围有3个或4个存活细胞时, 本单元细胞保持原样
细胞过剩:当周围有4个以上的存活细胞时,本单元活细胞死亡
繁殖:当周围有3个存活细胞时,本单元细胞存活/活化
HTML用到的背景图片
HTML代码
<!DOCTYPE html>
<html>
<head>
<title>细胞生命</title>
<meta charset='utf-8'>
<script type="text/javascript" src="细胞生命.js"></script>
</head>
<body background="细胞生命.jpeg">
<center>
<h2>细胞生命仿真模拟</h2>
<canvas id="canvas" width="602" height="602"></canvas>
<script type="text/javascript" src="fabric.min.js"></script>
<div style="margin:20px 50px 20px;padding-bottom:10px;border-bottom:1px solid #c1c1c1;text-align:center;width:502px">
<input type="button" value="重新开始" style="width:70px;" onclick="javascript:restartLifeGame()">
大小:<input type="text" value="50" id="lifeSize" style="width:50px;margin-right:20px;">
剩余生命:<span id="remainLifes" style="display:inline-block;width:50px;">0</span>
进化次数:<span id="period" style="display:inline-block;width:40px;">0</span>
</div>
</center>
<script>
var life;
var lifeTimer = 0;
var canvas = new fabric.Canvas('canvas');
canvas.renderOnAddRemove = false;
var lifes = [];
restartLifeGame();
function initLifeGame()
{
var rect = new fabric.Rect({
top : 0,
left : 0,
width : 600,
height : 600,
fill : 'white',
selectable: false,
strokeWidth: 1,
stroke: '#C1C1C1'
});
canvas.add(rect);
canvas.renderAll();
}
function restartLifeGame()
{
var size = parseInt(document.getElementById('lifeSize').value);
if (isNaN(size) || size <= 0) {
alert('请输入正确数值');
return;
}
if (lifeTimer != 0)
clearInterval(lifeTimer);
canvas.clear();
initLifeGame();
beginLife(size, size);
}
function beginLife(row,col)
{
life = new LifeGame(row,col);
life.initRandom(20);
var width = 600/life.col;
var height = 600/life.row;
for(var i=0;i<life.row;i++) {
lifes[i] = [];
for(var j=0;j<life.col;j++) {
lifes[i][j] = new fabric.Rect({
top: i*height,
left: j*width,
width: width,
height: height,
fill: 'black',
selectable: false,
stroke: 'rgba(100,200,200,0.5)',
strokeWidth: 1,
visible: false
});
canvas.add(lifes[i][j]);
}
}
refreshLife();
lifeTimer = setInterval(refreshLife, 300);
}
function refreshLife()
{
drawLife();
document.getElementById('remainLifes').innerText = life.remainLifes;
document.getElementById('period').innerText = life.period;
life.nextAround();
}
function drawLife()
{
for(var i=0;i<life.row;i++) {
for(var j=0;j<life.col;j++) {
if (life.isAlive(i,j)) {
lifes[i][j].visible = true;
} else {
lifes[i][j].visible = false;
}
}
}
canvas.renderAll();
}
function pauseLife()
{
clearInterval(lifeTimer);
}
</script>
</body>
</html>
js代码
function LifeGame(row, col) {
this.row = row;
this.col = col;
this.grid = [];
this.remainLifes = 0;
this.nextRemainLifes = 0;
this.period = 1;
}
//随机初始化生命,按百分比随机生成生命数量
LifeGame.prototype.initRandom = function(percent) {
this.remainLifes = 0;
this.period = 1;
for(var i=0;i<this.row;i++) {
this.grid[i] = [];
for(var j=0;j<this.col;j++) {
if (Math.random() * 100 <= percent) {
this.grid[i][j] = {'state':1, 'nextState':1};
this.remainLifes++;
this.nextRemainLifes = this.remainLifes;
} else {
this.grid[i][j] = {'state':0, 'nextState':0};
}
}
}
};
//计算某个生命的邻居生存个数
LifeGame.prototype.aliveCountAround = function(x,y) {
return this.grid[this.mapX(x-1)][this.mapY(y-1)].state +
this.grid[this.mapX(x-1)][y].state +
this.grid[this.mapX(x-1)][this.mapY(y+1)].state +
this.grid[x][this.mapY(y-1)].state +
this.grid[x][this.mapY(y+1)].state +
this.grid[this.mapX(x+1)][this.mapY(y-1)].state +
this.grid[this.mapX(x+1)][y].state +
this.grid[this.mapX(x+1)][this.mapY(y+1)].state;
};
//左右边界的映射,超出左边界则认为是右边界关联,如-1会映射为是最右侧,这样会让游戏的宽度是无限延展的
LifeGame.prototype.mapX = function(x) {
return (x >= this.row || x < 0 ) ? (x%this.row + this.row) % this.row : x;
};
//上下边界的映射,参见mapX
LifeGame.prototype.mapY = function(y) {
return (y >= this.col || y < 0 ) ? (y%this.col + this.col) % this.col: y;
};
//计算某个生命的下一回合的生存状态
LifeGame.prototype.nextState = function(x,y) {
var aliveCountAround = this.aliveCountAround(x,y);
if (aliveCountAround >= 4) {
return 0;
} else if(aliveCountAround === 3) {
return 1;
} else if(aliveCountAround >= 2) {
return this.grid[x][y].nextState;
} else {
return 0;
}
};
//计算所有生命的下一回合的生存状态
LifeGame.prototype.calcNextState = function() {
this.nextRemainLifes = 0;
for(var i=0;i<this.row;i++) {
for(var j=0;j<this.col;j++) {
this.grid[i][j].nextState = this.nextState(i, j);
if (this.grid[i][j].nextState === 1) {
this.nextRemainLifes++;
}
}
}
};
//转换到下一回合的生存状态
LifeGame.prototype.changeNextState = function() {
for(var i=0;i<this.row;i++) {
for(var j=0;j<this.col;j++) {
this.grid[i][j].state = this.grid[i][j].nextState;
}
}
this.remainLifes = this.nextRemainLifes;
this.period++;
};
//下一回合
LifeGame.prototype.nextAround = function() {
this.calcNextState();
this.changeNextState();
};
//某个生命是否存活
LifeGame.prototype.isAlive = function(x,y) {
return this.grid[x][y].state === 1;
};