Skip to content

Commit

Permalink
Merge pull request #4 from RoadieRich/Interface-instead-of-concrete
Browse files Browse the repository at this point in the history
Interface instead of concrete
  • Loading branch information
RoadieRich authored Jan 11, 2024
2 parents 231c883 + 40ea02e commit 46a2fdf
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 81 deletions.
6 changes: 3 additions & 3 deletions Demo/EvenState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

class EvenState : State
{
protected override void Enter(Dictionary<string, dynamic> vars) { }
protected override void Enter(IDictionary<string, dynamic> vars) { }

protected override void Exit(Dictionary<string, dynamic> vars) { }
protected override void Exit(IDictionary<string, dynamic> vars) { }

protected override void Inner(Dictionary<string, dynamic> vars)
protected override void Inner(IDictionary<string, dynamic> vars)
{
Console.WriteLine(" is even");
}
Expand Down
6 changes: 3 additions & 3 deletions Demo/IncrementState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

class IncrementState : State
{
protected override void Enter(Dictionary<string, dynamic> vars) { }
protected override void Enter(IDictionary<string, dynamic> vars) { }

protected override void Exit(Dictionary<string, dynamic> vars) { }
protected override void Exit(IDictionary<string, dynamic> vars) { }

protected override void Inner(Dictionary<string, dynamic> vars)
protected override void Inner(IDictionary<string, dynamic> vars)
{
vars["x"]++;
}
Expand Down
13 changes: 0 additions & 13 deletions Demo/InitState.cs

This file was deleted.

6 changes: 3 additions & 3 deletions Demo/MainState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

class MainState : State
{
protected override void Enter(Dictionary<string, dynamic> vars) { }
protected override void Enter(IDictionary<string, dynamic> vars) { }

protected override void Exit(Dictionary<string, dynamic> vars) { }
protected override void Exit(IDictionary<string, dynamic> vars) { }

protected override void Inner(Dictionary<string, dynamic> vars)
protected override void Inner(IDictionary<string, dynamic> vars)
{
Console.Out.Write(vars["x"]);
}
Expand Down
6 changes: 3 additions & 3 deletions Demo/OddState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

class OddState : State
{
protected override void Enter(Dictionary<string, dynamic> vars) { }
protected override void Enter(IDictionary<string, dynamic> vars) { }

protected override void Exit(Dictionary<string, dynamic> vars) { }
protected override void Exit(IDictionary<string, dynamic> vars) { }

protected override void Inner(Dictionary<string, dynamic> vars)
protected override void Inner(IDictionary<string, dynamic> vars)
{
Console.WriteLine(" is odd");
}
Expand Down
9 changes: 4 additions & 5 deletions Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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<string, dynamic> { ["x"] = 0 });
}
4 changes: 2 additions & 2 deletions StateMachine/Delegates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
/// </summary>
/// <param name="vars">A dictionary of variables shared between all states and transitions</param>
/// <returns>true if the transition should be taken</returns>
public delegate bool TransitionConditionDelegate(Dictionary<string, dynamic> vars);
public delegate bool TransitionConditionDelegate(IDictionary<string, dynamic> vars);

/// <summary>
/// A function run by <see cref="FunctionState"/>
/// </summary>
/// <param name="vars">A dictionary of variables shared between all states and transitions</param>
public delegate void FunctionStateFunctionDelegate(Dictionary<string, dynamic> vars);
public delegate void FunctionStateFunctionDelegate(IDictionary<string, dynamic> vars);
}
6 changes: 3 additions & 3 deletions StateMachine/ExitState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
{
public sealed class ExitState : State
{
protected override void Enter(Dictionary<string, dynamic> vars)
protected override void Enter(IDictionary<string, dynamic> vars)
{
throw new NotImplementedException();
}

protected override void Exit(Dictionary<string, dynamic> vars)
protected override void Exit(IDictionary<string, dynamic> vars)
{
throw new NotImplementedException();
}

protected override void Inner(Dictionary<string, dynamic> vars)
protected override void Inner(IDictionary<string, dynamic> vars)
{
throw new NotImplementedException();
}
Expand Down
6 changes: 3 additions & 3 deletions StateMachine/FunctionState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ public FunctionState(FunctionStateFunctionDelegate? enterFunc, FunctionStateFunc
}


protected sealed override void Enter(Dictionary<string, dynamic> vars)
protected sealed override void Enter(IDictionary<string, dynamic> vars)
{
_enterFunc?.Invoke(vars);
}

protected sealed override void Exit(Dictionary<string, dynamic> vars)
protected sealed override void Exit(IDictionary<string, dynamic> vars)
{
_exitFunc?.Invoke(vars);
}

protected sealed override void Inner(Dictionary<string, dynamic> vars)
protected sealed override void Inner(IDictionary<string, dynamic> vars)
{
_innerFunc(vars);
}
Expand Down
26 changes: 5 additions & 21 deletions StateMachine/State.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,7 @@ public void AddTransitionTo(State to, TransitionConditionDelegate? condition)
transitions.Add(new Transition(to, condition));
}

internal async Task<State> RunAndGetNextStateAsync(int delay, Dictionary<string, dynamic> 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<string, dynamic> vars)
internal State RunAndGetNextState(int delay, IDictionary<string, dynamic> vars)
{
Enter(vars);
State? next = null;
Expand All @@ -49,7 +33,7 @@ internal State RunAndGetNextState(int delay, Dictionary<string, dynamic> vars)
return next;
}

protected internal State? GetNextState(Dictionary<string, dynamic> vars)
protected internal State? GetNextState(IDictionary<string, dynamic> vars)
{
foreach (var transition in transitions)
{
Expand All @@ -63,19 +47,19 @@ internal State RunAndGetNextState(int delay, Dictionary<string, dynamic> vars)
/// Called when the State Machine enters the state
/// </summary>
/// <param name="vars">A <see cref="Dictionary{string, dynamic}"/> of variables to be used in execution</param>
protected abstract void Enter(Dictionary<string, dynamic> vars);
protected abstract void Enter(IDictionary<string, dynamic> vars);

/// <summary>
/// This method is executed repeatedly while the state machine is in this state
/// </summary>
/// <param name="vars">A <see cref="Dictionary{string, dynamic}"/> of variables to be used in execution</param>
protected abstract void Inner(Dictionary<string, dynamic> vars);
protected abstract void Inner(IDictionary<string, dynamic> vars);

/// <summary>
/// Called before the state machine exits this state
/// </summary>
/// <param name="vars">A <see cref="Dictionary{string, dynamic}"/> of variables to be used in execution</param>
protected abstract void Exit(Dictionary<string, dynamic> vars);
protected abstract void Exit(IDictionary<string, dynamic> vars);

protected virtual void Dispose(bool disposing)
{
Expand Down
21 changes: 5 additions & 16 deletions StateMachine/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,15 @@ public class StateMachine : IDisposable
/// <summary>
/// Start the state machine
/// </summary>
public void Run()
/// <param name="vars">A variable context to intialize with</param>
public void Run(IDictionary<string, dynamic>? vars = null)
{
var state = InitialState;
var vars = new Dictionary<string, dynamic>();
IDictionary<string, dynamic> myVars = vars ?? new Dictionary<string, dynamic>();

while (state != ExitState)
{
state = state.RunAndGetNextState(Delay, vars);
}
}

/// <summary>
/// Start the state machine
/// </summary>
public async void RunAsync(CancellationToken cancellationToken = default)
{
var state = InitialState;
var vars = new Dictionary<string, dynamic>();
while (state != ExitState && !cancellationToken.IsCancellationRequested)
{
state = await state.RunAndGetNextStateAsync(Delay, vars, cancellationToken);
state = state.RunAndGetNextState(Delay, myVars);
}
}

Expand Down
2 changes: 1 addition & 1 deletion StateMachine/Transition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal Transition(State to, TransitionConditionDelegate? condition)
}
public State To { get; }
public TransitionConditionDelegate? Condition { get; }
public bool CheckCondition(Dictionary<string, dynamic> vars)
public bool CheckCondition(IDictionary<string, dynamic> vars)
{
return Condition == null || Condition(vars);
}
Expand Down
28 changes: 23 additions & 5 deletions UnitTests/FunctionStateUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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);
}
}
Expand Down
46 changes: 46 additions & 0 deletions UnitTests/StateMachineUnitTests.cs
Original file line number Diff line number Diff line change
@@ -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<string, dynamic> vars = new()
{
["x"] = 0
};

using var sm = new StateMachine();
sm.Run(vars);
}

[TestMethod]
public void VarsCanBeModified()
{
Dictionary<string, dynamic> 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"]);
}
}
}

0 comments on commit 46a2fdf

Please sign in to comment.