Było to zaskakująco trudne i nie jestem przekonany, czy jest optymalne ...
<.@!$?
Po wypełnieniu i rozwinięciu kodu reprezentuje to następującą siatkę heksadecymalną:
Wykorzystuje to podobny przepływ kontrolny, jak mój ostatni bezbłędny program dla kotów , poruszający się po przekątnych. Aby to osiągnąć, zaczynamy od odchylenia wskaźnika instrukcji (IP) w lewo, gdzie fioletowa ścieżka otacza lewy dolny róg.
?
odczytuje dane wejściowe jako liczbę całkowitą. !
drukuje to z powrotem. .
jest po prostu zakazem. Teraz róg siatki działa jak gałąź:
Jeśli dane wejściowe były 0
, adres IP będzie kontynuowany wzdłuż czerwonej ścieżki, która po prostu kończy działanie programu @
.
Jeśli dane wejściowe były 1
, adres IP będzie kontynuowany na zielonej ścieżce. Ponownie, .
to po prostu brak możliwości, ale $
jest odpowiednikiem trampoliny Befunge: pomija następną instrukcję. Po zawinięciu następna instrukcja byłaby ?
, ale ze względu na $
wykonanie faktycznie kontynuuje na niebieskiej ścieżce, zaczynając od !
wydrukowania kolejnej kopii 1
. Ta pętla, która zawiera tylko, !..$
jest teraz powtarzana w nieskończoność.
Badanie przepływu kontrolnego w Hexagony ...
Uważam, że powyższe rozwiązanie jest optymalne. Napisałem brutalny forcer, który sprawdza wszystkie 6-bajtowe programy Hexagony, które zawierają co najmniej jeden z nich ?!@
(które są konieczne; sprawdziłem również :
i %
zamiast tego @
kończyć się błędem dzielenia przez zero, ale to też nie pomogło). Czek wypisuje wszystkie programy, które a) wytwarzają 0
wejście 0
i kończą oraz b) wytwarzają co najmniej dwa 1
s (i nic więcej) i nie kończą się w ciągu pierwszych 60 znaczników programu (200 znaczników dla rozwiązań 5-bajtowych) . Wątpię, aby każde prawidłowe rozwiązanie wymagało więcej niż 200 tyknięć, aby poprawnie wydrukować pierwszy 0
lub drugi 1
na tak małej siatce, więc nie sądzę, że nie przegapiłem żadnych potencjalnych rozwiązań.
Wyszukiwanie nie przyniosło żadnych wyników dla 5 bajtów, ale 57 wyników dla 6 bajtów (przy użyciu @
; nie trzeba kończyć z błędem, jeśli możemy rozwiązać ten problem w tej samej ilości bajtów). Z tych 57 tylko 6 było fałszywie dodatnich, które w rzeczywistości wydrukowały tylko dwie 1
sekundy, a następnie weszły w nieskończoną pętlę bez drukowania. Jedno rozwiązanie zostało wymienione dwukrotnie, ponieważ zawierało dwa !
polecenia. Pozostawia dokładnie 50 poprawnych rozwiązań.
Istnieje pewna degeneracja między rozwiązaniami, w których jedna lub dwie postacie nie są znaczące, np. Dlatego, że i tak nie działają. Rozwiązania można pogrupować w 23 zestawy naprawdę odrębnych programów (w niektórych przypadkach istnieje tylko jedna różnica znaków między dwoma zestawami, ale zmienia to znacznie przepływ sterowania, więc policzyłem je osobno). Dwie grupy nawet wykorzystują wiele wskaźników instrukcji w bardzo nieoczekiwany sposób. Ponieważ nigdy nie wymyśliłem większości tych sposobów korzystania z rozgałęzień i luster, robią one bardzo interesujące studium tego, jaki rodzaj kontroli jest możliwy w Hexagony, i na pewno nauczyłem się nowych sztuczek dla przyszłych golfów.
Ogólny przepływ sterowania jest prawie zawsze taka sama: odczytać numer, wydrukować go. Jeśli 0
znajdziesz sposób na @
, jeśli nie, kontynuuj zapętlanie, utrzymując !
jednocześnie wartość krawędzi 1
. Istnieją cztery godne uwagi wyjątki:
- Jedno rozwiązanie (jedno z dwoma
!
) drukuje dwie 1
s na iterację przez siatkę, dlatego drukuje około dwa razy szybciej niż większość programów. Oznacziłem ten x2
poniżej.
- Kilka rozwiązań (te, które zawierają
o
) wymieni 1
z 111
(kodem znaku o
), więc wydrukować trzy 1
s na iteracji, co czyni je wydrukować około trzy razy tak szybko, jak w większości programów. Oznacziłem je x3
poniżej.
- Dwa rozwiązania dodają a
1
do wartości krawędzi w każdej iteracji (więc 1
-> 11
-> 111
-> ...). Te drukują się bardzo szybko, ale w końcu zabraknie im pamięci. Oznacziłem je OoM
poniżej.
- Dwa rozwiązania wchodzą w bardzo ciasną pętlę, która jedynie odbija się w tę iz powrotem
!
, drukując co drugi tik (zamiast co piątego lub więcej), co czyni je nieco szybszymi (i bardziej uporządkowanymi). Oznacziłem je ><
poniżej.
Oto całe zoo:
#1 #5 #12 #19
?!/$.@ ?$!>$@ .?!/$@ |!|?$@ # ><
?!/$1@ # OoM ?$!|$@ =?!/$@
?!/$=@ #20
?!/$\@ #6 #13 $@.?<!
?!/$o@ # x3 ?/!<|@ .?/!$@ $@1?<! # OoM
?!/$!@ # x2 =?/!$@ $@=?<!
#7 $@o?<! # x3
#2 ?\!<|@ #14
?!>$)@ \!?__@ #21
?!>$1@ #8 _>_!?@
?!>$o@ # x3 ?<!>$@ # >< #15
?!|$)@ \_?!$@ #22
?!|$1@ #9 <!@.$?
?!|$o@ # x3 ?\$!@$ #16 <!@/$?
\_?!_@ <!@=$?
#3 #10 <$@!$?
?!|)$@ ?~#!@) #17 <.@!$?
?!|1$@ ?~#!@1 $$?\@! </@!$?
?!|o$@ # x3 <=@!$?
#11 #18
#4 ?$)\@! \$?\@! #23
?_!<@> ?$1\@! <<@]!?
?$o\@! # x3
Poniżej znajduje się krótki przewodnik dla kilku bardziej reprezentatywnych grup. Szczególnie warto sprawdzić grupy 10 i 23. Istnieje wiele innych interesujących i czasami skomplikowanych ścieżek w innych grupach, ale myślę, że na koniec dość mi się nudziłem. Dla każdego, kto naprawdę chce nauczyć się heksagonii, zdecydowanie warto je zbadać, ponieważ wykazują jeszcze więcej możliwych zastosowań lusterek i $
.
Grupa 1
To nie jest dużo bardziej skomplikowane niż moje oryginalne rozwiązanie, ale ścieżki idą w różnych kierunkach. Pozwala również na największą liczbę odmian w jednej komórce, ponieważ najbardziej prawy brak no-op można zastąpić 5 różnymi poleceniami, które nadal sprawiają, że jest to ważne bez zmiany struktury:
Grupa 2
Ten jest dość interesujący, ponieważ porusza się tylko w poziomie. Po zawinięciu do >
adresu IP natychmiast się odwraca, biorąc gałąź w rogu. Nie jest to całkiem dobrze widoczny schemat, ale w przypadku ponownie 1
przechodzimy do pierwszego rzędu, ale tym razem do tyłu. Oznacza to również, że ?
ponownie się na nie natkniemy, co teraz zwraca 0
(EOF). Jest to naprawione za pomocą )
(przyrostu), aby zachować drukowanie 1
. Ma to również 5 odmian, a )
może być również 1
lub o
, i >
może być także |
:
Grupa 3
Ten wygląda prawie identycznie jak poprzedni, ale jest bałaganiarski jak diabli. |
To samo dotyczy uderzenia, a następnie przejścia dolnego lub górnego rzędu. Ale w przypadku pętli $
teraz przeskakuje nad )
na lustrze. Idziemy więc turkusową ścieżką po prawej stronie, teraz uderzamy o przyrost, przeskakujemy @
przed, zanim |
ponownie się zawiniemy, a następnie wracamy do zielonej ścieżki u góry.
Grupa 4
Myślałem, że ten był wyjątkowo fajny:
_
Lustro w prawym górnym rogu jest początkowo no-op, więc drukować !
i hit <
. 0
Ścieżka uderza teraz poziome lustro i kończy. 1
Ścieżka trwa bardzo interesujący tor chociaż: to ugina w dół, zawija do !
, zostanie przekierowany do linii poziomej, a następnie owija powrotem do !
ponownie . Następnie porusza się w tym kształcie rombu, drukując dwa razy na iterację (co trzeci tik).
Grupa 8
To jedno z dwóch rozwiązań z bardzo ciasną pętlą drukującą:
Że <
działa jako oddział. Po dwukrotnym owinięciu 0
trafienia @
. 1
z drugiej strony, najpierw przeskakuje ?
, a następnie >
przesyła go $
ponownie do, więc przeskakuje @
. Następnie IP owija się w turkusową ścieżkę, gdzie odbija się tam iz powrotem między >
i <
(owija się wokół krawędzi pomiędzy).
Grupa 10
Jedna z dwóch grup, które używają innych wskaźników instrukcji i jest absolutnie piękna. Sześciokąty mają 6 - każdy zaczyna się od innego rogu wzdłuż krawędzi zgodnej z ruchem wskazówek zegara, ale tylko jeden z nich jest aktywny na raz.
Jak zwykle czytamy ?
. Teraz ~
jest jednoargumentacja: zamienia on 1
w -1
. Następnie trafiliśmy na #
. Jest to jeden ze sposobów przełączania między adresami IP: pobiera bieżącą wartość krawędzi modulo 6 i przełącza się na odpowiedni adres IP (adresy IP są ponumerowane 0
zgodnie z ruchem wskazówek zegara). Więc jeśli dane wejściowe były 0
, wtedy IP po prostu pozostaje takie samo i nudzi się prosto do przodu !@
. Ale jeśli dane wejściowe były 1
, to aktualna wartość jest taka, -1
która jest 5 (mod 6)
. Przełączamy się więc na adres IP, który zaczyna się w tej samej komórce (zielona ścieżka). Teraz #
nie ma opcji i ?
ustawia krawędź pamięci na 0
. )
przyrosty, więc !
drukuje a 1
. Teraz uderzamy~
ponownie, aby to zapewnić#
nadal nie działa (w przeciwieństwie do przełączania nas na IP 1, co kończy program). To niesamowite, jak dobrze wszystko pasuje do siebie w tym małym programie.
Grupa 22
Należy zauważyć, że jest to grupa, w której znajduje się moje oryginalne rozwiązanie. Zdarza się również, że jest to największa grupa, ponieważ brak operacji może znajdować się w dwóch różnych miejscach i istnieje kilka opcji dla rzeczywistego polecenia (efektywnego braku operacji).
Grupa 23
To druga grupa korzystająca z wielu adresów IP. W rzeczywistości ten wykorzystuje 3 różne adresy IP. W prawym górnym rogu jest trochę bałaganu, ale spróbuję przeprowadzić Cię przez to:
Tak więc początek, który widziałeś wcześniej: <
odbija północno-wschodni, ?
odczytuje dane wejściowe. Teraz ]
jest inny sposób przełączania między adresami IP: przekazuje kontrolę do następnego adresu IP w kolejności zgodnej z ruchem wskazówek zegara. Przełączamy więc kontrolę na turkusową ścieżkę, która (wiem, że trudno ją zobaczyć) zaczyna się w narożniku północno-wschodnim i przechodzi na południowy wschód. Od razu odbija się to <
tak, że owija się w południowo-wschodni róg, kierując się na północny zachód. To także uderza ]
więc przejść do następnego OD. To jest szara ścieżka rozpoczynająca się we wschodnim rogu, prowadząca na południowy zachód. Drukuje dane wejściowe, a następnie zawija do północno-wschodniego rogu. <
odchyla ścieżkę do poziomu, gdzie jest odbijana przez drugą <
. Teraz prawa ręka<
działa jak gałąź: jeśli dane wejściowe były0
, IP przenosi się na północny wschód i opada na @
. Jeśli dane wejściowe były 1
, IP przesuwa się do !
, zawija w lewą stronę, <
gdzie jest odbijane ... teraz w rogu, zawija się z powrotem !
, odbija się w prawo <
, odbija w lewo, <
a ścieżki zaczynają się koniec...
Całkiem bałagan, ale piękny bałagan. :)
Diagramy wygenerowane przez niesamowity HexagonyColorer Timwi .