Desdobrar o código fonte do Hexagony

52

Introdução

Se você não conhece o Hexagony , é uma linguagem esotérica criada por Martin Büttner. O fato é que esse idioma aceita vários formulários para o programa. Os seguintes programas são todos equivalentes:

abcdefg

e

 a b
c d e
 f g

Então, basicamente, o código foi acumulado em um hexágono regular. Mas observe que a adição de um novo comando ao código abcdefghresultaria no seguinte programa:

  a b c
 d e f g
h . . . .
 . . . .
  . . .

Como você pode ver, o primeiro passo é acumular o código em um hexágono e, depois disso, o hexágono é preenchido com no-ops ( .) para o próximo número hexagonal centralizado .

Sua tarefa é simples, quando uma string (o código fonte) recebe um código fonte hexagonal completo.

As regras

  • Você pode fornecer um programa ou uma função.
  • Espaços em branco à esquerda são permitidos, mas somente quando o hexágono não sai de forma
  • Espaço em branco à direita é permitido.
  • Observe que os espaços em branco no programa são ignorados . Então a b cé igual aabc
  • Somente os caracteres ASCII imprimíveis ( 32 - 126) são usados, portanto, apenas o Spacecaractere regular é ignorado.
  • Suponha que o comprimento da string seja maior que 0.
  • Isso é , então a submissão com a menor quantidade de bytes ganha!

Casos de teste

