zoukankan      html  css  js  c++  java
  • lua版本的一个状态机

    这个状态机http://www.cnblogs.com/flytrace/p/5587033.html的lua版本

    -- LUA 有实现枚举值的好办法么
    local sc_enum = 
    {
        -- event flow be broke up
        none        = "none",
        -- event flow pass througth next state
        forward     = "forward",
        -- deffer a speciafic event
        --deffer    = 4
    
    
    
        -- select by default(the first position in the list)
        shallow     = "shallow", 
        -- the state will remain actived state that last time was
        deep        = "deep", 
    
    
    
        -- 不实现。该模式定义不良好。多个嵌套的子并行状态相对复杂,流程不清晰,数据结构也不直接。
        -- 还没找到比较良好的并行状态语义。
        -- only one substate can run at one time by default 
        singleman   = "singleman", 
        -- the state will be enabled always, substate in parallel region can't transit to none-parallel region
        parallel    = "parallel", 
    
    }
    
    return sc_enum
    -- Date 2016-7-13
    -- yanfei
    
    --[[
        一个sc_fsm也是一个sc_state,状态机本身也可以作为一个状态,但目前子状态机只能视为状态,未能作为独立的机制运行
        sc_fsm
        {
            type2states         -- save all states accord to their name in a dictionary
        }
    --]]
    sc_helper = require("fsm.sc_helper")
    require("utils.sc_list")
    local sc_queue = require("utils.sc_queue")
    local sc_enum = require("fsm.sc_enum")
    local sc_state = require("fsm.sc_state")
    local classprototype = require("utils.middleclass")
    -- a fsm is a state 
    local fsm = classprototype("sc_fsm", sc_state)
    
    -- 建立类型-状态表
    local function buildstates(fsm, astate)
        fsm.type2states[astate.statename] = astate
        if astate.substates then 
            for statename_, state_ in ipairs(astate.substates) do
                assert(fsm.type2states[state_.statename] == nil, string.format("[%s]: has a state named [%s] already!", fsm.statename, state_.statename))
                buildstates(fsm, state_)
            end
        end
    end
    
    -- 重载initialize
    -- @statename_      状态名字
    -- @basestate_      父状态
    -- @statemode_      运行模式
    -- @operation_      激活操作
    function fsm:initialize(statename_, basestate_, statemode_, operation_)
        sc_state.initialize(self, statename_, basestate_, statemode_, operation_)
    end
    
    function fsm:init(initstatename)
        assert(type(self) == 'table', "Make sure that you are using 'fsm:init' instead of 'fsm.init'")
    
        self.type2states = {}
        buildstates(self, self)
    
        local initstate = self.type2states[initstatename]
        assert(initstate and initstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, initstatename))
    
        -- 建立主激活列表
        local astate = initstate
        while astate do
            astate.activesubstate = astate.substates and astate.substates[1] or nil
            self.ineststate = astate.activesubstate and astate.activesubstate or astate
            astate = astate.activesubstate
        end
    
        astate = initstate
        while astate ~= self do
            astate.basestate.activesubstate = astate
            astate = astate.basestate
        end
    
        astate = self
        while astate.activesubstate ~= nil do
            if astate.activesubstate.enter then astate.activesubstate:enter(self) end
            astate = astate.activesubstate
        end
    
        if not self.eventqueue then self.eventqueue = sc_queue:newqueue() end
        self.suspend = false
        self.__lockqueue = false
        self.__transiting = false
    
        return true
    end
    
    -- 跳转到目标状态
    function fsm:transit( targetname_ )
        assert(type(self) == 'table', "Make sure that you are using 'fsm:transit' instead of 'fsm.transit'")
    
        if self.__transiting then
            self.__transiting = false
            return sc_enum.none
        end
    
        self.__transiting = true;
    
        local targetstate = self.type2states[targetname_]
        assert(targetstate and targetstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, targetname_))
    
        -- detect the whole active state list
        local astate = self.ineststate
        -- 
        local bstate = targetstate
        local dis = astate.depth - bstate.depth
        if dis < 0 then 
            bstate, astate = astate, targetstate
        end -- bstate is topper
    
        dis = math.abs(dis)
        for i = 1, dis do astate = astate.basestate end --now they are both same depth
    
        if astate == bstate then -- is family
            self.__transiting = false
            return sc_enum.none
        end
    
        -- find  nearest ancestor both
        while astate ~= bstate do
            astate, bstate = astate.basestate, bstate.basestate
        end
    
        -- first we will call exit chain
        while self.ineststate ~= astate do
            if self.ineststate.exit then self.ineststate:exit(self) end
            self.ineststate = self.ineststate.basestate
        end
    
        -- now we will decide the enter chain
    
         -- build active chain down side
        local astate = targetstate
        while astate do
            astate.activesubstate =  (astate.substates and astate.statemode == sc_enum.shallow) 
                                     and astate.substates[1] or astate.activesubstate
            self.ineststate = astate.activesubstate and astate.activesubstate or astate
            astate = astate.activesubstate
        end
    
        -- build active chain up side
        astate = targetstate
        while astate ~= self do
            astate.basestate.activesubstate, astate = astate, astate.basestate
        end
    
        -- call enter chain
        while bstate.activesubstate ~= nil do
            if bstate.activesubstate.enter then bstate.activesubstate:enter(self) end
            bstate = bstate.activesubstate
        end
    
        return sc_enum.none
    end
    
    function fsm:process_event( eventname_, ...)
        assert(type(self) == 'table', "Make sure that you are using 'fsm:process_event' instead of 'fsm.process_event'")
        if self.suspend then return nil end
        if self.__lockqueue then return nil end
        
        local pevent = {...}
        pevent.eventname = eventname_
        if not self.eventqueue then self.eventqueue = sc_queue:newqueue() end
        local queue = self.eventqueue
        queue:push_back(pevent)
    
        self.__lockqueue = true
        --local eventcount = queue:count()
        while true do
            --eventcount = eventcount - 1
            local astate = self
            local processflag_ = sc_enum.forward
            pevent = queue:pop_front() 
            if not pevent then break end
            while astate.activesubstate ~= nil do
                processflag_ = astate.activesubstate:_internal_process(self, pevent)
                if processflag_ == sc_enum.none then 
                    return processflag_ 
               end
               astate = astate.activesubstate
            end
        end
    
        self.__lockqueue = false
    
        return processflag_
    end
    
     -- 
    function fsm:post_event( eventname_, ... )
        local pevent = {...}
        pevent.eventname = eventname_
        self.eventqueue:push_back(pevent)
    end
    
    function fsm:context( statename_ )
        local targetstate = self.type2states[targetname_]
        --assert(targetstate and targetstate:isInstanceOf(sc_state), string.format("[%s] hasn't state named [%s]", self.statename, targetname_))
        return targetstate
    end
    
    return fsm
    -- Date 2016-7-13
    -- yanfei
    
    --[[
    --  sc_state
    --  {
            statename,          -- 状态名
            basestate,          -- 父状态
            statemode,          -- 历史模式
            operation,          -- 操作,完成并行,筛选。目前保留字段
    
            substates,          -- 子状态列表
            activestate,        -- 当前激活状态
    
            events,,            -- event-callback表
    --  }
    --  一个状态是一个节点,basestate指向父状态,substates指向子状态列表,所有状态形成一棵树
    --]]
    
    require("utils.sc_list")
    local sc_enum = require("fsm.sc_enum")
    --require("fsm.sc_event")
    local classprototype = require("utils.middleclass")
    local sc_state = classprototype("sc_state")
    
    local rawget = rawget
    local rawset = rawset
    local string = string
    
    -- 重载initialize
    -- @statename_      状态名字
    -- @basestate_      父状态
    -- @statemode_      运行模式
    -- @operation_      激活操作
    function sc_state:initialize(statename_, basestate_, statemode_, operation_)
        assert(type(statename_) == "string","statename must be a string")
        self.statename = statename_
        self.statemode = statemode_ or sc_enum.shallow
        --self.operation = operation_ or sc_enum.singleman
    
        -- 处理父子关系
        assert(basestate_ and basestate_:isInstanceOf(sc_state) or basestate_ == nil, string.format("[%s]: base state must be a sc_state", statename_))
        self.basestate = basestate_
        if basestate_ then basestate_:addsubstate(self) end
    
        -- 状态深度 top state is 1
        self.depth = self.basestate and self.basestate.depth + 1 or 1
    
    end
    
    -- 创建该状态的子状态
    function sc_state:createstate(statename_, statemode_, operation_)
        return sc_state:new(statename_, self, statemode_, operation_)
    end
    
    -- 添加子状态
    function sc_state:addsubstate(...)
        -- 填充子状态列表
        self.substates = self.substates or {}
        for i, state_ in ipairs{...} do 
            assert(state_:isInstanceOf(sc_state), "substate must be a sc_state")
            assert(self.substates[state_.statename] == nil, string.format("%s has a substate named [%s] already!", self.statename, state_.statename))
            table.insert(self.substates, state_)
        end
    end
    
    -- 绑定事件
    -- @eventname_  事件名
    -- @callbackname_   事件处理函数名
    function sc_state:bind_event(eventname_, callbackname_)
        assert(type(self) == 'table', "Make sure that you are using 'sc_state:process_event' instead of 'sc_state.process_event'")
        assert(type(eventname_) == 'string', "Make sure that callbakname is a string")
        assert(type(callbackname_) == 'string', "Make sure that callbakname is a string")
        --local callback_ =  self[callbackname_]
        local callback_ = rawget(self, callbackname_)
        assert(callback_, string.format("[%s] bind_event event[%s]: hasn't a function named [%s]", self.statename, eventname_, callbackname_))
        assert(type(callback_) == "function", string.format("[%s]:bind_event event[%s]: callback must be a function", self.statename, eventname_))
    
        self.events = self.events or {}
        self.events[eventname_] = function(fsm, ...) return callback_(self, fsm, ...) end
    end
    
    function sc_state:_internal_process(fsm, pevent)
        if not self.events then return sc_enum.forward end
        local callback_ = self.events[pevent.eventname]
        if not callback_ then return sc_enum.forward end
        return callback_(fsm, unpack(pevent))
    end
    
    return sc_state
    --region *.lua
    --Date 2016.7.17
    -- yanfei
    
    local queue = {}
    
    function queue:newqueue()
        local o = {first = 0, last = -1}
        setmetatable(o, self)
        self.__index = self
        return o
    end
    
    function queue:push_front(v)
        local first = self.first - 1
        self.first = first
        self[first] = v
    end
    
    function queue:pop_front()
        local first = self.first
        if first > self.last then return nil end
        local v = self[first]
        self[first] = nil
        self.first = first + 1
        return v
    end
    
    function queue:push_back(v)
        local last = self.last + 1
        self.last = last
        self[last] = v
    end
    
    function queue:pop_back()
        local last = self.last
        if last < self.first then return nil end
        local v = self[last]
        self[last] = nil
        self.last = last - 1
        return v
    end
    
    function queue:count()
        return self.last - self.first + 1
    end
    
    return queue
    --endregion
    --region *.lua
    
    module ("sc_list", package.seeall)
    
    --- An iterator over the elements of a list.
    -- @param l list to iterate over
    -- @return iterator function which returns successive elements of the list
    -- @return the list <code>l</code> as above
    -- @return <code>true</code>
    function elems (l)
      local n = 0
      return function (l)
               n = n + 1
               if n <= #l then
                 return l[n]
               end
             end,
      l, true
    end
    
    --- An iterator over the elements of a list, in reverse.
    -- @param l list to iterate over
    -- @return iterator function which returns precessive elements of the list
    -- @return the list <code>l</code> as above
    -- @return <code>true</code>
    function relems (l)
      local n = #l + 1
      return function (l)
               n = n - 1
               if n > 0 then
                 return l[n]
               end
             end,
      l, true
    end
    
    --- Fold a binary function into an iterator.
    -- @param f function
    -- @param d initial first argument
    -- @param i iterator
    -- @return result
    function fold_x (f, d, i, t)
      local r = d
      for e in i (t) do
        r = f (r, e)
      end
      return r
    end
    
    function fold (f, d, t)
      local r = d
      for e in elems (t) do
        r = f (r, e)
      end
      return r
    end
    
    ----- Fold a binary function through a list right associatively.
    ---- @param f function
    ---- @param e element to place in right-most position
    ---- @param l list
    ---- @return result
    --function foldr (f, e, l)
    --  return _G.fold (function (x, y) return f (y, x) end,
    --                  e, relems, l)
    --end
    
    function find(f, t)
        for e in elems (t) do
            if f(e) then return e end
        end
        return nil
    end
    
    function findall(f, t)
        local result = {}
        for e in elems(t) do
            if f(e) then result[#result+1] = (e) end
        end
        return result
    end
    
    function join(a, b)
        return {unpack(a), unpack(b)}
    end 
    
    function cons(a, t)
        return {a, unpack(t)}
    end
    
    --endregion

    使用

    -- 只是一个例子
    
    local sc_enum = require("fsm.sc_enum")
    local sc_fsm_pt = require("fsm.sc_fsm")
    
    local gamemain_fsm = sc_fsm_pt:new("gamemain_fsm")
    
    local print = print
    
    --[[
    --  gamemain_state
    --]]
    local gamemain_state = gamemain_fsm:createstate("gamemain_state")
    function gamemain_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    function gamemain_state:update(fsm, delta)
        print("update "..self.statename)
        print("timedelta: "..tostring(delta))
    end
    
    gamemain_state:bind_event("game_update", "update")
    
    --[[
    --  gamemain_init_state
    --]]
    local gamemain_init_state = gamemain_state:createstate("gamemain_init_state")
    
    function gamemain_init_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_init_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    --[[
    --  gamemain_init_waiting_state
    --]]
    local gamemain_init_waiting_state = gamemain_init_state:createstate("gamemain_init_waiting_state")
    
    function gamemain_init_waiting_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_init_waiting_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    
    --[[
    --  gamemain_idle_state
    --]]
    local gamemain_idle_state = gamemain_state:createstate("gamemain_idle_state")
    
    function gamemain_idle_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_idle_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    function gamemain_idle_state:update(fsm, delta)
        print("update "..self.statename)
        print("timedelta: "..tostring(delta))
        --fsm:process_event("game_upadte", 0.0001)
    end
    
    function gamemain_idle_state:tolobby(fsm, msg1, msg2)
        print(msg1..' '..msg2)
        print("ready to taransit to lobby") 
        return fsm:transit("gamemain_lobby_state")
    end
    
    gamemain_idle_state:bind_event("game_update", "update")
    gamemain_idle_state:bind_event("event_tolobby", "tolobby")
    --[[
    --  gamemain_net_state
    --]]
    local gamemain_net_state = gamemain_state:createstate("gamemain_net_state")
    function gamemain_net_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_net_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    
    --[[
    --  gamemain_net_send_state
    --]]
    local gamemain_net_send_state = gamemain_net_state:createstate("gamemain_net_send_state")
    function gamemain_net_send_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_net_send_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    
    --[[
    --  gamemain_net_receive_state
    --]]
    local gamemain_net_receive_state = gamemain_net_state:createstate("gamemain_net_receive_state")
    function gamemain_net_receive_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_net_receive_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    
    
    
    --[[
    --  gamemain_lobby_state
    --]]
    local gamemain_lobby_state = gamemain_state:createstate("gamemain_lobby_state")
    function gamemain_lobby_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_lobby_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    
    --[[
    --  gamemain_lobby_shop_state
    --]]
    local gamemain_lobby_shop_state = gamemain_lobby_state:createstate("gamemain_lobby_shop_state")
    function gamemain_lobby_shop_state:enter(fsm)
        print("enter "..self.statename)
        fsm:post_event("event_opendoor", "opendoor")  
    end
    
    function gamemain_lobby_shop_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    function gamemain_lobby_shop_state:update(fsm, delta)
        print("update "..self.statename)
        print("timedelta: "..tostring(delta))
    end
    
    function gamemain_lobby_shop_state:opendoor(fsm)
        print("open the door")
    end
    
    gamemain_lobby_shop_state:bind_event("game_update", "update")
    gamemain_lobby_shop_state:bind_event("event_opendoor", "opendoor")
    
    --[[
    --  gamemain_lobby_dungeon_state
    --]]
    local gamemain_lobby_dungeon_state = gamemain_lobby_state:createstate("gamemain_lobby_dungeon_state")
    function gamemain_lobby_dungeon_state:enter(fsm)
        print("enter "..self.statename)
    end
    
    function gamemain_lobby_dungeon_state:exit(fsm)
        print("exit "..self.statename)
    end
    
    return gamemain_fsm
    --region *.lua
    --Date 
    
    sc_helper = require("fsm.sc_helper")
    local sc_event = require("fsm.sc_event")
    local queue = require("utils.sc_queue")
    
    -- 获取定义好的gamemain状态机
    local gamemain_fsm = require("game.gamemain_fsm")
    -- 打印状态树
    sc_helper:printstates(gamemain_fsm)
    
    -- 初始化状态机,初始状态为gamemain_init_state,进入gamemain_init_state时将自动进入其子状态gamemain_init_waiting_state
    -- 进入某个状态后,如果有子状态,会尝试进入其子状态
    gamemain_fsm:init("gamemain_init_state")
    
    -- 自顶向下打印当前激活的状态链
    sc_helper:printactivestates(gamemain_fsm)
    
    -- 状态迁移到gamemain_idle_state
    gamemain_fsm:transit("gamemain_idle_state")
    
    sc_helper:printactivestates(gamemain_fsm)
    
    -- 发送事件game_update, 在gamemain_idle_state中注册了对该事件的响应
    gamemain_fsm:process_event("game_update", 0.0333333)
    -- 发送事件event_tolobby,在gamemain_idle_state中注册了对该事件的响应。在响应中将调用gamemain_fsm:transit迁移到gamemain_lobby_state
    gamemain_fsm:process_event("event_tolobby", "hello", "world")
    sc_helper:printactivestates(gamemain_fsm)
    gamemain_fsm:process_event("game_update", 0.0333333)
    
    print("over")
    --endregion
  • 相关阅读:
    第二次站立会议4
    第二次站立会议3
    建议总结
    对搜狗输入法的个人评价
    第二期站立会议10
    典型用户和用户场景描述
    第二期站立会议9
    第二期站立会议8
    第二期站立会议7
    第二期站立会议6
  • 原文地址:https://www.cnblogs.com/flytrace/p/7128003.html
Copyright © 2011-2022 走看看