Animacja ASCII drabiny Jacoba

23

Być może widziałeś drabinę Jakuba w muzeach nauki dla dzieci. Jeśli nie znasz ich wyglądu, na Wikimedia Commons znajduje się kilka zdjęć i przykładów wideo . Dzisiejsze wyzwanie polega na stworzeniu animowanej wersji gadżetu elektrycznego ASCII. Ostatecznie powinien wyglądać mniej więcej tak:

LadderGIFExample


Konstrukcja drabiny

Oto podstawowy kształt drabiny o wysokości ( H ) 6:

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

Liczby po lewej wskazują po prostu numer wiersza dla tego przykładu i nie powinny być uwzględniane w danych wyjściowych. Odniesiemy się do danego wiersza po jego numerze ( R ). Wiersz 0 jest na dole ¯¯. Każdy rząd od 1 do H składa się z czterech części:

  • Spacja (U + 0020) powtórzona ( H - R ) razy
  • \Cięcie z tyłu (U + 005C)
  • Spacja (U + 0020) powtórzona (2 * R ) razy
  • /Cięcie do przodu (U + 002F)

Wiersz 0 jest identyczny, z wyjątkiem tego, że oba ukośniki zostały zastąpione makronem ¯(U + 00AF). Końcowe białe znaki na końcu każdej linii lub pod drabiną są w porządku. Wiodące białe znaki nie są.


Konstrukcja łuku

Po zbudowaniu drabiny możesz tworzyć łuki między lewą i prawą stroną. Jeden łuk znajduje się całkowicie w rzędzie i zastępuje odstępy między prowadzącym \a końcowym /. Dlatego rząd 2 będzie miał 4 znaki w swoim łuku, rząd 3 będzie miał 6 i tak dalej. Każdy łuk składa się według następujących zasad:

  • Jedynymi dozwolonymi znakami są _/¯\(U + 005F, U + 002F, U + 00AF, U + 005C)
  • Aby zapewnić gładki wygląd, po którymkolwiek ¯lub /musi następować ¯lub\
  • Aby zapewnić gładki wygląd, po którymkolwiek _lub \musi następować _lub/
  • Dwie powyższe zasady dotyczą również krawędzi drabiny
  • Trzy powyższe zasady skutecznie oznaczają, że pierwszy znak na łuku musi być _lub, /a ostatni znak musi być _lub \( \¯\_//jest nieprawidłowy na obu końcach, ale \_/¯\/jest OK)
  • Musi istnieć niezerowa szansa, aby każda dopuszczalna postać pojawiła się w danym punkcie
  • Każdy łuk jest niezależny od każdego innego

Animacja

Żywotność pojedynczego łuku jest tworzona przez rozpoczęcie go w pierwszym rzędzie i „przesunięcie” go w górę o jeden rząd na raz, aż osiągnie szczyt. IE, najpierw wygeneruj łuk w rzędzie 1, następnie ustaw go z powrotem w spacje i wygeneruj łuk w rzędzie 2 i tak dalej. Biorąc pod uwagę liczbę łuków do pokazania ( N ), pokaż pełne życie wielu łuków pojedynczo, stosując następujące wytyczne:

  • Tylko jeden łuk powinien być jednocześnie „żywy”. Następny łuk nie może rozpocząć się, dopóki bieżący nie osiągnie szczytu, a następnie nie zgaśnie.
  • Każdy rząd okresu łuku powinien być pokazany dla dokładnie jednej klatki
  • Przed rozpoczęciem nowego łuku powinna być jedna rama tylko podstawowej drabiny (bez łuków) (opcjonalnie przed pierwszym łukiem)
  • Animacja powinna pokazywać pełne życie N łuków. Jeśli N = 0, powinien animować losowe łuki na zawsze, aż do zatrzymania.
  • Jeśli N > 0, możesz nadal zapętlać animację na zawsze, ale musi to być pętla tych samych łuków w kółko. (Przykładowy GIF na górze tego postu ma H = 6 i N = 3, ale zapętla się na zawsze.)
  • Animacja powinna odbywać się w miejscu. Oznacza to, że każda ramka powinna całkowicie zastąpić następną ramkę i znajdować się w tym samym miejscu.
  • Długość każdej klatki może być dowolna, ale umożliwia oglądanie jej przez człowieka (IE, należy kierować się zdrowym rozsądkiem: niedopuszczalne są zarówno 0,01 s / ramka, jak i 30 s / ramka).

Wejście wyjście

  • Dane wejściowe i wyjściowe mogą być w dowolnym standardowym formacie
  • Możesz wyeksportować plik GIF, napisać tekst na ekranie, wydrukować pojedynczy plik dla każdej klatki lub w inny rozsądny sposób
  • Standardowe luki są zabronione
  • Wysokość drabiny H będzie dodatnią liczbą całkowitą
  • Liczba łuków pokazujących N będzie nieujemną liczbą całkowitą
  • Zarówno H, jak i N są przyjmowane jako dane wejściowe w dowolnej kolejności, którą wybierzesz (w odpowiedzi podaj kolejność)

Warunki wygranej

To jest więc wygrywa najkrótszy kod.

Piaskownica

Inżynier Toast
źródło
1
Czy łuk może być generowany symetrycznie przez jego środek? Nie widzę ograniczenia w przepisach
Dead Possum
Czy mogę wydrukować każdą ramkę po sobie na konsoli?
TFeld
@DeadPossum Myślałem, że masz rację, mimo że nie wyglądałby on błyskawicznie, ale w rzeczywistości jest to niedozwolone przez połączenie dwóch zasad: the first character in the arc must be _ or / and the last character must be _ or \ i There must be a non-zero chance for each allowable character to occur at a given point. Aby być symetrycznym, zarówno pierwsza, jak i ostatnia postać musiałyby być za _każdym razem, co oznacza, że ​​nie ma żadnej szansy na wystąpienie ani jednego, /ani „\”.
Inżynier Toast
@TFeld Tak długo, jak każda ramka pojawia się w tym samym miejscu na ekranie, tak. Oznacza to, że będziesz musiał wyczyścić konsolę (lub przewinąć w dół, jeśli to możliwe) za każdym razem.
Inżynier Toast
2
Czy wymóg dotyczący makr oznacza, że ​​QBasic nie może konkurować? Używa CP437 , w którym 0xAFznajduje się punkt kodowy ».
DLosc

Odpowiedzi:

5

Python 2 , 287 271 270 276 275 bajtów

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)