Input: ?({{&2'2':{):!/)'*/

Output:
  ? ( {
 { & 2 '
2 ' : { )
 : ! / )
  ' * /


Input: H;e;l;d;*;r;o;Wl;;o;*433;@.>;23<\4;*/

Output:
   H ; e ;
  l ; d ; *
 ; r ; o ; W
l ; ; o ; * 4
 3 3 ; @ . >
  ; 2 3 < \
   4 ; * /


Input: .?'.) .@@/'/ .!.>   +=(<.!)}    (  $>( <%

Output:
   . ? ' .
  ) . @ @ /
 ' / . ! . >
+ = ( < . ! )
 } ( $ > ( <
  % . . . .
   . . . .
Adnan
fonte
6
Além disso, não tenho certeza se você quer ser tão exigente, mas os backticks são ignorados no processo de determinação da largura do código porque eles anotam o próximo caractere. Então abc`defg, na verdade, ele se tornaria pastebin.com/ZrdJmHiR
Martin Ender
2
@ MartinBüttner Oh, eu não sabia disso :). Para esse desafio, os backticks não serão ignorados.
Adnan
18
Eu realmente quero ver uma resposta no Hexagony para esta pergunta.
quer
2
@Adnan Provavelmente, uma resposta melhor seria "Você pode assumir que a entrada não contém sinalizadores de depuração ( `caracteres)".
Riking
4
@Ampora Ask e você receberá.
Martin Ender

Respostas:

13

Pyth, 57 54 50 49 48 46

V+UJfgh*6sUTlK-zd1_UtJ+*d-JNjd:.[K\.^TJZ=+Z+JN

Suíte de teste

Imprime um espaço à esquerda em cada linha.

Esta versão requer uma prova de que 10 ^ n> = 3n (n - 1) + 1 para todos os n> = 1 . Agradecemos a ANerdI e ErickWong por fornecer provas.

Seguindo essas desigualdades: 10 ^ n> (1 + 3) ^ n = 1 + 3n + 9n (n - 1) + ...> 3n (n - 1) + 1 pode-se ver facilmente que isso está correto para n> = 2 . Examinar o caso n = 1 é bastante trivial, fornecendo 10> 1 .

Como alternativa, pegar as derivadas dessas equações duas vezes mostra que 10 ^ n tem uma segunda derivada maior para todos n> = 1 , que pode ser cascateada até as primeiras derivadas e, finalmente, às equações originais.

Explicação

              ##  Implicit: z=input(); Z=0
Jf...1        ##  Save to J the side length of the hexagon the code fills up
              ##  by finding the first number such that:
gh*6sUT       ##  the the T'th hexagonal number is greater than...
              ##  Computes 6 * T'th triangular number (by using sum 1..T-1) + 1
    lK-zd     ##  ...the length of the code without spaces (also save the string value to K)
V+UJ_UtJ      ##  For loop over N = [0, 1, ..., J-1, ..., 0]:
+*d-JN        ##  append J - N spaces to the front of the line
jd            ##  riffle the result of the next operation with spaces
:.[K\.yJ      ##  slice the string given by K padded to be the length of the Jth hexagon
              ##  number with noops
Z=+Z+JN       ##  from Z to Z + J + N, then set Z to be Z + J + N
FryAmTheEggman
fonte
2
Primeiro você precisa provar que ln (10) * 10 ^ n> 6n-3 (os derivados) para n> = 1. Isso é fácil, pois as derivadas dessas expressões são ln (10) ^ 2 10 ^ n e 6. Como 10 ^ n está aumentando monotonicamente e 10 ^ 1> 6 * 1, 10 ^ n é maior que 6n-3 para todos n> = 1. Você pode usar a mesma lógica para concluir a prova de 10 ^ ne 3n (n-1) +1.
Arcturus
@Ampora Obrigado, eu tinha pensado em usar derivativos, mas parecia impuro. Eu não consegui encontrar uma maneira melhor, muito apreciada!
FryAmTheEggman
Feliz em ajudar. O Calc pode ficar realmente feio às vezes.
quer
na ligação pyth.herokuapp.com/?code=etc acima i encontrar o compilador não correr ...
RosLuP
11
@FryAmTheEggman Existe uma maneira muito fácil de mostrar o limite muito mais forte 4 ^ n> 3n (n-1) + 1 para n> = 1, sem necessidade de cálculo. Apenas use o fato de que (1 + 3) ^ n = 1 + 3n + 9n (n-1) / 2 + ... por expansão binomial. O primeiro e o terceiro termos majorizam diretamente 1 + 3n (n-1), de modo que a desigualdade é imediata se o terceiro termo existir (ou seja, para n> = 2). Isso deixa apenas o caso n = 1, que é trivial, já que o RHS é 1. #
Erick Wong
90

Hexagonia , 271 bytes

Apresento a vocês os primeiros 3% de um auto-intérprete da Hexagony ...

|./...\..._..>}{<$}=<;>'<..../;<_'\{*46\..8._~;/;{{;<..|M..'{.>{{=.<.).|.."~....._.>(=</.\=\'$/}{<}.\../>../..._>../_....@/{$|....>...</..~\.>,<$/'";{}({/>-'(<\=&\><${~-"~<$)<....'.>=&'*){=&')&}\'\'2"'23}}_}&<_3.>.'*)'-<>{=/{\*={(&)'){\$<....={\>}}}\&32'-<=._.)}=)+'_+'&<

Experimente online! Você também pode executá-lo por si só, mas isso levará cerca de 5 a 10 segundos.

Em princípio, isso pode caber no comprimento lateral 9 (para uma pontuação de 217 ou menos), porque ele usa apenas 201 comandos, e a versão desolfada que escrevi primeiro (no comprimento lateral 30) precisava de apenas 178 comandos. No entanto, tenho certeza de que levaria uma eternidade para realmente fazer tudo encaixar, então não tenho certeza se vou realmente tentar.

Também deve ser possível jogar um pouco no tamanho 10, evitando o uso das últimas uma ou duas linhas, de modo que os no-ops finais possam ser omitidos, mas isso exigiria uma reescrita substancial, como um dos primeiros caminhos junta faz uso do canto inferior esquerdo.

Explicação

Vamos começar desdobrando o código e anotando os caminhos do fluxo de controle:

insira a descrição da imagem aqui

Isso ainda é bastante confuso, então aqui está o mesmo diagrama para o código "não-destruído" que eu escrevi primeiro (na verdade, esse é o comprimento do lado 20 e originalmente eu escrevi o código no comprimento do lado 30, mas era tão escasso que não para melhorar a legibilidade, então eu a compactuei um pouco para tornar o tamanho um pouco mais razoável):

insira a descrição da imagem aqui
Clique para uma versão maior.

As cores são exatamente as mesmas, com exceção de alguns detalhes muito menores, os comandos sem fluxo de controle também são exatamente os mesmos. Então, eu vou explicar como isso funciona com base na versão sem golfinhos, e se você realmente quiser saber como funciona a versão com golfe, poderá verificar quais partes correspondem a qual no hexágono maior. (O único problema é que o código golfado começa com um espelho, para que o código real comece no canto direito, à esquerda.)

O algoritmo básico é quase idêntico à minha resposta CJam . Existem duas diferenças:

  • Em vez de resolver a equação do número hexagonal centralizado, apenas calculo números hexagonais centralizados consecutivos até que um seja igual ou maior que o comprimento da entrada. Isso ocorre porque o Hexagony não possui uma maneira simples de calcular uma raiz quadrada.
  • Em vez de preencher a entrada com no-ops imediatamente, verifico mais tarde se já esgotei os comandos na entrada e imprimo um, .se tiver.

Isso significa que a ideia básica se resume a:

  • Leia e armazene a sequência de entrada enquanto calcula seu comprimento.
  • Encontre o menor comprimento lateral N(e o número hexagonal centralizado correspondente hex(N)) que pode conter toda a entrada.
  • Calcule o diâmetro 2N-1.
  • Para cada linha, calcule o recuo e o número de células (que somam 2N-1). Imprima o recuo, imprima as células (usando .se a entrada já estiver esgotada), imprima um avanço de linha.

Observe que existem apenas no-ops; portanto, o código real começa no canto esquerdo (o $, que pula sobre o >, de modo que realmente começamos no ,caminho no cinza escuro).

Aqui está a grade de memória inicial:

insira a descrição da imagem aqui

Portanto, o ponteiro da memória começa na entrada rotulada pela borda , apontando para o norte. ,lê um byte de STDIN ou a -1se tivermos atingido o EOF nessa borda. Portanto, o <seguinte é uma condição para saber se lemos toda a entrada. Vamos permanecer no loop de entrada por enquanto. O próximo código que executamos é

{&32'-

Isso grava um 32 no espaço rotulado pela borda e subtrai-o do valor de entrada no diff rotulado pela borda . Observe que isso nunca pode ser negativo, porque garantimos que a entrada contém apenas ASCII imprimível. Será zero quando a entrada for um espaço. (Como Timwi aponta, isso ainda funcionaria se a entrada pudesse conter linhas ou tabulações, mas também removeria todos os outros caracteres não imprimíveis com códigos de caracteres menores que 32.) Nesse caso, ele <desvia o ponteiro de instrução (IP) restante e o caminho cinza claro é seguido. Esse caminho simplesmente redefine a posição do MP {=e depois lê o próximo caractere - assim, os espaços são ignorados. Caso contrário, se o personagem não fosse um espaço, executamos

=}}})&'+'+)=}

