How to select your SCV!?
이 장에서 배울 것
SCV를 선택하고, 상대방의 진영으로 SCV를 정찰보내고 빙빙 돌리는 행위를 해볼 것입니다.
Let's Start!
시작해보겠습니다. 일단 지난번의 agent는 no_op()을 통해 아무 action없이 대기하는 agent를 만들었습니다. 이번엔 어떻게 처리해야 SCV를 선택하고, 상대방 진영에 보내고, 자원이 될 때, 커맨드 센터를 지을 수 있을까요?
이전의 step function을 살펴보겠습니다.
class Agent(base_agent.BaseAgent):
def step(self,obs):
super(Agent,self).step(obs)
return actions.FUNCTIONS.no_op()return을 통해 action을 output으로 주면 되는 간단한 구조인 것입니다! 먼저, SCV가 존재하는지 observation에서 확인해야 합니다.
from pysc2.lib import units
scvs = [unit for unit in obs.observation.feature_units if unit.unit_type == units.Terran.SCV]이렇게 input으로 현재 화면에서 보이는 scvs들을 잡습니다. 이제, 그중 scv 아무 놈을 잡아서, 선택해줍니다.
scv = scvs[0]
return actions.FUNCTIONS.select_point("select",(scv.x,scv.y))3select_point를 통해 scv의 좌표를 선택해 scv를 콕 찝은 상태입니다. 다음 step으로 넘어가게 되면 그 scv에게 명령을 내려야 하겠죠! 그리고 현재 scv를 잡는 행위는 그만해야합니다.
그렇다면 이를 다음과 같은 함수를 만들어 해결할 수 있습니다.
def unit_type_is_selected(obs,unit_type):
if len(obs.observation.single_select) > 0 and obs.observation.single_select[0].unit_type == unit_type:
return True
elif len(obs.observation.multi_select) > 0 and obs.observation.multi_select[0].unit_type == unit_type:
return True
else:
return False위의 행동들을 합쳐 SCV를 잡은채로 가만히 있도록 하는 Agent를 만들어 보겠습니다.
class Agent(base_agent.BaseAgent):
def unit_type_is_selected(self,obs,unit_type):
if (len(obs.observation.single_select) > 0 and obs.observation.single_select[0].unit_type == unit_type):
return True
elif (len(obs.observation.multi_select) > 0 and obs.observation.multi_select[0].unit_type == unit_type):
return True
else :
return False
def step(self,obs):
super(Agent,self).step(obs)
scvs = [unit for unit in obs.observation.feature_units if unit.unit_type == units.Terran.SCV]
if len(scvs) > 0 and not self.unit_type_is_selected(obs,units.Terran.SCV):
scv = scvs[0]
return actions.FUNCTIONS.select_point("select",(scv.x,scv.y))
else:
return actions.FUNCTIONS.no_op()이제는 agent를 골랐을 때, 아무것도 안하는게 아닌, SCV를 선택된 적 starting point로 정찰을 보내고, 빙빙돌려야합니다. 그렇게 하기 위해선,
SCV 한 기를 클릭(위에서진행)
SCV를 부대지정
SCV를 카메라로 따라다니며 및 컨트롤
아래처럼 부대지정이 1번이 되어있지않는 경우에 아래처럼 그룹지정을 합니다.
CONTROL_GROUP_SET = 1
CONTROL_GROUP_RECALL = 0
SCV_GROUP_ORDER = 1
def step(self,obs):
super(Agent,self).step(obs)
scvs = [unit for unit in obs.observation.feature_units if unit.unit_type == units.Terran.SCV]
if len(scvs) > 0 and not self.unit_type_is_selected(obs,units.Terran.SCV):
scv = scvs[0]
return actions.FUNCTIONS.select_point("select",(scv.x,scv.y))
elif self.unit_type_is_selected(obs,units.Terran.SCV) and obs.observation.control_groups[SCV_GROUP_ORDER][0] == 0:
return actions.FUNCTIONS.select_control_group([CONTROL_GROUP_SET], [SCV_GROUP_ORDER])
else :
return actions.FUNCTIONS.no_op()다음은, 카메라를 따라다니도록 해야하는데, 여기서 편법(?)을 사용할 예정입니다. FUNCTIONS에 camera move가 있긴하지만 미니맵을 쪼개서 사용하는 방식으로 사용하여, 부대지정한 유닛으로 이동하는게 까다롭습니다. 그렇기때문에 pynput 을 이용하겠습니다.
import pynput
keyboard_button = pynput.keyboard.Controller()
keyboard_key = pynput.keyboard.Key
CONTROL_GROUP_SET = 1
CONTROL_GROUP_RECALL = 0
SCV_GROUP_ORDER = 1
NOT_QUEUED = [0]
MOVE_SCREEN = 331
def step(self,obs):
super(Agent,self).step(obs)
scvs = [unit for unit in obs.observation.feature_units if unit.unit_type == units.Terran.SCV]
if len(scvs) > 0 and not self.unit_type_is_selected(obs,units.Terran.SCV):
scv = scvs[0]
return actions.FUNCTIONS.select_point("select",(scv.x,scv.y))
elif self.unit_type_is_selected(obs,units.Terran.SCV) and obs.observation.control_groups[SCV_GROUP_ORDER][0] == 0:
return actions.FUNCTIONS.select_control_group([CONTROL_GROUP_SET], [SCV_GROUP_ORDER])
elif len([x for x in obs.observation.feature_units if x.is_selected == 1]) == 0:
keyboard_button.press(str(SCV_GROUP_ORDER))
keyboard_button.release(str(SCV_GROUP_ORDER))
keyboard_button.press(str(SCV_GROUP_ORDER))
keyboard_button.release(str(SCV_GROUP_ORDER))
return actions.FUNCTIONS.select_control_group([CONTROL_GROUP_RECALL], [SCV_GROUP_ORDER])
elif len([x for x in obs.observation.feature_units if ((x.is_selected == 1) and x.order_length == 0)]) == 1 :
x,y = random.randint(0,64),random.randint(0,64)
return actions.FunctionCall(MOVE_SCREEN,[NOT_QUEUED,[x,y]])
else:
return actions.FUNCTIONS.no_op()부대지정한 유닛이 맵에서 사라지면 SCV부대지정 키를 눌러 그곳으로 camera를 이동하고, SCV가 아무것도안하면 random으로 이동시켜 뺑뺑 돌게 만드는 스크립트입니다.
적의 본진으로 정찰을 보내야하는데, 시작전에 자신의 본진이 어느 방향일지 알 수 없습니다. 그러므로, step내에 다음과 같은 라인을 추가합니다.
if obs.first():
player_y, player_x = (obs.observation.feature_minimap.player_relative == PLAYER_SELF).nonzero()
xmean = player_x.mean()
ymean = player_y.mean()
if xmean <= 31 and ymean <= 31:
self.scout_coordinates = (40, 40)
else:
self.scout_coordinates = (20, 20)step function의 전체를 정리하면 다음과 같습니다.
def step(self,obs):
super(Agent,self).step(obs)
if obs.first():
player_y, player_x = (obs.observation.feature_minimap.player_relative == PLAYER_SELF).nonzero()
xmean = player_x.mean()
ymean = player_y.mean()
if xmean <= MINIMAP_SIZE / 2 and ymean <= MINIMAP_SIZE / 2:
self.scout_coordinates = (40, 40)
else:
self.scout_coordinates = (20, 20)
scvs = [unit for unit in obs.observation.feature_units if unit.unit_type == units.Terran.SCV]
if len(scvs) > 0 and not self.unit_type_is_selected(obs,units.Terran.SCV):
#유닛 셀렉
scv = scvs[0]
return actions.FUNCTIONS.select_point("select",(scv.x,scv.y))
elif self.unit_type_is_selected(obs,units.Terran.SCV) and obs.observation.control_groups[SCV_GROUP_ORDER][0] == 0:
#control unit잡기
return actions.FUNCTIONS.select_control_group([CONTROL_GROUP_SET], [SCV_GROUP_ORDER])
elif len([x for x in obs.observation.feature_units if x.is_selected == 1]) == 0:
#화면밖벗어났을때
keyboard_button.press(str(SCV_GROUP_ORDER))
keyboard_button.release(str(SCV_GROUP_ORDER))
keyboard_button.press(str(SCV_GROUP_ORDER))
keyboard_button.release(str(SCV_GROUP_ORDER))
return actions.FUNCTIONS.select_control_group([CONTROL_GROUP_RECALL], [SCV_GROUP_ORDER])
elif len([x for x in obs.observation.feature_units if ((x.is_selected == 1) and x.order_length == 0)]) == 1 and\
SCREEN_ENEMY in [x.alliance for x in obs.observation.feature_units] :
#화면내 random 이동
x,y = random.randint(0,SCREEN_SIZE),random.randint(0,SCREEN_SIZE)
return actions.FunctionCall(MOVE_SCREEN,[NOT_QUEUED,[x,y]])
elif len([x for x in obs.observation.feature_units if (x.is_selected == 1)]) == 1 \
and SCREEN_ENEMY not in [x.alliance for x in obs.observation.feature_units]:
#적 위치로 정찰
x,y = self.scout_coordinates
return actions.FunctionCall(MOVE_MINIMAP,[NOT_QUEUED,[x,y]])
else:
return actions.FUNCTIONS.no_op()
전체의 스크립트는 다음과 같습니다.
from pysc2.env import sc2_env
from pysc2.agents import base_agent
from pysc2.lib import actions,units,features
from absl import app
import random
import pynput ###
keyboard_button = pynput.keyboard.Controller()
keyboard_key = pynput.keyboard.Key
MAPNAME = 'Simple64'
APM = 300
APM = int(APM / 18.75)
UNLIMIT = 0
VISUALIZE = True
REALTIME = True
CONTROL_GROUP_SET = 1
CONTROL_GROUP_RECALL = 0
SCV_GROUP_ORDER = 1
NOT_QUEUED = [0]
MOVE_SCREEN = 331
MOVE_MINIMAP = 332
SCREEN_ENEMY = 4
PLAYER_SELF =features.PlayerRelative.SELF
SCREEN_SIZE = 84
MINIMAP_SIZE = 64
players = [sc2_env.Agent(sc2_env.Race.terran),\
sc2_env.Bot(sc2_env.Race.zerg,\
sc2_env.Difficulty.very_easy)]
interface = features.AgentInterfaceFormat(\
feature_dimensions = features.Dimensions(\
screen = SCREEN_SIZE, minimap = MINIMAP_SIZE), use_feature_units = True)
class Agent(base_agent.BaseAgent):
def unit_type_is_selected(self,obs,unit_type):
if (len(obs.observation.single_select) > 0 and obs.observation.single_select[0].unit_type == unit_type):
return True
elif (len(obs.observation.multi_select) > 0 and obs.observation.multi_select[0].unit_type == unit_type):
return True
else :
return False
def step(self,obs):
super(Agent,self).step(obs)
if obs.first():
player_y, player_x = (obs.observation.feature_minimap.player_relative == PLAYER_SELF).nonzero()
xmean = player_x.mean()
ymean = player_y.mean()
if xmean <= MINIMAP_SIZE / 2 and ymean <= MINIMAP_SIZE / 2:
self.scout_coordinates = (40, 40)
else:
self.scout_coordinates = (20, 20)
scvs = [unit for unit in obs.observation.feature_units if unit.unit_type == units.Terran.SCV]
if len(scvs) > 0 and not self.unit_type_is_selected(obs,units.Terran.SCV):
#유닛 셀렉
scv = scvs[0]
return actions.FUNCTIONS.select_point("select",(scv.x,scv.y))
elif self.unit_type_is_selected(obs,units.Terran.SCV) and obs.observation.control_groups[SCV_GROUP_ORDER][0] == 0:
#control unit잡기
return actions.FUNCTIONS.select_control_group([CONTROL_GROUP_SET], [SCV_GROUP_ORDER])
elif len([x for x in obs.observation.feature_units if x.is_selected == 1]) == 0:
#화면밖벗어났을때
keyboard_button.press(str(SCV_GROUP_ORDER))
keyboard_button.release(str(SCV_GROUP_ORDER))
keyboard_button.press(str(SCV_GROUP_ORDER))
keyboard_button.release(str(SCV_GROUP_ORDER))
return actions.FUNCTIONS.select_control_group([CONTROL_GROUP_RECALL], [SCV_GROUP_ORDER])
elif len([x for x in obs.observation.feature_units if ((x.is_selected == 1) and x.order_length == 0)]) == 1 and\
SCREEN_ENEMY in [x.alliance for x in obs.observation.feature_units] :
#화면내 random 이동
x,y = random.randint(0,SCREEN_SIZE),random.randint(0,SCREEN_SIZE)
return actions.FunctionCall(MOVE_SCREEN,[NOT_QUEUED,[x,y]])
elif len([x for x in obs.observation.feature_units if (x.is_selected == 1)]) == 1 \
and SCREEN_ENEMY not in [x.alliance for x in obs.observation.feature_units]:
#적 위치로 정찰
x,y = self.scout_coordinates
return actions.FunctionCall(MOVE_MINIMAP,[NOT_QUEUED,[x,y]])
else:
return actions.FUNCTIONS.no_op()
def main(args):
agent = Agent()
try:
with sc2_env.SC2Env(map_name = MAPNAME, players = players,\
agent_interface_format = interface,\
step_mul = APM, game_steps_per_episode = UNLIMIT,\
visualize = VISUALIZE, realtime = REALTIME) as env:
agent.setup(env.observation_spec(), env.action_spec())
timestep = env.reset()
agent.reset()
while True:
step_actions = [agent.step(timestep[0])]
if timestep[0].last():
break
timestep = env.step(step_actions)
except KeyboardInterrupt:
pass
app.run(main)Last updated
Was this helpful?