What State Is the Marble In Right Now?
When I’m sketching the marble run at midnight—tubes leaning against tubes, cardboard ramps at angles I’m barely confident about—I’m not thinking about code. I’m thinking: where does this marble go next? Which is already the language of state machines.
A marble doesn’t exist in every location at once. It’s at a location, in a location, doing one thing: resting, rolling, bouncing, airborne. At each instant it’s in exactly one state. And from that state, only certain transitions are possible. A marble at rest won’t spontaneously fly; a marble rolling on a flat surface won’t stop unless it hits something. Gravity enforces the rule.
Mealy and Moore formalized this in the 1950s—the idea that a system’s behaviour can be modelled as a set of discrete states, and the transitions between them depend on inputs. The marble run is their model made physical: each peg a trigger, each angle a constraint on which next-state is reachable. Design the pegs and ramps, and you’re implicitly designing the state machine. Place the marble at the top in the rest state, release it—release is the action—and it transitions to rolling. Hits a peg? Transitions to bouncing. Lands back on the track? Rolls again.
When you want to think clearly about whether a particular configuration will work, you don’t calculate vectors. You trace state sequences: rest → rolling → bouncing → rolling → airborne → rest. If a sequence is impossible given the physics you’ve built, the design breaks. If every sequence reaches a valid end state, the run works.
In Scheme, a state machine is just a function that looks at the current state and the input action, and returns the next state:
(define (marble-state current action)
(case current
((rest) (if (eq? action 'release) 'rolling 'rest))
((rolling) (if (eq? action 'hit-peg) 'bouncing 'rolling))
((bouncing) (if (eq? action 'land-track) 'rolling
(if (eq? action 'fall-off) 'airborne 'bouncing)))
((airborne) (if (eq? action 'land) 'rest 'airborne))))
(marble-state 'rolling 'hit-peg) ; → bouncing
(marble-state 'bouncing 'fall-off) ; → airborne
Java’s approach is more verbose but it scales to complexity—you give each state its own transition logic, and the state itself “knows” what’s possible:
enum MarbleState {
REST { public MarbleState on(String action) {
return "release".equals(action) ? ROLLING : REST; } },
ROLLING { public MarbleState on(String action) {
return "hit-peg".equals(action) ? BOUNCING : ROLLING; } },
BOUNCING { public MarbleState on(String action) {
if ("land".equals(action)) return ROLLING;
if ("fall".equals(action)) return AIRBORNE;
return BOUNCING; } },
AIRBORNE { public MarbleState on(String action) {
return "land".equals(action) ? REST : AIRBORNE; } };
abstract MarbleState on(String action);
}
Walking back inside at 2 AM, I’ve got cardboard everywhere and a marble that successfully rolled, bounced, and fell into the catch at the bottom. I didn’t think about it as a state machine. But that’s exactly what I built.