The Game Of Life – 数据结构与算法的敲门砖
The Game Of Life(生命游戏,又称为细胞自动机)几乎是所有数据结构与算法导论教程前言的一个很经典的程序了。这是一个零玩家游戏,发生在一个平面网格里。每个格子的细胞都有死亡和存活两种状态,在代与代之间有两种状态,如果每一个细胞周围少于或等于1个细胞或多于4个细胞时,他会在下一代死亡;如果一个格子周围恰好有3个细胞,他将会重新活过来。
例如,当一种特别的状态被初始化后,会形成下列状态。

也会有循环或者稳定的状态:


更有甚者发现的「滑行者枪」,可以源源不断地向外发射滑行者。

接下来是一个用C++和控制台实现生命游戏的尝试。本可以很短小的,但上周闲下来的时候东增西补地加了许多东西,也算是当作初学面向对象的一个习题了吧,实现了一个可以自动运行,随机初始化和自定义规则的黑框框生命游戏。
运行效果
Source Code:
#include "pch.h"
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <windows.h>
constexpr int FIRST_INTRODUCTION = ;
constexpr int MANIPUNATE_INSTRUCTION = ;
constexpr int WITH_GENERATION_INFO = ;
constexpr int NO_GENERATION_INFO = ;
//Define rules;
int BOTTOM_LIMIT = ;
int UPPER_LIMIT = ;
int REBIRTH_NUM = ;
//define characters to print
const char LIVEC = '#';
const char DIEC = '.';
const int MAXL = ;
using namespace std;
class LifeMap {
public:
int width;
int height;
int matrix[MAXL][MAXL] = { };
int surround[MAXL][MAXL] = { };
int generation = ;
void instructor(int type) {
switch (type) {
case FIRST_INTRODUCTION:
cout << "Welcome to the Game of Life!" << endl << endl <<
"生命游戏是一个零玩家游戏,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相反,如果周围活细胞过少,这个细胞会因太孤单而死去。"
<< endl << "实际中,你可以设定周围活细胞的数目怎样时才适宜该细胞的生存。如果这个数目设定过低,世界中的大部分细胞会因为找不到太多的活的邻居而死去,直到整个世界都没有生命;如果这个数目设定过高,世界中又会被生命充满而没有什么变化。所以我们把这个数目选取为2或者3;这样整个生命世界才不至于太过荒凉或拥挤,而是一种动态的平衡。"
<< endl << "这样的话,游戏的规则就是:当一个方格周围有2或3个活细胞时,方格中的活细胞在下一个时刻继续存活;即使这个时刻方格中没有活细胞,在下一个时刻也会“诞生”活细胞。在这个游戏中,还可以设定一些更加复杂的规则,例如当前方格的状况不仅由父一代决定,而且还考虑祖父一代的情况。"
<< endl << "你作为这个世界的上帝,随意设定某个方格细胞的死活,以观察对世界的影响。在游戏的进行中,杂乱无序的细胞会逐渐演化出各种精致、有形的结构;这些结构往往有很好的对称性,而且每一代都在变化形状。一些形状已经锁定,不会逐代变化。有时,一些已经成形的结构会因为一些无序细胞的“入侵”而被破坏。但是形状和秩序经常能从杂乱中产生出来。"
<< endl << endl << " Please insert the width AND the height of the grid:" << endl << "(Max Length is " << MAXL - << ")" << endl;
break;
case MANIPUNATE_INSTRUCTION:
cout << endl << "Now you can insert \"L X Y\" (eg. L 2 3 or l 2 3 to set the cell located in the 2nd row, 3rd column) to set a cell to live,"
<< endl << "insert \"D X Y\" to set a cell to die. insert \"P\" to print the current status."
<< endl << "\"N\" for simply jump tp the next generation.\nN (optional)GENERATIONS (optional)INTERVAL_TIME for evolve multiple generation at a time."
<< endl << "Insert \"R\" to reset your gird and \"Q\" to quit." << endl
<< "Inset X to define rules."
<< "Most interstingly, you can radomlize every cell by insert \"!\"" << endl << "You can set multiple cells' status in one line." << endl;
break;
} }
void initialize(int W, int H) {
int i, j;
for (i = ; i < MAXL; i++) {
for (j = ; j < MAXL; j++) {
matrix[i][j] = ;
surround[i][j] = ;
}
}
width = W;
height = H;
generation = ;
}
void printer(int gen) {
//gen = 1 for printing with generation info.
if (gen == ) {
cout << "\n Generation No." << generation << ":" << endl;
}
cout << endl;
int i, j;
cout << " ";
for (i = ; i < width; i++) {
printf("%2d ", i + );
}
cout << endl;
for (i = ; i < height; i++) {
printf("%2d ", i + );
for (j = ; j < width; j++) {
if (matrix[i + ][j + ] == ) printf("%c ", DIEC);
else if (matrix[i + ][j + ] == ) printf("%c ", LIVEC);
}
cout << endl;
}
cout << endl;
//print ratio
if (gen == ) {
int liveCells = ;
for (i = ; i < height; i++) {
for (j = ; j < width; j++) {
if (matrix[i + ][j + ] == ) liveCells++;
}
}
cout << " Live cell ratio: " << (double)liveCells / (double)(width*height) * 100.0
<< "% (" << liveCells << "/" << width * height << ")" << endl << endl;
}
}
void SetLiving(int W, int H) {
if (W <= || W > width || H <= || H > height)
cout << "Unvalid input, please try again.\n";
else {
matrix[W][H] = ;
cout << "Cell (" << W << " ," << H << ") came alive." << endl;
} }
void SetDying(int W, int H) {
if (W <= || W > width || H <= || H > height)
cout << "Unvalid input, please try again.\n";
else {
matrix[W][H] = ;
cout << "Cell (" << W << " ," << H << ") has been dead." << endl;
} }
void Radomlize() {
int i, j;
cout << "Randomlizing cell status..." << endl;
for (i = ; i < height; i++) {
for (j = ; j < width; j++) {
//srand(time(NULL));
if ((rand() % ) == ) matrix[i + ][j + ] = ;
else {
matrix[i + ][j + ] = ;
cout << "Cell (" << i + << " ," << j + << ") came alive." << endl;
}
}
}
}
void Next() {
int i, j;
//Initialize the surround matrix.
for (i = ; i < MAXL; i++) {
for (j = ; j < MAXL; j++) {
surround[i][j] = ;
}
}
//Calculate the surrounded live cells
for (i = ; i < height; i++) {
for (j = ; j < width; j++) {
if (matrix[i][j] == ) surround[i + ][j + ]++;
if (matrix[i][j + ] == ) surround[i + ][j + ]++;
if (matrix[i][j + ] == ) surround[i + ][j + ]++;
if (matrix[i + ][j] == ) surround[i + ][j + ]++;
if (matrix[i + ][j + ] == ) surround[i + ][j + ]++;
if (matrix[i + ][j] == ) surround[i + ][j + ]++;
if (matrix[i + ][j + ] == ) surround[i + ][j + ]++;
if (matrix[i + ][j + ] == ) surround[i + ][j + ]++;
}
}
//Rules
for (i = ; i < height; i++) {
for (j = ; j < width; j++) {
if (surround[i + ][j + ] >= UPPER_LIMIT) matrix[i + ][j + ] = ;
if (surround[i + ][j + ] <= BOTTOM_LIMIT) matrix[i + ][j + ] = ;
if (surround[i + ][j + ] == REBIRTH_NUM) matrix[i + ][j + ] = ;
}
}
generation++;
}
void Definer() {
int i, j, k;
cout << "Please insert three value to define the rules: (Defaul Configuration:1 4 3)" << endl
<< "BOTTOM_LIMIT UPPER_LIMIT REBIRTH_NUM" << endl;
inputrule: cout << ">> "; cin >> i >> j >> k;
if (cin.fail() || i >= || j >= || k >= || i < || j < || k < ) {
cout << "Invalid input, please input three positive integers smaller than 8." << endl;
cin.clear();
while (cin.get() != '\n');
goto inputrule;
}
else {
BOTTOM_LIMIT = i;
UPPER_LIMIT = j;
REBIRTH_NUM = k;
printf("Rules have been defined to %d %d %d.\n", i, j, k);
}
}
};
int main()
{
LifeMap Map;
//These variables are used for manipunating.
int X, Y;
//these two labels are used for jumping for input that's too large and the reset command.
start: Map.instructor(FIRST_INTRODUCTION);
input: cin >> X >> Y;
while (cin.fail()) {
cout << "Unvalid input, please try again. Pleas type two integers." << endl;
cin.clear();
while (cin.get() != '\n') continue;
cin >> X >> Y;
}
while (X >= || Y >= ) {
cout << "Too long." << endl;
cin.clear();
goto input;
}
Map.instructor(MANIPUNATE_INSTRUCTION);
cout << "This is all the cells you have." << endl;
Map.initialize(X, Y);
Map.printer(NO_GENERATION_INFO);
cin.clear();
while (cin.get() != '\n') continue; // Clear datas in the cache.
char command;
while ((command = toupper(cin.get())) != 'Q') {
switch (command) {
case 'L':
cin >> X >> Y;
if (cin.fail()) {
cout << "Unvalid input, please try again." << endl;
cin.clear();
while (cin.get() != '\n') continue;
}
else
Map.SetLiving(X, Y);
break;
case 'D':
cin >> X >> Y;
if (cin.fail()) {
cout << "Unvalid input, please try again." << endl;
cin.clear();
while (cin.get() != '\n') continue;
}
else
Map.SetDying(X, Y);
break;
case 'P':
Map.printer(WITH_GENERATION_INFO);
break;
case 'N':
//gens mens the number of generations to evolve
//interval means the interval times between two evolutions
int i, gens, interval;
if (cin.get() != '\n') {
cin >> gens;
if (cin.get() == '\n') interval = ;
else cin >> interval;
if (gens <= || interval < ) {
cout << "Unvalid input, please enter positive integers." << endl;
cin.clear();
while (cin.get() != '\n') continue;
}
else {
for (i = ; i < gens; i++) {
system("cls");
Map.instructor(MANIPUNATE_INSTRUCTION);
Map.Next();
Map.printer(WITH_GENERATION_INFO);
Sleep(interval);
}
}
}
else {
system("cls");
Map.instructor(MANIPUNATE_INSTRUCTION);
Map.Next();
Map.printer(WITH_GENERATION_INFO);
}
break;
case 'R':
system("cls");
goto start;
break;
case '!':
Map.Radomlize();
cout << "OK, now it's just chaos." << endl;
Map.printer(WITH_GENERATION_INFO);
break;
case 'X':
Map.Definer();
case '\n': break;
case ' ': break;
default:
cout << "Unknown Command:(" << endl;
//while (cin.get() != '\n') continue;
}
}
cout << "See you:)\n\n";
system("pause");
return ;
}
GitHub:https://github.com/BillChen2000/LearnigRepo/blob/master/Course/数据结构与算法/LifeGame/
图源:https://www.guokr.com/article/439770/
另外这里还有一个LifeWiki有详细的整理和介绍:http://www.conwaylife.com/wiki/
来源:https://billc.io/2019/02/the-game-of-life/
The Game Of Life – 数据结构与算法的敲门砖的更多相关文章
- 开启基本数据结构和算法之路--初识Graphviz
在我的Linux刀耕开荒阶段,就想开始重拾C,利用C实现常用的基本数据结构和算法,而数据结构和算法的掌握的熟练程度正是程序的初学者与职业程序员的分水岭. 那么怎么开启这一段历程呢? 按照软件工程的思想 ...
- 【转】MySQL索引背后的数据结构及算法原理
摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...
- [转]MySQL索引背后的数据结构及算法原理
摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...
- MySQL索引背后的数据结构及算法原理【转】
本文来自:张洋的MySQL索引背后的数据结构及算法原理 摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持 ...
- 数据结构与算法JavaScript (一) 栈
序 数据结构与算法JavaScript这本书算是讲解得比较浅显的,优点就是用javascript语言把常用的数据结构给描述了下,书中很多例子来源于常见的一些面试题目,算是与时俱进,业余看了下就顺便记录 ...
- 数据结构与算法 Big O 备忘录与现实
不论今天的计算机技术变化,新技术的出现,所有都是来自数据结构与算法基础.我们需要温故而知新. 算法.架构.策略.机器学习之间的关系.在过往和技术人员交流时,很多人对算法和架构之间的关系感 ...
- 《java数据结构和算法》读书笔记
大学时并不是读计算机专业的, 之前并没有看过数据结构和算法,这是我第一次看. 从数据结构方面来说: 数组:最简单,遍历.查找很快:但是大小固定,不利于扩展 ...
- MySQL索引背后的数据结构及算法原理
摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...
- javascript数据结构与算法--高级排序算法
javascript数据结构与算法--高级排序算法 高级排序算法是处理大型数据集的最高效排序算法,它是处理的数据集可以达到上百万个元素,而不仅仅是几百个或者几千个.现在我们来学习下2种高级排序算法-- ...
随机推荐
- [Python] Advanced features
Slicing 12345 L[:10:2] # [0, 2, 4, 6, 8]L[::5] # 所有数,每5个取一个# [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, ...
- open()操作文件
open()函数用来读取.写文件 参数解释: r:只读 w:只写,此时进行读,会报错 a:只追加 r+:可读可写 w+:可读可写 a+:可读可写 rb\rb+\wb\wb+\ab\ab+ 针对二进制文 ...
- 无人工地,原来是靠AI这样运行的
随着全世界逐渐进入老龄化社会,适龄工作人口将急剧减少,必然导致用工成本增加,施工方降低人工成本.提升施工效率和质量的需求会越来越强烈,数字化施工技术应用前景广阔.在过去的十年中,无人机迎来了自己发展的 ...
- C\C++ 位域操作
几篇较全面的位域相关的文章: http://www.uplook.cn/blog/9/93362/ C/C++位域(Bit-fields)之我见 C中的位域与大小端问题 内存对齐全攻略–涉及位域的内存 ...
- Rancher安装多节点高可用(HA)
Rancher版本:Rancher v1.0.1 基本配置需求 多节点的HA配置请参照单节点需求 节点需要开放的端口 全局访问:TCP 端口22,80,443,18080(可选:用于在集群启动前 查看 ...
- Ubuntu14-04安装redis和php5-redis扩展
PS:在系统安装完后最好执行下列命令更新下软件 实际上只要软件源没什么问题的话,安装什么软件都是OK的. 来开始安装Redis吧~~ 一:如果你的其他都OK的话,可以执行下列命令直接安装 sudo a ...
- ltp压力测试结果分析脚本
最近工作性质发生了改变,在做操作系统方面的测试.接手的第一个任务是做ltp stress.测试内核稳定性. 做完之后会结果进行统计分析.因为统计的内容比较多,都是通过shell命令行进行操作.于是编写 ...
- js javascript 获取url,获得当前页面的url,静态html文件js读取url参数
获得当前页面的url window.location.href 静态html文件js读取url参数 location.search; //获取url中"?"符后的字串 下边为转载的 ...
- Gre 隧道与 Keepalived
这一篇文章是做了不少功课的. 什么是 Gre 隧道 什么是 Vrrp KeepAlived 是什么 用Keepalived 怎么玩 附录 什么是 Gre 隧道 GRE 隧道是一种 IP-2-IP 的隧 ...
- array, matrix, list and dataframe
总结一下"入门3R"(Reading, 'Riting, 'Rrithmetic)中的读和写,不同的数据结构下的读写还是有点区别的. vector 命名 12 month.days ...