达加兹:情节(第1部分)

我们动摇了您的心理过滤器,结果得到了答案。 该方法奏效,将永远有效。 所有需要做的就是摆脱偏见的额外负担...

雷蒙德·琼斯(Raymond Jones)“ 噪音水平

达加兹并没有从头出现。 我一直喜欢棋盘游戏和谜题,并且我尽力记得做编程,但是想到某个“通用”引擎根本就没想到。 我对此想法持怀疑态度。 直到我看到Zillions 。 不幸的是,当时该产品不再开发,源代码不可用,实际上该程序只能在Windows下运行。 一段时间后,我决定进行一个开放项目。

正如我已经说过的那样,我没有任何源代码,但是我碰到了 Zillions,我抓住了他的主要思想-最大限度地重用应用程序代码,这允许以不同的方式使用相同的构造,这似乎彼此完全不同。案件。 都是关于正确的用例。 我制定了计划

跳棋


这个重要但极其低估的游戏家族为该项目奠定了基础。 所有“跳棋”游戏彼此相似,仅在细节上有所不同。 在游戏设计方面,它们全部由三个主要思想结合在一起:

  • 通过支票
  • 优先考虑
  • 复合移动

第一段没有提出任何特殊问题,但是如果开发重点放在国际象棋游戏上,这可能会令人惊讶。 并非在所有游戏中,捕获都是在棋子完成移动的同一区域进行的。 通过优先移动,事情会更加有趣。 在跳棋中,此规则允许您构建复杂的组合游戏,以“少给多捡”的原则诱使敌人陷入陷阱。


当然,优先级迁移机制是在Zillions中实施的(并从那里迁移到Dagaz)。 如果没有它,几乎所有跳棋游戏(肯定包含在实施中必不可少的棋盘游戏的“绅士套装”中)都无法正常工作。 全部都是细节。 让我们看看这种机制是如何实现的:

以百万计
(move-priorities jump-type normal-type) ... (define checker-shift ( $1 (verify empty?) ;        add ;   )) (define checker-jump ( $1 (verify enemy?) ;         capture ;   ( capture  -   ) $1 (verify empty?) ;           add ;   )) ... (piece (name Man) (image White "images/stapeldammen/white.bmp" Red "images/stapeldammen/red.bmp") (moves (move-type jump-type) (checker-jump nw) (checker-jump ne) (checker-jump sw) (checker-jump se) (move-type normal-type) (checker-shift nw) (checker-shift ne) ) ) 

这是最简单的棋盘游戏的几乎完整的描述。 ZRF中引入了移动模式( move-type )的概念,移动优先级构造使我们可以说,如果存在优先级较高(举动)的移动,则不应考虑优先级较低(静默移动)。 可以定义优先级,并且可以定义两个以上的优先级,在这方面,设计是相当通用的,但是在Dagaz上进行游戏时,我遇到了这种机制的一些局限性。


在此游戏中 ,由所罗门·哥伦布(Solomon Golomb)发明,除了跳棋外,还有一些棋子。 困难在于这样的事实,即在保持跳棋优先级的同时,对棋子而言则并非如此(否则,诱捕和食用它们就太容易了)。 在此游戏中,无法使用关键字move-priorities进行幼稚的优先级排序。

实际上,如果优先动作中不包括棋子,则有可能同时下棋子和棋子,由于支票捕获是优先事项,因此我们将不能下棋。 如果象棋动作被视为同等优先,只要有机会,我们将不得不下棋。 两者都与游戏规则相抵触。

在Zillions中,实际上没有解决此问题。 这就是我考虑将JavaScript扩展机制引入Dagaz的主要原因。 这个想法本身很简单:由于某些游戏机制在ZRF中很难表达,所以为什么不引入动作的后处理阶段呢? 在这种情况下,扩展模块将完整扫描生成的移动的整个列表,并可以决定是否拒绝某些移动。 这是Shashmat的外观

简单紧凑的代码
 var CheckInvariants = Dagaz.Model.CheckInvariants; Dagaz.Model.CheckInvariants = function(board) { var design = Dagaz.Model.design; var types = []; types.push(design.getPieceType("Bishop")); types.push(design.getPieceType("Camel")); var isPriority = false; _.each(board.moves, function(move) { if (isCapturing(board, move)) { if (_.indexOf(types, getType(board, move)) < 0) isPriority = true; } }); if (isPriority) { _.each(board.moves, function(move) { if (!isCapturing(board, move)) { move.failed = true; } }); } CheckInvariants(board); } 

将来,扩展的想法不断发展,并以绚丽的色彩绽放。 我有一个方便而强大的机制来编码许多游戏,这些游戏在纯ZRF上的实现会遇到很多问题,但这是否意味着ZRF风格的优先级已过时? 当然不是! 首先,用ZRF写一行比用JavaScript写五十行要容易,但是更重要的是,“硬” ZRF样式的优先级的工作方式甚至不会产生低优先级的动作! 就性能而言,这很重要。 在达加兹生成动作是非常昂贵的操作。

另一个挑战性的游戏

达布洛特的游戏有点类似于意大利选秀 ,但更古老。 除普通人物外,其中还有“王子”和“国王”,年轻的人物无权殴打长者。 但这不是困难。 对于国王(在某些游戏中对于王子也是如此),捕获是可选的! 这里出现了与Shashmat相同的问题。 如果我们宣布国王为优先,那么我们将严重违反游戏规则,否则,我们将无法以简单的身分与国王抗衡。 只有Dagaz扩展引擎才能解决此问题。

