Míralos caer como fichas de dominó

22

Vives dentro de una terminal que tiene 80 caracteres de ancho. Estás aburrido, así que decides jugar al dominó. No, no del tipo aburrido que se parece a Scrabble, el tipo divertido donde pasas una hora configurándolos para verlos caer en un segundo.

En las terminales, el dominó se ve así:

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

Como todos sabemos, si un dominó inclinado toca uno vertical, el segundo dominó también se inclina. La única excepción a esto es si dos fichas de dominó inclinadas lo tocan:

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

Ajuste la constante gravitacional de su terminal para que esta transición tome 100 ms.

Si un dominó inclinado es apoyado por otro dominó o las paredes de la terminal, su viaje termina.

Ninguna de las fichas de dominó inclinadas en

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

(80 caracteres) se moverán, ya que las dos fichas de dominó inclinadas más externas están soportadas por las paredes de la terminal y todas las demás están respaldadas por otras fichas de dominó.

Sin embargo, si el espacio en la dirección de inclinación está vacío, el dominó se cae:

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

Terminal. Constante gravitacional. Tú entiendes…

Finalmente, hay un ligero viento de la izquierda, por lo que las fichas de dominó inclinadas a la derecha caen más rápido que las inclinadas a la izquierda:

|/ \| --> |__\|

Tarea

Escriba un programa / función que muestre una animación de jugar dominó en una terminal.

Su código debe hacer lo siguiente:

  1. Lea una cadena de entrada, que representa el estado inicial de las fichas de dominó.

    Esta cadena no contendrá más de 80 caracteres y consistirá únicamente en el dominó descrito anteriormente y los espacios vacíos.

  2. Imprima el estado y espere 100 ms.

  3. Transforme el estado como se explicó anteriormente.

  4. Si el estado cambió, regrese a 2.

Reglas adicionales

  • La longitud de la cadena de entrada no afecta el ancho del terminal; incluso si la cadena tiene menos de 80 caracteres, las paredes de la terminal aún están separadas por 80 caracteres.

  • Cada vez que se ejecuta el paso 2, el estado debe imprimirse en la misma ubicación, sobrescribiendo el estado anterior.

  • Dado que algunos idiomas no pueden esperar exactamente 100 ms, no dude en esperar cualquier cantidad entre 50 y 1000 ms.

  • Aplican reglas estándar de .

Ejemplos

  • Para el estado inicial

     ||\/||
    

    imprima lo siguiente (uno sobre el otro):

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

    /||||\
    

    imprime lo siguiente

    /||||\
    //||\\
    ///\\\
    
  • Para el estado inicial

    /|||\
    

    imprime lo siguiente

    /|||\
    //|\\
    
  • Para el estado inicial

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

    imprima lo siguiente:

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

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

    imprime lo siguiente

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    
Dennis
fuente

Respuestas:

13

Retina , 87 86 85 bytes

Gracias a Dennis por guardar 1 byte.

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

<ESC>debe reemplazarse por el carácter de control real (0x1B). <empty>representa una línea final vacía. Luego puede ejecutar el código anterior desde un solo archivo con la -sbandera.

El código requiere un terminal que admita códigos de escape ANSI. No puedo suprimir el avance de línea en la salida de Retina, así que necesito borrar toda la consola <ESC>ccada vez. He probado el código en bash usando Mono para ejecutar Retina.

Explicación

^.{0,79}$
$0 

Comenzamos agregando un espacio si la entrada contiene menos de 80 caracteres. Esto es para que a /en el extremo derecho no tenga que ser tratado por separado.

:`^
<ESC>c

Ahora anteponemos <ESC>cla cadena, que es el código de escape ANSI para borrar el terminal. Por lo tanto, cada vez que se imprime la cadena, lo hará en la parte superior del terminal. El le :`indica a Retina que imprima el resultado de esta sustitución, es decir, la configuración inicial.

(`/ | \\
__

(`comienza un bucle Como no hay coincidencia ), se supone que el ciclo continúa hasta la última etapa del programa. Cada iteración simulará un paso de la caída del dominó y luego "dormirá" un poco. Esta primera etapa reemplaza /y al \lado de un espacio en __. Esto maneja automáticamente el / \caso correctamente, porque las coincidencias no pueden solaparse y se buscan de izquierda a derecha. Entonces /<sp>, se emparejaría y se convertiría en __tal que \no se pueda emparejar, y obtenemos el correcto __\.

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

Esto se convierte /|en //siempre que no haya al \lado. Agregamos un atal que este nuevo /no se mete con la siguiente etapa (que aún no debería "saber" sobre este cambio).

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

La situación opuesta: convertirse |\en \\siempre que no haya al /lado. No necesitamos poner un aaquí, porque hemos terminado con este paso de la simulación.

Ahora la parte para dormir ...

$
aaaaa

Agrega 5 as más al final del código.

a
aaaa

Convierte cada uno aen 4 as, por lo que obtenemos 20 as al final.

(a+)+b|a
<empty>

Ahora la parte divertida ... dormimos un poco con la ayuda de un retroceso catastrófico . Hay una cantidad exponencial de formas de dividir una coincidencia (a+)+entre repeticiones del grupo. Debido a que bhace que la coincidencia falle, el motor retrocederá e intentará cada una de esas combinaciones antes de decidir que (a+)+bno puede coincidir. Por los veinte as al final que toma algo así como medio segundo.

Al mismo tiempo, permitimos que la expresión regular coincida con una sola a, pero solo después de hacer el retroceso. Cuando eso coincide, lo reemplazamos con una cadena vacía, eliminando todos los acorreos electrónicos que insertamos por una razón u otra de la cadena.

