Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
name: main

run-name: Initiated by ${{ github.actor }} | Branch ${{ github.ref }} | SHA ${{ github.sha }}

on:
push:
branches:
- main
workflow_dispatch:

jobs:
test:
main-build:
runs-on: ubuntu-latest
steps:
- name: checkout repository
Expand Down
16 changes: 15 additions & 1 deletion .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
name: pull request

run-name: Initiated by ${{ github.actor }} | Branch ${{ github.ref }} | SHA ${{ github.sha }}

on:
pull_request:
branches:
- main

# A workflow run that hasn’t been queued within 30 minutes of being triggered is discarded.
# A workflow run that has been queued but not processed by a GitHub-hosted runner within 45 minutes is discarded.
# Can only apply timeouts at job and step level
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true

jobs:
test:
pull-request-build:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
Expand All @@ -21,6 +32,9 @@ jobs:

- name: dotnet restore
run: dotnet restore

- name: dotnet format
run: dotnet format --verify-no-changes --no-restore

- name: dotnet build [Release]
run: dotnet build --no-restore --configuration Release
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:
types:
- published

concurrency:
cancel-in-progress: false
group: release

jobs:
release:
runs-on: ubuntu-latest
Expand All @@ -28,8 +32,8 @@ jobs:
run: dotnet build --configuration Release

