使用C++实现简单的二维细胞自动机

使用C++实现简单的二维细胞自动机

细胞自动机(cellular automata)是为模拟包括自组织结构在内的复杂现象提供的一个强有力的方法,也称为元胞自动机(Cellular Automaton)。细胞自动机模型的基本思想是:自然界里许多复杂结构和过程,归根到底只是由大量基本组成单元的简单相互作用所引起。细胞自动机主要研究由小的计算机或部件,按邻域连接方式连接成较大的、并行工作的计算机或部件的理论模型。它分为固定值型、周期型、混沌型以及复杂型。

以上摘自:百度百科
细胞自动机
一种周期型图案

分析

二维细胞自动机的运行规则:

  1. 每个细胞元有两个状态:生或死。
  2. t+1 时刻的状态由 t 时刻的状态决定。
  3. 状态变迁规则 1:如果一个活细胞元周围存在 2 或 3 个活细胞元,则他继续存活,否则他将死亡。
  4. 状态变迁规则 2:如果一个死细胞元周围恰好存在 3 个活细胞元,则他可以复活,否则他依旧是死亡状态。

根据上面列出的信息我们可以知道:

  1. 每个细胞元周围有 8 个细胞元
  2. 每个细胞元 t + 1 时刻的状态由 t 时刻时他附近的细胞元状态决定。

实现

  1. 构建细胞元类
    最开始的想法是将数据用链式结构存储在图里,但实现的过程中发现一个细胞元有 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();	// 转变状态
}

  1. 编写细胞元方法
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;
                }
            }
        }
    }
}


  1. 制作实例运行测试
// 我输入了一个周期型细胞元,他会不停地做周期性运动
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};

在這裡插入圖片描述
效果如图,成功!

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值