Isso foi surpreendentemente complicado, e não estou convencido de que seja ideal ...
<.@!$?
Depois de preencher e desdobrar o código, isso representa a seguinte grade hexadecimal:
Isso usa um fluxo de controle semelhante ao meu recente programa de gato sem erros , movendo-se ao longo de antiagonais. Para conseguir isso, começamos desviando o ponteiro de instrução (IP) para a esquerda, onde o caminho roxo passa pelo canto inferior esquerdo.
?
lê a entrada como um número inteiro. !
imprime de volta. .
é apenas um não-op. Agora o canto da grade atua como um ramo:
Se a entrada foi 0
, o IP continuará no caminho vermelho, que simplesmente encerra o programa @
.
Se a entrada foi 1
, o IP continuará no caminho verde. Novamente, .
é apenas um não-op, mas $
é o equivalente ao trampolim de Befunge: pula a próxima instrução. Após a quebra, a próxima instrução seria a ?
, mas devido à $
execução, na verdade, continua no caminho azul, começando com !
a impressão de outra cópia da 1
. Este loop que contém apenas !..$
agora é repetido indefinidamente.
Estudo do fluxo de controle em hexag ...
Eu acredito que a solução acima é ótima. Eu escrevi um forcer bruto, que verifica todos os programas Hexagony de 6 bytes, que contêm pelo menos um de cada um ?!@
(o que é necessário; eu também verifiquei :
e %
substitui @
para terminar com um erro de divisão por zero, mas isso também não ajudou). A verificação imprime todos os programas que a) produzem a 0
na entrada 0
e terminam eb) produzem pelo menos dois 1
segundos (e nada mais) e não terminam dentro dos primeiros 60 ticks do programa (200 ticks para soluções de 5 bytes) . Duvido que qualquer solução válida levasse mais de 200 ticks para imprimir corretamente o primeiro 0
ou o segundo 1
em uma grade tão pequena, então acho que não perdi nenhuma solução em potencial.
A pesquisa não produziu nenhum resultado para 5 bytes, mas 57 resultados para 6 bytes (usando @
; não é necessário terminar com um erro se pudermos resolver isso de maneira limpa na mesma quantidade de bytes). Desses 57, apenas 6 eram falsos positivos, que na verdade imprimiam apenas dois 1
se entraram em um loop infinito sem imprimir mais. Uma solução foi listada duas vezes porque continha dois !
comandos. Isso deixa exatamente 50 soluções válidas.
Há uma certa quantidade de degeneração entre as soluções em que um ou dois caracteres não são substanciais, por exemplo, porque eles são efetivamente não-ops de qualquer maneira. As soluções podem ser agrupadas em 23 conjuntos de programas genuinamente distintos (em alguns casos, há apenas uma única diferença de caracteres entre dois conjuntos, mas isso altera o fluxo de controle substancialmente, então eu os contei separadamente). Dois dos grupos até fazem uso de vários indicadores de instruções de uma maneira muito inesperada. Como eu nunca teria inventado a maioria dessas maneiras de usar os galhos e os espelhos, eles fazem um estudo muito interessante de que tipo de fluxo de controle é possível no Hexagony, e eu definitivamente aprendi alguns truques novos para futuros golfistas.
O fluxo geral de controle é quase sempre o mesmo: leia um número, imprima-o. Se 0
encontrar um caminho para o @
, se não continuar, continue !
repetindo o tempo mantendo um valor de borda de 1
. Há quatro exceções notáveis:
- Uma solução (aquela com dois
!
) imprime dois 1
s por iteração na grade, imprimindo cerca de duas vezes mais rápido que a maioria dos programas. Eu marquei este aqui com x2
abaixo.
- Algumas soluções (aquelas que contêm um
o
) substituem o 1
por um 111
(o código de caractere de o
), para que eles imprimam três 1
s por iteração, fazendo-os imprimir cerca de três vezes mais rápido que a maioria dos programas. Eu os marquei com x3
abaixo.
- Duas soluções acrescentar um
1
ao valor borda em cada iteração (assim 1
-> 11
-> 111
-> ...). Eles imprimem muito rápido, mas acabam ficando sem memória. Eu os marquei com OoM
abaixo.
- Duas soluções entram em um loop muito apertado, que apenas oscila de um lado para o outro
!
, imprimindo em todos os outros ticks (em vez de em cada 5ª), o que os torna um pouco mais rápidos (e organizados). Eu os marquei com ><
abaixo.
Então aqui está o zoológico inteiro:
#1 #5 #12 #19
?!/$.@ ?$!>$@ .?!/$@ |!|?$@ # ><
?!/$1@ # OoM ?$!|$@ =?!/$@
?!/$=@ #20
?!/$\@ #6 #13 $@.?<!
?!/$o@ # x3 ?/!<|@ .?/!$@ $@1?<! # OoM
?!/$!@ # x2 =?/!$@ $@=?<!
#7 $@o?<! # x3
#2 ?\!<|@ #14
?!>$)@ \!?__@ #21
?!>$1@ #8 _>_!?@
?!>$o@ # x3 ?<!>$@ # >< #15
?!|$)@ \_?!$@ #22
?!|$1@ #9 <!@.$?
?!|$o@ # x3 ?\$!@$ #16 <!@/$?
\_?!_@ <!@=$?
#3 #10 <$@!$?
?!|)$@ ?~#!@) #17 <.@!$?
?!|1$@ ?~#!@1 $$?\@! </@!$?
?!|o$@ # x3 <=@!$?
#11 #18
#4 ?$)\@! \$?\@! #23
?_!<@> ?$1\@! <<@]!?
?$o\@! # x3
A seguir, é apresentado um pequeno passo a passo de alguns grupos mais representativos. Vale a pena conferir especialmente os grupos 10 e 23. Existem muitos outros caminhos interessantes e, às vezes, complicados nos outros grupos, mas acho que o aborreci o suficiente no final disso. Para quem realmente quer aprender Hexagony, definitivamente vale a pena investigar, pois eles exibem ainda mais usos possíveis dos espelhos e $
.
Grupo 1
Este não é muito mais elaborado do que minha solução original, mas os caminhos seguem em direções diferentes. Ele também permite o maior número de variações em uma única célula, pois o no-op mais à direita pode ser substituído por 5 comandos diferentes que ainda o tornam válidos sem alterar a estrutura:
Grupo 2
Este é bastante interessante, porque só se move horizontalmente. Depois de quebrar para o >
, o IP reverte imediatamente, levando a ramificação no canto. Não é totalmente visível o diagrama, mas no caso de 1
atravessarmos a primeira linha novamente, mas desta vez para trás. Isso também significa que encontramos ?
novamente, que agora retorna 0
(EOF). Isso é corrigido com )
(incremento) para continuar imprimindo 1
s. Isso também tem 5 variações, como )
também pode ser 1
ou o
, e >
também pode ser |
:
Grupo 3
Este parece quase idêntico ao anterior, mas é confuso como o inferno. Até bater |
e, em seguida, percorrendo a linha inferior ou superior, é a mesma coisa. Mas, no caso de um loop, o $
agora salta sobre o )
sobre o espelho. Então, seguimos o caminho turquesa para a direita, agora atingimos o incremento, pulamos o @
antes de voltarmos para o |
novamente e depois voltamos para o caminho verde no topo.
Grupo 4
Eu pensei que este era particularmente bacana:
O _
espelho no canto superior direito é inicialmente um no-op, então imprimimos com !
e pressionamos o <
. O 0
caminho agora atinge o espelho horizontal e termina. O 1
caminho segue uma trajetória realmente interessante: desvia, desce para o !
, é redirecionado para a horizontal e depois volta para o !
novo . Em seguida, ele continua se movendo nessa forma de losango, imprimindo duas vezes por iteração (a cada terceiro tick).
Grupo 8
Esta é uma das duas soluções com um loop de impressão muito apertado:
Os <
atos como o ramo. Depois de embrulhar duas vezes, 0
acerta @
. 1
por outro lado, primeiro pula o ?
, depois o >
envia para o $
novamente, de modo que pula o @
. Em seguida, o IP passa pelo caminho turquesa, onde salta para frente e para trás entre >
e <
(envolvendo a borda no meio).
Grupo 10
Um dos dois grupos que usam outros indicadores de instruções, e é absolutamente lindo. A hexagonia possui 6 - cada uma começa em um canto diferente no sentido horário, mas apenas uma delas está ativa por vez.
Como sempre, lemos com ?
. Agora ~
é uma negação unária: transforma o 1
em um -1
. Em seguida, atingimos o #
. Esta é uma maneira de alternar entre IPs: ele pega o valor atual do módulo 6 e alterna para o IP correspondente (os IPs são numerados 0
no sentido horário). Portanto, se a entrada foi 0
, o IP simplesmente permanece o mesmo e segue entediante para a frente !@
. Mas se a entrada foi 1
, então o valor atual é -1
qual é 5 (mod 6)
. Então, mudamos para o IP que começa na mesma célula (o caminho verde). Agora #
é um no-op e ?
define a borda da memória como 0
. )
incrementos para !
imprimir a 1
. Agora batemos ~
novamente para garantir que#
ainda é um no-op (ao contrário de nos mudar para o IP 1 que encerraria o programa). É impressionante como tudo se encaixa nesse pequeno programa.
Grupo 22
Apenas para observar, esse é o grupo em que minha solução original está. Também é o maior grupo, porque o no-op pode estar em dois lugares diferentes e existem várias opções para o comando real (no-op efetivo).
Grupo 23
Este é o outro grupo usando vários IPs. De fato, este usa três IPs diferentes. O canto superior direito é um pouco confuso, mas vou tentar orientá-lo sobre isso:
Então, o começo que você já viu antes: <
desvia o Nordeste, ?
lê a entrada. Agora ]
é outra maneira de mudar entre IPs: ele passa o controle para o próximo IP na ordem horária. Então, mudamos o controle para o caminho turquesa que (eu sei que é difícil de ver) começa no canto nordeste, indo para sudeste. Ele é imediatamente refletido no <
modo que ele segue para o canto sudeste, indo para o noroeste. Ele também atinge o ]
botão, então mudamos para o próximo IP. Este é o caminho cinza que começa no canto leste, indo para o sudoeste. Ele imprime a entrada e, em seguida, passa para o canto nordeste. <
desvia o caminho para a horizontal, onde é refletido pelo outro <
. Agora a mão direita<
atua como um ramo: se a entrada foi 0
, o IP se move para o nordeste e passa para o @
. Se a entrada foi 1
, o IP se move para o !
, passa para a esquerda e para <
onde é refletido ... agora no canto, volta para o !
, é desviado pela direita <
, refletida pela esquerda <
e o caminho começa sobre...
Uma bagunça, mas uma bagunça linda. :)
Diagramas gerados com o incrível HexagonyColorer de Timwi .