Animar a escada ASCII de Jacob

23

Você pode ter visto Jacob's Ladder em museus de ciência infantil. Se você não estiver familiarizado com a aparência, existem vários exemplos de imagens e vídeos no Wikimedia Commons . O desafio hoje é criar uma versão ASCII animada do dispositivo elétrico. No final, deve ser algo como isto:

LadderGIFExample


Construção de escada

Aqui está o formato básico de uma escada com uma altura ( H ) de 6:

6   \            /
5    \          /
4     \        /
3      \      /
2       \    /
1        \  /
0         ¯¯

Os números à esquerda simplesmente indicam o número da linha para este exemplo e não devem ser incluídos na saída. Vamos nos referir a uma determinada linha pelo seu número ( R ). A linha 0 é a parte inferior ¯¯. Cada linha 1 a H é composta por quatro partes:

  • Um espaço (U + 0020) repetido ( H - R ) vezes
  • Uma barra invertida \(U + 005C)
  • Um espaço (U + 0020) repetido (2 * R ) vezes
  • Uma barra /(U + 002F)

A linha 0 é idêntica, exceto que as duas barras são substituídas por um macron ¯(U + 00AF). O espaço em branco à direita no final de cada linha ou abaixo da escada está OK. Os principais espaços em branco não são.


Construção de arco

