标题: 关于攻击最近敌类型的移动问题
性别:未知-离线 clubjack

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 544597
注册 2021-5-11


发表于 2025-2-5 23:59 资料 短消息 只看该作者
关于攻击最近敌类型的移动问题

在信都之战里, 底下的贼兵和步兵, 从 3 回合起变为了攻击最近敌类型. 参考周瑜或者月亮的 2699 攻略, 刘备在第 4 回合原地不动时, 步兵在刘备左, 他的行动却是左 4. 类似地, 在第 5 回合结束时, 贼兵在刘备左, 他的行动也是左 4. 根据孝直兄的移动型 AI 算法 ( https://www.xycq.org.cn/forum/vi ... horuid=0&page=1 ) 对于攻击最近敌类型的 AI, 他们显然可以直接攻击到刘备, 却选择了左 4, 令人百思不得其解...

关于左 4 的行动, 唯一合理的解释是他们把上方我军当作了最近敌 (因为左 4 连防御地形都不是), 而刘备堵住了桥头, 导致他们决定从左边绕上去. 但奇怪的是, 只要他们没挨着刘备, 就能正确识别攻击刘备. 例如第 5 回合的行动, 步兵左 1 下 1 进森林, 我理解这里是因为 AI 判断移动+攻击范围有敌人 (刘备), 但因为贼兵卡住了左攻刘备的位置, 因此步兵选择了最短的可以进防御地形的坐标. 当然, 此时贼兵的行动还是存在上面的问题, 没有直接攻击刘备, 而是左4.

[ 本帖最后由 clubjack 于 2025-2-6 10:30 编辑 ]


图片附件: 4回合结束.png (2025-2-5 23:59, 588.22 K)



图片附件: 5回合开始.png (2025-2-5 23:59, 587 K)



图片附件: 6回合开始.png (2025-2-6 00:06, 585.36 K)



顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-6 19:19 资料 短消息 只看该作者
我也看着孝直的AI算法进行这方面研究的,我想你应该大致对常见的几种AI类型和行动有所了解,所以就简单地对你的推论进行分析。

你是分析大部分没有问题,但“堵住了桥头”需要重新斟酌一下,应该要换一种说法,也就是“堵住了桥头”只是一个表象,真正的内因还要分析一下。
第4回合短兵和第5回合山贼左4的行动看似需要用最近敌是上方的我军来解释,从直观上似乎有点难以理解,有一个更加直接明了的解释,就是可以把劉備转换为一个无法通过的地形,比如说城墙(这游戏有城墙这个武将吗),这样就能解释为什么要从左边绕上去。
但这样又不能解释为什么第4回合山贼攻击劉備,第5回合短兵只走2步进森林的行动,也就是这两个行动又得把劉備当成一个攻击目标了。

那么可不可以这么解释,就是只有敌军紧挨着我军才会把我军当成障碍,有距离就正常看待。如果用这种解释,我相信你一定不会发这个贴,这种错误的想法一闪而过,但又找不到更合理的解释。
这种解释最大错误是它只能解释这种情况下的敌军AI,其他情况下用这种解释显然是苍白无力的,因此需要发散思维,把更多情况下来合并一起讨论,从中筛选出相似的情形,再归纳其中的共通点并分析得出结论。

你应该可以自己测试出反驳这种解释的情况,比如改变劉備堵住桥头的位置,让两个人堵住桥头等等,我还要先测试一些其他情况来完善想法,先给一些提示。


顶部
性别:未知-离线 clubjack

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 544597
注册 2021-5-11


发表于 2025-2-6 20:33 资料 短消息 只看该作者


QUOTE:
原帖由 漫漫苦短 于 2025-2-6 19:19 发表
我也看着孝直的AI算法进行这方面研究的,我想你应该大致对常见的几种AI类型和行动有所了解,所以就简单地对你的推论进行分析。

你是分析大部分没有问题,但“堵住了桥头”需要重新斟酌一下,应该要换一种说法,也就是“堵住了 ...

感谢漫漫苦短兄的提示! 其实, 我也正是近期看到了您关于英杰传代码的研究, 才又提到了相关问题. 我对汇编代码一窍不通, 所有相关机制和走位, 参考的都是龙吟、孝直、周瑜、月亮等几位前辈的研究成果. 正是看到您近期开始研究汇编代码, 才希望能有机会解答当前以及几年前心中的疑惑.

孝直的移动型 AI 算法帖子 (https://www.xycq.org.cn/forum/viewthread.php?tid=220902), 已经把机制描述得较为详尽, 并给出了伪代码. 但可能是考虑到源码的复杂和晦涩, 孝直兄已经将其翻译成了可读语言. 在这过程中, 难免有一些边缘情况是中文描述/伪代码没法覆盖的. 仅就本帖的例子而言,  它和原帖中 “以(A)为中心,如果移动范围+攻击范围内有敌人,则按照AI=3(休息)的方式操作” 的描述相关, 对应着伪代码 “最近敌代码=search(当前(x,y),8,移动力,(-1,-1))” 的部分.

我的第一感觉是, 在搜索移动+攻击范围内最近敌时, 确实没考虑当前位置. 并且, 在搜索不限范围的最近敌时, 也没有考虑当前位置. 目前在信都之战能够复现这一现象的位置有 3 个 (这里就不贴图了), 是桥上的两个位置, 和桥右边的位置. 这些位置的共同点是, 当敌人紧挨我方时, 无法以任何移动>=1的方式找到任一个可以攻击到我方的位置 (无所谓这一新位置是否已被其他敌方占位).

但这样的解释也诚如仁兄所说, 无法令人感到满意. 类似的“一夫当关, 万夫莫开”地形并不难找. 我会再找机会验证类似的猜想. 但需注意, 可能需要同时验证拓扑结构为环形的地图, 和一格可以卡住另半张地图的地形. 因为后一种情况下, 别的我方人员不可能是最近敌, 但显然 (?) 敌人不会原地不动任我方卡住 (需要找例子证实).

这个现象让我联想到 2 个问题 (和本问题可能无关). 一个是敌人被围时, 只能停在原地, 但不会用策略, 并且按固定的攻击顺序攻击我方. 另一个是移动时的亮格和暗格 (https://www.xycq.org.cn/forum/thread-305686-1-2.html) (另注, 这游戏还真的有城墙地形, 不过不是武将, 就是地形) 他们的共同点是, 明明貌似可以沿用简单的机制 (被围了就直接计算行动价值; 使用相邻格子是否有敌军来设置 ZOC ), 但结果却是必须通过打补丁的形式来描述行动机制. 这些机制的形成, 可能是游戏创作者的刻意为之; 但我相信应该有较为简明的实现 (尤其是暗格和亮格的机制: 有容易的地形, 有难的地形, 容易的地形是停止格或者只有一本道的出路时, 难的地形就不能多走; 有必要搞这么复杂吗...?). 一个另外和原地机制相关的例子是, 任何情况下, 停留在原地的行动价值+1. 亦即, 若原地的原始行动价值为 x, 移动后的行动价值为 x+1, 则敌方总是会停在原地.

因此回到本问题, 我相信关于移动型 AI 的算法, 还有些细节可以完善. 例如, 在孝直的贴子中, search 函数既用来寻找最近敌, 又用来判断目标坐标是否可达. 关于可达性, 使用上右下左确实没问题. 但关于 (叠加攻击范围后的) 最近敌判断, 其实是依赖兵种的攻击范围 (或者, 也包括策略范围?). 我在原帖的 15 楼也给了一个使用了攻击范围判断最近敌后, 因为下邳桥头的特殊地形导致攻击最近敌类型的敌军原地休息的例子.

[ 本帖最后由 clubjack 于 2025-2-7 00:38 编辑 ]
顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-7 19:19 资料 短消息 只看该作者
回复 #3 clubjack 的帖子



QUOTE:
在搜索移动+攻击范围内最近敌时, 确实没考虑当前位置. 并且, 在搜索不限范围的最近敌时, 也没有考虑当前位置.
这些位置的共同点是, 当敌人紧挨我方时, 无法以任何移动>=1的方式找到任一个可以攻击到我方的位置 (无所谓这一新位置是否已被其他敌方占位).

你的发现完全正确,只靠观察规律就能得出这个感觉想必也是一件难事,这也我在研究代码和不断测试后得出的结论。

QUOTE:
联想到 2 个问题 (和本问题可能无关). 一个是敌人被围时, 只能停在原地, 但不会用策略, 并且按固定的攻击顺序攻击我方. 另一个是移动时的亮格和暗格

我看到这里的心情溢于言表,发现了第一个真正能收益匪浅的读者。我写这些内容主要有四个目的:一是我看到前辈的文章想进行深度的分析,因为他们所提供的资料只是游戏的冰山一角,而且太分散了,需要进行整合;二是我希望这些分析结果都能有具体汇编代码作为印证,让这些分析都能有具体的论证过程而不是空穴来风,并且能让更多的人看到这些分析结论;三是把汇编代码以及我的具体分析展示出来,让也懂代码的同好进行评审,弥补自我研究带来的不足;四是虽然只有少数人懂得汇编代码,但其中的逻辑相信还是大部分玩家能够看得明白的,我希望大部分玩家能在其中收获自己的理解并且提出一些设想,并且能通过测试等等来完善这些的分析结论。你能有这样的联想让我真正感受到价值所在。

你联想到的这 2 个问题,恰好正是与你的提问相关的,而且你敏锐看出了其中相关联的依据。而这 2 个问题的出现,还恰恰是这个search函数在其中扮演“关键角色”。这search函数如此重要应该是技术总监牵头的吧!难道是那个资深程序员写的?把这个工作交给那个35岁的大佬也行吧。什么,刚来的实习生写的,还让他自己测试??这就是我在分析完这个函数的感受,传说中的屎山代码(反汇编就是把它分解了再看)。

言归正传,这个函数其实有好几处都被调用:在操作回合中无论点击我军还是敌军,都会出现亮格和暗格,传入的action=0;在敌军回合敌军要寻找行动目标,action=8;敌军判断是否能到达目标,action=2;甚至行动价值判断也要依靠这个函数进行路径搜索,action=4;不同的敌军AI导致传入的其他值还有区别。这些都有个共通点就是都有搜索路径的步骤,一段代码多处使用,把这些通同的部分整合成为一个函数是合理的,但这些过程各自有差别的部分也一起整合进了这个函数,这样糅合的方式注定让这个函数变得晦涩难懂,而且让其中的测试难度增大了。不过也有小概率是程序员编写的时候其实是分开写的,但编译时程序复用其中的通用部分代码并整合到了一起。

我等一下把这个函数的部分原始代码新开一贴发出来,里面的注解可能不算多但应该能看的懂,主要就是与搜索移动范围+攻击范围内相关的,你可以初步研究一下其中路径搜索的部分,如果有能力你应该能用上述的算法解释《三国志英杰传》移动力还有贴吧上发现【ZoC-Bug】的“次级”表现里的几个图片描述的场景。
顶部
性别:未知-离线 clubjack

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 11
编号 544597
注册 2021-5-11


发表于 2025-2-8 17:32 资料 短消息 只看该作者
回复 #4 漫漫苦短 的帖子

看到这一贴, 我的心情同样溢于言表! 看来真的很有必要入门一下汇编语言, 同时认真拜读一下兄台近期的技术贴.

说来惭愧, 小时第一次 (好像也是唯一一次) 通关, 啥也不懂, 靠着各种丢炸弹通了关. 前些年见识到了各种高端玩法, 重新捡起了英杰传, 但已不似年少时有大把自由时间, 至今尚未通关 N99. 最近一年参考月亮的全兵种 2699 开始重打, 也只刚打到了长坂坡. 鉴于人选和走位已经不需要动太多脑子, 乐趣重在“打谱”时的理解和感悟, 以及对少量关卡的精益求精 (虽然影响不到大局).

但正如亚马逊雨林中的蝴蝶扇动了两下翅膀, 少量人员经验的微量增加可能会在不经意的某个回合导致敌方走位大变, 不得不对整关的走位和顺序进行微调. 在理解游戏数值和机制设计的基础上, 找到可行或最优解的过程着实令人激动. 而反复 SL 过程中, 不符合预期的敌方行动和概率也让人百般费解.

相信在漫漫兄的技术贴加持下, 能给英杰传的微观机制描述画上一个圆满的句号. 与此同时, 几位前辈关于整个游戏的 planning 也愈加完善. 感觉英杰传即将在近年迎来真正的技术巅峰~
顶部
性别:男-离线 阿尔法孝直
(雀力日进)

闽国公
遂安军节度使
★★★★★★

Rank: 19Rank: 19Rank: 19Rank: 19
柱国(正二品) 轩辕春秋年度最佳(游戏人生区)
组别 节度使
级别 卫将军
好贴 2
功绩 1796
帖子 6037
编号 19070
注册 2004-10-16
家族 轩辕雀党


发表于 2025-2-9 03:36 资料 个人空间 短消息 只看该作者 QQ
回复 #4 漫漫苦短 的帖子

这几年的研究重心都在SFC的英杰传上,我才发现我已经好久没关注DOS英杰传了。
不过我有一件事儿比较好奇,就是英杰传有ZoC-Bug,孔明传似乎没有。那么将英杰传的什么地方改掉可以消除ZoC,或者说将孔明传的什么地方改掉可以出现ZoC?
(注:在IDA中,DOS英杰传的sub_38AD2函数对应孔明传的sub_41AF49函数,都是移动范围相关的算法,传入参数也比较相似)
顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-9 10:09 资料 短消息 只看该作者
回复 #5 clubjack #6 阿尔法孝直 的帖子

关于sub_38AD2函数的大部分代码,我已经发出来了,孝直前辈应该能看出来这是个广度优先搜索(BFS)算法,重点研究这段就行,clubjack如果有什么看不懂的可以回帖让我解释一下,孝直应该对此就不陌生了。

QUOTE:
英杰传有ZoC-Bug,孔明传似乎没有。那么将英杰传的什么地方改掉可以消除ZoC,或者说将孔明传的什么地方改掉可以出现ZoC?

孔明传的sub_41AF49函数孝直能不能发出来它的反汇编代码,我还没有看过孔明传的反汇编代码,也没有怎么玩过孔明传,如果说孔明传是英杰传的后续版本,对这种函数进行过优化,可能确实能解决英杰传的ZoC-Bug。但根据我的分析,英杰传的ZoC-Bug其实可以分为下面4个现象:

ZoC-Ignore: 就是本文提到的,部队在原地时忽略敌军,从而在action=8的时候找不到原地的敌军

ZoC-Stop: 其实就算是一个ZoC一个规则而已,但是使得英杰传出现了ZoC-End这个Bug。其实就相当于周瑜heyou他们的停止格的理论,但是从停止格到受限格的解释恕我直言我不是很能看得懂,我想以他们的理论并不能很好地解释ZoC-End和ZoC-Skip

ZoC-End: 提示一下,All(M) Stop => End

ZoC-Skip: 几乎没人知道这也是英杰传的一个ZoC-Bug,M => M+2(Skip M+1),当我分析出这个Bug,我顿时豁然开朗,加上这个规则已经能解释英杰传的路径算法中所有的已知Bug了,甚至我不敢保证英杰传的其他系列(曹操传孔明传等等)也解决了这个Bug,因为这个Bug很隐秘,几乎所有人都有可能在这个Bug出现的这个代码的地方忽略这段语句的错误,而这个Bug正是导致了发现【ZoC-Bug】的“次级”表现里第11楼出现的诡异现象

我之所以只写出了这段代码的浅层解释而没有写出其中深度分析是因为我希望能有人在看懂之后用他们的语言描述来分析这段代码,这样我能确信有人真的能看懂,也算是能与我一起完成了这段代码的分析总结,这也算是一种共同创作,毕竟众人拾柴火焰高。

[ 本帖最后由 漫漫苦短 于 2025-2-9 10:46 编辑 ]
顶部
性别:男-离线 阿尔法孝直
(雀力日进)

闽国公
遂安军节度使
★★★★★★

Rank: 19Rank: 19Rank: 19Rank: 19
柱国(正二品) 轩辕春秋年度最佳(游戏人生区)
组别 节度使
级别 卫将军
好贴 2
功绩 1796
帖子 6037
编号 19070
注册 2004-10-16
家族 轩辕雀党


发表于 2025-2-9 14:03 资料 个人空间 短消息 只看该作者 QQ
回复 #7 漫漫苦短 的帖子

这个是孔明传的IDA逆向工程,需要使用IDA 5.5打开,低于此版本可能打不开,高于此版本打开可能有BUG。IDA 5.5程序有点儿大(100多兆),这里附件塞不下。
打开之后,定位到sub_41AF49函数,就可以看到汇编代码。
由于孔明传是32位程序,在用IDA打开函数看到汇编代码之后,按F5可以反汇编成C语言。

直接在这里贴汇编代码恐怕不可取,因为太长了。


附件: EKD2W95.rar (2025-2-9 14:03, 883.21 K)
该附件被下载次数 14
顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-9 15:57 资料 短消息 只看该作者
回复 #8 阿尔法孝直 的帖子

我是用IDA8.3版本打开的,而且也找到了sub_41AF49函数,不过可能你文件里分析由于版本不同我看不到了,不过也能用F5或Tab(16位程序没法是真的痛苦啊),在看到了其中C语言就大致明白了,还一眼辨认出了sub_38AD2和sub_41AF49其中相同之处。

for ( j = 0; v17 && a3 > (int)j; j = v18 )
  {
    v17 = 0;
    v18 = -1;
    v24 = -1;
    for ( k = 0; (unsigned __int8)byte_46FCC9 > k; ++k )
    {
      for ( m = 0; (unsigned __int8)byte_46FCC8 > m; ++m )
      {
        v13 = *(_BYTE *)(m + k * (unsigned __int8)byte_46FCC8 + v20);
        if ( v13 > (int)j && v13 != 255 && v13 < (int)v24 )
          v24 = *(_BYTE *)(m + k * (unsigned __int8)byte_46FCC8 + v20);

这段很明显就是sub_38AD2里的那个三重循环,a3是最大移动力,byte_46FCC9是高,byte_46FCC8是宽,v17与var_1E作用类似,v18与var_8作用类似。
v24与英杰传不同,它应该优化了Zoc-Skip的问题,v24在一轮循环后的最终值是所有点里大于j中的最小值,就不会出来有点漏过搜索的问题,不过反汇编好像还不够智能。直接v24 = v13不就行了。

            for ( n = 0; n < 4; ++n )
            {
              v9 = sub_419655(v31, n);
              sub_401890(v9);
              if ( (unsigned __int8)v27[0] != 255 )
              {
                v19 = (unsigned __int8)v27[0] + (unsigned __int8)v27[1] * (unsigned __int8)byte_46FCC8;

这段是sub_38AD2里的探索四个方向的新点再保存到v27里,和sub_38AD2保存到var_1A应该是相似的。

                  v12 = sub_4195DA(v27);
                  v11 = sub_42A2D9(v12);
                  if ( v11 != 255
                    && v11 + v13 <= a3
                    && (*v29 == 255 || sub_409220((char *)dword_46C098 + 23 * *v29) == v25) )
                  {
                    *v32 = v11 + v13;
                    v17 = 1;

这段就是判断如何新点是否能到底以及是不是不超过a3,v11就是新点的移动消耗,判断v11 + v13 <= a3也是一样的,更新v17就是英杰传的sub_38AD2更新var_1E,真是浪费,一个变量就不停在0和1之间改。

                    if ( *v32 < (int)v18 )
                    {
                      if ( !a7 || v26 )
                      {
                        v18 = *v32;
                      }
                      else if ( !sub_41922C(v27, v25) )
                      {
                        v18 = *v32;
                        if ( v24 < (int)*v32 )
                          v18 = v24;
                      }
                    }

v18由于有v24的辅助,不会出现sub_38AD2的var_8问题了。

                    if ( (a4 & 8) != 0 && *(unsigned __int8 *)(v19 + sub_447720(0, 0, 4)) != 255 )
                    {
                      sub_447720(0, 0, 4);
                      if ( sub_409360(32) )
                        return *(_BYTE *)(v19 + sub_447720(0, 0, 4));
                    }
                    if ( (a4 & 2) != 0 && sub_41E280(v27, v34) )
                      return 1;
                    if ( (a4 & 4) != 0 )
                      sub_41D148(v27);

这也太明显了,连test a4, 8到test a4, 2到test a4, 4的顺序都是一样的。

sub_41AF49应该修正了Zoc-Bug,太不容易了,这个实习生写的代码终于有同事帮忙改了。。
只用了一点点时间分析,如果有什么错的还希望你能指正一下。
顶部
性别:男-离线 阿尔法孝直
(雀力日进)

闽国公
遂安军节度使
★★★★★★

Rank: 19Rank: 19Rank: 19Rank: 19
柱国(正二品) 轩辕春秋年度最佳(游戏人生区)
组别 节度使
级别 卫将军
好贴 2
功绩 1796
帖子 6037
编号 19070
注册 2004-10-16
家族 轩辕雀党


发表于 2025-2-10 01:10 资料 个人空间 短消息 只看该作者 QQ
回复 #9 漫漫苦短 的帖子

似乎确实如此。
-------------------------------------
另外关于楼主的问题我想到一件事儿,大家有没有印象,就是说,在长坂坡之战I,通常刘备要去占村庄,然后三个民众会围着刘备转圈圈。那么为什么会转圈圈而不是都不动?会不会也是和楼主描述的问题一样?
顶部

正在浏览此帖的会员 - 共 1 人在线




当前时区 GMT+8, 现在时间是 2025-3-14 04:12
京ICP备2023018092号 轩辕春秋 2003-2023 www.xycq.org.cn

Powered by Discuz! 5.0.0 2001-2006 Comsenz Inc.
Processed in 0.014155 second(s), 10 queries , Gzip enabled

清除 Cookies - 联系我们 - 轩辕春秋 - Archiver - WAP