Regardez-les tomber comme des dominos


22

Vous vivez à l'intérieur d'un terminal de 80 caractères de large. Vous vous ennuyez, vous décidez donc de jouer aux dominos. Non, pas le genre ennuyeux qui ressemble au Scrabble, le genre amusant où vous passez une heure à les fixer pour les regarder tomber en une seconde.

Dans les terminaux, les dominos ressemblent à ceci:

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

Comme nous le savons tous, si un domino incliné touche un montant, le deuxième domino est également incliné. La seule exception à cela est que si deux dominos inclinés le touchent:

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

Ajustez la constante gravitationnelle de votre terminal pour que cette transition prenne 100 ms.

Si un domino incliné est soutenu par un autre domino ou les murs du terminal, son voyage se termine.

Aucun des dominos inclinés dans

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

(80 caractères) se déplacera, car les deux dominos inclinés les plus à l'extérieur sont supportés par les murs du terminal et tous les autres sont supportés par d'autres dominos.

Cependant, si l'espace dans le sens de l'inclinaison est vide, le domino tombe:

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

Terminal. Constante gravitationnelle. Tu obtiens le point…

Enfin, il y a un léger vent de gauche, donc les dominos inclinés à droite tombent plus vite que les dominos inclinés à gauche:

|/ \| --> |__\|

Tâche

Écrivez un programme / une fonction qui montre une animation de jouer aux dominos dans un terminal.

Votre code doit effectuer les opérations suivantes:

  1. Lire une chaîne d'entrée, représentant l'état initial des dominos.

    Cette chaîne ne contiendra pas plus de 80 caractères et se composera uniquement des dominos décrits ci-dessus et des espaces vides.

  2. Imprimez l'état et attendez 100 ms.

  3. Transformez l'état comme expliqué ci-dessus.

  4. Si l'état a changé, revenez à 2.

Règles supplémentaires

  • La longueur de la chaîne d'entrée n'affecte pas la largeur du terminal; même si la chaîne est inférieure à 80 caractères, les murs du terminal sont toujours séparés de 80 caractères.

  • Chaque fois que l'étape 2 est exécutée, l'état doit être imprimé au même emplacement, écrasant l'état précédent.

  • Étant donné que certaines langues sont incapables d'attendre exactement 100 ms, n'hésitez pas à attendre n'importe quelle quantité entre 50 et 1000 ms.

  • Les règles de standard s'appliquent.

Exemples

  • Pour l'état initial

     ||\/||
    

    imprimer ce qui suit (l'un sur l'autre):

     ||\/||
     |\\//|
     \\\///
    __\\//__
    
  • Pour l'état initial

    /||||\
    

    imprimer ce qui suit

    /||||\
    //||\\
    ///\\\
    
  • Pour l'état initial

    /|||\
    

    imprimer ce qui suit

    /|||\
    //|\\
    
  • Pour l'état initial

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

    imprimer ce qui suit:

    |__\|__\|__\|__\|
    
  • Pour l'état initial (80 caractères)

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

    imprimer ce qui suit

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

Réponses:


13

Rétine , 87 86 85 octets

Merci à Dennis d'avoir économisé 1 octet.

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

<ESC>doit être remplacé par le caractère de contrôle réel (0x1B). <empty>représente une ligne de fin vide. Vous pouvez ensuite exécuter le code ci-dessus à partir d'un seul fichier avec l' -sindicateur.

Le code nécessite un terminal qui prend en charge les codes d'échappement ANSI. Je ne peux pas supprimer le saut de ligne dans la sortie de Retina, j'ai donc besoin d'effacer la console entière à <ESC>cchaque fois. J'ai testé le code en bash en utilisant Mono pour exécuter Retina.

Explication

^.{0,79}$
$0 

Nous commençons par ajouter un espace si l'entrée contient moins de 80 caractères. Il en est ainsi pour qu'un /à l'extrémité droite ne doive pas être traité séparément.

:`^
<ESC>c

Maintenant, nous ajoutons <ESC>cla chaîne, qui est le code d'échappement ANSI pour effacer le terminal. Ainsi, chaque fois que la chaîne est imprimée, elle le fera en haut du terminal. Le :`demande à Retina d'imprimer le résultat de cette substitution, c'est-à-dire la configuration initiale.

(`/ | \\
__

(`commence une boucle. Puisqu'il n'y a pas de correspondance ), la boucle est supposée aller jusqu'à la dernière étape du programme. Chaque itération simulera une étape de la chute des dominos puis "dormira" un peu. Cette première étape remplace /et à \côté d'un espace en __. Cela gère automatiquement le / \cas correctement, car les correspondances ne peuvent pas se chevaucher et sont recherchées de gauche à droite. Donc, le /<sp>serait apparié et transformé en __tel que le \ne peut pas être apparié, et nous obtenons le bon __\.

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

Cela devient /|à //condition qu'il n'y en ait pas à \côté. Nous ajoutons un atel que ce nouveau /ne dérange pas avec la prochaine étape (qui ne devrait pas "connaître" encore ce changement).

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

La situation inverse: se transformer |\en \\pourvu qu'il n'y en ait pas à /côté. Nous n'avons pas besoin de mettre un aici, car nous avons terminé cette étape de la simulation.

Maintenant la partie endormie ...

$
aaaaa

Ajoute 5 as supplémentaires à la fin du code.

a
aaaa

Transforme chacun aen 4 as, nous obtenons donc 20 as à la fin.

(a+)+b|a
<empty>

Maintenant, la partie amusante ... nous dormons un peu avec l'aide d' un retour en arrière catastrophique . Il existe une quantité exponentielle de façons de diviser une correspondance (a+)+entre les répétitions du groupe. Parce que la bcause de l'échec de la correspondance, le moteur va revenir en arrière et essayer chacune de ces combinaisons avant de décider que (a+)+bcela ne peut pas correspondre. Pour les vingt aà la fin, cela prend environ une demi-seconde.

En même temps, nous permettons à l'expression régulière de correspondre à un seul a , mais uniquement après avoir effectué le retour arrière. Lorsque cela correspond, nous le remplaçons par une chaîne vide, supprimant tous les as que nous avons insérés pour une raison ou une autre dans la chaîne.

Cela laisse l'impression de la chaîne à la fin de l'itération de la boucle. Ici, il est pratique que je n'ai pas encore corrigé le comportement d'impression des boucles dans Retina. Actuellement, il n'y a qu'un seul indicateur pour chaque étape, qui dit "imprimer" ou "ne pas imprimer". La valeur par défaut est «ne pas imprimer», sauf pour la dernière étape du programme, qui par défaut est «imprimer». Mais la scène est bouclée, ce qui signifie qu'elle affiche en fait la chaîne actuelle à chaque itération. Normalement, c'est vraiment ennuyeux, et vous devez presque toujours inclure une étape vide supplémentaire à la fin si vous ne voulez que le résultat final, mais ici, cela me permet d'économiser quatre octets.


6

Javascript (ES6), 206 148 129 158 octets

Je l'avais finalement descendu à un point bien bas, mais cela n'effacerait pas la console ou ajouterait un espace supplémentaire; ces problèmes ont été résolus maintenant.

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

Version alternative de 153 octets qui devrait fonctionner dans 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)}

À mon humble avis, c'est assez amusant de jouer avec. Essayez une version HTML ici:

Il y a probablement beaucoup plus de place pour le golf. Bienvenue suggestions!


+1 pour la démo exécutable qui m'a fait perdre 10 minutes de mon temps, et +1 pour la fonction de randomisation. Cependant, comme Dennis le mentionne, il échoue pour le premier cas de test. Essayez /ou /|et vous verrez que la tuile ne tombe pas complètement comme il se doit.
dberm22

@Dennis Merci d'avoir signalé ces problèmes. Je crois que je les ai réparés tous les deux maintenant.
ETHproductions

Node n'est pas satisfait de la grosse flèche, mais cela fonctionne bien sinon. Vous pouvez le remplacer \033par un octet ESC littéral, économisant 3 octets.
Dennis

2

Perl 5, 154 146

J'ai dû utiliser un caractère temporaire pour maintenir l'état entre 2 regex.
Pour faire face au risque que quelque chose comme / | | | \ finirait par / / / \ \ au lieu de / / | \ \.

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

Tester

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

1
Vous pouvez éliminer plusieurs barres obliques inverses si vous utilisez un délimiteur autre que la barre oblique - par exemple s, \\|/ ,__,gau lieu de s/ \\|\/ /__/g.
hobbs

Bon conseil. J'ai oublié cette astuce. Et quelques octets supplémentaires ont été coupés en utilisant des ensembles niés.
LukStorms du

2

ES6 , 220 218 195 195 octets

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

Plus lisible

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
Bienvenue sur Programmation Puzzles & Code Golf! 1. Je ne sais pas pourquoi vous utilisez la notation ES6. () = > {et }()peut simplement être supprimé de votre code. 2. Je ne pense pas que les boîtes d'alerte soient un format de sortie acceptable pour une animation. Vous pouvez soit intégrer votre JS en HTML, soit apporter la modification requise pour que cela fonctionne à partir de la ligne de commande. 3. Dans les deux cas, votre code doit attendre env. 100 ms entre l'impression d'un état et le suivant.
Dennis

2
Bienvenue chez PPCG! Je suggère de consulter cet article et cet article pour améliorer votre golf.
jrich

Merci pour les suggestions et les liens vers des astuces de golf. Il est encore un peu long, mais je l'ai raccourci un peu et ajouté la minuterie. Je vais parcourir les conseils plus en profondeur et voir ce que je peux raccourcir.
user3000806

1
Cela devrait fonctionner dans un terminal maintenant, mais il n'imprimera toujours pas l'état mis à jour sur l'ancien. Sous Linux, vous pouvez résoudre ce problème en appelant à la console.log("^[c"+d)place, où se ^[trouve le caractère ESC (un octet).
Dennis

1
Si vous changez le premier .replaceen [R='replace'], puis chaque suivant en [R], cela réduira un peu. Vous pouvez également enregistrer quelques octets en utilisant setTimeout(f,100,d)à la place de la configuration actuelle.
ETHproductions

2

C #, 335 octets

Pas un grand choix de langue.

J'ai abusé du délai autorisé entre 50 et 1000 pour sélectionner un nombre à deux chiffres.

Nouvelles lignes et indentation ajoutées pour plus de clarté:

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 octets

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

Non minifié:

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

Golf regex fondamentalement. Aplatit d'abord tous les dominos tombants qui ont de l'espace (et en raison de l'ordre de correspondance de gauche à droite, le «vent» souffle). Vient ensuite la partie laide (maudissez-vous des barres obliques!)

  • Match /|\, puis sautez-le.
  • Associez (/)|et remplacez par//
  • Associez |(\)et remplacez par\\

Cela fait tomber les dominos. Enfin, attendez 100 ms pour la prochaine étape.

L'utilisation ()comme délimiteurs sur l'expression régulière signifie que les /s n'ont pas besoin de s'échapper, ce qui aide très peu!


Vous êtes autorisé à attendre 50 ms au lieu de 100, économisant 1 caractère;) PHP autorise-t-il 10 ^ 5?
BlueCacti

1

Coque 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

C'est en deux parties. Le travail principal de renversement des dominos est le sedremplacement de modèle standard , accumulant des lignes dans l'espace de cale. Nous nous transformons temporairement /|\en/:\ pour le protéger, récupérant à la fin.

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

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

x
y/:/|/

Puisque sed il n'y a aucun moyen d'insérer des retards (j'ai cherché dans terminfo / termcap, mais je n'ai pas trouvé de moyen standard), j'encapsule chaque ligne printf "...\r"; sleep .1 pour imprimer une ligne toutes les 100 ms. En fait, je le fais d'abord, lorsque nous n'avons qu'une seule ligne, car les caractères de la commande ne seront touchés par aucune des substitutions de basculement.

Tous testés en utilisant dashet GNU coreutils, avec POSIXLY_CORRECTensemble dans l'environnement.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.