Hyperheptefunge-98
Hyperheptefunge-98 is a version of Funge-98 on the heptagonal tiling of the 2D hyperbolic plane by User:BoundedBeans. Funge-98 is not a single language, but rather a template language for various topologies and number of dimensions, which the official ones are Unefunge-, Befunge-, and Trefunge-98. However, Hyperheptefunge-98 is impossible to directly specify as "Funge-98 in the heptagonal tiling in hyperbolic 2D space", as coordinates and many other concepts work differently.
The 7 Directions
As you can see in the above graphic, every cell is a heptagon with 7 neighbors. This forms a sort of 7-direction system of movement, but due to this number being odd, none are direct inverses of another. Also, moving in the 1st direction, then the 3rd, is not the same as moving in the 3rd, then the first, and similar things happen for other pairs of directions. This is different from 2D Euclidean space, which is the "standard" thing we usually think of when talking about 2D space. (It's the topology of a sheet of paper, while hyperbolic space would correspond to an infinite Pringle-sort of thing, except that eventually the curves would start branching into frills the further you go away from the center).
Coordinates are specified as a list of numbers 1-7, each number specifying the direction to move to from the previous spot (clockwise relative to a starting direction which is unspecified since it doesn't matter which one it is as changing it is completely equivalent under the rules of this language (it would sort of be like taking regular Unefunge-98 and flipping it, but also making all coordinates and deltas negative of what they normally are)). This list can be arbitrarily long and is terminated with 0. The storage offset also follows the format.
IP deltas are expressed as relative coordinates from the current IP's position to the next.
Note that directions and deltas are not reversed like they are in other Funges (Euclidean funges have y at the top so putting stuff on the stack is easier, but Hyperheptefunge does no such thing), so the first direction should be at the top of the stack, not directly above the terminating 0.
A coordinate or delta a1 a2 a3 ... 0 can be represented as (a1 a2 a3). So the ^
command changes your direction to (3), while 0 3 6 4 on the stack while executing x
changes your direction to (4 6 3).
Multiplying a delta by a number n means repeating the numbers before the terminating 0 n times, then putting the terminator after.
Adding two deltas means concatenating their non-terminator parts, then adding the terminating 0 to the end.
Pushing a delta onto a stack means pushing a 0, then pushing the non-terminator parts in reverse.
Altered functionality
The stack absolutely must be pseudo-infinite to account for arbitrary coordinates (that is, it should only be limited by the memory given to the program).
The stack stack (for {}u
commands) still exists but may have a finite amount of stacks (why bother making it finite though, since with infinite sub-stacks it likely won't make any optimizations over an infinite stack-stack).
Since reflecting to the opposite direction does not exist, anywhere where Funge-98 would normally reflect actually just changes all the coordinate numbers c to ((c-1)+3 mod 7) + 1
.
The []mhlw_|
commands exist, but are different in meaning..
Direction commands (note that the arrows don't correspond to anything here, < is NOT the reverse of > and they don't point to anything anymore; I have to be quite careful here since the only basic ASCII characters I can use for directions are the ones that don't have an easy analogue here, since most characters mean something else and the uppercase letters are reserved for fingerprints):
> |
1 |
< |
2 |
^ |
3 |
v |
4 |
[ |
5 |
] |
6 |
m |
7 |
Conditional direction commands:
h |
If popped value is 0, delta=(1), otherwise delta=(5) |
l |
If popped value is 0, delta=(1), otherwise delta=(4) |
w |
If popped value is 0, delta=(2), otherwise delta=(3) |
_ |
If popped value is 0, delta=(3), otherwise delta=(7) |
| |
If popped value is 0, delta=(3), otherwise delta=(6) |
The xpg
commands use a different structure for coordinates and deltas.
The io
commands have to be rewritten, since they can't rely on a rectangular area anymore.
i
pops the file name, then the starting coordinate, then the delta that each successive character should be written to. It is always in binary mode. It writes the first byte to the starting coordinate, adds the delta, writes the second byte, and continues doing thing until all bytes have been written. It pushes the starting coordinate, followed by the number of bytes written, followed by the delta. The ending coordinate will likely be much longer than the starting one.
o
pops a file name, a delta, the number of bytes to read, and a starting coordinate. Starting at the starting coordinate, it writes the byte at that coordinate, then moves by the delta and writes the byte there, etc. repeats (the number of bytes to read as popped) times. Cells are 32-bit of course, so the bytes used are modulo 256 from the actual cells value. It is always in binary mode, and newlines must be written into Funge-space as 10, 13, 13 10, whatever newline sequence is used on the system, since there is no rectangle to add the newlines at the right edges of.
Fingerprints
A lot of Funge-98 fingerprints will work fine in Hyperheptefunge. Even the ones that accept deltas or coordinates can be easily interpreted in a semantical sense to use the new system, as the words "delta" and "coordinate" now literally mean "a 0-terminated list of numbers 1-7 with specific meaning". You'd have to specify it was a "Euclidean 2D coordinate" or "Euclidean 3D delta", which they don't. However, a few already existing ones, such as 3DSP, rely on Funge-space having a way to represent certain things (3DSP requires a 4x4 rectangle for matrices).
Syntax
Programs are written as a list of lines of this format:
[Coordinates] : [Character][Comments]
The comments can be arbitrarily long, and must be used when dealing with spaces or tabs.
Also, a comment can be written like:
#[comment]
To completely ignore a line. This also allows Unix scripting with #![path]
Remember the coordinates can be arbitrarily long lists. Since they're only a single digit for each number, the spaces between digits, parentheses and whitespace around the colon are option. So these are equivalent:
(1 3 3 7) : > (13 37): > 1 3 3 7:> 1337:>
If there is a space or tab followed by an underscore as the character, it counts as the space or tab, not the underscore. If a space or tab is followed by any other character, it counts as the latter.
Also, coordinates that are very long, but follow some programmable structure can be replaced with a C format string representing a Hyperheptefunge-98 program that outputs the digits. This takes the place of a single digit, any before will be prepended, after will be appended, and multiple strings can be in one coordinates. So this is valid:
6"123:+\n124:*"72"1:4\n3:/":|
Strings can also be written like:
"[Text]"-'[Name]'
Which assigns the string/program to the name in single-quotes so you can use it later. Then you can refer to it with the name in single quotes later on the same line or on a line further down (you may not use it before). This way, long programs can be turned into shorter ones. Also, the output of the first program will be copied in there, it won't be run again for a single-quoted copy, but will be for a double-quoted repitition.
In the total-interpreted model of this language, the double quoted text shares the input stream with the resulting program. In the sort-of-transpiled model, the double quoted and single quoted texts compile to a resulting new program, and when the double-quoted programs input or output, it will do it while running the compiler, and the interpreter part of it will do it separately. However, nested double-quoted programs will share the same streams since the compiler will call itself during the same execution.
Interpreters will ideally support both models, but they could also support either one.
Compilers would be hard to create, like in regular Funges, but if one is created, it could follow either one or both, depending on where the coordinate interpolation is performed.
Weirdness
Hyperbolic space is infinite, much like Euclidean is infinite. However, hyperbolic space is sort of "more infinite" than Euclidean space, in the sense that if you travel 100 units in hyperbolic space you are much farther away from where you started than if you were in Euclidean space. If you had a house on one of these heptagons, and you walked too far and didn't know the area, you'd be very likely to never see your house again unless you could guess the way back quickly. Too many travels in the wrong direction might take you further away from your house. Similarly, IPs in this language can get really far away from other ones traveling only a reasonable distance.
Programs can also be much more compact because of this extra space and more directions, though due to the syntax compact programs does not necessarily mean smaller numbers of characters or bytes.
Also, there are no parallel lines in hyperbolic space. So two IPs traveling in the same direction on adjacent heptagons would quickly get further and further from each other, with a wedge-type thing in between their paths. This is part of the reason why programs can be more compact; you can fill that wedge with lots of instructions to save space.
Example programs
Well, it's hard to visualize, so programs would be much easier to create with a GUI/IDE that accounts for this geometry, but a simple program requiring no branches or loops is quite easy.
Hello, world!
:" 1:H 11:e 111:l 1111:l 11111:o 111111:, 1111111: _ 11111111:w 111111111:o 1111111111:r 11111111111:l 111111111111:d 1111111111111:" 11111111111111:, 111111111111111:, 1111111111111111:, 11111111111111111:, 111111111111111111:, 1111111111111111111:, 11111111111111111111:, 111111111111111111111:, 1111111111111111111111:, 11111111111111111111111:, 111111111111111111111111:, 1111111111111111111111111:, 11111111111111111111111111:,
Truth-machine
This was really tricky to do as I don't quite understand what directional combinations are the same as other ones. I'm not even completely confident this works, as no implementation exists yet.
If you look at the above image and rack your brain really hard remembering where all the commands are, you can write small programs, but then things get too small at the edges to work with if your program is too big. Luckily the tiling is regular so you can just pretend the origin corresponds to a different tile (here tile 15 or 7), and as long as the full loop fits you can do it. It's still hard to figure out where a direction is from a certain tile due to the stretching. A Hyperheptefunge-98 IDE would really help here.
:& Input the number 1:h Conditional, either direction 1 or 5. 11:0 Stack: [0] 111:. Output 0 1111:@ Halt # Coordinates 15 is the same as 7 7:[ Set direction to 5 to make loop easier 75:1 755:. 7555:> Set direction to 1 to start loop 75551111:v Set direction to 4, will lead directly into coordinate 7