Scena di neve animata ASCII


22

Scrivi il programma più breve per trasformare qualsiasi pezzo di arte ASCII in una scena di neve animata che inizia a formarsi dalla neve che cade ( esempio JavaScript non golfato ultimo aggiornamento 2011-12-19).

Specifica di input : il programma deve accettare combinazioni arbitrarie di spazi, asterischi e newline. L'input conterrà al massimo 23 righe e 80 caratteri per riga. Non ci saranno righe vuote, tuttavia le righe possono essere composte solo da spazi bianchi. Verrà inclusa una nuova riga finale finale che deve essere ignorata.

Output : Output caratteri ASCII (spazi, asterischi) e codici di controllo (ritorni a capo, avanzamenti riga, codici di escape ANSI, ecc.) Per la console di testo o l'emulatore di terminale del sistema operativo fino a quando l'utente non termina manualmente il programma. È possibile supporre che la finestra del terminale sia di 80x24 caratteri se il sistema operativo consente tale impostazione.

Regole :

  • L'animazione deve essere fluida e veloce (preferibilmente 15 fps).
  • La densità della neve deve essere compresa tra il 5% e il 15%.
  • Non più di uno schermo di neve può scorrere al secondo. (Ciò significa che non è possibile aggiungere più di 24 linee di neve fresca in un secondo periodo di tempo.)
  • La neve non deve mostrare alcun motivo evidente quando entra nella parte superiore dello schermo; deve apparire casuale.
  • Il programma deve riempire tutte le file dello schermo di neve il più rapidamente possibile all'avvio; il riempimento iniziale delle singole righe dello schermo non deve essere ovvio per lo spettatore.
  • L'angolo inferiore sinistro della grafica ASCII di input deve trovarsi nell'angolo inferiore sinistro dello schermo (Figura 1 per ulteriori chiarimenti).
  • L'area all'interno o sotto l'arte ASCII non deve essere riempita permanentemente di asterischi. Tuttavia, gli asterischi possono (ma non sono tenuti a) scorrere in quest'area.
  • La neve non deve accumularsi nella parte inferiore dello schermo o sopra la neve esistente ad eccezione di quanto mostrato nell'input.
  • Gli spazi inferiori devono essere riempiti prima di quelli superiori, poiché riempire gli spazi nell'ordine opposto rende l'animazione dell'albero di Natale molto diversa dall'output del mio codice originale. (aggiunto il 20-12-2011)

Buone vacanze!

Figura 1: aree etichettate di uno schermo 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

Input di esempio

Banner da golf codice

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

Logo Stack Overflow

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

Alberi di Natale

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

1
Il terzo albero di Natale è rotto.
Bobby,

Bella sfida! Penso che le regole debbano essere enumerate per un riferimento più semplice, e non capisco la terza e la sesta regola ...
hallvabo,

@hallvabo Ho chiarito queste due regole, quest'ultima aggiungendo una figura etichettata.
PleaseStand,

Richiesta di chiarimento: la nuova riga è inclusa nella lunghezza massima della riga di 80 caratteri o è max 80 caratteri più la nuova riga? (Ho assunto il secondo, ma alcuni argomenti sembrano aver assunto il primo.)
Ilmari Karonen,

@IlmariKaronen Quest'ultimo.
PleaseStand,

Risposte:


5

Perl, 196/239 caratteri

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}

Questa soluzione differisce dal tuo esempio JS in quanto lo schema è riempito dall'alto verso il basso piuttosto che dal basso verso l'alto, ma presumo che sia OK poiché non hai detto nulla al riguardo nelle regole.

Una banale riduzione di 1 carattere può essere ottenuta sostituendo \e con un carattere letterale ESC, ma ciò rende il codice molto più difficile da leggere e modificare.


Aggiornamento: sono riuscito a trovare una versione che riempie il modello dal basso verso l'alto e non consente alla neve di cadere attraverso le parti riempite del modello, come nell'esempio di implementazione JS, al costo di 43 caratteri aggiuntivi:

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}

