Animieren Sie Jacobs ASCII-Leiter

23

Vielleicht haben Sie Jacobs Leiter in Wissenschaftsmuseen für Kinder gesehen. Wenn Sie nicht wissen, wie sie aussehen, finden Sie auf Wikimedia Commons mehrere Bilder und Videobeispiele . Die Herausforderung besteht heute darin, eine animierte ASCII-Version des elektrischen Gadgets zu erstellen. Am Ende sollte es ungefähr so ​​aussehen:

LadderGIFExample


Leiteraufbau

Hier ist die Grundform einer Leiter mit einer Höhe ( H ) von 6:

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

Die Zahlen auf der linken Seite geben lediglich die Zeilennummer für dieses Beispiel an und sollten nicht in die Ausgabe einbezogen werden. Wir verweisen auf eine bestimmte Zeile mit der Nummer ( R ). Zeile 0 ist der Boden ¯¯. Jede Reihe 1 bis H besteht aus vier Teilen:

  • Ein Leerzeichen (U + 0020) wird ( H - R ) Mal wiederholt
  • Ein Schrägstrich \(U + 005C)
  • Ein Leerzeichen (U + 0020 ) wird zweimal (2 * R ) wiederholt
  • Ein Schrägstrich /(U + 002F)

Zeile 0 ist identisch, außer dass beide Schrägstriche durch ein Macron ¯(U + 00AF) ersetzt werden. Das nachfolgende Leerzeichen am Ende jeder Zeile oder unterhalb der Leiter ist in Ordnung. Führendes Leerzeichen ist das nicht.


Lichtbogenbau

Sobald die Leiter erstellt ist, können Sie Bögen zwischen der linken und rechten Seite erstellen. Ein Bogen befindet sich vollständig in einer Reihe und ersetzt die Leerzeichen zwischen dem führenden \und dem nachfolgenden Bogen /. Daher hat Zeile 2 4 Zeichen in seinem Bogen, Zeile 3 6 usw. Jeder Bogen wird nach folgenden Regeln erstellt:

  • Die einzigen zulässigen Zeichen sind _/¯\(U + 005F, U + 002F, U + 00AF, U + 005C).
  • Um ein glattes Aussehen zu gewährleisten, jede ¯oder /werden muß , gefolgt von einem ¯oder\
  • Um ein glattes Aussehen zu gewährleisten, jede _oder \werden muß , gefolgt von einem _oder/
  • Die beiden obigen Regeln gelten auch für die Kanten der Leiter
  • Die drei obigen Regeln bedeuten effektiv, dass das erste Zeichen im Bogen _oder sein muss /und das letzte Zeichen _oder sein muss \( \¯\_//ist an beiden Enden ungültig, aber \_/¯\/in Ordnung).
  • Es muss eine Chance ungleich Null geben, dass jedes zulässige Zeichen an einem bestimmten Punkt auftritt
  • Jeder Lichtbogen ist unabhängig von jedem anderen Lichtbogen

Animation

Die Lebensdauer eines einzelnen Bogens wird erstellt, indem er in Zeile 1 gestartet und um eine Zeile nach oben "verschoben" wird, bis er den oberen Rand erreicht. Erstellen Sie zuerst einen Bogen in Zeile 1, setzen Sie ihn dann auf Leerzeichen zurück und erstellen Sie einen Bogen in Zeile 2 usw. Zeigen Sie unter Berücksichtigung der Anzahl der zu zeigenden Bögen ( N ) die gesamte Lebensdauer dieser vielen Bögen nacheinander anhand der folgenden Richtlinien an:

  • Es sollte immer nur ein Bogen "lebendig" sein. Der nächste Lichtbogen kann erst beginnen, wenn der aktuelle den oberen Punkt erreicht und dann erlischt.
  • Jede Reihe der Lebensdauer des Bogens sollte für genau ein Bild angezeigt werden
  • Es sollte nur ein Frame der Basisleiter (ohne Bögen) vorhanden sein, bevor ein neuer Bogen beginnt (optional vor dem ersten Bogen).
  • Die Animation sollte die volle Lebensdauer von N Bögen zeigen. Wenn N = 0 ist, sollten zufällige Bögen für immer animiert werden, bis sie gestoppt werden.
  • Wenn N > 0, können Sie die Animation immer noch in einer Endlosschleife abspielen, es muss sich jedoch immer wieder um eine Schleife mit denselben Bögen handeln. (Das Beispiel-GIF oben in diesem Beitrag hat H = 6 und N = 3, aber es wird für immer wiederholt.)
  • Die Animation sollte an Ort und Stelle erfolgen. Das heißt, jeder Frame sollte den nächsten Frame vollständig überschreiben und sich an derselben Stelle befinden.
  • Die Länge jedes Frames kann beliebig sein, aber für einen Menschen einsehbar sein (IE, verwenden Sie Ihren gesunden Menschenverstand: 0,01 s / Frame und 30 s / Frame sind inakzeptabel.)

Input-Output

  • Eingabe und Ausgabe können in jedem Standardformat erfolgen
  • Sie können ein GIF exportieren, Text auf den Bildschirm schreiben, eine einzelne Datei für jeden Frame ausgeben oder auf jede andere sinnvolle Weise
  • Standardlücken sind verboten
  • Die Höhe der Leiter H ist eine positive ganze Zahl
  • Die Anzahl der Bögen, die N anzeigen sollen, ist eine nicht negative ganze Zahl
  • Sowohl H als auch N werden als Eingaben in einer von Ihnen gewählten Reihenfolge verwendet. (Bitte geben Sie die Reihenfolge in Ihrer Antwort an.)

Gewinnbedingung

Das ist also gewinnt der kürzeste Code.

Sandkasten

Ingenieur Toast
quelle
1
Kann ein Bogen symmetrisch von seinem Zentrum erzeugt werden? Ich kann keine Einschränkung in den Regeln sehen
Dead Possum
Kann ich jeden Frame nacheinander auf der Konsole drucken?
TFeld
@DeadPossum Ich dachte, Sie hätten Recht, obwohl es nicht sehr blitzschnell aussieht, aber es wird durch die Kombination von zwei Regeln tatsächlich nicht zugelassen: the first character in the arc must be _ or / and the last character must be _ or \ und There must be a non-zero chance for each allowable character to occur at a given point. Um symmetrisch zu sein, müssten sowohl das erste als auch das letzte Zeichen _jedes Mal sein, was bedeutet, dass keine Chance besteht, dass entweder /"\" oder "" auftritt.
Ingenieur Toast
@TFeld Solange sich jeder Frame an derselben Stelle auf dem Bildschirm befindet, ja. Das bedeutet, dass Sie die Konsole jedes Mal leeren müssen (oder nach unten scrollen müssen, falls dies möglich ist).
Ingenieur Toast
2
Bedeutet die Macron-Anforderung, dass QBasic nicht mithalten kann? Es wird CP437 verwendet , in dem sich der Codepunkt 0xAFbefindet ».
DLosc

Antworten:

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)

