编写辐射硬化的辐照器


17

任务是编写辐射硬化的辐射器。我到底是什么意思?

辐照器是一个程序,当给定一个字符串作为输入时,它将输出该字符串的所有可能版本,并删除一个字符。例如,给定input Hello, world!,程序应输出:

ello, world!
Hllo, world!
Helo, world!
Helo, world!
Hell, world!
Hello world!
Hello,world!
Hello, orld!
Hello, wrld!
Hello, wold!
Hello, word!
Hello, worl!
Hello, world

但是,必须保护辐照器免受其辐射,因此您编写的辐照器在穿过自身时也必须生存。也就是说,当删除程序的任何单个字节时,程序仍必须正常运行。

测试用例

abc -> bc; ac; ab
foo bar -> oo bar:fo bar:fo bar:foobar:foo ar:foo br:foo ba
source -> ource;surce;sorce;souce;soure;sourc;

技术指标

  • 您可以按照我们的标准I / O规则通过任何可接受的方法进行输入
  • 输出可以是字符串列表,也可以是由一个字符或一组字符分隔的打印列表。尾随定界符是可以接受的
  • 输出包含任何可能的版本,可以按任何顺序排列
  • 重复的条目(例如Helo, world!第一个示例中的两个)可以被过滤掉,但这不是必需的
  • 因为这是,所以最小的程序(以字节为单位)将获胜

...或者逗号?
乔纳森·艾伦,

4
这个真的很喜欢高尔夫语言,因为vin中的C程序void不会被编译
Krzysztof Szewczyk

3
@Krzysztof tbh我认为,即使不是全部,大多数实用语言也无法幸免于辐射强化,因为它们的冗长和语法。不仅是这个挑战,而且还有所有RH挑战。
Shieru Asakoto

Answers:


13

05AB1E29 26字节

