Zobacz, jak spadają jak domino

22

Mieszkasz w terminalu o szerokości 80 znaków. Jesteś znudzony, więc decydujesz się na grę w domino. Nie, nie nudny rodzaj, który wygląda jak Scrabble, zabawny rodzaj, w którym spędzasz godzinę, ustawiając je, aby patrzyły, jak upadają w sekundę.

Domina w terminalach wyglądają tak:

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

Jak wszyscy wiemy, jeśli przechylone domino dotknie pionowego, drugie domino również zostanie przechylone. Jedynym wyjątkiem jest sytuacja, gdy dotkną go dwa przechylone domino:

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

Dostosuj stałą grawitacyjną terminala, aby przejście to trwało 100 ms.

Jeśli przechylone domino jest obsługiwane przez inne domino lub ściany terminala, jego podróż się kończy.

Żadne z przechylonych domino w

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

Przesuną się (80 znaków), ponieważ dwa skrajnie nachylone domino są obsługiwane przez ściany terminala, a wszystkie inne są obsługiwane przez inne domino.

Jeśli jednak przestrzeń w kierunku przechylania jest pusta, domino spada:

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

Terminal. Stała grawitacyjna. Dostajesz punkt…

Wreszcie, po lewej stronie jest lekki wiatr, więc domina przechylone w prawo spadają szybciej niż domina pochylone:

|/ \| --> |__\|

Zadanie

Napisz program / funkcję, która pokazuje animację gry w domino w terminalu.

Twój kod powinien wykonać następujące czynności:

  1. Odczytaj ciąg wejściowy reprezentujący początkowy stan domina.

    Ciąg ten będzie zawierał nie więcej niż 80 znaków i będzie składał się wyłącznie z domino opisanych powyżej i pustych spacji.

  2. Wydrukuj stan i poczekaj 100 ms.

  3. Przekształć stan, jak wyjaśniono powyżej.

  4. Jeśli stan się zmienił, wróć do 2.

Dodatkowe zasady

  • Długość ciągu wejściowego nie wpływa na szerokość terminala; nawet jeśli ciąg znaków jest krótszy niż 80 znaków, ściany terminala wciąż są od siebie oddalone o 80 znaków.

  • Za każdym razem, gdy wykonywany jest krok 2, stan powinien zostać wydrukowany w tej samej lokalizacji, zastępując poprzedni stan.

  • Ponieważ niektóre języki nie są w stanie czekać dokładnie 100 ms, możesz poczekać dowolną ilość między 50 a 1000 ms.

  • Obowiązują standardowe zasady .

Przykłady

  • Dla stanu początkowego

     ||\/||
    

    wydrukuj następujące (jeden na drugim):

     ||\/||
     |\\//|
     \\\///
    __\\//__
    
  • Dla stanu początkowego

    /||||\
    

    wydrukuj następujące

    /||||\
    //||\\
    ///\\\
    
  • Dla stanu początkowego

    /|||\
    

    wydrukuj następujące

    /|||\
    //|\\
    
  • Dla stanu początkowego

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

    wydrukuj następujące:

    |__\|__\|__\|__\|
    
  • Dla stanu początkowego (80 znaków)

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

    wydrukuj następujące

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    
Dennis
źródło

Odpowiedzi:

13

Retina , 87 86 85 bajtów

Dzięki Dennisowi za zaoszczędzenie 1 bajtu.

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

<ESC>należy zastąpić rzeczywistym znakiem kontrolnym (0x1B). <empty>reprezentuje pustą linię końcową. Następnie możesz uruchomić powyższy kod z jednego pliku z -sflagą.

Kod wymaga terminala obsługującego kody specjalne ANSI. Nie mogę ukryć przesunięcia linii w danych wyjściowych Retiny, więc muszę za <ESC>ckażdym razem wyczyścić całą konsolę . Przetestowałem kod bash przy użyciu Mono do uruchomienia Retina.

Wyjaśnienie

^.{0,79}$
$0 

Zaczynamy od dodania spacji, jeśli dane wejściowe zawierają mniej niż 80 znaków. Jest tak, że /po prawej stronie nie trzeba traktować osobno.

:`^
<ESC>c

Teraz przechodzimy <ESC>cdo łańcucha, który jest kodem ucieczkowym ANSI do czyszczenia terminala. Za każdym razem, gdy łańcuch zostanie wydrukowany, zrobi to na górze terminala. :`Instruuje Retina wydrukować wynik tego zastąpienia, tj początkowej konfiguracji.

