关于使用 C++ 实现的 Conway’s Game of Life 模拟程序的项目报告
引言
本项目报告旨在介绍使用C++语言实现Conway's Game of Life(康威生命游戏)的过程和结果。康威生命游戏是由英国数学家约翰·康威于1970年提出的一种细胞自动机模拟,它以简洁的规则和复杂的演化过程闻名于世,是计算机科学和人工生命领域中的经典问题。康威生命游戏的规则简单明了:在一个二维的细胞板中有数个紧密排列的方形细胞,每个细胞处于生存或死亡的状态,并根据周围细胞的状态进行更新。通过迭代应用生命游戏的规则,细胞板上的细胞会形成各种不同的模式和结构。这种简单的规则和无限的潜在变化使得康威生命游戏成为一个有趣的主题,也是研究复杂系统的理想模型。
本项目的目标是实现一个能够在命令行界面下运行的康威生命游戏模拟程序。使用C++编程语言的原因是它的高效性和广泛应用性。本报告将展示项目的设计和实现细节,包括算法、数据结构和游戏规则的实现方式。此外,本报告还将讨论实现的功能和特性,例如交互机制和保存/加载游戏状态等。
本项目报告能为读者提供一个全面的概述,使读者对我们的实施过程和成果有所了解,并为进一步研究和改进康威生命游戏提供启发。
设计和实现
本项目的设计和实现基于C++语言,采用面向对象的编程方式。主要的类包括GoL(模拟引擎)、CellState(细胞状态)、Cell(细胞)和CommonUtil(常用工具)。下面对每个类进行详细介绍:
GoL类:
GoL
类是整个康威生命游戏的模拟引擎,采用了单例模式设计。它负责支持游戏模拟的运行和显示,以及数据的读写操作。该类的字段包括:
bool flNoBorder
:表示细胞板是否处于无边界状态。如果为true,超出边界的坐标将发生环绕。int currentGeneration
:当前迭代次数。int lines
:细胞板的真实行数(包括边界)。int columns
:细胞板的真实列数(包括边界)。vector<vector<Cell>> cells
:一个存储Cell对象的二维vector,表示当前的细胞板。stack<vector<vector<Cell>>> previousCells
:用于存储之前细胞板状态的stack。
GoL类的主要函数包括:
calculateNextGeneration()
:使细胞板中的所有细胞计算并保存其迭代后的状态。applyNextGeneration()
:将所有细胞的当前状态用其迭代后的状态覆盖,即完成一次迭代。cacheCellNeighbours()
:对每个细胞缓存其周边8个细胞的状态。init(int, int)
:使用指定的行数和列数或文件路径初始化细胞板。save()
:将细胞板的当前状态作为文本文件保存到指定路径。run()
:进行一次迭代。display(bool)
:将细胞板的当前状态打印到标准输出。getCurrentGeneration()
,getLines()
,getColumns()
:获取当前迭代次数、细胞板行数和列数。isNoBorder()
:获取细胞板是否处于无边界状态。getCell(int, int)
:获取指定位置的细胞的引用。setStateOf(int, int)
:设定指定位置的细胞的状态。toggleNoBorder(bool)
:控制细胞板无边界状态的开关。revert(int)
,forward(int)
:回退或前进指定次数的迭代。
CellState类:
CellState
类表示一个细胞的状态,包括边界、死细胞和活细胞。它是一个枚举类型,用于区分细胞的状态。
Cell类:
Cell
类表示一个细胞,包含细胞的当前状态、迭代后的状态以及周围8个细胞的指针。该类的主要函数包括:
calculateNextState()
:根据周围8个细胞的当前状态,计算该细胞迭代后的状态。setNeighbour(Cell*)
:设置该细胞周围8个细胞的指针。
CommonUtil类:
CommonUtil
类包含一些实用工具函数,用于解析细胞状态、暂停程序执行一段时间、清除控制台内容等。
在项目的实现过程中,我们根据 GoL
类提供的接口在 Main.cpp
中设计了一个命令行界面,用户可以通过命令行输入来初始化细胞板、进行迭代、保存状态等操作。同时,我们通过调用 CommonUtil
类中的函数实现了一些辅助功能,如在每次迭代后暂停一段时间,以便用户观察模拟过程。
通过以上设计和实现,我们能够模拟康威生命游戏并展示细胞板的演化过程。具体的算法、数据结构和细胞状态的计算方式将在下文的代码实现部分进行详细介绍。
功能和特性
本项目实现的康威生命游戏模拟程序具有以下功能和特性:
初始化细胞板:
- 支持通过命令行参数创建新的空细胞板。
- 支持通过命令行参数指定文本文件路径,从文件中读取细胞板的初始状态。
模拟迭代:
- 每次迭代根据康威生命游戏规则更新细胞的状态,实现演化过程。
- 可以随时暂停和恢复自动迭代。
- 可以设置迭代的最大次数。
- 可以设置每次迭代之间的延迟时间,以便观察模拟过程。
细胞板状态的显示和保存:
- 可以将当前细胞板的状态打印到命令行界面,用字符表示细胞的状态。
- 可以将当前细胞板的状态保存到文本文件中,以便后续加载和分析。
细胞板的操作:
- 支持编辑细胞板。用户可以在交互菜单中编辑指定位置细胞的状态,以便观察不同情况下细胞板中细胞的演化。
- 支持以无边界状态或有边界状态初始化。
- 支持在显示细胞板状态时包括细胞板边界。
- 支持在特定迭代次数之间跳转。
用户友好的命令行界面:
- 在程序运行时,提供帮助信息和参数说明,以便用户正确使用程序。
- 在每次迭代后,显示当前迭代次数、细胞板的大小,并提供用户交互选项。
通过上述功能和特性,本项目实现了康威生命游戏的基本模拟和控制功能,用户可以通过命令行界面进行交互,观察细胞板的演化过程,以及进行一些操作,如编辑细胞状态、保存和加载细胞板状态等。
代码实现
CellState类的实现:
CellState
定义了三种可能的细胞状态:
STATE_BORDER
:表示边界状态。STATE_DEAD
:表示死细胞状态。STATE_ALIVE
:表示活细胞状态。
将"边界"作为与活细胞和死细胞并列的一种状态的原因是边界在细胞自动机中起到了特殊的作用。边界细胞通常被用来定义细胞板的边界,以便在模拟中限制细胞的扩散范围。边界细胞的状态与活细胞和死细胞不同,因此可以将其定义为一种独立的状态。
通过将边界状态与活细胞和死细胞状态并列,可以更方便地处理邻近细胞板边界的细胞,使对细胞状态的操作和判断可以统一应用于所有位置的细胞。这种设计可以简化代码逻辑,并增加细胞自动机的实现的一致性。
CommonUtil类中关键函数的实现:
freeze
函数用于暂停程序执行一段时间。在Windows系统下使用 Sleep
函数,单位为毫秒;在其他系统下使用 usleep
函数,单位为微秒。
clearScreen
函数用于清除控制台的内容。在Windows系统下调用系统命令 cls
来清除屏幕,在其他系统下使用VT-100控制字符来清除屏幕。
transparent
函数用于将无边界状态下的坐标映射到有边界状态下的坐标。给定一个坐标x和排除边界的长度l,该函数返回x在有边界状态下的坐标。当x能整除l时,返回l;当x大于0时,返回x对l取余的结果;当x小于0时,返回l减去-x对l取余的结果。
Cell类中关键函数的实现:
toString
函数返回表示细胞状态的字符串。该函数会根据编译环境决定是否使用Unicode字符表示细胞状态,或使用传统的ASCII字符表示细胞状态。
toChar
函数返回表示细胞状态的字符,该函数主要用于将细胞板状态写入到本地文件。如果细胞状态为边界,则返回'#';如果细胞状态为死细胞,则返回'0';如果细胞状态为活细胞,则返回'1'。
calculateNextState
函数根据细胞周围8个细胞的状态计算细胞的下一个状态。如果细胞状态为边界,则不做任何处理,返回边界状态。对于活细胞,当周围有2个活细胞或3个活细胞时,细胞保持活跃,否则变为死亡状态。对于死细胞,当周围有3个活细胞时,细胞变为活跃状态,否则保持死亡状态。
setNeighbour
函数用于设置细胞的邻居细胞。细胞具有8个邻居细胞,通过数组 neighbours
存储。该函数将邻居细胞指针存储在数组中,并确保循环使用数组的位置,从而设置所有邻居细胞。
GoL类中关键函数的实现:
init(int, int)
函数用于初始化细胞板。它接受初始行数和列数作为参数。首先为初始大小添加1细胞宽的边界,然后创建一个二维vector来存储所有细胞对象。对于边界位置的细胞,状态设置为 STATE_BORDER
,其他位置的细胞设置为 STATE_DEAD
。最后,调用 cacheCellNeighbours
函数来缓存边界内细胞的邻居关系。
run
函数用于进行一次迭代。它首先将当前细胞板的副本压入 previousCells
堆栈中,然后计算下一代细胞的状态,将其应用到当前细胞板上,并递增当前代数。
cacheCellNeighbours
函数用于缓存细胞的邻居关系。该函数遍历细胞板中的每个细胞,并设置其8个邻居细胞的指针。如果细胞板启用了无边界模式,则使用 CommonUtil::transparent
函数对邻居细胞的坐标进行转换,否则直接设置邻居细胞的指针。
getCell
函数用于获取指定位置的细胞对象。如果启用了无边界模式,则使用 CommonUtil::transparent
函数将位置转换为实际细胞板上的位置,并返回对应的细胞对象。如果未启用无边界模式,并且指定位置超出了细胞板的边界,则抛出 out_of_range
异常。
revert
函数用于回退到之前的细胞板状态。该函数接受一个步数参数 steps
,表示要回退的步数。如果步数小于1,则不做任何操作;否则,函数从 previousCells
堆栈中依次弹出历史细胞板的副本,并将其与当前细胞板进行交换,以恢复到之前的状态,之后将当前代数递减。最后,重新缓存细胞的邻居关系以避免潜在的细胞板元素内存地址改变导致的悬空指针问题。
forward
函数用于前进指定的步数。它接受一个步数参数 steps
,表示要前进的步数。如果步数大于0,则循环调用 run
函数执行下一代细胞的计算和应用,以前进到指定的代数。
用户界面的实现:
用户界面的实现通常涉及用户交互和显示当前状态的功能。在 Main.cpp
的代码中,有以下与用户界面相关的函数:
showMenu(int)
函数显示一个菜单供用户选择不同的操作。用户可以选择退出、开始/恢复模拟、编辑细胞状态、回滚到上一代、跳转到指定代和将当前细胞板的状态保存到本地文件。根据用户的选择,相应的操作会在代码中执行。该菜单会在无限模拟开始时和用户按下Ctrl+C时显示。
mainLoop()
函数包括自动化模拟的主要循环。该函数循环运行,直到达到目标代数。在每次迭代完成后时,它会显示细胞板的状态并等待一段时间。它在每次迭代时调用 app.run().display(flShowBorder)
以计算和显示下一代的状态。
resetStdin()
函数用于重置标准输入。该函数清空输入缓冲区并重置输入流,以便在无效输入时重新提示用户。
main(int argc, char** argv)
函数是程序的入口点。该函数解析命令行参数,初始化GoL引擎,并根据参数执行相应的操作,之后注册Ctrl+C事件的处理函数,最后进入主循环。
以上这些函数和代码提供了一个基本的用户界面,用户可以通过菜单进行交互,并观察和控制模拟的进行。
测试和验证
我们对程序进行了全面的测试和验证,以确保其功能完整、工作正常。通过测试和验证,我们确认程序在各种情况下均能正常运行,并生成符合预期的细胞演化结果。程序能够正确处理细胞板的初始化、细胞演化、边界细胞和特殊情况,并提供了编辑、保存、回滚和前进功能,使用户能够探索不同的细胞演化状态。
结论
Conway's Game of Life(康威生命游戏)是一种经典的模拟程序,可以模拟细胞的生命周期和演化过程。通过对GoL程序的开发和测试,我们得出了以下结论:
- 功能完善:GoL程序具备了基本的功能,包括细胞板的初始化、细胞的演化和状态变化、边界细胞的处理,编辑、保存以及回滚和前进功能等。它能够准确地模拟细胞的生命周期,并产生符合预期的演化结果。
- 代码实现:程序的代码结构清晰,易于理解和维护。程序使用了适当的数据结构和算法,如二维vector和邻居关系缓存,以提高程序的执行效率。同时,程序还充分考虑了不同操作系统的差异,提供了跨平台的兼容性。
- 测试和验证:我们对GoL程序进行了全面的测试和验证。通过测试,我们确认程序在各种情况下均能正常运行,并产生预期的结果,而且程序在处理边界条件和特殊情况时仍然能表现出良好的稳定性。
基于以上评估,我们认为GoL程序是一个功能完善且可靠的细胞自动机模拟工具。同时,我们也注意到一些潜在的改进方向:
- 用户界面改进:目前的程序使用命令行界面进行交互,对于某些用户而言可能不够友好。我们可以考虑利用Qt或Electron等跨平台图形库开发更直观的图形用户界面,以提供更好的用户体验和交互性。
- 性能优化:随着细胞板规模的增大,程序的性能可能受到影响。我们可以进一步优化算法和数据结构,以提高程序的运行效率和响应速度。
- 更多扩展功能:可以考虑添加更多的扩展功能,如保存和加载细胞板状态、自定义规则和模式等,以增加程序的灵活性和可定制性。
综上所述,GoL程序是一个可靠的细胞自动机模拟工具,具备良好的代码实现和测试验证。通过进一步改进和扩展,可以进一步提升程序的用户体验和性能。
附录
程序项目地址:https://github.com/MCUmbrella/GoL
Wikipedia关于Game of Life的百科页面:https://en.wikipedia.org/wiki/Conway's_Game_of_Life
提供Game of Life在线模拟的网站: