Animate la scala ASCII di Jacob


23

Potresti aver visto Jacob's Ladder nei musei della scienza per bambini. Se non hai familiarità con quello che sembrano, ci sono diverse immagini ed esempi di video su Wikimedia Commons . La sfida oggi è quella di creare una versione animata ASCII del gadget elettrico. Alla fine, dovrebbe assomigliare a questo:

LadderGIFExample


Costruzione di scale

Ecco la forma base di una scala con un'altezza ( H ) di 6:

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

I numeri a sinistra indicano semplicemente il numero di riga per questo esempio e non devono essere inclusi nell'output. Faremo riferimento a una determinata riga per numero ( R ). La riga 0 è il fondo ¯¯. Ogni riga da 1 a H è composta da quattro parti:

  • Uno spazio (U + 0020) ripetuto ( H - R ) volte
  • Una barra \rovesciata (U + 005C)
  • Uno spazio (U + 0020) ripetuto (2 * R ) volte
  • Una barra /(U + 002F)

La riga 0 è identica, tranne per il fatto che entrambe le barre vengono sostituite con un macron ¯(U + 00AF). Lo spazio bianco finale alla fine di ogni riga o sotto la scala è OK. Lo spazio bianco principale non lo è.


Costruzione ad arco

Una volta costruita la scala, è possibile creare archi tra il lato sinistro e destro. Un arco è interamente all'interno di una riga e sostituisce gli spazi tra il primo \e il finale /. Pertanto, la riga 2 avrà 4 caratteri nel suo arco, la riga 3 ne avrà 6 e così via. Ogni arco è composto usando le seguenti regole:

  • Gli unici caratteri consentiti sono _/¯\(U + 005F, U + 002F, U + 00AF, U + 005C)
  • Al fine di garantire un aspetto uniforme, qualsiasi ¯o /deve essere seguito da un ¯o\
  • Al fine di garantire un aspetto uniforme, qualsiasi _o \deve essere seguito da un _o/
  • Le due regole sopra si applicano anche ai bordi della scala
  • Le tre regole sopra indicano che il primo carattere nell'arco deve essere _o /e l'ultimo carattere deve essere _o \( \¯\_//non è valido su entrambe le estremità ma \_/¯\/è OK)
  • Ci deve essere una possibilità diversa da zero per ogni personaggio ammissibile che si verifichi in un dato punto
  • Ogni arco è indipendente da ogni altro arco

Animazione

La vita di un singolo arco viene creata avviandolo alla riga 1 e "spostandolo" di una riga alla volta fino a raggiungere la cima. IE, prima genera un arco nella riga 1, quindi ripristina gli spazi e genera un arco nella riga 2 e così via. Dato un numero di archi da mostrare ( N ), mostra la vita completa di tanti archi uno alla volta usando le seguenti linee guida:

  • Solo un arco dovrebbe essere "vivo" alla volta. L'arco successivo non può iniziare fino a quando quello attuale raggiunge la cima e quindi si spegne.
  • Ogni riga della vita dell'arco dovrebbe essere mostrata esattamente per un fotogramma
  • Dovrebbe esserci un fotogramma solo della scala base (senza archi) prima che inizi un nuovo arco (opzionale prima del primo arco)
  • L'animazione dovrebbe mostrare la vita completa di N archi. Se N = 0, dovrebbe animare per sempre archi casuali fino all'arresto.
  • Se N > 0, puoi comunque ripetere l'animazione per sempre ma deve essere sempre un loop con gli stessi archi. (La GIF di esempio nella parte superiore di questo post ha H = 6 e N = 3 ma è in loop per sempre.)
  • L'animazione dovrebbe avvenire sul posto. Cioè, ogni frame dovrebbe sovrascrivere completamente il frame successivo ed essere nella stessa posizione.
  • La lunghezza di ogni fotogramma può essere qualsiasi cosa tu voglia ma rendila guardabile da un essere umano (IE, usa il tuo buon senso: 0,01 s / frame e 30 s / frame sono entrambi inaccettabili).

Input Output

  • Input e Output possono essere in qualsiasi formato standard
  • Puoi esportare una GIF, scrivere del testo sullo schermo, produrre un singolo file per ogni fotogramma o qualsiasi altro mezzo ragionevole
  • Sono vietate le scappatoie standard
  • L'altezza della scala H sarà un numero intero positivo
  • Il numero di archi per mostrare N sarà un numero intero non negativo
  • Sia H che N sono presi come input in qualsiasi ordine tu scelga (includi l'ordine nella tua risposta)

Condizioni vincenti

Questo è quindi vince il codice più corto.

sandbox


1
L'arco può essere generato simmetrico dal suo centro? Non riesco a vedere alcuna limitazione a ciò nelle regole
Dead Possum,

Posso stampare ogni fotogramma dopo l'altro sulla console?
TFeld

@DeadPossum Pensavo che avessi ragione, nonostante non sembrasse molto lampante, ma in realtà è vietato dalla combinazione di due regole: 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. Per essere simmetrici, sia il primo che l'ultimo personaggio dovrebbero essere _ogni volta, il che significa che non vi è alcuna possibilità che si /verifichi uno o l'altro.
ingegnere Toast il

@TFeld Finché ogni fotogramma appare nella stessa posizione sullo schermo, sì. Ciò significa che dovrai cancellare la console (o forse scorrere verso il basso, se possibile) ogni volta.
Ingegnere Toast,

2
Il requisito macron significa che QBasic non può competere? Utilizza CP437 , in cui si 0xAFtrova il punto di codice ».
DLosc

Risposte:


5

Python 2 , 287 271 270 276 275 byte

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)

Provalo online!

Non cancella lo schermo su tio, ma funziona in una console.

Gif di esso in esecuzione:

inserisci qui la descrizione dell'immagine


Un po 'subdolo ma è possibile utilizzare print'\n'*99invece di os.system('cls')e perdere l' osimportazione. Non funziona ancora su TIO ma funziona su console sia Windows che Linux.
ElPedro,

1
Dovrebbe esserci un fotogramma solo della scala base (senza archi) prima che inizi un nuovo arco (opzionale prima del primo arco)
wastl

5
Penso che tu stia usando trattini (U + 002D) invece di macron (U + 00AF). Non penso che aumenterà il conteggio dei byte per risolverlo. Inoltre, come ha sottolineato @wastl, non esiste una cornice di scala vuota tra gli archi.
ingegnere Toast il

La riga inferiore usa i macron, ma gli archi no
Ingegnere Toast

1
@EngineerToast Risolto ora :)
TFeld