(`/ | \\
__

(`zaczyna pętlę. Ponieważ nie ma dopasowania ), zakłada się, że pętla przechodzi do ostatniego etapu programu. Każda iteracja będzie symulować jeden krok spadania domina, a następnie trochę „spać”. Ten pierwszy etap zastępuje /i \obok spacji do __. To automatycznie obsługuje / \przypadek poprawnie, ponieważ dopasowania nie mogą się pokrywać i są wyszukiwane od lewej do prawej. Więc /<sp>zostaną dopasowane i zamienione w __takie, że \nie da się dopasować, a my otrzymamy poprawne __\.

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

Zmienia się to /|pod //warunkiem, że nie ma go \obok. Dołączamy ataki, aby ten nowy /nie bałaganił następnego etapu (który nie powinien jeszcze „wiedzieć” o tej zmianie).

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

Odwrotna sytuacja: zamień się |\pod \\warunkiem, że nie ma /obok niej. Nie musimy atu umieszczać, ponieważ skończyliśmy z tym krokiem symulacji.

Teraz część do spania ...

$
aaaaa

Dołącza 5 kolejnych as na końcu kodu.

a
aaaa

Zamienia każde ana 4 as, więc ana końcu dostajemy 20 s.

(a+)+b|a
<empty>

Teraz część zabawy ... śpimy trochę przy pomocy katastrofalnego cofania się . Istnieje wykładnicza ilość sposobów na podzielenie dopasowania (a+)+między powtórzeniami grupy. Ponieważ bprzyczyną niepowodzenia jest dopasowanie, silnik cofnie się i wypróbuje każdą z tych kombinacji, zanim zdecyduje, że (a+)+bnie może się równać. Na koniec dwudziestu asekund zajmuje to około pół sekundy.

Jednocześnie zezwalamy, aby wyrażenie regularne pasowało do jednego a, ale tylko po wykonaniu cofania. Kiedy to pasuje, zastępujemy go pustym ciągiem, usuwając wszystkie as, które wstawiliśmy z tego lub innego powodu z ciągu.

To pozostawia drukowanie łańcucha na końcu iteracji pętli. Przydaje się tutaj to, że nie naprawiłem jeszcze sposobu drukowania pętli w Retinie. Obecnie na każdym etapie jest tylko jedna flaga z napisem „drukuj” lub „nie drukuj”. Domyślnie jest to „nie drukuj”, z wyjątkiem ostatniego etapu programu, który domyślnie to „drukuj”. Ale scena jest zapętlona, ​​co oznacza, że ​​faktycznie drukuje bieżący ciąg przy każdej iteracji. Zwykle jest to naprawdę denerwujące i prawie zawsze musisz dołączyć dodatkowy pusty etap na końcu, jeśli chcesz tylko końcowy wynik, ale tutaj pozwala mi zaoszczędzić cztery bajty.

Martin Ender
źródło
6

JavaScript (ES6), 206 148 129 158 bajtów

W końcu udało mi się sprowadzić go do ładnie niskiego poziomu, ale nie wyczyści konsoli ani nie doda dodatkowej przestrzeni; problemy te zostały już naprawione.

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

Alternatywna wersja 153-bajtowa, która powinna działać w 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, fajnie się z nią bawić. Wypróbuj wersję HTML tutaj:

Prawdopodobnie jest o wiele więcej miejsca na grę w golfa. Sugestie mile widziane!

ETHprodukcje
źródło
+1 za uruchamialne demo, które zmarnowało 10 minut mojego czasu, i +1 za funkcję randomizatora. Jednak, jak wspomina Dennis, zawodzi w pierwszym przypadku testowym. Spróbuj /lub, /|a zobaczysz, że kafelek nie spada całkowicie tak, jak powinien.
dberm22,
@Dennis Dziękujemy za zwrócenie uwagi na te problemy. Myślę, że naprawiłem teraz oba z nich.
ETHprodukcje
Węzeł nie jest zadowolony z grubej strzały, ale w przeciwnym razie działa dobrze. Możesz zastąpić \033literalnym bajtem ESC, oszczędzając 3 bajty.
Dennis
2

Perl 5, 154 146

