How I Implemented HFSM In Python. Learning step-by-step implementation of... | by Debby Nirwan | Towards Data Science

I wanted to use the HFSM (Hierarchical Finite State Machine) in my Pacman AI Agent Implementation to fully understand the concepts and to compare it with the Behavior Tree. In my experience, after reading books and papers of some technical topics, my comprehension of the topics would greatly improve if I implemented them in code.
Pacman AI is written in Python and so I tried searching HFSM implementation on Github, but couldn’t find a good one for my implementation and decided to write one and release it on Github.
In this article, I want to share what I have learned when implementing it. We will see the step-by-step transformation from concepts to code. Hopefully, this is useful for you and encourages you to code and share your works.
If you are not familiar with HFSM yet, you may want to read my previous post below:
towardsdatascience.com
Let’s get started.
An HFSM consists of multiple FSMs, so we will start with implementing an FSM.
An FSM consists of the following building blocks:
State
Event
Transition
We start with the first building block, state. Here is one example of a state:
A state can be as simple as having just a name, but it can also have some actions associated with it such as entry, do, and exit. We can create a class of a state as follows.
Our state class simply has a name, which is important to distinguish one state from another which we use for equality check in the code snippet above.
We also have two lists of callbacks, for entry and exit which can be set by on_entry() and on_exit() functions. do action is not implemented because it is easier to implement it as an action in Transition which we will turn to in a moment.
start() and stop() functions are used by the transition later to enter and exit a state when a transition occurs.
The next building block is an Event, it only has a name, so it is quite simple.
The last building block in an FSM is a Transition. Transition is depicted by an arrow in the picture below.
It has four components:
EventConditionActionState(s)
Event
Condition
Action
State(s)
When an Event is raised, a Transition will occur if all of the following are satisfied:
The current state matches “state 1”
There is no condition set or the condition holds (returns True)
If the action exists, it will be executed.
There are three types of transitions:
Normal Transition: a transition from a state to another state, the one that we use most
Self Transition: a transition from and to itself
Null Transition: a transition that only executes the action, no entry or exit. This type of transition is useful for do action in a state
We can start by creating a base class of Transition:
We have everything we need for a Transition here, but the __call__() function is not implemented because it will be different for the three types.
Normal Transition will look like this, where a source state is exited and the destination state is entered.
Self Transition is as follows. There is no change in state, but all callbacks are called if any.
And a Null Transition is as follows:
We can see that a Null Transition is not really a transition because there are no exiting and entering states occurring. It only executes an action. If the action doesn’t exist, it basically does nothing.
Now that we have all the building blocks for an FSM, we are ready to implement the main class the StateMachine class.
An FSM (or, we simply call the class StateMachine) has a collection of:
States
Events
and, Transitions
It also has a name, because we may have multiple FSMs in HFSM and also has to track its current state.
The other things that we want to add are:
Exit State
Initial State
They are the two black circles in the picture. And finally, we also want to add an exit callback, for us to check the result of an FSM when it has finished.
That is the full implementation of an FSM. We have a couple of functions to add states, events, and transitions into the FSM.
In the add_xxx_transition() functions we return the transition object so that the caller can simply add condition and action like in the example below:
The most important function is trigger_event() which checks the conditions whether a transition can occur or not and if it is an exit state, whether an exit callback should be called or not.
That’s all we need for an FSM Implementation. Next, we turn our focus on the implementation of HFSM.
An example of HFSM is shown in the picture below, it is an HFSM version of the FSM we saw earlier.
To transform our FSM implementation into HFSM implementation we have to:
be able to add a child state machine to a statestart a child state machine when parent state is enteredstop a child state machine when parent state is exitedpropagate an event to the lower-level state machine(s)
be able to add a child state machine to a state
start a child state machine when parent state is entered
stop a child state machine when parent state is exited
propagate an event to the lower-level state machine(s)
First, we need to modify our State Class to support (1), (2), and (3):
And finally, we need to update the trigger_event() function in StateMachine implementation to be able to propagate an event.
Hopefully, this walkthrough is useful for you to see how I implemented HFSM from concepts to code and you can learn something from it. Just like I learned when I implemented it.
The full implementation can be seen in the repository hosted in Github below:
github.com
Or, if you want to use it, you can simply install it with
pip install hfsm
Thank you for reading!