Assign1 LifeGame
简介
《生命游戏》是英国数学家J.H.康威于1970年构思的模拟,由马丁·加德纳在他的《科学美国人》专栏中推广。该游戏使用二维细胞网格对细菌的生命周期进行模型。根据最初的模式,游戏使用一套简单的规则模拟后代细胞的出生和死亡。在此任务中,您将实现 J.H.康威 模拟的简化版本和观看细菌随着时间而增长的基本用户界面。
模拟从网格上的细胞初始模式开始,并根据以下规则计算连续几代细胞:
- 一个零或一个邻居的位置将在下一代空。如果一个细胞在那里,它就会死亡。
- 有两个邻居的位置是稳定的。如果它有一个细胞,它仍然包含一个细胞。如果是空的,还是空的。
- 一个有三个邻居的位置将包含下一代的细胞。如果它以前无人居住,一个新的细胞诞生了。如果它目前包含一个细胞,细胞仍然存在。
- 在下一代中,一个有四个或更多邻居的位置将空无一人。如果那个地方有一个牢房,它就会因人满为患而死亡。
功能模块
用户交互模块
您的生命游戏计划应首先提示用户提供文件名称,并使用该文件的内容来设置细菌群落网格的初始状态。然后,它应该询问模拟是否应该环绕网格。然后,该计划将允许用户通过几代人的增长推进殖民地。用户可以键入 t 以”勾选”一代的细菌模拟,或开始一个动画循环,将模拟向前滴答作响几代人,每 50 毫秒一次:或q退出
选择初始游戏网格
- 供用户选择初始游戏网格
- fileExists(filePath) -Checks if a file with the corresponding fileName exists. Returns a bool.
1
2
3
4
5
6
7
8
9
10
11void input_File(string filePath, ifstream& fin) {
while(true) {
string filename = getLine("Grid input file name? ");
filePath = "res/" + filename + ".txt";
if(!fileExists(filePath)) {
cout << "Can't locate the file, try again.\n" <<endl;
}else {
break;
}
}
}
选择游戏模式(wrap)
模式1:wrap around the grid
游戏网格无边界,显示的不足8个neighbor边界网格将以其他边界网格替代
模式2: no wrap around the grid
游戏网格存在边界,不足8个neighbor的边界网格不作填充
游戏’ui‘
- animate 指定动画次数,自动每50ms显示下一代细胞繁衍情况
- tick 手动点击显示下一代细胞繁衍情况
- quit 停止游戏 退出
模拟算法模块
存储
1 | #include"grid.h" |
- 采用grid存储而非二维vector因为grid更利于操作,例如g.inBounds(r,c)可判断(r,c)位置是否越界
- grid需要严格设置边界为初始化网格的长宽,因为初始化文件中还有其他杂项(注释等)
g.resize(r,c)可重置网格维度
模式
思路: 遍历网格的每一个单元格,计算其neighbor数,从而设置该单元格的显示内容(细胞的生命状态)
难点: 不同模式不同的单元格neighbor数的计算方式
解决方案:
对于指定位置遍历其相邻8个网格的方法 –相对位置
与中心网格相邻的网格(’边界‘网格)与中心网格的横纵相对位置差值均在-1-+1中 (0为本身的情况需排除)
模式1:wrap around the grid
游戏网格无边界,显示的不足8个neighbor边界网格将以其他边界网格替代
无边界就是边界外部的网格对应到网格内的网格,%操作即可
1 | int neighbourNum_Wrap(const Grid<char>& g, int r, int c) { |
模式2: no wrap around the grid
游戏网格存在边界,不足8个neighbor的边界网格不作填充
因为存在边界,因此边界外部的网格需要舍弃,添加inBounds判断即可
1 | int neighbourNum_NonWrap(const Grid<char>& g, int r, int c) { |
知识点汇总
File Pointer 文件指针
文件位置指针是一个整数值,用于标注文件当前读写位置。
文件指针以字节为单位,文件第一个字节位置号为0,长度为N字节的文件有效读写范围为0~N-1。指针位置在此之外进行读/写操作,则失败;
istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。
这些成员函数包括关于 istream 的 seekg(”seek get”)和关于 ostream 的 seekp(”seek put”)。
seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 ios::beg(默认的,从流的开头开始定位),
也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。
下面是关于定位 “get” 文件位置指针的实例:
1 |
|
I/O流
cin.fail() –文件流操作失败 读取的类型不匹配等 返回类型为bool
!cin –相当于cin.fail() 返回类型为bool
cin.peek() –返回文件指针当前指向的字符 返回类型为char字符
连续输入时操作失败处理方法
cin.rdstate() 查看错误错误标识符
cin.clear() 清除错误标识符(错误状态)
cin.sync() 清除输入错误时存入缓冲区的字符
cin.ignore(999, ‘\n’) 清除缓冲区前999个字符,第一个参数足够大可用于清除换行符之前的所有缓冲区字符,消除上一次输出对本次输入的影响