Isso primeiro se move ao redor do hexágono pela borda do comprimento até seu lado oposto à borda do diferencial , com =}}}. Em seguida, ele copia o valor da frente do comprimento de aresta para o comprimento do bordo, e incrementa com )&'+'+). Veremos em um segundo por que isso faz sentido. Por fim, movemos a nova borda com =}:

insira a descrição da imagem aqui

(Os valores das arestas particulares são do último caso de teste apresentado no desafio.) Nesse ponto, o loop se repete, mas com tudo mudou um hexágono para nordeste. Então, depois de ler outro personagem, temos o seguinte:

insira a descrição da imagem aqui

Agora você pode ver que estamos escrevendo gradualmente a entrada (menos espaços) ao longo da diagonal nordeste, com os caracteres em todas as outras arestas, e o comprimento até esse caractere sendo armazenado paralelamente ao comprimento rotulado pela borda .

Quando terminarmos o loop de entrada, a memória ficará assim (onde eu já identifiquei algumas novas arestas para a próxima parte):

insira a descrição da imagem aqui

O %é o último caractere que lemos, 29é o número de caracteres não espaciais que lemos. Agora queremos encontrar o comprimento lateral do hexágono. Primeiro, há algum código de inicialização linear no caminho verde / cinza escuro:

=&''3{

Aqui, =&copia o comprimento (29 no nosso exemplo) para o comprimento rotulado da borda . Em seguida, ''3move-se para a aresta rotulada 3 e define seu valor como 3(o que precisamos apenas como uma constante no cálculo). Finalmente, {move-se para a aresta rotulada N (N-1) .

Agora entramos no loop azul. Esse loop é incrementado N(armazenado na célula N ) e calcula seu número hexagonal centralizado e subtrai-o do comprimento da entrada. O código linear que faz isso é:

{)')&({=*'*)'-

Aqui, {)move-se para e incrementa N . ')&(move-se para a borda N-1 , copia Nlá e diminui-o. {=*calcula seu produto em N (N-1) . '*)multiplica isso pela constante 3e incrementa o resultado na aresta rotulada hex (N) . Como esperado, este é o número hexagonal centésimo enésimo. Finalmente '-calcula a diferença entre isso e o comprimento da entrada. Se o resultado for positivo, o comprimento lateral ainda não é grande o suficiente e o loop será repetido (onde }}mova o MP de volta para a borda rotulada N (N-1) ).

