Cena de neve animada ASCII

22

Escreva o programa mais curto para transformar qualquer peça de arte ASCII em uma cena de neve animada que comece a se formar a partir da neve que cai ( exemplo de JavaScript sem golfe ) atualizado pela última vez em 2011-12-19).

Especificação de entrada : Seu programa deve aceitar combinações arbitrárias de espaços, asteriscos e novas linhas. A entrada conterá no máximo 23 linhas e 80 caracteres por linha. Não haverá linhas vazias, mas as linhas podem consistir apenas em espaço em branco. Uma única nova linha à direita será incluída e deve ser ignorada.

Saída : Saída de caracteres ASCII (espaços, asteriscos) e códigos de controle (retornos de carro, alimentações de linha, códigos de escape ANSI, etc.) para o console de texto do sistema operacional ou emulador de terminal até o usuário finalizar manualmente o programa. Você pode supor que a janela do terminal tenha 80x24 caracteres se o seu sistema operacional permitir essa configuração.

Regras :

  • A animação deve ser suave e rápida (preferencialmente 15 qps).
  • A densidade da neve deve estar entre 5% e 15%.
  • Não mais do que uma tela de neve pode rolar por segundo. (Isso significa que não é possível adicionar mais de 24 linhas de neve nova em um segundo período de tempo.)
  • A neve não deve exibir nenhum padrão óbvio quando entra na parte superior da tela; deve parecer aleatório.
  • O programa deve preencher todas as linhas da tela com neve o mais rápido possível quando for iniciado; o preenchimento inicial das linhas individuais da tela não deve ser óbvio para o visualizador.
  • O canto inferior esquerdo da arte ASCII de entrada deve estar no canto inferior esquerdo da tela (Figura 1 para maiores esclarecimentos).
  • A área dentro ou sob a arte ASCII não deve ser permanentemente preenchida com asteriscos. No entanto, os asteriscos podem (mas não precisam) rolar por essa área.
  • A neve não deve acumular-se na parte inferior da tela ou em cima da neve existente, exceto conforme mostrado na entrada.
  • Os espaços inferiores devem ser preenchidos antes dos superiores, pois o preenchimento de espaços na ordem oposta faz com que a animação da árvore de Natal pareça muito diferente da saída do meu código original. (adicionado 20-12-2011)

Boas festas!

Figura 1: áreas rotuladas de uma tela 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 Exemplo

Código Golf Banner

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

Logotipo do estouro de pilha

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

Árvores de Natal

                                        *
                                       ***                           *
                *                     *****                         ***
               ***                   *******           *           *****
              *****                 *********         ***            *
                *                  ***********       *****
                       *          *************     *******
        *             ***        ***************       *               *
       ***           *****      *****************                     ***
      *****         *******    *******************                   *****
     *******           *      *********************                 *******
    *********                           *                          *********
        *                                                              *
PleaseStand
fonte
1
A terceira árvore de natal está quebrada.
Bobby
Bom desafio! Eu acho que as regras devem ser enumerados para facilitar a referência, e eu não entendo o terceiro e sexto regra ...
hallvabo
@hallvabo Esclarei essas duas regras, adicionando uma figura rotulada.
PleaseStand
Solicitação de esclarecimento: a nova linha está incluída no comprimento máximo da linha de 80 caracteres ou no máximo 80 caracteres mais a nova linha? (Eu assumi o último, mas algumas apresentações parecem ter assumido o ex.)
Ilmari Karonen
@IlmariKaronen Este último.
usar o seguinte código

Respostas:

5

Perl, 196/23 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}

Essa solução difere do seu exemplo de JS, pois o padrão é preenchido de cima para baixo em vez de de baixo para cima, mas presumo que não há problema, pois você não disse nada sobre isso nas regras.

Uma redução trivial de 1 caractere pode ser obtida substituindo-se \epor um caractere ESC literal, mas isso torna o código muito mais difícil de ler e editar.


Update: Eu fiz gerir para chegar a uma versão que enche o padrão de até inferior, e não permite que a neve a cair através das partes cheias do padrão, como no exemplo de implementação JS, ao custo de 43 caracteres extras:

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}

Substituir ($s[$_]&=~$f[$_])por apenas $s[$_]economizaria 11 caracteres, deixando a neve cair passar pelas partes preenchidas do padrão (que corresponde à especificação, mas não à implementação de exemplo).


OK, como ainda continuo liderando a corrida depois de uma semana, acho que devo explicar como minha solução funciona para incentivar mais concorrência. (Nota: Esta explicação é para a versão de preenchimento de cima para baixo de 196 caracteres. Eu posso alterá-la para incluir a outra versão posteriormente.)

