詳解 pysc2 中的 Observation 和 Action
關(guān)于pysc2使用的全部內(nèi)容請(qǐng)參考:【文集:pysc2的簡單教程】
SCII 環(huán)境具有非常豐富的動(dòng)作空間和狀態(tài)空間。Pysc2 接口從游戲中讀取并提供給我們使用的信息有兩類:空間/視覺類的(spatial/visual);結(jié)構(gòu)性的元素(structured elements),其中結(jié)構(gòu)性的元素主要是提供一些重要的數(shù)據(jù),而且這些數(shù)據(jù)對(duì)于機(jī)器來說往往很難通過游戲畫面直接讀取,比如燃料的數(shù)量,所以接口直接提供這些數(shù)據(jù)。
Important!
從observation中獲取得到的坐標(biāo)信息是 (y,x) 形式的,即縱坐標(biāo)寫在前,而有些action需要跟上一個(gè)屏幕/小地圖的坐標(biāo)位參數(shù),比如移動(dòng)某單位到哪個(gè)點(diǎn)去,這里需要傳入的坐標(biāo)是 (x,y) 形式的,即橫坐標(biāo)寫在前。坐標(biāo)原點(diǎn) (0,0) 都是左上角,y軸正方向向下,x軸正方向向右。
例如,一個(gè)將 observation 中獲取得到的坐標(biāo)傳遞給某個(gè) action 的例子:
# Spatial observations have the y-coordinate first:
y, x = (obs.observation["feature_screen"][_PLAYER_RELATIVE] == _PLAYER_NEUTRAL).nonzero()
# Actions expect x-coordinate first:
target = [int(x.mean()), int(y.mean())]
action = actions.FunctionCall.Move_screen("now", target)
Observation
Spitial/Visual
RGB Pixels
這一類Observation就是屏幕和小地圖真實(shí)顯示的畫面,即在規(guī)定的分辨率下所看到的游戲界面,和正常游戲看到的一致(除了分辨率較低),但是,不包括下邊的UI等信息。
Feature Layers
Feature layers 是對(duì)于一些游戲中重要信息的表示,并對(duì)這些信息進(jìn)行了結(jié)構(gòu)化處理,根據(jù)所顯示的范圍分為兩類:feature_screen 和 feature_minimap,即屏幕的feature和小地圖的feature,如下圖中所示的都是feature layers:

