翻转棋,以及被重复使用的代码

nealian 2020-04-13 PM 898℃ 0条
翻转棋,以及被重复使用的代码

如果把程序比作建筑,把代码比作砖,是不合适的,因为同一块砖不能彻在建筑的两个地方,而前者可以。代码在时间上是经过压缩的,因此,理解代码与否决定是否能看清其原貌。这也是编程与砌墙相比,似乎更有意思的一个地方。

问题

有一个段代码,实现了一个翻转棋(黑白棋)的基本功能,包括人机对战、人人对战以及保存棋局, 需要在其上添加:

  • 复盘功能
  • 困难模式;目前人机包含简单(随机法)和普通(翻转最多子法)两种模式

0.jpg

图1 翻转棋局面(来源

复盘
  • 复盘包含棋局保存、读取以及局面的推进和退回
  • 原代码已经实现了棋局保存,将棋局双方落子的坐标依次保存在文本文件中
  • 原代码中实现了人人对战模式,双方依次点击棋盘可落子处,推动棋局发展;考虑人人对战复盘的顺序推进两个过程十分相似,只是获得落子坐标的方式不同,前者通过鼠标点击,后者从文件中读取
  • 将复盘当作人人对战,将文件中的坐标作为参数,重复使用点击事件的代码,即可实现下一步的功能而几乎不需要添加新的代码逻辑,问题在于:

    • 人人对战在执行落子的时候,并没有保留上一步的状态,正常实现退回上一步的功能需要写不少代码
  • 考虑到达上一步状态的方式其实有两种,一种是从当前状态退回,另一种是之前已经经历过的,即从起始状态一步一步到达上一步状态;若使用第二种方式,即可采用与实现下一步时类似的处理,几乎不需要添加新的代码逻辑

1.png

图2 下一步和上一步示意图

困难模式
  • 困难模式的电脑落子算法,需要尝试所有可行的落子位置,并对落子后的局面进行评估,选择分数最高的落子
  • 原代码中board对象有applyMove方法,调用即可得到落子后的局面,但它并没有提供撤回的方法,要在调用applyMove方法之前,保存落子前的状态,以便进行其它尝试
  • 考虑每次尝试落子前对board对象进行深拷贝,然后在副本上进行演算,问题在于:

    • 原代码中board对象除了表示棋盘还负责棋盘界面的渲染,因此持有许多UI元素,如位图等,这些元素阻碍了对board对象的深拷贝
  • 考虑新建一个BoardRenderer类,把与棋盘界面相关的代码从Board类中提取到此类中
  • 至此,即可在副本上演算,确定落子位置后再在真实的board对象上落子,实现困难模式而除了落子算法,几乎不需要添加其它新的代码逻辑

2.png

图3 新的BoardRenderer类

总结

世上有一个规律,即复杂的东西往往由简单的部分组合而成。问题也是如此。分解问题可行的原因在于,我们能在已经解决过的问题中发现与此问题存在某些相似的部分,并且能够理解这些部分与此问题的组成关系。

标签: none

非特殊说明,本博所有文章均为博主原创。

评论啦~