Position plus heading
A grid pointer has two pieces of state that a line-based program counter does not: where it is, and which way it is facing. The heading is what makes the pointer a traveler rather than a counter. Unless an instruction changes it, the pointer keeps going in the same direction, one cell per step, no matter what it passes. In Befunge-style languages, the heading starts as “right”, and the pointer starts at the top-left cell, so a program is read left to right along its first row until something redirects it.
In the classic two-dimensional case, there are exactly four headings: right, left, up and down. Each step adds a small offset to the position, such as one column to the right or one row up. Everything about movement in these languages comes down to when the heading changes.
Arrow cells
The simplest way to change heading is an arrow. Each of the four characters sets the heading to a fixed direction:
| Character | New heading |
|---|---|
> |
right |
< |
left |
^ |
up |
v |
down |
When the pointer lands on one, it keeps its position, adopts the new heading, and carries on from there. An arrow does nothing else. It doesn’t consume or produce values.
Arrows are the corners of the corridors in a grid program. To make a square route, you place an arrow at each of the four corners. To make a long straight run, you place nothing and let the pointer keep going. A single arrow pointing back the way the pointer came simply reverses it, which gives a back-and-forth path with no extra effort.
Turns that depend on a value
Fixed arrows give routes but no decisions. For decisions, grid languages offer cells that check a value and pick a heading. In Befunge-93 there are two:
- The horizontal conditional removes the top value of the stack. If it is zero, the pointer heads right; otherwise it heads left.
- The vertical conditional does the same for up and down: zero sends the pointer down, any other value sends it up.
Because these two cells replace the whole if structure, the “then” and “else” parts are simply two corridors leaving one cell in opposite directions. The pointer picks one, and the other stays unvisited for that pass. The practical patterns for this are covered in the article on conditional branching, so only the essentials are given here.
One detail that surprises beginners: these cells consume the value they test. If you need it again afterward, you must duplicate it before the test. Another is that reading a value from an empty stack gives zero in Befunge-93, which means the zero path is taken if the stack is empty. That can hide a bug where a value was never pushed, a mistake that shows up on the list of beginner mistakes.
Choosing a direction at random
There is also a cell that picks one of the four headings at random. Programs that use it behave differently from run to run, and are harder to reason about because there is no single route to trace. That feature is useful for simple simulations or games, and for the exercises on random direction in the algorithms section. For most programs it is best used sparingly, since it makes reproducing a bug difficult.
Jumping over a cell
A different kind of change is the skip. The trampoline instruction makes the pointer move two cells in one step, leaving out the cell it jumps over. The heading doesn’t change; what changes is which cell is visited. It is handy for stepping past a single instruction, such as an exit marker, without needing a turn. It also changes which cells count as unvisited, so it interacts with the idea of dead code.
Beyond four directions
Funge-98 extends the idea. It adds instructions to turn left or right by ninety degrees relative to the current heading, to reverse, and to set the heading directly from values on the stack. These direct settings are described as delta vectors, which give the pointer a step of any size in any direction, including diagonal moves and steps longer than one cell. In languages with more dimensions, such as Trefunge, there are also two more headings, for movement along the third axis. The details differ by implementation level, so check the specification you are using.
Some other two-dimensional languages use a related mechanism in which cells act as mirrors that reflect the pointer. The mirror commands in Fish-style languages bend the path in the same way an arrow does, though the cell does not set an absolute direction; it reflects whichever direction the pointer arrived in.
Working with direction in practice
A few habits make direction easier to manage:
- Think in corridors. Decide the heading you want on each stretch, then place arrows only where the heading changes.
- Watch the approach direction. A conditional turn sends the pointer along an axis that is perpendicular to its arrival, so there needs to be room for the exits.
- Avoid accidental arrows. A letter or a symbol left in a path can be an instruction. Characters meant as data belong out of the pointer’s way.
- Check corners after edits. Moving a line often leaves an arrow pointing at nothing, and the pointer then wanders into blank space and eventually wraps around.
Because the heading is invisible in the source text, it is easy to lose track of it when reading. Writing the heading beside each leg of a route, or using a pencil sketch, brings it back into view. Direction is the one piece of state that decides everything else in a grid program, and nearly every other idea in this cluster builds on it.