基于 元胞自动机-森林火灾模拟_vonneumann邻域-CSDN博客
进行略微修改,解决固定方向着火问题,用了一个meshv2数组记录下一状态,避免旧状态重叠数据失效。
参数调整
澳洲森林火灾蔓延数学建模,基于元胞自动机模拟多模式下火灾蔓延(附部分源码)海啸传播模型_51CTO博客_森林火灾蔓延数学模型
生命游戏 - CodeBus
// 生命游戏学习
#include <easyx/easyx.h>
#include <conio.h>
#include <time.h>
int world[102][102]; // 二维世界,每个格子是一个细胞
IMAGE imgLive, imgEmpty;
// 方形世界
void SquareWorld() {
memset(world, 0, 102 * 102 * sizeof(int)); // 分配内存
for (int i = 1; i <= 100; i++) {
world[i][1] = world[i][100] = 1;
}
for (int j = 1; j <= 100; j++) {
world[1][j] = world[100][j] = 1;
}
}
// 设置界面
void Init() {
initgraph(640, 480);
srand((unsigned)time(NULL)); // 设置随机种子
Resize(&imgLive, 4, 4); // 一个格子放置一个细胞,一个细胞占4像素
Resize(&imgEmpty, 4, 4);
SetWorkingImage(&imgLive); // 在imgLive上绘制
setfillcolor(GREEN);
fillellipse(0, 0, 3, 3); // 给格子涂色
SetWorkingImage(&imgEmpty); // 在imgEmpty上绘制
setfillcolor(DARKGRAY);
rectangle(1, 1, 2, 2); // 给格子涂色
SetWorkingImage();
SquareWorld(); // 产生默认的细胞以方形分布的世界
}
// 绘制世界
void PaintWorld() {
for (int i = 1; i <= 100; i++)
for (int j = 1; j <= 100; j++)
putimage(16 + j * 4, 56 + i * 4, world[i][j] == 1 ? &imgLive : &imgEmpty); // 格子是1,就房子imgLive,否则就贴图贴入imgEmpty
}
// 进化
void Evolution() {
int tmp[102][102]={0}; // 临时数组 一定要初始化为0,否则会图像不对称,因为有数据残留
int sum; // 九宫格中的活细胞格个数
for (int i = 1; i <= 100; i++) { // 一个细胞在下一个时刻的生死取决于相邻八个方格中的活细胞数量,
for (int j = 1; j <= 100; j++) {
sum = world[i - 1][j - 1] + world[i - 1][j + 0] + world[i - 1][j + 1] + // 序号与排版对应,i-1就是上一行,j-1就是最左列,
world[i + 0][j - 1] + + world[i + 0][j + 1] + // i是本行,j是本列
world[i + 1][j - 1] + world[i + 1][j + 0] + world[i + 1][j + 1]; // i+1是下一行,j+1是最右列
switch (sum) {
case 3:
tmp[i][j] = 1; // 若周围有 3 个活细胞,则该方格产生一个活细胞(模拟繁殖)
break;
case 2:
tmp[i][j] = world[i][j]; // 若周围有 2 个或 3 个活细胞,保持原样。
break;
default:
tmp [i][j] = 0; // 若周围少于 2 个活细胞,则该方格的细胞死亡(模拟人口稀疏),若周围多于 3 个活细胞,则该方格的细胞死亡(模拟极度拥挤)
break;
}
}
}
// tmp 产生原因:隔断新旧世界数据
// 因为 sum 需要的是旧世界周围的数据,如果直接更新,导致旧世界数据不知,直接拿新世界的数据采样,结果更不可理解,于是使用 tmp 数组记录
// 将临时数组恢复为世界
memcpy(world, tmp, 102 * 102 * sizeof(int)); // 就是复制粘贴,主要是int 而不是 __int8
}
int main() {
Init();
int speed = 10; // 游戏刷新速度10毫秒
while (1) {
Evolution();
PaintWorld();
Sleep(speed);
}
return 0;
}
// 联系方式 b站 民用级脑的研发记录
// 细胞自动机森林火灾模型演示,
// 开发环境 小熊猫c++ 2.25.1
// 基于 //https://blog.csdn.net/weixin_43524214/article/details/105099165
// 使用 easyx 图形库
#include <stdio.h>
#include <stdlib.h>
#include <easyx/graphics.h>
#include <time.h>
#include <math.h>
// 定义树木状态,给数字贴上意义,用于快速理解代码
#define no_tree 0
#define burning 2
#define nor_tree 1
#define WIDTH 600 // 宽度
#define HEIGHT 600 // 高度
#define treeSize 4 // 树木大小
#define treeNum 150 // 树木个数
#define Row 150 // 150 行
#define Col 150 // 150 列
#define burning_rate 0.0001 // 初始燃烧概率0.01%
#define proTree_rate 0.02 // 空地长出新的树木的概率:10%
#define unsafe_rate 0.00005 // 新长出树木又失火的概率 0,0005%
#define N 10000
/*
演化规则如下:
(1)若某树木元胞的4个邻居中有燃烧着的,那么该元胞下一时刻的状态是燃烧;
(2)一个燃烧着的元胞,在下一时刻变成空位;
(3)所有树木元胞(状态为2)以一个低概率开始燃烧(模拟闪电造成森林火灾);
(4)所有空位元胞以一个低概率变为树木(模拟新树木的生长)。
*/
int treeState_Matrix[treeNum][treeNum]; // 树木格子,一个格子就是一棵树
struct POS {
int x;
int y;
};
// 画边界,画黑色线
void InitBoard() {
setlinecolor(RGB(0, 0, 0)); // 黑线
for (int i = 0; i < treeNum; i++) { // 每个格子横纵线
line(0, i * treeSize, WIDTH, i * treeSize);
line(i * treeSize, 0, i * treeSize, HEIGHT);
}
}
// 随机产生树木
void InitTree(int treeState_Matrix[treeNum][treeNum]) {
int i, j, count = 0;
float randVal; // 记录概率
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
randVal = rand() % (N + 1) / (float)(N + 1); // % 取余数用于获取小于N+1的数字,(float)强制转换为小数,解决整数除整数只取商的默认情况导致的数据为0问题。
if (randVal >= 0.40) { // 随机数大于0.40,就生成树木,格子里记录为1
treeState_Matrix[i][j] = 1; // [i][j]代表当前树木的行列
count++;
} else {
treeState_Matrix[i][j] = 0; // 随机数小,就没有树
}
}
}
}
// 画树,就是从数字数组到图片的查找过程
void drawTree(int treeState_Matrix[treeNum][treeNum]) {
int i, j;
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
if (treeState_Matrix[i][j] == 1) { // 没燃烧的树是 1
setfillcolor(RGB(0, 255, 0)); // 绿色
} else if (treeState_Matrix[i][j] == 2) { // 燃烧的树木记录为2
setfillcolor(RGB(255, 0, 0)); // 红色
} else {
setfillcolor(RGB(0, 0, 0)); // 记录为0就是没有树,用黑色填空
}
fillrectangle(j * treeSize, i * treeSize, j * treeSize + treeSize, i * treeSize + treeSize); // 数组 i 选择哪一行,就是控制选择高度 j 对应哪一列,就是对应宽 x坐标
}
}
}
// 随机一个燃烧位置,同随机产生树木
void InitRandTreePos() {
int i, j, count = 0;
float randVal;
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
if (treeState_Matrix[i][j] == 1) {
randVal = rand() % (N + 1) / (float)(N + 1);
if (randVal < burning_rate) {
treeState_Matrix[i][j] = 2; // 树木网格里第i行第j列记录为2,对应红色,着火
count++;
}
}
}
}
printf("着了%d 棵树\n", count);
}
// 检查随机数,随机小于就着火
int isRight(float state) {
float randVal = rand() % (N + 1) / (float)(N + 1);
if (randVal < state) {
return 1;
}
return 0;
}
// 根据旧网格存储新网格数据
void getNextTreeState(int treeState_Matrix[treeNum][treeNum]) {
int meshv2[treeNum][treeNum] = {0};
int i, j, state = 0;
for (i = 0; i < treeNum; i++) {
for (j = 0; j < treeNum; j++) {
state = treeState_Matrix[i][j];
if (state == 1) { // 检查上下左右
if (0 || (treeState_Matrix[i - 1][j - 0] == 2) ||
(treeState_Matrix[i - 0][j - 1] == 2) || (treeState_Matrix[i + 0][j + 1] == 2) ||
0 || (treeState_Matrix[i + 1][j + 0] == 2)
) {
meshv2[i][j] = 2; // 于是被其他着火的树木点着
}else{
meshv2[i][j]=1; // 否则不着火
}
} else if (state == 2) { // 现在着火,下次就空
meshv2[i][j] = 0;
} else if (state == 0) { // 是空的就看看会不会长树
if (isRight(proTree_rate)) { // 小于概率就长树木
meshv2[i][j] = 1;
}
if (isRight(unsafe_rate)) { // 新的树木再着火概率
meshv2[i][j] = 2;
}
}
}
}
memcpy(treeState_Matrix, meshv2, treeNum* treeNum* sizeof(int));
}
//主函数:程序入口
int main(int args, char* argv) {
initgraph(WIDTH, HEIGHT, SHOWCONSOLE); //初始化窗体:500*500
HWND hwnd = GetHWnd(); //获取窗体句柄
SetWindowText(hwnd, _T("林森火灾模拟")); //设置窗体标题
SetWindowPos(hwnd, NULL, 600, 200, HEIGHT, WIDTH, 0);//设置窗体位置
//1、初始化网格布局
InitBoard();
srand((int)time(NULL)); //随机数种子
//2、初始化数目状态矩阵
InitTree(treeState_Matrix);
//3、绘制树木
drawTree(treeState_Matrix);
//4、初始化燃烧树木的位置
InitRandTreePos();
//5、绘制森林-树木
drawTree(treeState_Matrix);
//开启批量绘图模式
BeginBatchDraw();
while (true) { //进入循环迭代
//6、获取下一时刻的树木状态矩阵
getNextTreeState(treeState_Matrix);
//7、仍然有可能产生新的火源
// burn_again(); // 被整合进getNextTreeState
//8、重新绘制森林-树木
drawTree(treeState_Matrix);
//显示绘图结果
FlushBatchDraw(); // 显示绘制
//停顿0.001s
Sleep(1);
// printf("ok\n");
}
EndBatchDraw(); //结束批量绘图模式
closegraph(); //关闭设备窗口
return 0;
}
getNextTreeState 微调参数
右边着火比左边快,往左跑
// 联系方式 b站 民用级脑的研发记录
// 细胞自动机森林火灾模型演示,
// 开发环境 小熊猫c++ 2.25.1
// 基于 //https://blog.csdn.net/weixin_43524214/article/details/105099165
// 使用 easyx 图形库
#include <stdio.h>
#include <stdlib.h>
#include <easyx/graphics.h>
#include <time.h>
#include <math.h>
// 定义树木状态,给数字贴上意义,用于快速理解代码
#define no_tree 0
#define burning 2
#define nor_tree 1
#define WIDTH 600 // 宽度
#define HEIGHT 600 // 高度
#define treeSize 4 // 树木大小
#define treeNum 150 // 树木个数
#define Row 150 // 150 行
#define Col 150 // 150 列
#define burning_rate 0.0001 // 初始燃烧概率0.01%
#define proTree_rate 0.02 // 空地长出新的树木的概率:10%
#define unsafe_rate 0.00005 // 新长出树木又失火的概率 0,0005%
#define N 10000
/*
演化规则如下:
(1)若某树木元胞的4个邻居中有燃烧着的,那么该元胞下一时刻的状态是燃烧;
(2)一个燃烧着的元胞,在下一时刻变成空位;
(3)所有树木元胞(状态为2)以一个低概率开始燃烧(模拟闪电造成森林火灾);
(4)所有空位元胞以一个低概率变为树木(模拟新树木的生长)。
*/
int treeState_Matrix[treeNum][treeNum]; // 树木格子,一个格子就是一棵树
struct POS {
int x;
int y;
};
// 画边界,画黑色线
void InitBoard() {
setlinecolor(RGB(0, 0, 0)); // 黑线
for (int i = 0; i < treeNum; i++) { // 每个格子横纵线
line(0, i * treeSize, WIDTH, i * treeSize);
line(i * treeSize, 0, i * treeSize, HEIGHT);
}
}
// 随机产生树木
void InitTree(int treeState_Matrix[treeNum][treeNum]) {
int i, j, count = 0;
float randVal; // 记录概率
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
randVal = rand() % (N + 1) / (float)(N + 1); // % 取余数用于获取小于N+1的数字,(float)强制转换为小数,解决整数除整数只取商的默认情况导致的数据为0问题。
if (randVal >= 0.40) { // 随机数大于0.40,就生成树木,格子里记录为1
treeState_Matrix[i][j] = 1; // [i][j]代表当前树木的行列
count++;
} else {
treeState_Matrix[i][j] = 0; // 随机数小,就没有树
}
}
}
}
// 画树,就是从数字数组到图片的查找过程
void drawTree(int treeState_Matrix[treeNum][treeNum]) {
int i, j;
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
if (treeState_Matrix[i][j] == 1) { // 没燃烧的树是 1
setfillcolor(RGB(0, 255, 0)); // 绿色
} else if (treeState_Matrix[i][j] == 2) { // 燃烧的树木记录为2
setfillcolor(RGB(255, 0, 0)); // 红色
} else {
setfillcolor(RGB(0, 0, 0)); // 记录为0就是没有树,用黑色填空
}
fillrectangle(j * treeSize, i * treeSize, j * treeSize + treeSize, i * treeSize + treeSize); // 数组 i 选择哪一行,就是控制选择高度 j 对应哪一列,就是对应宽 x坐标
}
}
}
// 随机一个燃烧位置,同随机产生树木
void InitRandTreePos() {
int i, j, count = 0;
float randVal;
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
if (treeState_Matrix[i][j] == 1) {
randVal = rand() % (N + 1) / (float)(N + 1);
if (randVal < burning_rate) {
treeState_Matrix[i][j] = 2; // 树木网格里第i行第j列记录为2,对应红色,着火
count++;
}
}
}
}
printf("着了%d 棵树\n", count);
}
// 检查随机数,随机小于就着火
int isRight(float state) {
float randVal = rand() % (N + 1) / (float)(N + 1);
if (randVal < state) {
return 1;
}
return 0;
}
//参数调整自 https://blog.51cto.com/u_15458165/4807865
// 根据旧网格存储新网格数据
void getNextTreeState(int treeState_Matrix[treeNum][treeNum]) {
int meshv2[treeNum][treeNum] = {0};
int i, j, state = 0;
for (i = 0; i < treeNum; i++) {
for (j = 0; j < treeNum; j++) {
state = treeState_Matrix[i][j];
if (state == 1) { // 检查上下左右
if (0 || (treeState_Matrix[i - 1][j - 0] == 2) ||
(treeState_Matrix[i - 0][j - 1] == 2) || (treeState_Matrix[i + 0][j + 1] == 2) ||(treeState_Matrix[i+0][j+2]==2)|| // 右边火势大,隔两个也能着火,于是往左跑的快
0 || (treeState_Matrix[i + 1][j + 0] == 2)
) {
meshv2[i][j] = 2; // 于是被其他着火的树木点着
}else{
meshv2[i][j]=1; // 否则不着火
}
} else if (state == 2) { // 现在着火,下次就空
meshv2[i][j] = 0;
} else if (state == 0) { // 是空的就看看会不会长树
if (isRight(proTree_rate)) { // 小于概率就长树木
meshv2[i][j] = 1;
}
if (isRight(unsafe_rate)) { // 新的树木再着火概率
meshv2[i][j] = 2;
}
}
}
}
memcpy(treeState_Matrix, meshv2, treeNum* treeNum* sizeof(int));
}
//主函数:程序入口
int main(int args, char* argv) {
initgraph(WIDTH, HEIGHT, SHOWCONSOLE); //初始化窗体:500*500
HWND hwnd = GetHWnd(); //获取窗体句柄
SetWindowText(hwnd, _T("林森火灾模拟")); //设置窗体标题
SetWindowPos(hwnd, NULL, 600, 200, HEIGHT, WIDTH, 0);//设置窗体位置
//1、初始化网格布局
InitBoard();
srand((int)time(NULL)); //随机数种子
//2、初始化数目状态矩阵
InitTree(treeState_Matrix);
//3、绘制树木
drawTree(treeState_Matrix);
//4、初始化燃烧树木的位置
InitRandTreePos();
//5、绘制森林-树木
drawTree(treeState_Matrix);
//开启批量绘图模式
BeginBatchDraw();
while (true) { //进入循环迭代
//6、获取下一时刻的树木状态矩阵
getNextTreeState(treeState_Matrix);
//7、仍然有可能产生新的火源
// burn_again(); // 被整合进getNextTreeState
//8、重新绘制森林-树木
drawTree(treeState_Matrix);
//显示绘图结果
FlushBatchDraw(); // 显示绘制
//停顿0.001s
Sleep(1);
// printf("ok\n");
}
EndBatchDraw(); //结束批量绘图模式
closegraph(); //关闭设备窗口
return 0;
}
getNextTreeState 微调参数
往下吹风
// 联系方式 b站 民用级脑的研发记录
// 细胞自动机森林火灾模型演示,
// 开发环境 小熊猫c++ 2.25.1
// 基于 //https://blog.csdn.net/weixin_43524214/article/details/105099165
// 使用 easyx 图形库
#include <stdio.h>
#include <stdlib.h>
#include <easyx/graphics.h>
#include <time.h>
#include <math.h>
// 定义树木状态,给数字贴上意义,用于快速理解代码
#define no_tree 0
#define burning 2
#define nor_tree 1
#define WIDTH 600 // 宽度
#define HEIGHT 600 // 高度
#define treeSize 4 // 树木大小
#define treeNum 150 // 树木个数
#define Row 150 // 150 行
#define Col 150 // 150 列
#define burning_rate 0.0001 // 初始燃烧概率0.01%
#define proTree_rate 0.02 // 空地长出新的树木的概率:10%
#define unsafe_rate 0.00005 // 新长出树木又失火的概率 0,0005%
#define N 10000
/*
演化规则如下:
(1)若某树木元胞的4个邻居中有燃烧着的,那么该元胞下一时刻的状态是燃烧;
(2)一个燃烧着的元胞,在下一时刻变成空位;
(3)所有树木元胞(状态为2)以一个低概率开始燃烧(模拟闪电造成森林火灾);
(4)所有空位元胞以一个低概率变为树木(模拟新树木的生长)。
*/
int treeState_Matrix[treeNum][treeNum]; // 树木格子,一个格子就是一棵树
struct POS {
int x;
int y;
};
// 画边界,画黑色线
void InitBoard() {
setlinecolor(RGB(0, 0, 0)); // 黑线
for (int i = 0; i < treeNum; i++) { // 每个格子横纵线
line(0, i * treeSize, WIDTH, i * treeSize);
line(i * treeSize, 0, i * treeSize, HEIGHT);
}
}
// 随机产生树木
void InitTree(int treeState_Matrix[treeNum][treeNum]) {
int i, j, count = 0;
float randVal; // 记录概率
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
randVal = rand() % (N + 1) / (float)(N + 1); // % 取余数用于获取小于N+1的数字,(float)强制转换为小数,解决整数除整数只取商的默认情况导致的数据为0问题。
if (randVal >= 0.40) { // 随机数大于0.40,就生成树木,格子里记录为1
treeState_Matrix[i][j] = 1; // [i][j]代表当前树木的行列
count++;
} else {
treeState_Matrix[i][j] = 0; // 随机数小,就没有树
}
}
}
}
// 画树,就是从数字数组到图片的查找过程
void drawTree(int treeState_Matrix[treeNum][treeNum]) {
int i, j;
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
if (treeState_Matrix[i][j] == 1) { // 没燃烧的树是 1
setfillcolor(RGB(0, 255, 0)); // 绿色
} else if (treeState_Matrix[i][j] == 2) { // 燃烧的树木记录为2
setfillcolor(RGB(255, 0, 0)); // 红色
} else {
setfillcolor(RGB(0, 0, 0)); // 记录为0就是没有树,用黑色填空
}
fillrectangle(j * treeSize, i * treeSize, j * treeSize + treeSize, i * treeSize + treeSize); // 数组 i 选择哪一行,就是控制选择高度 j 对应哪一列,就是对应宽 x坐标
}
}
}
// 随机一个燃烧位置,同随机产生树木
void InitRandTreePos() {
int i, j, count = 0;
float randVal;
for (i = 0; i < Row; i++) {
for (j = 0; j < Col; j++) {
if (treeState_Matrix[i][j] == 1) {
randVal = rand() % (N + 1) / (float)(N + 1);
if (randVal < burning_rate) {
treeState_Matrix[i][j] = 2; // 树木网格里第i行第j列记录为2,对应红色,着火
count++;
}
}
}
}
printf("着了%d 棵树\n", count);
}
// 检查随机数,随机小于就着火
int isRight(float state) {
float randVal = rand() % (N + 1) / (float)(N + 1);
if (randVal < state) {
return 1;
}
return 0;
}
// 根据旧网格存储新网格数据
void getNextTreeState(int treeState_Matrix[treeNum][treeNum]) {
int meshv2[treeNum][treeNum] = {0};
int i, j, state = 0;
for (i = 0; i < treeNum; i++) {
for (j = 0; j < treeNum; j++) {
state = treeState_Matrix[i][j];
if (state == 1) { // 检查上下左右
if (0 || (treeState_Matrix[i - 1][j - 0] == 2) ||
(treeState_Matrix[i - 0][j - 1] == 2) || (treeState_Matrix[i + 0][j + 1] == 2) ||0 // 模拟往下吹风情况,因为上面的着火树,下面着火,下面着火,上面不一定着火,吹不上去,风是往下吹
// 0 || (treeState_Matrix[i + 1][j + 0] == 2)
) {
meshv2[i][j] = 2; // 于是被其他着火的树木点着
}else{
meshv2[i][j]=1; // 否则不着火
}
} else if (state == 2) { // 现在着火,下次就空
meshv2[i][j] = 0;
} else if (state == 0) { // 是空的就看看会不会长树
if (isRight(proTree_rate)) { // 小于概率就长树木
meshv2[i][j] = 1;
}
if (isRight(unsafe_rate)) { // 新的树木再着火概率
meshv2[i][j] = 2;
}
}
}
}
memcpy(treeState_Matrix, meshv2, treeNum* treeNum* sizeof(int));
}
//主函数:程序入口
int main(int args, char* argv) {
initgraph(WIDTH, HEIGHT, SHOWCONSOLE); //初始化窗体:500*500
HWND hwnd = GetHWnd(); //获取窗体句柄
SetWindowText(hwnd, _T("林森火灾模拟")); //设置窗体标题
SetWindowPos(hwnd, NULL, 600, 200, HEIGHT, WIDTH, 0);//设置窗体位置
//1、初始化网格布局
InitBoard();
srand((int)time(NULL)); //随机数种子
//2、初始化数目状态矩阵
InitTree(treeState_Matrix);
//3、绘制树木
drawTree(treeState_Matrix);
//4、初始化燃烧树木的位置
InitRandTreePos();
//5、绘制森林-树木
drawTree(treeState_Matrix);
//开启批量绘图模式
BeginBatchDraw();
while (true) { //进入循环迭代
//6、获取下一时刻的树木状态矩阵
getNextTreeState(treeState_Matrix);
//7、仍然有可能产生新的火源
// burn_again(); // 被整合进getNextTreeState
//8、重新绘制森林-树木
drawTree(treeState_Matrix);
//显示绘图结果
FlushBatchDraw(); // 显示绘制
//停顿0.001s
Sleep(1);
// printf("ok\n");
}
EndBatchDraw(); //结束批量绘图模式
closegraph(); //关闭设备窗口
return 0;
}