Having spent the better part of 16 years creating complex drawing sets for various trades in the AEC industry where I not only created their complex 3D models but the sheets which were submitted for permit, or shop/field install drawings. I have had the privilege of working with Experts in their trades and I have built a career on using the software on their behalf and automating the software to do what their expertise prescribes.
When I started building my FSM_API, it began as an offshoot of a hobby, building Starcraft 2 bots. Frustrated with the complex commands and controls available in this domain, I explored all sorts of options, eventually realizing the absolute necessity for Finite State Machines.
However, the typical way of doing this—what we learn in the forums or watch in the tutorials—is itself valid, yet quite lacking. As I've also, as part of my career, driven efficiency up everywhere I work by analyzing complex workflows and identifying the bottlenecks, just like debugging.
The Problem with Traditional FSMs
The final piece of the puzzle clicked when we implement an FSM: we don't want to be blocked by having to always ensure it follows proper sequence, we don't want to have to provide similar or the same implementations, and most importantly, we want to be able to apply an FSM's behavior to as many contexts as possible. Here I recognized the need for a central abstraction, what I call the "Universal Unit of Work" which I believe I took from college math, but that was decades ago, so don't quote me on it.
This fundamental realization led to the core design principles of the FSM_API
, which solves these issues by decoupling the FSM logic from its data, a concept referred to as the Context. This allows a single FSM blueprint to be instantiated and applied to many different objects or systems, streamlining development and ensuring consistency across an entire application. The core of this abstraction is that a domain expert, regardless of their coding background, can describe a complex process, which can then be directly translated into a functional FSM.
The shocker to me was that we can have an expert do something like this:
This diagram represents the expertise of a professional in their trade. They can capture every state, condition, and transition necessary to describe a complex workflow. By using a fluent, human-readable API, we can empower them to define this logic directly.
Translating Expertise into Code
The complex diagram above, which a typical coder would spend hours trying to replicate with convoluted if/else
statements, can be translated into a clean and maintainable C# code block using the FSM_API
. The API's fluent interface and clear method names allow for a near-direct transcription of the domain expert's diagram into functional, debuggable code.
The following code shows a comprehensive implementation of a Car Transmission FSM, demonstrating the power of the FSMBuilder
to define a full domain of knowledge in a single, cohesive unit. Each state's actions (onEnter
, onUpdate
, onExit
) are defined along with the specific transitions that connect them. This includes complex logic for gear shifting and critical safety protocols like the Fault
state.
using System;
using TheSingularityWorkshop.FSM_API;
// 1. Define the Context Class
// This "data bag" holds all the real-time information the FSM needs to make decisions.
public class CarContext : IStateContext
{
public float Speed { get; set; } = 0.0f;
public bool BrakePedalPressed { get; set; } = false;
public bool ThrottleDown { get; set; } = false;
public bool EngineFault { get; set; } = false;
}
// 2. Define the FSM
// This class contains the blueprint for the entire car transmission system.
public static class CarTransmissionFSM
{
public static void CreateComplexFSM()
{
FSM_API.Create.CreateFiniteStateMachine("CarFSM", -1, "VehicleUpdate")
// --- Define Top-Level States ---
.State("Park",
onEnter: (ctx) => Console.WriteLine("Transmission is in Park."),
onUpdate: (ctx) => { /* Check for input */ },
onExit: (ctx) => Console.WriteLine("Exiting Park."))
.State("Reverse",
onEnter: (ctx) => Console.WriteLine("Transmission is in Reverse."),
onUpdate: (ctx) => { /* Reverse logic */ },
onExit: (ctx) => Console.WriteLine("Exiting Reverse."))
.State("Neutral",
onEnter: (ctx) => Console.WriteLine("Transmission is in Neutral."),
onUpdate: (ctx) => { /* Idle engine logic */ },
onExit: (ctx) => Console.WriteLine("Exiting Neutral."))
.State("Fault",
onEnter: (ctx) =>
{
Console.WriteLine("CRITICAL FAULT: Transitioning to Fault state.");
// Simulate a critical safety action: disable the engine
((CarContext)ctx).EngineFault = true;
},
onUpdate: (ctx) => Console.WriteLine("In Fault mode. Cannot proceed."),
onExit: (ctx) => Console.WriteLine("Fault has been manually reset."))
// --- Define Drive Sub-States (Gears) ---
.State("First_Gear",
onEnter: (ctx) => Console.WriteLine("Shifted into First Gear."),
onUpdate: (ctx) => { /* Apply torque for first gear */ },
onExit: (ctx) => { /* Prepare for shift up */ })
.State("Second_Gear",
onEnter: (ctx) => Console.WriteLine("Shifted into Second Gear."),
onUpdate: (ctx) => { /* Apply torque for second gear */ },
onExit: (ctx) => { /* Prepare for shift */ })
.State("Third_Gear",
onEnter: (ctx) => Console.WriteLine("Shifted into Third Gear."),
onUpdate: (ctx) => { /* Apply torque for third gear */ },
onExit: (ctx) => { /* Prepare for shift */ })
.State("Fourth_Gear",
onEnter: (ctx) => Console.WriteLine("Shifted into Fourth Gear."),
onUpdate: (ctx) => { /* Apply torque for fourth gear */ },
onExit: (ctx) => { /* Prepare for shift */ })
// --- Set Initial State ---
.WithInitialState("Park")
// --- Define Transitions ---
// Top-level transitions (P, R, N, D)
.Transition("Park", "Reverse", (ctx) => ((CarContext)ctx).BrakePedalPressed)
.Transition("Park", "First_Gear", (ctx) => ((CarContext)ctx).BrakePedalPressed)
.Transition("Reverse", "Park", (ctx) => !((CarContext)ctx).BrakePedalPressed)
.Transition("Reverse", "Neutral", (ctx) => true) // Always can shift from R to N
.Transition("Neutral", "Park", (ctx) => true)
.Transition("Neutral", "Reverse", (ctx) => ((CarContext)ctx).BrakePedalPressed)
.Transition("Neutral", "First_Gear", (ctx) => ((CarContext)ctx).BrakePedalPressed)
// A transition from any Drive gear back to Neutral or Park
.AnyTransition("Neutral", (ctx) => !((CarContext)ctx).BrakePedalPressed && ((CarContext)ctx).Speed < 1.0f)
.AnyTransition("Park", (ctx) => !((CarContext)ctx).BrakePedalPressed && ((CarContext)ctx).Speed < 1.0f)
// Drive sub-state transitions (Gear Shifting)
.Transition("First_Gear", "Second_Gear", (ctx) => ((CarContext)ctx).Speed > 15.0f && !((CarContext)ctx).ThrottleDown)
.Transition("Second_Gear", "Third_Gear", (ctx) => ((CarContext)ctx).Speed > 30.0f)
.Transition("Third_Gear", "Fourth_Gear", (ctx) => ((CarContext)ctx).Speed > 45.0f)
// Downshifting transitions
.Transition("Fourth_Gear", "Third_Gear", (ctx) => ((CarContext)ctx).Speed < 40.0f)
.Transition("Third_Gear", "Second_Gear", (ctx) => ((CarContext)ctx).Speed < 25.0f)
.Transition("Second_Gear", "First_Gear", (ctx) => ((CarContext)ctx).Speed < 10.0f || ((CarContext)ctx).ThrottleDown)
// "Any-State" transitions for safety and error handling
.AnyTransition("Fault", (ctx) => ((CarContext)ctx).EngineFault)
.AnyTransition("Park", (ctx) => ((CarContext)ctx).BrakePedalPressed && ((CarContext)ctx).Speed < 1.0f)
// Finalize the FSM blueprint
.BuildDefinition();
}
}
The Paradigm Shift
Now if we are able to get to this point where an expert has been able to input their domain of expertise into FSMs, then we only need a competent coder to follow behind implementing the scaffolded code. This changes everything! The complex, abstract logic that once resided solely within the mind of the expert is now a tangible, version-controlled asset. It becomes a unified logical architecture that is easily accessible and maintainable, bridging the gap between domain knowledge and software implementation. The FSM_API
proves that by building the right tools, we can empower experts to define their complex worlds with ease.
Get the FSM_API
You can access and support the FSM_API
at the following links:
-
GitHub Repository:
https://github.com/trentbest/fsm_api
-
NuGet Package:
https://www.nuget.org/packages/TheSingularityWorkshop.FSM_API/
-
PayPal:
https://www.paypal.com/paypalme/TheSingularityWorkshop
Top comments (0)