`
cloudhe
  • 浏览: 107682 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

[Copy] 有限状态自动机(FSM)的简单实现

阅读更多
Sometimes, a quite simple concept comes out with lots of related theories that make beginners have to spend an exausting time before figure out the basic one. Watching something through such thick fog, we'll never make it without a powerful straight light which directly reach the key point.

The light for programmers is sure the code.

This article I copied, is trying to explain an useful basic concept of "FSM" in an uncommon way, which I think, greatly shows how "code rules". Think about it while learning what FSM is.



简述

有限状态机(以下用 FSM 指代)是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。在 Gof 的 23 种设计模式里的 state 模式是一种面向对象的状态机思想,可以适应非常复杂的状态管理。
现在,FSM 被普遍用于搜索引擎的分词、编译器实现和我们普遍关注的游戏开发中。游戏开发中,通常用 FSM 实现 NPC 控制,如当 NPC 受到攻击时根据健康、力量等选择逃跑还是反攻的行为,一般是用FSM实现的。FSM 的实现方法有很多种,不能简单地说孰优孰劣,但现代开发中,一般都比较推荐面向对象的实现方式:因为可重用性和健壮性更高,而且当需求变更的时候,也有很好的适应性。


实践

理论从实践中来,也要回到实践中去。我们现在通过实例来探索一下 FSM 的实现吧。首先假设有这样一个世界(World),世界里只有一台永不缺乏动力的汽车(Car),汽车是次世代的,没有油门方向盘之类的落后设备,只有两个互斥的按钮——停止(Stop)和行进(Run),随着时间的流逝,汽车根据驾驶员的操作走走停停。下面的代码可以实现这种功能:
while True:
    key = get_key() # 按下什么键
     if key == "stop":
        stop(car)
    elif key == "run":
        go(car)
    keep(car) # 保持原态

完成了功能而且直观、简洁的程序员万岁!但这时候客户(策划或者玩家)觉得走走停停太没意思了,他们想要掉头、左转和右转的功能,我们就要在 while 循环里增加更多的 if...else 分支;他们想要更多的车,我们就要要在每一个分枝里增加循环;他们不仅仅想要 Car 了,他们还要要玩 Truck,这时我们就需要在分枝的循环里判断当前的车是否支持这个操作(如 Truck 的装卸货物 Car 就不支持);他们……
这个 while 循环终于无限地庞大起来,我们认识到这样的设计的确是有点问题的,所以我们尝试用另一种方法去实现FSM。首先我们来实现汽车(Car):

class Car(object):
    def stop(self):
        print "Stop!!!"
 
    def go(self):
        print "Goooooo!!!"

只有两个方法 stop 和 go,分别执行 Stop 和 Run 两个按钮功能。接下来我们编写两个状态管理的类,用以处理当按钮被按下、弹起和保持时需要工作的流程:
class stop_fsm(base_fsm):
    def enter_state(self, obj):
        print "Car%s enter stop state!"%(id(obj))
 
    def exec_state(self, obj):
        print "Car%s in stop state!"%(id(obj))
        obj.stop()
 
    def exit_state(self, obj):
        print "Car%s exit stop state!"%(id(obj)) 

class run_fsm(base_fsm):
    def enter_state(self, obj):
        print "Car%s enter run state!"%(id(obj))
 
    def exec_state(self, obj):
        print "Car%s in run state!"%(id(obj))
        obj.go()
 
    def exit_state(self, obj):
        print "Car%s exit run state!"%(id(obj))

stop_fsm 和 run_fsm 都继承自 base_fsm,base_fsm,是一个纯虚的接口类:
class base_fsm(object):
    def enter_state(self, obj):
        raise NotImplementedError()
 
    def exec_state(self, obj):
        raise NotImplementedError()
 
    def exit_state(self, obj):
        raise NotImplementedError()

enter_state 在 obj 进入某状态的时候调用——通常用来做一些初始化工作;exit_state 也离开某状态的时候调用——通常做一些清理工作;而 exec_state 则在每一帧的时候都会被调用,通常做一些必要的工作,如检测自己的消息队列并处理消息等。在 stop_fsm 和 run_fsm 两个类的 exec_state 函数中,就调用了对象的 stop 和 go 函数,让汽车保持原有的状态。
至现在为止,Car 还没有接触到 FSM,所以我们需要提供一个接口,可以让它拥有一个 FSM:

def attach_fsm(self, state, fsm):
        self.fsm = fsm
        self.curr_state = state

我们还需要为Car提供一个状态转换函数:
def change_state(self, new_state, new_fsm):
        self.curr_state = new_state
        self.fsm.exit_state(self)
        self.fsm = new_fsm
        self.fsm.enter_state(self)
        self.fsm.exec_state(self)

为Car提供一个保持状态的函数:
def keep_state(self):
        self.fsm.exec_state(self)

现在只有两个状态,但我们知道需求随时会改动,所以我们最好弄一个状态机管理器来管理这些状态:
class fsm_mgr(object):
    def __init__(self):
        self._fsms = {}
        self._fsms[0] = stop_fsm()
        self._fsms[1] = run_fsm()
       
    def get_fsm(self, state):
        return self._fsms[state]
       
    def frame(self, objs, state):
        for obj in objs:
            if state == obj.curr_state:
                obj.keep_state()
            else:
                obj.change_state(state, self._fsms[state])

fsm_mgr 最重要的函数就是 frame,在每一帧都被调用。在这里,frame 根据对象现在的状态和当前的输入决定让对象保持状态或者改变状态。
这时候,我们的实例基本上完成了。但我们还要做一件事,就是建立一个世界(World)来驱动状态机:

class World(object):
    def init(self):
        self._cars = []
        self._fsm_mgr = fsm_mgr()
        self.__init_car()
 
    def __init_car(self):
        for i in xrange(1):   # 生产汽车
            tmp = Car()
            tmp.attach_fsm(0, self._fsm_mgr.get_fsm(0))
            self._cars.append(tmp)
 
    def __frame(self):
        self._fsm_mgr.frame(self._cars, state_factory())
 
    def run(self):
        while True:
            self.__frame()
            sleep(0.5)

从代码可见,World 里有 Car 对象,fsm_mgr 对象;在 run 函数里,每 0.5s 执行一次 __frame 函数(FPS = 2),而 __frame 函数只是驱动了 fsm_mgr 来刷新对象,新的命令是从 state_factory 函数里取出来的,这个函数用以模拟驾驶员的操作(按下 Stop 或者 Run 按钮之一):
def state_factory():
    return random.randint(0, 1)

现在我们就要初始化世界(World)可以跑起我们的 FSM 了!
if __name__ == "__main__":
    world = World()
    world.init()
    world.run()

用 python 解释器执行上面的代码,我们可以看到程序不停地输出 Car 的状态:
引用
......
Car8453392 exit run state!
Car8453392 enter stop state!
Car8453392 in stop state!
Stop!!!
Car8453392 in stop state!
Stop!!!
Car8453392 exit stop state!
Car8453392 enter run state!
Car8453392 in run state!
Goooooo!!!
Car8453392 exit run state!
Car8453392 enter stop state!
Car8453392 in stop state!
Stop!!!
Car8453392 exit stop state!
Car8453392 enter run state!
Car8453392 in run state!
Goooooo!!!
Car8453392 in run state!
Goooooo!!!
Car8453392 exit run state!
Car8453392 enter stop state!
Car8453392 in stop state!
Stop!!!
Car8453392 in stop state!
Stop!!!
Car8453392 exit stop state!
Car8453392 enter run state!
Car8453392 in run state!
Goooooo!!!
......


结论

这时再回头来看看我们之前的问题:
  • 1、玩家想要功能更多的 Car,比如掉头
  • 我们可以通过为 Car 增加一个调头(back)的方法来执行掉头,然后从 base_fsm 中继承一个 back_fsm 来处理调头。之后在 fsm_mgr 里增加一个 back_fsm 实例,及让 state_factory 产生调头指令。听起来似乎比之前 while+if 的方式还要麻烦不少,其实不然,这里只有 back_fsm 和为 fsm_mgr 增加 back_fsm 实例才是特有的,其它步骤两种方法都要执行。
  • 2、玩家要更多的 Car
  • 这对于面向对象的 FSM 实现就太简单了,我们只要把 World.__init_car 里的生产数量修改一下就行了,要多少有多少。
  • 3、玩家要更多型号的车,如 Truck
  • 从 Car 派生一个 Truck,然后增加装货、卸货的接口。最大的改动在于 Truck 状态转换的时候需要一些判断,如不能直接从装货状态转换到开动状态,而是装货、停止再开动。
    通过这几个简单的问题分析,我们可以看到,使用面向对象的方式来设计 FSM,在需求变更的时候,一般都只增删代码,而仍少需要改动已有代码,程序的扩展性、适应性和健壮性都得很大的提高;因此,在世界庞大、物种烦多、状态复杂且条件交错的游戏开发中应用面向对象的 FSM 实在是明智之选。还有一点,面向对象的 FSM 可以非常容易地模拟消息机制,这有利于模块清晰化,更容易设计出正交的程序。


    本文发表于恋花蝶的博客:http://blog.csdn.net/lanphaday,欢迎转载,但必须保留文章内容完整且包含本声明。违者必究。
    分享到:
    评论

    相关推荐

      有限状态自动机,JAVA实现

      本篇将深入探讨如何使用Java语言实现有限状态自动机,并通过分析JFA_0.1_src这个压缩包中的源码,来理解其实现细节。 有限状态自动机分为几种类型,包括确定有限状态自动机(Deterministic Finite Automaton,DFA)...

      不确定有限状态自动机的确定化

      不确定有限状态自动机的确定化,以及原理和源程序。

      Finite automaton 有限状态自动机

      Finite Automaton 有限状态自动机的实现方法: * 硬件实现:Finite Automaton 可以使用硬件来实现,例如使用状态机器来实现 Finite Automaton。 * 软件实现:Finite Automaton 可以使用软件来实现,例如使用编程...

      乔明达:有限状态自动机.pdf

      有限状态自动机(FSM "finite state machine" 或者FSA "finite state automaton" )是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多...

      形式语言与自动机:第五讲 有限状态自动机 -正规表达式

      "形式语言与自动机:第五讲 有限状态自动机 -正规表达式" 形式语言与自动机是计算机科学中的一门重要课程,本讲主要介绍了有限状态自动机和正规表达式之间的关系。 有限状态自动机(Finite Automaton)是一种计算...

      AhoCorasickAutomation.rar_字符串字典_有限状态自动机

      总结起来,Aho-Corasick算法是字符串匹配领域的一个强大工具,它通过字典树和有限状态自动机的结合,实现了对多个模式串的一次性高效查找。在处理大量字符串匹配问题时,该算法能够显著提高性能,节省计算资源。在...

      有限状态自动机(NFA)的确定化

      编译原理实验 输入有限(穷)状态自动机,输出确定化的有限(穷)状态自动机

      有限自动机 VC++ MFC

      在实现有限自动机时,我们首先需要定义状态和转换函数。状态是有限自动机在处理输入时可能处于的不同情况。每个状态都有一个或多个转换,这些转换定义了当特定输入字符出现时,自动机应如何从当前状态转移到另一个...

      一种通过模糊有限状态自动机识别设计模式的方法

      一种通过模糊有限状态自动机识别设计模式的方法 设计模式的识别方法

      编译原理有穷状态自动机

      判断一个文法是否为正则文法,可以通过构造对应的有穷状态自动机来实现,如果能够构造出与文法对应的DFA或NFA,则该文法是正则的。 实验二有穷状态自动机可能涉及的实际操作包括: 1. 设计和构建DFA或NFA模型,...

      确定有限状态自动机(DFA)的Matalb代码实现

      确定有限状态自动机(DFA)的Matalb代码实现,自行编写,代码可跑通,有问题请下载后联系作者!

      有限状态自动机

      有限状态自动机(Finite State Machine,FSM)是一种计算模型,用于描述具有有限数量状态的系统行为。在计算机科学和信息技术领域,它们被广泛应用于语言识别、编译器设计、网络协议、数据解析等多个场景。有限状态...

      FSM自动机,实现状态转换

      有限状态机(Finite State Machine,FSM)是计算机科学中的一个重要概念,广泛应用于计算机科学、电子工程、语言学等多个领域。它是一种数学模型,用来描述一个系统随时间变化的行为。在这个模型中,系统处于一系列...

      基于java实现的有限状态自动机,轻松,快捷,高效的关联状态的扭转.zip

      在Java中实现有限状态自动机,通常涉及以下几个关键部分: 1. **状态定义**:首先,我们需要定义各种可能的状态,这可以通过创建一系列枚举类型(enum)来实现。枚举类型允许我们列举出所有可能的状态,并且确保...

      不确定的有限状态自动机

      不确定的有限状态自动机(NFA,Non-Deterministic Finite Automaton)是计算机科学和理论编程中的一个重要概念,尤其在正则表达式处理和形式语言理论中占据着核心地位。NFA是一种数学模型,用于识别和解析特定的字符...

      论文研究-不确定DNA有限状态自动机在基因网络中的应用 .pdf

      不确定DNA有限状态自动机在基因网络中的应用,李汪根,,传统电子计算机产生的随机数是伪随机数,因而其随机算法不是严格意义上的随机计算。由于生化反应的随机性,随机分子生物计算机比

      有限自动机理论3章有限状态自动机.ppt

      有限自动机理论3章有限状态自动机.ppt

      论文研究-基于有限状态自动机的人眼开度PERCLOS实现算法.pdf

      针对驾驶员驾驶过程中因疲劳引起的眼睛开度变化问题,在原有PERCLOS(percentage of eyelid closure over the pupil over time)标准的基础上,提出了一种基于有限状态自动机的人眼开度PERCLOS计算方法,并将其应用...

      形式语言与自动机:第五讲 有限状态自动机 -正规表达式.pdf

      【形式语言与自动机:第五讲 有限状态自动机 -正规表达式】 形式语言与自动机是一门重要的计算机科学理论,主要研究如何描述和处理不同的字符串集合,即语言。有限状态自动机(Finite State Automata, FSA)是这类...

      形式语言与自动机理论--第三章 有限状态自动机

      是形式语言与自动机理论--第三章 有限状态自动机的ppt

    Global site tag (gtag.js) - Google Analytics