Retina , 353 339 178 175 150 130 129 117 bajtów
R
5$*r
T`aq\we\ds`so`r.+
)`r(.*)
$1
^
:
a
sq
e
wd
+`(.+)q
w$1
+`(.+)d
s$1
+`sw
(.*)(\1w?):
$0$2
+`sw|ws
w+
-$0
\w
1
Dane wyjściowe są jednostkowe, oddzielone dwukropkiem. Oznacza to, że tak naprawdę nie zobaczysz zer na wyjściu (chociaż obecność dwukropka powie ci, która z dwóch współrzędnych jest równa zero, jeśli jest tylko jedna).
Wypróbuj online!
To było naprawdę zabawne i skończyło się zaskakująco krótko. :)
Wyjaśnienie
Najpierw trochę tła. Istnieje kilka układów współrzędnych opisujących siatki heksagonalne. Ten, o który prosi się, używa współrzędnych przesunięcia. Zasadniczo przypomina to prostokątne współrzędne siatki, z tą różnicą, że jedna oś „trochę się chwieje”. W szczególności pytanie dotyczy układu „nieparzystego q” pokazanego na połączonej stronie. Ten układ współrzędnych jest nieco denerwujący w pracy, ponieważ sposób zmiany współrzędnych podczas ruchu zależy nie tylko od kierunku ruchu, ale także od bieżącej pozycji.
Inny układ współrzędnych wykorzystuje współrzędne osiowe. Zasadniczo wyobrażam sobie sześciokąt jako przekątny przekrój przez objętość sześcianów i użycie dwóch osi (np. Xiz), aby znaleźć pozycję na płaszczyźnie 2D. Na siatce sześciokątnej oznacza to, że dwie osie tworzą kąt 60 (lub 120) stopni. Ten system jest nieco mniej intuicyjny, ale o wiele łatwiejszy w obsłudze, ponieważ każdy kierunek odpowiada ustalonemu wektorowi „delta”. (Aby uzyskać lepsze wyjaśnienie, jak dojść do tego układu współrzędnych, sprawdź link oraz piękne diagramy i animacje).
Oto co zrobimy: obliczamy ruch we współrzędnych osiowych (dbając o obrót zgodnie z sugestią w wyzwaniu, zmieniając znaczenie poleceń), a kiedy skończymy, konwertujemy osiowe na przesunięcie nieparzyste współrzędne
Sześć ruchów odwzorowuje się na następujące wektory delta we współrzędnych osiowych (xz):
q => (-1, 0)
w => ( 0, -1)
e => ( 1, -1)
d => ( 1, 0)
s => ( 0, 1)
a => (-1, 1)
Czekaj, to jest Retina, będziemy musieli pracować z liczbami jednoargumentowymi. Jak pracujemy z ujemnymi liczbami jednoargumentowymi? Chodzi o to, aby użyć dwóch różnych cyfr. Jeden reprezentuje, +1a drugi reprezentuje -1. Oznacza to, że niezależnie od tego, czy chcemy dodać, czy odjąć 1od bieżącej pozycji, zawsze możemy to zrobić, dodając cyfrę. Po zakończeniu zwijamy wynik do jego wielkości (odpowiadającej mu cyfry), anulując cyfry zrównoważone. Następnie obliczamy znak na podstawie pozostałej cyfry i zastępujemy wszystkie cyfry znakiem 1.
Plan polega na zbudowaniu osiowych komponentów xiz po lewej i prawej stronie :(jako separatora), przed wejściem. wi sdoda po prawej stronie. qi dprzyczyni się do lewej strony, a ei adoda do obu stron. Ponieważ wi ssą już po właściwej stronie :(która będzie na wierzchu), użyjemy ich odpowiednio jako cyfr -1i +1.
Przejdźmy przez kod.
R
5$*r
Zaczynamy od przekształcenia każdego z nich Rw pięć rsekund. Oczywiście jeden skręt w lewo jest taki sam jak pięć skrętów w prawo na siatce heksów, a dzięki temu możemy dużo powielić na etapie odwzorowywania.
T`aq\we\ds`so`r.+
Jest to etap transliteracji, który obraca sześć poleceń, jeśli zostaną one znalezione po pierwszym r(przetwarzając w ten sposób pierwsze r). wi dtrzeba uciec, aby nie mogły się rozwinąć w klasy postaci. oWstawia zestaw źródłowy do zbioru docelowego co oszczędza kilka bajtów dla tych zadań obrotu. Mapowanie znaków jest zatem następujące:
aqweds
saqweds
gdzie ostatni sw drugim rzędzie można po prostu zignorować.
)`r(.*)
$1
To usuwa pierwszy r z ciągu, ponieważ został przetworzony (szkoda, że nie wprowadziłem już limitów podstawień ...). )Mówi również Retina uruchomić wszystkie etapy aż do tego w pętli aż łańcuch przestaje się zmieniać. W kolejnych iteracjach pierwszy etap jest bezoperacyjny, ponieważ nie ma już Rs, a drugi etap zastosuje kolejny obrót, dopóki rpozostanie s w ciągu.
Kiedy skończyliśmy, zamapowaliśmy wszystkie polecenia w kierunku, w którym odpowiadają na nieobrotowej siatce i możemy je przetworzyć. Oczywiście ruch ten jest tylko sumą tych wektorów delta, a sumy są przemienne, więc tak naprawdę nie ma znaczenia, w jakiej kolejności je przetwarzamy teraz, gdy rotacje zostały wyeliminowane.
^
:
Wstaw ogranicznik współrzędnych z przodu.
Teraz tak naprawdę nie musimy przetwarzać si w. Oni są naszymi +1i -1cyfr, a oni już na właściwej stronie :, więc oni po prostu rezygnują wymagane w końcu. Możemy wprowadzić kolejne uproszczenie: ajest po prostu s + qi ejest w + d. Zróbmy to:
a
sq
e
wd
Ponownie, te si wpo prostu odpadną. Wszystko, co musimy zrobić, to przenieść te qs i ds do przodu i przekształcić je w ws i ss sami. Robimy to za pomocą dwóch oddzielnych pętli:
+`(.+)q
w$1
+`(.+)d
s$1
To już koniec. Czas konwersji ze współrzędnych osiowych na przesunięte. W tym celu musimy zwinąć cyfry. Jednak na razie dbamy tylko o lewą stronę. Ze względu na sposób, w jaki przetworzyliśmy qs i ds, wiemy, że wszystkie ss po lewej stronie pojawią się przed dowolnymi ws, więc musimy sprawdzić tylko jedną parę pod kątem ich zwinięcia:
+`sw
Teraz faktyczna konwersja. Oto pseudokod pobrany z powyższego linku:
# convert cube to odd-q offset
col = x
row = z + (x - (x&1)) / 2
Racja, więc lewa strona jest już poprawna. Prawa strona potrzebuje jednak terminu korekty (x - (x&1)) / 2. Przyjmowanie &1jest takie samo jak modulo 2. Zasadniczo analizuje się to jako x/2dzielenie liczb całkowitych, zaokrąglone w kierunku minus nieskończoności. Tak więc dla xliczby dodatniej dodajemy połowę liczby cyfr (zaokrągloną w dół), a dla wartości ujemnej xodejmujemy połowę liczby cyfr (zaokrągloną w górę). Można to wyrazić zaskakująco zwięźle w wyrażeniu regularnym:
(.*)(\1w?):
$0$2
Ze względu na chciwość, na przykład x, grupa 1 będzie pasować dokładnie do połowy cyfr, \1druga połowa i możemy zignorować w?. Wstawiamy tę połowę po :(która jest x/2). Jeśli xjest parzyste, musimy rozróżnić pozytywne i negatywne. Jeśli xjest dodatni, to w?nigdy nie będzie pasował, więc obie grupy będą musiały dopasować tę samą liczbę cyfr. Nie ma problemu, jeśli pierwszy szostanie po prostu pominięty, więc zaokrąglamy w dół. Jeśli xjest ujemne i nieparzyste, to możliwe dopasowanie jest z \1(połowa xzaokrąglona w dół) i to opcjonalne w. Ponieważ oba z nich wchodzą w grupę 2, piszemy x/2z wielkością zaokrągloną w górę (zgodnie z wymaganiami).
+`sw|ws
Teraz zwijamy cyfry po prawej stronie. Tym razem nie znamy kolejności si w, więc musimy uwzględnić obie pary.
w+
-$0
Obie części są teraz zredukowane do jednej powtarzającej się cyfry (lub nic). Jeśli ta cyfra jest w, wstawiamy znak minus z przodu.
\w
1
I w końcu zamieniamy zarówno w, jak wi sw jedną rozsądną, jednolitą cyfrę. (Przypuszczam, że mógłbym zapisać bajt za pomocą wlub sjako jednoznaczną cyfrę, ale wydaje się to trochę rozciągnięte.)