Sostituire ($s[$_]&=~$f[$_])con just $s[$_]salverebbe 11 caratteri lasciando passare la neve che cade attraverso le parti piene del modello (che corrisponde alla specifica, ma non all'implementazione di esempio).


OK, dato che dopo una settimana mi sembra ancora di guidare la gara, credo che dovrei spiegare come funziona la mia soluzione per incoraggiare una maggiore competizione. (Nota: questa spiegazione è per la versione di riempimento top-down da 196 caratteri. Posso modificarla per includere l'altra versione in un secondo momento.)

Prima di tutto, l'unico grande trucco su cui si basa la mia soluzione è che, a causa del modo in cui sono organizzati i codici dei caratteri ASCII, i 1 bit nel codice ASCII per uno spazio sono solo un sottoinsieme di quelli nel codice per un asterisco.

Pertanto, le seguenti espressioni sono vere: " " & "*" eq " "e " " | "*" eq "*". Questo è ciò che mi consente di utilizzare le operazioni di stringa bit a bit per combinare le parti statiche e mobili della scena senza dover ricorrere a singoli caratteri.

Quindi, a parte questo, andiamo oltre il codice. Ecco una versione de-golfed di esso:

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 prima riga imposta gli array @f(per "fixed") e @p(per "pattern"). @fformerà la parte fissa del display e inizierà a contenere nient'altro che spazi, mentre @p, che non è mostrato direttamente, contiene il modello di input; man mano che l'animazione procede, aggiungeremo sempre più asterischi @ffino a quando alla fine sembrerà proprio come@p .

In particolare, @f = ($" x 80) x 23imposta @f24 stringhe di 80 spazi ciascuna. ( $"è una variabile Perl speciale il cui valore predefinito è solo uno spazio.) Quindi prendiamo questo elenco, aggiungiamo le linee di input ad esso usando l'operatore readline <>, prendiamo le ultime 24 linee di questo elenco combinato e lo assegniamo a @p: questo è un modo compatto per riempire @pcon linee vuote in modo che il motivo appaia dove dovrebbe. Infine, inseriamo chomple righe di input @pper rimuovere eventuali newline finali in modo che non causino problemi in seguito.

Ora diamo un'occhiata al ciclo principale. Si scopre che {...;redo}è un modo più breve di scrivere un ciclo infinito di while(1){...}o addirittura for(;;){...}, soprattutto se prima omettiamo il punto e virgola redoperché segue immediatamente un ifblocco.

La prima riga del ciclo principale introduce l'array @s(per "snow", ovviamente), al quale antepone una stringa casuale di 80 caratteri con spazi del 90% e asterischi del 10% su ogni iterazione. (Per salvare alcuni caratteri, non ho mai effettivamente fatto uscire righe extra dalla fine @sdell'array, quindi continua ad allungarsi sempre più. Alla fine ciò fermerà il programma quando l'array diventa troppo lungo per adattarsi alla memoria, ma quello impiegherà molto più tempo di quanto la maggior parte delle persone guarderebbe mai questa animazione. L'aggiunta di una pop@s;dichiarazione prima della selectrisolverà ciò al costo di sette caratteri.

Il resto del ciclo principale è racchiuso in un ifblocco, in modo che venga eseguito solo quando l' @sarray contiene almeno 24 righe. Questo è un modo semplice per rispettare le specifiche, che richiede che l'intero display sia pieno di neve che cade fin dall'inizio, e semplifica anche un po 'le operazioni bit a bit.

Segue un foreachloop, che nella versione golf è in realtà una singola istruzione con un for 0..23modificatore. Dato che il contenuto del loop probabilmente ha bisogno di qualche spiegazione, lo spaccherò un po 'più sotto:

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
}

Prima di tutto, $_è la variabile contatore loop predefinita in Perl a meno che non venga specificata un'altra variabile. Qui corre da 0 a 23, cioè sopra le 24 linee nel riquadro di visualizzazione. $foo[$_]indica l'elemento indicizzato $_dall'array @foo.

Sulla prima riga del loop de-golfed, stampiamo una nuova riga (convenientemente ottenuta dalla $/variabile speciale) o, quando è $_uguale a 0, la stringa "\e[H", dove \eindica un carattere ESC. Questo è un codice di controllo del terminale ANSI che sposta il cursore nell'angolo in alto a sinistra dello schermo. Tecnicamente, potremmo ometterlo se assumessimo una dimensione dello schermo specifica, ma l'ho tenuto in questa versione poiché significa che non devo ridimensionare il mio terminale per eseguire l'animazione.

Sulla $t = $f[$_]linea, salviamo semplicemente il valore corrente di $f[$_]in una variabile "temporanea" (quindi $t) prima di cambiarlo potenzialmente nella riga successiva, dove $s[$_] & $p[$_]fornisce l'intersezione (AND bit a bit) della neve che cade e il modello di input e gli |=operatori OR quello nella linea di uscita fissa $f[$_].

Sulla riga sottostante, $t ^ $f[$_]fornisce l'XOR bit a bit dei valori precedenti e correnti di $f[$_], ovvero un elenco dei bit che abbiamo modificato nella riga precedente, se presente, e negando una delle stringhe di input con ~nega l'uscita. Pertanto, ciò che otteniamo è una maschera di bit con tutti i bit impostati su 1 tranne quelli che abbiamo appena aggiunto alla $f[$_]riga precedente. ANDing che la maschera di bit su $s[$_]rimuove quei bit da esso; in effetti, ciò significa che quando un fiocco di neve che cade riempie un buco nel modello fisso, viene rimosso dall'array di neve che cade.

Infine, print $f[$_] | $s[$_](che nella versione golfizzata è implementato semplicemente ORing le due righe precedenti insieme) stampa solo l'unione (OR bit a bit) dei fiocchi di neve fissi e mobili sulla riga corrente.

Un'altra cosa rimasta da spiegare è il select '', '', '', 0.1ciclo interno sottostante. Questo è solo un modo klugy per dormire 0,1 secondi in Perl; per qualche stupida ragione storica, il sleepcomando Perl standard ha una risoluzione di un secondo e l'importazione di un migliore sleepdal Time::HiResmodulo richiede più caratteri che l'abuso di 4-argselect .


Sembra che ci sia un leggero bug: la 24a linea non viene utilizzata e la linea di fondo dell'arte ASCII è la 23a linea. E dovrebbe davvero riempire gli spazi inferiori prima di riempire quelli superiori, anche se inizialmente non l'ho specificato.
PleaseStand,

@PleaseStand: ho modificato il codice per utilizzare tutte e 24 le righe (e, per inciso, me ne sono liberato say), al costo di 2 caratteri extra. Non credo di poter cambiare l'ordine di riempimento molto facilmente, però; la mia implementazione è sostanzialmente legata ad essa.
Ilmari Karonen,

Grande spiegazione! Vorrei poter votare nuovamente questa voce.
Dillon Cower il

@PleaseStand: in realtà sono riuscito a creare una versione di riempimento dal basso, che ora è praticamente uguale al tuo esempio JS; è un po 'più lungo di quello dall'alto verso il basso, ma finora è ancora più corto delle altre voci.
Ilmari Karonen,

3

HTML e JavaScript, 436 caratteri

Prependerlo all'input:

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

Guardalo correre per ogni esempio: codice golf , logo Stack Overflow , alberi di Natale . Gli utenti di Internet Explorer devono eseguire la versione 9 e impostare la "Modalità documento" su "Standard IE9" (utilizzando gli strumenti di sviluppo F12) affinché questa presentazione funzioni correttamente.


1
Tutti e 3 sembrano essere rotti? pasteall.org/pic/show.php?id=66297
CoDEmanX

1

Python, 299 caratteri

Ciò dovrebbe essere conforme alle regole, supponendo che la nuova riga sia inclusa nel limite di 80 caratteri.

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)

