c# 状态机实现

c#仿boost statechart的状态机。去年转到unity使用c#,statechart原来的风格蛮爽的,缺点是编译忒慢,在c#则编译根本不是问题。

不一样的地方首先是简单!因为没做一些东西如region。其次是每个状态是持久存在的,不像boost statechart当transit时重建。所以entry,exit自己管一下清理。

重入时不包括双方最近的共同outer state,个人喜好。

这不是一个快速的状态机,在它该用的地方保证很爽就是了。不该用的地方比如字符匹配就不要用了。

最近想实现一下行为树,但重新回顾这个状态机,我发现就AI来说,2者其实并无什么质变。该状态机如果实现动态的树结构,那其实就是另一个变异的行为树了。

using System;
using System.Collections;
using System.Collections.Generic;

namespace StateChart
{
    public enum EResult
    {
        None,
        Forward,
        Resume,
        Defered,
    }

    public enum EHistory
    {
        Shallow,
        Deep,
    }

    public abstract class IEvent { public Type type { get { return GetType(); } } }

    public delegate void Reaction<T>(T fsm);
    public delegate EResult Reaction<U, T>(U fsm, T evt);
    interface IReaction {
        EResult Execute<FSM, EVENT>(FSM fsm_, EVENT evt);
    }
    public class CReaction<FSM, EVENT> : IReaction {
        Reaction<FSM, EVENT> reaction;
        public CReaction(Reaction<FSM, EVENT> reaction_) { reaction = reaction_; }
        public EResult Execute<F, E>(F fsm_, E evt)
        { return reaction((FSM)(object)fsm_, (EVENT)(object)evt); }
    }

    public abstract class IState<FSM> where FSM : IStateMachine<FSM>
    {
        public Type type { get { return GetType(); } }
        public EHistory History { get; set; }
        public Reaction<FSM> Entry { get; set; }
        public Reaction<FSM> Exit { get; set; }

        //could calc on runtime, but we need more fast spped this time.
        public int Depth { get; set; }
        public IState<FSM> OuterState { get; set; }
        public IState<FSM> ActiveState { get; set; }

        Dictionary<Type, IReaction> reactions = new Dictionary<Type, IReaction>();
        Dictionary<Type, Type> transitions = new Dictionary<Type, Type>();
        List<IState<FSM>> subStates = new List<IState<FSM>>();

        IState<FSM> initState = null;
        public IState<FSM> InitState
        {
            get
            {
                if (initState == null)
                    if (subStates.Count > 0)
                        initState = subStates[0];
                return initState;
            }
            set { initState = value; }
        }

        public IState(IState<FSM> ostate)
        {
            History = EHistory.Shallow;
            OuterState = ostate;
            if (OuterState != null) OuterState.AddSubState(this);
        }

        public IState(IState<FSM> ostate, EHistory history_)
        {
            OuterState = ostate;
            History = history_;
            if (OuterState != null) OuterState.AddSubState(this);
        }

        public void DoEntry(FSM fsm_)
        {
            //UnityEngine.Debug.Log("Entry: " + type.ToString());
            Console.WriteLine("Entry: " + type.ToString());
            if (Entry != null) Entry(fsm_);
            else OnEntry(fsm_);
        }
        public void DoExit(FSM fsm_)
        {
            //UnityEngine.Debug.Log("Exit : " + type.ToString());
            Console.WriteLine("Exit : " + type.ToString());
            if (Exit != null) Exit(fsm_);
            else OnExit(fsm_);
        }

        protected virtual void OnEntry(FSM fsm_) { }
        protected virtual void OnExit(FSM fsm_) { }

        public EResult Process<EVENT>(FSM fsm_, EVENT evt) where EVENT : IEvent
        {
            IReaction reaction = null;
            bool hasit = reactions.TryGetValue(evt.type, out reaction);
            if (!hasit) return EResult.Forward;
            return reaction.Execute<FSM, EVENT>(fsm_, evt);
        }

        public void Bind<EVENT>(Reaction<FSM, EVENT> reaction) where EVENT : IEvent
        {
            if (transitions.ContainsKey(typeof(EVENT)))
                throw new System.InvalidOperationException();
            IReaction ireaction = new CReaction<FSM, EVENT>(reaction);
            reactions.Add(typeof(EVENT), ireaction);
        }

        public void Bind<EVENT, TSTATE>()
            where EVENT : IEvent
            where TSTATE : IState<FSM>
        {
            if (reactions.ContainsKey(typeof(EVENT)))
                throw new System.InvalidOperationException();
            transitions.Add(typeof(EVENT), typeof(TSTATE));
        }

        public void AddSubState(IState<FSM> sstate)
        {
            IState<FSM> state = subStates.Find((x) => x.type == sstate.type);
            if (state != null) return;
            subStates.Add(sstate);
        }

        public IEnumerable<IState<FSM>> IterateSubState()
        {
            foreach (IState<FSM> state in subStates)
                yield return state;
        }
    }
}
using System;
using System.Collections;
using System.Collections.Generic;

