炸飞机场游戏开发反思。Review of bomb airfield game
这是一篇草稿!
本次开发过程最大的特点是各成员的参与度均较高,且充分使用了 git 协助开发。就系统设计和实现而言,我们通过网页接受用户 输入并显示游戏状态,网页与 C# 二进制之间通过 WebSocket 交互。本系统是基于 TCP 协议实现点对点联机的双人对战游戏, 主要由游戏主循环构成。为了简单起见,这一游戏循环是顺序执行并使用阻塞式 IO。即 等待用户的输入、渲染游戏状态、收发 TCP 数据包时整个程序阻塞。我们设计的游戏主循环又不同于通常意义上的游戏循环, 在交互式电脑游戏程序中,这样的游戏循环常需要每秒钟执行 60 次,每次循环都要刷新屏幕。
手写纯 JavaScript 通过 WebSocket 和原生二进制沟通是非常困难的,应该基于现有的 RPC 协议栈来实现。
一个突出的问题是推拉转换。推拉转换会带来 JavaScript 环境下,有很多代码的执行是基于回调的“推”模式,例如大量使用的事件监听器模式,是由程序员提供事件处理函数, 由框架、库、运行时中的代码来调用这一事件处理函数,将数据以函数调用时传入参数的形式,“推”给程序员编写的代码。 而我们设计的游戏循环,是按照“拉”模式来设计的,即游戏循环中通过调用 UI 模块的方法来获取用户最近的输入。 这种编程范式在处理操作系统原生窗口相关事件的程序中是比较常见的,例如在 SDL 中,程序员如下所示的这样拉取窗口接收到的事件。
1
2
3
4
5
6
7
while (game_is_still_running) {
SDL_Event event;
while (SDL_PollEvent(&event)) { // poll until all events are handled!
// decide what to do with this event.
}
// update game state, draw the current frame
}
这里的问题在于,游戏循环阻塞在
人工智能辅助计算使用的是完全搜索算法,用简单的文本存储了所有的棋盘数据。我用 C# 实现的基于 DFS 的棋盘状态穷举程序速度很慢 ,原因尚不明确,怀疑是内存分配太多太频繁。后来用 C++ 实现并输出为文本。示例如下,一共有六万八千多个这样的飞机场上飞机的摆法的记录。
1
2
3
4
5
6
7
8
9
10
@ @
**********
* *
*** ***
*
* *
@***
* *
*
算法简单介绍如下:一开始时对方机场上一百个方格的状态都是未知的,当人工智能模块接收到对方机场上方格的更新时,便根据这一个方格 的状态筛选出余下的可能的所有棋盘。例如若已知某个方格上命中了对方的飞机,则可以排除那些此处没有飞机或者是飞机驾驶舱的机场。 这之后再对一百个方格进行静态估值,选出下一回合要轰炸的位置。最简单的静态估值可以是对每一个方格,统计余下的所有可能的机场状态中 这个方格上放有飞机的机场数量,数量多者得分高。
团队统一的整个代码仓库的代码风格自动整理是非常必要的。 代码风格整理可以帮助发现语法错误,例如,在 这个提交 中解决的问题,如果有自动代码风格整理,一开始就不会出错。这是把 JavaScript 的逗号当成分号来使用了,经过 prettier 的自动整理 问题变得很明显,prettier 做的整理如 这个提交所示。