L'output diventa complicato se l'input ha delle righe lunghe esattamente 80 caratteri (più newline). Ho chiesto a PleaseStand di chiarire se va bene.
Ilmari Karonen,

Questo perché includo una nuova riga alla fine di ogni riga larga da 80 caratteri. La descrizione è ambigua qui, poiché specifica che devono essere incluse le nuove righe, ma anche che si può presumere che il terminale sia largo 80 caratteri (nel qual caso si possono omettere le nuove righe e dipendere dal wrapping automatico).
hallvabo,

Penso che il vero problema sia che non si rimuovono le newline prima di riempire le righe di input a 80 caratteri, in modo che un input di 80 caratteri più newline finisca per aggiungere effettivamente 81 caratteri ae quindi incasinare l'indicizzazione. L'ho appena provato e sembra che la sostituzione %econ la %e.rstrip()riga 6 risolva il problema. (Certo, potrebbe esserci una soluzione più breve; non sono bravo a giocare a golf in Python.)
Ilmari Karonen il

Se vuoi supportare 81 linee di caratteri, basta cambiare i numeri, e funziona bene. Fintanto che rimani sotto i 100 caratteri per riga, non cambierà il conteggio dei caratteri :-)
hallvabo

Se cambio il codice per utilizzare le righe da 81 caratteri, non funzionerà correttamente su un terminale a 80 colonne, ora lo farà? Stai producendo un output di 80 colonne bene, è solo che non stai accettando correttamente l' input di 80 colonne . Provalo: crea un file di input con un paio di righe con 80 asterischi su ciascuna e guarda cosa succede. Non dovrebbe essere così difficile: la mia soluzione lo gestisce bene.
Ilmari Karonen,

0

Java, 625 caratteri

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 soluzione semplice in Java.


Bello, ma non credo che tu stia rispettando le specifiche. In particolare, mostri l'intero modello dall'inizio - non si "forma dalla neve che cade" come nella demo di esempio. (Certo, questo potrebbe essere spiegato meglio nella domanda. Devi davvero guardare la demo per capire cosa dovrebbe fare.) Inoltre, il tuo framerate è troppo lento, non inizi con uno schermo pieno di neve, sembra che tu assumendo un formato di input diverso dagli esempi forniti, e dovresti davvero ripristinare la posizione del cursore tra i frame (la stampa "\033[H"dovrebbe farlo).
Ilmari Karonen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.