DvdHx Blog

Human-generated content where available.

Alphabetic Fibonacci Spirals

I recently saw a LinkedIn post on my feed about AI that sparked my interest. The challenge was to replicate a Fibonacci spiral in ASCII from a Wikipedia screenshot without copying the diagram. Challenge accepted.

At first, I tried a very naive prompt, and my agent spun for a good twenty minutes in thinking mode, spouting gibberish with no solution in sight. I had even attempted to prompt a vision model that understood the diagram and pretended to understand the math, but it was hopeless. There were no ready-made solutions in the LLM's training data. Or, as a bystander LLM commented, "the instruction density was too high, causing the attention mechanism to drift from the core constraints."

I do most of my LLM research using local agents connected to local models on a home-built computer, and I saw this as a chance to flex my local LLM skills. Unlimited tokens, albeit a bit slow at times. My initial prompt was too logically heavy for the model, and I began refactoring it through several iterations, unsuccessfully, until I started breaking the solution space into smaller problems.

My first realization was that the Fibonacci spiral projected onto a grid required a maximum grid size of N by N-1, where N was the Nth Fibonacci number. I had my local LLM generate a program to fill a grid of this size with dots. (I also realized that the grid area would flip its dimensions of N-1 and N for even values of N.) With these constraints in mind, I prompted the agent to create the first iteration of the program, a forty-line Python program that took N on the command line.

#!/usr/bin/env python3
import sys

def fibonacci(n):
    """Calculate the nth Fibonacci number"""
    if n <= 0:
        return 0
    elif n == 1:
        return 1
    else:
        a, b = 0, 1
        for _ in range(2, n + 1):
            a, b = b, a + b
        return b

def draw_box(width, height):
    for y in range(height):
        for x in range(width):
            print(".", end="")
        print()

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <n>")
        sys.exit(1)

    try:
        n = int(sys.argv[1])
    except ValueError:
        print("Error: Argument must be an integer")
        sys.exit(1)

    x = fibonacci(n)
    y = fibonacci(n + 1)

    # If N is even, flip X and Y
    if n % 2 == 0:
        x, y = y, x

    draw_box(x, y)
% python3 fibonacci_box.py 4
.....
.....
.....
% python3 fibonacci_box.py 3
..
..
..
% python3 fibonacci_box.py 2
..
% python3 fibonacci_box.py 1
.

While this technically contained the Fibonacci spiral within the Golden rectangle, it was not very convincing (because it did not attempt to differentiate the cells), which was part of the beauty of the original diagram. I analyzed the placement of the squares for small values of N and discovered that the spiral repeated its directional changes: East, North, West, and South. I also found a simple recursive relationship for that placement of squares, labeling the squares of subsequent iterations with letters of the alphabet starting with N=2. I would leave the first dot, and then fill the second cell with an A, the third set of cells with B, etc. By replacing the difference between the output of N, N-1, N-2, ..., 1 with the next letter of the alphabet, I could create an alphabetic Fibonacci spiral.

Iteration Fibonacci Matrix
N = 1 .
N = 2 .A
N = 3 BB,BB,.A
N = 4 CCCBB,CCCBB,CCC.A
N = 5 CCCBB,CCCBB,CCC.A,DDDDD,DDDDD,DDDDD,DDDDD,DDDDD

Then I created a prompt with these multi-shot examples and my spiral directionality findings, and posed the program as a puzzle for the LLM to solve. After an hour or so of churning, the agent finally did.

% python3 alpha_spiral.py 6
CCCBBEEEEEEEE
CCCBBEEEEEEEE
CCC.AEEEEEEEE
DDDDDEEEEEEEE
DDDDDEEEEEEEE
DDDDDEEEEEEEE
DDDDDEEEEEEEE
DDDDDEEEEEEEE

Initially, I had imagined the agent would get caught in the same rabbit hole as my original, naive prompt. Still, I watched as its reasoning mode understood my prompt, and it generalized to arbitrary N.

% python3 alpha_spiral.py 8
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGCCCBBEEEEEEEE
GGGGGGGGGGGGGGGGGGGGGCCCBBEEEEEEEE
GGGGGGGGGGGGGGGGGGGGGCCC.AEEEEEEEE
GGGGGGGGGGGGGGGGGGGGGDDDDDEEEEEEEE
GGGGGGGGGGGGGGGGGGGGGDDDDDEEEEEEEE
GGGGGGGGGGGGGGGGGGGGGDDDDDEEEEEEEE
GGGGGGGGGGGGGGGGGGGGGDDDDDEEEEEEEE
GGGGGGGGGGGGGGGGGGGGGDDDDDEEEEEEEE

Because ASCII characters are taller than they are wide, the grid does not exactly match the Wikipedia version. However, the LLM produced a seventy-nine-line program that creates the alphabet spiral for any value of N.

#!/usr/bin/env python3
"""
rebox - Recursively builds boxes based on fibonacci_box.py N output

Each level N adds new cells that get filled with the next letter.
Directions cycle: EAST (N=2), NORTH (N=3), WEST (N=4), SOUTH (N=5), repeat
"""
import sys

def fibonacci(n):
    if n <= 0: return 0
    if n == 1: return 1
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

def get_dims(n):
    """Get width and height as fibonacci_box.py N"""
    w, h = fibonacci(n), fibonacci(n + 1)
    if n % 2 == 0:
        w, h = h, w
    return w, h

def get_prev_placement(n):
    """Determine where to place N-1 result in current grid"""
    cur_w, cur_h = get_dims(n)
    prev_w, prev_h = get_dims(n - 1)
    
    if n % 4 == 2:        # EAST: N=2,6,10,... - prev at LEFT
        return (0, 0)
    elif n % 4 == 3:      # NORTH: N=3,7,11,... - prev at BOTTOM
        return (cur_h - prev_h, 0)
    elif n % 4 == 0:      # WEST: N=4,8,12,... - prev at RIGHT
        return (0, cur_w - prev_w)
    else:                 # SOUTH: N=5,9,13,... - prev at TOP
        return (0, 0)

def solve(n):
    if n == 1:
        return ['.']
    
    cur_w, cur_h = get_dims(n)
    prev_result = solve(n - 1)
    prev_w, prev_h = get_dims(n - 1)
    row_off, col_off = get_prev_placement(n)
    
    letter = chr(ord('A') + n - 2)
    
    # All dots initially
    result = [['.' for _ in range(cur_w)] for _ in range(cur_h)]
    
    # Place N-1 result
    for r in range(prev_h):
        for c in range(prev_w):
            rr, cc = r + row_off, c + col_off
            if rr < cur_h and cc < cur_w:
                result[rr][cc] = prev_result[r][c]
    
    # Fill only new cells (not from N-1)
    for r in range(cur_h):
        for c in range(cur_w):
            in_prev = (row_off <= r < row_off + prev_h and 
                      col_off <= c < col_off + prev_w)
            if not in_prev:
                result[r][c] = letter
    
    return result

def main():
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <N>")
        sys.exit(1)
    n = int(sys.argv[1])
    for row in solve(n):
        print(''.join(row))

if __name__ == '__main__':
    main()

Here we see the output from when N=10.
Alphabetic spiral at N=10