顺便说一句,“意大利草案”并不是那么简单。 在许多不同类型的草稿中,都有一条规则规定玩家必须拿最大数量的棋子。 也就是说,他不能仅仅中断捕获链,而必须选择他将获得更多碎片的路径! 由于下面将要讨论的原因,该规则无法以通用形式在Zillions中实施,因此开发人员被迫对其进行硬编码。 在意大利的选秀中,“多数统治”听起来更加复杂:“您需要击败最大数量的对手选秀,而在相同的战斗选择中,您需要击败最大的贵妇”。

复合动作是跳棋游戏的第二个重要组成部分。 如此重要,以至于我定期进行“ 土耳其草案 ”中对正确捕获的测试 ,直到现在。 几次,当我用下一个更改打破模型时,它确实有所帮助。

移动-复合和局部
让我们看看如何在ZRF中实现复合移动
 (define checker-shift ( $1 (verify empty?) (if (in-zone? promotion) (add King) else add ) )) (define checker-jump ( $1 (verify enemy?) capture $1 (verify empty?) (if (in-zone? promotion) (add King) else (add-partial jump-type) ) )) (define king-shift ( $1 (verify empty?) add )) (define king-jump ( $1 (verify enemy?) capture $1 (verify empty?) (add-partial jump-type) )) 

这是多么简单。 add-partial命令说,如果仍然存在指定模式的移动,则可以继续移动(同一块,这一点很重要)。 换句话说:“在有这样的机会的同时,这个数字必须继续保持下去。” 一切似乎都很好,但有一个警告。 Zillions认为每一个这样的举动都是单独的“部分”举动。 让我们看看这会导致什么。


在此游戏中 ,棋子执行的“步数”由棋子所在的图标确定。 现在,怀特(White)采取行动,达米(Damyo)正在行走(一块标有红色珠子的碎片)。 在Zillions中,完成两次局部移动后,她可以轻松地进入左上角,从该位置不再可以进行最后的剩余移动(您不能返回)。 也禁止在第二部分动作中抓住对手的棋子。 在Zillions中,没有任何方法可以阻止一系列导致死胡同的行动。

在达加斯,情况有所不同! 一连串的部分动作总是组合成完整的合成动作。 无法完成的举动根本不会发生! 这是一种资源密集的方法,因此,在Dagaz中生成移动列表是一项非常昂贵的操作。 但是他的优势是巨大的。 例如,机器人会完整接收整个复合动作,并且不应向前看,执行其余的部分动作。

更重要的是,这种方法提供了机会来考虑条件可接受的移动的整个列表,执行更复杂的检查,并根据其他移动的存在来禁止某些移动。 例如,我在Dagaz中提到的“多数规则”非常简单地实现。 而且,对于“意大利跳棋”也是如此。 Zillions的开发人员通过对“ 最大捕获 ”选项进行硬编码来“解决”了他们对于跳棋游戏所知的问题,但是有大量带有其他复杂检查功能的游戏,那时他们还不知道!

在开发新游戏的过程中,还开发了复合移动的概念。 FanoronaPasang提出了一种有趣的游戏机制,其中执行移动的玩家应选择从棋盘上移除的一组棋子:


另外,《 法诺罗纳》是其中一些玩家有权中断捕获链的稀有游戏之一。 第一次捕获是强制性的,随后是同一动作-由玩家自行决定。 在Dagaz中,此选项( pass-partial )通过将图形移到适当位置来实现。 Missclick可能在这里,这显然不是很方便,但是随着会话管理器的引入,现在可以回滚错误的动作。

该主题的进一步发展是“射击”动作。 我首先在Hanga RoaKo Shogi中制作它们,但是后来发现,我做错了! 错误的实现在bot的控制下是行不通的(而且由于这两款游戏我都没有bot,所以我什么也没注意到就不足为奇了)。 后来,当我使用Amazons时 ,我设法将问题本地化并修复。 这个想法在1957年由我们的一位同胞发明的游戏中达到顶峰。


还有一个问题与达加兹联合行动的实施有关。 事实是,从特定的游戏状态开始,允许动作的清单即刻形成-整个过程。 在Zillions中,它的局部移动并不是很关键,但是在Dagaz中,如果该棋子有机会“绕圈”,则移动的生成阶段将永远不会完成(很明显,扩展无法解决此问题,因为它很容易处理达不到)。 这是重要的游戏之一:


在这里,这些棋子不会从板上移开,同一棋子可以连续跳很多次。 显而易见的解决方案是禁止每回合两次访问同一字段,但是我必须彻底进入内核才能实现这种检查。 这有点像“ 延迟捕获 ”选项的实现,但是由于我早些时候做过俄罗斯草案 ,所以问题就少得多了。

不幸的是,有些游戏中即使这样的检查也无法保存
StapeldammenStapeldammen)的规则(这是一种“ 支柱 ”,明确规定同一回合每回合可以被打败多次。执行此动作的回合多次返回同一位置并继续战斗,而在敌人中Dagaz的复合动作无法解决这个问题。“极点吃水”战斗的逻辑对于内核来说太复杂了,因为循环的循环是循环的,所以它无法扩展,当然,有一条出路:


Dagaz中没有局部移动,但是我们可以通过跳过对手的下一个移动来模拟它们( 贴花中使用相同的方法)。 扩展很容易实现这种逻辑。 我们只是在特定条件下禁止所有举动, pass-turn = force选项会自动生成一个空举。 这是另一个具有类似仿真的游戏


将合成动作人为地分为部分动作对于AI机器人来说不是很好,但是有时候,别无选择。

总的来说,复合的概念推动了生活的发展。 最近,我不得不为一项古埃及游戏做出另一种新选择( 完全部分 )。


除了自动完成沿箭头的图形移动外,它还具有其他有趣的技术解决方案。 但是大约在其他时间。

Source: https://habr.com/ru/post/zh-CN459456/


All Articles