- name: Extract version from tag
id: get_version
run: |
id: get_version
run: |
# Remove the "refs/tags/" prefix from GITHUB_REF
TAG=${GITHUB_REF#refs/tags/}
# Remove leading 'v' if tag looks like "v1.2.3"
Expand Down
14 changes: 8 additions & 6 deletions FiniteStateMachine/FiniteStateMachineBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public sealed class FiniteStateMachineBuilder<TState, TTrigger>
where TState : notnull
{
private readonly TState _initialState;
private readonly Dictionary<TState, Dictionary<TTrigger, TState>> _transitions = new();
private readonly Dictionary<TState, Dictionary<TTrigger, TransitionOption<TTrigger, TState>>> _transitions = new();

/// <summary>
/// Initializes a new instance of the <see cref="FiniteStateMachineBuilder{TState, TTrigger}"/> class.
Expand All @@ -20,7 +20,7 @@ internal FiniteStateMachineBuilder(TState initialState)
{
_initialState = initialState;
}

/// <summary>
/// Defines a state and its possible transitions.
/// </summary>
Expand All @@ -36,7 +36,7 @@ public FiniteStateMachineBuilder<TState, TTrigger> State(
_transitions.Add(state, cfg.Transitions);
return this;
}

/// <summary>
/// Builds and returns the configured state machine.
/// </summary>
Expand All @@ -52,7 +52,7 @@ private Dictionary<RuleKey<TState, TTrigger>, Rule<TState, TTrigger>> GetTransit
var rules = new Dictionary<RuleKey<TState, TTrigger>, Rule<TState, TTrigger>>();
foreach (var (fromState, transitions) in _transitions)
{
foreach (var (trigger, targetState) in transitions)
foreach (var (trigger, transitionOption) in transitions)
{
var ruleKey = new RuleKey<TState, TTrigger>
{
Expand All @@ -62,8 +62,10 @@ private Dictionary<RuleKey<TState, TTrigger>, Rule<TState, TTrigger>> GetTransit
var rule = new Rule<TState, TTrigger>
{
From = fromState,
To = targetState,
Trigger = trigger
To = transitionOption.TargetState,
Trigger = trigger,
EntryActions = transitionOption.EntryActions,
ExitActions = transitionOption.ExitActions,
};
rules.Add(ruleKey, rule);
}
Expand Down
6 changes: 5 additions & 1 deletion FiniteStateMachine/Rule.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
namespace FiniteStateMachine;

public sealed record Rule<TState, TTrigger>
public sealed record Rule<TState, TTrigger>
where TTrigger : Enum
{
public required TState From { get; init; }
public required TState To { get; init; }
public required TTrigger Trigger { get; init; }

public required List<Action> EntryActions { get; init; }

public required List<Action> ExitActions { get; init; }
}
2 changes: 1 addition & 1 deletion FiniteStateMachine/RuleKey.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace FiniteStateMachine;

public sealed record RuleKey<TState, TTrigger>
public sealed record RuleKey<TState, TTrigger>
where TTrigger : Enum
{
public required TState From { get; init; }
Expand Down
33 changes: 30 additions & 3 deletions FiniteStateMachine/StateConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ namespace FiniteStateMachine;
public sealed class StateConfiguration<TState, TTrigger> where TTrigger : Enum
{
private readonly TState _state;


private readonly List<Action> _entryActions;
private readonly List<Action> _exitActions;

internal IReadOnlyList<Action> EntryActions => _entryActions;
internal IReadOnlyList<Action> ExitActions => _exitActions;

/// <summary>
/// Gets the transitions defined for the current state.
/// </summary>
internal Dictionary<TTrigger, TState> Transitions { get; } = new();
internal Dictionary<TTrigger, TransitionOption<TTrigger, TState>> Transitions { get; } = new();

/// <summary>
/// Initializes a new instance of the <see cref="StateConfiguration{TState, TTrigger}"/> class.
Expand All @@ -21,15 +27,36 @@ public sealed class StateConfiguration<TState, TTrigger> where TTrigger : Enum
internal StateConfiguration(TState state)
{
_state = state;
_entryActions = new List<Action>();
_exitActions = new List<Action>();
}


internal void ClearActions()
{
_entryActions.Clear();
_exitActions.Clear();
}

/// <summary>
/// Starts the configuration of a transition for a given trigger.
/// </summary>
/// <param name="trigger">The trigger that causes a transition.</param>
/// <returns>A configuration object to setup the transition.</returns>
public TransitionConfiguration<TState, TTrigger> On(TTrigger trigger)
{

return new TransitionConfiguration<TState, TTrigger>(this, trigger);
}

public StateConfiguration<TState, TTrigger> OnEnter(Action action)
{
_entryActions.Add(action);
return this;
}

public StateConfiguration<TState, TTrigger> OnExit(Action action)
{
_exitActions.Add(action);
return this;
}
}
14 changes: 10 additions & 4 deletions FiniteStateMachine/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ public sealed class StateMachine<TState, TTrigger>
/// A dictionary containing rules for state transitions.
/// </summary>
private readonly Dictionary<RuleKey<TState, TTrigger>, Rule<TState, TTrigger>> _rules;

/// <summary>
/// Gets the rules for state transitions as a read-only dictionary.
/// </summary>
public IReadOnlyDictionary<RuleKey<TState, TTrigger>, Rule<TState, TTrigger>> Rules => _rules;

/// <summary>
/// Gets the current state of the state machine.
/// </summary>
public TState CurrentState { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="StateMachine{TState, TTrigger}"/> class.
/// </summary>
Expand All @@ -43,7 +43,7 @@ public static FiniteStateMachineBuilder<TState, TTrigger> WithInitialState(TStat
{
return new FiniteStateMachineBuilder<TState, TTrigger>(initialState);
}

/// <summary>
/// Triggers a transition based on the specified trigger, updating the current state if a rule is matched.
/// </summary>
Expand All @@ -57,7 +57,13 @@ public bool Trigger(TTrigger trigger)
Trigger = trigger
};
if (!_rules.TryGetValue(key, out var rule)) return false;

rule.ExitActions.ForEach(action => action());

CurrentState = rule.To;

rule.EntryActions.ForEach(action => action());

return true;
}
}
13 changes: 11 additions & 2 deletions FiniteStateMachine/TransitionConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@ internal TransitionConfiguration(StateConfiguration<TState, TTrigger> stateConfi
_stateConfiguration = stateConfiguration;
_trigger = trigger;
}

public StateConfiguration<TState, TTrigger> GoTo(TState target)
{
_stateConfiguration.Transitions[_trigger] = target;
_stateConfiguration.Transitions[_trigger] = new TransitionOption<TTrigger, TState>
{
Trigger = _trigger,
TargetState = target,
EntryActions = _stateConfiguration.EntryActions.ToList(),
ExitActions = _stateConfiguration.ExitActions.ToList()
};

_stateConfiguration.ClearActions();

return _stateConfiguration;
}
}
9 changes: 9 additions & 0 deletions FiniteStateMachine/TransitionOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FiniteStateMachine;

internal sealed record TransitionOption<TTrigger, TState>
{
public required TTrigger Trigger { get; init; }
public required TState TargetState { get; init; }
public required List<Action> EntryActions { get; init; }
public required List<Action> ExitActions { get; init; }
}
2 changes: 1 addition & 1 deletion FiniteStateMachineTest/ClassState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public override bool Equals(object? obj)
{
return false;
}

return state.Name == Name;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace FiniteStateMachineTest;

internal enum EnumState
internal enum State
{
Playing,
Paused,
Expand Down
Loading