ASCII Hilbert Curve

23

Biorąc pod uwagę liczbę całkowitą nwyświetlamy na nth iteracji Hilberta Curve w ASCII za pomocą znaków _i |.

Oto pierwsze 4 iteracje:

n=1
 _ 
| |

n=2
 _   _ 
| |_| |
|_   _|
 _| |_

n=3
 _   _   _   _ 
| |_| | | |_| |
|_   _| |_   _|
 _| |_____| |_ 
|  ___   ___  |
|_|  _| |_  |_|
 _  |_   _|  _ 
| |___| |___| |

n=4
 _   _   _   _   _   _   _   _ 
| |_| | | |_| | | |_| | | |_| |
|_   _| |_   _| |_   _| |_   _|
 _| |_____| |_   _| |_____| |_ 
|  ___   ___  | |  ___   ___  |
|_|  _| |_  |_| |_|  _| |_  |_|
 _  |_   _|  _   _  |_   _|  _ 
| |___| |___| |_| |___| |___| |
|_   ___   ___   ___   ___   _|
 _| |_  |_|  _| |_  |_|  _| |_ 
|  _  |  _  |_   _|  _  |  _  |
|_| |_| | |___| |___| | |_| |_|
 _   _  |  ___   ___  |  _   _ 
| |_| | |_|  _| |_  |_| | |_| |
|_   _|  _  |_   _|  _  |_   _|
 _| |___| |___| |___| |___| |_ 

Wyjaśnienia

  • Moje pytanie jest podobne do Narysuj krzywą Hilberta i Narysuj krzywą Hilberta za pomocą ukośników .
  • Konwersja między znakami podkreślenia ( _) i pionowymi słupkami ( |) jest u=2*v-1gdzie ujest liczbą _s i vjest liczbą |s.
  • Aby zachować spójność z moim pierwotnym postem, krzywa musi zaczynać się i kończyć na dole.
  • Możesz mieć pełny program lub funkcję.
  • Wyjście na standardowe wyjście (lub coś podobnego).
  • Możesz mieć wiodące lub końcowe białe spacje, dane wyjściowe muszą po prostu zostać wyrównane, aby wyglądały jak przykłady.
  • To jest golf golfowy, więc wygrywa najkrótsza odpowiedź w bajtach.
Bobas_Pett
źródło
3
Czy możesz podać definicję krzywej Hilberta w swoim poście i dokładne specyfikacje dotyczące budowy wersji ASCII?
Loovjo
@Bobas_Pett: Nie złożoność
kolmogorowa
@shooqie trwa debata na ten temat na temat meta
trichoplax
@Loovjo W punkcie „Wyjaśnienia” dodałem w punkcie dotyczącym długości znaków podkreślenia (_) i pionowych pasków (|), jeśli nadal potrzebuję więcej informacji lub dokładnej definicji, proszę mi powiedzieć.
Bobas_Pett
@shooqie usunąłem tag
Bobas_Pett

Odpowiedzi:

5

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 ).

Poziom 3 Krzywa Hilberta pokazująca odbicie w poprzek osi pionowej

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 ).

Poziom 3 Krzywa Hilberta pokazująca odbicie w poprzek osi poziomej w lewym dolnym rogu

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.

Poziom 3 Krzywa Hilberta pokazująca, jak można uzyskać prawy górny blok lewego dolnego rogu

Blok lewej ręki można wyprowadzić z pionowego odbicia bloku w lewym górnym rogu pełnej krzywej.

Poziom 3 Krzywa Hilberta pokazująca, jak można uzyskać lewy górny blok lewego dolnego rogu

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 .

Poziom 3 Krzywa Hilberta pokazująca, jak można uzyskać lewy górny blok lewego dolnego rogu

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 .

Sekwencja obrazów pokazująca, w jaki sposób uzyskuje się pozostałe części krzywej

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.

Poziom 2 Krzywa Hilberta renderowana jako grafika ASCII ze znakami pudełkowymi

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.

James Holderness
źródło
2

C, 267 bajtów

const n=4,s=1<<n,a[]={1,-s,-1,s};l,p,q;
t(d){d&=3;p-q||printf("%.2s",&"__|      _|       |___ _|_| | | "[l*8+d*2]);p+=a[l=d];}
h(d,r,n){n--&&(h(d+r,-r,n),t(d+r),h(d,r,n),t(d),h(d,r,n),t(d-r),h(d-r,-r,n));}
main(){for(;p=s*s-s,l=1,q<s*s;++q%s||putchar(10))h(0,1,n),t(3);}

Wypróbuj online!

h()używa rekurencji do generowania pociągnięć krzywej Hliberta. t()drukuje znak obrysu tylko wtedy, gdy położenie pisaka pjest równe bieżącej pozycji wyjściowej q.

Jest to nieefektywne, ale proste.

Jeśli krzywa zaczyna się w lewym górnym rogu, kod można zmniejszyć do 256 bajtów.

Milo Yip
źródło
Sugeruj puts("")zamiast putchar(10)i "..."+l*8+d*2zamiast &"..."[l*8+d*2]i n--?h(d+r...-r,n):0zamiastn--&&(h(d+r...-r,n))
ceilingcat