Texto animado sinusoidal de arte ASCII

11

De alguna manera extraño esas viejas demostraciones que muestran las capacidades de las computadoras cuando se llamaban x86 en lugar de i3, i5 e i7. Una de las primeras que vi en mi 386 fue la demostración de Unreal de Future Crew que ahora celebra su 25 aniversario. En el minuto 0:43 comienza la primera parte de la demostración y vemos un texto desplazable siguiendo una ruta sinusoidal. ¡Intentemos imitar ese efecto en el arte ASCII!

El reto

Dado este camino:

***                                ***
   ***                          ***
      **                      **
        *                    *
         *                  *
         *                  *
          *                *
           **            **
             ***      ***
                ******

y un texto de entrada, dibuje el texto siguiendo esa ruta, así:

Thi                                Golf! 
   s i                          de       Yay
      s                       Co            !
        P                     
         r                  d
         o                  n
          g                a
           ra            s 
             mmi      zle
                ng Puz

Tenga en cuenta que los espacios cuentan como caracteres en la ruta y que la ruta se repite si el texto es más largo que la muestra.

La parte de animación

Una vez que haya dibujado el texto, espere 100 ms (para crear una animación de alrededor de 10 fps) y vuelva a dibujar el texto pero comenzando desde la siguiente posición de la ruta. Entonces, para el marco #n, calcule n modulo 40y comience a dibujar en la siguiente posición de la ruta con el texto siempre alineado a la izquierda del lienzo:

***                                ***
|  ***                          ***  |
|     **                      **     |
|       *                    *       |
|        *                  *        |
|        *                  *        |
|         *                *         |
|          **            **          |
|            ***      ***            |
|               ******               |
Position 0                 Position 39

Entonces para el cuadro 10 tendremos:

                           and Co
                        es       de 
                      zl            Go
                     z                l
                    u                  f
T                   P                  !
 h                                       
  is             ng                       Ya
     is       mmi                           y!
        Progra

Notas

  • La entrada será simple string(o charmatriz, lo que sea) con el texto para animar, y siempre tendrá al menos 1 carácter.
  • Los caracteres válidos para animar son aquellos en el conjunto ASCII imprimible .
  • El camino a seguir será exactamente como se indica.
  • El texto siempre estará alineado a la izquierda del lienzo, por lo que el efecto será el texto ondeando como una bandera, sin desplazamiento de texto. Y por lienzo me refiero a la pantalla o lo que sea que vayas a usar para mostrar el texto . ;-)
  • Los cuadros deben estar libres de caracteres / píxeles de cuadros anteriores a menos que el carácter / píxel sea el mismo en ambos cuadros.
  • La velocidad de la animación no importa siempre que se ejecute sin problemas o tan rápido como su dispositivo (podemos establecer un mínimo de 5 fps, pero esto no es un requisito). Simplemente ajuste la velocidad para que sea fluido y no se preocupe si los tiempos de espera no son exactamente los mismos.
  • La animación se repetirá sin parar.

Este es el , ¡así que puede que gane el programa o la función más corta capaz de animar el texto!

Charlie
fuente
1
Puedo contar 38 columnas en lugar de 40.
Arnauld
1
@Arnauld es porque lo que cuenta son las posiciones en la ruta, no las columnas.
Charlie
Oh ya veo. Tiene sentido.
Arnauld
¿Es esta bien como salida? Muestra la entrada en forma de onda sinusoidal y bucles sin fin. Por supuesto, dado que el video está en formato Graphics Interchange, es más rápido en realidad.
R. Kap

Respuestas:

9

HTML + ES6, 241 244 237 bytes

Descompostura:

  • HTML: 16 bytes
  • Función JS: 221 bytes

let f =

s=>setInterval(_=>o.innerHTML=[...s].map((c,i)=>([j,y]=[...Array(40)].map((_,k)=>[j=k%20,y,y+=77732>>j&1?k<20||-1:0],y=0)[(i+p)%40],a[y]=a[y]||[...s].fill` `)[x]=(x+=j!=9,c),x=0,a=[],p++)&&a.map(r=>r.join``).join`
`,p=30)

f("This is Programming Puzzles and Code Golf! Yay!")
<pre id=o></pre>

¿Cómo?

Construyendo el camino

El siguiente código construye la ruta:

[...Array(40)].map((_, k) =>
  [
    j = k % 20,
    y,
    y += 77732 >> j & 1 ? k < 20 || -1 : 0
  ],
  y = 0
)

Esto proporciona una matriz de matrices [j, y, z]donde j es la posición del módulo 20, y es la coordenada y en esta posición yz no se usa más tarde (simplemente se calcula aquí para guardar algunos bytes).

Debido a que la ruta es simétrica, solo necesitamos codificar las 20 primeras posiciones. Hacemos eso usando un número binario donde cada 1bit significa que y debe actualizarse (+1 para la primera mitad, -1 para la segunda mitad).

001
   001
      01
        1
         1
         1
          1
           01
             001
                000

Con la primera posición asignada al bit menos significativo, esto da:

00010010111110100100 as binary = 77732 as decimal

Debido a que este número binario también es simétrico, podemos leerlo en el mismo orden para la segunda mitad.

De ahí la fórmula:

y += (77732 >> j) & 1 ? (k < 20 ? 1 : -1) : 0

que también se puede escribir como:

y += (77732 >> j) & 1 ? k < 20 || -1 : 0

donde k es la posición y j es la posición módulo 20.

Actualizar x es mucho más fácil: solo tenemos que detectar un caso especial comparando el módulo de posición 20 con 9.

Dibujando el texto

En el siguiente código, el código de construcción de ruta descrito anteriormente ha sido reemplazado por una pathvariable para facilitar la lectura.

s => setInterval(                       // set a periodic timer:
  _ =>                                  //   with an anonymous callback
    o.innerHTML =                       //   which updates the content of 'o'
      [...s].map((c, i) => (            //   for each character c of the string s
          [j, y] = path[(i + p) % 40],  //     retrieve j and y from the path
          a[y] = a[y] || [...s].fill` ` //     initialize a[y] if it's undefined
        )[x] = (x += j! = 9, c),        //     update a[y][x] / update x
        x = 0,                          //     initialize x
        a = [],                         //     initialize a[]
        p++                             //     increment the phase
      ) &&                              //   end of map()
      a.map(r => r.join``).join`\n`,    //   join everything together
  p = 30                                //   initial phase = interval period = 30
)                                       // end of timer definition
Arnauld
fuente
Esto es brillante, y bastante cerca del camino solicitado, pero el camino no es exactamente el que dibujé. Las dos columnas que contienen dos *en vertical no están alineadas (a la misma altura), y otra pequeña pica. Pero debo decir que todavía no sé cómo su código crea el efecto de onda (¿qué hace y+=155464?). ¡Felicidades!
Charlie
@CarlosAlejo Creo que el camino ahora debería estar arreglado ¿Podría por favor verificar dos veces? Agregaré una explicación del método utilizado.
Arnauld
1
Ruta comprobada, y muchas gracias por la explicación!
Charlie