书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
第7章目录
7.7 编写生命游戏
1、用二维数组表示细胞状态
- 现在,我们需要将之前的Wolfram CA扩展到二维空间。前面我们用一维数组存放细胞的状态,在生命游戏中,我们需要用二维数组表示细胞状态。
int [][] board = new int[columns][rows];
- 首先,我们用随机的状态值(0或1)初始化board数组中的每个细胞。
for (int x = 0; x < columns; x++) {
for (int y = 0; y < rows; y++) {
current[x][y] = int(random(2)); 用0或1初始化每个细胞
}
}
2、计算下一次迭代
- 为了计算下一次迭代,就像以前一样,我们需要一个新的二维数组。并在遍历过程中将新状态写入这个数组。
int[][] next = new int[columns][rows];
for (int x = 0; x < columns; x++) {
for (int y = 0; y < rows; y++) {
next[x][y] = _________?; 为每个细胞设置新状态
}
}
3、如何引用邻居细胞
- 在研究新状态的计算方法之前,我们要先搞清楚如何引用邻居细胞。
- 在一维CA中,引用邻居细胞很简单:如果细胞的下标是i,那么邻居的下标就是i-1和i+1。
-
在二维CA中,每个细胞都有两个下标:列下标x和行下标y。如图7-27所示,细胞的邻居分别为:(x-1,y-1)、(x,y-1)、(x+1,y-2)、(x-1,y)、(x+1,y)、(x-1,y+1)、(x,y+1)和(x+1,y+1)。
4、邻居数变量
- 在生命游戏中,所有规则都只涉及“活着”的邻居细胞的数量。因此,我们可以引入一个邻居计数器变量,每次发现一个“活着”的邻居,就递增这个变量,最后就能得到“活着”的邻居的总数。
int neighbors = 0;
if (board[x-1][y-1] == 1) neighbors++; 最顶行的邻居
if (board[x ][y-1] == 1) neighbors++;
if (board[x+1][y-1] == 1) neighbors++;
if (board[x-1][y] == 1) neighbors++; 中间的邻居(不包括自身)
if (board[x+1][y] == 1) neighbors++;
if (board[x-1][y+1] == 1) neighbors++; 最底行的邻居
if (board[x ][y+1] == 1) neighbors++;
if (board[x+1][y+1] == 1) neighbors++;
- 如同Wolfram CA,上面的实现方式在教学方面非常有用,它让我们看到了每一个计算步骤(每次找到一个状态为1的邻居,就递增计数器)。但是,“如果细胞状态等于1,则让计数器加1”和“计数器加上细胞状态”这两种描述是等价的,只是后者比前者更巧妙。毕竟,如果细胞只有0和1两个状态,所有邻居细胞状态的和就等于“活着”的邻居的总数。由于邻居处在一个3 × 3的网格内,我们可以把这一步放到另一个循环中。
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
neighbors += board[x+i][y+j]; 将所有邻居的状态相加
}
}
- 上面的代码中有一个错误,在生命游戏中,细胞并不是自己的邻居。因此我们应该加一个条件判断语句:如果i和j同时等于0,则跳过当前邻居;但还有另一种方案,就是在结束循环时再减去自身的状态。
neighbors -= board[x][y]; 减去自身的状态,我们不想把自身也包括在内
5、确定新状态
- 最后,一旦知道“活着”邻居的总数,我们下一步要做的就是确定细胞的新状态,也就是实现生命游戏的规则:新生、死亡或者静止。
if ((board[x][y] == 1) && (neighbors < 2)) { 如果细胞活着,但活着的邻居少于两个,它就因孤next[x][y] = 0;
}
else if ((board[x][y] == 1) && (neighbors > 3)) { 如果细胞活着,但活着的邻居多于3个,它就next[x][y] = 0;
}
else if ((board[x][y] == 0) && (neighbors == 3)) { 如果细胞的状态为死亡,但它有3个活着的next[x][y] = 1;
}
else { 其他情况下,细胞的状态保持不变
next[x][y] = board[x][y];
}
6、显示
下一代状态计算完成后,我们就可以用之前的方法绘制生命游戏——黑色方块代表“活着”,白色方块代表“死亡”。
for ( int i = 0; i < columns;i++) {
for ( int j = 0; j < rows;j++) { 如果状态=1,就绘制黑色方块
if ((board[i][j] == 1)) fill(0);
else fill(255); 如果状态为0,就绘制白色方块
stroke(0);
rect(i*w, j*w, w, w);
}
}