Finite State Machine

A finite state machine (FSM) has a bunch of states that it can be in, and while in a certain state, it transitions to other states based on inputs (events/actions) received while in that state.

graph LR;
    State1 -->|input| State2;
    State2 -->|input| State3;
    State2 -->|input2| State4;

In Software

In software, certain things can be modeled as FSMs, and when done so, makes their implementation/maintenance/extensibility significantly easier.

Examples include:

  • User interface navigation (e.g. different screens/states)
  • Game character behavior (e.g. idle, walking, jumping)
  • Protocols (e.g. TCP connection states)

GoF State Pattern

The Gang of Four (GoF) State Pattern is one implementation of a FSM in software.

First, identify (methods) that depend on state. This is usually input/event handlers, and process()/update() type of methods. Some examples:

  • on_key_pressed()
  • on_mouse_clicked()
  • update(dt)
  • on_message_received()

Then create an interface with these methods.

from abc import ABC, abstractmethod

class HeroState(ABC):
    @abstractmethod
    def on_key_pressed(self):
        pass

    @abstractmethod
    def update(self, dt):
        pass

Then, have the main object hold a reference to its current state. It should delegate state-dependent methods to its current state.

class Hero:
    def __init__(self):
        self._state = None

    def on_key_pressed(self):
        self._state.on_key_pressed()

    def update(self, dt):
        self._state.update(dt)

Generally, you want each State to have an enter() and exit() method to initialize/cleanup when that state is entered or exited.

You also usually want the Context class (Hero) to have a set_state() method. When a particular State, wants to change the state of the Context, it can call set_state() on the context. Obviously, this means each State needs a reference to the Context.

class HeroState(ABC):
    def __init__(self, context: Hero):
        self._context = context

    @abstractmethod
    def enter(self):
        pass

    @abstractmethod
    def exit(self):
        pass

    @abstractmethod
    def on_key_pressed(self):
        pass

    @abstractmethod
    def update(self, dt):
        pass
class Hero:
    def __init__(self):
        self._state = None

    def set_state(self, state: HeroState):
        if self._state:
            self._state.exit()
        self._state = state
        self._state.enter()

    def on_key_pressed(self):
        self._state.on_key_pressed()

    def update(self, dt):
        self._state.update(dt)

In GoF State Pattern, you encapsulate the data/behavior of each state in its own class.

Further Reading

This article was based on game programming patterns - state