Depois que a escada é construída, você pode criar arcos entre o lado esquerdo e o direito. Um arco está inteiramente dentro de uma linha e substitui os espaços entre o início \e o final /. Portanto, a linha 2 terá 4 caracteres em seu arco, a linha 3 terá 6 e assim por diante. Cada arco é composto usando as seguintes regras:

  • Os únicos caracteres permitidos são _/¯\(U + 005F, U + 002F, U + 00AF, U + 005C)
  • Para garantir uma aparência suave, qualquer um ¯ou /deve ser seguido por um ¯ou\
  • Para garantir uma aparência suave, qualquer um _ou \deve ser seguido por um _ou/
  • As duas regras acima também se aplicam às bordas da escada
  • As três regras acima significam efetivamente que o primeiro caractere do arco deve ser _ou /e o último caractere deve ser _ou \( \¯\_//é inválido nas duas extremidades, mas \_/¯\/está OK)
  • Deve haver uma chance diferente de zero para cada caractere permitido ocorrer em um determinado ponto
  • Cada arco é independente de qualquer outro arco

Animação

A vida de um único arco é criada iniciando-o na linha 1 e "movendo-o" uma linha por vez até chegar ao topo. IE, primeiro gere um arco na linha 1, depois defina-o para espaços e gere um arco na linha 2, e assim por diante. Dado um número de arcos para mostrar ( N ), mostre a vida útil completa de muitos arcos, um de cada vez, usando as seguintes diretrizes:

  • Apenas um arco deve estar "vivo" a qualquer momento. O próximo arco não pode ser iniciado até que o atual chegue ao topo e depois se apague.
  • Cada linha da vida do arco deve ser mostrada para exatamente um quadro
  • Deve haver um quadro apenas da escada básica (sem arcos) antes do início de um novo arco (opcional antes do primeiro arco)
  • A animação deve mostrar a vida útil completa de N arcos. Se N = 0, ele deve animar arcos aleatórios para sempre até parar.
  • Se N > 0, você ainda pode repetir a animação para sempre, mas ela deve ser repetida repetidamente dos mesmos arcos. (O GIF de exemplo na parte superior desta postagem tem H = 6 e N = 3, mas faz um loop para sempre.)
  • A animação deve ocorrer no local. Ou seja, cada quadro deve substituir completamente o próximo quadro e estar no mesmo local.
  • O comprimento de cada quadro pode ser o que você quiser, mas torná-lo assistível por um ser humano (ou seja, use seu bom senso: 0,01s / quadro e 30s / quadro são inaceitáveis).

Entrada / Saída

  • Entrada e Saída podem estar em qualquer formato padrão
  • Você pode exportar um GIF, escrever texto na tela, gerar um único arquivo para cada quadro ou qualquer outro meio razoável
  • As brechas padrão são proibidas
  • A altura da escada H será um número inteiro positivo
  • O número de arcos para mostrar N será um número inteiro não negativo
  • Ambos H e N são tomadas como entradas em qualquer ordem que você escolher (Por favor inclua a ordem na sua resposta)

Condição vencedora

Isso é então o código mais curto vence.

Caixa de areia

Engenheiro Toast
fonte
1
O arco pode ser gerado simétrico por seu centro? Eu não posso ver restrição ao de regras
Morto Possum
Posso imprimir cada quadro um após o outro no console?
perfil completo de Tfeld
@DeadPossum Eu estava pensando que você estava certo, apesar do fato de não parecer muito relâmpago, mas na verdade é proibido pela combinação de duas regras: the first character in the arc must be _ or / and the last character must be _ or \ e There must be a non-zero chance for each allowable character to occur at a given point. Para ser simétrico, tanto o primeiro quanto o último caractere teriam que ser _todas as vezes, o que significa que há zero chance de /ocorrer uma ocorrência de `\`.
Engineer Toast
@TFeld Desde que cada quadro apareça no mesmo local na tela, sim. Isso significa que você terá que limpar o console (ou talvez rolar para baixo, se possível) a cada vez.
Engineer Toast
2
O requisito de macron significa que o QBasic não pode competir? Ele usa o CP437 , no qual 0xAFestá o ponto de código ».
DLosc

Respostas:

5

Python 2 , 287 271 270 276 275 bytes

import time,random
r,n=input()
c=n*-~r or-r
while c:
 c-=1;L=[list(' '*i+'\\'+'  '*(r-i)+'/')for i in range(r)];x=c%-~r;time.sleep(1);y=x+1;exec"L[x][y]=random.choice('\xaf/\_'[L[x][y-1]in'\_'::2][y==2*r-x:]);y+=1;"*2*(r-x)
 for l in['']*99+L+[' '*r+'\xaf'*2]:print''.join(l)

Experimente online!

Não limpa a tela no tio, mas funciona em um console.

Gif dele em execução:

insira a descrição da imagem aqui

TFeld
fonte
Um pouco sorrateiro, mas você pode usar em print'\n'*99vez de os.system('cls')e perder a osimportação. Ainda não funciona no TIO, mas funciona nos consoles Windows e Linux.
ElPedro
1
Não deve [ser] um quadro de apenas a escada básica (sem arcos) antes de um novo começa a arco (opcional antes do primeiro arco)
Wastl
5
Eu acho que você está usando hífens (U + 002D) em vez de macrons (U + 00AF). Eu não acho que isso aumentará sua contagem de bytes para corrigi-lo. Além disso, como @wastl apontou, não há estrutura de escada vazia entre arcos.
Engenheiro Toast
A última linha usa macrons mas os arcos não
Engenheiro Toast
1
@EngineerToast Corrigido agora :) #
TFeld
4

JavaScript (ES6), 245 bytes

f=(o,h,n,i=0)=>(o.innerText=[...Array(h+1)].map((_,j)=>` `.repeat(j)+(j<h?`\\${[...Array(w--*2)].map((_,k)=>h+~j-i?` `:k>w*2|Math.random()<.5?s[s=t,1]:s[s=`¯\\`,0],s=t=`/_`).join``}/`:`¯¯`),w=h).join`
`,(++i<h||--n)&&setTimeout(f,250,o,h,n,i%h))
Height: <input type=number min=1 value=6 id=h><br>Arcs: <input type=number min=0 value=3 id=n><br><input type=button value=Go! onclick=f(o,+h.value,+n.value)><pre id=o></pre>

A contagem de bytes assume a codificação ISO-8859-1.

Neil
fonte
É possível reduzi-lo para 242, definindo A=x=>[...Array(x)].map;no início e substituindo os dois usos.
Bary12
@ Bary12 Você não pode voltar map, isso é apenas uma propriedade Array.prototypee sem uso por si só. Eu tentei versões de trabalho, mas todas saíram com mais de 245 bytes.
911 Neil
3

C (gcc) , 406 bytes

#define p(X) printf(X),usleep(999)
#define x(X) do{s[X]=0;p(s);s[X]=' ';}while(0)
char c[2][2]={95,47,92,'¯'};R;i;j;k;a(){char s[2]={92,0};for(j=0;j<2*R-1;++j,p(s))*s=c[*s<50][rand()%2];*s=c[*s<50][0];p(s);}f(H,N){char s[99];for(i=0;i<99;++i)s[i]=' ';p("\e[s");for(i=0;;++i){i%=(N?N:i+1);srand(i^H^N);for(k=1;k<H;++k){for(R=H;--R;){x(H-R+1);p("\\");if(R==k)a();else x(2*R);p("/\n");}x(H);p(" ¯¯\n\e[u");}}}

Experimente online!

Descrição:

#define p(X) printf(X),usleep(999)              // Define p to printf(p) + delay
#define x(X) do{s[X]=0;p(s);s[X]=' ';}while(0)  // Define x(X) to print X spaces
                                                // This uses a string s full of
                                                // spaces and adds the null
                                                // terminator where approrpiate
char c[2][2]={95,47,92,'¯'};                    // 2d array of 'next arc' options
R;i;j;k;                                        // Variables
a(){                                            // a() -> print arc for row R
    char s[2]={92,0};                           // s is a string of next char
                                                // initialize to backslash
    for(j=0;j<2*R-1;++j                         // loop over each character
            ,p(s))                              // printing s each time
        *s=c[*s<50][rand()%2];                  // set s to the next arc char
    *s=c[*s<50][0];                             // set s to the 'first' arc char
                                                // note that in definition of c
                                                // first means appropriate as
                                                // final character before /
    p(s);}                                      // print the last character
f(H,N){                                         // f(H,N) -> print jacob ladder
    char s[99];for(i=0;i<99;++i)s[i]=' ';       // this is the space string for x
    p("\e[s");                                  // ANSI terminal save position
    for(i=0;;++i){i%=(N?N:i+1);                 // loop i->N (or i->INT_MAX if N=0)
        srand(i^H^N);                           // seed random with i XOR H XOR N
        for(k=1;k<H;++k){                       // for each row (bottom to top)
            for(R=H;--R;){                      // for each row (top to bottom)
                x(H-R+1);p("\\");               // print left "    \"
                if(R==k)                        // if on the arc row
                    a();                        // print the arc
                else x(2*R);                    // otherwise print spaces
                p("/\n");}                      // finish off the row
            x(H);p(" ¯¯\n\e[u");}}}             // print bottom line and move back
LambdaBeta
fonte
Nota: realmente funciona apenas no Xterm ... muitos emuladores de terminal simplesmente não suportam a posição de salvar / restaurar.
LambdaBeta 4/18
A linha inferior é a linha 0 e possui apenas duas macrons. Não é \--/. Provavelmente é uma solução fácil. Você é capaz de capturar e postar um GIF dele trabalhando no Xterm?
Engenheiro Toast
Infelizmente, eu não tenho as ferramentas para fazer isso (apenas brincando durante os tempos de construção no trabalho). Vou atualizar com a linha 0 correta, porém, é uma solução fácil.
LambdaBeta 4/18
Inválido: Não deve [ser] um quadro de apenas a escada básica (sem arcos) antes de um novo começa a arco (opcional antes do primeiro arco)
Wastl
alterar k = 1 para k = 0 corrige o custo de ... 0 bytes. Será atualizado em breve.
LambdaBeta 4/18
2

PowerShell , 347 319 bytes

filter c{Param($h,$n)if($n-eq0){$n=-1}for($x=0;$x++-ne$n;){($h..1)|%{$l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯"
$r="Get-Random"
$i=0
$z=-join((0..(($h-$_)*2))|%{$i=switch($i%3){0{&$r 0,1}default{&$r 2,3}}"_/¯\"[$i]})+"_\\_"[$i]
$l[$_-1]=($l[$_-1]).Substring(0,$_)+"$z/"
cls
$l
Sleep -m 250}}}

Experimente online! Não foi possível obter $argsum bom desempenho; portanto, o link chama a função sem limpar o console.

Ungolfed

filter c{
    Param($h,$n)
    if($n -eq 0){$n=-1} # inelegant swap to allow for an infinite loop. 
                        # Curse you zero-indexing!
    for($x=0;$x++-ne$n;)
    {
        ($h..1) | % {         
            $l=(($h..1)|%{ # (( double paren is needed to induce each line 
                           # as a new array element
                "$(" "*($h-$_))\$(" "*$_*2)/" # offset by total height. 
                                              # N spaces + rung + N*2 spaces + rung
            })+"$(" "*$h)¯¯" # last line is the floor of the ladder

            $r="Get-Random" # shorter to declare once and execute with & operator

            $i=0 # initialize $i so we choose only _ or / for the first char

            $z=-join( # build an electric ZAP!
                (0..(($h-$_)*2))|%{                    
                    $i = switch($i%3) { # choose next char based on previous selection
                        0{&$r 0,1}
                        default{&$r 2,3}
                    }    
                    "_/¯\"[$i]
                }
            )+"_\\_"[$i] # final char is \ or _ to rejoin the ladder        
            $l[$_-1]=($l[$_-1]).Substring(0,$_)+"$z/" # select one rung of the ladder 
                                                      # append an electric ZAP!                
            cls # clear the console
            $l  # display the ladder
            Sleep -m 250
        }
    }
}
Peter Vandivier
fonte
É uma coisa pequena, mas a linha de baixo são hífens em vez de macrons. É um zero-byte mudança$l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯"
Engenheiro Toast
1
¯ \ (° _o) / ¯ opa! trocado em macrons: p
Peter Vandivier 06/06
1
Não conheço o PowerShell muito bem, mas você pode remover a maioria das novas linhas. Além disso for($x=0;$x-ne$n;$x++)pode ser for($x=0;$x++-ne$n;). Eu reduzi para 324 bytes (321 caracteres) fazendo isso. Dicas para jogar golfe em <todos os idiomas> e Dicas para jogar golfe no PowerShell também podem ser interessantes para ler.
Kevin Cruijssen
1
sleep 1economiza um pouco (o padrão é -segundos), mas é bem lento, mas ainda razoável, sleep -m 99é muito rápido, mas também razoável. Economiza 5/1 bytes, dependendo do que você gosta. Não verificar a tentativa de Kevin, mas functiona filteré um byte livre também.
Veskah #
1

Ruby , 293 bytes

m={}
"   __/\\_/¯¯\\/¯\\".chars.each_slice(3){|e|u,*v=e;m[u]=v}
a=->l,c{l<1?"/":(d=m[c].sample;c+a[l-1,d])}
n=gets.to_i
h=gets.to_i
o=0
while o<n||n<1
h.times{|i|puts (0...h).map{|j|" "*j+"\\"+a[2*(h-j),i==h-j-1?["_","/"].sample: " "]}*"\n";puts" "*h+"¯¯";sleep(0.3);puts"\n"*99}
o+=1
end

Experimente online!

Eu estou no Windows, então apenas imprime muitos "\ n" para limpar o console. Leva 2 argumentos ne hduas linhas em stdin.

crashoz
fonte