Primeiro de tudo, o grande truque em que minha solução se baseia é que, devido à maneira como os códigos de caracteres ASCII são organizados, os 1 bits no código ASCII de um espaço são um subconjunto daqueles no código de um asterisco.

Assim, as seguintes expressões são verdadeiras: " " & "*" eq " "e " " | "*" eq "*". É isso que me permite usar operações de string bit a bit para combinar as partes estáticas e móveis da cena sem ter que passar por caracteres individuais.

Então, com isso fora do caminho, vamos revisar o código. Aqui está uma versão descodificada:

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

A primeira linha configura as matrizes @f(para "fixo") e @p(para "padrão"). @fformará a parte fixa da tela e começará a conter nada além de espaços, enquanto @p, que não é mostrado diretamente, contém o padrão de entrada; À medida que a animação prosseguir, adicionaremos mais e mais asteriscos @faté que eventualmente pareça @p.

Especificamente, @f = ($" x 80) x 23define @fpara 24 strings de 80 espaços cada. ( $"é uma variável Perl especial cujo valor padrão é apenas um espaço.) Em seguida, pegamos essa lista, anexamos as linhas de entrada usando o operador readline <>, pegamos as últimas 24 linhas dessa lista combinada e atribuímos a @p: this is uma maneira compacta de preencher @plinhas em branco para que o padrão apareça onde deveria. Por fim, inserimos chompas linhas de entrada @ppara remover as novas linhas finais, para que não causem problemas mais tarde.

Agora, vamos olhar para o loop principal. Acontece que {...;redo}é uma maneira mais curta de escrever um loop infinito do que while(1){...}ou mesmo for(;;){...}, especialmente se conseguirmos omitir o ponto-e-vírgula antes redoporque ele segue imediatamente um ifbloco.

A primeira linha do loop principal apresenta a matriz @s(para "neve", é claro), à qual ela precede uma sequência aleatória de 80 caracteres com 90% de espaços e 10% de asteriscos em todas as iterações. (Para salvar alguns caracteres, eu nunca retiro linhas extras no final da @smatriz, por isso ela fica cada vez mais longa. Eventualmente, isso fará com que o programa pare, pois a matriz fica muito longa para caber na memória, mas isso demorará muito mais tempo do que a maioria das pessoas assistiria a essa animação. Adicionar uma pop@s;declaração antes de selectconsertar isso ao custo de sete caracteres.)

O restante do loop principal é agrupado em um ifbloco, para que seja executado apenas quando a @smatriz contiver pelo menos 24 linhas. Essa é uma maneira simples de cumprir as especificações, que exige que toda a tela seja preenchida com neve caindo desde o início e também simplifica um pouco as operações bit a bit.

Em seguida, vem um foreachloop, que na versão golfed é na verdade uma única declaração com um for 0..23modificador. Como o conteúdo do loop provavelmente precisa de explicações, vou descompactá-lo um pouco mais abaixo:

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
}

Primeiro, $_é a variável do contador de loop padrão no Perl, a menos que outra variável seja especificada. Aqui, ele varia de 0 a 23, ou seja, ao longo das 24 linhas no quadro de exibição. $foo[$_]denota o elemento indexado por $_na matriz @foo.

Na primeira linha do loop descolado, imprimimos uma nova linha (convenientemente obtida da $/variável especial) ou, quando $_igual a 0, a string "\e[H", onde \edenota um caractere ESC. Este é um código de controle de terminal ANSI que move o cursor para o canto superior esquerdo da tela. Tecnicamente, poderíamos omitir isso se assumirmos um tamanho de tela específico, mas eu o mantive nesta versão, pois significa que não preciso redimensionar meu terminal para executar a animação.

Na $t = $f[$_]linha, apenas salvamos o valor atual de $f[$_]uma variável "temporária" (daí $t) antes de potencialmente alterá-lo na próxima linha, onde $s[$_] & $p[$_]fornece a interseção (AND bit a bit) da neve que cai e o padrão de entrada e os |=ORs do operador isso na linha de saída fixa $f[$_].

Na linha abaixo disso, $t ^ $f[$_]fornece o XOR bit a bit dos valores anteriores e atuais de $f[$_], ou seja, uma lista dos bits que alteramos na linha anterior, se houver, e negar uma das seqüências de entrada ~nega a saída. Portanto, o que obtemos é uma máscara de bits com todos os bits definidos como 1, exceto aqueles que acabamos de adicionar $f[$_]na linha anterior. ANDing nesse bitmask $s[$_]remove esses bits dele; de fato, isso significa que, quando um floco de neve caindo preenche um buraco no padrão fixo, ele é removido da matriz de neve que cai.

