Perl, 1116 1124 bytes, n = 3, pontuação = 1124 ^ (2/3) ou aproximadamente 108,1
Atualização : verifiquei agora que isso funciona com n = 3 via força bruta (que levou alguns dias); com um programa desse complexo, é difícil verificar a resistência à radiação manualmente (e cometi um erro em uma versão anterior, e é por isso que a contagem de bytes aumentou). Finalizar atualização
Eu recomendo redirecionar o stderr para algum lugar que você não verá; esse programa produz vários avisos sobre sintaxe dúbia, mesmo quando você não está excluindo caracteres.
É possível que o programa possa ser reduzido. Trabalhar nisso é bastante doloroso, facilitando a perda de possíveis micro-otimizações. Meu objetivo era obter o maior número possível de caracteres deletáveis (porque essa é a parte realmente desafiadora do programa), e tratava o tie - break de código-golfe como algo que era bom de se procurar, mas como algo que eu não colocaria esforço ridículo para otimizar (com base em que é muito fácil quebrar a resistência à radiação por acidente).
O programa
Nota: existe um _
caractere de controle literal (ASCII 31) imediatamente antes de cada uma das quatro ocorrências de -+
. Eu não acho que ele copiou e colou corretamente no StackOverflow, portanto, você precisará adicioná-lo novamente antes de executar o programa.
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
A explicação
Esse programa, claramente, é composto de quatro programas menores idênticos concatenados juntos. A idéia básica é que cada cópia do programa verifique se foi danificado demais para ser executado ou não; se foi, não fará nada (além de possivelmente emitir avisos) e permitirá que a próxima cópia seja executada; se não tiver sido (ou seja, nenhuma exclusão ou o caractere que foi excluído não faz diferença na operação do programa), ele fará o que é minucioso (imprimir o código-fonte completo do programa; esse é um quine apropriado, com cada parte contendo uma codificação de todo o código-fonte) e, em seguida, saia (impedindo que outras cópias não danificadas imprimam o código-fonte novamente e, assim, arruinem a solução imprimindo muito texto).
Cada parte, por sua vez, é composta de duas partes que são efetivamente independentes funcionalmente; um invólucro externo e algum código interno. Como tal, podemos considerá-los separadamente.
Invólucro externo
O wrapper externo é, basicamente, eval<+eval<+eval< ... >####>####...>###
(mais um ponto e vírgula e novas linhas cujo objetivo deve ser bastante óbvio; é para garantir que as partes do programa permaneçam separadas, independentemente de alguns pontos e vírgulas ou as novas linhas antes deles serem excluídas ) Isso pode parecer bastante simples, mas é sutil de várias maneiras, e a razão pela qual escolhi Perl para esse desafio.
Primeiro, vamos ver como o wrapper funciona em uma cópia não danificada do programa. eval
analisa como uma função interna, que requer um argumento. Como se espera um argumento, +
aqui está um unário +
(que já será muito familiar para os golfistas de Perl; eles são úteis surpreendentemente com frequência). Ainda estamos esperando um argumento (acabamos de ver um operador unário); portanto, o <
que vem a seguir é interpretado como o início do <>
operador (que não aceita argumentos de prefixo ou postfix e, portanto, pode ser usado na posição de operando).
<>
é um operador bastante estranho. Seu objetivo usual é ler identificadores de arquivo e você coloca o nome do identificador dentro dos colchetes angulares. Como alternativa, se a expressão não é válida como um nome de arquivo, ela ocorre (basicamente, o mesmo processo que os shells do UNIX usam para converter o texto inserido pelo usuário em uma sequência de argumentos da linha de comando; versões muito mais antigas do Perl realmente usavam a casca para isso, mas hoje em dia o Perl lida internamente com o globbing). O uso pretendido, portanto, é <*.c>
semelhante ao de , que normalmente retornaria uma lista como ("foo.c", "bar.c")
. Em um contexto escalar (como o argumento paraeval
), apenas retorna a primeira entrada encontrada na primeira vez que é executada (o equivalente ao primeiro argumento) e retorna outras entradas em execuções hipotéticas futuras que nunca acontecem.
Agora, os shells geralmente lidam com argumentos de linha de comando; se você der algo como -r
sem argumentos, ele será transmitido literalmente ao programa, independentemente de haver ou não um arquivo com esse nome. O Perl age da mesma maneira, desde que asseguremos que não haja caracteres especiais para o shell ou para Perl entre o <
e o correspondente >
, podemos usá-lo efetivamente como uma forma realmente estranha de literal de cadeia de caracteres. Melhor ainda, o analisador de Perl para operadores do tipo cotação tem uma tendência compulsiva a combinar colchetes, mesmo em contextos como este, onde não faz sentido, para que possamos aninhar <>
com segurança (que é a descoberta necessária para que este programa seja possível). A principal desvantagem de todos esses aninhados <>
é que escapar do conteúdo do<>
é quase impossível; parece haver duas camadas de <>
fuga sem escape para cada uma delas ; portanto, para escapar de algo dentro das três, ele precisa ser precedido com 63 barras invertidas. Decidi que, embora o tamanho do código seja apenas uma consideração secundária nesse problema, quase certamente não valia a pena pagar esse tipo de penalidade na minha pontuação, por isso decidi escrever o restante do programa sem usar os caracteres incorretos.
Então, o que acontece se partes do wrapper são excluídas?
- Exclusões na palavra
eval
fazem com que ela se transforme em uma palavra de barra , uma string sem significado. Perl não gosta disso, mas trata-os como se estivessem cercados de aspas; assim eal<+eval<+...
é interpretado como"eal" < +eval<+...
. Isso não tem efeito na operação do programa, porque é basicamente pegar o resultado dos resultados fortemente aninhados (que não usamos de qualquer maneira), convertê-lo em um número inteiro e fazer algumas comparações inúteis. (Esse tipo de coisa causa muitos avisos de spam, pois claramente não é algo útil em circunstâncias normais; estamos apenas usando-o para absorver exclusões.) Isso altera o número de colchetes de ângulo de que precisamos (porque o colchete de abertura agora está sendo interpretado como um operador de comparação), mas a cadeia de comentários no final garante que a sequência termine com segurança, não importa quantas vezes seja aninhada. (Há mais #
sinais do que o estritamente necessário aqui; escrevi como fiz para tornar o programa mais compressível, permitindo que eu use menos dados para armazenar o quine.)
- Se um
<
for excluído, o código agora será analisado como eval(eval<...>)
. O secundário, outside eval
não tem efeito, porque os programas que estamos avaliando não retornam nada que tenha efeitos reais como um programa (se eles retornam normalmente, normalmente é uma string nula ou uma palavra de barra; mais comumente eles retornam via exceção, que faz eval
com que retorne uma cadeia nula ou use exit
para evitar retornar).
- Se um
+
for excluído, isso não terá efeito imediato se o código adjacente estiver intacto; unário +
não tem efeito no programa. (A razão pela qual os originais +
existem existe para ajudar a reparar danos; eles aumentam o número de situações nas quais <
é interpretado como um operador unário <>
e não como relacional, o que significa que você precisa de mais exclusões para produzir um programa inválido.)
O wrapper pode ser danificado com exclusões suficientes, mas é necessário fazer uma série de exclusões para produzir algo que não é analisado. Com quatro exclusões, você pode fazer o seguinte:
eal<evl<eval+<...
e no Perl, o operador relacional <
não é associativo e, portanto, você recebe um erro de sintaxe (o mesmo que ocorreria 1<2<3
). Como tal, o limite para o programa como está escrito é n = 3. Adicionar mais unários +
parece ser uma maneira promissora de aumentá-lo, mas isso tornaria cada vez mais provável que o interior do wrapper também pudesse quebrar, verificando se a nova versão do programa funciona poderia ser muito difícil.
O motivo pelo qual o wrapper é tão valioso é que eval
no Perl captura exceções, como (por exemplo) a exceção que você recebe ao tentar compilar um erro de sintaxe. Como se eval
trata de uma string literal, a compilação da string acontece no tempo de execução e, se o literal falhar na compilação, a exceção resultante é capturada. Isso faz eval
com que retorne uma cadeia nula e defina o indicador de erro $@
, mas nunca a verificamos (exceto executando ocasionalmente a cadeia nula retornada em algumas versões modificadas do programa). Fundamentalmente, isso significa que, se algo acontecer com o código dentroo invólucro, causando um erro de sintaxe, o invólucro fará apenas com que o código não faça nada (e o programa continuará sendo executado na tentativa de encontrar uma cópia não danificada de si mesmo). Portanto, o código interno não precisa ser tão à prova de radiação quanto o invólucro; tudo o que nos preocupa é que, se danificado, ele agirá de forma idêntica à versão não danificada do programa ou falhará (permitindo eval
capturar a exceção e continuar) ou sairá normalmente sem imprimir nada.
Dentro do invólucro
O código dentro do wrapper, basicamente, tem a seguinte aparência (novamente, há um controle _
que o Stack Exchange não mostrará imediatamente antes do -+
):
eval+(q(...)=~y=A-Z=-+;-AZz-~=r)
Este código é escrito inteiramente com caracteres seguros para globos, e seu objetivo é adicionar um novo alfabeto de sinais de pontuação que possibilite escrever um programa real, através da transliteração e avaliação de uma string literal (não podemos usar '
ou "
como nossa citação marcas, mas q(
… )
também é uma maneira válida de formar uma string em Perl). (A razão para o caractere não imprimível é que precisamos transliterar algo no caractere de espaço sem um caractere de espaço literal no programa; assim, formamos um intervalo que começa no ASCII 31 e capturamos o espaço como o segundo elemento do intervalo.) Obviamente, se estamos produzindo alguns caracteres via transliteração, temos que sacrificar caracteres para transliterá-los de, mas letras maiúsculas não são muito úteis e é muito mais fácil escrever sem acesso a elas do que sem acesso a sinais de pontuação.
Aqui está o alfabeto de sinais de pontuação que se tornam disponíveis como resultado da glob (a linha superior mostra a codificação, a linha inferior o caractere que codifica):
BCDEFGHIJKLMNOPQRSTUVWXYZ
! "# $% & '() * +; <=>? @ AZz {|} ~
Mais notavelmente, temos um monte de sinais de pontuação que não são seguros para o globo, mas são úteis para escrever programas Perl, juntamente com o caractere de espaço. Também salvei duas letras maiúsculas, a literal A
e Z
(que codifica não para si, mas para T
e U
, porque A
era necessária como ponto final superior e inferior do intervalo); isso nos permite escrever a própria instrução de transliteração usando o novo conjunto de caracteres codificados (embora as letras maiúsculas não sejam tão úteis, elas são úteis na especificação de alterações nas letras maiúsculas). Os caracteres mais notáveis que não temos disponíveis são [
,, \
e ]
, mas nenhum é necessário (quando eu precisava de uma nova linha na saída, eu a produzi usando a nova linha implícita desay
ao invés de precisar escrever \n
; chr 10
também teria funcionado, mas é mais detalhado).
Como sempre, precisamos nos preocupar com o que acontece se o interior do wrapper for danificado fora da literal da string. Um corrompido eval
impedirá que algo funcione; estamos bem com isso. Se as aspas ficarem danificadas, o interior da string não é Perl válido e, portanto, o wrapper a capturará (e as inúmeras subtrações nas strings significam que, mesmo que você pudesse torná-lo válido, Perl não faria nada, o que é um resultado aceitável). O dano à transliteração, se não for um erro de sintaxe, irá mutilar a cadeia de caracteres que está sendo avaliada, normalmente fazendo com que ela se torne um erro de sintaxe; Não tenho 100% de certeza de que não haja casos em que isso ocorra, mas estou forçando-o no momento a garantir, e deve ser fácil o suficiente para corrigir, se houver.
O programa codificado
Olhando dentro da string literal, revertendo a codificação que usei e adicionando espaço em branco para torná-la mais legível, obtemos isso (novamente, imagine um sublinhado de controle antes de -+
, que é codificado como A
):
$o=q<
length$o ==181 || zzzz((()));
do {
chop ($t = "eval+<"x4);
$r = '=-+;-AZz-~=';
$s = '$o=q<' . $o . '>;eval$o';
eval '$s=~y' . $r . 'A-Z=';
say "$t(q($s)=~y=A-Z${r}r)" . "####>"x6;
say ";" for 1..4
} for 1..4;
exit>;
eval $o
As pessoas que estão acostumadas com o método Quines reconhecerão essa estrutura geral. A parte mais crucial está no início, onde verificamos que $ o não está danificado; se os personagens foram excluídos, seu comprimento não irá corresponder 181
, então corremos zzzz((()))
o que, se não é um erro de sintaxe, devido aos suportes inigualáveis, será um erro de execução mesmo se você excluir todos os três personagens, porque nenhum de zzzz
, zzz
, zz
, e z
é uma função, e não há como impedir a análise como uma função além de excluir (((
e causar um erro de sintaxe óbvio. O cheque em si também é imune a danos; o ||
pode ser danificado, |
mas isso fará com que a zzzz((()))
chamada seja executada incondicionalmente; variáveis ou constantes danosas causarão uma incompatibilidade porque você está comparando uma delas 0
,180
, 179
, 178
De igualdade para um subconjunto dos dígitos de 181
; e remover um =
causará uma falha na análise e dois =
inevitavelmente farão com que o LHS avalie o número inteiro 0 ou uma seqüência nula, sendo ambos falsey.
Atualização : essa verificação estava um pouco errada na versão anterior do programa, então tive que editá-la para corrigir o problema. A versão anterior ficou assim após a decodificação:
length$o==179||zzzz((()))
e foi possível excluir os três primeiros sinais de pontuação para obter isso:
lengtho179||zzz((()))
lengtho179
, sendo uma palavra de barra, é verdadeira e, portanto, quebra o cheque. Corrigi isso adicionando dois B
caracteres extras (que codificam caracteres de espaço), o que significa que a versão mais recente do quine faz isso:
length$o ==181||zzzz((()))
Agora é impossível ocultar os =
sinais e o $
sinal sem gerar um erro de sintaxe. (Eu tinha para adicionar dois espaços em vez de um, porque um comprimento de 180
se colocar um literal 0
de caracteres na fonte, o que poderia ser abusado neste contexto a zero com um bareword, que sucede. Comparar-inteiro) atualização End
Depois que a verificação de comprimento passa, sabemos que a cópia não está danificada, pelo menos em termos de exclusão de caracteres, por isso é tudo simples e simples a partir daí (substituições de sinais de pontuação devido a uma tabela de decodificação corrompida não seriam capturadas com essa verificação , mas eu já verifiquei por meio de força bruta que não há três exclusões somente da tabela de decodificação que quebram o quine; presumivelmente a maioria delas causa erros de sintaxe). Temos $o
em uma variável já, portanto, tudo o que precisamos fazer é codificar os invólucros exteriores (com alguma pequena grau de compressão, eu não saltar para fora do código-golf parte da questão inteiramente ). Um truque é que armazenamos a maior parte da tabela de codificação em$r
; podemos imprimi-lo literalmente para gerar a seção da tabela de codificação do invólucro interno ou concatenar algum código ao seu redor e eval
executar o processo de decodificação ao contrário (nos permitindo descobrir qual é a versão codificada de $ o , tendo apenas a versão decodificada disponível neste momento).
Finalmente, se fôssemos uma cópia intacta e, portanto, pudéssemos imprimir o programa original inteiro, chamamos exit
para impedir que as outras cópias também tentem imprimir o programa.
Script de verificação
Não é muito bonito, mas publicá-lo porque alguém perguntou. Eu executei isso várias vezes com uma variedade de configurações (normalmente alterando $min
e $max
para verificar várias áreas de interesse); não foi um processo totalmente automatizado. Ele tende a parar de funcionar devido à carga pesada da CPU em outro local; quando isso aconteceu, mudei $min
para o primeiro valor do $x
que não estava totalmente verificado e continuei executando o script (garantindo assim que todos os programas no intervalo fossem verificados eventualmente). Eu só verifiquei exclusões da primeira cópia do programa, porque é bastante óbvio que exclusões das outras cópias não podem fazer mais.
use 5.010;
use IPC::Run qw/run/;
undef $/;
my $program = <>;
my $min = 1;
my $max = (length $program) / 4 - 3;
for my $x ($min .. $max) {
for my $y ($x .. $max) {
for my $z ($y .. $max) {
print "$x, $y, $z\n";
my $p = $program;
substr $p, $x, 1, "";
substr $p, $y, 1, "";
substr $p, $z, 1, "";
alarm 4;
run [$^X, '-M5.010'], '<', \$p, '>', \my $out, '2>', \my $err;
if ($out ne $program) {
print "Failed deleting at $x, $y, $z\n";
print "Output: {{{\n$out}}}\n";
exit;
}
}
}
}
say "All OK!";
Subleq
. Eu acho que seria ideal para esse tipo de desafio!