Animar la escalera ASCII de Jacob

23

Es posible que haya visto la escalera de Jacob en los museos de ciencias para niños. Si no está familiarizado con su aspecto, hay varias imágenes y ejemplos de video en Wikimedia Commons . El desafío hoy es crear una versión animada ASCII del dispositivo eléctrico. Al final, debería verse así:

LadderGIFExample


Construcción de escalera

Aquí está la forma básica de una escalera con una altura ( H ) de 6:

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

Los números a la izquierda simplemente indican el número de fila para este ejemplo y no deben incluirse en la salida. Nos referiremos a una fila dada por su número ( R ). La fila 0 es la parte inferior ¯¯. Cada fila 1 a H se compone de cuatro partes:

  • Un espacio (U + 0020) repetido ( H - R ) veces
  • Una barra diagonal \inversa (U + 005C)
  • Un espacio (U + 0020) repetido (2 * R ) veces
  • Una barra diagonal /(U + 002F)

La fila 0 es idéntica, excepto que ambas barras son reemplazadas por un macron ¯(U + 00AF) El espacio en blanco al final de cada línea o debajo de la escalera está bien. El espacio en blanco principal no lo es.


Construcción de arco

Una vez que se construye la escalera, puede crear arcos entre el lado izquierdo y el derecho. Un arco está completamente dentro de una fila y reemplaza los espacios entre el inicio \y el final /. Por lo tanto, la fila 2 tendrá 4 caracteres en su arco, la fila 3 tendrá 6, y así sucesivamente. Cada arco se compone usando las siguientes reglas:

  • Los únicos caracteres permitidos son _/¯\(U + 005F, U + 002F, U + 00AF, U + 005C)
  • Para garantizar una apariencia suave, a cualquiera ¯o /debe seguirle un ¯o\
  • Para garantizar una apariencia suave, a cualquiera _o \debe seguirle un _o/
  • Las dos reglas anteriores se aplican también a los bordes de la escalera.
  • Las tres reglas anteriores significan efectivamente que el primer carácter del arco debe ser _o /y el último carácter debe ser _o \( \¯\_//no es válido en ambos extremos pero \_/¯\/está bien)
  • Debe haber una probabilidad distinta de cero para que cada personaje permitido ocurra en un punto dado
  • Cada arco es independiente de cualquier otro arco

Animación

La vida de un solo arco se crea al comenzarlo en la fila 1 y "moverlo" una fila a la vez hasta que llegue a la parte superior. IE, primero genere un arco en la fila 1, luego vuelva a configurarlo en espacios y genere un arco en la fila 2, y así sucesivamente. Dado un número de arcos para mostrar ( N ), muestre la vida completa de tantos arcos de uno en uno utilizando las siguientes pautas:

  • Solo un arco debe estar "vivo" a la vez. El siguiente arco no puede comenzar hasta que el actual llegue a la cima y luego se extinga.
  • Cada fila de la vida del arco debe mostrarse exactamente para un cuadro
  • Debe haber un marco de la escalera básica (sin arcos) antes de que comience un nuevo arco (opcional antes del primer arco)
  • La animación debe mostrar la vida completa de N arcos. Si N = 0, debería animar arcos aleatorios para siempre hasta que se detenga.
  • Si N > 0, aún puede repetir la animación para siempre, pero debe ser un ciclo de los mismos arcos una y otra vez. (El ejemplo de GIF en la parte superior de esta publicación tiene H = 6 y N = 3 pero se repite para siempre).
  • La animación debe ocurrir en el lugar. Es decir, cada fotograma debería sobrescribir completamente el siguiente fotograma y estar en la misma ubicación.
  • La longitud de cada cuadro puede ser lo que quieras, pero haz que un humano la pueda ver (es decir, usa tu sentido común: 0.01s / frame y 30s / frame son inaceptables).

De entrada y salida

  • La entrada y la salida pueden estar en cualquier formato estándar
  • Puede exportar un GIF, escribir texto en la pantalla, generar un solo archivo para cada fotograma o cualquier otro medio razonable
  • Las lagunas estándar están prohibidas
  • La altura de la escalera H será un número entero positivo.
  • El número de arcos para mostrar N será un número entero no negativo
  • Tanto H como N se toman como entradas en el orden que elija (incluya el orden en su respuesta)

Condición ganadora

Este es el por lo que gana el código más corto.

Salvadera

Tostadas de ingeniero
fuente
1
¿Se puede generar un arco simétrico por su centro? No puedo ver restricciones a eso en las reglas
Dead Possum
¿Puedo imprimir cada cuadro uno tras otro en la consola?
TFeld
@DeadPossum Estaba pensando que tenías razón a pesar del hecho de que no se vería muy relámpago, pero en realidad no está permitido por la combinación de dos reglas: the first character in the arc must be _ or / and the last character must be _ or \ y There must be a non-zero chance for each allowable character to occur at a given point. Para ser simétrico, tanto el primer como el último carácter deberían ser _cada vez, lo que significa que hay cero posibilidades de que /ocurra uno u otro '\'.
Engineer Toast
@TFeld Siempre que cada cuadro aparezca en la misma ubicación en la pantalla, sí. Eso significa que tendrá que limpiar la consola (o tal vez desplazarse hacia abajo, si es posible) cada vez.
Engineer Toast
2
¿El requisito de macron significa que QBasic no puede competir? Utiliza CP437 , en el que se 0xAFencuentra el punto de código ».
DLosc

Respuestas:

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)