Quando o comprimento lateral for grande o suficiente, a diferença será zero ou negativa e obtemos o seguinte:

insira a descrição da imagem aqui

Primeiro, agora existe o caminho verde linear realmente longo, que faz alguma inicialização necessária para o loop de saída:

{=&}}}32'"2'=&'*){=&')&}}

Os {=&começa por copiar o resultado do diff borda no comprimento de ponta, porque depois precisa de algo não-positivo lá. }}}32grava um 32 no espaço rotulado da borda . '"2escreve uma constante 2 na aresta não identificada acima de diff . '=&copia N-1para a segunda borda com a mesma etiqueta. '*)multiplica por 2 e incrementa para obter o valor correto na aresta 2N-1 na parte superior. Este é o diâmetro do hexágono. {=&')&copia o diâmetro para a outra borda rotulada 2N-1 . Finalmente, }}volta para a borda 2N-1 na parte superior.

Vamos rotular novamente as arestas:

insira a descrição da imagem aqui

A borda em que estamos atualmente (que ainda mantém o diâmetro do hexágono) será usada para iterar sobre as linhas da saída. O recuo rotulado da borda calculará quantos espaços são necessários na linha atual. As células rotuladas de borda serão usadas para iterar sobre o número de células na linha atual.

Agora estamos no caminho rosa que calcula o recuo . ('-diminui o iterador de linhas e o subtrai de N-1 (para a borda de recuo ). O pequeno ramo azul / cinza no código simplesmente calcula o módulo do resultado ( ~nega o valor se for negativo ou zero e nada acontece se for positivo). O restante do caminho rosa é o "-~{que subtrai o recuo do diâmetro para a borda das células e depois volta para a borda do recuo .

O caminho amarelo sujo agora imprime o recuo. O conteúdo do loop é realmente apenas

'";{}(

Onde se '"move para a borda do espaço , ;imprime, {}volta para recuo e (diminui.

Quando terminamos, o (segundo) caminho cinza escuro procura o próximo caractere a ser impresso. Os =}movimentos na posição (o que significa, na borda da célula , apontando para o sul). Então temos um loop muito apertado, {}que simplesmente desce duas arestas na direção sudoeste, até atingirmos o final da string armazenada:

insira a descrição da imagem aqui

Percebe que eu rotulei uma borda para lá EOF? . Depois de processarmos esse caractere, tornaremos a borda negativa, para que o {}loop termine aqui em vez da próxima iteração:

insira a descrição da imagem aqui