namespace StateChart
{

    public abstract class IStateMachine<HOST>  where HOST : IStateMachine<HOST>
    {
        Dictionary<Type, IState<HOST>> typeStates = new Dictionary<Type, IState<HOST>>();
        List<IState<HOST>> activeStates = new List<IState<HOST>>();
        Queue<IEvent> eventQueue = new Queue<IEvent>();
        IState<HOST> outestState = null;
        bool bSuspend = false;

        public IStateMachine() { }

        public void Init(IState<HOST> state)
        {
            IState<HOST> pstate = state;

            //add outer states
            while (pstate.OuterState != null) {
                pstate.OuterState.ActiveState = pstate;
                activeStates.Add(pstate);
                pstate = pstate.OuterState;
            }
            activeStates.Add(pstate);
            outestState = pstate;

            //build global type-to-state table
            BuildStateTable(outestState, 0);

            //add init sub states
            pstate = state;
            while (pstate.InitState != null) {
                pstate.ActiveState = pstate.InitState;
                pstate = state.InitState;
                if(pstate != null) activeStates.Add(pstate);
            }

            activeStates.Sort((x, y) => x.Depth - y.Depth);
            foreach (IState<HOST> astate in activeStates) {
                astate.DoEntry((HOST)this);
            }
        }

        void BuildStateTable(IState<HOST> state, int depth_)
        {
            if (state == null) return;
            state.Depth = depth_;
            typeStates.Add(state.type, state);
            foreach (IState<HOST> sstate in state.IterateSubState()) {
                BuildStateTable(sstate, depth_ + 1);
            }
        }

        EResult Transit(IState<HOST> state)
        {
            IState<HOST> lstate = null;

            lstate = outestState;
            while (lstate.ActiveState != null) {  // we could save it if state tree is too high.
                lstate = lstate.ActiveState;
            }

            IState<HOST> rstate = state;
            if (state.History == EHistory.Shallow)
                while (rstate.InitState != null)
                    rstate = state.InitState;
            else
                while (rstate.ActiveState != null)
                    rstate = rstate.ActiveState;

            IState<HOST> ltail = lstate;  //save tail of active states
            IState<HOST> rtail = rstate;    //save tail of init states

            int dis = lstate.Depth - rstate.Depth;
            if (dis > 0)
                { IState<HOST> tstate = lstate; lstate = rstate; rstate = tstate; } //rstate will be deepest state

            dis = Math.Abs(dis);
            for (int i = 0; i < dis; i++)  {
                rstate = rstate.OuterState;
            }
            if (rstate == lstate)  //is family
                return EResult.None;
            do
            { //find nearest outer state
                rstate = rstate.OuterState;
                lstate = lstate.OuterState;
            } while (lstate != rstate);

            do  // call exit chain
            {
                ltail.DoExit((HOST)this);
                ltail = ltail.OuterState;
            } while (ltail != lstate);

            //add tail chain active states
            activeStates.RemoveRange(rstate.Depth + 1, activeStates.Count - rstate.Depth - 1);
            do
            {
                activeStates.Add(rtail);
                lstate = rtail;
                rtail = rtail.OuterState;
                rtail.ActiveState = lstate;
            } while (rtail != rstate);

            // do entry chain
            while (rstate.ActiveState != null)
            {
                rstate = rstate.ActiveState;
                rstate.DoEntry((HOST)this);
            }

            activeStates.Sort((x, y) => x.Depth - y.Depth);
            return EResult.None;
        }

        public EResult Transit(Type stateType)
        {
            IState<HOST> state = null;
            if (!typeStates.TryGetValue(stateType, out state))
                return EResult.None;
            return Transit(state);
        }

        public EResult Transit<TSTATE>()
        { return Transit(typeof(TSTATE)); }

        public void Process<EVENT>(EVENT evt) where EVENT : IEvent
        {
            if (bSuspend) return;

            eventQueue.Enqueue(evt);
            int eventCount = eventQueue.Count;
            while (eventCount > 0){
                eventCount--;
                IEvent pevent = eventQueue.Dequeue();
                foreach (IState<HOST> state in activeStates)
                    if (bSuspend || state.Process((HOST)this, pevent) == EResult.None)
                        break;
            }
        }

        public void PostEvent<EVENT>(EVENT evt) where EVENT : IEvent
        {
            if (bSuspend) return;
            eventQueue.Enqueue(evt);
        }

