Assista-os cair como dominós


22

Você mora dentro de um terminal com 80 caracteres de largura. Você está entediado e decide jogar dominó. Não, não o tipo chato que se parece com Scrabble, o tipo divertido em que você passa uma hora ajustando-os para vê-los cair em um segundo.

Nos terminais, o dominó fica assim:

|   upright domino
\   left-tilted domino
/   right-tilted domino
__  fallen domino

Como todos sabemos, se um dominó inclinado toca um vertical, o segundo dominó também é inclinado. A única exceção é se dois dominós inclinados o tocarem:

|\ --> \\        /| --> //        /|\ --> /|\

Ajuste a constante gravitacional do seu terminal para que essa transição leve 100 ms.

Se um dominó inclinado é suportado por outro dominó ou pelas paredes do terminal, sua jornada termina.

Nenhum dos dominós inclinados em

\||||____||||/__                /|\    /\    /|\                __\||||____||||/

(80 caracteres) se moverá, pois os dois dominós mais inclinados são suportados pelas paredes do terminal e todos os outros são suportados por outros dominós.

No entanto, se o espaço na direção de inclinação estiver vazio, o dominó cai:

| \\ --> |__\        // | --> /__|

Terminal. Constante gravitacional. Você entendeu…

Finalmente, há um leve vento da esquerda, de modo que os dominós inclinados para a direita caem mais rapidamente do que os dominados para a esquerda:

|/ \| --> |__\|

Tarefa

Escreva um programa / função que mostre uma animação de jogar dominó em um terminal.

Seu código deve fazer o seguinte:

  1. Leia uma sequência de caracteres de entrada, representando o estado inicial dos dominós.

    Essa sequência não conterá mais de 80 caracteres e consistirá apenas nos dominós descritos acima e em espaços vazios.

  2. Imprima o estado e aguarde 100 ms.

  3. Transforme o estado conforme explicado acima.

  4. Se o estado mudou, volte para 2.

Regras adicionais

  • O comprimento da sequência de entrada não afeta a largura do terminal; mesmo que a cadeia tenha menos de 80 caracteres, as paredes do terminal ainda terão 80 caracteres.

  • Cada vez que a etapa 2 é executada, o estado deve ser impresso no mesmo local, substituindo o estado anterior.

  • Como alguns idiomas não conseguem esperar exatamente 100 ms, fique à vontade para aguardar qualquer quantidade entre 50 e 1000 ms.

  • Aplicam-se as regras padrão de .

Exemplos

  • Para o estado inicial

     ||\/||
    

    imprima o seguinte (um sobre o outro):

     ||\/||
     |\\//|
     \\\///
    __\\//__
    
  • Para o estado inicial

    /||||\
    

    imprima o seguinte

    /||||\
    //||\\
    ///\\\
    
  • Para o estado inicial

    /|||\
    

    imprima o seguinte

    /|||\
    //|\\
    
  • Para o estado inicial

    |/ \|/ \|/ \|/ \|
    

    imprima o seguinte:

    |__\|__\|__\|__\|
    
  • Para o estado inicial (80 caracteres)

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    

    imprima o seguinte

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    

Respostas:


13

Retina , 87 86 85 bytes

Agradecemos a Dennis por economizar 1 byte.

^.{0,79}$
$0 
:`^
<ESC>c
(`/ | \\
__
/\|(?!\\)
//a
(?<!/)\|\\
\\
$
aaaaa
a
aaaa
(a+)+b|a
<empty>

<ESC>deve ser substituído pelo caractere de controle real (0x1B). <empty>representa uma linha de fuga vazia. Você pode executar o código acima a partir de um único arquivo com o -ssinalizador.

O código requer um terminal que suporte códigos de escape ANSI. Não consigo suprimir o avanço de linha na saída da Retina, portanto, preciso limpar o console inteiro a <ESC>ccada vez. Testei o código no bash usando o Mono para executar o Retina.

Explicação

^.{0,79}$
$0 

Começamos adicionando um espaço se a entrada contiver menos de 80 caracteres. Isso é feito para que /a extremidade direita não precise ser tratada separadamente.

:`^
<ESC>c

Agora, acrescentamos <ESC>ca string, que é o código de escape ANSI para limpar o terminal. Portanto, toda vez que a string for impressa, ela será feita na parte superior do terminal. O :`instrui o Retina a imprimir o resultado dessa substituição, ou seja, a configuração inicial.

(`/ | \\
__

(`inicia um loop. Como não há correspondência ), presume-se que o loop vá até a última etapa do programa. Cada iteração simula uma etapa do dominó caindo e depois "dorme" um pouco. Esta primeira etapa substitui /e \ao lado de um espaço __. Isso lida com o / \caso automaticamente corretamente, porque as correspondências não podem se sobrepor e são pesquisadas da esquerda para a direita. Assim, o /<sp>seria correspondido e transformado em __tal que o \não possa ser correspondido, e obtemos o correto __\.

/\|(?!\\)
//a

Isso se /|transforma, //desde que não haja \próximo a ele. Anexamos um atal que este novo /não mexa com o próximo estágio (que ainda não deveria "saber" sobre essa alteração).