æIg<ùˆ\æIg<ùˆ\æIg<ùˆ¯¯{Å`s

在线尝试!,或尝试所有辐照版本

我能找到的最短的辐射器是5个字节:

æ        # powerset of the input
 Ig      # length of the input
   <     # - 1
    ù    # elements of a with length b

想法是重复3次,然后进行多数表决:

æIg<ù         # irradiate
     ˆ        # add the result to the global array
      \       # pop (in case the above instruction gets irradiated)
æIg<ùˆ\       # idem
æIg<ùˆ        # no pop, it's okay to dirty the stack at this point
¯             # push global array
 ¯            # and again, so at least one goes through
  {           # sort
   Å          # conveniently ignored by the parser
    `         # dump
     s        # swap
              # and implicitly output

Å是2字节命令的前缀,但是没有Å`命令,这就是为什么Å忽略get的原因。不过,稍后我们将需要它。

排序可确保多数票位于数组的中间。转储然后交换使该值到达堆栈的顶部。

初始部分的任何辐照只会导致全局阵列出现错误,而该错误可通过多数表决解决。最后{Å`s一点的辐射很难解释:

  • Å 无论如何都会被忽略,所以可以辐照它

  • 如果照射了反引号,则Å`s变为Ås,这是“获取数组中间”扩展命令。

  • 如果{或被s照射,则意味着没有其他东西,因此全局数组是相同值的三倍。在这种情况下,我们不需要排序/交换,任何值都可以使用。


3
非常令人印象深刻!我认为我不会在RH挑战中看到05AB1E答案。我会立即提供赏金来奖励该答案(我猜想还会给这个挑战更多的机会)。您打了很多我的答案,所以您也应该为自己赢得这些荣誉!:)
Kevin Cruijssen

3
实际上,我以前曾在RH挑战中看到05AB1E答案。仍然,非常令人印象深刻!
凯文·克鲁伊森

5

8086机器代码(MS-DOS .COM),83字节

可在DOSBox或您最喜欢的蒸汽动力计算引擎中运行。要照射的字符串作为命令行参数给出。

二进制:

00000000 : EB 28 28 8A 0E 80 00 49 BD 83 00 B4 02 51 8A 0E : .((....I.....Q..
00000010 : 80 00 BE 82 00 AC 39 EE 74 04 88 C2 CD 21 E2 F5 : ......9.t....!..
00000020 : 59 45 B2 0A CD 21 E2 E5 C3 90 EB D7 D7 8A 0E 80 : YE...!..........
00000030 : 00 49 BD 83 00 B4 02 51 8A 0E 80 00 BE 82 00 AC : .I.....Q........
00000040 : 39 EE 74 04 88 C2 CD 21 E2 F5 59 45 B2 0A CD 21 : 9.t....!..YE...!
00000050 : E2 E5 C3                                        : ...

可读性:

cpu 8086
org 0x100
    jmp part2
    db 0x28

part1:
    mov cl, [0x80]
    dec cx
    mov bp, 0x83
    mov ah, 0x02

.l:
    push cx
    mov cl, [0x80]
    mov si, 0x82
.k:
    lodsb
    cmp si, bp
    je .skip
    mov dl, al
    int 0x21
.skip:
    loop .k
    pop cx
    inc bp
    mov dl, 10
    int 0x21
    loop .l
    ret

    nop
part2:
    jmp part1
    db 0xd7
    mov cl, [0x80]
    dec cx
    mov bp, 0x83
    mov ah, 0x02

.l:
    push cx
    mov cl, [0x80]
    mov si, 0x82
.k:
    lodsb
    cmp si, bp
    je .skip
    mov dl, al
    int 0x21
.skip:
    loop .k
    pop cx
    inc bp
    mov dl, 10
    int 0x21
    loop .l
    ret

撞倒

活动部分是重复的,因此始终不会受到辐射的影响。我们通过跳跃选择健康的版本。每次跳转都是一次短跳转,因此只有两个字节长,其中第二个字节是位移(即跳转距离,由符号确定方向)。

我们可以将代码分为四个可以照射的部分:跳转1,代码1,跳转2和代码2。其思想是确保始终使用干净的代码部分。如果照射了其中一个代码部分,则必须选择另一个代码部分,但是如果照射了其中一个跳转,则两个代码部分都将是干净的,因此选择哪个代码部分都没有关系。

具有两个跳跃部分的原因是通过跳过第一部分来检测辐射。如果第一个代码部分受到照射,则意味着我们将比标记少一个字节。如果我们确保这样的错误着陆选择了代码2,而正确的着陆选择了代码1,那么我们就是黄金。

对于两次跳转,我们都复制位移字节,使每个跳转部分长3个字节。这确保了最后两个字节之一中的照射仍将使跳转有效。因为最后两个字节将形成完全不同的指令,所以在第一个字节中进行辐照将完全停止跳转。

进行第一跳:

EB 28 28        jmp +0x28 / db 0x28

如果两个0x28字节中的任何一个被删除,它仍将跳到同一位置。如果0xEB删除了字节,我们将最终得到

28 28           sub [bx + si], ch

这是在MS-DOS上的良性指令(其他口味可能会不同),然后我们进入代码1,该代码必须是干净的,因为损坏在跳转1中。

如果进行了跳跃,我们将降落在第二次跳跃:

EB D7 D7        jmp -0x29 / db 0xd7

如果此字节序列完好无损,并且我们正好位于标记上,则意味着代码1是干净的,并且该指令跳回到了该部分。即使这些移位字节之一被损坏,重复的移位字节也可以保证这一点。如果我们要么掉一个字节(由于损坏的代码1或跳转1),要么该0xEB字节是损坏的字节,那么剩下的两个字节在这里也是良性的:

D7 D7           xlatb / xlatb

无论哪种情况,如果最终执行这两条指令,我们都知道照射了跳转1,代码1或跳转2,这使代码2的安全下降。

测试中

使用以下程序自动创建.COM文件的所有版本。它还创建可以在目标环境中运行的BAT文件,该文件运行每个照射的二进制文件,并将其输出通过管道传输到单独的文本文件。比较输出文件以进行验证很容易,但是DOSBox没有fc,因此没有将其添加到BAT文件中。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    FILE *fin, *fout, *fbat;
    int fsize;
    char *data;

    if (!(fin = fopen(argv[1], "rb")))
    {
        fprintf(stderr, "Could not open input file \"%s\".\n", argv[1]);
        exit(1);
    }

    if (!(fbat = fopen("tester.bat", "w")))
    {
        fprintf(stderr, "Could not create BAT test file.\n");
        exit(2);
    }

    fseek(fin, 0L, SEEK_END);
    fsize = ftell(fin);
    fseek(fin, 0L, SEEK_SET);

    if (!(data = malloc(fsize)))
    {
        fprintf(stderr, "Could not allocate memory.\n");
        exit(3);
    }

    fread(data, 1, fsize, fin);

    fprintf(fbat, "@echo off\n");

    for (int i = 0; i < fsize; i++)
    {
        char fname[512];

        sprintf(fname, "%03d.com", i);
        fprintf(fbat, "%s Hello, world! > %03d.txt\n", fname, i);

        fout = fopen(fname, "wb");

        fwrite(data, 1, i, fout);
        fwrite(data + i + 1, 1, fsize - i - 1, fout);

        fclose(fout);
    }

    free(data);
    fclose(fin);
    fclose(fbat);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.