Guardali cadere come domino


22

Vivi all'interno di un terminale di 80 caratteri di larghezza. Sei annoiato, quindi decidi di giocare a domino. No, non il tipo noioso che assomiglia a Scrabble, il tipo divertente in cui passi un'ora a guardarli cadere in un secondo.

Nei terminali, i domino si presentano così:

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

Come tutti sappiamo, se un domino inclinato tocca un montante, anche il secondo domino viene inclinato. L'unica eccezione a ciò è se due domino inclinati lo toccano:

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

Regola la costante gravitazionale del tuo terminale in modo che questa transizione richieda 100 ms.

Se un domino inclinato è supportato da un altro domino o dalle pareti del terminale, il suo viaggio termina.

Nessuno dei domino inclinati dentro

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

(80 caratteri) si sposteranno, poiché i due domino inclinati più estesi sono supportati dalle pareti del terminale e tutti gli altri sono supportati da altri domino.

Tuttavia, se lo spazio nella direzione di inclinazione è vuoto, il domino cade:

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

Terminale. Costante gravitazionale. Ottieni il punto ...

Infine, c'è un leggero vento da sinistra, quindi i domino inclinati a destra cadono più velocemente di quelli inclinati a sinistra:

|/ \| --> |__\|

Compito

Scrivi un programma / funzione che mostri un'animazione di giocare a domino in un terminale.

Il tuo codice dovrebbe effettuare le seguenti operazioni:

  1. Leggi una stringa dall'input, che rappresenta lo stato iniziale dei domino.

    Questa stringa conterrà non più di 80 caratteri e sarà composta esclusivamente dai domino sopra descritti e spazi vuoti.

  2. Stampa lo stato e attendi 100 ms.

  3. Trasforma lo stato come spiegato sopra.

  4. Se lo stato è cambiato, torna a 2.

Regole aggiuntive

  • La lunghezza della stringa di input non influisce sulla larghezza del terminale; anche se la stringa è più corta di 80 caratteri, le pareti del terminale sono comunque distanti 80 caratteri.

  • Ogni volta che viene eseguito il passaggio 2, lo stato deve essere stampato nella stessa posizione, sovrascrivendo lo stato precedente.

  • Poiché alcune lingue non sono in grado di attendere esattamente 100 ms, non esitare ad attendere qualsiasi importo compreso tra 50 e 1000 ms.

  • Si applicano le regole standard del .

