Dziękujemy FryAmTheEggman za niezbędną inspirację dla rozwiązania XOR.
0000 !@
0001 ?.|@!
0010 #?#!)@
0011 ?!@
0100 +?|@!?
0101 ??!@
0110 ?<@!!<_\~(
0111 ?<<@!
1000 )\!#?@{
1001 (~?/@#!
1010 ??|@!)
1011 \#??!1@
1100 ?(~!@
1101 ?.|@!)
1110 ?$@#)!<
1111 1!@
Wszystkie programy używają wartości 0
false i 1
true.
Wypróbuj online! To nie jest zestaw testowy, musisz sam skopiować w różnych programach i danych wejściowych.
Powyższe rozwiązanie mieści się w granicach 2 bajtów optymalności (chyba, że rozluźnimy interpretację prawdy / fałszu, jak sądzę). Pozwoliłem, by wyszukiwanie siłowe trwało prawie dwa dni we wszystkich programach, które pasują do długości 2, tj. Do 7 bajtów (nie do końca wszystkie programy - poczyniłem kilka założeń na temat tego, czego potrzebuje każdy prawidłowy program, a czego nie prawidłowy program może mieć). Wyszukiwanie znalazło rozwiązania dla 15 z 16 możliwych bramek - i często o wiele więcej niż tylko jednej. Możesz znaleźć listę wszystkich alternatywnych rozwiązań w tym pastebinie, gdzie pogrupowałem je według równoważnych zachowań. Te, które pokazuję powyżej, wybrałem, ponieważ są albo najprostszym, albo najciekawszym rozwiązaniem, i dodam je jutro.
Jeśli chodzi o 16. bramę: XOR jest jedyną bramą, która najwyraźniej nie może być zaimplementowana w 7 bajtach. Wyszukiwanie przy użyciu brutalnej siły nad większymi programami jest niestety niemożliwe przy użyciu kodu, który mam obecnie. Więc XOR musiał być napisany ręcznie. Najkrótszy, jaki do tej pory znalazłem, to powyższy 10-bajtowy program, który jest oparty na nieudanej (ale bardzo bliskiej) próbie FryAmTheEggman. Możliwe, że istnieje rozwiązanie 8-bajtowe lub 9-bajtowe, ale poza tym wszystkie rozwiązania powinny być rzeczywiście optymalne.
Objaśnienia
Ostrzeżenie: ściana tekstu. Na wszelki wypadek, gdy ktoś jest zainteresowany tym, jak działają te wysoce skompresowane programy Hexagony, zamieściłem objaśnienia do każdego z nich poniżej. Próbowałem wybrać najprostsze rozwiązanie dla każdej bramki w przypadkach, gdy istnieje więcej niż jeden optymalny program, aby wyjaśnienia były dość krótkie. Jednak niektóre z nich wciąż psują umysł, więc pomyślałem, że zasługują na nieco więcej rozwinięcia.
0000
: Fałszywe
Nie sądzę, że potrzebujemy diagramu dla tego:
! @
. . .
. .
Ponieważ cała siatka pamięci jest inicjowana na zera, !
po prostu drukuje zero i @
kończy działanie programu.
Jest to również jedyne rozwiązanie 2-bajtowe.
0001
: I
? .
| @ !
. .
To w zasadzie powoduje zwarcie . Szary schemat poniżej pokazuje początek programu, w którym czytane jest pierwsze wejście, ?
a wskaźnik instrukcji (IP) otacza lewy róg, w którym |
odbija go lustro. Teraz narożnik działa jako warunek, na przykład istnieją dwie różne ścieżki wykonania w zależności od wartości pierwszego wejścia. Czerwony schemat pokazuje przepływ sterowania dla, A = 0
a zielony schemat dla A = 1
:
Jak widać, kiedy A
jest 0
, to po prostu go drukujemy i kończymy (pamiętaj, że .
nie ma żadnych operacji). Ale kiedy A
jest 1
, adres IP ponownie przechodzi do pierwszego wiersza, B
zamiast tego odczytując i drukując.
W sumie dla tej bramki istnieje szesnaście 5-bajtowych rozwiązań. Czternaście z nich jest zasadniczo takich samych, jak powyższe, albo używając >
zamiast, |
albo zastępując .
komendą, która faktycznie nie działa, lub umieszczając ?
na drugiej pozycji:
?.|@! .?|@! ?=|@! =?|@! ?_|@! _?|@! ?0|@!
?.>@! .?>@! ?=>@! =?>@! ?_>@! _?>@! ?0>@!
A potem są dwa inne rozwiązania (które są sobie równoważne). Implementują one również tę samą logikę zwarć, ale ścieżki wykonania są nieco bardziej szalone (i pozostawione jako ćwiczenie dla czytelnika):
?<!@|
?<!@<
0010
: A, a nie B
# ?
# ! )
@ .
Wprowadza to również formę zwarcia, ale ze względu na użycie #
przepływu sterującego jest znacznie trudniejsze. #
jest warunkowym przełącznikiem IP. Sześciokąty faktycznie ma sześć adresów IP oznaczonych 0
na 5
, które zaczynają się w sześciu rogach siatki, wskazując wzdłuż ich krawędzi zgodnej z ruchem wskazówek zegara (a program zawsze zaczyna się od IP 0
). Gdy #
napotkamy a, bieżąca wartość jest pobierana modulo 6
, a przepływ sterowania jest kontynuowany z odpowiednim adresem IP. Nie jestem pewien, jaki przypływ szaleństwa zmusił mnie do dodania tej funkcji, ale na pewno pozwala ona na niektóre zaskakujące programy (takie jak ten).
Rozróżnimy trzy przypadki. Gdy A = 0
program jest dość prosty, ponieważ wartość jest zawsze 0
, kiedy #
jest taka, że nie napotkał IP przełączanie odbywa się:
#
nic nie robi, ?
czyta A
(tzn. też nic nie robi), #
nadal nic nie robi, !
drukuje 0
, )
inkrementuje to (jest to ważne, w przeciwnym razie IP nie przeskakuje do trzeciej linii), @
kończy działanie programu. Wystarczająco proste. Rozważmy teraz przypadek (A, B) = (1, 0)
:
Czerwona ścieżka nadal odpowiada IP 0
, a ja dodałem zieloną ścieżkę dla IP 1
. Widzimy, że po ?
odczytach A
( 1
tym razem) #
przełącza się na adres IP, który zaczyna się w prawym górnym rogu. Oznacza to, że ?
można przeczytać B
( 0
). Teraz )
zwiększa to 1
, aby #
w lewym górnym rogu nic nie robi i pozostajemy z IP 1
. W !
drukuje 1
i OD otacza lewy przekątnej. #
nadal nic nie robi i @
kończy działanie programu.
Wreszcie, naprawdę dziwny przypadek, w którym oba dane wejściowe to 1
:
Tym razem, drugie wejście jest również 1
i )
powiększa go 2
. Oznacza to, że #
w lewym górnym rogu powoduje kolejną zmianę adresu IP na IP 2
, oznaczoną kolorem niebieskim. Na tej ścieżce najpierw zwiększamy ją 3
(choć nie ma to znaczenia), a następnie przechodzimy ?
po raz trzeci. Ponieważ mamy teraz wciśnięty EOF (tzn. Dane wejściowe są wyczerpane), ?
zwraca 0
, !
drukuje to i @
kończy działanie programu.
Warto zauważyć, że jest to jedyne 6-bajtowe rozwiązanie dla tej bramki.
0011
: A
? !
@ . .
. .
Jest to na tyle proste, że nie potrzebujemy diagramu: ?
odczytuje A
, !
drukuje go, @
kończy.
Jest to jedyne 3-bajtowe rozwiązanie dla tej bramki. (Zasadniczo byłoby to również możliwe ,;@
, ale wyszukiwanie nie obejmowało ;
, ponieważ nie sądzę, że może kiedykolwiek zaoszczędzić bajty !
dla tego zadania).
0100
: B, a nie A
+ ?
| @ !
? .
Ten jest o wiele prostszy niż jego „brat” 0010
. Przepływ sterowania jest w rzeczywistości taki sam, jak widzieliśmy powyżej dla 0001
(I). Jeśli A = 0
, to IP przechodzi przez dolną linię, odczytując B
i drukując ją przed zakończeniem. Jeśli A = 1
następnie IP ponownie przechodzi do pierwszego wiersza, również odczytuje B
, ale +
dodaje dwie nieużywane krawędzie pamięci, więc wszystko, co robi, to resetuje bieżącą wartość do 0
, aby !
zawsze drukować 0
.
Istnieje całkiem sporo 6-bajtowych alternatyw (w sumie 42). Po pierwsze, istnieje mnóstwo rozwiązań równoważnych powyższym. Możemy ponownie swobodnie wybierać między |
i >
, i +
możemy je zastąpić dowolnym innym poleceniem, które daje nam pustą przewagę:
"?|@!? &?|@!? '?|@!? *?|@!? +?|@!? -?|@!? ^?|@!? {?|@!? }?|@!?
"?>@!? &?>@!? '?>@!? *?>@!? +?>@!? -?>@!? ^?>@!? {?>@!? }?>@!?
Ponadto możemy również użyć ]
zamiast ?
. ]
przenosi do następnego adresu IP (tzn. wybiera adres IP 1
), dzięki czemu gałąź ta zamiast tego ponownie wykorzystuje ?
w prawym górnym rogu. To daje kolejne 18 rozwiązań:
"?|@!] &?|@!] '?|@!] *?|@!] +?|@!] -?|@!] ^?|@!] {?|@!] }?|@!]
"?>@!] &?>@!] '?>@!] *?>@!] +?>@!] -?>@!] ^?>@!] {?>@!] }?>@!]
I jest jeszcze sześć innych rozwiązań, które działają inaczej z różnym poziomem szaleństwa:
/[<@!? ?(#!@] ?(#>@! ?/@#/! [<<@!? [@$\!?
0101
: B
? ?
! @ .
. .
Woohoo, kolejny prosty: czytaj A
, czytaj B
, drukuj B
, zakończ. Istnieją jednak alternatywy dla tego. Ponieważ A
jest to tylko jeden znak, możemy go również odczytać za pomocą ,
:
,?!@
Istnieje również opcja użycia pojedynczego ?
i dwukrotnego uruchomienia lustra:
?|@! ?>@!
0110
: Xor
? < @
! ! < _
\ ~ ( . .
. . . .
. . .
Jak powiedziałem powyżej, była to jedyna brama, która nie zmieściłaby się w boku o długości 2, więc jest to odręczne rozwiązanie FryAmTheEggman i mnie, i istnieje duża szansa, że nie jest optymalna. Istnieją dwa przypadki do rozróżnienia. Jeśli A = 0
przepływ kontrolny jest dość prosty (ponieważ w takim przypadku wystarczy wydrukować B
):
Zaczynamy czerwoną ścieżką. ?
czyta A
, <
to gałąź, która odchyla zero w lewo. IP zawija się w dół, a następnie _
jest kolejnym lustrem, a kiedy IP uderza w narożnik, zawija się w lewym górnym rogu i kontynuuje na niebieskiej ścieżce. ?
czyta B
, !
drukuje. Teraz (
to zmniejsza. Jest to ważne, ponieważ zapewnia, że wartość nie będzie dodatnia (ani jedna, 0
ani -1
teraz). To powoduje, że IP zawija się w prawy róg, gdzie @
kończy program.
Kiedy A = 1
sprawy stają się trochę trudniejsze. W takim przypadku chcemy wydrukować not B
, co samo w sobie nie jest zbyt trudne, ale ścieżka wykonania jest nieco trippy.
Tym razem <
odchyla adres IP w prawo, a następnie <
działa jak lustro. Tak więc IP przemierza tę samą ścieżkę w odwrotnej kolejności, czytając, B
gdy napotka ?
ponownie. Adres IP zawija się w prawym rogu i kontynuuje zieloną ścieżkę. Również kolejne spotkania (~
, która jest „dekrementuj mnożenie przez -1”, który swapy 0
i 1
i oblicza zatem not B
. \
jest tylko lustrem i !
drukuje pożądany rezultat. Następnie ?
próbuje zwrócić inną liczbę, ale zwraca zero. Adres IP jest teraz kontynuowany w lewym dolnym rogu na niebieskiej ścieżce. (
zmniejsza, <
odzwierciedla,(
ponownie się zmniejsza, tak że bieżąca wartość jest ujemna, gdy IP uderza w narożnik. Porusza się po prawej dolnej przekątnej, a następnie w końcu uderza, @
aby zakończyć program.
0111
: Lub
? <
< @ !
. .
Więcej zwarć.
A = 0
Przypadek (czerwona ścieżka) jest nieco mylące tutaj. Adres IP jest odchylany w lewo, zawija się do lewego dolnego rogu, natychmiast jest odzwierciedlany przez <
i wraca ?
do odczytu B
. Następnie zawija do rigt rogu, drukuje B
się !
i kończy.
Obudowa A = 1
(zielona ścieżka) jest nieco prostsza. <
Gałąź ugina IP rację, więc po prostu wydrukować !
, zawinąć z powrotem do góry po lewej, a kończą się @
.
Jest tylko jedno 5-bajtowe rozwiązanie:
\>?@!
Działa zasadniczo tak samo, ale rzeczywiste ścieżki wykonania są zupełnie inne i używa rozgałęzienia zamiast rozgałęzienia <
.
1000
: Nor
) \
! # ?
@ {
To może być mój ulubiony program znaleziony podczas tego wyszukiwania. Najfajniejsze jest to, że ta implementacja nor
faktycznie działa dla maksymalnie 5 wejść. Będę musiał trochę zagłębić się w szczegóły modelu pamięci, aby to wyjaśnić. Tak więc, jako szybki odświeżacz, model pamięci Hexagony jest oddzielną heksagonalną siatką, w której każda krawędź zawiera wartość całkowitą (początkowo wszystkie zero). Istnieje wskaźnik pamięci (MP), który wskazuje krawędź i kierunek wzdłuż tej krawędzi (tak, że przed i za bieżącą krawędzią znajdują się dwie sąsiednie krawędzie, z wyraźnymi lewymi i prawymi sąsiadami). Oto schemat krawędzi, których będziemy używać, a MP zacznie się jak pokazano na czerwono:
Rozważmy najpierw przypadek, w którym oba dane wejściowe to 0
:
Zaczynamy od szarej ścieżki, która po prostu zwiększa krawędź A do, 1
tak aby #
przełącza się na IP, 1
która jest niebieską ścieżką, zaczynając od prawego górnego rogu. \
nic tam nie robi i ?
czyta dane wejściowe. Zawijamy do lewego górnego rogu, gdzie )
inkrementuje to wejście. Teraz, dopóki dane wejściowe są równe zero, spowoduje to 1
, że #
nic nie zrobi. Następnie {
przesuwa MP w lewo, czyli na pierwszej iteracji z do B . Ponieważ ta krawędź wciąż ma swoje początkowe zero, IP zawija się z powrotem w prawy górny róg i na nowej krawędzi pamięci. Tak więc ta pętla będzie kontynuowana tak długo, jak odczyta zera, przesuwając MP wokół sześciokąta od B?
do C do D i tak dalej. Nie ma znaczenia, czy ?
zwraca zero, ponieważ było to dane wejściowe, czy dlatego, że było to EOF.
Po sześciu powtórzeń przez tę pętlę, {
powraca do . Tym razem krawędź przechowuje już wartość z pierwszej iteracji, więc IP zawija się do lewego rogu i kontynuuje na zielonej ścieżce. po prostu drukuje to i kończy program.1
!
1
@
A co jeśli jakieś dane wejściowe są 1
?
Następnie ?
czyta to 1
w pewnym momencie i )
zwiększa to 2
. Oznacza to, #
że teraz zmieni ponownie adresy IP i będziemy kontynuować w prawym rogu na czerwonej ścieżce. ?
odczytuje kolejne dane wejściowe (jeśli takie są), które tak naprawdę nie mają znaczenia i {
przesuwa się o jedną krawędź dalej. To musi być niewykorzystane zbocze, dlatego działa dla maksymalnie 5 wejść. Adres IP zawija się w prawym górnym rogu, gdzie jest natychmiast odbijany i zawija się w lewym rogu. !
drukuje 0
nieużywaną krawędź i #
przełącza z powrotem na IP 0
. To IP wciąż czekało na #
południowo-zachodniej (szara ścieżka), więc natychmiast trafia @
i kończy program.
W sumie dla tej bramki istnieje siedem 7-bajtowych rozwiązań. 5 z nich działa tak samo jak to i po prostu użyj innych poleceń, aby przejść do nieużywanej krawędzi (i może chodzić po innym sześciokącie lub w innym kierunku):
)\!#?@" )\!#?@' )\!#?@^ )\!#?@{ )\!#?@}
Jest jeszcze jedna klasa rozwiązań, która działa tylko z dwoma danymi wejściowymi, ale których ścieżki wykonania są w rzeczywistości jeszcze bardziej bałagan:
?]!|<)@ ?]!|<1@
1001
: Równość
( ~
? / @
# !
To również bardzo sprytnie wykorzystuje warunkowy wybór adresu IP. Musimy ponownie rozróżnić między A = 0
i A = 1
. W pierwszym przypadku chcemy wydrukować not B
, w drugim chcemy wydrukować B
. Dla A = 0
nas również rozróżnić dwa przypadki dla B
. Zacznijmy od A = B = 0
:
Zaczynamy na szarej ścieżce. (~
można zignorować, IP zawija się w lewym rogu (wciąż na szarej ścieżce) i czyta za A
pomocą ?
. (
zmniejsza to, więc otrzymujemy -1
i IP zawijamy do lewego dolnego rogu. Teraz, jak powiedziałem wcześniej, #
bierze wartość modulo 6
przed wyborem jego IP, więc wartość -1
faktycznie wychodzi z IP 5
, która zaczyna się w lewym rogu na czerwonej ścieżce. ?
czyta B
, również to (
zmniejsza, abyśmy pozostali na IP, 5
kiedy uderzymy #
ponownie. ~
neguje -1
tak, że IP zawija się w prawym dolnym rogu, drukuje 1
i kończy.
Teraz, jeśli B
jest 1
, obecna wartość będzie, 0
gdy uderzymy #
drugi raz, więc przełączamy się z powrotem na IP 0
(teraz na zielonej ścieżce). To uderza ?
po raz trzeci, ustępując 0
, !
drukuje i @
kończy.
Wreszcie przypadek, w którym A = 1
. Tym razem obecna wartość jest już zerowa, gdy uderzamy #
po raz pierwszy, więc nigdy nie przełącza się ona na IP 5
. Po prostu kontynuujemy natychmiast zieloną ścieżkę. ?
teraz nie tylko podaje zero, ale zwraca B
. !
drukuje i @
kończy ponownie.
W sumie istnieją trzy 7-bajtowe rozwiązania dla tej bramki. Pozostałe dwa działają bardzo odmiennie (nawet od siebie) i jeszcze dziwniej je wykorzystują #
. W szczególności odczytują jedną lub więcej wartości za pomocą ,
(odczytuje kod znakowy zamiast liczby całkowitej), a następnie używają tej wartości modulo 6, aby wybrać adres IP. To całkiem szalone.
),)#?@!
?~#,~!@
1010
: Nie b
? ?
| @ !
) .
Ten jest dość prosty. Ścieżka wykonania jest gałęzią poziomą, którą znamy and
wcześniej. ??
czyta, A
a potem natychmiast B
. Po odbiciu |
i rozgałęzieniu B = 0
wykonamy dolną gałąź, w której )
zwiększa się wartość, do 1
której jest następnie drukowany !
. Na górnej gałęzi (jeśli B = 1
) po ?
prostu zresetuj krawędź, do 0
której jest następnie drukowane !
.
Istnieje osiem 6-bajtowych programów dla tej bramki. Cztery z nich są prawie takie same, używając albo >
zamiast |
albo 1
zamiast )
(lub obu):
??>@!) ??>@!1 ??|@!) ??|@!1
Dwa używają jednego, ?
który jest używany dwa razy z powodu lustra. Negacja dzieje się wtedy tak, jak w xor
przypadku jednego z nich (~
lub ~)
.
?>!)~@ ?>!~(@
I na koniec, dwa rozwiązania używają warunkowego przełącznika IP, ponieważ po co korzystać z prostego sposobu, jeśli zawiłe również działa:
??#)!@ ??#1!@
1011
: B oznacza A
\ #
? ? !
1 @
Wykorzystuje to dość skomplikowane przełączanie adresów IP. A = 1
Tym razem zacznę od przypadku, ponieważ jest to prostsze:
Zaczynamy na szarym drogi, który odczytuje A
z ?
czym uderza #
. Ponieważ A
jest 1
to przełącza się na IP 1
(zielona ścieżka). !
Natychmiast drukuje, że IP zawija się w lewym górnym rogu, czyta B
(niepotrzebnie) i kończy.
Kiedy A = 0
sprawy stają się bardziej interesujące. Najpierw zastanówmy się A = B = 0
:
Tym razem #
nic nie robi i pozostajemy na IP 0
(czerwona ścieżka od tego momentu). ?
czyta B
i 1
zamienia go w 1
. Po owinięciu w lewym górnym rogu, uderzamy #
ponownie, więc ostatecznie trafiamy na zieloną ścieżkę i drukujemy 1
jak poprzednio, przed zakończeniem.
Wreszcie, oto (A, B) = (0, 1)
fałszywy przypadek:
Zauważ, że usunąłem początkową szarą ścieżkę dla przejrzystości, ale program zaczyna się w ten sam sposób i kończymy na czerwonej ścieżce jak poprzednio. Tym razem drugi ?
powraca 1
. Teraz spotykamy 1
. W tym momencie ważne jest, aby zrozumieć, co faktycznie robią cyfry w Heksagonii (do tej pory używaliśmy ich tylko na zerach): gdy napotkasz cyfrę, bieżąca wartość zostanie pomnożona przez 10, a następnie cyfra zostanie dodana. Zwykle jest to używane do zapisywania liczb dziesiętnych dosłownie w kodzie źródłowym, ale oznacza to, że B = 1
faktycznie jest odwzorowane na wartość 11
. Więc kiedy trafiliśmy #
, to jest brane modulo 6
do orzekania 5
, a więc możemy przejść do IP 5
(zamiast 1
, jak poprzednio) i dalej na niebieskiej ścieżce. Uderzenie?
trzeci raz zwraca zero, więc !
drukuje to, a po kolejnych dwóch ?
IP zawija się w dolnym prawym rogu, gdzie kończy się program.
Istnieją cztery 7-bajtowe rozwiązania tego problemu i wszystkie działają inaczej:
#)/!?@$ <!?_@#1 \#??!1@ |/)#?@!
1100
: Nie A
? (
~ ! @
. .
Wystarczy jeden prosty liniowy: czytaj A
z ?
, neguje się (~
, drukować !
, kończą się @
.
Istnieje jedno alternatywne rozwiązanie, a ~)
zamiast tego neguje się :
?~)!@
1101
: A oznacza B
? .
| @ !
) .
Jest to o wiele prostsze niż przeciwna implikacja, o której właśnie mówiliśmy. To znowu jeden z tych horyzontalnych programów branżowych, jak ten dla and
. Jeśli A
tak 0
, po prostu zwiększa się 1
na dolnej gałęzi i drukuje. W przeciwnym razie górna gałąź jest wykonywana ponownie tam, gdzie ?
czyta, B
a następnie ją !
drukuje.
Jest tu mnóstwo alternatyw (w sumie 66 rozwiązań), głównie ze względu na swobodny wybór skutecznych no-opów. Na początek możemy zmieniać powyższe rozwiązanie we wszystkich taki sam sposób moglibyśmy za and
i możemy również wybierać pomiędzy )
i 1
:
?.|@!) .?|@!) ?=|@!) =?|@!) ?_|@!) _?|@!) ?0|@!)
?.|@!1 .?|@!1 ?=|@!1 =?|@!1 ?_|@!1 _?|@!1 ?0|@!1
?.>@!) .?>@!) ?=>@!) =?>@!) ?_>@!) _?>@!) ?0>@!)
?.>@!1 .?>@!1 ?=>@!1 =?>@!1 ?_>@!1 _?>@!1 ?0>@!1
A potem jest inna wersja wykorzystująca warunkowy wybór adresu IP, w którym pierwsze polecenie można wybrać prawie arbitralnie, a także istnieje wybór pomiędzy )
i 1
dla niektórych z tych opcji:
"?#1!@ &?#1!@ '?#1!@ )?#1!@ *?#1!@ +?#1!@ -?#1!@ .?#1!@
0?#1!@ 1?#1!@ 2?#1!@ 3?#1!@ 4?#1!@ 5?#1!@ 6?#1!@ 7?#1!@
8?#1!@ 9?#1!@ =?#1!@ ^?#1!@ _?#1!@ {?#1!@ }?#1!@
"?#)!@ &?#)!@ '?#)!@ *?#)!@ +?#)!@ -?#)!@
0?#)!@ 2?#)!@ 4?#)!@ 6?#)!@
8?#)!@ ^?#)!@ _?#)!@ {?#)!@ }?#)!@
1110
: Nand
? $
@ # )
! <
Ostatni skomplikowany. Jeśli nadal czytasz, prawie to zrobiłeś. :) Spójrzmy A = 0
najpierw:
?
czyta, A
a potem trafiamy $
. Jest to polecenie skoku (takie jak Befunge #
), które pomija kolejną instrukcję, abyśmy nie zakończyli na @
. Zamiast tego adres IP jest kontynuowany w #
. Jednak ponieważ A
znaczy 0
, to nie robi nic. )
zwiększa ją, aby 1
adres IP był kontynuowany na dolnej ścieżce, w której 1
drukowany jest. W <
odchyla IP w prawo, gdzie owija się w lewym rogu i program kończy.
Następnie, gdy dane wejściowe są (A, B) = (1, 0)
, otrzymujemy tę sytuację:
Jest to w zasadzie takie same jak przed tą różnicą, że u #
nas włączyć do OD 1
(zielony ścieżka), ale ponieważ B
jest 0
nam wrócić do OD 0
, kiedy uderzył #
po raz drugi (teraz niebieski ścieżki), gdzie drukuje 1
jak poprzednio.
Wreszcie A = B = 1
sprawa:
Tym razem, gdy my #
po raz drugi, bieżąca wartość jest nadal 1
, abyśmy nie zmienili IP ponownie. <
Odzwierciedla to i trzeci raz trafiliśmy ?
otrzymujemy zero. Dlatego IP zawija się w lewym dolnym rogu, gdzie !
wypisuje zero i program się kończy.
Łącznie jest na to dziewięć 7-bajtowych rozwiązań. Pierwsza alternatywa po prostu używa 1
zamiast )
:
?$@#1!<
Są też dwa rozwiązania, które pozwolą ci zmierzyć się z ilością przełączanych adresów IP:
)?#_[!@ 1?#_[!@
To naprawdę zadziwiło mnie: interesującą częścią jest to, że przełączanie adresów IP może być używane jako odroczony warunek. Reguły przełączania adresów IP w języku są takie, że bieżący adres IP robi kolejny krok przed przełączeniem. Jeśli ten krok zdarzy się przejść przez róg, to bieżąca wartość decyduje o tym, w której gałęzi adres IP będzie kontynuowany, jeśli kiedykolwiek wrócimy do niego. Dokładnie dzieje się tak, gdy wejście jest A = B = 1
. Chociaż wszystko to jest spójne z tym, jak zaprojektowałem język, nigdy nie zdawałem sobie sprawy z tego wpływu specyfikacji, więc miło jest, gdy mój język uczy mnie nowych sztuczek: D.
Jest też trzecie rozwiązanie, którego przełączanie adresów IP jest jeszcze gorsze (chociaż nie wykorzystuje tego odroczonego efektu warunkowego):
>?1]#!@
A potem jeszcze jeden:
?$@#)!<
A potem są te cztery równoważne rozwiązania, które wykorzystują pewne bezwarunkowe przełączanie adresów IP i zamiast tego implementują całą logikę za pośrednictwem gałęzi i narożników:
]<?<@!) ]<?<@!1 ]|?<@!) ]|?<@!1
1111
: Prawdziwe
1 !
@ . .
. .
Na koniec zasłużyłeś sobie na coś prostego: ustaw krawędź na 1
, drukuj !
, zakończ z @
. :)
Oczywiście istnieje jedna alternatywa:
)!@
Jak zwykle wszystkie diagramy kontrolne utworzone za pomocą HexagonyColorer Timwi i diagram pamięci z jego EsotericIDE .