Movement as a pair of numbers
In the original Befunge, the pointer moves east, west, north or south. It is natural to think of that as four states, but there is a simpler way to describe it: a pair of numbers saying how far to move along each axis on every step. Moving east is one step in the horizontal direction and none in the vertical, so it is the pair (1, 0). Moving west is (-1, 0). On screen-style grids, where rows count downward, moving down is (0, 1) and moving up is (0, -1).
Funge-98 makes that description real. Each instruction pointer stores a movement vector, usually called its delta. After every instruction, the pointer adds its delta to its position. The familiar direction instructions simply set the delta to one of the four unit values. Once you see it that way, the question “why only four?” has an easy answer: there is no reason.
Setting the delta directly
The instruction x, often called absolute delta, pops a vector from the stack and uses it as the new delta. In two dimensions that means two values. They are pushed in the order of the axes, with the horizontal component first, so the vertical component is on top when the instruction runs.
Here is a very small example:
11x
@
The two 1 digits push (1, 1). When the pointer reaches the x in column 2 of the first row, the delta becomes (1, 1): one step right and one step down on every move. The next cell it visits is column 3 of the second row, which holds @, and the program ends. A pointer that was moving along a row has turned onto a diagonal in a single instruction.
Negative components work in the same way. Pushing a negative number needs a little arithmetic, such as subtracting from zero, because there is no single character for a negative literal.
What other deltas allow
Diagonals are the obvious use, but the idea goes further:
- Long steps. A delta of (2, 0) moves two cells at a time, visiting every other cell. The skipped cells can hold data, or code for a different path.
- Knight-like moves. A delta such as (2, 1) moves two across and one down on each step. It is unusual, but perfectly legal.
- Standing still. A delta of (0, 0) leaves the pointer in place. What an interpreter does there can vary, and a program that relies on it is hard to reason about, so most programs avoid it.
- Higher dimensions. In three dimensions the vector has three components, and the same instruction sets all of them. This is why the same design works across the Trefunge dimensions.
The quarter-turn instructions are worth comparing. Turning left or right rotates the current delta by 90 degrees. For (1, 0) that gives a vertical movement, but for (2, 0) the result keeps the length, so the pointer still steps two cells. In other words, the turn instructions preserve the speed, and only the absolute-delta instruction sets it from scratch. The reverse instruction flips the sign of every component.
What jumps over, and what does not
Skipping cells is not the same as skipping instructions in the usual sense. A pointer stepping two cells at a time does not execute the cell it passes over, and it also does not read that cell as a string character in quote mode. If you rely on a long step, be sure the cells you skip are meant to be skipped.
Compare this with the trampoline instruction, which skips exactly one cell and then resumes normal movement, and with the jump instruction, which moves a set number of cells along the current direction in one go. The delta changes how every future step behaves; those instructions affect a single move.
Wrapping and the edges
Wrapping is where diagonal movement can surprise people. In a toroidal playfield, a pointer leaving one side re-enters from the other. For horizontal or vertical movement this is easy to picture. For a diagonal delta, the interpreter has to compute where the path meets an edge and continue along the same line on the far side. Funge-98 defines this by moving backwards along the line until it would leave the space, and then continuing from that end. Implementers sometimes get the details wrong, and a diagonal that wraps is a good test of an interpreter. If you plan to write one, it is worth making your wrapping logic deal with arbitrary deltas from the start rather than only the four compass directions.
Design benefits and risks
Arbitrary deltas make several things easier. Paths can be shorter, because a diagonal cuts across a layout instead of zigzagging. Code can be laid out in a table-like pattern where each column of a row holds different data. And two pointers can cross each other’s paths without sharing cells.
The cost is readability. When the pointer moves in unusual steps, you can no longer see where it goes by following lines of arrows with your eye. Good habits help:
- Put the setting of the delta in an easy-to-find place, and comment it in a design note outside the code.
- Mark the cells a long step passes over so they are not mistaken for dead code.
- Trace a few steps by hand, writing down the position after each move.
- Prefer a plain turn when a turn will do. Reserve exotic deltas for cases where they genuinely simplify things.
Used sparingly, the idea turns the pointer from a tram on four tracks into something closer to a cursor with a velocity, which is a good mental picture for the rest of the language, and a pleasant exercise in grid debugging when something goes off course.