Furacão Matthew e os raios

27

Desafio

Inspirados por esse desafio e pelo desagradável furacão Matthew , estaremos gerando alguns relâmpagos dinamicamente.

n = 15:

   \
   /\
  /  \
 /   /
/\  /\
 /  \ \
/   / /\
   /\ \
  / /  \
 /\ \  /\
  /  \ \
 /\  /  
   \
    \
    /\

Entrada

Número inteiro positivo ndetermina a profundidade do raio.

Regras e restrições

  • /e \deve ser usado
  • A probabilidade que guia a direção do raio é a seguinte:
    • 25% Divide-se em 2 caminhos
    • Caminho de 25% atinge o beco sem saída
    • 25% vão para a esquerda
    • 25% dá certo
    • Existem algumas exceções sobre sobreposição e beco sem saída abaixo:
  • O código não deve ser determinístico, um novo raio deve ser gerado aleatoriamente toda vez
  • Os parafusos não devem se sobrepor: por exemplo, se já houver um parafuso à esquerda do parafuso atual, o parafuso atual deve terminar ou ir para a direita, mas não para a esquerda ou dividir (a probabilidade ainda se aplica, neste caso, se torna 50% final / 50% à direita)
  • Se não existir outro caminho de divisão disponível, o caminho não deverá terminar: por exemplo, no início, quando houver apenas 1 caminho, o caminho não deverá terminar até que seja dividido, também se aplica quando houver vários caminhos, mas todos, exceto um caminho, estiverem mortos. , (a probabilidade se torna 33% dividida / 33% esquerda / 33% direita) sua meta é chegar ao fundo
  • Espaços em branco podem ser adicionados à esquerda (tudo o que você precisa deve ser apenas altura 1)
  • No entanto, você deseja gerar o parafuso, você pode ir de baixo para cima, da esquerda para a direita, etc. Desde que todas as regras acima sejam atendidas

Outro exemplo

n = 10

 \
 /
 \
 /\
  /\
 / /
/\ \
 / /\
 \   \
 /

O furacão Matthew aparentemente está disparando raios vermelhos no céu, chamados sprites

Fique seguro e divirta-se jogando golfe! Golfe com responsabilidade apenas quando estiver em uma área segura!

Zukaberg
fonte
7
Stay safe and have fun golfing!Talvez também especifique que, se o EAS ocorrer, abandone tudo e siga as ordens! Código de golfe não é sua prioridade em tal situação.
Erik the Outgolfer
17
@EriktheGolfer você não é um verdadeiro jogador de golfe.
Blue
4
Não acredito que "o caminho mais central deva ser o que chega ao chão" é consistente com o restante da descrição da geração aleatória. Por exemplo, é possível que o parafuso original seja dividido aleatoriamente duas vezes e depois os dois parafusos do meio terminem; como essa possibilidade pode ser substituída, preservando as probabilidades indicadas?
Greg Martin
Além disso, o que acontece se (por exemplo) as duas primeiras etapas forem duas divisões? Então os dois parafusos do meio estão se tocando, o que parece problemático, mas também não é descartado pelos casos especiais.
Greg Martin
@GregMartin Bom ponto na parte central, originalmente eu esperava que gerasse um raio balanceado, mas agora que penso nisso, mesmo sem essa restrição, 50% das vezes deve terminar em algum lugar no meio, uma profundidade de 15 teria apenas 1-2% de chance onde o caminho mais à direita ou à esquerda pousa. Vou remover essa regra. E para a parte de divisão de 2 etapas, a única coisa a ser evitada é que não há 2 caminhos que dois caminhos se juntem: \/a qualquer momento.
Zukaberg 07/10

Respostas:

6

Perl, 92 90 89 84 bytes

Inclui +1 para -n

Dê altura em STDIN:

perl -M5.010 bolt.pl <<< 15

bolt.pl:

#!/usr/bin/perl -n
map{$_=$;until$;=$_,s/.6|3.?/53|16*rand/eg,/3|6/>/36/;say y|3615|\\/ |r}(1x$_.6)x$_

Explicação

Se você chamar o deslocamento do ponto inicial 0 (um ponto está no canto de uma caixa de caractere), na próxima linha você poderá ter ido para a esquerda ou direita (ou não) e poderá acabar com pontos em compensações -1,1. A próxima linha fornece os -2,0,2possíveis desvios, etc. Eles diferem por 2. Se você chamar o caractere para o canto inferior esquerdo de um ponto par e o caractere para o canto inferior direito, você poderá estender isso para atribuir pares ou ímpares a cada posição do personagem em uma fileira que alterna par e ímpar (na verdade, todo o plano é lado a lado em um padrão quadriculado). Uma posição par pode ter um /ou , uma posição ímpar pode ter \ou .

