复制器(第二部分)

嗨,很高兴再次见到你。

上次我们讨论了基本的复制器概念,现在我将尝试讲述一些高级主题。 首先,让我们来看看另一个复制引擎,在我看来这是最好的一个,最初由 Jippo Pohjalainen 在其战士Timescape中提出。我们稍作简化地报告一下,这是 Nandor 和 Stefan 在比赛中提出的“白战士(White warrior)”策略。

warrior
        spl     1,              <-200
        mov.i   -1,             0           ;这个块生成6个进程
        spl     1,              <-300

tim2    spl     @tim2, }TSTEP
tim2a   mov.i   }tim2,  >tim2

cel2    spl     @cel2,  }CSTEP  ;这四行是主体
cel2a   mov.i   }cel2,  >cel2
                                ;在这里,你可以插入一些轰炸行
ncl2a   mov.i   {cel2,  <ncl2
ncl2    jmp     @ncl2,  >NSTEP

在阅读了第一部分后,大家应该知道前四行是如何工作的:它们会分裂并复制战士的身体,而进程将在那里执行。值得注意的是,cel2cel2a这两行并不是从一开始就复制战士,而是在ncl2之后,在底部复制两行空白行。 由于前缀减量,第ncl2a行再次从cel2向后复制到ncl2+2,而最后一行则跳转到此次复制的开头,重置指针。 这种结构的主要优点是,所有代码只执行一次,从而诱骗扫描器;与第一种提示的旧结构相比,这是一个很大的优势。另一个优点是,即使战士在最后两行被炸弹炸伤,它也会继续缓慢工作。 在 Paul Kline 创作Die Hard之前,这个家伙是最难杀死的。基于这种结构,后来又出现了一些成功的模仿者,值得一提的是 Mike Nonemacher 的Nobody special和……我创作的Marcia Trionfale

现在我们有了一个坚实的结构可以着手改进,为了使其更具杀伤力,我们可以添加一些其他形式的攻击,而不是简单地覆盖我们的对手。原始的Timescapecel2a之后插入了这条轰炸线:

        mov.i   <-FSTEP,{FSTEP

它是如何工作的呢?记住,我们有一些并行工作的进程:每个进程都会获取一个单元 -FSTEP,将其 b 字段递减,然后获取它所指向的单元,并将其移动到FSTEP单元中递减后的 a 字段所指向的位置。简单吗?不!:-) 好的。从头开始:

        dat     0,0
-FSTEP  dat     0,0             ;将变成 dat 0,-1
...
        mov.i   <-FSTEP,{FSTEP  ;我们在这
...
begin   mov     bomb,   nearme
...     [enemy code]            ;我们的敌人在这,真幸运 :-)

end     jmp     begin
        dat     0,0
FSTEP   dat     0,0             ;将变成 dat -1,0

现在,第一个进程获取单元-FSTEP并递减其 b 字段,然后获取递减后的 b 字段所指向的单元(在本例中为前一个单元)并移动它;移动到哪里?它获取单元FSTEP并递减其 a 字段,然后获取它所指向的单元,这里它击中了 (译注:不解,看起来没击中)。没击中?别担心,我们有第二个进程获取单元-FSTEP-2并将其移动到FSTEP-2的位置,以此类推,直到我们的进程执行完轰炸线。最后,敌人消失了,至少在本例中是这样。

轰炸不仅有助于消灭我们的敌人,也有助于消灭我们自己……没错,敌人的扫描器会恶意用大量 spl 0 和类似的有害物质覆盖我们脆弱的复制体。这些炸弹不会直接杀死我们,但会让我们产生无用的进程,从而减缓我们的传播速度。如果我们用数据轰炸那些有可能被感染的旧副本,就能减轻这种影响;如果我们碰巧击中了好的副本,也不用担心,我们数量众多,能承受一些损失。

其他战士使用不同类型的炸弹,这些炸弹在杀敌方面更为有效,但缺点是我们必须随身携带炸弹。轰炸行将会变成:

        mov     bomb,   <target ;or > or { or }

现在,首先布设的炸弹将成为后续地毯式布设的指向标。 最常用的炸弹是反 imp 炸弹

        dat     <-2666, <2667

这枚炸弹非常擅长消灭三段式的 imp 环,否则它很难被复制者杀死。 在Jack in the Box中,我使用的另一个比较成功的炸弹是下面这个简单的:

        dat     1,      1

这个炸弹针对的是djn流(djn streams)和前进式清除,这是纸的敌人们常用的两种攻击方式。对流的影响是使流程脱离循环,浪费时间;对前进式清除的影响则是致命的,看看一个简单的前进式清除就知道了

gate    dat     100,    1000    ;这个清除正在1000单元外运行
....
clr     mov     bomb,   >gate   ;bomb是什么不重要,但是一定没有
        jmp     clr             ;一个为1的b字段

如果我们用dat 1,1击中gate,清除程序就会开始在自己的内部运行,直到它到达clr行并自我销毁,非常有效,也非常有趣:-)