Wypróbuj online!

Nie czyści ekranu na Tio, ale działa w konsoli.

Gif z tego działa:

wprowadź opis zdjęcia tutaj

TFeld
źródło
Trochę podstępny, ale możesz użyć print'\n'*99zamiast os.system('cls')i stracić osimport. Nadal nie działa na TIO, ale działa zarówno w konsolach Windows, jak i Linux.
ElPedro
1
Powinna być [jedna] rama tylko podstawowej drabiny (bez łuków) przed rozpoczęciem nowego łuku (opcjonalnie przed pierwszym łukiem)
odpady
5
Myślę, że używasz łączników (U + 002D) zamiast makronów (U + 00AF). Nie sądzę, że to zwiększy liczbę bajtów, aby to naprawić. Ponadto, jak wskazał @wastl, między łukami nie ma pustej ramy drabiny.
Inżynier Toast
Dolny rząd używa makronów, ale łuki nie
Engineer Toast
1
@EngineerToast Naprawiono teraz :)
TFeld
4

JavaScript (ES6), 245 bajtów

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>

Liczba bajtów zakłada kodowanie ISO-8859-1.

Neil
źródło
Możliwe jest zmniejszenie go do 242 poprzez zdefiniowanie A=x=>[...Array(x)].map;na początku i zastąpienie obu zastosowań.
Bary12
@ Bary12 Nie możesz wrócić map, to tylko własność Array.prototypei bezużyteczne. Próbowałem działających wersji, ale wszystkie wyszły dłuższe niż 245 bajtów.
Neil
3

C (gcc) , 406 bajtów

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

Wypróbuj online!

Opis:

#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
źródło
Uwaga: naprawdę działa tylko w Xterm ... wiele emulatorów terminali po prostu nie obsługuje pozycji zapisu / przywracania.
LambdaBeta
Dolny wiersz to wiersz 0 i ma tylko dwa makra. Nie jest \--/. To prawdopodobnie łatwa poprawka. Czy jesteś w stanie uchwycić i opublikować GIF, który działa w Xterm?
Inżynier Toast
Niestety brakuje mi do tego narzędzi (po prostu bawię się podczas kompilacji w pracy). Zaktualizuję z poprawnym wierszem 0, jest to łatwa poprawka.
LambdaBeta
Nieprawidłowy: przed rozpoczęciem nowego łuku powinna być [jedna] rama podstawowej drabiny (bez łuków) (opcjonalnie przed pierwszym łukiem)
wastl
zmiana k = 1 na k = 0 naprawia ten ... 0 bajtowy koszt. Wkrótce się zaktualizuje.
LambdaBeta
2

PowerShell , 347 319 bajtów

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

Wypróbuj online! Nie mogłem dostać$args grać ładnie, więc link wywołuje tę funkcję bez czyszczenia konsoli.

Bez golfa

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
źródło
To drobiazg, ale dolny rząd to łączniki zamiast makronów. Jest to zmiana zero-bajtowa$l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯"
Toast Engineer
1
¯ \ (° _o) / ¯ ups! zamienione w makronach: p
Peter Vandivier
1
Nie znam zbyt dobrze programu PowerShell, ale możesz usunąć większość nowych wierszy. Ponadto for($x=0;$x-ne$n;$x++)może być for($x=0;$x++-ne$n;). Sprowadziłem to do 324 bajtów (321 znaków) . Wskazówki na temat gry w golfa w <wszystkich językach> i Wskazówki na temat gry w golfa w programie PowerShell również mogą być interesujące.
Kevin Cruijssen
1
sleep 1zapisuje trochę (domyślnie jest to -sekund), ale jest dość powolny, ale wciąż rozsądny, sleep -m 99jest dość szybki, ale także rozsądny. Zapisuje 5/1 bajtów w zależności od tego, co lubisz. Nie sprawdzić próbę Kevina ale functionaby filterto darmowa bajt, jak również.
Veskah
1

Rubinowy , 293 bajtów

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

Wypróbuj online!

Jestem w systemie Windows, więc po prostu drukuje wiele „\ n” w celu wyczyszczenia konsoli. Przyjmuje 2 argumenty ni hjako dwie linie na standardowe wejście.

crashoz
źródło