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

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


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

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

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

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


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

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 48
编号 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】的“次级”表现里的几个图片描述的场景。


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

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 48
编号 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: 1
组别 百姓
级别 在野武将
功绩 0
帖子 48
编号 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,太不容易了,这个实习生写的代码终于有同事帮忙改了。。
只用了一点点时间分析,如果有什么错的还希望你能指正一下。
顶部

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




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

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

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