No código, estamos no final do caminho cinza escuro, onde 'retrocede um passo no caractere de entrada. Se a situação for um dos dois últimos diagramas (ou seja, ainda houver um caractere da entrada que ainda não imprimimos), seguiremos o caminho verde (o inferior, para pessoas que não são boas com verde e azul). Essa é bastante simples: ;imprime o próprio personagem. 'move-se para a borda do espaço correspondente que ainda retém 32 e ;imprime esse espaço. Então {~faz o nosso EOF? negativo para a próxima iteração, 'recua um passo para que possamos retornar ao extremo noroeste da sequência com outro }{loop apertado . Que termina no comprimento(a não positiva abaixo de hex (N) . Finalmente, }retorna à borda da célula .

Se já esgotamos a entrada, o loop que procura por EOF? realmente terminará aqui:

insira a descrição da imagem aqui

Nesse caso, 'move-se para a célula de comprimento e seguimos o caminho azul claro (superior), que imprime um não-op. O código neste ramo é linear:

{*46;{{;{{=

Ele {*46;grava um 46 na borda rotulada como não operacional e o imprime (isto é, um ponto). Depois, {{;move-se para a borda do espaço e imprime isso. A {{=volta para a borda da célula para a próxima iteração.

Nesse ponto, os caminhos se juntam novamente e (diminuem a borda das células . Se o iterador ainda não for zero, seguiremos o caminho cinza claro, que simplesmente inverte a direção do MP =e depois procura o próximo caractere a ser impresso.

Caso contrário, chegamos ao final da linha atual e o IP seguirá o caminho roxo. É assim que a grade de memória se parece nesse ponto:

insira a descrição da imagem aqui

O caminho roxo contém o seguinte:

=M8;~'"=

O =inverte a direção do MP novamente. M8define o valor definido como 778(porque o código de caractere de Mé 77e dígitos serão anexados ao valor atual). Isso acontece 10 (mod 256), então, quando o imprimimos ;, obtemos um avanço de linha. Em seguida, ~torna a aresta negativa novamente, '"volta para a aresta das linhas e =reverte o MP mais uma vez.

Agora, se a borda das linhas é zero, terminamos. O IP seguirá o caminho vermelho (muito curto), onde @finaliza o programa. Caso contrário, continuamos no caminho púrpura, que volta ao rosa, para imprimir outra linha.


Diagramas de fluxo de controle criados com o HexagonyColorer da Timwi . Diagramas de memória criados com o depurador visual em seu IDE esotérico .

Martin Ender
fonte
19
Eu me pego dizendo isso muito em respostas hexagonais: Apenas whoa.
Conor O'Brien
5
Ahh ... mas .. wat ... mente = queimada #
Adnan
Eu esperava que alguém fizesse isso e ... Uau. Estou sem palavras. Fantástico.
quer
19
Etapa dois - escreva os outros 97%. :)
ASCIIThenANSI
Etapa três - como a resposta com o mínimo de bytes.
Tom M
19

CJam, 56 52 50 48 bytes

Meu primeiro pensamento foi: "ei, eu já tenho código para isso!" Mas não me incomodei em juntar as peças necessárias do código Ruby, principalmente porque elas não pareciam muito adequadas para jogar golfe. Então tentei outra coisa no CJam ...

lS-{_,4*(3/mq:D1%}{'.+}wD{D(2/-z_S*D@-@/(S*N@s}/

Teste aqui.

Explicação

Um pouco de matemática sobre números hexagonais centralizados primeiro. Se o hexágono regular tiver comprimento lateral N, ele conterá 3N(N-1)+1células, que devem ser iguais ao comprimento do código-fonte k. Podemos resolver isso Nporque é uma equação quadrática simples:

N = 1/2 ± √(1/4 + (k-1)/3)

Podemos ignorar a raiz negativa, porque isso dá um N. negativo. Para que isso tenha uma solução, precisamos que a raiz quadrada seja um meio inteiro. Ou, em outras palavras, √(1 + 4(k-1)/3) = √((4k-1)/3)precisa ser um número inteiro (felizmente, esse número inteiro é o diâmetro D = 2N-1do hexágono, que será necessário de qualquer maneira). Portanto, podemos adicionar repetidamente um único .até que essa condição seja atendida.

O resto é um loop simples que define o hexágono. Uma observação útil para esta parte é que os espaços no recuo mais os não espaços no código em cada linha somam o diâmetro.

lS-     e# Read input and remove spaces.
{       e# While the first block yields something truthy, evaluate the second...
  _,    e#   Duplicate the code and get its length k.
  4*(   e#   Compute 4k-1.
  3/    e#   Divide by 3.
  mq    e#   Take the square root.
  :D    e#   Store this in D, just in case we're done, because when we are, this happens
        e#   to be the diameter of the hexagon.
  1%    e#   Take modulo 1. This is 0 for integers, and non-zero for non-integers.
}{      e# ...
  '.+   e#   Append a no-op to the source code.
}w
D{      e# For every i from 0 to D-1...
  D(2/  e#   Compute (D-1)/2 = N, the side length.
  -z    e#   Subtract that from the current i and get its modulus. That's the size of the
        e#   indentation on this line.
  _S*   e#   Duplicate and get a string with that many spaces.
  D@-   e#   Subtract the other copy from D to get the number of characters of code
        e#   in the current line.
  @/    e#   Pull up the source code and split into chunks of this size.
  (S*   e#   Pull off the first chunk and riffle it with spaces.
  N     e#   Push a linefeed character.
  @s    e#   Pull up the remaining chunks and join them back into a single string.
}/

Acontece que não precisamos usar aritmética dupla (exceto a raiz quadrada). Devido à multiplicação por 4, não há colisões ao dividir por 3, e o desejado kserá o primeiro a produzir uma raiz quadrada inteira.

Martin Ender
fonte
8

Perl, 203 200 198

inclui + 1 para -p

s/\s//g;{($l=y///c)>($h=1+3*++$n*($n-1))&&redo}$s=$_.'.'x($h-$l);for($a=$n;$a<($d=2*$n-1);$a++){$s=~s/.{$a}/$&\n/,$s=reverse($s)for 0..1}$_=join$/,map{(' 'x abs($n-$i++-1)).$_}$s=~/\S+/g;s/\S/ $&/g

correr como: echo abc | perl -p file.pl

Uma abordagem muito ingênua:

#!/usr/bin/perl -p

s/\s//g;                            # ignore spaces and EOL etc.
{                                   # find the smallest hex number:
    ($l=y///c)                      # calc string length
    > ($h=1+3*++$n*($n-1))          # 
    && redo                         # (should use 'and', but..)
}

$s = $_                             # save $_ as it is used in the nested for
   . '.' x ($h-$l);                 # append dots to fill hexagon

for ( $a = $n; $a < ($d=2*$n-1); $a++ )
{
        $s=~s/.{$a}/$&\n/,          # split lines
        $s=reverse($s)              # mirror
    for 0..1                        # twice
}

$_ = join$/,                        # join using newline
map {                               # iterate the lines
    (' 'x abs($n-$i++-1)) .$_       # prepend padding
} $s=~/\S+/g;                       # match lines

s/\S/ $&/g                          # prepend spaces to characters
                                    # -p takes care of printing $_

  • a atualização 200 salva uma atribuição de variável em movimento de byte e outras 2, omitindo final ;; codifique-se com menos de 200 bytes agora!
  • atualização 198 economize 2 bytes usando em $s=~/\S+/gvez desplit/\n/,$s
Kenney
fonte
7

JavaScript (ES6), 162 172

Função anônima

O tamanho do hexágono é encontrado resolvendo a equação da wikipedia

3*n*(n-1)-1 = l

A fórmula de solução é basicamente

n = ceil(3+sqrt(12*l-3))/6)

Com alguma álgebra e alguma aproximação (thx para @ user18655 também), torna-se

n = trunc(sqrt(l/3-1/12)+1.4999....)
s=>eval("s=s.match(/\\S/g);m=n=Math.sqrt(s.length/3-1/12)+1.49999|0;p=o=``;for(i=n+n;--i;i>n?++m:--m)for(o+=`\n`+` `.repeat(n+n-m),j=m;j--;o+=` `)o+=s[p++]||`.`")

Mais legível

s=>{
  s=s.match(/\S/g);
  m=n=Math.sqrt(s.length/3-1/12)+1.49999;
  p=o='';
  for(i=n+n; --i; i>n?++m:--m)
    for(o += '\n'+' '.repeat(n+n-m), j=m; j--; o += ' ')
      o+=s[p++]||'.';
  return o
}

Snippet de teste (melhor página inteira - tempo de execução ~ 1 minuto)

f=s=>eval("s=s.match(/\\S/g);m=n=Math.sqrt(s.length/3-1/12)+1.49999|0;p=o=``;for(i=n+n;--i;i>n?++m:--m)for(o+=`\n`+` `.repeat(n+n-m),j=m;j--;o+=` `)o+=s[p++]||`.`")

t=0;
r='0';
(T=_=>t++<816?(O.innerHTML=f(r=t%10+r),setTimeout(T,20)):0)()
pre { font-size: 66% }
<pre id=O></pre>

edc65
fonte
11
Você pode usar em n=...+1-1e-9|0vez de n=Math.ceil(...)salvar 2 bytes. Você também pode ir ES7 e usar **0.5em vez de Math.sqrt, mas isso é até você. Normalmente eu apenas mantenho minhas respostas ES6 porque elas funcionam no meu navegador haha!
user81655
@ user81655 boa dica, graças
edc65
5

Pitão, 52 51 bytes

Jfgh**3TtTl=H-zd1=+H*\.*lHTV+UJt_UJAcH]+JN+*-JNdjdG

Experimente online. Suíte de teste.

Cada linha possui um espaço inicial extra, conforme permitido pelo OP.

Explicação

 f              1          |   find first number n for which
             -zd           |           remove spaces from input
           =H              |         put result in H
          l                |       length of input without spaces
  g                        |     is less than or equal to
   h**3TtT                 |       nth centered hexagonal number
J                          | put result (hexagon side length) in J
                           |
      *lHT                 |      ten times length of input without spaces
   *\.                     |   that amount of dots
=+H                        | append to H
                           |
  UJ                       |    numbers 0 up to side length - 1
 +  t_UJ                   |   add numbers side length - 2 down to 0
V                          | loop over result
            +JN            |       current loop number + side length
         cH]               |     split to two parts at that position
        A                  |   put parts to G and H
                 -JN       |       side length - current loop number - 1
                *   d      |     that many spaces
                     jdG   |     join code on the line (G) by spaces
               +           |   concatenate parts and print
PurkkaKoodari
fonte
5

Retina , 161 bytes

Agradecemos a FryAmTheEggman por salvar 2 bytes.

Esta resposta não é competitiva. O Retina recebeu algumas atualizações desde esse desafio e tenho certeza de que estou usando alguns dos recursos mais recentes (embora ainda não tenha verificado).

A contagem de bytes assume a codificação ISO 8859-1. A primeira linha contém um único espaço. Observe que a maioria dos ·pontos são na verdade pontos centrais (0xB7).

 

^
$._$*·¶
^·¶
¶
((^·|\2·)*)·\1{5}·+
$2·
^·*
$.&$* ·$&$&$.&$* 
M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$
+m`^( *·+)· *¶(?=\1)
$& 
·
 ·
O$`(·)|\S
$1
·
.
G-2`

Experimente online!

Bem...

Explicação

Parece mais fácil criar o layout primeiro usando apenas um único caractere ( ·nesse caso) e, em seguida, preencher o layout resultante com os caracteres de entrada. As principais razões para isso são que o uso de um único caractere permite que eu use referências anteriores e repetição de caracteres, onde o layout da entrada diretamente exigiria grupos de balanceamento caros.

 

Embora não pareça muito, esse primeiro estágio remove espaços da entrada.

^
$._$*·¶

Começamos anexando uma linha adicional que contém Mpontos centrais, onde Mé o comprimento da entrada (após remover os espaços).

^·¶
¶

Se a entrada tiver um único caractere, removeremos esse ponto central novamente. Este é um caso especial infeliz que não é coberto pela próxima etapa.

((^·|\2·)*)·\1{5}·+
$2·

Isso calcula o comprimento lateral necessário Nmenos 1. Aqui está como isso funciona: os números hexagonais centralizados têm a forma 3*N*(N-1) + 1. Como os números triangulares são N*(N-1)/2, isso significa que os números hexagonais são seis vezes um número triangular mais 1. Isso é conveniente porque combinar números triangulares (que na verdade são justos 1 + 2 + 3 + ... + N) em uma regex é bastante fácil com referências a frente. A (^·|\2·)*corresponde ao maior número triangular que pode. Como um bom bônus, $2manterá o índice desse número triangular. Para multiplicá-lo por 6, capturamos em grupo 1e combinamos mais 5 vezes. Garantimos que há pelo menos mais dois ·com ·os·+. Dessa forma, o índice do número triangular encontrado não aumenta até que exista um caractere a mais que um número hexagonal centralizado.

No final, essa partida nos dá dois a menos do que o comprimento lateral do hexágono necessário no grupo $2, então escrevemos isso de volta junto com mais um ponto central a ser obtido N-1.

^·*
$.&$* ·$&$&$.&$* 

Isso transforma nossa série de N-1pontos centrais em N-1espaços, 2N-1pontos centrais e outros N-1espaços. Observe que esse é o recuo máximo, seguido pelo diâmetro do hexágono, seguido pelo recuo novamente.

M!&m`(?<=(?= *(·)+)^.*)(?<-1>.)+(?(1)!)|^.+$

Isso é desagradávelmente longo, mas basicamente nos fornece todas as correspondências sobrepostas , que são: a) 2N-1caracteres longos e na primeira linha ou b) na segunda linha. Isso expande o resultado do estágio anterior para o hexágono completo, mas estranhamente recuado. Por exemplo, para entrada 12345678, obteríamos:

  ···
 ····
·····
···· 
···  
12345678

É por isso que também precisamos acrescentar espaços no estágio anterior.

+m`^( *·+)· *¶(?=\1)
$& 

Isso corrige o recuo das linhas após o centro, recuando repetidamente qualquer linha que seja mais curta que a anterior (ignorando os espaços finais), para obtermos o seguinte:

  ···
 ····
·····
 ···· 
  ···  
12345678

Agora apenas inserimos alguns espaços com

·
 ·

O que nos dá:

   · · ·
  · · · ·
 · · · · ·
  · · · · 
   · · ·  
12345678

Ufa, está feito.

O$`(·)|\S
$1

Hora de preencher a sequência de entrada nos pontos centrais. Isso é feito com a ajuda de um estágio de classificação. Combinamos todos os pontos centrais e cada caractere na última linha e os ordenamos pelo resultado da substituição fornecida. Essa substituição está vazia para os caracteres na última linha e ·para os pontos centrais, então o que acontece é que os pontos centrais são simplesmente classificados até o fim (já que a classificação é estável). Isso move os caracteres de entrada no lugar:

   1 2 3
  4 5 6 7
 8 · · · ·
  · · · · 
   · · ·  
········

Restam apenas duas coisas:

·
.

Isso transforma pontos centrais em períodos regulares.

G-2`

E isso descarta a última linha.

Martin Ender
fonte
1

JavaScript (ES6), 144 bytes

(s,n=1,l=0,p=0,m=s.match(/\S/g))=>m[n]?f(s,n+6*++l,l):[...Array(l+l+1)].map((_,i,a)=>a.map((_,j)=>j<l-i|j<i-l?``:m[p++]||`.`).join` `).join`\n`

Onde \nrepresenta o caractere literal de nova linha. Usa uma técnica para criar um hexágono que eu já usei em várias outras respostas da . Para o ES7, a obtenção de raízes quadradas é um pouco menor que a abordagem recursiva:

(s,p=0,m=s.match(/\S/g),l=(~-m.length/3)**.5+.5|0)=>[...Array(l+l+1)].map((_,i,a)=>a.map((_,j)=>j<l-i|j<i-l?``:m[p++]||`.`).join` `).join`\n`
Neil
fonte
1

Python 3 , 144 bytes

c=input().replace(' ','')
n=x=1
while x<len(c):x+=n*6;n+=1
c=c.ljust(x,'.')
while c:print(' '*(x-n)+' '.join(c[:n]));c=c[n:];n-=(len(c)<x/2)*2-1

Experimente online!

Isso usa uma quantidade bastante variável de espaço em branco à esquerda para hexágonos de tamanhos diferentes, mas a forma geral sobrevive.

Brincadeira
fonte