Escribir un irradiador endurecido por radiación

17

La tarea es escribir un irradiador endurecido por radiación. ¿Qué quiero decir con eso exactamente?

Un irradiador es un programa que, cuando se le asigna una cadena como entrada, generará todas las versiones posibles de la cadena con un carácter eliminado. Por ejemplo, dada la entrada Hello, world!, el programa debería generar:

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

Sin embargo, un irradiador debe protegerse de su radiación, por lo que el irradiador que usted escriba también debe sobrevivir cuando se transforme. Es decir, cuando se elimina cualquier byte de su programa, el programa aún debe funcionar correctamente.

Casos de prueba

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;

Especificaciones

  • Puede recibir información por cualquier método aceptable según nuestras reglas estándar de E / S
  • La salida puede ser una lista de cadenas o una lista impresa delimitada por un carácter o grupo de caracteres. Un delimitador final es aceptable
  • La salida puede estar en cualquier orden siempre que contenga todas las versiones posibles
  • Las entradas duplicadas (como las dos Helo, world!s en el primer ejemplo) pueden filtrarse, pero esto no es necesario
  • Como se trata de , el programa más pequeño, en bytes, gana
TheOnlyMrCat
fuente
... o coma quizás?
Jonathan Allan
44
este realmente está favoreciendo los lenguajes de golf, porque el programa C con vin voideliminado no compilará
Krzysztof Szewczyk
3
@ Krzysztof tbh Creo que la mayoría, si no todos los lenguajes prácticos, no sobrevivirán al endurecimiento por radiación debido a la verbosidad y las sintaxis. No solo este desafío, sino TODOS los desafíos de HR.
Shieru Asakoto

Respuestas:

13

05AB1E , 29 26 bytes

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

Pruébalo en línea! o pruebe todas las versiones irradiadas .

El irradiador más corto que pude encontrar es de 5 bytes:

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

La idea es repetir eso 3 veces, luego votar por mayoría:

æ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

Åes un prefijo para comandos de 2 bytes, pero no hay ningún Å`comando, por lo que Åse ignora. Sin embargo, lo necesitaremos más tarde.

La clasificación asegura que el voto mayoritario esté en el medio de la matriz. Volcar y luego intercambiar lleva ese valor a la parte superior de la pila.

Cualquier irradiación en la parte inicial solo da como resultado un error en la matriz global, que se resuelve con el voto de la mayoría. Las irradiaciones en la parte final {Å`sson mucho más difíciles de razonar sobre:

  • Å se ignora de todos modos, así que está bien irradiarlo

  • Si se irradia el backtick, se Å`sconvierte en el Åscomando extendido "get middle of the array".

  • Si están irradiados {o sno, eso significa que nada más lo está, por lo que la matriz global tiene el mismo valor tres veces. En ese caso no necesitamos ordenar / intercambiar, cualquier valor funcionará.

Mugriento
fuente
3
¡Muy impresionante! No pensé que vería una respuesta 05AB1E en un desafío de HR. Agregaré una recompensa para recompensar esta respuesta (y supongo que daré al desafío un poco más de exposición también) de inmediato. Has jugado tantas respuestas mías, ¡así que también mereces un montón de crédito por esas! :)
Kevin Cruijssen
3
En realidad, he visto respuestas 05AB1E en un desafío de RH antes . Aún así, muy impresionante!
Kevin Cruijssen
5

Código de máquina 8086 (MS-DOS .COM), 83 bytes

Ejecutable en DOSBox o su motor informático de vapor favorito. La cadena a irradiar se proporciona como un argumento de línea de comando.

Binario:

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                                        : ...

Legible:

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

En mal estado

La parte activa se duplica para que siempre haya una que no haya sido tocada por la radiación. Seleccionamos la versión saludable a modo de saltos. Cada salto es un salto corto, por lo que tiene solo dos bytes de longitud, donde el segundo byte es el desplazamiento (es decir, la distancia al salto, con el signo que determina la dirección).

Podemos dividir el código en cuatro partes que podrían irradiarse: salto 1, código 1, salto 2 y código 2. La idea es asegurarse de que siempre se use una parte limpia del código. Si se irradia una de las partes del código, se debe elegir la otra, pero si se irradia uno de los saltos, ambas partes del código estarán limpias, por lo que no importa cuál se elija.

La razón para tener dos partes de salto es detectar la irradiación en la primera parte saltando sobre ella. Si se irradia la primera parte del código, significa que llegaremos un byte fuera de la marca. Si nos aseguramos de que un aterrizaje fallido seleccione el código 2, y un aterrizaje adecuado seleccione el código 1, somos dorados.

Para ambos saltos, duplicamos el byte de desplazamiento, haciendo que cada parte del salto tenga 3 bytes de longitud. Esto asegura que la irradiación en uno de los dos últimos bytes todavía hará que el salto sea válido. La irradiación en el primer byte detendrá el salto, ya que los dos últimos bytes formarán una instrucción completamente diferente.

Da el primer salto:

EB 28 28        jmp +0x28 / db 0x28

Si 0x28se elimina cualquiera de los bytes, seguirá saltando al mismo lugar. Si 0xEBse elimina el byte, terminaremos con

28 28           sub [bx + si], ch

que es una instrucción benigna en MS-DOS (otros sabores pueden estar en desacuerdo), y luego pasamos al código 1, que debe estar limpio, ya que el daño estaba en el salto 1.

Si se da el salto, aterrizamos en el segundo salto:

EB D7 D7        jmp -0x29 / db 0xd7

Si esta secuencia de bytes está intacta, y aterrizamos justo en la marca, eso significa que el código 1 estaba limpio, y esta instrucción salta a esa parte. El byte de desplazamiento duplicado garantiza esto, incluso si uno de estos bytes de desplazamiento está dañado. Si aterrizamos un byte (debido a un código dañado 1 o salto 1) o el 0xEBbyte es el dañado, los dos bytes restantes también serán benignos:

D7 D7           xlatb / xlatb

Cualquiera que sea el caso, si terminamos ejecutando esas dos instrucciones, sabemos que el salto 1, el código 1 o el salto 2 fueron irradiados, lo que hace que la caída al código 2 sea segura.

Pruebas

El siguiente programa se utilizó para crear automáticamente todas las versiones del archivo .COM. También crea un archivo BAT que se puede ejecutar en el entorno de destino, que ejecuta cada binario irradiado y canaliza sus salidas a archivos de texto separados. Comparar los archivos de salida para validar es bastante fácil, pero DOSBox no lo tiene fc, por lo que no se agregó al archivo 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);
}
gastropner
fuente