diff --git a/Demo/EvenState.cs b/Demo/EvenState.cs index 427e216..c23c533 100644 --- a/Demo/EvenState.cs +++ b/Demo/EvenState.cs @@ -2,11 +2,11 @@ class EvenState : State { - protected override void Enter(Dictionary vars) { } + protected override void Enter(IDictionary vars) { } - protected override void Exit(Dictionary vars) { } + protected override void Exit(IDictionary vars) { } - protected override void Inner(Dictionary vars) + protected override void Inner(IDictionary vars) { Console.WriteLine(" is even"); } diff --git a/Demo/IncrementState.cs b/Demo/IncrementState.cs index d024d7f..1460366 100644 --- a/Demo/IncrementState.cs +++ b/Demo/IncrementState.cs @@ -3,11 +3,11 @@ class IncrementState : State { - protected override void Enter(Dictionary vars) { } + protected override void Enter(IDictionary vars) { } - protected override void Exit(Dictionary vars) { } + protected override void Exit(IDictionary vars) { } - protected override void Inner(Dictionary vars) + protected override void Inner(IDictionary vars) { vars["x"]++; } diff --git a/Demo/InitState.cs b/Demo/InitState.cs deleted file mode 100644 index 0cc6c89..0000000 --- a/Demo/InitState.cs +++ /dev/null @@ -1,13 +0,0 @@ -using RoadieRichStateMachine; - -class InitState : State -{ - protected override void Enter(Dictionary vars) - { - vars["x"] = 0; - } - - protected override void Exit(Dictionary vars) { } - - protected override void Inner(Dictionary vars) { } -} diff --git a/Demo/MainState.cs b/Demo/MainState.cs index fcd85ba..2a23d84 100644 --- a/Demo/MainState.cs +++ b/Demo/MainState.cs @@ -2,11 +2,11 @@ class MainState : State { - protected override void Enter(Dictionary vars) { } + protected override void Enter(IDictionary vars) { } - protected override void Exit(Dictionary vars) { } + protected override void Exit(IDictionary vars) { } - protected override void Inner(Dictionary vars) + protected override void Inner(IDictionary vars) { Console.Out.Write(vars["x"]); } diff --git a/Demo/OddState.cs b/Demo/OddState.cs index f29d6fa..6e63010 100644 --- a/Demo/OddState.cs +++ b/Demo/OddState.cs @@ -2,11 +2,11 @@ class OddState : State { - protected override void Enter(Dictionary vars) { } + protected override void Enter(IDictionary vars) { } - protected override void Exit(Dictionary vars) { } + protected override void Exit(IDictionary vars) { } - protected override void Inner(Dictionary vars) + protected override void Inner(IDictionary vars) { Console.WriteLine(" is odd"); } diff --git a/Demo/Program.cs b/Demo/Program.cs index df97572..12f3e5d 100644 --- a/Demo/Program.cs +++ b/Demo/Program.cs @@ -31,14 +31,11 @@ using (var classSM = new StateMachine()) { - var initState = new InitState(); var mainState = new MainState(); var oddState = new OddState(); var evenState = new EvenState(); var incrementState = new IncrementState(); - initState.AddTransitionTo(mainState, null); - mainState.AddTransitionTo(oddState, (vars) => vars["x"] % 2 == 1); mainState.AddTransitionTo(evenState, (vars) => vars["x"] % 2 == 0); @@ -49,6 +46,8 @@ incrementState.AddTransitionTo(StateMachine.ExitState, (vars) => vars["x"] > 10); incrementState.AddTransitionTo(mainState, null); - classSM.InitialState = initState; - classSM.Run(); + classSM.InitialState = mainState; + + // you can also pass in a vars dictionary + classSM.Run(new Dictionary { ["x"] = 0 }); } \ No newline at end of file diff --git a/StateMachine/Delegates.cs b/StateMachine/Delegates.cs index 1c34a8a..b49e2eb 100644 --- a/StateMachine/Delegates.cs +++ b/StateMachine/Delegates.cs @@ -5,11 +5,11 @@ /// /// A dictionary of variables shared between all states and transitions /// true if the transition should be taken - public delegate bool TransitionConditionDelegate(Dictionary vars); + public delegate bool TransitionConditionDelegate(IDictionary vars); /// /// A function run by /// /// A dictionary of variables shared between all states and transitions - public delegate void FunctionStateFunctionDelegate(Dictionary vars); + public delegate void FunctionStateFunctionDelegate(IDictionary vars); } diff --git a/StateMachine/ExitState.cs b/StateMachine/ExitState.cs index 70c9d5f..0a785b2 100644 --- a/StateMachine/ExitState.cs +++ b/StateMachine/ExitState.cs @@ -2,17 +2,17 @@ { public sealed class ExitState : State { - protected override void Enter(Dictionary vars) + protected override void Enter(IDictionary vars) { throw new NotImplementedException(); } - protected override void Exit(Dictionary vars) + protected override void Exit(IDictionary vars) { throw new NotImplementedException(); } - protected override void Inner(Dictionary vars) + protected override void Inner(IDictionary vars) { throw new NotImplementedException(); } diff --git a/StateMachine/FunctionState.cs b/StateMachine/FunctionState.cs index 88a1c5e..a3ecc1c 100644 --- a/StateMachine/FunctionState.cs +++ b/StateMachine/FunctionState.cs @@ -32,17 +32,17 @@ public FunctionState(FunctionStateFunctionDelegate? enterFunc, FunctionStateFunc } - protected sealed override void Enter(Dictionary vars) + protected sealed override void Enter(IDictionary vars) { _enterFunc?.Invoke(vars); } - protected sealed override void Exit(Dictionary vars) + protected sealed override void Exit(IDictionary vars) { _exitFunc?.Invoke(vars); } - protected sealed override void Inner(Dictionary vars) + protected sealed override void Inner(IDictionary vars) { _innerFunc(vars); } diff --git a/StateMachine/State.cs b/StateMachine/State.cs index 7ad26fc..937b73e 100644 --- a/StateMachine/State.cs +++ b/StateMachine/State.cs @@ -19,23 +19,7 @@ public void AddTransitionTo(State to, TransitionConditionDelegate? condition) transitions.Add(new Transition(to, condition)); } - internal async Task RunAndGetNextStateAsync(int delay, Dictionary vars, CancellationToken cancellationToken) - { - Enter(vars); - - State? next = null; - while (next == null) - { - cancellationToken.ThrowIfCancellationRequested(); - Inner(vars); - next = GetNextState(vars); - await Task.Delay(delay, cancellationToken); - } - Exit(vars); - return next; - } - - internal State RunAndGetNextState(int delay, Dictionary vars) + internal State RunAndGetNextState(int delay, IDictionary vars) { Enter(vars); State? next = null; @@ -49,7 +33,7 @@ internal State RunAndGetNextState(int delay, Dictionary vars) return next; } - protected internal State? GetNextState(Dictionary vars) + protected internal State? GetNextState(IDictionary vars) { foreach (var transition in transitions) { @@ -63,19 +47,19 @@ internal State RunAndGetNextState(int delay, Dictionary vars) /// Called when the State Machine enters the state /// /// A of variables to be used in execution - protected abstract void Enter(Dictionary vars); + protected abstract void Enter(IDictionary vars); /// /// This method is executed repeatedly while the state machine is in this state /// /// A of variables to be used in execution - protected abstract void Inner(Dictionary vars); + protected abstract void Inner(IDictionary vars); /// /// Called before the state machine exits this state /// /// A of variables to be used in execution - protected abstract void Exit(Dictionary vars); + protected abstract void Exit(IDictionary vars); protected virtual void Dispose(bool disposing) { diff --git a/StateMachine/StateMachine.cs b/StateMachine/StateMachine.cs index dd76b3c..a1e5950 100644 --- a/StateMachine/StateMachine.cs +++ b/StateMachine/StateMachine.cs @@ -22,26 +22,15 @@ public class StateMachine : IDisposable /// /// Start the state machine /// - public void Run() + /// A variable context to intialize with + public void Run(IDictionary? vars = null) { var state = InitialState; - var vars = new Dictionary(); + IDictionary myVars = vars ?? new Dictionary(); + while (state != ExitState) { - state = state.RunAndGetNextState(Delay, vars); - } - } - - /// - /// Start the state machine - /// - public async void RunAsync(CancellationToken cancellationToken = default) - { - var state = InitialState; - var vars = new Dictionary(); - while (state != ExitState && !cancellationToken.IsCancellationRequested) - { - state = await state.RunAndGetNextStateAsync(Delay, vars, cancellationToken); + state = state.RunAndGetNextState(Delay, myVars); } } diff --git a/StateMachine/Transition.cs b/StateMachine/Transition.cs index 52ed7a1..9571c24 100644 --- a/StateMachine/Transition.cs +++ b/StateMachine/Transition.cs @@ -11,7 +11,7 @@ internal Transition(State to, TransitionConditionDelegate? condition) } public State To { get; } public TransitionConditionDelegate? Condition { get; } - public bool CheckCondition(Dictionary vars) + public bool CheckCondition(IDictionary vars) { return Condition == null || Condition(vars); } diff --git a/UnitTests/FunctionStateUnitTests.cs b/UnitTests/FunctionStateUnitTests.cs index f9bbbd9..564ee1b 100644 --- a/UnitTests/FunctionStateUnitTests.cs +++ b/UnitTests/FunctionStateUnitTests.cs @@ -65,14 +65,32 @@ public void FunctionStateRunsExitFunction() Assert.IsTrue(aBool); } + [TestMethod] + public void InnerIsOnlyCalledRepeatedly() + { + var anInt = 0; + + var funcState = new FunctionState((vars) => anInt++); + + funcState.AddTransitionTo(StateMachine.ExitState, (vars) => anInt > 10); + + using var sm = new StateMachine(); + + sm.InitialState = funcState; + sm.Run(); + + Assert.AreNotEqual(notExpected: 0, actual: anInt); + } + [TestMethod] public void EnterIsOnlyCalledOnce() { var anInt = 0; + var anotherInt = 0; - var funcState = new FunctionState((vars) => anInt++, (vars) => { }, null); + var funcState = new FunctionState((vars) => anInt++, (vars) => anotherInt++, null); - funcState.AddTransitionTo(StateMachine.ExitState, null); + funcState.AddTransitionTo(StateMachine.ExitState, (vars) => anotherInt > 10); using var sm = new StateMachine(); @@ -87,17 +105,17 @@ public void ExitIsOnlyCalledOnce() { var anInt = 0; + var anotherInt = 0; - var funcState = new FunctionState(null, (vars) => { }, (vars) => anInt++); + var funcState = new FunctionState(null, (vars) => anotherInt++, (vars) => anInt++); - funcState.AddTransitionTo(StateMachine.ExitState, null); + funcState.AddTransitionTo(StateMachine.ExitState, (vars) => anotherInt > 10); using var sm = new StateMachine(); sm.InitialState = funcState; sm.Run(); - Assert.AreEqual(expected: 1, actual: anInt); } } diff --git a/UnitTests/StateMachineUnitTests.cs b/UnitTests/StateMachineUnitTests.cs new file mode 100644 index 0000000..49a00da --- /dev/null +++ b/UnitTests/StateMachineUnitTests.cs @@ -0,0 +1,46 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RoadieRichStateMachine; + +namespace UnitTests +{ + [TestClass] + public class StateMachineUnitTests + { + [TestMethod] + public void StateMachineCanRun() + { + using var sm = new StateMachine(); + sm.Run(); + } + + [TestMethod] + public void VarsCanBePassedIn() + { + Dictionary vars = new() + { + ["x"] = 0 + }; + + using var sm = new StateMachine(); + sm.Run(vars); + } + + [TestMethod] + public void VarsCanBeModified() + { + Dictionary vars = new() + { + ["x"] = 0 + }; + + var funcState = new FunctionState((vars) => vars["x"]++); + funcState.AddTransitionTo(StateMachine.ExitState, null); + + using var sm = new StateMachine(); + sm.InitialState = funcState; + sm.Run(vars); + + Assert.AreEqual(expected: 1, actual: vars["x"]); + } + } +} \ No newline at end of file