生命游戏简易版
今天照着书学习了个新的游戏,生命游戏。似乎这个游戏模拟的是细胞种群的繁衍,很有意思。程序员和生物学爱好者双厨狂喜~~
贴上实现基础功能的代码片:实现的就是一下子满屏被“*”组成的细胞填满。
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<Windows.h>
#include<time.h>
#define Height 25
#define Width 50
int cells[Height][Width];
void gotoxy(int x, int y)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}
void StartUp()
{
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)//随机初始化为1或0
{
cells[i][j] = rand() % 2;
}
}
}
void Show()
{
gotoxy(0, 0);
for (int i = 0; i <= Height; i++)
{
for (int j = 0; j <= Width; j++)
{
if (cells[i][j] == 1)//输出活细胞
{
printf("*");
}
else//输出空格
{
printf(" ");
}
}
printf("\n");
}
Sleep(500);
}
void UpdateWithoutInput()
{
int NewCells[Height][Width] = { 0 };
int NeighbourNumber = 0;//记得初始化为0
for (int i = 1; i < Height - 1; i++)
{
for (int j = 1; j < Width - 1; j++)
{
NeighbourNumber = cells[i - 1][j - 1] + cells[i - 1][j] + cells[i - 1][j + 1] + cells[i][j - 1] + cells[i][j + 1]
+ cells[i + 1][j - 1] + cells[i + 1][j] + cells[i + 1][j + 1];//看细胞周围存活的"邻居"数量
if (NeighbourNumber == 3)//为什么不写大于等于三,我觉得可能是种群密度过高也不利于细胞存活,养分什么的可能不够吧
{
NewCells[i][j] = 1;//周围有三个存活的细胞,该细胞存活
}
else if (NeighbourNumber == 2)
{
NewCells[i][j] = cells[i][j];//维持原状态不变
}
else
{
NewCells[i][j] = 0;//死亡
}
}
}
for (int i = 1; i <= Height - 1; i++)
{
for (int j = 1; j <= Width - 1; j++)
{
cells[i][j] = NewCells[i][j];//更新下一帧的细胞存亡数据
}
}
}
void UpdateWithInput()
{
}
int main()
{
StartUp();
while (1)
{
Show();
UpdateWithInput();
UpdateWithoutInput();
}
return 0;
}
查资料时发现了知乎一位大佬的讲解很清晰,对于理解这个游戏的设定规则很有帮助:这也解释了我一开始犯的错误,把条件改成了“邻居大于等于3个就存活,后来一运行就发现结果很不理想,细胞把整个屏幕占满了,没有体现出种群的更迭来,因为结合实际考虑的话,种群数量过多也会抑制种群的发展。(似乎还有什么负反馈调节机制??高中生物学的记不得了············)
如果一个生命,其周围的同类生命太少,会因为得不到帮助而死亡;如果太多,则会因为得不到足够的生命资源而死亡。
——英国数学家约翰·康威
原帖:https://www.zhihu.com/question/30782166
图源百度,S形曲线就是种群生长受到环境阻力干扰的结果。
这里还有关于生命游戏的一些其他资源:
https://www.cnblogs.com/lfri/p/9733883.html
【生命游戏----简单规则创造复杂生命(】https://mr.baidu.com/r/ksxBNDHEru?f=cp&u=fb26ab163992ebda
3.1.2课后题全部实现的版本,使用了枚举常量来表示死细胞,活细胞,水源,食物和天敌:
typedef enum Status//枚举常量,这里用一个数字代表一种状态
{
dead = 0,//死亡
live = 1,//存货
water = 2,//水源
food = 3,//细胞的食物
enemy=4//细胞的天敌
};
但是运行出来不知道为什么,几乎所有细胞无一生还······倒是这些除去细胞之后的元素生长得极其嚣张野蛮·····我感觉可能是跟我的数值设置有关,毕竟都是靠感觉来的,没有科学依据,所以造成了结果变成了瘟疫公司,细胞团灭···········
我的运行效果:
阿巴阿巴阿巴阿巴阿巴阿巴······
实现按下R之后刷新游戏(这个R必须是开了大写锁定的R,没错,R和r不一样,区分大小写,不讲武德!!!)
为了方便操作,程序里我改成了r
细胞灭亡的全代码如下:
资源稍后也会上传,需要的可主页自取
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<Windows.h>
#include<time.h>
#define Height 25
#define Width 50
int sleeptime = 150;//默认Show()函数中刷新间隔为150s
typedef enum Status//枚举常量,这里用一个数字代表一种状态
{
dead = 0,//死亡
live = 1,//存货
water = 2,//水源
food = 3,//细胞的食物
enemy=4//细胞的天敌
};
int cells[Height][Width];
void gotoxy(int x, int y)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}
void StartUp()
{
gotoxy(0, 0);
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)//随机初始化为1或0
{
cells[i][j] = rand() % 10;//随机生成0-9的数字来对应枚举常量死细胞、活细胞、水源、食物、敌人
//数字1,7,8,9都输出活细胞,这样让生成细胞的概率较大以免细胞过少导致游戏无法继续
}
}
}
void Show()
{
gotoxy(0, 0);
for (int i = 0; i <= Height; i++)
{
for (int j = 0; j <= Width; j++)
{
if (cells[i][j] == 1|| (cells[i][j] >= 7 && cells[i][j] <=9))
//数字1,7,8,9都输出活细胞,死细胞不输出(感觉这个@比较像细胞的形状)
{
printf("@");
}
else if (cells[i][j] == 2)//输出水源
{
printf("~");
}
else if (cells[i][j] == 3)//输出食物(感觉这个*图标长得更像食物所以就换了这个作为食物)
{
printf("*");
}
else if (cells[i][j] == 4)//输出敌人(感觉这个&长得像噬菌体所以用了这个)
{
printf("&");
}
else//5,6输出空格
{
printf(" ");
}
}
printf("\n");
}
Sleep(sleeptime);
}
void UpdateWithoutInput()
{
int NewCells[Height][Width] = { 0 };
int NeighbourNumber = 0;//记得初始化为0
for (int i = 1; i < Height - 1; i++)
{
for (int j = 1; j < Width - 1; j++)
{
NeighbourNumber = 0;//每个格子把邻居数量初始化为0
if (cells[i][j] == 1 || (cells[i][j] >= 7 && cells[i][j] <= 9))//必须得是这个格子是活细胞的情况下才会判断后面的死活问题
{
for (int k = i - 1; k <= i + 1; k++)//看细胞周围8个格子存活的"邻居"数量、水源、敌人、食物情况
{
for (int l = j - 1; l <= j + 1; l++)
{
if (k != i && l != j)//不能算cells【i】【j】这个细胞自己
{
if (cells[k][l] == 1 || (cells[k][l] >= 7 && cells[k][l] <= 9))//符合是活细胞
{
NeighbourNumber++;
}
else if (cells[k][l] == 2)//该处是水源,细胞更容易存活
{
NeighbourNumber += 2;//因为有水和食物的地方更利于细胞种群的繁衍,所以这里种群数量(邻居)可能更多
}
else if (cells[k][l] == 3)//该处是食物
{
NeighbourNumber += 1;
}
else if (cells[k][l] == 4)//该处是敌人
{
NeighbourNumber -= 2;//敌人不利于细胞繁衍,邻居个数会少
}
}
}
}
if (NeighbourNumber == 3)//为什么不写大于等于三,我觉得可能是种群密度过高也不利于细胞存活,养分什么的可能不够吧
{
NewCells[i][j] = 1;//周围有三个存活的细胞,该细胞存活
}
else if (NeighbourNumber == 2)
{
;//该位置是细胞,就维持原状,什么也不做
}
else
{
NewCells[i][j] = 0;//死亡
}
}
else if(cells[i][j]>=2&& cells[i][j]<=6)//不是细胞,就重新随机生成水源。食物,空格或者敌人
{
NewCells[i][j] = rand() % 5+2;//随机生成0-4,+2就是2-6,即随机生成的是水源,食物和天敌、空格的一种
}
}
}
for (int i = 1; i <= Height - 1; i++)
{
for (int j = 1; j <= Width - 1; j++)
{
cells[i][j] = NewCells[i][j];//更新下一帧的细胞存亡数据
}
}
}
void UpdateWithInput()
{
char input;
if (_kbhit())
{
input = _getch();
if (input == 27)//ESC暂停
{
system("pause");
}
else if (input == 'r')//restart重新开始,注意r是区分大小写的,这里要大写R的话必须开大写锁定!!!坑爹!!!
{
system("cls");//清屏
StartUp();
Show();
Sleep(sleeptime);//增加一个Sleep缓冲,视觉体验更加,不至于很快一闪而过
//goto RestartLable;//不能跳转到main函数中的StartUp,因为goto和label只能在一个函数中使用
//goto语句可以使程序在没有任何条件的情况下跳转到指定的位置,所以goto语句又被称为是无条件跳转语句。
}
else if (input == '+')//加速游戏
{
sleeptime -= 50;//刷新时间间隔减少50秒
}
else if (input == '-')//减速游戏
{
sleeptime += 50;
}
}
}
int main()
{
StartUp();//初始化
while (1)
{
Show();
UpdateWithInput();
UpdateWithoutInput();
}
return 0;
}
“康威因与COVID-19相关的并发症去世,享年82岁。
生命游戏之父、数学家约翰·康威因新冠肺炎去世”
天哪太可惜了,没想到发明这个生命游戏的伟人竟然是和我们同时期的人物………跟我以前脑海里“课本上的伟人都是不在世的”的固有观念截然不同,这让我更加感受到世事无常和遗憾。
“此次疫情虽然夺去了生命游戏之父的生命,但是生命游戏将永远继续下去,我们将永远缅怀他。”