Escena animada de nieve ASCII

22

Escriba el programa más corto para convertir cualquier pieza de arte ASCII en una escena animada de nieve que comienza a formarse a partir de la nieve que cae ( ejemplo de JavaScript no golfizado actualizado por última vez el 2011-12-19).

Especificación de entrada : su programa debe aceptar combinaciones arbitrarias de espacios, asteriscos y líneas nuevas. La entrada contendrá como máximo 23 líneas y 80 caracteres por línea. No habrá líneas vacías, pero las líneas pueden consistir solo en espacios en blanco. Se incluirá una nueva línea final y se debe ignorar.

Salida : salida de caracteres ASCII (espacios, asteriscos) y códigos de control (retornos de carro, avances de línea, códigos de escape ANSI, etc.) para la consola de texto de su sistema operativo o emulador de terminal hasta que el usuario finalice manualmente el programa. Puede suponer que la ventana del terminal tiene 80x24 caracteres si su sistema operativo permite esa configuración.

reglas :

  • La animación debe ser suave y rápida (se prefieren 15 fps).
  • La densidad de nieve debe estar entre 5% y 15%.
  • No se puede desplazar más de una pantalla de nieve por segundo. (Eso significa que no se pueden agregar más de 24 líneas de nieve nueva en un segundo período de tiempo).
  • La nieve no debe mostrar ningún patrón obvio al entrar en la parte superior de la pantalla; Debe parecer aleatorio.
  • El programa debe llenar todas las filas de la pantalla con nieve lo más rápido posible cuando se inicia; El llenado inicial de las filas individuales de la pantalla no debe ser obvio para el espectador.
  • La esquina inferior izquierda del arte ASCII de entrada debe estar en la esquina inferior izquierda de la pantalla (Figura 1 para mayor aclaración).
  • El área dentro o debajo del arte ASCII no debe llenarse permanentemente con asteriscos. Sin embargo, los asteriscos pueden (pero no están obligados a hacerlo) desplazarse por esta área.
  • La nieve no debe acumularse en la parte inferior de la pantalla o encima de la nieve existente, excepto como se muestra en la entrada.
  • Los espacios inferiores deben llenarse antes que los superiores, ya que llenar espacios en el orden opuesto hace que la animación del árbol de Navidad se vea muy diferente de la salida de mi código original. (añadido 2011-12-20)

¡Felices vacaciones!

Figura 1: áreas etiquetadas de una pantalla de 80x24

---------------------------New snow added on this line--------------------------
                                                                             |
                                                                             |
----------------------------------------------------------+                  |
                                                    ****  |                  |
    Snow MUST fall  Snow MAY fall ---------------->  **** |                  |
    through this    through these          ****      **** |  Snow MUST fall  |
    area.           areas of a              ****     **** |  through this    |
                    completed   \--------->  ****     ****|  area.           |
        ASCII art   scene.    \     ***        ****   ****|                  |
          area         \       \   *******      ****  ****|                  |
                        \       \    ********     ***  ***|  (ALL CAPS terms |
      (located in        \       \-->   *********  ***    |  have standard   |
       lower left         \     *******     ******  MAY   |     RFC 2119     |
       corner of           \    *************  **   fall  |    meanings.)    |
       screen)              \        ***********    here  |                  |
                         *** +--->          ****  ***     |                  |
                         *** | ****************   ***     |                  |
  | Snow MUST fall       *** | ****************   ***     |                  |
  | through this         *** +--->                ***     |                  |
  | area.                *** | ****************   ***     |                  |
--+---------------------+*** +--->                ***+----+------------------+--
  |   Snow MUST NOT     |****************************|      Snow MUST NOT    |
  V  accumulate here.   |****************************|     accumulate here.  V

Entradas de ejemplo

Código Golf Banner

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

Stack Overflow Logo

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

