Scène de neige animée ASCII

22

Écrivez le programme le plus court pour transformer n'importe quelle œuvre d'art ASCII en une scène de neige animée qui commence à se former à partir de la neige qui tombe ( exemple JavaScript non-golfé dernière mise à jour le 2011-12-19).

Spécification d'entrée : votre programme doit accepter des combinaisons arbitraires d'espaces, d'astérisques et de nouvelles lignes. L'entrée contiendra au maximum 23 lignes et 80 caractères par ligne. Il n'y aura pas de lignes vides, mais les lignes peuvent être constituées uniquement d'espaces. Une seule nouvelle ligne de fin sera incluse et doit être ignorée.

Sortie : affichez des caractères ASCII (espaces, astérisques) et des codes de contrôle (retours chariot, sauts de ligne, codes d'échappement ANSI, etc.) pour la console texte ou l'émulateur de terminal de votre système d'exploitation jusqu'à ce que l'utilisateur termine manuellement le programme. Vous pouvez supposer que la fenêtre du terminal contient 80 x 24 caractères si votre système d'exploitation autorise ce paramètre.

Règles :

  • L'animation doit être fluide et rapide (15 ips de préférence).
  • La densité de la neige doit être comprise entre 5% et 15%.
  • Pas plus d'un écran de neige peut défiler par seconde. (Cela signifie qu'au plus 24 lignes de neige fraîche peuvent être ajoutées au cours d'une même période.)
  • La neige ne doit présenter aucun motif évident lorsqu'elle pénètre en haut de l'écran; il doit avoir l'air aléatoire.
  • Le programme doit remplir toutes les rangées de l'écran de neige le plus rapidement possible au démarrage; le remplissage initial des lignes individuelles de l'écran ne doit pas être évident pour le spectateur.
  • Le coin inférieur gauche de l'art ASCII d'entrée doit être situé dans le coin inférieur gauche de l'écran (figure 1 pour plus de précisions).
  • La zone à l'intérieur ou sous l'art ASCII ne doit pas être remplie en permanence d'astérisques. Cependant, les astérisques peuvent (mais ne sont pas obligatoires) faire défiler cette zone.
  • La neige ne doit pas s'accumuler au bas de l'écran ou sur le dessus de la neige existante, sauf comme indiqué dans l'entrée.
  • Les espaces inférieurs doivent être remplis avant les espaces supérieurs, car le remplissage des espaces dans l'ordre opposé rend l'animation de l'arbre de Noël très différente de la sortie de mon code d'origine. (ajouté le 2011-12-20)

Joyeuses fêtes!

Figure 1: zones étiquetées d'un écran 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

Exemples d'entrées

Code Golf Banner

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

Logo de débordement de pile

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

Arbres de Noël

                                        *
                                       ***                           *
                *                     *****                         ***
               ***                   *******           *           *****
              *****                 *********         ***            *
                *                  ***********       *****
                       *          *************     *******
        *             ***        ***************       *               *
       ***           *****      *****************                     ***
      *****         *******    *******************                   *****
     *******           *      *********************                 *******
    *********                           *                          *********
        *                                                              *
Veuillez vous lever
la source
1
Le troisième sapin de Noël est cassé.
Bobby
Beau défi! Je pense que les règles devraient être énumérées pour une référence plus facile, et je ne comprends pas les troisième et sixième règles ...
hallvabo
@hallvabo J'ai clarifié ces deux règles, cette dernière en ajoutant une figure étiquetée.
PleaseStand
Demande de clarification: la nouvelle ligne est-elle incluse dans la longueur de ligne maximale de 80 caractères, ou est-elle au maximum 80 caractères plus la nouvelle ligne? (J'ai supposé ce dernier, mais certaines soumissions semblent avoir supposé le premier.)
Ilmari Karonen
@IlmariKaronen Ce dernier.
PleaseStand

Réponses:

5

Perl, 196/239 caractères

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}

Cette solution diffère de votre exemple JS en ce que le motif est rempli de haut en bas plutôt que de bas en haut, mais je suppose que c'est OK car vous n'avez rien dit à ce sujet dans les règles.

Une réduction triviale de 1 caractère peut être obtenue en remplaçant \epar un caractère ESC littéral, mais cela rend le code beaucoup plus difficile à lire et à modifier.


Mise à jour: Je l' ai réussi à trouver une version qui remplit le modèle de bas en haut, et ne permet pas la neige de tomber à travers les parties remplies du modèle, comme dans l'exemple la mise en œuvre JS, au prix de 43 caractères supplémentaires:

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}

Le remplacement ($s[$_]&=~$f[$_])par juste $s[$_]sauverait 11 caractères en laissant passer la neige qui tombe à travers les parties remplies du motif (ce qui correspond à la spécification, mais pas à l'exemple d'implémentation).