Esempi

  • Per lo stato iniziale

     ||\/||
    

    stampa quanto segue (uno sopra l'altro):

     ||\/||
     |\\//|
     \\\///
    __\\//__
    
  • Per lo stato iniziale

    /||||\
    

    stampa quanto segue

    /||||\
    //||\\
    ///\\\
    
  • Per lo stato iniziale

    /|||\
    

    stampa quanto segue

    /|||\
    //|\\
    
  • Per lo stato iniziale

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

    stampa quanto segue:

    |__\|__\|__\|__\|
    
  • Per lo stato iniziale (80 caratteri)

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

    stampa quanto segue

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

Risposte:


13

Retina , 87 86 85 byte

Grazie a Dennis per aver salvato 1 byte.

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

<ESC>deve essere sostituito dal carattere di controllo effettivo (0x1B). <empty>rappresenta una riga finale vuota. È quindi possibile eseguire il codice sopra riportato da un singolo file con il -sflag.

Il codice richiede un terminale che supporti i codici di escape ANSI. Non riesco a sopprimere il feed di riga nell'output di Retina, quindi devo cancellare l'intera console <ESC>cogni volta. Ho testato il codice in bash usando Mono per eseguire Retina.

Spiegazione

^.{0,79}$
$0 

Iniziamo aggiungendo uno spazio se l'input contiene meno di 80 caratteri. Questo è così che un /alla fine giusta non deve essere trattato separatamente.

:`^
<ESC>c

Ora anteponiamo <ESC>calla stringa, che è il codice di escape ANSI per cancellare il terminale. Quindi ogni volta che la stringa viene stampata, lo farà nella parte superiore del terminale. Il :`incarica Retina stampare il risultato di questa sostituzione, cioè la configurazione iniziale.

(`/ | \\
__

(`inizia un ciclo. Poiché non esiste corrispondenza ), si presume che il ciclo vada fino all'ultima fase del programma. Ogni iterazione simulerà una fase della caduta del domino e poi "dormirà" un po '. Questo primo stadio sostituisce /e \accanto a uno spazio in __. Questo gestisce automaticamente il / \caso correttamente, poiché le corrispondenze non possono sovrapporsi e vengono cercate da sinistra a destra. Quindi /<sp>verrebbe abbinato e trasformato in modo __tale che \non possa essere abbinato, e otteniamo il corretto __\.

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

Questo si trasforma /|in //purché non ci sia \accanto. Aggiungiamo un atale che questo nuovo /non si scherza con la fase successiva (che non dovrebbe ancora "sapere" di questo cambiamento).

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

La situazione opposta: trasformati |\in \\purché non ci sia /accanto. Non abbiamo bisogno di metterne uno aqui, perché abbiamo finito con questo passaggio della simulazione.

Ora la parte addormentata ...

$
aaaaa

Aggiunge altri 5 asecondi alla fine del codice.

a
aaaa

Trasforma ciascuno ain 4 asecondi, quindi aalla fine otteniamo 20 secondi.

(a+)+b|a
<empty>

Ora la parte divertente ... dormiamo un po 'con l'aiuto di un catastrofico backtracking . Esiste una quantità esponenziale di modi per dividere una corrispondenza (a+)+tra le ripetizioni del gruppo. Poiché ciò bcausa il fallimento della partita, il motore tornerà indietro e proverà ciascuna di queste combinazioni prima di decidere che (a+)+bnon può corrispondere. Per i venti asecondi alla fine ci vuole qualcosa come mezzo secondo.

Allo stesso tempo, consentiamo al regex di abbinare un singolo a, ma solo dopo aver eseguito il backtracking. Quando questo corrisponde, lo sostituiamo con una stringa vuota, rimuovendo tutti i messaggi ache abbiamo inserito per un motivo o un altro dalla stringa.

Ciò lascia stampare la stringa alla fine dell'iterazione del ciclo. Qui è utile che non ho ancora risolto il comportamento di stampa dei loop in Retina. Attualmente, esiste una sola bandiera per ogni fase, che dice "stampa" o "non stampare". L'impostazione predefinita è "non stampare" ad eccezione dell'ultima fase del programma, che per impostazione predefinita è "stampa". Ma lo stage è in loop, quindi ciò significa che in realtà stampa la stringa corrente su ogni iterazione. Normalmente, questo è davvero fastidioso e quasi sempre è necessario includere un ulteriore stadio vuoto alla fine se si desidera solo il risultato finale, ma qui mi consente di salvare quattro byte.


6

Javascript (ES6), 206 148 129 158 byte

Alla fine l'avevo ridotto a un punto piacevolmente basso, ma non avrebbe cancellato la console o aggiunto uno spazio aggiuntivo; questi problemi sono stati risolti ora.

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

Versione alternativa a 153 byte che dovrebbe funzionare in 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, è abbastanza divertente giocarci. Prova una versione HTML qui:

Probabilmente c'è molto più spazio per giocare a golf. Suggerimenti benvenuti!


+1 per la demo eseguibile che ha perso 10 minuti del mio tempo e +1 per la funzione randomizer. Tuttavia, come menziona Dennis, fallisce per il primo caso di test. Prova /o /|e vedrai che la tessera non cade completamente come dovrebbe.
dberm22,

@Dennis Grazie per aver segnalato questi problemi. Credo di averli risolti entrambi ora.
ETHproductions,

Il nodo non è contento della freccia grassa, ma altrimenti funziona bene. È possibile sostituire \033con un byte ESC letterale, salvando 3 byte.
Dennis,

2

Perl 5, 154 146

Ho dovuto usare un carattere temporaneo per mantenere lo stato tra 2 regex.
Per far fronte al rischio che qualcosa di simile / | | | \ finirebbe come / / / \ \ invece di / / | \ \.

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

Test

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

1
È possibile eliminare diverse barre rovesciate se si utilizza un delimitatore diverso dalla barra, ad esempio s, \\|/ ,__,ganziché s/ \\|\/ /__/g.
Hobbs,

Bel consiglio. Dimenticato quel trucco. E alcuni byte extra sono stati tagliati usando set negati.
LukStorms,

2

ES6 , 220 218 195 byte

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

Più leggibile

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
Benvenuto in Programmazione di puzzle e codice golf! 1. Non sono sicuro del motivo per cui stai usando la notazione ES6. () = > {e }()può essere semplicemente rimosso dal tuo codice. 2. Non credo che le caselle di avviso siano un formato di output accettabile per un'animazione. Puoi incorporare il tuo JS in HTML o apportare le modifiche necessarie in modo che funzionino dalla riga di comando. 3. In entrambi i casi, il codice deve attendere ca. 100 ms tra la stampa di uno stato e quello successivo.
Dennis,

2
Benvenuti in PPCG! Suggerirei di controllare questo post e questo post per aiutarti a migliorare il tuo golf.
jrich

Grazie per i suggerimenti e i collegamenti ai suggerimenti sul golf. È ancora un po 'lungo, ma l'ho accorciato un po' e ho aggiunto il timer. Esaminerò i suggerimenti in modo più approfondito e vedrò cosa posso accorciare.
user3000806

1
Ora dovrebbe funzionare su un terminale, ma non stampa lo stato aggiornato su quello precedente. Su Linux, puoi risolverlo chiamando console.log("^[c"+d)invece, dove si ^[trova il carattere ESC (un byte).
Dennis,

1
Se cambi il primo .replacein [R='replace'], poi in quello successivo [R], questo si ridurrà un po '. È inoltre possibile salvare alcuni byte utilizzando setTimeout(f,100,d)al posto della configurazione corrente.
ETHproductions,

2

C #, 335 byte

Non è un'ottima scelta di lingua.

Ho abusato del ritardo consentito tra 50 e 1000 per selezionare un numero a due cifre.

Nuove linee e rientri aggiunti per maggiore chiarezza:

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 byte

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

Un-ridotte di:

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

Fondamentalmente regex golf. Prima appiattisce tutti i domino in caduta che hanno spazio (e a causa dell'ordine di corrispondenza da sinistra a destra, il "vento" soffia). Poi arriva la parte brutta (maledizione ti taglia!)

  • Incontro /|\ , quindi salta.
  • Abbina (/)|e sostituisci con//
  • Abbina |(\)e sostituisci con\\

Questo fa cadere i domino. Infine, attendi 100 ms per il passaggio successivo.

Usare ()come delimitatori su regex significa /che non è necessario scappare, il che aiuta minimamente!


Puoi aspettare 50ms invece di 100, risparmiando 1 carattere;) PHP consente 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

Questo è in due parti. Il lavoro principale di rovesciare i domino è la sedsostituzione del modello standard , accumulando linee nello spazio di stiva. Ci trasformiamo temporaneamente /|\in /:\per proteggerlo, recuperando alla fine.

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

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

x
y/:/|/

Dal momento che sednon ho modo di inserire ritardi (ho esaminato terminfo / termcap, ma non sono riuscito a trovare alcun modo standard), avvolgo ciascuna riga printf "...\r"; sleep .1 per stampare una riga ogni 100 ms. In realtà lo faccio prima, quando abbiamo solo una riga, poiché i caratteri nel comando non saranno toccati da nessuna delle sostituzioni di rovesciamento.

Tutti testati usando dashe GNU coreutils, con POSIXLY_CORRECTset nell'ambiente.

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.