Arboles de navidad

                                        *
                                       ***                           *
                *                     *****                         ***
               ***                   *******           *           *****
              *****                 *********         ***            *
                *                  ***********       *****
                       *          *************     *******
        *             ***        ***************       *               *
       ***           *****      *****************                     ***
      *****         *******    *******************                   *****
     *******           *      *********************                 *******
    *********                           *                          *********
        *                                                              *
Por favor levantese
fuente
1
El tercer árbol de navidad está roto.
Bobby
Buen desafío! Creo que las reglas deberían enumerarse para una referencia más fácil, y no entiendo la tercera y sexta regla ...
hallvabo
@hallvabo He aclarado esas dos reglas, la última agregando una figura etiquetada.
favor
Solicitud de aclaración: ¿la nueva línea está incluida en la longitud máxima de la línea de 80 caracteres o es un máximo de 80 caracteres más la nueva línea? (Asumí lo último, pero algunas presentaciones parecen haber asumido lo primero.)
Ilmari Karonen
@IlmariKaronen El último.
PleaseStand

Respuestas:

5

Perl, 196/239 caracteres

chomp(@p=(@f=($"x80)x24,<>)[-24..-1]);{@s=(join("",map rand>.1?$":"*",1..80),@s);if(@s>23){$t=$f[$_],print$_?$/:"\e[H",($f[$_]|=$s[$_]&$p[$_])|($s[$_]&=~$t^$f[$_])for 0..23;select"","","",.1}redo}

Esta solución difiere de su ejemplo de JS en que el patrón se llena de arriba hacia abajo en lugar de de abajo hacia arriba, pero supongo que está bien ya que no dijo nada al respecto en las reglas.

Se puede obtener una reducción trivial de 1 carácter reemplazando \econ un carácter ESC literal, pero eso hace que el código sea mucho más difícil de leer y editar.


Actualización: Me hice las arreglan para llegar a una versión que llena el patrón de arriba abajo, y no permite que la nieve caiga a través de las partes llenas del patrón, como en el ejemplo de implementación JS, al precio de 43 caracteres adicionales:

chomp(@p=(@q=@f=($"x80)x24,<>)[-24..-1]);{@s=(join("",map rand>.1?$":"*",1..80),@s);if(@s>23){my$q;$q[-1-$_]=($q|=$p[-$_]&~$f[-$_])for@a=0..23;print$_?$/:"\e[H",($f[$_]|=$s[$_]&$p[$_]&~$q[$_])|($s[$_]&=~$f[$_])for@a;select"","","",.1}redo}

Reemplazar ($s[$_]&=~$f[$_])con solo $s[$_]ahorrará 11 caracteres al permitir que la nieve que cae pase a través de las partes rellenas del patrón (que coincide con la especificación, pero no con la implementación del ejemplo).


De acuerdo, dado que todavía parece estar liderando la carrera después de una semana, creo que debería explicar cómo funciona mi solución para fomentar una mayor competencia. (Nota: esta explicación es para la versión de relleno de arriba a abajo de 196 caracteres. Puedo modificarla para incluir la otra versión más adelante).

En primer lugar, el gran truco en el que se basa mi solución es que, debido a la forma en que se organizan los códigos de caracteres ASCII, los 1 bits en el código ASCII para un espacio resultan ser un subconjunto de aquellos en el código para un asterisco.

Por lo tanto, las siguientes expresiones son verdaderas: " " & "*" eq " "y " " | "*" eq "*". Esto es lo que me permite usar operaciones de cadena bit a bit para combinar las partes estáticas y en movimiento de la escena sin tener que recorrer los caracteres individuales.

Entonces, con eso fuera del camino, repasemos el código. Aquí hay una versión sin golf:

