[教你做小游戏] 一种记录象棋历史记录的方案:平均每步仅占10bit位

6,331 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

背景

兄弟们,之前我开发了支持联机对战的五子棋、斗地主、UNO。在大家的呼吁之下,我准备开发「象棋」啦!

😄 不出意外,国庆假期,联机象棋就能跟大家见面了!

之前的进展:

继续给大家同步进展:今天,设计了象棋历史记录的保存方案,尽可能降低了空间的占用。

基于该方案,我们可以实现悔棋操作,非常实用。

思考

我之前开发的五子棋,是支持悔棋的,并且可以无限悔棋直到第一步。

但是象棋不一样。象棋的步骤是无限的。随着步数增多,历史记录的空间占用一定是线性增长的,并且永不止境。所以我只会保存最近1000步(或者9999步等)。

这里罗列一些方案:

方案优点缺点
记录每次操作后,当前棋局所有棋子的位置比较无脑,开发快占用空间大
从初始局面开始,顺序记录用户操作的棋子、移动后该棋子的位置占用空间很小本方案不适用,因为每一次记录都依赖上一步的局面(不然不知道吃了谁、也不知道移动前的位置)
从现状局面开始,逆序记录用户操作的棋子、移动前该棋子的位置、吃了谁占用空间相对小

我选了第三个方案,因为它是逆序记录,可以从当前局面非常快的推演出悔棋后的局面。

image.png

数据结构

确定了方案:从现状局面开始,逆序记录用户操作的棋子、移动前该棋子的位置、吃了谁。

还需要决策,具体怎么存。

初步的方案

  • 棋子一共有32个,标识一个棋子需要用5个二进制位。但是因为红黑是轮流进行的,所以标识棋子可以缩短到16个数字,即4个二进制位。
  • 而标识吃掉了谁(也可能没吃掉),需要17个数字,只能用5个二进制位了。
  • 棋子移动前的位置一共有90个,需要7位二进制来存储。

共计每一步操作需要4+5+7=16个二进制位。

改进:优化吃掉了谁

其实吃掉将帅后 游戏就结束了。将帅是否被吃掉,可以通过当前棋盘状态来看。因此我们可以优化「吃掉了谁」这个变量:把0改为没吃掉任何人或吃掉了将帅。

这样每一步操作需要4+4+7=15个二进制位。

改进:优化移动前的位置

我还是不够满意,因为每个棋子移动的范围是有限的:

  • 将帅最多有4个可移动范围。只需要2位。
  • 士最多有4个可移动范围。只需要2位。
  • 象最多有4个可移动范围。只需要2位。
  • 兵最多有3个可移动范围。只需要2位。
  • 马最多有8个可移动范围。只需要3位。
  • 车最多有17个可移动范围。只需要5位。
  • 炮最多有17个可移动范围。只需要5位。

而我们知道了用户操作的棋子是谁,就知道了它的可移动范围了。

这样每一步操作需要4+4+2=10至4+4+5=13个二进制位。

改进:优化吃掉了谁

还可以进一步压缩。因为大多数移动,都是没吃掉任何棋子。我们可以用一个位标识用户是否吃掉了棋子。如果吃掉了,再用4位来标识吃掉了谁。如果没吃掉,就不需要这4位了。

这样每一步操作需要4+1+2=7至4+1+4+5=14个二进制位。

写在最后

我是HullQin,独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费无广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:《教你做小游戏》《极致用户体验》