Pruébalo en línea!

No borra la pantalla en tio, pero funciona en una consola.

Gif de él corriendo:

ingrese la descripción de la imagen aquí

TFeld
fuente
Un poco astuto, pero podría usarlo en print'\n'*99lugar de os.system('cls')perder la osimportación. Todavía no funciona en TIO pero funciona en consolas Windows y Linux.
ElPedro
1
Debe haber [un] marco de solo la escalera básica (sin arcos) antes de que comience un nuevo arco (opcional antes del primer arco)
wastl
55
Creo que estás usando guiones (U + 002D) en lugar de macrones (U + 00AF). No creo que aumente tu número de bytes para arreglarlo. Además, como señaló @wastl, no hay un marco de escalera vacío entre los arcos.
Engineer Toast
La fila inferior usa macrones pero los arcos no.
Engineer Toast
1
@EngineerToast Reparado ahora :)
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>

El recuento de bytes asume la codificación ISO-8859-1.

Neil
fuente
Es posible reducirlo a 242 definiendo A=x=>[...Array(x)].map;al principio y reemplazando ambos usos.
Bary12
@ Bary12 No puede regresar map, eso es solo una propiedad Array.prototypey no tiene uso por sí solo. Intenté versiones de trabajo pero todas salieron más allá de 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");}}}

Pruébalo en línea!

Descripción:

#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
fuente
Nota: solo funciona realmente en Xterm ... muchos emuladores de terminal simplemente no admiten la posición de guardar / restaurar.
LambdaBeta
La fila inferior es la fila 0 y solo tiene dos macrones. No es \--/. Esa es probablemente una solución fácil. ¿Eres capaz de capturar y publicar un GIF trabajando en Xterm?
Engineer Toast
Lamentablemente, me faltan las herramientas para hacerlo (solo jugando durante los tiempos de compilación en el trabajo). Sin embargo, actualizaré con la fila 0 correcta, es una solución fácil.
LambdaBeta
No válido: debe haber [un] marco de la escalera básica (sin arcos) antes de que comience un nuevo arco (opcional antes del primer arco)
wastl
cambiar k = 1 a k = 0 corrige que ... 0 bytes de costo. Se actualizará pronto.
LambdaBeta
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}}}

Pruébalo en línea! No se pudo$args pudo jugar bien, por lo que el enlace llama a la función sin borrar la consola.

Sin golf

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
fuente
Es algo pequeño, pero la fila inferior son guiones en lugar de macrones. Es un cambio de cero bytes$l=(($h..1)|%{"$(" "*($h-$_))\$(" "*$_*2)/"})+"$(" "*$h)¯¯"
Engineer Toast
1
¯ \ (° _o) / ¯ ¡Uy! intercambiado en macrons: p
Peter Vandivier
1
No conozco demasiado bien PowerShell, pero puedes eliminar la mayoría de las nuevas líneas. Además for($x=0;$x-ne$n;$x++)puede ser for($x=0;$x++-ne$n;). Lo reduje a 324 bytes (321 caracteres) al hacerlo. Los consejos para jugar golf en <todos los idiomas> y los consejos para jugar golf en PowerShell también pueden ser interesantes de leer.
Kevin Cruijssen
1
sleep 1ahorra un poco (el valor predeterminado es -segundos) pero es bastante lento pero aún así razonable, sleep -m 99es bastante rápido pero también razonable. Ahorra 5/1 bytes dependiendo de lo que quiera. No la salida intento de Kevin, pero functiona filteres un byte libre también.
Veskah
1

Rubí , 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

Pruébalo en línea!

Estoy en Windows, así que solo imprime muchos "\ n" para borrar la consola. Toma 2 argumentos ny hcomo dos líneas en stdin.

crashoz
fuente