完整的列表在 pysc2.lib.features 中。
collections.namedtuple("ScreenFeatures", ["height_map", "visibility_map", "creep", "power", "player_id", "player_relative", "unit_type", "selected", "unit_hit_points", "unit_hit_points_ratio", "unit_energy", "unit_energy_ratio", "unit_shields", "unit_shields_ratio", "unit_density", "unit_density_aa", "effects", "hallucinations", "cloaked", "blip", "buffs", "buff_duration", "active", "build_progress", "pathable", "buildable", "placeholder"])
collections.namedtuple("MinimapFeatures", ["height_map", "visibility_map", "creep", "camera", "player_id", "player_relative", "selected", "unit_type", "alerts", "pathable", "buildable"])
Minimap(小地圖)
小地圖即對(duì)全局的低分辨率圖像,可以設(shè)置minimap的分辨率,一些主要的 features_minimap:
-
height_map:顯示地形特征 -
visibility:顯示地圖的哪一部分是隱藏的、已經(jīng)被看到的或當(dāng)前可見的 -
creep:哪些部分是蟲族勢力 -
camera:地圖的哪一部分在屏幕中可見 -
player_id:顯示所擁有的單位,以及其ID -
player_relative:顯示所有的單位是己方的還是地方的。 取[0,4]的值,分別表示[background, self, ally, neutral, enemy]([背景,自身,盟友,中立,敵人]單位 -
selected:選擇了哪些單位
Screen(屏幕)
屏幕顯示的即當(dāng)前玩家所看到的區(qū)域,擁有比小地圖更高的分辨率(of course),一些主要的 feature_screen:
-
height_map:顯示地形特征 -
visibility:顯示地圖的哪一部分是隱藏的、已經(jīng)被看到的或當(dāng)前可見的 -
creep:哪些部分是蟲族勢力 -
power:哪些部分是神族勢力,只會(huì)顯示你的勢力 -
player_id:顯示所擁有的單位,以及其ID -
player_relative:顯示所有的單位是己方的還是地方的。 取[0,4]的值,分別表示[background, self, ally, neutral, enemy]([背景,自身,盟友,中立,敵人]單位 -
unit_type:單位類型的ID,可以在pysc2/lib/units.py中查看完整列表 -
selected:選擇了哪些單位 -
hit_points:單位所具有的生命值 -
energy:設(shè)備所具有的能量值 -
shields:裝置所具有的防御值/盾值(僅適用于神族單位) -
unit_density:該像素中有多少個(gè)單位 -
unit_density_aa:unit_density的抗鋸齒版本,每個(gè)像素每單位最多16個(gè)
Structured(結(jié)構(gòu)化的數(shù)據(jù))
Structured(結(jié)構(gòu)化的)數(shù)據(jù)是那些無法從像素中直接讀取的信息,通過 tensor 表示。詳細(xì)的說明請(qǐng)參考:
https://github.com/deepmind/pysc2/blob/master/docs/environment.md#structured
這里列一下主要的結(jié)構(gòu)化信息有:
- 基本的玩家信息(General player information)
- 控制組信息(Control groups)
- 單獨(dú)選中的單位信息(Single Select)
- 多選中的單位信息(Multi Select)
- 多個(gè)選中的正在移動(dòng)中的單位信息(Cargo)
- 多個(gè)選中的正在修建的單位信息(Build Queue)
- 可用動(dòng)作(的id)(Available Actions)
- 上一輪中成功運(yùn)行的動(dòng)作(的id)(Last Actions)
- 給出動(dòng)作的結(jié)果(Action Result)
- 提醒被攻擊的信號(hào)(一般為空)(Alerts)
可以通過Debug清楚地看到這些變量(如何在PyCharm中對(duì)pysc2的Agent類進(jìn)行Debug):

Actions
函數(shù)化動(dòng)作(Function Actions)
SCII 環(huán)境中的動(dòng)作空間非常非常復(fù)雜且龐大,因此,pysc2 使用了函數(shù)化動(dòng)作(function actions),所有可用的動(dòng)作/函數(shù)類型定義在了 pysc2.lib.action 中的 ValidActions 類中,每一個(gè)動(dòng)作就是一個(gè) FunctionCall,同樣也定義在了 pysc2.lib.action 中。
There are hundreds of possible actions, many of which take a point in either screen or minimap space, and many of which take an additional modifier.
We created function actions that are rich enough to give composability, without the complexity of an arbitrary hierarchy.
collections.namedtuple("ValidActions", ["types", "functions"])
collections.namedtuple("FunctionCall", ["function", "arguments"])
簡單說就是,在pysc2中,每一個(gè)動(dòng)作(action)其實(shí)是一個(gè) FunctionCall 類的實(shí)例,需要兩個(gè)參數(shù)來確定,一個(gè)是 function_id,表示某個(gè)函數(shù)的id,另一個(gè)就是與該函數(shù)對(duì)應(yīng)的參數(shù)。比如已經(jīng)確定一個(gè)函數(shù)id,表示為 fun_id,以及對(duì)應(yīng)的滿足要求的參數(shù),表示為 args,此時(shí)一個(gè)真正的action就可以這樣寫:
(from pysc2.lib import actions)
action = actions.FunctionCall(fun_id, args)
值得一提的是,所有的函數(shù)及其類型均被定義在了 pysc2.lib.actions 中,如果你有自定義的環(huán)境需要用到自定義的動(dòng)作、函數(shù)類型,需要把它加到這個(gè)列表中才能起作用。
顯示動(dòng)作列表
可以使用如下命令顯示所有動(dòng)作:
Python -m pysc2.bin.valid_actions
可選的參數(shù)有:
-
--hide_specific:不顯示具體的動(dòng)作(specific actions)(下邊會(huì)提到什么是specific actions) -
--screen_resolution:只顯示應(yīng)用于游戲屏幕的動(dòng)作 -
--minimap_resolution:只顯示應(yīng)用于小地圖的動(dòng)作
比如列出了如下的動(dòng)作們(實(shí)際上多得多):
0/no_op ()
1/move_camera (1/minimap [64, 64])
11/build_queue (11/build_queue_id [10])
12/Attack_screen (3/queued [2]; 0/screen [84, 84])
23/Behavior_CloakOff_quick (3/queued [2])
42/Build_Barracks_screen (3/queued [2]; 0/screen [84, 84])
220/Effect_Repair_screen (3/queued [2]; 0/screen [84, 84])
264/Harvest_Gather_screen (3/queued [2]; 0/screen [84, 84])
303/Morph_Lair_quick (3/queued [2])
331/Move_screen (3/queued [2]; 0/screen [84, 84])
每一行的格式為:
<function id>/<function name>(<type id>/<type name> [<value size>, *]; *)
表示為:
【函數(shù)ID】/【函數(shù)名稱】 (【函數(shù)參數(shù)類型的ID】/【函數(shù)參數(shù)的類型名稱】 【函數(shù)參數(shù)的取值類型與范圍】)
兩個(gè)例子:
-
1/move_camera (1/minimap [64, 64]):表示名字為move_camera的函數(shù),它對(duì)應(yīng)函數(shù)id為1,他的參數(shù)類型為minimap,對(duì)應(yīng)函數(shù)參數(shù)類型中的第1類,參數(shù)取兩個(gè)數(shù)值,都是 [0,64) 范圍內(nèi)的數(shù)值,表示小地圖上的一個(gè)坐標(biāo) -
331/Move_screen (3/queued [2]; 0/screen [84, 84]):表示名字為Move_screen的函數(shù),它對(duì)應(yīng)函數(shù)id為331,它接收兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)queued類型的參數(shù),對(duì)應(yīng)的參數(shù)類型id為3,是一個(gè)布爾值,表示該操作是應(yīng)該立即發(fā)生還是在之前的操作之后發(fā)生;第二個(gè)參數(shù)是一個(gè)screen類型的參數(shù),對(duì)應(yīng)類型id為0,后邊的 [84, 84] 同上邊的 [64, 64]
pysc2 中所定義的函數(shù)以及參數(shù)名稱都代表一定的意義,通??梢酝ㄟ^名稱來大致理解其作用。
完整的列表同樣也是定義在 pysc2.lib.actions 中。
官方給出的 random agent 就是一個(gè)非常好的理解 function actions 的例子:
import numpy as np
from pysc2.lib import actions
def step(self, obs):
super(MyAgent, self).step(obs)
function_id = np.random.choice(obs.observation.available_actions)
args = [[np.random.randint(0, size) for size in arg.sizes]
for arg in self.action_spec.functions[function_id].args]
return actions.FunctionCall(function_id, args)
根據(jù)前后綴對(duì)動(dòng)作的分類
所有動(dòng)作函數(shù)的名稱都具有一定的意義,而且可以通過其前綴、后綴來進(jìn)行分類,(但是前后綴的使用并不是完全嚴(yán)格的),比如以 Morph 開頭的動(dòng)作即“轉(zhuǎn)換”動(dòng)作,比如蟲族中的一些孵化動(dòng)作;以 Effect 開頭的一般是一個(gè)單獨(dú)的效果,通常不可以被取消;以 Behavior 開頭的通常是一個(gè)可以調(diào)為 on 或 off 的值;以 screen 結(jié)尾的通常是作用于游戲屏幕;而以 minimap 結(jié)尾的通常是作用與小地圖中的動(dòng)作。
一般動(dòng)作 vs 具體動(dòng)作(General vs. Specific Actions)
SCII中,動(dòng)作可以被分為兩類,一般動(dòng)作(general actions)和具體動(dòng)作(specific actions),一般動(dòng)作是一個(gè)大類,而具體動(dòng)作則更加具體。舉個(gè)例子,Burrow(打洞)這個(gè)動(dòng)作,有很多單位可以完成,比如蟲族中的 Zergling 和 Drone(兩種不同的作戰(zhàn)單位),但是他們自己的打洞動(dòng)作有自己對(duì)應(yīng)的具體動(dòng)作,BurrowDown_Zergling 和 BurrowDown_Drone,其中 BurrowDown_Zergling 只能被應(yīng)用于 Zergling 單位, Drone同理,那么如果在某一幀下,我們選中的單位既有 Zergling 也有 Drone 呢,那此時(shí)如果使用 BurrowDown_Zergling 動(dòng)作,那其中的 Drone 單位就沒有辦法行動(dòng)了,所以需要 BurrowDown 這個(gè)一般動(dòng)作來執(zhí)行,選中這個(gè)動(dòng)作,所有能夠執(zhí)行這個(gè)動(dòng)作的單位都可以執(zhí)行。
在pysc2的定義中,可以理解為具體動(dòng)作是相應(yīng)的一般動(dòng)作的繼承,只是多了一個(gè)額外的參數(shù)來確定其“具體的部分”。
本文主要參考官方文檔:https://github.com/deepmind/pysc2/blob/master/docs/environment.md#actions-and-observations