阅读完前辈的关于游戏中的人工智能的博客后,突然觉得游戏变得好有趣,人工智能不仅可以让超级玛丽的乌龟追着你跑而不是在墙壁之间做往复运动,也可以模拟小动物的生活习性。
主要内容: 人工智能初探、有限状态机、transitions库
以前我们看到的超级玛丽里面的乌龟动作就是在墙壁之间晃悠,只会撞墙,就算有人经过也没有变化…乌龟的行为可以这样描述:
self.move_forward()
if self.hit_wall():
self.change_direction()
我们是否可以让它变得有趣一些。比如这样改改:
if self.state == "exploring":
self.random_heading()
if self.can_see(player):
self.state = "seeking"
elif self.state == "seeking":
self.head_towards("player")
if self.in_range_of(player):
self.fire_at(player)
if not self.can_see(player):
self.state = "exploring"
这样,我们的小乌龟就获得了两种状态:搜寻和攻击,没人时它在搜寻晃悠,人出现便进入攻击状态,直到人消失在视野内。通过简单优化,小乌龟就像变聪明了。
有限状态机(finite-state machine)简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。一个状态机包含初始状态、结束状态、有限状态集合以及状态转移条件,我们现在来创建一个简单的状态机:
step1: 画出状态图
如图,我们定义3个状态:solid、gas、liquid和6个状态转移条件。
step2: 写出状态转移函数
一般,一个状态函数包含状态内的行为和状态转移判断两部分,可以用下面的方式描述状态转移函数:
def state():
do_something()
if action == 'state_i':
return 'state1_i'
最后,我们得到这样的状态转移函数:
def Solid():
print("current state: solid")
action = input(">>")
if action == "sublime":
return "gas"
if action == "melt":
return "liquid"
if action == "quit":
return "exit"
else:
Solid()
def Liquid():
print("current state: liquid")
action = input(">>")
if action == "gasify":
return "gas"
if action == "breeze":
return "solid"
if action == "quit":
return "exit"
else:
Liquid()
def Gas():
print("current state: gas")
action = input(">>")
if action == "desublime":
return "solid"
if action == "liquiefy":
return "liquid"
if action == "quit":
return "exit"
else:
Gas()
step3: 搭建状态名和状态转移函数的联系
每个状态有其对应的状态转移函数,当进入这个状态的时候就要调用状态转移函数,并返回为下一个状态,这样就实现了状态之间的转化。我们用一个字典来把状态和其状态转移函数对应起来:
state_actions = {
'solid': Solid,
'liquid': Liquid,
'gas': Gas
}
step4: 开启状态机循环
我们初始化状态机的状态为固态solid,之后我们使用while语句循环,状态转移函数返回exit状态时,停止循环(退出状态机),否则,下一状态等于当前状态的状态转移函数的返回值。这样我们就可以从一个状态到另一个状态不断循环了。
def run():
state = 'solid'
#状态机开始循环
while state != 'exit':
state = state_actions[state]()
return
run()
最后一行代码中,state_actions[state]为函数名,即state的状态转移函数,即表示下一状态为状态转移函数的返回值。
step5: 运行结果
将上面3部分代码拼接起来运行,可以得到可以正常运行的状态机:
current state: solid
>> melt
current state: liquid
>> gasify
current state: gas
>> liquiefy
current state: liquid
>> breeze
current state: solid
>> quit
***Repl Closed***
前面我们提到了如何自己写一个状态机,看起来过程其实有点复杂。我们可以直接使用python的transitions库,transitions库就是一个用来设计状态机的库。
step1: 安装transitions库
transitions库是一个比较冷门的库,我们可以使用pip安装它,得到successfully提示就安装成功了:
pip install transitions
step2: 使用transitions库
我们直接西安看一段简单的代码:
from transitions import Machine
class Matter(object):
pass
model = Matter()
# 定义状态
states = ["solid", "liquid", "gas"]
# 定义状态转移
transitions = [
{'trigger':'melt', 'source': 'solid', 'dest': 'liquid'},
{'trigger':'sublime', 'source': 'solid', 'dest': 'gas'},
{'trigger':'gasify', 'source': 'liquid', 'dest': 'gas'},
{'trigger':'freeze', 'source': 'liquid', 'dest': 'solid'},
{'trigger':'desublime', 'source': 'gas', 'dest': 'solid'},
{'trigger':'liquiefy', 'source': 'gas', 'dest': 'liquid'},
]
# 初始化
machine = Machine(model=model, states=states, transitions=transitions, initial='solid')
# melt - gasify - liquiefy - freeze
triggers = ["model.melt()", "model.gasify()", "model.liquiefy()", "model.freeze()"]
for trigger in triggers:
print(model.state)
eval(trigger)
在上面的例子中,我们定义了一个物质类Matter,定义了一个model的状态机。
构建状态机
1、首先定义了一个状态列表
states = ["solid", "liquid", "gas"]
2、在定义了状态转移方程列表:对于每一个转移方程,都包含触发条件trigger,起始状态source和目标状态dest,这些状态都是一个状态名,用字符串表示
{'trigger':'melt', 'source': 'solid', 'dest': 'liquid'},
3、将状态、状态转移方程和对象联系起来
machine = Machine(model=model, states=states, transitions=transitions, initial='solid')
这里调用了Machine构造函数,指定对象为model,状态集合为states,转移条件为transitions,初始状态为solid。下面是Machine()的定义,包含了可选择的参数:
def __init__(self, model='self', states=None, initial='initial', transitions=None,
send_event=False, auto_transitions=True,
ordered_transitions=False, ignore_invalid_triggers=None,
before_state_change=None, after_state_change=None, name=None,
queued=False, prepare_event=None, finalize_event=None, **kwargs):
4、我们运行状态机时,分别用了四个触发条件:melt - gasify - liquiefy - freeze,初始状态为固态,输出得到:
solid
liquid
gas
liquid
***Repl Closed***
第 5 篇pygame学习笔记完结 cheers! ?
附上:源代码
提取码:xggc
参考博客:用Python和Pygame写游戏-从入门到精通
图标引用:Iconfont