Skip to content

Instantly share code, notes, and snippets.

@johnlcox
Last active February 9, 2017 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnlcox/ae9443c3c16e8d691123cc4c712ca795 to your computer and use it in GitHub Desktop.
Save johnlcox/ae9443c3c16e8d691123cc4c712ca795 to your computer and use it in GitHub Desktop.
Event Driven State Machine
public class Example
{
public enum States { One, Two }
public interface IExampleMessage
{
}
public class OneMessage : IExampleMessage
{
public string one = "one";
}
public class TwoMessage : IExampleMessage
{
public string two = "two";
}
public class MyStateMachine : StateMachine<States, IExampleMessage>
{
protected override void Configure()
{
When(States.One, state =>
{
state.Receive<OneMessage>(message =>
{
Console.WriteLine(message.one);
});
});
When(States.Two, state =>
{
state.Receive <TwoMessage>(message =>
{
Console.WriteLine(message.two);
});
});
}
}
}
using System;
using System.Collections.Generic;
namespace SparrowHawk.Scripts.FSM
{
public abstract class StateMachine<TS, TM>
{
private readonly Dictionary<TS, Behaviour> _states = new Dictionary<TS, Behaviour>();
private TS _initialState;
private Behaviour _currentBehaviour;
protected abstract void Configure();
protected void StartWith(TS state)
{
_initialState = state;
}
protected void When(TS state, Action<Behaviour> stateHandler)
{
if (_states.ContainsKey(state)) throw new InvalidOperationException();
Behaviour behaviour = new Behaviour(this);
stateHandler(behaviour);
_states.Add(state, behaviour);
}
protected void OnReceive(TM message)
{
if (_currentBehaviour == null && _initialState == null) throw new InvalidOperationException();
if (_currentBehaviour == null && !_states.ContainsKey(_initialState)) throw new InvalidOperationException();
if (_currentBehaviour == null)
{
_currentBehaviour = _states[_initialState];
}
_currentBehaviour.OnReceive(message);
}
private void TransitionTo(TS state)
{
if (!_states.ContainsKey(state)) throw new InvalidOperationException();
_currentBehaviour = _states[state];
}
protected class Behaviour
{
// TODO: Add real predicate/guard support
private static readonly Predicate<TM> TruePredicate = _ => true;
private readonly StateMachine<TS, TM> _stateMachine;
private readonly Dictionary<Type, PredicateAndAction> _receiveHandlers =
new Dictionary<Type, PredicateAndAction>();
public Behaviour(StateMachine<TS, TM> stateMachine)
{
_stateMachine = stateMachine;
}
internal void OnReceive(TM message)
{
PredicateAndAction predicateAndAction;
if (!_receiveHandlers.TryGetValue(message.GetType(), out predicateAndAction)) return;
if (((Predicate<TM>) predicateAndAction.Predicate)(message))
{
((Action<TM>) predicateAndAction.Action)(message);
}
}
public void Receive<TMu>(Action<TMu> handler) where TMu : class, TM, new()
{
var handlerType = typeof(TMu);
_receiveHandlers.Add(handlerType, new PredicateAndAction(TruePredicate, handler));
}
public void TransitionTo(TS state)
{
_stateMachine.TransitionTo(state);
}
private class PredicateAndAction
{
private readonly object _predicate;
private readonly object _action;
public PredicateAndAction(object predicate, object action)
{
_predicate = predicate;
_action = action;
}
public object Predicate
{
get { return _predicate; }
}
public object Action
{
get { return _action; }
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment