Befunge, 444 368 323 bajtów
&1>\1-:v
0v^*2\<_$00p>
_>:10p\:20pv^_@#-*2g00:+1,+55$
^!-<v*2g000<>$#<0>>-\:v
g2*^>>10g20g+v \ ^*84g_$:88+g,89+g,\1+:00
v#*!-1g02!g01_4^2_
>::00g2*-!\1-:10g-\20g-++>v
87+#^\#p01#<<v!`g01/2\+76:_
vv1-^#1-g01:\_$:2/20g`!
_ 2/^>:10g#vv#`g02/4*3:\+77
v>0p^^/2:/2_
<^2-1-g02</2`#*3:
0g+10p2*:^*3_1
! "#%$
%$"#!
!!##%
|||_
_ __
Wypróbuj online!
Typowe podejście do rysowania Krzywej Hilberta polega na podążaniu ścieżką jako serii pociągnięć i zwojów, renderowaniu wyniku w mapę bitową lub w pewnym obszarze pamięci, a następnie zapisywaniu tego renderowania po ukończeniu ścieżki. Jest to po prostu niewykonalne w Befunge, gdy mamy tylko 2000 bajtów pamięci do pracy, i obejmuje to źródło samego programu.
Podjęliśmy więc formułę, która mówi nam dokładnie, który znak wyprowadzić dla danej współrzędnej x, y. Aby zrozumieć, jak to działa, to najłatwiej zignorować rendering ASCII na początek, i tylko myśleć o krzywej jako składający się ze znaków skrzynkowych: ┌
, ┐
, └
, ┘
, │
, i ─
.
Kiedy spojrzymy na taką krzywą, natychmiast zobaczymy, że prawa strona jest dokładnym lustrem lewej strony. Znaki po prawej stronie można po prostu określić, patrząc na partnera po lewej stronie i odzwierciedlając go w poziomie (tj. Wystąpienia ┌
i ┐
zamiana, podobnie jak └
i ┘
).
Następnie patrząc na lewy dolny róg, ponownie widzimy, że dolna połowa jest odbiciem górnej połowy. Tak więc postacie na dole są po prostu określane, patrząc na swojego partnera powyżej i odzwierciedlając go w pionie (tj. Wystąpienia ┌
i └
są zamieniane, tak jak ┐
i ┘
).
Pozostała połowa tego rogu jest nieco mniej oczywista. Blok po prawej stronie można uzyskać z pionowego odbicia bloku przyległego do niego po przekątnej.
Blok lewej ręki można wyprowadzić z pionowego odbicia bloku w lewym górnym rogu pełnej krzywej.
W tym momencie pozostaje nam tylko lewy górny róg, który jest tylko kolejną krzywą Hilberta o jedną iterację niżej. Teoretycznie powinniśmy teraz po prostu powtórzyć proces, ale jest trochę haczyka - na tym poziomie lewa i prawa połowa bloku nie są dokładnymi zwierciadłami.
Tak więc na czymkolwiek innym niż najwyższy poziom, znaki dolnego rogu należy traktować jako specjalny przypadek, w którym ┌
postać jest odzwierciedlana jako ─
, a │
postać jest odzwierciedlana jako └
.
Ale poza tym naprawdę możemy po prostu powtórzyć ten proces rekurencyjnie. Na ostatnim poziomie kodujemy lewy górny znak jako ┌
, a znak pod nim jako │
.
Teraz, gdy mamy sposób na określenie kształtu krzywej przy określonej współrzędnej x, y, jak przełożyć to na renderowanie ASCII? Jest to po prostu proste mapowanie, które tłumaczy każdy możliwy kafelek na dwa znaki ASCII.
┌
staje się _
(spacja plus podkreślenie)
┐
staje się
(dwie spacje)
└
staje się |_
(pionowy pasek plus podkreślenie)
┘
staje się |
(pionowy pasek plus spacja)
│
staje się |
(ponownie pionowy pasek plus spacja)
─
staje się __
(dwa podkreślenia)
To odwzorowanie początkowo nie jest intuicyjne, ale możesz zobaczyć, jak to działa, patrząc na dwa odpowiadające sobie renderingi obok siebie.
I to w zasadzie wszystko. W rzeczywistości implementacja tego algorytmu w Befunge to zupełnie inny problem, ale wyjaśnię to na inny czas.