作为应用于强化学习的SCII环境是被定义在 pysc2.env.sc2_env
中的,(action 和 observation 空间是定义在 pysc2.lib.features
中的),环境的类即 SC2Env
,继承自 pysc2.env.environment.Base
类。可以作为使用一般的gym库的强化学习的环境一样使用它。
要实例化并使用一个 SC2Env
,需要传入几个重要的参数,完整的参数列表如下:
_only_use_kwargs=None,
map_name=None,
battle_net_map=False,
players=None,
agent_interface_format=None,
discount=1.,
discount_zero_after_timeout=False,
visualize=False,
step_mul=None,
realtime=False,
save_replay_episodes=0,
replay_dir=None,
replay_prefix=None,
game_steps_per_episode=None,
score_index=None,
score_multiplier=None,
random_seed=None,
disable_fog=False,
ensure_available_actions=True,
version=None
一些重要的参数说明如下:
map_name
:地图的名称,一个字符串
pysc2.bin.map_list
中,可以使用如下的命令来列出所有的地图名称:python -m pysc2.bin.map_list
melee_maps = ["Flat32", "Flat48", "Flat64", "Flat96", "Flat128", "Simple64", "Simple96", "Simple128",]
players
:传入一个list,list中是一个或两个 pysc2.env.sc2_env.Agent
或 pysc2.env.sc2_env.Bot
的实例,目前只支持一个或者两个,一个就是单 Agent 与环境交互,两个就是互相对战,有些地图只能传入一个。注意,就是使用一个 Agent
或 Bot
的实例时,也要使用 list 包裹。
pysc2.env.sc2_env.Agent
和 pysc2.env.sc2_env.Bot
是不同于我们前文中所提到的自定义的继承自 pysc2.agetns.base_agent
的,这是两个不同的类,这里使用的 Agent 和 Bot 用于定义与环境交互的player的类型、名称、种族、困难度等信息,这两个类的定义很简单:class Agent(collections.namedtuple("Agent", ["race", "name"])):
"""Define an Agent. It can have a single race or a list of races."""
def __new__(cls, race, name=None):
return super(Agent, cls).__new__(cls, to_list(race), name or "<unknown>")
class Bot(collections.namedtuple("Bot", ["race", "difficulty", "build"])):
"""Define a Bot. It can have a single or list of races or builds."""
def __new__(cls, race, difficulty, build=None):
return super(Bot, cls).__new__(cls, to_list(race), difficulty, to_list(build or BotBuild.random))
agent_interface_format
:用于规定 observation 和 action 的形式,比如地图或者feature layers的分辨率等,接受 pysc2.lib.features.AgentInterfaceFormat
实例,或者 pysc2.env.sc2_env.AgentInterfaceFormat
实例。(实际上两者相同,sc2_env.py
中使用了 = 将其两者赋值)
AgentInterfaceFormat
的实例同时应用于两个player,或者传入一个包含两个 AgentInterfaceFormat
的list,和players的顺序一致地分别应用于两个playerstep_mul
参数接收一个整数,可以理解为:每次采集observation和应用action之间跳过多少个帧(但是不是实际上的帧数)。1 秒相当于 16 steps,如果这里设置为 16
,就代表每秒采集一次。game_steps_per_episode
:一代多少个 steps,比如这里设置为 200*16
,就表示一代经历 200 秒结束save_replay_episodes
:多少代保存一次replayreplay_dir
:replay保存在哪个位置定义了自己的env以及agent,就可以像使用其他gym环境一样进行交互了,一个很好的官方提供的例子就是这个 pysc2/env/run_loop.py 文件下的 run_loop
函数,它接收一个/两个 agent(s) 实例和一个 env 实例,运行若干帧/代:
def run_loop(agents, env, max_frames=0, max_episodes=0):
"""A run loop to have agents and an environment interact."""
total_frames = 0
total_episodes = 0
start_time = time.time()
observation_spec = env.observation_spec()
action_spec = env.action_spec()
for agent, obs_spec, act_spec in zip(agents, observation_spec, action_spec):
agent.setup(obs_spec, act_spec)
try:
while not max_episodes or total_episodes < max_episodes:
total_episodes += 1
timesteps = env.reset()
for a in agents:
a.reset()
while True:
total_frames += 1
actions = [agent.step(timestep) for agent, timestep in zip(agents, timesteps)]
if max_frames and total_frames >= max_frames:
return
if timesteps[0].last():
break
timesteps = env.step(actions)
except KeyboardInterrupt:
pass
finally:
elapsed_time = time.time() - start_time
print("Took %.3f seconds for %s steps: %.3f fps" % (elapsed_time, total_frames, total_frames / elapsed_time))
最后再来看一下官方提供的,实例化一个 SC2Env
实例,一个 Agent
实例,然后使用上述的 run_loop
函数进行测试的例子,若干个例子可以在 pysc2/tests/easy_scripted_test.py 中找到:
def test_move_to_beacon(self):
with sc2_env.SC2Env(
map_name="MoveToBeacon",
players=[sc2_env.Agent(sc2_env.Race.terran)],
agent_interface_format=sc2_env.AgentInterfaceFormat(
feature_dimensions=sc2_env.Dimensions(
screen=84,
minimap=64)),
step_mul=self.step_mul,
game_steps_per_episode=self.steps * self.step_mul) as env:
agent = scripted_agent.MoveToBeacon()
run_loop.run_loop([agent], env, self.steps)
这里使用了 with as
的形式,我们需要创建长久的交互式直接使用 evn = SC2Env(…)
即可。
启动真正的SCII程序需要使用 absl.app
下的 run
函数,然后传入一个运行我们上述代码的主要函数作为参数,比如我们设置了一个主函数来设置env、agent等,以及完成了基本的循环条件,把这些都封装在了一个名为 main
的函数中,最后,需要使用 absl.app.run(main)
来启动真正的 SCII 程序。一个完整的例子如下:
from pysc2.env import run_loop, sc2_env
from pysc2.agents import random_agent
from absl import app
def main(args):
agent = random_agent.RandomAgent()
with sc2_env.SC2Env(map_name="MoveToBeacon", players=[sc2_env.Agent(sc2_env.Race.terran)],
agent_interface_format=sc2_env.AgentInterfaceFormat(
feature_dimensions=sc2_env.Dimensions(screen=84, minimap=64)), step_mul=16,
game_steps_per_episode=200 * 16, visualize=True) as env:
run_loop.run_loop([agent], env, 200)
if __name__ == "__main__":
app.run(main)
几个非常需要注意的点:
app.run
内传入的参数,第一个参数是必须传入的,是主函数的名字,不带括号和参数,第二个参数可选,当主函数需要参数的时候,在第二个参数这里提供,需要将所用到的参数打包成list作为参数传入main
函数时,必须为其设置至少一个参数,比如这里的 args
,这个参数可以不在函数中使用,但是如果只定义 def main()
就会报错run_loop
函数,传入时,第一个参数为我们的 agent 们,即使只有一个,也要打包成list传入重要参考官方文档:https://github.com/deepmind/pysc2/blob/master/docs/environment.md#rl-environment
参考一篇blog:https://itnext.io/build-a-zerg-bot-with-pysc2-2-0-295375d2f58e