Finalmente, print $f[$_] | $s[$_](que na versão golfed é implementada apenas ORing as duas linhas anteriores juntas) apenas imprime a união (OR bit a bit) dos flocos de neve fixos e móveis na linha atual.

Mais uma coisa a explicar é a parte select '', '', '', 0.1inferior do loop interno. Esta é apenas uma maneira klugy de dormir 0,1 segundos no Perl; por alguma razão histórica bobo, o padrão Perl sleepcomando tem uma segunda resolução, e importar um melhor sleepdo Time::HiResmódulo leva mais caracteres do que abusar de 4-argselect .

Ilmari Karonen
fonte
Parece haver um pequeno erro: a 24ª linha não é usada e a linha inferior da arte ASCII é a 23ª linha. E ele realmente deve preencher os espaços inferiores antes de preencher os superiores, embora eu não tenha especificado isso originalmente.
usar o seguinte código
@PleaseStand: mudei o código para usar todas as 24 linhas (e aliás me livrei do say), ao custo de 2 caracteres extras. Eu acho que não posso alterar a ordem de preenchimento com muita facilidade; minha implementação está fundamentalmente ligada a ela.
Ilmari Karonen
Ótima explicação! Eu gostaria de poder votar novamente nesta entrada.
Dillon Cower
@PleaseStand: Na verdade , consegui fazer uma versão de preenchimento de baixo para cima, que agora parece praticamente a mesma do seu exemplo de JS; é um pouco mais longo que o de cima para baixo, mas ainda menor do que as outras entradas até agora.
Ilmari Karonen
3

HTML e JavaScript, 436 caracteres

Anexe-o à 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>

Veja executar para cada exemplo: código de golfe , logotipo Stack Overflow , árvores de Natal . Os usuários do Internet Explorer precisam executar a versão 9 e definir o "Modo de Documento" como "IE9 standards" (usando as ferramentas de desenvolvedor F12) para que esse envio funcione corretamente.

PleaseStand
fonte
1
Todos os 3 parecem estar quebrados? pasteall.org/pic/show.php?id=66297
CoDEmanX
1

Python, 299 caracteres

Isso deve estar de acordo com as regras, assumindo que a nova linha esteja incluída no limite 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
fonte
Sua saída fica imprecisa se a entrada tiver alguma linha com exatamente 80 caracteres (mais nova linha). Pedi ao PleaseStand para esclarecer se está tudo bem.
Ilmari Karonen
Isso ocorre porque eu incluo uma nova linha no final de cada linha de 80 caracteres. A descrição é ambígua aqui, pois especifica que as novas linhas devem ser incluídas, mas também que se possa assumir que o terminal tem 80 caracteres de largura (nesse caso, pode-se omitir as novas linhas e depender do empacotamento automático).
hallvabo
Eu acho que o problema real é que você não retira as novas linhas antes de preencher as linhas de entrada com 80 caracteres, para que uma entrada de 80 caracteres mais nova linha acabe anexando 81 caracteres ae, portanto, atrapalha a indexação. Eu apenas tentei isso, e parece que a substituição %ecom %e.rstrip()na linha 6 corrige o problema. (Claro, pode muito bem haver uma correção mais curto, eu não sou bom em Python golfe.)
Ilmari Karonen
Se você deseja suportar 81 linhas de caracteres, basta alterar os números, e tudo funciona bem. Enquanto você ficar com menos de 100 caracteres por linha, isso não vai mudar o caractere contagem :-)
hallvabo
Se eu alterar o seu código para usar linhas de 81 caracteres, ele não funcionará corretamente em um terminal de 80 colunas, agora será? Você está produzindo uma saída de 80 colunas muito bem, apenas não está aceitando a entrada de 80 colunas corretamente. Experimente: crie um arquivo de entrada com duas linhas com 80 asteriscos em cada uma e veja o que acontece. Não deve ser tão difícil: minha solução lida bem com isso.
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);}}

Uma solução simples em Java.

SiZ
fonte
Legal, mas acho que você não está cumprindo as especificações. Em particular, você mostra todo o padrão desde o início - ele não "se forma da neve que cai", como na demonstração de exemplo. (É certo que isso pode ser explicado melhor na pergunta. Você realmente precisa assistir à demonstração para entender o que deve fazer.) Além disso, sua taxa de quadros é muito lenta, você não começa com uma tela cheia de neve, parece que assumindo um formato de entrada diferente dos exemplos fornecidos, e você realmente deve redefinir a posição do cursor entre os quadros (a impressão "\033[H"deve fazê-lo).
Ilmari Karonen