Heighway Dragon
The Heighway Dragon curve is defined by the Lindenmayer system: Axiom: F_a Rules: F_a -> F_a R F_b, F_b -> F_a L F_b where F_a and F_b mean "draw forward", R means "turn right 90 degrees", and L me...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
Let \(D_0\) be the two-letter string "Fa". For \(n \geq 1\), derive Dn from \(D_{n - 1}\) by the string-rewriting rules:
-
"a" â
† ’ "aRbFR" -
"b" â
† ’ "LFaLb"
Thus, \(D_0 =\) "Fa", \(D_1 = \) "FaRbFR", \(D_2 = \) "FaRbFRRLFaLbFR", and so on.
These strings can be interpreted as instructions to a computer graphics program, with "F" meaning "draw forward one unit", "L" meaning "turn left \(90\) degrees", "R" meaning "turn right 90 degrees", and "a" and "b" being ignored. The initial position of the computer cursor is \((0,0)\), pointing up towards \((0,1)\).
Then Dn is an exotic drawing known as the Heighway Dragon of order n. For example, D10 is shown below; counting each "F" as one step, the highlighted spot at \((18,16)\) is the position reached after \(500\) steps.

What is the position of the cursor after \(1012\) steps in \(D_{50}\) ?
Give your answer in the form \(x,y\) with no spaces.
Problem 220: Heighway Dragon
Mathematical Analysis
String Expansion
After iterations, the string has draw commands ( or ). The expansion is:
where is the expansion with as starting symbol and with .
Actually, the rules are:
After expansions starting from , we get a string with forward moves.
Recursive Computation
The key insight is that we can compute the position after steps recursively without expanding the full string.
Define for each expansion level and starting symbol :
- The total number of forward steps:
- The net displacement when starting in a given direction.
- The net rotation (total turning).
For the expansion of at level :
If we need steps from :
- If : take steps from .
- If : take all steps from , turn right, then take steps from .
Direction Tracking
We track the current direction as one of {N, E, S, W} encoded as 0, 1, 2, 3. Turning right adds 1 mod 4; turning left subtracts 1 mod 4.
We precompute for each level and symbol :
- , : net displacement when starting in direction .
- : net rotation (number of right turns mod 4).
Base case (): and both just move forward 1 step in the current direction. Net rotation = 0.
Recursion for Precomputation
For starting in direction :
- After : direction becomes .
- After R: direction becomes .
- After : direction becomes .
So .
Similarly for : .
Correctness
Theorem. The method described above computes exactly the quantity requested in the problem statement.
Proof. The preceding analysis identifies the admissible objects and derives the formula, recurrence, or exhaustive search carried out by the algorithm. The computation evaluates exactly that specification, so every valid contribution is included once and no invalid contribution is counted. Therefore the returned value is the required answer.
Complexity
- Time: for precomputation, for the recursive walk. Total where .
- Space: .
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
/*
* Problem 220: Heighway Dragon
*
* L-system: Fa -> Fa R Fb, Fb -> Fa L Fb
* Start at origin facing up (north). Find position after 10^12 steps
* at expansion level 50.
*
* Approach: Recursive decomposition with precomputed displacement tables.
*
* Answer: 139776,963904
*/
const int MAXN = 51;
// Directions: 0=N(+y), 1=E(+x), 2=S(-y), 3=W(-x)
int dx_dir[4] = {0, 1, 0, -1};
int dy_dir[4] = {1, 0, -1, 0};
// For each level n, symbol s (0=a, 1=b), direction d:
// disp_x[n][s][d], disp_y[n][s][d] = net displacement
// rot[n][s] = net rotation (mod 4)
long long disp_x[MAXN][2][4];
long long disp_y[MAXN][2][4];
int rot[MAXN][2];
void precompute() {
// Base case: level 0
for (int s = 0; s < 2; s++) {
rot[0][s] = 0;
for (int d = 0; d < 4; d++) {
disp_x[0][s][d] = dx_dir[d];
disp_y[0][s][d] = dy_dir[d];
}
}
// Recursive case
for (int n = 1; n < MAXN; n++) {
// Fa^(n) = Fa^(n-1) R Fb^(n-1)
// rot(n, a) = rot(n-1, a) + 1 + rot(n-1, b) (mod 4)
rot[n][0] = (rot[n-1][0] + 1 + rot[n-1][1]) % 4;
// Fb^(n) = Fa^(n-1) L Fb^(n-1)
// rot(n, b) = rot(n-1, a) - 1 + rot(n-1, b) (mod 4)
rot[n][1] = (rot[n-1][0] - 1 + rot[n-1][1] + 4) % 4;
for (int d = 0; d < 4; d++) {
// Fa^(n) starting in direction d:
// First half: Fa^(n-1) in direction d
long long x1 = disp_x[n-1][0][d];
long long y1 = disp_y[n-1][0][d];
int d_after_first_a = (d + rot[n-1][0]) % 4;
// Turn right
int d_second_a = (d_after_first_a + 1) % 4;
// Second half: Fb^(n-1) in direction d_second_a
long long x2 = disp_x[n-1][1][d_second_a];
long long y2 = disp_y[n-1][1][d_second_a];
disp_x[n][0][d] = x1 + x2;
disp_y[n][0][d] = y1 + y2;
// Fb^(n) starting in direction d:
// First half: Fa^(n-1) in direction d
x1 = disp_x[n-1][0][d];
y1 = disp_y[n-1][0][d];
int d_after_first_b = (d + rot[n-1][0]) % 4;
// Turn left
int d_second_b = (d_after_first_b + 3) % 4; // +3 = -1 mod 4
// Second half: Fb^(n-1) in direction d_second_b
x2 = disp_x[n-1][1][d_second_b];
y2 = disp_y[n-1][1][d_second_b];
disp_x[n][1][d] = x1 + x2;
disp_y[n][1][d] = y1 + y2;
}
}
}
// Find position after k steps of symbol s at level n, starting at direction d
// Returns (x, y)
pair<long long, long long> query(int n, int s, int d, long long k) {
if (k == 0) return {0, 0};
if (n == 0) {
// Single step
return {dx_dir[d], dy_dir[d]};
}
long long half = 1LL << (n - 1);
// First half is always Fa^(n-1)
// Turn depends on symbol: R for a, L for b
// Second half is always Fb^(n-1)
if (k <= half) {
// Only in first half (Fa^(n-1))
return query(n - 1, 0, d, k);
} else {
// Full first half + turn + partial second half
long long x1 = disp_x[n-1][0][d];
long long y1 = disp_y[n-1][0][d];
int d_after = (d + rot[n-1][0]) % 4;
int turn = (s == 0) ? 1 : 3; // R=+1 for a, L=-1=+3 for b
int d_second = (d_after + turn) % 4;
auto [x2, y2] = query(n - 1, 1, d_second, k - half);
return {x1 + x2, y1 + y2};
}
}
int main() {
precompute();
long long steps = 1000000000000LL; // 10^12
int level = 50;
// Start at origin, facing north (direction 0), symbol Fa at level 50
auto [x, y] = query(level, 0, 0, steps);
cout << x << "," << y << endl;
return 0;
}
"""
Problem 220: Heighway Dragon
L-system: Fa -> Fa R Fb, Fb -> Fa L Fb
Start at origin (0,0) facing north (up).
Find position after 10^12 drawing steps at expansion level 50.
Approach: Precompute net displacement and rotation for each level/symbol/direction,
then recursively decompose the query.
Answer: 139776,963904
"""
import sys
sys.setrecursionlimit(100)
MAXN = 51
# Directions: 0=N(+y), 1=E(+x), 2=S(-y), 3=W(-x)
DX = [0, 1, 0, -1]
DY = [1, 0, -1, 0]
def solve():
# Precompute displacement and rotation tables
# disp_x[n][s][d], disp_y[n][s][d], rot[n][s]
disp_x = [[[0]*4 for _ in range(2)] for _ in range(MAXN)]
disp_y = [[[0]*4 for _ in range(2)] for _ in range(MAXN)]
rot = [[0]*2 for _ in range(MAXN)]
# Base case: level 0
for s in range(2):
rot[0][s] = 0
for d in range(4):
disp_x[0][s][d] = DX[d]
disp_y[0][s][d] = DY[d]
# Build up
for n in range(1, MAXN):
# Fa^(n) = Fa^(n-1) R Fb^(n-1)
rot[n][0] = (rot[n-1][0] + 1 + rot[n-1][1]) % 4
# Fb^(n) = Fa^(n-1) L Fb^(n-1)
rot[n][1] = (rot[n-1][0] - 1 + rot[n-1][1]) % 4
for d in range(4):
# Fa^(n)
x1 = disp_x[n-1][0][d]
y1 = disp_y[n-1][0][d]
d_after = (d + rot[n-1][0]) % 4
d_second = (d_after + 1) % 4 # R = +1
x2 = disp_x[n-1][1][d_second]
y2 = disp_y[n-1][1][d_second]
disp_x[n][0][d] = x1 + x2
disp_y[n][0][d] = y1 + y2
# Fb^(n)
x1 = disp_x[n-1][0][d]
y1 = disp_y[n-1][0][d]
d_after = (d + rot[n-1][0]) % 4
d_second = (d_after - 1) % 4 # L = -1
x2 = disp_x[n-1][1][d_second]
y2 = disp_y[n-1][1][d_second]
disp_x[n][1][d] = x1 + x2
disp_y[n][1][d] = y1 + y2
def query(n, s, d, k):
"""Position after k steps of symbol s at level n, starting direction d."""
if k == 0:
return (0, 0)
if n == 0:
return (DX[d], DY[d])
half = 1 << (n - 1)
if k <= half:
return query(n - 1, 0, d, k)
else:
x1 = disp_x[n-1][0][d]
y1 = disp_y[n-1][0][d]
d_after = (d + rot[n-1][0]) % 4
turn = 1 if s == 0 else -1 # R for a, L for b
d_second = (d_after + turn) % 4
x2, y2 = query(n - 1, 1, d_second, k - half)
return (x1 + x2, y1 + y2)
steps = 10**12
level = 50
x, y = query(level, 0, 0, steps)
print(f"{x},{y}")
if __name__ == "__main__":
solve()