Skip to content

Instantly share code, notes, and snippets.

@eugeniop
Created August 27, 2012 05:25
Show Gist options
  • Save eugeniop/3485809 to your computer and use it in GitHub Desktop.
Save eugeniop/3485809 to your computer and use it in GitHub Desktop.
A simple state machine
public class State<S, E>
{
private readonly List<StateEntry<S,E>> entries;
public State()
{
this.entries = new List<StateEntry<S,E>>();
}
public StateEntry<S,E> When(Func<E, bool> selector)
{
var e = new StateEntry<S, E>() { Selector = selector, State = this};
this.entries.Add(e);
return e;
}
public Func<E, S> FindFirstOrDefaultAction(E @event)
{
var t = this.entries.FirstOrDefault(f => f.Selector(@event));
if (t == null)
return null;
return t.Action;
}
}
public class StateEntry<S, E>
{
public State<S, E> State { get; set; }
public Func<E, bool> Selector { get; set; }
public Func<E, S> Action { get; set; }
public State<S, E> Then(Func<E, S> action)
{
this.Action = action;
return this.State;
}
public State<S, E> Ignore()
{
return this.State;
}
}
public class StateMachine<S,E>
{
public StateMachine()
{
this.machine = new Dictionary<S, State<S,E>>();
}
public S CurrentState { get; set; }
public State<S,E> AddState(S state)
{
var s = new State<S,E>();
machine.Add(state,s);
return s;
}
public void Process(E @event)
{
var action = machine[this.CurrentState]
.FindFirstOrDefaultAction(@event);
if (action != null)
{
this.CurrentState = action(@event);
}
}
private readonly Dictionary<S, State<S, E>> machine;
}
[TestClass]
public class StateMachineTests
{
private enum states { Idle, Active }
private bool EventOne(int i)
{
return i == 1;
}
private bool EventTwo(int i)
{
return i == 2;
}
private bool Any(int i)
{
return true;
}
[TestMethod]
public void SimpleStateMachineWithTwoStates()
{
var sm = new StateMachine<states, int>();
sm.AddState(states.Idle)
.When(EventOne).Then(i => states.Idle)
.When(EventTwo).Then(i => states.Active)
.When(Any).Then(i => states.Idle);
sm.AddState(states.Active)
.When(EventOne).Then(i => states.Active)
.When(EventTwo).Then(i => states.Idle)
.When(Any).Then(i => states.Active);
sm.CurrentState = states.Idle;
sm.Process(1);
Assert.AreEqual(sm.CurrentState, states.Idle);
sm.Process(2);
Assert.AreEqual(sm.CurrentState, states.Active);
sm.Process(45);
Assert.AreEqual(sm.CurrentState, states.Active);
sm.Process(2);
Assert.AreEqual(sm.CurrentState, states.Idle);
sm.Process(98);
Assert.AreEqual(sm.CurrentState, states.Idle);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment