Skip to content

Instantly share code, notes, and snippets.

@W4RH4WK
Last active April 14, 2024 13:16
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save W4RH4WK/5dea8f55532e0526da8b6e60c566c259 to your computer and use it in GitHub Desktop.
Save W4RH4WK/5dea8f55532e0526da8b6e60c566c259 to your computer and use it in GitHub Desktop.
C# Generic Finite State Machine
using System;
using System.Collections.Generic;
using System.Reflection;
namespace GenericFSM {
public class FSM<T> where T : struct, IConvertible {
public T State { get; private set; }
private const BindingFlags FLAGS = BindingFlags.NonPublic | BindingFlags.Instance;
private IDictionary<T, MethodInfo> states = new Dictionary<T, MethodInfo>();
private IDictionary<T, MethodInfo> transitions = new Dictionary<T, MethodInfo>();
public FSM(T init) {
if(!typeof(T).IsEnum) {
throw new ArgumentException("T must be an enumeration");
}
// Cache state and transition functions
foreach(T value in typeof(T).GetEnumValues()) {
var s = GetType().GetMethod(value.ToString() + "State", FLAGS);
if(s != null) {
states.Add(value, s);
}
var t = GetType().GetMethod(value.ToString() + "Transition", FLAGS);
if(t != null) {
transitions.Add(value, t);
}
}
State = init;
}
public void Transition(T next) {
MethodInfo method;
if(transitions.TryGetValue(next, out method)) {
method.Invoke(this, new object[] { State });
}
State = next;
}
public void StateDo() {
MethodInfo method;
if(states.TryGetValue(State, out method)) {
method.Invoke(this, null);
}
}
}
}
using System;
namespace GenericFSM {
enum MyStates { Init, Foo, Bar, Empty, Finish };
class MyFSM : FSM<MyStates> {
public MyFSM() : base(MyStates.Init) { }
void InitTransition(MyStates prev) {
Console.Out.WriteLine(prev.ToString() + " -> Init");
}
void InitState() {
Console.Out.WriteLine("Init State");
Transition(MyStates.Foo);
}
void FooTransition(MyStates prev) {
Console.Out.WriteLine(prev.ToString() + " -> Foo");
}
void FooState() {
Console.Out.WriteLine("Foo State");
Transition(MyStates.Bar);
}
// Simply omit empty transitions
void BarState() {
Console.Out.WriteLine("Bar State");
Transition(MyStates.Finish);
}
// Simply omit empty states
void FinishTransition(MyStates prev) {
Console.Out.WriteLine(prev.ToString() + " -> Finish");
Transition(MyStates.Bar);
}
void FinishState() {
Console.Out.WriteLine("Finish State");
Environment.Exit(0);
}
}
class Program {
static void Main(string[] args) {
MyFSM fsm = new MyFSM();
// Trigger first transition manually (if needed)
fsm.Transition(MyStates.Init);
// Have Fun
while(true) {
fsm.StateDo();
}
}
}
}
@Monsoonexe
Copy link

It's so simple it's beautiful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment