我的目标是写一个基本的国际象棋玩AI.它并不需要令人难以置信,但我希望它能够对某些熟悉游戏的人有一定程度的能力.
我有一个名为Piece的特征,它有抽象方法canMakeMove(m:Move,b:Board)和allMovesFrom(p:Position,b:Board).出于显而易见的原因,这些方法对于程序逻辑很重要,并且由具体类King,Queen,Pawn,Rook,Bishop和Knight实现.因此在其他地方,例如在确定特定板是否具有King的代码中,这些方法在类型为抽象类型Piece的值上调用,(piece canMakeMove(...,... ))所以调用的实际方法是在运行时通过动态调度确定的.
我想知道这对于国际象棋AI程序来说是否太昂贵了,而这个程序将不得不多次执行此代码.在网上浏览并阅读有关国际象棋编程的更多内容之后,我发现国际象棋棋盘的最常见表现形式不像我的Vector [Vector [Option [Piece]],而是一个可能使用的int矩阵('bit board')关于板上值的switch语句,以实现我目前依靠动态调度来实现的效果.这会阻止我的AI达到可行的性能水平吗?
我会在这个答案中建议你在编程中遇到一个常见的陷阱.我从同事那里学到了一个非常重要的智慧,它已经一次又一次证明了它的价值:
只有在问题发生时解决问题,而不是更快.
请记住,很长一段时间里都有国际象棋程序和国际象棋电脑.C64上甚至还有国际象棋程序,还有那些嵌入国际象棋棋盘的小型旅行棋牌计算机,因此您可以轻松携带它们.
这些系统的资源远远少于现在的计算机几个数量级.如果你有一个类似于在那些旧系统上运行的算法,并且该算法是在Scala中实现的,使用动态调度,宽大的向量而不是位数组等,它们仍然比它们在旧硬件上更快从过去的日子.
尽管如此,为国际象棋问题编写一个适度的搜索启发式算法对于单个开发人员来说是一项艰巨的任务.当面对一项巨大的任务时,一个人直观地倾向于寻找能够解决问题的第一个问题,希望这自然会导致你可以解决的下一个小问题,等等,最终领先彻底解决这个巨大的问题.分而治之.
但是,我个人的经验告诉我,这不是解决更大编程问题的好方法.通常,如果您遵循此路径,您最终会得到一个真正优化的棋盘表示,只消耗很少的内存,并尽可能地优化运行时间.
然后你就被困住了.你已经把所有精力投入到这个中,你真的为你的棋盘级别而感到骄傲,不知何故,你真的不满意你觉得没有更接近实际拥有一个有效的国际象棋AI.
这就是我在开始时提到的规则派上用场的地方.简而言之:不要优化.然而.如果有必要,您仍可以稍后进行优化.
相反,请执行以下操作:
通过这种方式,尽可能快地获得国际象棋AI的第一个版本,它可以做一些有趣的事情.您可以在此上下文中定义"有趣"的含义.
如果国际象棋AI的第一个版本很糟糕,并且每场比赛都输了,因为它做出了非常糟糕的决定,这没关系.它应该只有一件事:它应该做一些你至少发现有趣的东西.
然后,确定您对国际象棋AI的最大问题.想想如何解决它,提出一个只能解决一个问题的计划,以你能想象到的最简单的方式,即使你的程序员本能告诉你做一些更复杂的事情.抵制快速深入研究复杂性的冲动,因为您编写的最复杂的代码最终将成为您最大的问题,也是您代码库中最不灵活的部分.
解决方案的简短,简单,几乎是愚蠢的步骤.原型很多,在短时间内创建简单版本,并且只在需要时进行优化.
至于优化 - 这真的不是问题.例如,让我们说在开始时你认为定义这样的董事会职位是个好主意:
case class Position(x:Int, y:Int)
Run Code Online (Sandbox Code Playgroud)
后来,你认为只使用一个整数元组来表示一个董事会职位是一个更好的选择.只需将您之前的定义替换为:
type Position = (Int, Int)
Run Code Online (Sandbox Code Playgroud)
你去吧 进行更改,编译代码,编译错误将显示代码中需要适应的所有位置.重构这个并不需要很长时间.
即使在线下,您也可以决定,由于电路板上只有64个可能的位置,您可以轻松地用0到64之间的数字表示位置 - 为"板外"保留数字64.同样,这是一个简单的改变:
type Position = Byte
Run Code Online (Sandbox Code Playgroud)
保存,编译,修复编译错误.不应超过15分钟.
在这个例子中,我想说明你不应该害怕以后优化,等待它们实际发生时才解决问题.这总是需要一些重构,但通常不值得一提.我倾向于认为这是"按摩代码".
当然我们知道初级程序员有时会写"意大利面条代码",以后很难重构.也许这就是为什么有一种对后期优化的恐惧,几乎每个开发人员都知道,以及早期优化的冲动.
对这种"意大利面条代码"唯一真正的解药是多次经历这个痛苦的过程.过了一段时间,您将对如何编写可以轻松优化和重构的代码形成非常好的直觉.这将匹配所有常见的编程智慧,如关注点分离,简短方法,封装等.在这一点上,我会推荐"清洁代码"这本书作为一个很好的指导方针.
更多提示:
scalatest库可能是一个不错的选择.使用像SBT这样的工具,它将在每个构建上运行所有自动化测试.如果您对所编写的类有某些假设,请将这些假设硬编码到自动化测试中.一开始会感觉有点愚蠢和多余.但是,当您的一个自动化测试第一次向您显示本来很难找到的错误的原因时,编写这些自动化测试的所有时间都将得到支付.jacoco4sbt.祝你好运,充满乐趣!