Befunge, 444 368 323 byte
&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
! "#%$
%$"#!
!!##%
|||_
_ __
Hãy thử trực tuyến!
Cách tiếp cận điển hình để vẽ Hilbert Curve là đi theo đường dẫn dưới dạng một chuỗi các nét và biến, hiển thị kết quả thành một bitmap hoặc một số vùng nhớ, sau đó viết ra kết xuất đó khi đường dẫn hoàn thành. Điều này chỉ không khả thi trong Befunge khi chúng ta chỉ có 2000 byte bộ nhớ để làm việc và bao gồm nguồn của chính chương trình.
Vì vậy, cách tiếp cận chúng tôi đã thực hiện ở đây là đưa ra một công thức cho chúng ta biết chính xác nhân vật nào sẽ xuất cho tọa độ x, y đã cho. Để hiểu cách thức hoạt động này, đó là dễ dàng nhất để bỏ qua render ASCII để bắt đầu, và chỉ cần nghĩ về đường cong như tạo thành từ các nhân vật hộp: ┌
, ┐
, └
, ┘
, │
, và─
.
Khi chúng ta nhìn vào đường cong như vậy, chúng ta có thể thấy ngay phía bên tay phải là một tấm gương chính xác của phía bên tay trái. Các ký tự bên phải có thể được xác định đơn giản bằng cách tra cứu đối tác của họ ở bên trái và phản ánh nó theo chiều ngang (nghĩa là sự xuất hiện của ┌
và ┐
được hoán đổi, như là └
và ┘
).
Sau đó nhìn vào góc dưới bên trái, một lần nữa chúng ta có thể thấy rằng nửa dưới là sự phản chiếu của nửa trên. Do đó, các ký tự ở phía dưới được xác định đơn giản bằng cách tra cứu đối tác của chúng ở trên và phản ánh nó theo chiều dọc (nghĩa là sự xuất hiện của ┌
và └
được hoán đổi, như là ┐
và ┘
).
Nửa còn lại của góc này là một chút ít rõ ràng. Khối bên tay phải có thể được bắt nguồn từ một phản xạ dọc của khối theo đường chéo liền kề với nó.
Và khối bên tay trái có thể được bắt nguồn từ sự phản xạ dọc của khối ở góc trên cùng bên trái của đường cong đầy đủ.
Tại thời điểm này, tất cả những gì chúng ta còn lại là góc trên cùng bên trái, chỉ là một lần lặp khác của Hilbert Curve. Về lý thuyết, bây giờ chúng ta chỉ cần lặp lại quá trình một lần nữa, nhưng có một chút khó khăn - ở cấp độ này, nửa bên trái và bên phải của khối không phải là gương chính xác của nhau.
Vì vậy, ở bất cứ thứ gì ngoài cấp cao nhất, các ký tự góc dưới cùng cần được xử lý như một trường hợp đặc biệt, trong đó ┌
ký tự được phản ánh là ─
và │
ký tự được phản ánh là └
.
Nhưng khác với điều đó, chúng tôi thực sự có thể lặp lại quá trình này một cách đệ quy. Ở cấp độ cuối cùng, chúng tôi mã hóa ký tự trên cùng bên trái là ┌
và ký tự bên dưới là │
.
Bây giờ chúng ta có một cách để xác định hình dạng của đường cong tại một tọa độ x, y cụ thể, làm thế nào để chúng ta dịch nó thành kết xuất ASCII? Đây thực sự chỉ là một ánh xạ đơn giản giúp chuyển từng ô có thể thành hai ký tự ASCII.
┌
trở thành _
(không gian cộng với dấu gạch dưới)
┐
trở thành
(hai không gian)
└
trở thành |_
(thanh dọc cộng với dấu gạch dưới)
┘
trở thành |
(thanh dọc cộng với không gian)
│
trở thành |
(một thanh dọc cộng với không gian)
─
trở thành __
(hai dấu gạch dưới)
Ánh xạ này ban đầu không trực quan, nhưng bạn có thể thấy nó hoạt động như thế nào khi nhìn vào hai kết xuất tương ứng cạnh nhau.
Và đó là cơ bản tất cả là có nó. Trên thực tế, việc thực hiện thuật toán này trong Befunge là một vấn đề khác, nhưng tôi sẽ để lại lời giải thích đó cho một lần khác.