与石头和扫描器的轰炸/扫描步长一样,扩散常数可以决定一个战士的好坏。你必须选择合适的扩散常数,以确保核心中的副本能够良好扩散。Jay Han 的Corestep.c和 Stefan Strack 的Mopt可在 FTP 站点获取,它们可以作为你的起点,但对于复制器来说,这项工作要复杂得多,因为它们在扩散过程中会改变自己的常数;让我举个例子来说明,同样的结构,4 个并行过程:

a       spl     @0,     100
b       mov     }-1,    >-1
c       mov     {d,     <d
d       jmp     @0,     >1000

第一次执行 a-b 行时,它们会进行拆分并复制 100 个位置,但当执行 c-d 行复制它们时,b 字段的值变为 104,以此类推。我不知道任何数学方法或优化程序来找到最佳值,我只是通过使用 pmarsv 来观察会发生什么。如果我注意到模块分布不均,我就会进行一些调整,等等,这更多是艺术而非科学。在我目前正在使用的复制器中,我对第一个常数使用 200 取模(100 到 400 之间的任何值都可以),对第二个常数使用 20/40 取模,对最后一个常数……我就凭感觉了:-)。Stefan Strack 建议使用 Pmars 宏来至少部分自动化搜索过程;他是这么说的:


优化常量的一个更好方法是使用 pmars 运行你的战士程序,并使用 cdb 宏来更改代码段并记录结果。假设我们想要优化 T.Hsu 的Ryooki的一个稍微“未优化”的版本:

nxt_paper equ 100 ;有待改进的选择

boot_paper spl 1 ,>4000
mov.i -1,#0
mov.i -1,#0

