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, +1
a drugi reprezentuje -1
. Oznacza to, że niezależnie od tego, czy chcemy dodać, czy odjąć 1
od 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. w
i s
doda po prawej stronie. q
i d
przyczyni się do lewej strony, a e
i a
doda do obu stron. Ponieważ w
i s
są już po właściwej stronie :
(która będzie na wierzchu), użyjemy ich odpowiednio jako cyfr -1
i +1
.
Przejdźmy przez kod.
R
5$*r
Zaczynamy od przekształcenia każdego z nich R
w pięć r
sekund. 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
). w
i d
trzeba uciec, aby nie mogły się rozwinąć w klasy postaci. o
Wstawia 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 s
w 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ż R
s, a drugi etap zastosuje kolejny obrót, dopóki r
pozostanie 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ć s
i w
. Oni są naszymi +1
i -1
cyfr, a oni już na właściwej stronie :
, więc oni po prostu rezygnują wymagane w końcu. Możemy wprowadzić kolejne uproszczenie: a
jest po prostu s + q
i e
jest w + d
. Zróbmy to:
a
sq
e
wd
Ponownie, te s
i w
po prostu odpadną. Wszystko, co musimy zrobić, to przenieść te q
s i d
s do przodu i przekształcić je w w
s i s
s 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 q
s i d
s, wiemy, że wszystkie s
s po lewej stronie pojawią się przed dowolnymi w
s, 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 &1
jest takie samo jak modulo 2. Zasadniczo analizuje się to jako x/2
dzielenie liczb całkowitych, zaokrąglone w kierunku minus nieskończoności. Tak więc dla x
liczby dodatniej dodajemy połowę liczby cyfr (zaokrągloną w dół), a dla wartości ujemnej x
odejmujemy 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, \1
druga połowa i możemy zignorować w?
. Wstawiamy tę połowę po :
(która jest x/2
). Jeśli x
jest parzyste, musimy rozróżnić pozytywne i negatywne. Jeśli x
jest 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 s
zostanie po prostu pominięty, więc zaokrąglamy w dół. Jeśli x
jest ujemne i nieparzyste, to możliwe dopasowanie jest z \1
(połowa x
zaokrąglona w dół) i to opcjonalne w
. Ponieważ oba z nich wchodzą w grupę 2
, piszemy x/2
z wielkością zaokrągloną w górę (zgodnie z wymaganiami).
+`sw|ws
Teraz zwijamy cyfry po prawej stronie. Tym razem nie znamy kolejności s
i 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 w
i s
w jedną rozsądną, jednolitą cyfrę. (Przypuszczam, że mógłbym zapisać bajt za pomocą w
lub s
jako jednoznaczną cyfrę, ale wydaje się to trochę rozciągnięte.)