(?<!/)\|\\
\\

A situação oposta: transforme |\-se \\desde que não haja /próximo a ele. Não precisamos colocar um aaqui, porque terminamos esta etapa da simulação.

Agora a parte de dormir ...

$
aaaaa

Anexa mais 5 as ao final do código.

a
aaaa

Transforma cada um aem 4 as, então temos 20 as no final.

(a+)+b|a
<empty>

Agora a parte divertida ... dormimos um pouco com a ajuda de um retorno catastrófico . Há uma quantidade exponencial de maneiras de dividir uma correspondência (a+)+entre repetições do grupo. Como as bcausas da falha na correspondência, o mecanismo retornará e tentará cada uma dessas combinações antes de decidir que (a+)+bnão pode corresponder. Pelos vinte anos ano final, isso leva meio segundo.

Ao mesmo tempo, permitimos que a regex corresponda a uma única a, mas somente depois de fazer o backtracking. Quando isso corresponde, substituí-lo por uma string vazia, removendo todos os as inseridos por um motivo ou outro da string.

Isso deixa a impressão da sequência no final da iteração do loop. Aqui é útil que eu ainda não tenha corrigido o comportamento de impressão de loops no Retina. Atualmente, há apenas um sinalizador para cada estágio, que diz "imprimir" ou "não imprimir". O padrão é "não imprimir", exceto o último estágio do programa, cujo padrão é "imprimir". Mas o palco está em loop, o que significa que ele realmente imprime a sequência atual em cada iteração. Normalmente, isso é realmente irritante, e você quase sempre precisa incluir um estágio vazio adicional no final, se você quiser apenas o resultado final, mas aqui permite salvar quatro bytes.


6

Javascript (ES6), 206 148 129 158 bytes

Eu finalmente tinha chegado a um ponto muito baixo, mas não limpava o console ou acrescentava um espaço extra; esses problemas foram corrigidos agora.

c=console;d=s=>{c.clear(s[79]||(s+=' ')),c.log(s),t=s[R='replace'](/\/ | \\/g,'__')[R](/\/\|/g,'//a')[R](/\|\\/g,'\\\\')[R](/a/g,'');t!=s&&setTimeout(d,99,t)}

Versão alternativa de 153 bytes, que deve funcionar no Node.JS:

d=s=>{s[79]||(s+=' '),console.log("\033c"+s),t=s[R='replace'](/\/ | \\/g,'__')[R](/\/\|/g,'//a')[R](/\|\\/g,'\\\\')[R](/a/g,'');t!=s&&setTimeout(d,99,t)}

IMHO, é muito divertido brincar. Experimente uma versão HTML aqui:

Provavelmente há muito mais espaço para jogar golfe. Sugestões são bem-vindas!


+1 na demonstração executável que desperdiçou 10 minutos do meu tempo e +1 na função randomizer. No entanto, como Dennis menciona, ele falha no primeiro caso de teste. Tente /ou /|e você verá que o bloco não cai completamente como deveria.
dberm22

@ Dennis Obrigado por apontar esses problemas. Eu acredito que eu consertei os dois agora.
ETHproductions

Node não está feliz com a flecha gorda, mas funciona bem caso contrário. Você pode substituir \033por um byte ESC literal, economizando 3 bytes.
Dennis

2

Perl 5, 154 146

Teve que usar um caractere temporário para manter o estado entre 2 regexes.
Para lidar com o risco de algo como / | | | \ acabaria como / / / \ \ em vez de / / | \ \.

$_=substr(pop.' ',0,80);$|++;while($}ne$_){print"$_\r";$}=$_;s@ \\|/ @__@g;s@/\|(?=[^\\])@/F@g;s@([^/])\|\\@$1\\\\@g;tr@F@/@;select($\,$\,$\,0.1)}

Teste

$ perl dominos.pl '|\ |\/|||\/|'
|\__\//|\\/__

1
Você pode eliminar várias barras invertidas se usar um delimitador que não seja barra - por exemplo, em s, \\|/ ,__,gvez de s/ \\|\/ /__/g.
Hbbs

Boa dica. Esqueceu esse truque. E alguns bytes extras foram cortados usando conjuntos negados.
LukStorms

2

ES6 , 220 218 195 bytes

Minificado

f=d=>{var e,c=console;if(!d[79])d+=' ';c.clear();c.log(d);e=d;d=d[R='replace'](/\/\|\\/g,'a')[R](/\/ | \\/g,'__')[R](/\/\|/g,'//')[R](/\|\\/g,'\\\\')[R]('a','/|\\');if(e!=d)setTimeout(f,100,d);};

Mais legível

f=d=> {
    var e,
    c=console;
    if(!d[79])
        d+=' ';
    c.clear();
    c.log(d);
    e=d;
    d = d[R='replace'](/\/\|\\/g, 'a')  //Substitute '/|\' with 'a' so it doesn't get replaced
        [R](/\/ |  \\/g, '__')     //Replace '/ ' and ' \' with '__'
        [R](/\/\|/g, '//')    //Replace '/|' with '//'
        [R](/\|\\/g, '\\\\')  //Replace '|\' with '\\'
        [R]('a', '/|\\');     //Put '/|\' back
    if(e!=d)
        setTimeout(f,100,d);
};

