【转载】 Deepmind星际争霸2平台使用第一轮-完成采矿

原文地址:

https://blog.csdn.net/woaipichuli/article/details/78645999

-----------------------------------------------------------------------------------------

这篇博文简单的介绍PySC2的基本使用以及分析一份网友提供的代码(使用DQN来让计算机玩星际2)。

##1-PySC2 ##

Deepmind公布的这段python与PySC2通信的源码主要包含以下的几个方面(原图来于博客):

 

这里我们只讲解对于强化学习算法开发过程当中需要用到的一些内容:主要就是env
在env当中包装了观测量集合、动作集合、状态推进功能、状态重置功能等,它所具有的实例化参数如下原图来于博客

 

观测量集合
可以通过SC2Env的实例获得,也可以利用step方法返回的元组来获得,内容都是以python当中的字典的方式进行组织的。

动作集合
有了观测值,我们就具有了动作部分所需要的输入,星际2当中的动作空间非常的大,就单纯考虑其中的动作类型就具有524个动作函数:

python -m pysc2.bin.valid_vactions

对于这些函数都是具有其函数ID的,我们可以通过FunctionCall来完成调用,返回的是一个action。


状态推进
既然有了动作值,那么我们怎么进一步的去推进游戏进程呢?这里使用的方法与Gym当中一样,使用step方法就可以完成,返回的内容是一个环境的字典(前面已经提到过了)。


状态重置
星际争霸是一个没有固定时限的测试环境,我们当然可以把时间设置成无穷大,让游戏结束的时候返回,但往往为了效率,我们可以设置一个时间的长度,当到达这个时间长度以后对环境状态进行reset即可。

2-挖矿代码解读

这是一个网友分享出来的代码当中的一个部分,在这段代码当中他使用了一个DQN网络对星际争霸2当中的挖矿任务进行了测试:

#导入需要的模块
import sys

from absl import flags
import baselines.common.tf_util as U
import numpy as np
from baselines import deepq
from pysc2.env import environment
from pysc2.env import sc2_env    #导入环境模块
from pysc2.lib import actions
from pysc2.lib import actions as sc2_actions   #导入动作模块
from pysc2.lib import features

import deepq_mineral_shards

#获取屏幕上的个体信息,其实就是一个矩阵,里面0表示环境中可以移动的位置,1表示队友,3表示矿产
_PLAYER_RELATIVE = features.SCREEN_FEATURES.player_relative.index
_PLAYER_FRIENDLY = 1
_PLAYER_NEUTRAL = 3  # beacon/minerals
_PLAYER_HOSTILE = 4
_NO_OP = actions.FUNCTIONS.no_op.id
#记录相应动作函数的ID
_MOVE_SCREEN = actions.FUNCTIONS.Move_screen.id
_ATTACK_SCREEN = actions.FUNCTIONS.Attack_screen.id
_SELECT_ARMY = actions.FUNCTIONS.select_army.id
_NOT_QUEUED = [0]
_SELECT_ALL = [0]

step_mul = 16
steps = 400

FLAGS = flags.FLAGS


