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