chomp(@p = (@f = ($" x 80) x 24, <ARGV>)[-24..-1]);
{
    @s = (join('', map((rand > 0.1 ? $" : '*'), 1..80)), @s);
    if (@s > 23) {
        foreach (0 .. 23) {
            $t = $f[$_];
            print( $_ ? $/ : "\e[H" );
            print( ($f[$_] |= $s[$_] & $p[$_]) | ($s[$_] &= ~$t ^ $f[$_]) );
        }
        select '', '', '', 0.1;
    }
    redo;
}

La primera línea configura las matrices @f(para "fijo") y @p(para "patrón"). @fformará la parte fija de la pantalla y comenzará con nada más que espacios, mientras @pque, que no se muestra directamente, contiene el patrón de entrada; A medida que avanza la animación, agregaremos más y más asteriscos @fhasta que finalmente se vea igual @p.

Específicamente, se @f = ($" x 80) x 23establece @fen 24 cadenas de 80 espacios cada una. ( $"es una variable Perl especial cuyo valor predeterminado resulta ser un espacio). Luego tomamos esta lista, agregamos las líneas de entrada con el operador readline <>, tomamos las últimas 24 líneas de esta lista combinada y la asignamos a @p: esto es Una forma compacta de rellenar @pcon líneas en blanco para que el patrón aparezca donde debería. Finalmente, chompintroducimos las líneas de entrada @ppara eliminar las nuevas líneas finales para que no causen problemas más adelante.

Ahora, veamos el bucle principal. Resulta que {...;redo}es una forma más corta de escribir un bucle infinito que while(1){...}o incluso for(;;){...}, especialmente si omitimos el punto y coma antes redoporque inmediatamente sigue a un ifbloque.

La primera línea del bucle principal introduce la matriz @s(para "nieve", por supuesto), a la que antepone una cadena aleatoria de 80 caracteres de 90% de espacios y 10% de asteriscos en cada iteración. (Para guardar algunos caracteres, en realidad nunca saco líneas adicionales del final de la @smatriz, por lo que cada vez se alarga más y más. Eventualmente eso detendrá el programa a medida que la matriz se alargue demasiado para caber en la memoria, pero eso tomará mucho más tiempo de lo que la mayoría de la gente vería esta animación. Agregar una pop@s;declaración antes de que selectlo arreglaría al costo de siete caracteres).

El resto del bucle principal está envuelto en un ifbloque, por lo que solo se ejecuta una vez que la @smatriz contiene al menos 24 líneas. Esta es una manera simple de cumplir con la especificación, que requiere que toda la pantalla se llene con nieve que cae desde el principio, y también simplifica un poco las operaciones bit a bit.

Luego viene un foreachbucle, que en la versión de golf es en realidad una sola declaración con un for 0..23modificador. Dado que el contenido del bucle probablemente necesita alguna explicación, lo descomprimiré un poco más abajo:

foreach (0 .. 23) {
    print $_ ? $/ : "\e[H";     # move cursor top left before first line, else print newline
    $t = $f[$_];                # save the previous fixed snowflakes
    $f[$_] |= $s[$_] & $p[$_];  # snowflakes that hit the pattern become fixed 
    $s[$_] &= ~$t ^ $f[$_];     # ...and are removed from the moving part
    print $f[$_] | $s[$_];      # print both moving and fixed snowflakes ORed together
}

En primer lugar, $_es la variable de contador de bucle predeterminada en Perl a menos que se especifique otra variable. Aquí se ejecuta de 0 a 23, es decir, sobre las 24 líneas en el marco de la pantalla. $foo[$_]denota el elemento indexado por $_en la matriz @foo.

En la primera línea del bucle de golf, imprimimos una nueva línea (obtenida convenientemente de la $/variable especial) o, cuando $_es igual a 0, la cadena "\e[H", donde \edenota un carácter ESC. Este es un código de control de terminal ANSI que mueve el cursor a la esquina superior izquierda de la pantalla. Técnicamente, podríamos omitir eso si asumimos un tamaño de pantalla específico, pero lo mantuve en esta versión, ya que significa que no tengo que cambiar el tamaño de mi terminal para ejecutar la animación.

En la $t = $f[$_]línea, simplemente guardamos el valor actual de $f[$_]en una variable "temporal" (por lo tanto $t) antes de cambiarlo potencialmente en la siguiente línea, donde $s[$_] & $p[$_]da la intersección (Y bit a bit) de la nieve que cae y el patrón de entrada, y los |=operadores OR eso en la línea de salida fija $f[$_].

En la línea debajo de eso, $t ^ $f[$_]da el XOR bit a bit de los valores anteriores y actuales de $f[$_], es decir, una lista de los bits que cambiamos en la línea anterior, si existe, y al negar cualquiera de las cadenas de entrada con ~la salida. Por lo tanto, lo que obtenemos es una máscara de bits con todos los bits establecidos en 1, excepto los que acabamos de agregar $f[$_]en la línea anterior. ANDing esa máscara de bits $s[$_]elimina esos bits; en efecto, esto significa que cuando un copo de nieve que cae llena un agujero en el patrón fijo, se elimina del conjunto de nieve que cae.

Finalmente, print $f[$_] | $s[$_](que en la versión de golf se implementa simplemente ORing las dos líneas anteriores juntas) simplemente imprime la unión (OR bit a bit) de los copos de nieve fijos y móviles en la línea actual.

Una cosa más que queda por explicar es select '', '', '', 0.1debajo del bucle interno. Esta es solo una forma perezosa de dormir 0.1 segundos en Perl; Por alguna razón histórica tonta, el sleepcomando Perl estándar tiene una resolución de un segundo, y la importación de un mejor sleepdesde el Time::HiResmódulo requiere más caracteres que abusar de 4-argselect .

Ilmari Karonen
fuente
Parece haber un pequeño error: la línea 24 no se usa, y la línea inferior del arte ASCII es la línea 23. Y realmente debería llenar los espacios inferiores antes de llenar los superiores, aunque originalmente no especifiqué eso.
favor
@PleaseStand: cambié el código para usar las 24 líneas (y de paso me deshice de ellas say), a costa de 2 caracteres adicionales. Sin embargo, no creo que pueda cambiar el orden de llenado muy fácilmente; mi implementación está fundamentalmente ligada a esto.
Ilmari Karonen
¡Gran explicación! Desearía poder votar esta entrada nuevamente.
Dillon Cower
@PleaseStand: En realidad me las arreglo para hacer una versión de llenado de abajo hacia arriba, lo que ahora se ve más o menos el mismo que el ejemplo JS; es un poco más largo que el de arriba hacia abajo, pero aún más corto que las otras entradas hasta ahora.
Ilmari Karonen
3

HTML y JavaScript, 436 caracteres

Anteponerlo a la entrada:

<body onload="for(a=[],b=[],c=document.body.firstChild,e=c[H='innerHTML'].split(N='\n'),f=e.length-1,g=24,h=g-f;f--;)for(X=80;X--;)b[80*(h+f)+X]='*'==e[f][X];for(setInterval(F='for(y=24;y--;)for(x=80;x--;)if(a[w=80*y+x]){d=1;if(b[w])for(d=0,z=y+1;24>z;++z)b[s=80*z+x]&&!a[s]&&(d=1);d&&(a[w]=0,a[w+80]=1)}for(x=80;x--;).1>Math.random(i=0)&&(a[x]=1);for(t=\'\';1920>i;++i)t+=\'* \'[+!a[i]],79==i%80&&(t+=N);c[H]=t',67);g--;)eval(F)"><pre>

Vea cómo se ejecuta para cada ejemplo: código de golf , logotipo de Stack Overflow , árboles de Navidad . Los usuarios de Internet Explorer deben ejecutar la versión 9 y configurar el "Modo de documento" en "estándares IE9" (usando las herramientas de desarrollo F12) para que este envío funcione correctamente.

Por favor levantese
fuente
1
¿Los 3 parecen estar rotos? pasteall.org/pic/show.php?id=66297
CoDEmanX
1

Python, 299 caracteres

Esto debe cumplir con las reglas, suponiendo que la nueva línea esté incluida en el límite de 80 caracteres.

import random,sys,time
C=1920
v=_,x=' *'
a=['']*C
f=lambda n:[random.choice(e*9+x)for e in _*n]
for e in sys.stdin:a+="%-80s"%e
a=a[-C:]
s=f(C)
while 1:
 z=0;t=''
 for e in s:
    t+=v[x<a[z]or e>_]
    if(e>_<a[z])>(x in a[z+80::80]):a[z]='+'
    t+=z%80/79*'\n';z+=1
 print t;s=f(80)+s[:-80];time.sleep(.1)
hallvabo
fuente
Su salida se vuelve problemática si la entrada tiene líneas que son exactamente 80 caracteres (más nueva línea) de largo. Le he pedido a PleaseStand que aclare si está bien.
Ilmari Karonen
Esto se debe a que incluyo una nueva línea al final de cada línea de 80 caracteres de ancho. La descripción es ambigua aquí, ya que especifica que se deben incluir nuevas líneas, pero también que se puede suponer que el terminal tiene 80 caracteres de ancho (en cuyo caso se pueden omitir las nuevas líneas y depender del ajuste automático).
hallvabo
Creo que el problema real es que no eliminas las líneas nuevas antes de rellenar las líneas de entrada a 80 caracteres, de modo que una entrada de 80 caracteres más línea nueva termina agregando 81 caracteres ay desordena la indexación. Acabo de probarlo, y parece que reemplazarlo %econ la %e.rstrip()línea 6 soluciona el problema. (Por supuesto, puede haber una solución más corta; no soy bueno en Python golf.)
Ilmari Karonen
Si desea admitir 81 líneas de caracteres, simplemente cambie los números y funcionará bien. Como siempre y cuando permanezca por debajo de 100 caracteres por línea, no va a cambiar el carbón recuento :-)
hallvabo
Si cambio su código para usar líneas de 81 caracteres, no se ejecutará correctamente en un terminal de 80 columnas, ¿verdad? Está produciendo una salida de 80 columnas muy bien, es solo que no está aceptando la entrada de 80 columnas correctamente. Pruébelo: cree un archivo de entrada con un par de líneas con 80 asteriscos en cada una y vea qué sucede. No debería ser tan difícil: mi solución lo maneja bien.
Ilmari Karonen
0

Java, 625 caracteres

import java.io.*;import java.util.*;class s extends TimerTask {int _c,_k;char _i[],_o[];boolean _b[];public s(String f) throws IOException {_i=new char[23*80];_o=new char[80];_b=new boolean [23*80];BufferedReader br = new BufferedReader(new FileReader(f));while (br.read(_i,_c++*80,80)!=-1);} public void run(){_k=--_k<0?_c:_k;for(int i=0;i<80;_b[_k*80+i]=Math.random()>0.9?true:false,i++);for(int m=0;m<_c;m++){for(int n=0;n<80;_o[n]=_b[(_k+m)%_c*80+n]?'*':_i[m*80+n],n++);System.out.println(_o);}}public static void main(String[] a) throws IOException{Timer timer=new Timer();timer.scheduleAtFixedRate(new s(a[0]),0,500);}}

Una solución simple en Java.

SiZ
fuente
Bien, pero no creo que estés cumpliendo con las especificaciones. En particular, muestra todo el patrón desde el principio: no se "forma a partir de la nieve que cae" como en la demostración de muestra. (Es cierto que esto podría explicarse mejor en la pregunta. Realmente necesita ver la demostración para comprender lo que debe hacer). Además, su velocidad de fotogramas es demasiado lenta, no comienza con una pantalla llena de nieve, parece que suponiendo un formato de entrada diferente de los ejemplos dados, y realmente debería restablecer la posición del cursor entre cuadros (la impresión "\033[H"debería hacerlo).
Ilmari Karonen