OK, puisque je semble toujours mener la course après une semaine, je suppose que je devrais expliquer comment ma solution fonctionne pour encourager plus de compétition. (Remarque: cette explication concerne la version de remplissage descendante de 196 caractères. Je pourrai la modifier pour inclure l'autre version ultérieurement.)

Tout d'abord, la seule grosse astuce sur laquelle ma solution est basée est que, en raison de la façon dont les codes de caractères ASCII sont organisés, les 1 bits du code ASCII pour un espace se trouvent être un sous-ensemble de ceux du code pour un astérisque.

Ainsi, les expressions suivantes sont vraies: " " & "*" eq " "et " " | "*" eq "*". C'est ce qui me permet d'utiliser des opérations de chaîne au niveau du bit pour combiner les parties statiques et mobiles de la scène sans avoir à boucler sur des caractères individuels.

Donc, avec cela à l'écart, passons en revue le code. En voici une version dé-golfée:

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 première ligne configure les tableaux @f(pour "fixe") et @p(pour "motif"). @fformera la partie fixe de l'affichage et commencera par ne contenir que des espaces, tandis que @p, qui n'est pas affiché directement, contient le motif d'entrée; au fur et à mesure de l'animation, nous ajouterons de plus en plus d'astérisques @fjusqu'à ce que cela ressemble finalement @p.

Plus précisément, @f = ($" x 80) x 23définit @f24 chaînes de 80 espaces chacune. ( $"est une variable Perl spéciale dont la valeur par défaut se trouve être un espace.) Nous prenons ensuite cette liste, y ajoutons les lignes d'entrée à l'aide de l'opérateur readline <>, prenons les 24 dernières lignes de cette liste combinée et l'affectons à @p: c'est une façon compacte de remplir @pavec des lignes vides afin que le motif apparaisse où il devrait. Enfin, nous chompintroduisons les lignes d'entrée @ppour supprimer tous les retours à la ligne afin qu'ils ne causent pas de problèmes plus tard.

Maintenant, regardons la boucle principale. Il s'avère que {...;redo}c'est un moyen plus court d'écrire une boucle infinie que while(1){...}ou même for(;;){...}, surtout si nous arrivons à omettre le point-virgule avant redocar il suit immédiatement un ifbloc.

La première ligne de la boucle principale présente le tableau @s(pour "snow", bien sûr), auquel il ajoute une chaîne aléatoire de 80 caractères de 90% d'espaces et 10% d'astérisques à chaque itération. (Pour enregistrer quelques caractères, je n’ajoute jamais de lignes supplémentaires à la fin du@s tableau, donc il continue de s'allonger de plus en plus. prendra beaucoup plus de temps que la plupart des gens ne regarderaient jamais cette animation. L'ajout d'une pop@s;déclaration avant le selectcorrigerait au prix de sept caractères.)

Le reste de la boucle principale est enveloppé dans un ifbloc, de sorte qu'il ne s'exécute qu'une fois que le @stableau contient au moins 24 lignes. C'est un moyen simple de se conformer à la spécification, qui nécessite que tout l'affichage soit rempli de neige tombante dès le début, et simplifie également un peu les opérations au niveau du bit.

Vient ensuite une foreachboucle, qui dans la version golfée est en fait une seule déclaration avec un for 0..23modificateur. Puisque le contenu de la boucle a probablement besoin d'explications, je vais le décompresser un peu plus bas:

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
}

Tout d'abord, $_c'est la variable de compteur de boucle par défaut en Perl sauf si une autre variable est spécifiée. Ici, il va de 0 à 23, c'est-à-dire sur les 24 lignes du cadre d'affichage. $foo[$_]désigne l'élément indexé par $_dans le tableau @foo.

Sur la première ligne de la boucle dé-golfée, nous imprimons soit une nouvelle ligne (obtenue commodément à partir de la $/variable spéciale) ou, lorsqu'elle $_est égale à 0, la chaîne "\e[H", où \edénote un caractère ESC. C'est un code de contrôle de terminal ANSI qui déplace le curseur dans le coin supérieur gauche de l'écran. Techniquement, nous pourrions omettre cela si nous supposions une taille d'écran spécifique, mais je l'ai gardée dans cette version car cela signifie que je n'ai pas à redimensionner mon terminal pour exécuter l'animation.

Sur la $t = $f[$_]ligne, nous enregistrons simplement la valeur actuelle de $f[$_]dans une variable "temporaire" (donc $t) avant de la changer potentiellement dans la ligne suivante, où $s[$_] & $p[$_]donne l'intersection (au niveau du bit ET) de la neige qui tombe et le motif d'entrée, et l' |=opérateur OU que dans la ligne de sortie fixe $f[$_].

Sur la ligne ci-dessous, $t ^ $f[$_]donne le XOR au niveau du bit des valeurs précédentes et actuelles de $f[$_], c'est-à-dire une liste des bits que nous avons modifiés dans la ligne précédente, le cas échéant, et la négation de l'une des chaînes d'entrée avec ~annule la sortie. Ainsi, ce que nous obtenons est un masque de bits avec tous les bits mis à 1 à l' exception de ceux que nous venons d'ajouter à $f[$_]la ligne précédente. ANDing ce bitmask sur en $s[$_]supprime ces bits; en effet, cela signifie que lorsqu'un flocon de neige qui tombe remplit un trou dans le motif fixe, il est retiré du réseau de neige qui tombe.

Enfin, print $f[$_] | $s[$_](qui dans la version golfée est implémentée en effectuant simplement un OR des deux lignes précédentes ensemble) imprime simplement l'union (OR au niveau du bit) des flocons de neige fixes et mobiles sur la ligne actuelle.

Une dernière chose à expliquer est le select '', '', '', 0.1dessous de la boucle intérieure. C'est juste une façon délicate de dormir 0,1 seconde en Perl; pour une raison historique stupide, la sleepcommande Perl standard a une résolution d'une seconde, et l'importation d'un meilleur sleepdepuis le Time::HiResmodule prend plus de caractères que d'abuser de 4-argselect .

Ilmari Karonen
la source
Il semble y avoir un léger bug: la 24ème ligne n'est pas utilisée, et la dernière ligne de l'art ASCII est la 23ème ligne. Et il devrait vraiment remplir les espaces inférieurs avant de remplir les espaces supérieurs, même si je ne l'avais pas spécifié à l'origine.
PleaseStand
@PleaseStand: J'ai changé le code pour utiliser les 24 lignes (et je me suis d'ailleurs débarrassé du say), au prix de 2 caractères supplémentaires. Je ne pense pas que je puisse changer l'ordre de remplissage très facilement, cependant; ma mise en œuvre y est plutôt fondamentalement liée.
Ilmari Karonen
Grande explication! J'aimerais pouvoir voter à nouveau pour cette entrée.
Dillon Cower
@PleaseStand: En fait , je ne parviens à faire une version de remplissage bas vers le haut, qui ressemble maintenant à peu près la même chose que votre exemple JS; il est un peu plus long que celui descendant, mais toujours plus court que les autres entrées jusqu'à présent.
Ilmari Karonen
3