        public void Suspend()
        { bSuspend = true; }
        public void Resume()
        { bSuspend = false; }
    }
时间: 2024-11-20 19:04:34

c# 状态机实现的相关文章

简易状态机

SimpleFSM 包含状态切换以及事件驱动传递参数 下面的代码是登录的状态码 1 using System; 2 using UnityEngine; 3 using System.Collections; 4 5 public class LoginState : SingletonPrivider<LoginState>, GameState 6 { 7 private delegate void LoginEventHandler(object sender, LoginEventAr

MediaPlayer 状态机 API 详解 示例

简介 public class android.media.MediaPlayer extends Object implements VolumeAutomation 可能需要的权限: One may need to declare a corresponding(相应) WAKE_LOCK permission <uses-permission> element. <uses-permission android:name="android.permission.WAKE_

人生就是一个状态机

从出生到死亡人生走的是一个过程. 从宏观来看,人分为幼年.青年.中年和老年:从微观来看.人每天吃饭.睡觉.学习.工作和娱乐.古语有云:良田千倾只是一日三餐.广厦万间仅仅睡卧榻三尺.不是必需我一己私利而贪得无厌.而我看来人生就如同一个大型状态机. 人生的状态机从大的方面看,它的输入为时间,输出为做出的成果.而人生的不同年龄为所处的状态.时间我们能够看做连续的,也能够看做离散的.时间在不知不觉中流逝,也在分秒钟跳变.而时间人生的输入,他使我们从孩童成长为青年,又使我们从青年变为老年,时间不会停止.输

停车场门禁控制系统的状态机

其状态机如下图: #include<iostream>using namespace std;void main(){ char enter_or_out; //1表示入闸传感器ture,0表示出闸传感器ture int up_or_downt = 0; //1表示起落杆升起,0表示起落杆落下 cout << "状态初始化..." << '\n'; cout << "当前状态为起落杆落下,红灯状态,禁止通行" <

UNITY 状态机 + SVN + 码云 下篇

上篇说到自己写的一个FSM状态机,这篇写怎么把代码和码云联系在一起! 首先,我们应该知道为什么使用码云? 码云是开源中国社区2013年推出的基于 Git 的完全免费的代码托管服务,这个服务是基于 Gitlab 开源软件所开发的,我们在 Gitlab 的基础上做了大量的改进和定制开发,目前已经成为国内最大的代码托管系统,致力于为国内开发者提供优质稳定的托管服务. 码云除了提供最基础的 Git 代码托管之外,还提供代码在线查看.历史版本查看.Fork.Pull Request.打包下载任意版本.Is

001-初识状态机

状态机 FPGA的灵魂 状态机的设计贯穿FPGA设计的始终 一.状态机的概念 1.状态机简述 状态机:描述状态变迁的状态转移图,体现系统对外界事件的反应和行为. 有限状态机(FSM):状态节点数和输入.输出范围有限的状态机. 2.状态机的组成六要素 (1)状态集合   (必备要素):包含了状态机所能达到的所有状态. (2)初态         (必备要素):是整个状态机开始工作的起点.初态是一个相对的参考态. (3)终态   () :状态机的结束状态.事实上,大部分运行在FPGA上的状态机是没有

【slighttpd】基于lighttpd架构的Server项目实战(8)—状态机机制回顾

有限状态机FSM(Finite State Machine) 关于状态机的一个极度确切的描述是它是一个有向图形,由一组节点和一组相应的转移函数组成.状态机通过响应一系列事件而"运行".每个事件都在属于"当前" 节点的转移函数的控制范围内,其中函数的范围是节点的一个子集.函数返回"下一个"(也许是同一个)节点.这些节点中至少有一个必须是终态.当到达终态, 状态机停止. 传统应用程序的控制流程基本是顺序的:遵循事先设定的逻辑,从头到尾地执行.很少有事

三、状态机的设计指导原则

---恢复内容开始--- 1.状态机设计关键是什么? 如何才能把一个电路系统抽象为一个或者多个相互配合嵌套的状态机和组合系统模块?是关键. ---恢复内容结束--- 1.状态机设计关键是什么? 如何才能把一个电路系统抽象为一个或者多个相互配合嵌套的状态机和组合系统模块?是关键.

用状态机来设计cell动画

前言 一个cell可能有好几种状态,比方说选中状态与未选中状态,以及失效状态等等状态,我们将这些不同的情形抽象成一个个状态机,用状态机切换逻辑来设计复杂的动画效果,达到简化设计的目的,大大增加程序可维护性. * 大家需要注意一点,cell因为是可以复用的控件,因为存在复用,所以里面存在较为恶心的复用逻辑,设计动画接口时是需要注意这些小细节的.(亲手写过的人一定会深有体会) 效果 源码 https://github.com/YouXianMing/CellStateAnimation // //

处理浮点数的状态机

这个问题来自于 leetcode 065: Valid Number 如果用正则表达式来写一个浮点数的解析,大概是这样的: [+-]?(([0-9]+\.?) | (\.[0-9]))[0-9]*([eE][+-]?[0-9]+)? 我自己使用 dot 画了一个状态机出来: 如果编写代码来模拟这个状态机,可以拆分为 “eE" 前后两部分分开处理: “eE” 前面的部分,还可以拆成 “.” 前后两部分(如果有点)来编写. 这道题目在 leetcode 上难度为 Hard,可以看出来这种不起眼的小细