`
javayestome
  • 浏览: 1008999 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

[python]有限状态机(FSM)简单实现

阅读更多
本文发表于恋花蝶的博客http://blog.csdn.net/lanphaday,欢迎转载,但必须保留文章内容完整且包含本声明。违者必究。
[python]有限状态机(FSM)简单实现
简述
有限状态机(以下用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可以非常容易地模拟消息机制,这有利于模块清晰化,更容易设计出正交的程序。
分享到:
评论

相关推荐

    fsm:使用具有内置模型检查功能的antlr4的基于Python的有限状态机编译器

    fsm 基于Python的有限状态机编译器,使用带有内置模型检查功能的antlr4。

    fsm:Go的有限状态机

    FSM是Go的有限状态机。 它很大程度上基于两个FSM实现: Javascript有限状态机, 适用于Python的Fysom, (在分叉) 有关API文档和示例,请参见 基本范例 从examples / simple.go: package main import ( ...

    Qfsm:图形化有限状态机(FSM)设计器。-开源

    一种图形工具,用于设计有限状态机并将其导出到硬件描述语言,例如C,C ++,Objective-C,Java,Python,PHP,Perl,Lua代码生成的VHDL,AHDL,Verilog或Ragel / SMC文件。

    Python_DNA_FSM

    使用有限状态机寻找基因。 目标 輸入一串以核甘酸ATGC組成的染色體序列,三個為一組基因碼,以ATG為開頭,結束於TAA、TAG或TGA,若序列中 找不到基因碼則顯示no gene is found 条件限制 1.基因碼為3的倍數 2.只有1...

    django-fsm:Django友好的有限状态机支持

    django-fsm:Django友好的有限状态机支持

    pyeds:Python 事件驱动系统

    该软件包提供了一种有效地手动编写有限状态机 (FSM) 的方法。 重点是使 API 尽可能简单,因为不包含用于定义 FSM 的 GUI 工具。 这个包允许你创建有/没有状态层次结构的状态机。 安装 使用画中画 可以使用标准的...

    Python, PyGame, and Raspberry Pi Game Development, 2nd Edition.pdf

    您将了解面向对象编程(OOP)以及设计模式,如模型视图控制器(MVC)和有限状态机(FSM)。无论是使用Windows、MacOS、Linux还是Raspberry PI,您都可以释放Python和PyGames的力量来创建漂亮的游戏。这本书还包括完整的...

    欧拉公式求圆周率的matlab代码-python-graphwalker:Python重新实现graphwalker测试工具

    Python-Graphwalker是用于基于有限状态机图进行测试的工具。 Graphwalker读取图指定的FSM,计划路径,从图标签按名称调用模型方法,并报告进度和结果。 虽然从Graphwalker项目(从Java实现)的概念上衍生而来,但这...

    pyfsm:有限状态机变得容易

    这是一个旨在帮助实现有限状态机的库。 还不稳定 要求 pip install -r requirements.txt 用法 有限状态机必须在json中定义。 请参阅提供的示例。 from pyfsm . finite_state_machine import FiniteStateMachine ...

    python-to-verilog:为给定的Moore FSM生成Verilog源文件和testbench文件-python source file

    python程序获取一个描述状态机的配置文件,并为其生成源文件和verilog测试平台文件。 输入状态机配置 在出样本输入状态机文件 输入测试文件名 检查样本输入测试文件@ 怎么跑 Verilog源文件生成—要生成源文件module...

    polymorphic-csv

    CSV CSV dsl在CSV上运行,并允许以下操作: 打开写打印行数CSV dsl编译为: Javajava + python(使用csv库)有限状态机FSM dsl允许定义由命名状态和转换组成的简单FSM。 它还提供格式完善的规则,例如最终节点的...

    re-core:发布引擎——有限状态机(核心组件)

    核心本质上是一个连接到消息总线和数据库的有限状态机 ( FSM )。 核心监督任何给定项目的所有发布步骤的执行。 核心与每个发布步骤的实际执行是分开的。 执行委托给工作组件。 有关文档,请参阅文档。

    dfa-minimization

    执行和测试文件用于无输出的有限状态机该文件可以在终端中以以下方式执行: python file_name.py test_filename.fsm提供了示例有限状态机文件demo.fsm ,如果终端中未提供test_filename.fsm ,则将其解析为默认值。...

    django-fsm-log:Django FSM的自动日志记录

    Django有限状态机日志 自动记录出色的软件包。 通过启用缓存的后端,可以在发生过渡之前和将日志持久保存到数据库之前访问日志。 请参阅 变更日志 2.0.2-dev(未发布) 2.0.1(2020-03-26) 添加对django3.0的...

    cozmo-tools:Anki Cozmo机器人编程工具

    输入以下python3 simple_cli来运行它: python3 simple_cli cozmo_fsm是用于Cozmo编程的有限状态机软件包。 genfsm是一个预处理器,可以将以cozmo_fsm表示法编写的.fsm文件转换为可以运行的.py文件。 注意:您可以...

    marmoolak:另一个带有内存和回调的有限状态机

    马尔穆拉克 安装 pip install marmoolak 用法 import marmoolak marmoolak.REDIS_HOST = '192.168.99.100' marmoolak.REDIS_PORT = 6379 machine = marmoolak.Machine ...fsm = machine('myname', 'version

    telegram-xkcd-password-generator:电报的可读密码生成器(Bot API)

    该机器人的灵感来自著名的 Strip。 立即尝试: : 产品特点 不同复杂性的预设; ... 具有彩色复杂性的内联模式;... 作为Aiogram的有限状态机(FSM)的后端; –不用说:) 您可以使用pip install -r requirements

    speech_nlp_ntua:NTUA(2018)的实验室练习,课程为语音和自然语言处理,第七学期

    speech_nlp_ntua NTUA(2018)的语音和自然语言处理课程第七学期的... 为了完成此任务,我们创建了有限状态机(FSM)管道,其中每个FSM都有不同的角色。 遵循的方法是: 第一步:训练FSM 拉丁字母与希腊字母之间的

    Chimaera:KG + NLP + LISP + Prolog + ANN + Hook =垃圾

    用知识图谱(KG)存储有限状态机(FSM) 用二级存储状态标志符号(SFS),用关系存储状态转移函数符号(STFS) 在此基础上利用多种工具操作,解析矩阵-关系信息 模块2。 NLTK 用于使用自然语言命令行进行交互,创建...

Global site tag (gtag.js) - Google Analytics