Przedstawiam wam, pierwsze 3% autopretera Hexagony ...
|./...\..._..>}{<$}=<;>'<..../;<_'\{*46\..8._~;/;{{;<..|M..'{.>{{=.<.).|.."~....._.>(=</.\=\'$/}{<}.\../>../..._>../_....@/{$|....>...</..~\.>,<$/'";{}({/>-'(<\=&\><${~-"~<$)<....'.>=&'*){=&')&}\'\'2"'23}}_}&<_3.>.'*)'-<>{=/{\*={(&)'){\$<....={\>}}}\&32'-<=._.)}=)+'_+'&<
Wypróbuj online! Możesz także uruchomić go na sobie, ale zajmie to około 5-10 sekund.
Zasadniczo może się to zmieścić w boku o długości 9 (dla wyniku 217 lub mniej), ponieważ używa tylko 201 poleceń, a wersja bez golfa, którą napisałem jako pierwsza (na boku o długości 30), potrzebowała tylko 178 poleceń. Jestem jednak pewien, że wszystko zajmie wieczność, więc nie jestem pewien, czy spróbuję.
Powinno być także możliwe zagranie w golfa nieco w rozmiarze 10, unikając użycia ostatniego jednego lub dwóch rzędów, tak aby można było pominąć końcowe brakujące operacje, ale wymagałoby to znacznego przepisania, jako jednej z pierwszych ścieżek złączenia wykorzystuje lewy dolny róg.
Wyjaśnienie
Zacznijmy od rozwinięcia kodu i opatrzenia adnotacjami ścieżek kontroli:
To wciąż dość niechlujny, więc oto ten sam schemat dla kodu „nie golfowego”, który napisałem jako pierwszy (w rzeczywistości jest to długość boku 20 i pierwotnie napisałem kod na boku długości 30, ale był tak rzadki, że nie w ogóle nie poprawiają czytelności, więc trochę go skompaktowałem, aby rozmiar był bardziej rozsądny):
Kliknij, aby zobaczyć większą wersję.
Kolory są dokładnie takie same, z wyjątkiem kilku bardzo drobnych szczegółów, polecenia niekontrolowanego przepływu również są dokładnie takie same. Wyjaśnię więc, jak to działa w oparciu o wersję bez golfa, a jeśli naprawdę chcesz wiedzieć, jak działa gra w golfa, możesz sprawdzić, które części odpowiadają tym w większym sześciokącie. (Jedynym haczykiem jest to, że kod do gry w golfa zaczyna się od lustra, tak że rzeczywisty kod zaczyna się w prawym rogu, po lewej stronie.)
Podstawowy algorytm jest prawie identyczny z moją odpowiedzią CJam . Istnieją dwie różnice:
- Zamiast rozwiązać wyśrodkowane równanie liczb heksagonalnych, po prostu obliczam kolejne wyśrodkowane liczby heksagonalne, aż jedna będzie równa lub większa niż długość danych wejściowych. Wynika to z faktu, że Hexagony nie ma prostego sposobu na obliczenie pierwiastka kwadratowego.
- Zamiast od razu uzupełniać dane wejściowe brakiem operacji, sprawdzam później, czy wyczerpałem już polecenia wejściowe i
.
zamiast tego wypisuję polecenie .
Oznacza to, że podstawowa idea sprowadza się do:
- Odczytaj i zapisz ciąg wejściowy podczas obliczania jego długości.
- Znajdź najmniejszą długość boku
N
(i odpowiadającą jej wyśrodkowaną liczbę sześciokątną hex(N)
), która może pomieścić całe dane wejściowe.
- Oblicz średnicę
2N-1
.
- Dla każdej linii oblicz wcięcie i liczbę komórek (które sumują się
2N-1
). Wydrukuj wcięcie, wydrukuj komórki (używając, .
jeśli dane wejściowe są już wyczerpane), wydrukuj linię.
Zauważ, że nie ma żadnych operacji, więc rzeczywisty kod zaczyna się w lewym rogu (ten $
, który przeskakuje nad >
, więc naprawdę zaczynamy od ,
ciemnoszarej ścieżki).
Oto początkowa siatka pamięci:
Wskaźnik pamięci zaczyna się od wejścia oznaczonego krawędziami , wskazując na północ. ,
czyta bajt ze STDIN lub a, -1
jeśli trafiliśmy EOF w tę krawędź. Dlatego <
zaraz po tym jest warunek, czy przeczytaliśmy wszystkie dane wejściowe. Pozostańmy na razie w pętli wejściowej. Następny kod, który wykonujemy, to
{&32'-
Spowoduje to zapisanie 32 w przestrzeni oznaczonej krawędzią , a następnie odejmuje ją od wartości wejściowej w różnicy oznaczonej krawędzią . Zauważ, że to nigdy nie może być ujemne, ponieważ gwarantujemy, że dane wejściowe zawierają tylko drukowalne ASCII. Wyniesie zero, gdy wejście będzie spacją. (Jak wskazuje Timwi, nadal działałoby to, gdyby dane wejściowe mogły zawierać linie lub tabulatory, ale również usuwałyby wszystkie inne niedrukowalne znaki o kodach znaków mniejszych niż 32.) W takim przypadku <
odchyla wskaźnik instrukcji (IP) w lewo i wybrano jasnoszarą ścieżkę. Ta ścieżka po prostu resetuje pozycję MP za pomocą, {=
a następnie odczytuje następny znak - w ten sposób spacje są pomijane. W przeciwnym razie, jeśli postać nie jest spacją, wykonujemy
=}}})&'+'+)=}
To najpierw porusza się wokół sześciokąta przez krawędź długości, aż znajdzie się naprzeciwko krawędzi różnicy , przy pomocy =}}}
. Następnie kopiuje wartość z naprzeciwko długości krawędzi do długości krawędzi i inkrementuje się )&'+'+)
. Zobaczymy za chwilę, dlaczego ma to sens. Wreszcie przenosimy nową przewagę dzięki =}
:
(Konkretne wartości krawędzi pochodzą z ostatniego przypadku testowego podanego w wyzwaniu). W tym momencie pętla się powtarza, ale wszystko przesuwa się o jeden sześciokąt na północny wschód. Po przeczytaniu innej postaci otrzymujemy:
Teraz możesz zobaczyć, że stopniowo zapisujemy dane wejściowe (minus spacje) wzdłuż północno-wschodniej przekątnej, z znakami na każdej drugiej krawędzi, a długość do tego znaku jest przechowywana równolegle do długości oznaczonej krawędzi .
Kiedy skończymy z pętlą wejściową, pamięć będzie wyglądać następująco (gdzie już oznaczyłem kilka nowych krawędzi dla następnej części):
Jest %
to ostatni znak, który czytamy, 29
to liczba znaków spacji, które czytamy. Teraz chcemy znaleźć długość boku sześciokąta. Po pierwsze, na ciemnozielonej / szarej ścieżce znajduje się kod liniowej inicjalizacji:
=&''3{
Tutaj =&
skopiuj długość (29 w naszym przykładzie) na długość oznaczoną krawędzią . Następnie ''3
przechodzi do krawędzi oznaczonej jako 3 i ustawia jego wartość na 3
(której potrzebujemy po prostu jako stałej w obliczeniach). Na koniec {
przechodzi do krawędzi oznaczonej jako N (N-1) .
Teraz wchodzimy w niebieską pętlę. Przyrosty pętli N
(przechowywane w komórce oznaczonej jako N ) obliczają następnie wyśrodkowaną liczbę heksagonalną i odejmują ją od długości wejściowej. Kod liniowy, który to robi, to:
{)')&({=*'*)'-
Tutaj, {)
przesuwa się i zwiększa N . ')&(
przesuwa się do krawędzi oznaczonej jako N-1 , kopiuje N
tam i zmniejsza ją. {=*
oblicza swój produkt w N (N-1) . '*)
mnoży to przez stałą 3
i inkrementuje wynik na krawędzi oznaczonej hex (N) . Zgodnie z oczekiwaniami jest to n-ta środkowa liczba heksagonalna. Na koniec '-
oblicza różnicę między tym a długością wejściową. Jeśli wynik jest dodatni, długość boku nie jest jeszcze wystarczająco duża, a pętla powtarza się (tam, gdzie }}
przesuwa się MP z powrotem do krawędzi oznaczonej N (N-1) ).
Gdy długość boku będzie wystarczająco duża, różnica wyniesie zero lub będzie ujemna i otrzymamy to:
Po pierwsze, jest teraz naprawdę długa zielona liniowa ścieżka, która dokonuje niezbędnej inicjalizacji pętli wyjściowej:
{=&}}}32'"2'=&'*){=&')&}}
Do {=&
rozpoczyna kopiując wynik w diff krawędzi do długości krawędzi, bo później trzeba coś tam non-dodatnich. }}}32
zapisuje 32 w spacji oznaczonej krawędzią . '"2
zapisuje stałą 2 w nieoznakowanej krawędzi nad różnicą . '=&
kopiuje N-1
na drugą krawędź z tą samą etykietą. '*)
mnoży go przez 2 i zwiększa, aby uzyskać prawidłową wartość na krawędzi oznaczonej 2N-1 u góry. Jest to średnica sześciokąta. {=&')&
kopiuje średnicę na drugą krawędź oznaczoną 2N-1 . Na koniec }}
przesuwa się z powrotem do krawędzi oznaczonej 2N-1 u góry.
Ponownie oznakuj krawędzie:
Krawędź, na której się obecnie znajdujemy (która nadal ma średnicę sześciokąta), zostanie wykorzystana do iteracji po liniach wyjścia. Wcięcie oznaczone krawędzią obliczy, ile spacji jest potrzebnych w bieżącym wierszu. Komórki oznaczone krawędzią zostaną użyte do iteracji po liczbie komórek w bieżącym wierszu.
Jesteśmy teraz na różowej ścieżce, która oblicza wcięcie . ('-
zmniejsza iterator linii i odejmuje go od N-1 (do krawędzi wcięcia ). Krótka niebieska / szara gałąź w kodzie po prostu oblicza moduł wyniku ( ~
neguje wartość, jeśli jest ujemna lub zero, i nic się nie dzieje, jeśli jest dodatnia). Reszta różowej ścieżki "-~{
odejmuje wcięcie od średnicy do krawędzi komórki, a następnie przesuwa się z powrotem do krawędzi wcięcia .
Brudna żółta ścieżka drukuje teraz wcięcie. Zawartość pętli jest naprawdę sprawiedliwa
'";{}(
Gdzie '"
przesuwa się do krawędzi przestrzeni , ;
drukuje ją, {}
wraca do wcięcia i (
zmniejsza.
Kiedy skończymy, (druga) ciemnoszara ścieżka szuka następnego znaku do wydrukowania. W =}
przesuwa się w pozycji (co oznacza, na komórki krawędzi, wskazujące na południu). Następnie mamy bardzo ciasną pętlę, {}
która po prostu przesuwa się w dół o dwie krawędzie w kierunku południowo-zachodnim, dopóki nie trafimy na koniec przechowywanego ciągu:
Czy zauważyłeś, że ponownie oznakowałem jedną krawędź EOF? . Po przetworzeniu tego znaku zmienimy krawędź na ujemną, aby {}
pętla zakończyła się tutaj zamiast następnej iteracji:
W kodzie jesteśmy na końcu ciemnoszarej ścieżki, gdzie '
cofamy się o jeden krok na znak wejściowy. Jeśli sytuacja jest jednym z dwóch ostatnich schematów (tzn. Z danych wejściowych, których jeszcze nie wydrukowaliśmy, jest jeszcze znak), to podążamy zieloną ścieżką (dolną, dla osób, które nie są dobre w zieleni i niebieski). To jest dość proste: ;
drukuje samą postać. '
przesuwa się na odpowiednią krawędź spacji, która wciąż zawiera 32 z wcześniejszego miejsca i ;
drukuje tę przestrzeń. To {~
sprawia, że nasz EOF? ujemny dla następnej iteracji, '
przesuwa się o krok wstecz, abyśmy mogli powrócić do północno-zachodniego końca struny za pomocą kolejnej ciasnej }{
pętli. Która kończy się na długościkomórka (nie dodatnia poniżej heksa (N) . W końcu }
przesuwa się z powrotem do krawędzi komórki .
Jeśli jednak wyczerpaliśmy już dane wejściowe, to pętla, która szuka EOF? zakończy się tutaj:
W takim przypadku '
przesuwa się na komórkę długości i zamiast tego bierzemy jasnoniebieską (górną) ścieżkę, która drukuje brak operacji. Kod w tej gałęzi jest liniowy:
{*46;{{;{{=
W {*46;
zapisuje 46 do krawędzi oznaczony nie-op i drukuje (to znaczy okres). Następnie {{;
przesuwa się do krawędzi przestrzeni i drukuje to. W {{=
wraca do komórek krawędzi dla kolejnej iteracji.
W tym momencie ścieżki łączą się ze sobą i (
zmniejszają krawędź komórek . Jeśli iterator nie jest jeszcze zerowy, pójdziemy jasnoszarą ścieżką, która po prostu odwraca kierunek MP, =
a następnie szuka następnej postaci do wydrukowania.
W przeciwnym razie dotarliśmy do końca bieżącej linii, a adres IP przejdzie purpurową ścieżką. Oto jak wygląda siatka pamięci w tym momencie:
Purpurowa ścieżka zawiera to:
=M8;~'"=
=
Odwraca kierunek MP ponownie. M8
ustawia ustawia jego wartość na 778
(ponieważ kod znaku M
jest, 77
a cyfry dołączą się do bieżącej wartości). Tak się dzieje 10 (mod 256)
, więc kiedy go wydrukujemy ;
, otrzymujemy wysuw linii. Następnie ponownie ~
powoduje, że krawędź jest ujemna, '"
wraca do krawędzi linii i ponownie =
odwraca MP.
Teraz, jeśli krawędź linii wynosi zero, jesteśmy skończeni. Adres IP podąży (bardzo krótką) czerwoną ścieżką, gdzie @
kończy program. W przeciwnym razie kontynuujemy fioletową ścieżkę, która zapętla się z powrotem do różowej, aby wydrukować kolejną linię.
Kontroluj diagramy przepływu utworzone za pomocą HexagonyColorer firmy Timwi . Diagramy pamięci utworzone za pomocą wizualnego debuggera w jego Esoteric IDE .
abc`defg
naprawdę stałby się pastebin.com/ZrdJmHiR