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๋กœ ์ •์ฐฐ์„ ๋ณด๋‚ด๊ณ , ๋น™๋น™๋Œ๋ ค์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„ ,

  1. SCV ํ•œ ๊ธฐ๋ฅผ ํด๋ฆญ(์œ„์—์„œ์ง„ํ–‰)

  2. SCV๋ฅผ ๋ถ€๋Œ€์ง€์ •

  3. 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?