2
Bem-vindo à programação de quebra-cabeças e código de golfe! 1. Não sei por que você está usando a notação ES6. () = > {e }()pode simplesmente ser removido do seu código. 2. Não acho que caixas de alerta sejam um formato de saída aceitável para uma animação. Você pode incorporar seu JS em HTML ou fazer as alterações necessárias para que funcionem na linha de comandos. 3. Em qualquer um dos casos, seu código deve aguardar aprox. 100 ms entre a impressão de um estado e o seguinte.
Dennis

2
Bem-vindo ao PPCG! Gostaria de sugerir verificar este post e este post para ajudar a melhorar o seu golfe.
jrich

Obrigado pelas sugestões e links para dicas de golfe. Ainda é um pouco longo, mas diminuí um pouco e adicionei o cronômetro. Vou examinar as dicas mais detalhadamente e ver o que posso encurtar.
User3000806

1
Isso deve funcionar em um terminal agora, mas ainda não imprimirá o estado atualizado sobre o antigo. No Linux, você pode corrigir isso chamando console.log("^[c"+d), onde ^[está o caractere ESC (um byte).
Dennis

1
Se você alterar o primeiro .replacepara [R='replace'], então cada um subsequente para [R], isso reduzirá bastante. Você também pode salvar alguns bytes usando setTimeout(f,100,d)no lugar da configuração atual.
ETHproductions

2

C #, 335 bytes

Não é uma ótima escolha de idioma.

Eu abusei do atraso permitido entre 50 e 1000 para selecionar um número de dois dígitos.

Novas linhas e recuo adicionados para maior clareza:

namespace System.Threading{
    class P{
        static void Main(string[]z){
            var c=@"/|\,/|\,/|,//,|\,\\,/ ,__, \,__".Split(',');
            for(string a=z[0].PadRight(80),b="";a!=b;){
                Console.Clear();
                Console.Write(b=a);
                Thread.Sleep(99);
                a="";
                for(int i,j;(i=a.Length)<80;)
                    a+=(j=Array.FindIndex(c,d=>b.Substring(i).StartsWith(d)))%2==0
                        ?c[j+1]
                        :b.Substring(i,1);
            }
        }
    }
}

1

PHP, 175 bytes

$i=sprintf("%-80s",$argv[1]);$p='preg_replace';do{echo($o=$i)."\r";$i=$p('(/\|\\\\(*SKIP)(?!)|(?|(/)\||\|(\\\\)))','$1$1',$p('(/ | \\\\)','__',$i));usleep(1e5);}while($i!=$o);

Não minificado:

$input = sprintf("%-80s",$argv[1]);
do {
  echo $input."\r";
  $old = $input;
  $input = preg_replace('(/ | \\\\)','__',$input);
  $input = preg_replace('(/\|\\\\(*SKIP)(?!)|(?|(/)\||\|(\\\\)))','$1$1',$input);
  usleep(100000);
}
while( $input != $old);

Basicamente regex golf. Primeiro aplaina qualquer dominó em queda que tenha espaço (e devido à ordem de correspondência da esquerda para a direita, o "vento" sopra). Então vem a parte feia (amaldiçoa você corta!)

  • Combine /|\e depois pule.
  • Combine (/)|e substitua por//
  • Combine |(\)e substitua por\\

Isso faz com que os dominós caiam. Por fim, aguarde 100 ms pela próxima etapa.

Usar ()como delimitadores na regex significa que os /s não precisam escapar, o que ajuda minimamente!


Você pode esperar 50ms em vez de 100, economizando 1 caractere;) O PHP permite 10 ^ 5?
BlueCacti

1

Shell POSIX + sed, 144

sed 's/^.\{1,79\}$/& /;s/.*/printf '"'&\\r'"';sleep .1/;h;:;s,/|\\,/:\\,g;s,\(/ \| \\\),__,g;s,/|,//,g;s,|\\,\\\\,g;H;t;x;y/:/|/;s/\\/\\\\/g'|sh

Isso está em duas partes. O principal trabalho de derrubar os dominós é a sedsubstituição padrão de padrões , acumulando linhas no espaço de espera. Nós temporariamente nos transformamos /|\em /:\protegê-lo, recuperando no final.

s/^.\{0,79\}$/& /
h

:
s,/|\\,/:\\,g
s,\(/ \| \\\),__,g
s,/|,//,g
s,|\\,\\\\,g
H
t

x
y/:/|/

Como sednão há como inserir atrasos (consultei terminfo / termcap, mas não consegui encontrar nenhuma maneira padrão), envolvo cada linha emprintf "...\r"; sleep .1 para imprimir uma linha a cada 100 ms. Na verdade, eu faço isso primeiro, quando temos apenas uma linha, pois os caracteres no comando não serão tocados por nenhuma das substituições de tombamento.

Todos testados usando dashe GNU coreutils, com POSIXLY_CORRECTconjunto no ambiente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.