dev only

State Machines: Logic

The Problem: Tracking State With Booleans Doesn't Scale

A subsystem often needs to remember "what am I currently doing" — an intake might be idle, actively intaking, holding a game piece, or scoring. Tracking that with a pile of booleans gets unmanageable fast:

private boolean isIdle;
private boolean isIntaking;
private boolean isHolding;
private boolean isScoring;

Nothing stops two of these from accidentally being true at the same time — a bug that's easy to introduce and hard to spot, since "impossible" combinations (isIdle and isScoring both true) compile just fine.

The Fix: One Enum, One Set of Named States

Enums (Ch.11) are exactly the tool for this — a fixed, named set of mutually exclusive states, with the compiler enforcing that a variable can only ever be one of them:

public enum IntakeState
{
    IDLE,
    INTAKING,
    HOLDING,
    SCORING
}

private IntakeState currentState = IntakeState.IDLE;

Now the subsystem's state is a single variable, and it's structurally impossible for it to be two states at once. This is a state machine: a system that's always in exactly one of a fixed set of named states, and moves between them according to defined rules.

Real Codebases Do This at Scale

This exact pattern — an enum representing a fixed set of named states for a subsystem — is how real, competitive FRC codebases (like Team 5817's own production code) structure their subsystems, just with more states and richer per-state behavior than this introductory example: an elevator's states might be tied to interpolated target heights that shift based on scoring distance, or a roller's states might each carry their own target voltage. The underlying idea is identical to IntakeState above — a state machine core built on an enum — dressed up with more sophisticated per-state data as a subsystem's real requirements grow.

What Comes Next

Having a named state is only half the picture — a state machine also needs logic that reads the current state and decides what to do (and when to move to a different state). That's the subject of Lesson 25.5.

Common Pitfalls

  • Reaching for several independent booleans instead of one enum. This lets impossible/contradictory combinations exist in the code, even though only one is ever supposed to be true — an enum makes that mistake structurally impossible.
  • Forgetting a state machine needs an initial value. currentState should always be initialized to a real starting state (like IDLE) — an uninitialized state variable is a null waiting to cause a NullPointerException (Ch.12/13) the first time anything reads it.

Key Takeaways

  • Tracking "what state am I in" with multiple independent booleans allows impossible combinations and doesn't scale.
  • A single enum field, initialized to a sensible starting value, makes a subsystem's current state both self-documenting and structurally guaranteed to be exactly one thing at a time.
  • This pattern — enum-based state machines for subsystems — is exactly what real, competitive FRC codebases use, just with richer per-state data as requirements grow.

Derived from other-reference-repo: team-5817-training/subsystems/02-state-based-servo-subsystems.md, 03-state-based-roller-subsystems.md (T5817 31.1-4) Deck context: mechacoder-test/src/lessons/java-2.js, slide 10 ("State Machines: Logic") — IntakeState enum example