444 368 323 bytes
g2*^>>10g20g+v \ ^*84g_$:88+g,89+g,\1+:00
Try it online!
The typical approach to drawing the Hilbert Curve is to follow the path as a series of strokes and turns, rendering the result into a bitmap or some area of memory, and then writing out that rendering when the path is complete. This is just not feasible in Befunge when we only have 2000 bytes of memory to work with, and that includes the source of the program itself.
So the approach we've taken here is to come up with a formula that tells us exactly which character to output for a given x,y coordinate. To understand how this works, it's easiest to ignore the ASCII rendering to start with, and just think of the curve as made up of box characters:
When we look at the curve like that, we can immediately see that right hand side is an exact mirror of the left hand side. Characters on the right can simply be determined by looking up their partner on the left, and reflecting it horizontally (i.e. occurrences of
┐ are swapped, as are
Then looking at the bottom left corner, again we can see that the lower half is a reflection of the top half. Thus the characters on the bottom are simply determined by looking up their partner above, and reflecting it vertically (i.e. occurrences of
└ are swapped, as are
The remaining half of this corner is a little less obvious. The right hand block can be derived from a vertical reflection of the block diagonally adjacent to it.
And the left hand block can be derived from a vertical reflection of the block in the very top left of the full curve.
At this point, all we're left with is the top left corner, which is just another Hilbert Curve one iteration lower. In theory, we should now just need to repeat the process again, but there's a bit of a catch - at this level, the left and right halves of the block aren't exact mirrors of each other.
So at anything other than the top level, the bottom corner characters need to be handled as a special case, where the
┌ character is reflected as
─, and the
│ character is reflected as
But other than that, we really can just repeat this process recursively. At the last level we hardcode the top left character as
┌, and the character below it as
Now that we have a way to determine the shape of the curve at a particular x,y coordinate, how do we translate that into the ASCII rendering? It's actually just a simple mapping that translates each possible tile into two ASCII characters.
_ (space plus underscore)
|_ (vertical bar plus underscore)
| (vertical bar plus space)
| (again a vertical bar plus space)
__ (two underscores)
This mapping isn't intuitive at first, but you can see how it works when looking at two corresponding renderings side by side.
And that's basically all there is to it. Actually implementing this algorithm in Befunge is another problem altogether, but I'll leave that explanation for another time.