使用C++实现简单的二维细胞自动机
细胞自动机(cellular automata)是为模拟包括自组织结构在内的复杂现象提供的一个强有力的方法,也称为元胞自动机(Cellular Automaton)。细胞自动机模型的基本思想是:自然界里许多复杂结构和过程,归根到底只是由大量基本组成单元的简单相互作用所引起。细胞自动机主要研究由小的计算机或部件,按邻域连接方式连接成较大的、并行工作的计算机或部件的理论模型。它分为固定值型、周期型、混沌型以及复杂型。
以上摘自:百度百科
一种周期型图案
分析
二维细胞自动机的运行规则:
- 每个细胞元有两个状态:生或死。
- t+1 时刻的状态由 t 时刻的状态决定。
- 状态变迁规则 1:如果一个活细胞元周围存在 2 或 3 个活细胞元,则他继续存活,否则他将死亡。
- 状态变迁规则 2:如果一个死细胞元周围恰好存在 3 个活细胞元,则他可以复活,否则他依旧是死亡状态。
根据上面列出的信息我们可以知道:
- 每个细胞元周围有 8 个细胞元。
- 每个细胞元 t + 1 时刻的状态由 t 时刻时他附近的细胞元状态决定。
实现
- 构建细胞元类
最开始的想法是将数据用链式结构存储在图里,但实现的过程中发现一个细胞元有 8 个指针域,过于复杂,于是改用顺序结构存储。
// 定义细胞元的两种状态
typedef enum {
dead = 0,
alive,
} stateCell;
// 定义细胞元结构体
// ulState upState urState
// \ | /
// leftState -- selfState -- rightState
// / | \
// dlState downState drState
typedef struct {
stateCell selfState;
stateCell upState;
stateCell downState;
stateCell leftState;
stateCell rightState;
stateCell ulState;
stateCell urState;
stateCell dlState;
stateCell drState;
} singleCell;
// 定义地图的大小是 10 x 10
#define WIDTH 10
#define LENGTH 10
class cells {
private:
singleCell cell[LENGTH][WIDTH] = {dead}; // 使用二维数组存储细胞元
public:
explicit cells(char map[LENGTH][WIDTH]); // 通过用户输入的二维数组来生成地图
void drawCell(); // 在地图上画出细胞元
void playGame(); // 转变状态
}
- 编写细胞元方法
cells::cells(char map[LENGTH][WIDTH]) {
for (int line = 0; line < LENGTH; line++) {
for (int col = 0; col < WIDTH; col++) {
///-------------------------------------------------------- 若为0则死,若为1则生
cell[line][col].selfState = map[LENGTH][WIDTH] == 0 ? dead : alive;
if (line > 0) {
///---------------------------------------------------- 上方
cell[line][col].upCellState = map[LENGTH - 1][WIDTH] == 0 ? dead : alive;
if (col > 0) {
///------------------------------------------------ 左上
cell[line][col].ulCellState = map[LENGTH - 1][WIDTH - 1] == 0 ? dead : alive;
}
if (col < WIDTH - 1) {
///------------------------------------------------ 右上
cell[line][col].urCellState = map[LENGTH - 1][WIDTH + 1] == 0 ? dead : alive;
}
}
if (line < LENGTH - 1) {
///---------------------------------------------------- 下方
cell[line][col].downCellState = map[LENGTH + 1][WIDTH] == 0 ? dead : alive;
if (col > 0) {
///------------------------------------------------ 左下
cell[line][col].dlCellState = map[LENGTH + 1][WIDTH - 1] == 0 ? dead : alive;
}
if (col < WIDTH - 1) {
///------------------------------------------------ 右下
cell[line][col].drCellState = map[LENGTH + 1][WIDTH + 1] == 0 ? dead : alive;
}
}
if (col > 0) {
///---------------------------------------------------- 左方
cell[line][col].leftCellState = map[LENGTH][WIDTH - 1] == 0 ? dead : alive;
}
if (col < WIDTH - 1) {
///---------------------------------------------------- 右方
cell[line][col].rightCellState = map[LENGTH][WIDTH + 1] == 0 ? dead : alive;
}
}
}
}
// 这里我使用了 C 的图形库:EasyX库来绘制图像,生成的画板大小为 600 x 600,因此坐标也要经过计算进行转换
// EasyX库只支持 VS 和 VC6.0,我使用的 IDE 是 CLion,如果你和我一样使用了别的 IDE,需要在CMake中自行配置
void cells::drawCell() {
for (int i = 0; i < LENGTH; i++) {
int yPos = i * 60;
for (int j = 0; j < WIDTH; j++) {
int xPos = j * 60;
///-------------------------------------------------------- 若为生,在对应的坐标画一个黑色正方形
if (cell[i][j].selfState == alive) {
setfillcolor(BLACK);
solidrectangle(xPos, yPos, xPos + 60, yPos + 60);
setlinecolor(WHITE);
rectangle(xPos, yPos, xPos + 60, yPos + 60);
///---------------------------------------------------- 若为死,在对应的坐标画一个白色正方形
} else {
setfillcolor(WHITE);
solidrectangle(xPos, yPos, xPos + 60, yPos + 60);
setlinecolor(BLACK);
rectangle(xPos, yPos, xPos + 60, yPos + 60);
}
}
}
}
// 遵循二维细胞自动机的运行规则编写的方法
// 由于细胞元是存储在二维数组中的,所以必须先遍历更改细胞元的状态,遍历完成后再更新细胞元储存的周围细胞元状态的信息
// 更新的信号存储在一个临时的二维数组里
void cells::playGame() {
///---------------------------------------------------------------- 设置标志位
char indexChange[LENGTH][WIDTH] = {0};
///---------------------------------------------------------------- 转变细胞状态
for (int i = 0; i < LENGTH; i++) {
for (int j = 0; j < WIDTH; j++) {
unsigned sum = cell[i][j].upCellState + cell[i][j].urCellState + cell[i][j].rightCellState
+ cell[i][j].drCellState + cell[i][j].downCellState + cell[i][j].dlCellState
+ cell[i][j].leftCellState + cell[i][j].ulCellState;
///-------------------------------------------------------- 若活细胞周围有2或3个活细胞,则活细胞继续存活
if (cell[i][j].selfState == alive) {
if (sum == 2 || sum == 3) {
cell[i][j].selfState = alive;
} else {
cell[i][j].selfState = dead;
indexChange[i][j] = 1;
}
///---------------------------------------------------- 若死细胞周围恰有3个活细胞,则死细胞复活
} else {
if (sum == 3) {
cell[i][j].selfState = alive;
indexChange[i][j] = -1;
} else {
cell[i][j].selfState = dead;
}
}
}
}
///---------------------------------------------------------------- 更新发生变化的细胞周围的细胞的存储数据
for (int i = 0; i < LENGTH; i++) {
for (int j = 0; j < WIDTH; j++) {
if (indexChange[i][j] > 0) {
if (i > 0) {
///------------------------------------------------ 更新正上方的细胞
cell[i - 1][j].downCellState = dead;
if (j > 0) {
///-------------------------------------------- 更新左上方的细胞
cell[i - 1][j - 1].drCellState = dead;
}
if (j < WIDTH - 1) {
///-------------------------------------------- 更新右上方的细胞
cell[i - 1][j + 1].dlCellState = dead;
}
}
if (i < LENGTH - 1) {
///------------------------------------------------ 更新正下方的细胞
cell[i + 1][j].upCellState = dead;
if (j > 0) {
///-------------------------------------------- 更新左下方的细胞
cell[i + 1][j - 1].urCellState = dead;
}
if (j < WIDTH - 1) {
///-------------------------------------------- 更新右下方的细胞
cell[i + 1][j + 1].ulCellState = dead;
}
}
if (j > 0) {
///------------------------------------------------ 更新正左方的细胞
cell[i][j - 1].rightCellState = dead;
}
if (j < WIDTH - 1) {
///------------------------------------------------ 更新正右方的细胞
cell[i][j + 1].leftCellState = dead;
}
} else if (indexChange[i][j] < 0) {
if (i > 0) {
///------------------------------------------------ 更新正上方的细胞
cell[i - 1][j].downCellState = alive;
if (j > 0) {
///-------------------------------------------- 更新左上方的细胞
cell[i - 1][j - 1].drCellState = alive;
}
if (j < WIDTH - 1) {
///-------------------------------------------- 更新右上方的细胞
cell[i - 1][j + 1].dlCellState = alive;
}
}
if (i < LENGTH - 1) {
///------------------------------------------------ 更新正下方的细胞
cell[i + 1][j].upCellState = alive;
if (j > 0) {
///-------------------------------------------- 更新左下方的细胞
cell[i + 1][j - 1].urCellState = alive;
}
if (j < WIDTH - 1) {
///-------------------------------------------- 更新右下方的细胞
cell[i + 1][j + 1].ulCellState = alive;
}
}
if (j > 0) {
///------------------------------------------------ 更新正左方的细胞
cell[i][j - 1].rightCellState = alive;
}
if (j < WIDTH - 1) {
///------------------------------------------------ 更新正右方的细胞
cell[i][j + 1].leftCellState = alive;
}
}
}
}
}
- 制作实例运行测试
// 我输入了一个周期型细胞元,他会不停地做周期性运动
char map[10][10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 1, 1, 0, 0, 0,
0, 0, 1, 1, 0, 1, 1, 0, 0, 0,
0, 0, 0, 1, 0, 1, 0, 0, 0, 0,
0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
效果如图,成功!