Musiał użyć postaci tymczasowej, aby utrzymać stan między 2 wyrażeniami regularnymi.
Aby poradzić sobie z ryzykiem, że coś takiego / | | | \ skończyłby jako / / / \ \ zamiast / / | \ \.

$_=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 '|\ |\/|||\/|'
|\__\//|\\/__
LukStorms
źródło
1
Możesz wyeliminować kilka ukośników odwrotnych, jeśli używasz separatora innego niż ukośnik - np. s, \\|/ ,__,gZamiast s/ \\|\/ /__/g.
hobbs
Dobra wskazówka. Zapomniałem o tej sztuczce. I kilka dodatkowych bajtów zostało wyciętych przy użyciu zanegowanych zbiorów.
LukStorms,
2

ES6 , 220 218 195 bajtów

Zminimalizowane

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

Bardziej czytelny

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);
};
użytkownik3000806
źródło
2
Witamy w Programowaniu Puzzle i Code Golf! 1. Nie jestem pewien, dlaczego używasz notacji ES6. () = > {i }()można po prostu usunąć z kodu. 2. Nie sądzę, aby pola alertów były akceptowalnym formatem wyjściowym animacji. Możesz albo osadzić JS w HTML, albo wprowadzić wymaganą zmianę, aby działała z wiersza poleceń. 3. W obu przypadkach kod musi czekać ok. 100 ms między drukowaniem jednego stanu a drugim.
Dennis
2
Witamy w PPCG! Proponuję sprawdzić ten post i ten post, aby poprawić grę w golfa.
jrich
Dziękujemy za sugestie i linki do wskazówek golfowych. Jest jeszcze trochę długi, ale nieco go skróciłem i dodałem minutnik. Omówię te wskazówki bardziej szczegółowo i zobaczę, co mogę skrócić.
user3000806,
1
To powinno działać teraz w terminalu, ale nadal nie wydrukuje zaktualizowanego stanu na starym. W systemie Linux można to naprawić, wywołując console.log("^[c"+d)zamiast tego, gdzie ^[jest znak ESC (jeden bajt).
Dennis
1
Jeśli zmienisz pierwszy .replacena [R='replace'], a następnie każdy następny na [R], spowoduje to znaczne zmniejszenie. Możesz również zapisać kilka bajtów, używając setTimeout(f,100,d)zamiast bieżącej konfiguracji.
ETHprodukcje
2

C #, 335 bajtów

Niezły wybór języka.

Nadużyłem dozwolonego opóźnienia między 50 a 1000, aby wybrać dwucyfrową liczbę.

Dodano nowe linie i wcięcia dla przejrzystości:

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
źródło
1

PHP, 175 bajtów

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

Nie zminimalizowane:

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

Zasadniczo regex golf. Najpierw spłaszcza spadające domino, które mają miejsce (a ze względu na kolejność dopasowywania od lewej do prawej, wieje „wiatr”). Potem pojawia się brzydka część (przeklnij cię!)

  • Dopasuj /|\, a następnie pomiń.
  • Dopasuj (/)|i zamień na//
  • Dopasuj |(\)i zamień na\\

To powoduje, że domina spadają. Na koniec poczekaj 100 ms na następny krok.

Użycie ()ograniczników w wyrażeniu regularnym oznacza, że /nie trzeba uciekać, co pomaga minimalnie!

Niet the Dark Absol
źródło
Możesz poczekać 50 ms zamiast 100, oszczędzając 1 znak;) Czy PHP pozwala na 10 ^ 5?
BlueCacti,
1

Powłoka 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

Jest to w dwóch częściach. Główną pracą polegającą na obaleniu domina jest standardowe sedzastępowanie wzorów, gromadzenie linii w przestrzeni ładunkowej. Tymczasowo się odwracamy/|\ się, /:\aby go chronić, odzyskując na końcu.

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

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

x
y/:/|/

Ponieważ sednie ma żadnego sposobu wstawiania opóźnień (szukałem terminfo / termcap, ale nie mogłem znaleźć żadnego standardowego sposobu), zawijam każdą linię, printf "...\r"; sleep .1 aby drukować linię co 100 ms. Robię to najpierw, gdy mamy tylko jedną linię, ponieważ znaki w poleceniu nie zostaną dotknięte żadną z podstawień toppingu.

Wszystkie testowane przy użyciu dashi GNU coreutils, z POSIXLY_CORRECTustawieniem w środowisku.

Toby Speight
źródło