O personagem imediatamente antes de a /está em uma posição ímpar, de modo que pode ser um \ou outro , mas \/é proibido apenas para que seja possível. Da mesma forma, o caractere após a \ deve ser a (assumindo que a linha seja preenchida com espaços suficientes à esquerda e à direita, para que os limites da linha não sejam problema). Assim, um raio continua na próxima linha sempre diretamente abaixo de a \ou abaixo de a /. Em qualquer caso, o ponto mais baixo está no meio e a fileira seguinte pode ter um de , /, \ou /\directamente abaixo da parte superior 2 caracteres. Então, para gerar a próxima linha, posso simplesmente substituir qualquer \ou/por qualquer uma dessas 4 expansões com igual probabilidade (você também pode substituir independentemente o primeiro caractere por ou /e o segundo caractere por ou \). Em perl, você pode fazer isso com algo como:

s#\\ | /#("  "," \\","/ ","/\\")[rand 4]#eg

Se a linha resultante no entanto contém \/(proibido juntar-se) ou não /ou \em todos (dies parafuso e não chegar ao fundo), o resultado é inválido. Nesse caso, jogo fora toda a linha e simplesmente tento novamente. Sempre existe uma continuação válida e, se você tentar com frequência, será encontrada uma (por exemplo, tudo morre, exceto 1 fluxo). Essa é uma distribuição de probabilidade um pouco diferente do algoritmo anti-sobreposição sugerido, mas acho que isso é realmente melhor, pois não possui viés direcional. A validade pode ser testada de maneira golfista usando

m#\\|/#>m#\\/#

O problema aqui é que a substituição aleatória é muito lenta e todas essas \fugas também comem bytes. Por isso, decidi construir minhas linhas usando cadeias de dígitos e substituir os dígitos apropriados por , /e \pouco antes da impressão. A substituição aleatória básica é

53|16*rand

o que dá uma das 53, 55, 61ou 63com igual probabilidade. Eu então interpreto 5e 1como , 3como \e 6como /. Isso explica a impressão da linha:

say y|3615|\\/ |r

Em uma competição de golfe séria, eu começaria a explorar sistematicamente fórmulas mágicas alternativas, mas isso deve ser muito bom (a menos de 3 bytes do ideal)

O restante dos componentes do programa:

1x$_.6

Isso inicializa $_(veja o próximo mapa) para espaços de altura seguidos por a /. Esta é uma linha invisível acima da primeira que está sendo impressa e garante que o campo seja amplo o suficiente para que o parafuso nunca fique sem espaço à esquerda

map{ ... ; say ...}(1x$_.6)x$_

Eu processarei essa mesma altura inicial da string vezes imprimindo uma nova linha a cada vez

$_=$;until$;=$_,...

Salve a linha atual em $;. Se a substituição for uma restauração inválida $_de$;

s/.6|3.?/53|16*rand/eg

Faça a substituição real. Não preciso verificar o que é antes /ou depois, \pois deve ser um espaço. Isso é conveniente, pois o espaço pode ser representado por um 1ou por um 5. Como eu apenas coloquei a corda à esquerda, o espaço após a \continuação ainda pode estar ausente, portanto, torne esse caractere opcional

/3|6/>/36/

Verifique se a nova linha é válida

Ton Hospel
fonte
+1 limpo! você deve incluir este testador on - lineperl -M5.010 main.pl <<< 25 , eu tenho conseguido ótimos resultados!
Zukaberg 07/10
Importa-se de explicar um pouco como isso funciona? Estou me divertindo muito gerando-os haha, honestamente, não previ bons resultados.
Zukaberg 07/10
Infelizmente, você precisa adicionar 3 bytes para-n , porque o espaço e o traço também contam. A mesma regra é para argumentos de linha de comando. Veja "Invocações Especiais", segundo ponto-marcador: conto-os como uma diferença na contagem de caracteres até a menor invocação equivalente sem eles.
Erik the Outgolfer
1
@EriktheGolfer Não, +1 está OK porque este programa funciona bem a partir da linha de comando, com -nEapenas 1 caractere a mais -E. (Consulte o artigo que você referenciou. Isso também elimina a necessidade -M5.010) Eu sempre apresento meu código como arquivos porque é mais conveniente, mas sempre conto opções como esta: Se puder ser executado a partir da linha de comando, não conto o espaço e o traço. Se ele deve estar em um arquivo (por exemplo, porque ele usa do$0) , conto o espaço e o traço
Ton Hospel 8/16/16
@ TonHospel Oh, eu não sabia que você usava -E. Se sim, você é bom.
Erik the Outgolfer
0

JavaScript (ES6), 154 bytes

f=(n,r=[],s=" ".repeat(n)+"/",t=s.replace(/ \/|\\ |\\$/g,_=>"  /  \\/\\".substr(Math.random()*8&6,2)))=>n?/^ +$|\\\//.test(t)?f(n,r,s):f(n-1,[...r,t],t):r
<input type="number" min=1 oninput=o.textContent=f(this.value).join`\n`><pre id=o>

Eu lutei com a implementação até ver a resposta do @ TonHospel, quando ela apenas degenerou em uma porta. Saída de amostra:

         /\
        / /\
       /\   \
        /\   \
         /\  /
          / /\
         / / /\
            / /\
            \   \
             \
Neil
fonte