paper spl @paper,<nxt_paper ; A字段为源,B字段为目标
copy mov.i }paper,>paper
mov.i bomb ,>paper ; 反imp
mov.i bomb ,}800 ; 反吸血鬼(vampire)
jmn.f @copy ,{paper
bomb dat <2667 ,<2667\*2

我们希望找到一个比 nxt_paper EQU 中的“100”更好的纸副本之间的偏移量。首先,我们需要想出一些好办法来测量核心中纸张主体之间的均匀分布。以下是 cdb 可以轻松提供的一个近似值:

经过几千次循环后,具有良好的偏移量的纸张

  1. 拥有更多进程
  2. 覆盖更多核心位置

相对于具有不好的偏移量的纸张

现在的思路是进行多轮操作,在每轮开始时系统地改变丝绸的偏移量,并在大约 5000 个周期后让 cdb 报告流程编号和覆盖的核心位置数量。这些都可以通过宏指令实现自动化,这样你就可以一边喝咖啡(来一杯?:)一边让 pmars 找到最优常数。一旦你有了几个候选偏移量,你应该通过查看核心显示来确保它们按预期工作。然后,你可以用几乎相同的方式为你的最优偏移量集找到最优轰炸常数。以上面使用Ryooki为例:

pmars -br 1000 -e ryooki.red
00000 SPL.B $ 1, > 4000
(cdb) 0,7
00000 SPL.B $ 1, > 4000
00001 MOV.I $ -1, # 0
00002 MOV.I $ -1, # 0
00003 SPL.B @ 0, < 100
00004 MOV.I } -1, > -1
00005 MOV.I $ 3, > -2
00006 MOV.I $ 2, } 800
00007 JMN.F @ -3, { -4
(cdb) calc i=99
99

这将变量“i”设置为我们的起始常数。

(cdb)@ed 3~spl @0,<i=i+1~@sk 5000~@pq~ca i,$+1~@pq off~m count~@go~@st
100,987
1830
(cdb)

这有点复杂。”@ed 3~spl @0,<i=i+1“序列编辑地址 3,并向其写入指令”SPL @ 0, < 100“,同时将“i”变量加 1。”@sk 5000“会静默执行 5000 个周期,然后”@pq“会切换到“进程队列”显示/编辑模式。”calc i,$+1“会回显“i”变量的当前值,后跟进程数量(“$”表示最后一个进程的编号)。输出结果见下一行:”100,987“。然后”@pq off“会切换回核心显示/编辑模式。”macro count“会执行一个已在 pmars.mac 中定义的宏;”count“宏会回显其中包含除“dat 0,0”以外的任何内容的核心位置数量(此处为 1830)。最后,”@go~@st“会跳到本轮结束,并进入下一轮的第一个周期。

现在,当您按下Enter键时,命令序列将以偏移值 101 重复执行:

(cdb) <Enter>
101,1058
1971
(cdb)

偏移量为 101 时,会生成更多进程(1058 个)并写入更多地址(1971 个)。如果你想让整个过程自动化运行,只需将命令序列包含在循环中(!!~...~!),并将结果发送到文件中,如下所示:

(cdb) ca i=99
99
(cdb) write ryooki.opt
Opening logfile
(cdb) !!~&ed 3~spl @0,<i=i+1~&sk 5000~&pq~ca i,$+1~&pq off~m count~&go~&st~!

为了避免向日志文件发送大量无用输出,我们必须在 pmars.mac 中的这个宏和宏计数中使用&而不是@;只需进行编辑即可。

count= &ca z=.~m w?~&ca x=.,c=0~!!~m w?~&ca c=c+1~if .!=x~!~ca c~&l z
w?= &search ,

如果#processes/locations 的值比迄今为止的任何值都大,你可以通过仅回显这些值来使操作变得更为复杂(这留给读者作为练习),但此时,你可能已经准备好通过定义自己的宏来节省一些打字时间。请记住,你可以使用”@macro ,user”命令(简写为”m=”)在 cdb 会话中添加宏。你甚至可以用一个更复杂的宏来替换对#processes/locations 的简单检查,该宏可以计算纸张之间间隔的方差。


现在,我们已经准备好开始制作纸战士了,我们要做的就是将各个部分组合起来并开始工作。

首先是结构,我们将制作一个中型战士模型,有 8 行,所以我们需要 8 个流程。

start spl 1, <300 ;所以我们需要制造8个并行进程
spl 1, <400 ;不需要<###来使其工作
spl 1, <500 ;但它可能会损坏某些东西,对我们却不会造成任何损失

silk spl @0, {dest0
mov.i }-1, >-1
silk1 spl @0, <dest1
mov.i }-1, >-1
mov.i bomba, }range
mov {silk1, <silk2
silk2 jmp @0, >dest2
bomba dat <2667, <1

现在来看常量:dest0 使用较少,我们取模 200 的值;对于 dest1,我们取模 20 的值。现在,我们开始使用 Stefan 方法进行优化。我的电脑速度较慢,所以我选择分析-2000 到-1000 之间的值。在这么做之前,我将 mov bomb 指令行更改为 nop 指令,稍后再进行优化。

运行 Stefan 的宏程序,我得到的最佳值为-1278。

然后我用一条 mov 指令替换了 nop 指令,并再次运行宏,选择 500 到 1000 之间的轰炸范围。最佳值为 933

我在战士模型中输入了数值,并将其提交到 94 hill:得分:125.98。还不错,比手工制作的模型要稍微好一些。

为了让您在这里玩得开心,我提供了可以试玩的代码。

山上的男孩们,准备好你们的扫描器。他们来了:-)

;redcode-94
;name paper01o
;author Beppe Bezzi
;strategy 纸模块,已使用pmars进行部分优化

;assert CORESIZE == 8000

dest0 equ 2200
dest1 equ 3740
dest2 equ -1278 ;pmars optimized
range equ 933 ;pmars optimized

paper
spl 1, <300 ;\
 spl 1, <400 ;-> 生成8个连续的进程
spl 1, <500 ;/

silk spl @0, {dest0
mov.i }-1, >-1
silk1 spl @0, <dest1
mov.i }-1, >-1
mov.i bomba, }range
mov {silk1, <silk2
silk2 jmp @0, >dest2
bomba dat <2667, <1

end paper

对于下一个 hint,我想听听你对将要讨论的论点的看法;我首先选择的是 p-space,其次是轰炸机,这两个论点我至少有一些了解,也培养了一些成功的战士,但我希望听听你的意见。