Probieren Sie es online!

Löscht den Bildschirm auf tio nicht, funktioniert aber in einer Konsole.

Gif davon läuft:

Bildbeschreibung hier eingeben

TFeld
quelle
Etwas hinterhältig könnte man aber benutzen und print'\n'*99stattdessen os.system('cls')den osImport verlieren . Funktioniert immer noch nicht mit TIO, funktioniert aber sowohl auf Windows- als auch auf Linux-Konsolen.
ElPedro
1
Es sollte nur einen Frame der Basisleiter (ohne Bögen) geben, bevor ein neuer Bogen startet (optional vor dem ersten Bogen)
verschwenden Sie den
5
Ich denke, Sie verwenden Bindestriche (U + 002D) anstelle von Makros (U + 00AF). Ich glaube nicht, dass es Ihre Byteanzahl erhöht, um es zu beheben. Außerdem gibt es, wie @wastl hervorhob, keinen leeren Leiterrahmen zwischen den Bögen.
Ingenieur Toast
In der unteren Reihe werden Makros verwendet, in den Bögen jedoch nicht
Engineer Toast
1
@EngineerToast Jetzt behoben :)
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>

Die Byteanzahl setzt die ISO-8859-1-Codierung voraus.

Neil
quelle
Es ist möglich, es auf 242 zu reduzieren, indem A=x=>[...Array(x)].map;zu Beginn beide Verwendungen definiert und ersetzt werden.
Bary12
@ Bary12 Du kannst nicht zurückkehren map, das ist nur eine Eigenschaft von Array.prototypeund keine Verwendung für sich. Ich habe versucht, Arbeitsversionen, aber sie alle kamen länger als 245 Bytes.
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");}}}

Probieren Sie es online!

Beschreibung:

#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
quelle
Hinweis: Funktioniert nur in Xterm wirklich ... viele Terminal-Emulatoren unterstützen das Speichern / Wiederherstellen von Positionen einfach nicht.
LambdaBeta
Die unterste Zeile ist Zeile 0 und enthält nur zwei Makros. Es ist nicht \--/. Das ist wahrscheinlich eine einfache Lösung. Können Sie ein GIF davon erfassen und veröffentlichen, wenn Sie in Xterm arbeiten?
Ingenieur Toast
Leider fehlen mir die Werkzeuge, um das zu tun (nur während der Bauzeiten bei der Arbeit herumzuspielen). Ich werde mit der korrekten Zeile 0 aktualisieren, dies ist jedoch eine einfache Lösung.
LambdaBeta
Ungültig: Es sollte [sein] einen Rahmen von nur den Grundleiter (ohne Bögen) vor einem neuen Bogen beginnt (optional vor dem ersten Bogen)
Wastl
Das Ändern von k = 1 in k = 0 behebt, dass ... 0 Byte kosten. Wird bald aktualisiert.
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}}}

Probieren Sie es online! Konnte nicht $argsschön spielen, also ruft der Link die Funktion auf, ohne die Konsole zu löschen.

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
quelle
Es ist eine kleine Sache, aber die unterste Reihe besteht aus Bindestrichen anstelle von Makros. Es ist eine Null-Byte-Änderung$l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯"
Engineer Toast
1
¯ \ (° _o) / ¯ Hoppla! in Makronen getauscht: p
Peter Vandivier
1
Ich kenne PowerShell nicht so gut, aber Sie können die meisten neuen Zeilen entfernen. Außerdem for($x=0;$x-ne$n;$x++)kann for($x=0;$x++-ne$n;). Ich habe es auf 324 Bytes (321 Zeichen) reduziert. Tipps zum Golfen in <allen Sprachen> und Tipps zum Golfen in PowerShell könnten ebenfalls interessant sein.
Kevin Cruijssen
1
sleep 1spart ein wenig (standardmäßig -seconds), ist aber ziemlich langsam, aber immer noch vernünftig, sleep -m 99ist ziemlich schnell, aber auch vernünftig. Spart je nach Belieben 5/1 Bytes. Ich habe Kevins Versuch nicht überprüft, aber functiones filterist auch ein freies Byte.
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

Probieren Sie es online!

Ich arbeite unter Windows und drucke nur viele "\ n", um die Konsole zu leeren. Nimmt 2 Argumente nund hals zwei Zeilen auf stdin.

crashoz
quelle