生命游戏没有游戏玩家各方之间的竞争,也谈不上输赢,可以把它归类为仿真游戏。事实上,也是因为它模拟和显示的图像看起来颇似生命的出生和繁衍过程而得名为“生命游戏”。在游戏进行中,杂乱无序的细胞会逐渐演化出各种精致、有形的结构;这些结构往往有很好的对称性,而且每一代都在变化形状。一些形状一经锁定就不会逐代变化。有时,一些已经成形的结构会因为一些无序细胞的“入侵”而被破坏。但是形状和秩序经常能从杂乱中产生出来。
每个方格中都可放置一个生命细胞,每个生命细胞只有两种状态:
“生”或“死”。用黑色方格表示该细胞为“生”,空格(白色)表示该细胞为“死”。或者说方格网中黑色部分表示某个时候某种“生命”的分布图。生命游戏想要模拟的是:随着时间的流逝,这个分布图将如何一代一代地变化
游戏规则只有四条:
1 当周围仅有1个或没有存活细胞时, 原来的存活细胞进入死亡状态。(模拟生命数量稀少)
2 当周围有2个或3个存活细胞时, 网格保持原样。
3 当周围有4个及以上存活细胞时,原来的存活细胞亦进入死亡状态。(模拟生命数量过多)
4 当周围有3个存活细胞时,空白网格变成存活细胞。(模拟繁殖)
先看实现效果
实现逻辑
整个游戏都是一个二维数组,实现动画只需要在一个while循环内不断给这个数组赋值,清屏,重新显示
直接看代码
引入头文件,定义公共变量
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
char i = 0, j = 0,k,h,w;
首先我们需要初始化一个二维数组当地图,遍历数组并随机生成生命,这里每个格子生成生命的概率为50%
//地图宽高
#define MAP_HEIGHT 20
#define MAP_WIDTH 40
static char Map[MAP_HEIGHT][MAP_WIDTH];
//初始化
void Init(){
//随机种子
srand((unsigned int)time(NULL));
for (i = 0; i < MAP_HEIGHT; i++)
for (j = 0; j < MAP_WIDTH; j++)
Map[i][j]=rand()%2?'*':' '; //随机生成' '或者'*',概率各一半
}
从游戏规则中不难看出,生命的存亡完全取决于周围八个格子有多少生命存在,于是我们需要另一个二维数组来存放每个格子周围存活生命的数量
//邻居表
static unsigned char neighborList[MAP_HEIGHT][MAP_WIDTH];
//八个邻居相对于自身的坐标差异
static char neighborLocation[8][2]={
{-1,-1},{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0}
};
//设置邻居表
void setNeighborList(){
for (i = 0; i < MAP_HEIGHT;i++)
{
for (j = 0; j < MAP_WIDTH; j++)
{
neighborList[i][j]=0;
//遍历周围格子
for(int k=0;k<8;k++){
h=i+neighborLocation[k][1];
w=j+neighborLocation[k][0];
//如果周围格子存在生命,后面是防止越界的判断
if(Map[h][w]=='*'&&h>-1&&w>-1&&h<MAP_HEIGHT&&w<MAP_WIDTH)
neighborList[i][j]++;
}
}
}
}
自身和周围坐标的差异如下图,可用一个数组存放,方便遍历周围坐标
有了邻居表,就能判断每个格子在这个回合的存亡
//演化
void evolution(){
for (i = 0; i < MAP_HEIGHT;i++)
{
for (j = 0; j < MAP_WIDTH; j++)
{
if(Map[i][j]=='*')
{
if(neighborList[i][j]<2||neighborList[i][j]>3)Map[i][j]=' ';
}
else{
if(neighborList[i][j]==3)Map[i][j]='*';
}
}
}
}
再写一个简单的显示函数,再用主循环把以上串起来,整个游戏就做完了
//打印整个地图
void show(){
for (i = 0; i < MAP_HEIGHT;i++)
{
for (j = 0; j < MAP_WIDTH; j++) printf("%c",Map[i][j]);
printf("\n");
}
}
//游戏主循环(按q退出,长按可持续演化)
void game()
{
do
{
system("cls");
setNeighborList();
evolution();
show();
Sleep(50);
} while (getch() != 'q');
}
//main函数
int main(){
Init();
game();
return 0;
}
经过观察发现,几乎每个函数都需要使用双层for循环对二维数组进行遍历,结构如下
{
//语句
for (i = 0; i < MAP_HEIGHT; i++){
for (j = 0; j < MAP_WIDTH; j++){
//语句
}
//语句
}
}
于是我们可以把整个结构抽象成一个宏定义的函数,只要传入语句,就能按需执行相应的代码块(这里的TRA 取自遍历的英文 traversal)
#define TRA(insize, outsize) ({ \
for (i = 0; i < MAP_HEIGHT; i++) \
{ \
for (j = 0; j < MAP_WIDTH; j++) \
{ \
insize; \
} \
outsize; \
} \
})
于是前面的函数可以简写为
#define TRA(insize, outsize) ({ \
for (i = 0; i < MAP_HEIGHT; i++) \
{ \
for (j = 0; j < MAP_WIDTH; j++) \
{ \
insize; \
} \
outsize; \
} \
})
void Init()
{
srand((unsigned int)time(NULL));
TRA(Map[i][j] = rand() % 2 ? '*' : ' ', NULL);
}
void show()
{
TRA(printf("%c", Map[i][j]), printf("\n"));
}
void setNeighborList()
{
TRA(
{
neighborList[i][j] = 0;
for (int k = 0; k < 8; k++)
{
h = i + neighborLocation[k][1];
w = j + neighborLocation[k][0];
if (Map[h][w] == '*' && h > -1 && w > -1 && h < MAP_HEIGHT && w < MAP_WIDTH)
neighborList[i][j]++;
}
},
NULL);
}
void evolution()
{
TRA(
{
if (Map[i][j] == '*')
{
if (neighborList[i][j] < 2 || neighborList[i][j] > 3)
Map[i][j] = ' ';
}
else
{
if (neighborList[i][j] == 3)
Map[i][j] = '*';
}
},
NULL);
}
当然这是个只有两层的for循环,现在看来也节约不了太多代码量,但是如果以后遇到更复杂的结构需要复用,就可以用这个小技巧节省代码量
最后附上完整代码
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
#define MAP_HEIGHT 20
#define MAP_WIDTH 40
static char neighborList[MAP_HEIGHT][MAP_WIDTH];
static char Map[MAP_HEIGHT][MAP_WIDTH];
static char neighborLocation[8][2] = {
{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}};
char i, j, k, h, w;
#define TRA(insize, outsize) ({ \
for (i = 0; i < MAP_HEIGHT; i++) \
{ \
for (j = 0; j < MAP_WIDTH; j++) \
{ \
insize; \
} \
outsize; \
} \
})
void Init()
{
srand((unsigned int)time(NULL));
TRA(Map[i][j] = rand() % 2 ? '*' : ' ', NULL);
}
void show()
{
TRA(printf("%c", Map[i][j]), printf("\n"));
}
void setNeighborList()
{
TRA(
{
neighborList[i][j] = 0;
for (int k = 0; k < 8; k++)
{
h = i + neighborLocation[k][1];
w = j + neighborLocation[k][0];
if (Map[h][w] == '*' && h > -1 && w > -1 && h < MAP_HEIGHT && w < MAP_WIDTH)
neighborList[i][j]++;
}
},
NULL);
}
void evolution()
{
TRA(
{
if (Map[i][j] == '*')
{
if (neighborList[i][j] < 2 || neighborList[i][j] > 3)
Map[i][j] = ' ';
}
else
{
if (neighborList[i][j] == 3)
Map[i][j] = '*';
}
},
NULL);
}
void game()
{
do
{
system("cls");
setNeighborList();
evolution();
show();
Sleep(50);
} while (getch() != 'q');
}
int main()
{
Init();
game();
return 0;
}