4

JavaScript (ES6), 245 byte

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>

Il conteggio dei byte presuppone la codifica ISO-8859-1.


È possibile ridurlo a 242 definendo A=x=>[...Array(x)].map;all'inizio e sostituendo entrambi gli usi.
Bary12

@ Bary12 Non puoi tornare map, questa è solo una proprietà Array.prototypee non serve da sola. Ho provato le versioni funzionanti ma sono uscite tutte più lunghe di 245 byte.
Neil,

3

C (gcc) , 406 byte

#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");}}}

Provalo online!

Descrizione:

#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

Nota: funziona davvero solo in Xterm ... molti emulatori di terminali non supportano la posizione di salvataggio / ripristino.
LambdaBeta,

La riga inferiore è la riga 0 e ha solo due macron. Non lo è \--/. Questa è probabilmente una soluzione semplice. Sei in grado di catturare e pubblicare una GIF funzionante in Xterm?
ingegnere Toast il

Purtroppo mi mancano gli strumenti per farlo (solo giocando durante i tempi di costruzione al lavoro). Aggiornerò con la riga 0 corretta, tuttavia, è una soluzione semplice.
LambdaBeta,

Non
wastl

la modifica di k = 1 in k = 0 consente di correggere il costo di ... 0 byte. Si aggiornerà presto.
LambdaBeta,

2

PowerShell , 347 319 byte

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}}}

Provalo online! Non riesco $argsa giocare bene, quindi il link chiama la funzione senza cancellare la 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
        }
    }
}

È una cosa piccola, ma la riga inferiore è trattini anziché macron. È una modifica a zero byte$l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯"
ingegnere Toast il

1
¯ \ (° _o) / ¯ oops! scambiato in macron: p
Peter Vandivier,

1
Non conosco troppo bene PowerShell, ma puoi rimuovere la maggior parte delle nuove linee. Inoltre for($x=0;$x-ne$n;$x++)può essere for($x=0;$x++-ne$n;). Sono riuscito a farlo scendere a 324 byte (321 caratteri) . Suggerimenti per giocare a golf in <tutte le lingue> e Suggerimenti per giocare a golf in PowerShell potrebbero anche essere interessanti da leggere.
Kevin Cruijssen,

1
sleep 1salva un po '(il valore predefinito è -secondi) ma è piuttosto lento ma comunque ragionevole, sleep -m 99è piuttosto veloce ma anche ragionevole. Salva 5/1 byte a seconda di ciò che ti piace. Non ho controllato il tentativo di Kevin ma anche functionquello filterè un byte libero.
Veskah,

1

Rubino , 293 byte

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

Provalo online!

Sono su Windows, quindi stampa molti "\ n" per cancellare la console. Accetta 2 argomenti ne hcome due righe su stdin.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.