HTML et JavaScript, 436 caractères

Ajoutez-le à l'entrée:

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

Voyez-le fonctionner pour chaque exemple: code golf , logo Stack Overflow , arbres de Noël . Les utilisateurs d'Internet Explorer doivent exécuter la version 9 et définir le "Mode Document" sur "Normes IE9" (à l'aide des outils de développement F12) pour que cette soumission fonctionne correctement.

Veuillez vous lever
la source
1
Les 3 semblent être cassés? pasteall.org/pic/show.php?id=66297
CoDEmanX
1

Python, 299 caractères

Cela doit être conforme aux règles, en supposant que la nouvelle ligne est incluse dans la limite de 80 caractères.

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
la source
Votre sortie devient fade si l'entrée a des lignes qui font exactement 80 caractères (plus la nouvelle ligne). J'ai demandé à PleaseStand de préciser si c'est OK.
Ilmari Karonen
C'est parce que j'inclus une nouvelle ligne à la fin de chaque ligne large de 80 caractères. La description est ambiguë ici, car elle spécifie que les sauts de ligne doivent être inclus, mais aussi que l'on peut supposer que le terminal a une largeur de 80 caractères (auquel cas on peut omettre les sauts de ligne et dépendre de l'habillage automatique).
hallvabo
Je pense que le problème réel est que vous ne supprimez pas les sauts de ligne avant de remplir les lignes d'entrée à 80 caractères, de sorte qu'une entrée de 80 caractères plus la nouvelle ligne finit par ajouter 81 caractères aet gâche ainsi l'indexation. Je viens de l'essayer et il semble que le remplacement %epar la %e.rstrip()ligne 6 corrige le problème. (Bien sûr, il pourrait bien y avoir une solution plus courte; je ne suis pas bon au golf Python.)
Ilmari Karonen
Si vous voulez prendre en charge 81 lignes de caractères, changez simplement les nombres, et cela fonctionne très bien. Tant que vous restez sous 100 caractères par ligne, cela ne changera pas le nombre de caractères :-)
hallvabo
Si je change votre code pour utiliser des lignes de 81 caractères, il ne fonctionnera pas correctement sur un terminal de 80 colonnes, maintenant? Vous produisez très bien une sortie sur 80 colonnes, c'est juste que vous n'acceptez pas correctement une entrée sur 80 colonnes . Essayez-le: créez un fichier d'entrée avec quelques lignes avec 80 astérisques sur chacune et voyez ce qui se passe. Cela ne devrait pas être si difficile: ma solution le gère très bien.
Ilmari Karonen
0

Java, 625 caractères

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

Une solution simple en Java.

SiZ
la source
Bien, mais je ne pense pas que vous respectiez les spécifications. En particulier, vous montrez le motif complet depuis le début - il ne "se forme pas à partir de la neige qui tombe" comme dans l'exemple de démonstration. (Certes, cela pourrait être mieux expliqué dans la question. Vous avez vraiment besoin de regarder la démo pour comprendre ce qu'elle doit faire.) De plus, votre fréquence d'images est trop lente, vous ne commencez pas avec un écran rempli de neige, vous semblez en supposant un format d'entrée différent des exemples donnés, et vous devez vraiment réinitialiser la position du curseur entre les images (impression"\033[H" devrait le faire).
Ilmari Karonen