def main():
  FLAGS(sys.argv)
  #实例化环境
  with sc2_env.SC2Env(
      map_name="CollectMineralShards",
      step_mul=step_mul, #推进的速度,通俗理解就是人类玩家的每秒的有效操作
      visualize=True,  #是否可视化
      game_steps_per_episode=steps * step_mul #每轮的运行步长,None则表示没有时间限制
      ) as env:

    #调用Baselines中的deepq,以后会再做讲解
    model = deepq.models.cnn_to_mlp(
      convs=[(32, 8, 4), (64, 4, 2), (64, 3, 1)],
      hiddens=[256],
      dueling=True)

    def make_obs_ph(name):
      return U.BatchInput((64, 64), name=name)

    act_params = {
      'make_obs_ph': make_obs_ph,
      'q_func': model,
      'num_actions': 4,
    }
    #导入训练好的模型参数
    act = deepq_mineral_shards.load(
      "mineral_shards.pkl", act_params=act_params)

    while True:
      #环境初始化
      obs = env.reset()
      episode_rew = 0

      done = False
      #这里固定了第一步的操作:选择所有的个体,一共就两个农民,利用step执行该命令,获得新的环境
      step_result = env.step(actions=[
        sc2_actions.FunctionCall(_SELECT_ARMY, [_SELECT_ALL])
      ])

      while not done:
        #查看返回的字典中屏幕中的目标关系分布图:1表示着地图中个体的位置,3表示着矿物的位置
        #这里面因为一个个体可能占好几个像素点,所以个体可能是有1构成块状体表示
        player_relative = step_result[0].observation["screen"][
          _PLAYER_RELATIVE]

        obs = player_relative
        #筛选里面个体是盟军的整列,变成了一个0-1矩阵,盟军的位置表示为1
        player_y, player_x = (
          player_relative == _PLAYER_FRIENDLY).nonzero()
        #计算平均位置player_x表示各个1所在的位置x坐标集合,player_y则是y坐标上的集合
        #注意这里是一个位置矩阵,其实就是行列坐标
        player = [int(player_x.mean()), int(player_y.mean())]
        #shift函数就是对屏幕视角进行中心化移动,然后将溢出矩阵的部分数据无效化(赋值为2)
        #这里是相当于DQN的输入的设定
        if (player[0] > 32):
          obs = shift(LEFT, player[0] - 32, obs)
        elif (player[0] < 32):
          obs = shift(RIGHT, 32 - player[0], obs)

        if (player[1] > 32):
          obs = shift(UP, player[1] - 32, obs)
        elif (player[1] < 32):
          obs = shift(DOWN, 32 - player[1], obs)

        #将观察情况输入到Baselines中的act函数里,获得返回的行为
        action = act(obs[None])[0]
        coord = [player[0], player[1]]
        #根据输出的action让个体在64*64的地图内进行移动,移动到矿物所在的位置就可以进行采矿
        #这里设置移动的步长都是16,这里是DQN生成的行为的解码
        if (action == 0):  #UP

          if (player[1] >= 16):
            coord = [player[0], player[1] - 16]
          elif (player[1] > 0):
            coord = [player[0], 0]

        elif (action == 1):  #DOWN

          if (player[1] <= 47):
            coord = [player[0], player[1] + 16]
          elif (player[1] > 47):
            coord = [player[0], 63]

        elif (action == 2):  #LEFT

          if (player[0] >= 16):
            coord = [player[0] - 16, player[1]]
          elif (player[0] < 16):
            coord = [0, player[1]]

        elif (action == 3):  #RIGHT

          if (player[0] <= 47):
            coord = [player[0] + 16, player[1]]
          elif (player[0] > 47):
            coord = [63, player[1]]

        #在这里我们需要对DQN生成的行为转化为星际争霸里面的操作
        new_action = [
          sc2_actions.FunctionCall(_MOVE_SCREEN,
                                   [_NOT_QUEUED, coord])
        ]

        step_result = env.step(actions=new_action)
        #获取游戏反馈的回报值,也就是矿物的采集情况
        rew = step_result[0].reward
        #判断是否需要进入下一轮游戏
        done = step_result[0].step_type == environment.StepType.LAST

        episode_rew += rew
      print("Episode reward", episode_rew)


UP, DOWN, LEFT, RIGHT = 'up', 'down', 'left', 'right'


def shift(direction, number, matrix):
  ''' shift given 2D matrix in-place the given number of rows or columns
    in the specified (UP, DOWN, LEFT, RIGHT) direction and return it
'''
  if direction in (UP):
    matrix = np.roll(matrix, -number, axis=0)
    matrix[number:, :] = -2
    return matrix
  elif direction in (DOWN):
    matrix = np.roll(matrix, number, axis=0)
    matrix[:number, :] = -2
    return matrix
  elif direction in (LEFT):
    matrix = np.roll(matrix, -number, axis=1)
    matrix[:, number:] = -2
    return matrix
  elif direction in (RIGHT):
    matrix = np.roll(matrix, number, axis=1)
    matrix[:, :number] = -2
    return matrix
  else:
    return matrix


if __name__ == '__main__':
  main()

----------------------------------------------------------------------------------------------------

参考资料地址:

https://zhuanlan.zhihu.com/p/28434323

原文地址:https://www.cnblogs.com/devilmaycry812839668/p/10676023.html