Books · The Fiddler: Solutions
Chapter 21
Can You Hop in a Spiral?
Frankie the frog hops on a vast hexagonal lattice of lily pads, each pad of diameter , so neighbouring centres sit apart. Starting from pad , she hops to neighbouring pads in a counterclockwise spiral, subject to two rules: each pad must be in a more counterclockwise direction from than the previous one, and each pad must be farther from than the previous one. After spiralling around, she finds herself once again due east of . How close to can she be?
The Fiddler, Zach Wissner-Gross, January 30, 2026(original post)
Solution
She can be no closer than
The two rules say her bearing from strictly increases and her distance from strictly increases at every hop. To come back due east she must wind her bearing all the way from round to . The lattice has six-fold symmetry, with pads lined up along the six rays at . Crossing from one ray to the next, the tightest legal corner Frankie can turn, still gaining both bearing and distance, is a –– triangle, which doubles her distance from . Six such sectors carry her once around, multiplying her distance by : starting one pad out, she returns due east no nearer than .
The computation
Encode the two rules on the lattice: process pads in order of distance from (a valid order, since each legal hop strictly increases distance) and mark a pad reachable if some marked neighbour hops to it while gaining both distance and counterclockwise bearing. The nearest reachable due-east pad past the start is .
import numpy as np
from math import atan2, pi, hypot
S3 = np.sqrt(3)/2
NB = [(1,0),(-1,0),(0,1),(0,-1),(1,-1),(-1,1)] # 6 lattice neighbours
def rad(a, b): return hypot(a + b/2, b*S3)
def prin(a, b):
t = atan2(b*S3, a + b/2); return t if t >= 0 else t + 2*pi
def ccw(P, Q): # bearing strictly increases
return ((prin(*Q) - prin(*P) + pi) % (2*pi) - pi) > 1e-9
RMAX = 80.0; M = int(RMAX) + 2
pts = sorted([(a, b) for a in range(-M, M+1) for b in range(-M, M+1)
if 1e-9 < rad(a, b) <= RMAX], key=lambda p: rad(*p))
reach = {(1, 0)}
for P in pts: # radius order = valid topological order
if P not in reach: continue
for da, db in NB:
Q = (P[0]+da, P[1]+db)
if rad(*Q) > rad(*P)+1e-9 and rad(*Q) <= RMAX and ccw(P, Q): reach.add(Q)
east = sorted(p[0] for p in reach if p[1] == 0 and p[0] > 1)
print(east[0]) # 64
Extra Credit
Frankie stores food on pad . Every second, the food on each pad splits into six equal portions that jump to its six neighbours. After how many seconds does pad hold less than percent of the original amount?
Solution
The fraction left on after seconds is exactly the probability that a random walk on the lattice, stepping to a uniformly chosen neighbour each second, sits back at its start after steps. Propagating the distribution second by second, this return fraction falls below percent for the first time at At it is still , at it is . (The source’s value is behind its paywall; this is my own.)
The computation
Diffuse the food directly: hold the amount on every pad and, each second, split each pad’s amount evenly to its six neighbours. Watch the amount back on until it drops below percent.
from collections import defaultdict
cur = {(0, 0): 1.0}
for N in range(1, 30):
nxt = defaultdict(float)
for (a, b), m in cur.items():
for da, db in NB: nxt[(a+da, b+db)] += m/6
cur = nxt
if N > 2 and cur[(0, 0)] < 0.01:
print(N, round(cur[(0, 0)], 5)); break # 28 0.00967