Eso deja la impresión de la cadena al final de la iteración del bucle. Aquí resulta útil que todavía no haya solucionado el comportamiento de impresión de los bucles en Retina. Actualmente, solo hay una bandera para cada etapa, que dice "imprimir" o "no imprimir". El valor predeterminado es "no imprimir", excepto la última etapa del programa, que por defecto es "imprimir". Pero el escenario está en bucle, lo que significa que en realidad imprime la cadena actual en cada iteración. Normalmente, eso es realmente molesto, y casi siempre necesita incluir una etapa vacía adicional al final si solo desea el resultado final, pero aquí me permite guardar cuatro bytes.

Martin Ender
fuente
6

Javascript (ES6), 206 148 129 158 bytes

Finalmente lo puse en un punto bastante bajo, pero no despejaría la consola ni agregaría un espacio adicional; Estos problemas se han solucionado ahora.

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

Versión alternativa de 153 bytes que debería funcionar en 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)}

En mi humilde opinión, es bastante divertido jugar con él. Pruebe una versión HTML aquí:

Probablemente haya un poco más de espacio para jugar al golf. Sugerencias bienvenidas!

ETHproducciones
fuente
+1 para la demostración ejecutable que desperdició 10 minutos de mi tiempo, y +1 para la función de aleatorización. Sin embargo, como menciona Dennis, falla en el primer caso de prueba. Intente /o /|y verá que el mosaico no cae del todo como debería.
dberm22
@ Dennis Gracias por señalar estos problemas. Creo que los he arreglado a ambos ahora.
ETHproductions
Nodo no está contento con la flecha gorda, pero de lo contrario funciona bien. Puede reemplazar \033con un byte ESC literal, ahorrando 3 bytes.
Dennis
2

Perl 5, 154 146

Tuve que usar un carácter temporal para mantener el estado entre 2 expresiones regulares.
Para lidiar con el riesgo de que algo como / | El | El | \ terminaría como / / / \ \ en lugar de / / | \ \.

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

Prueba

$ perl dominos.pl '|\ |\/|||\/|'
|\__\//|\\/__
LukStorms
fuente
1
Puede eliminar varias barras diagonales inversas si utiliza un delimitador distinto de la barra diagonal, por ejemplo, en s, \\|/ ,__,glugar de s/ \\|\/ /__/g.
hobbs
Buen consejo. Olvidé ese truco. Y se cortaron algunos bytes adicionales mediante el uso de conjuntos negados.
LukStorms
2

ES6 , 220 218 195 bytes

Minified

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

Más legible

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);
};
user3000806
fuente
2
¡Bienvenido a Programming Puzzles & Code Golf! 1. No estoy seguro de por qué estás usando la notación ES6. () = > {y }()simplemente se puede eliminar de su código. 2. No creo que los cuadros de alerta sean un formato de salida aceptable para una animación. Puede incrustar su JS en HTML o hacer el cambio requerido para que funcione desde la línea de comandos. 3. En cualquier caso, su código debe esperar aprox. 100 ms entre la impresión de un estado y el siguiente.
Dennis
2
Bienvenido a PPCG! Sugeriría revisar esta publicación y esta publicación para ayudar a mejorar su golf.
jrich
Gracias por las sugerencias y enlaces a consejos de golf. Todavía es un poco largo, pero lo acorté un poco y agregué el temporizador. Revisaré los consejos con mayor profundidad y veré qué puedo acortar.
user3000806
1
Eso debería funcionar en un terminal ahora, pero aún no imprimirá el estado actualizado sobre el anterior. En Linux, puede solucionar esto llamando en su console.log("^[c"+d)lugar, donde ^[está el carácter ESC (un byte).
Dennis
1
Si cambia el primero .replacea [R='replace'], luego cada uno a [R], esto se reducirá bastante. También puede guardar algunos bytes si lo utiliza setTimeout(f,100,d)en lugar de la configuración actual.
ETHproductions
2

C #, 335 bytes

No es una gran elección de idioma.

Abusé de la demora permitida entre 50 y 1000 para seleccionar un número de dos dígitos.

Nuevas líneas y sangría agregadas para mayor claridad:

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);
            }
        }
    }
}
Hand-E-Food
fuente
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);

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

Básicamente regex golf. Primero aplana cualquier dominó que caiga que tenga espacio (y debido al orden de combinación de izquierda a derecha, el "viento" sopla). Luego viene la parte fea (¡maldición, cortes!)

  • Emparejar /|\, luego saltearlo.
  • Emparejar (/)|y reemplazar con//
  • Emparejar |(\)y reemplazar con\\

Esto hace caer las fichas de dominó. Finalmente, solo espere 100 ms para el siguiente paso.

Usar ()como delimitadores en la expresión regular significa /que no es necesario escapar, ¡lo que ayuda mínimamente!

Niet the Dark Absol
fuente
Se le permite esperar 50 ms en lugar de 100, ahorrando 1 carácter;) ¿PHP permite 10 ^ 5?
BlueCacti
1

POSIX shell + 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

Esto es en dos partes. El trabajo principal de derribar las fichas de dominó es el sedreemplazo estándar de patrones, acumulando líneas en el espacio de espera. Nos dirigimos temporalmente /|\en /:\protegerlo, recuperando al final.

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

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

x
y/:/|/

Como sedno tengo ninguna forma de insertar retrasos (busqué terminfo / termcap, pero no pude encontrar ninguna forma estándar), envuelvo cada línea printf "...\r"; sleep .1 para imprimir una línea cada 100 ms. Realmente hago esto primero, cuando tenemos solo una línea, ya que los caracteres en el comando no serán tocados por ninguna de las sustituciones de derribo.

Todo probado usando dashy GNU coreutils, con POSIXLY_CORRECTset en el entorno.

Toby Speight
fuente