스타봇/스타봇 강의 및 개발

스타크래프트 봇 예제 저그편

오잎 클로버 2021. 12. 30. 18:00
728x90

교내 해커톤이 끝나 일주일만에 다시 돌아왔습니다.

 

먼저 저그편을 적기 전에 시험이에요.

 

시험이에요

제목 그대로입니다. 12월 1일자부터 기말고사가 저를 기다리고 있기에 지금 하고 있습니다. 기말은 12월 13일 ~ 12월 15일까지인데 수학, DB 제외해서는 다 할 만한데 그 2개가 제일 버거운 것 같습니

workshop-6349.tistory.com

..에 작성하였던 

위 리스트에서 스타크래프트 봇 Whiskey를 개발하려고 했으나

오늘 터졌습니다.

 

오류 수천 라인을 보고선 바로 때려쳤습니다.

그래서 도저히 bwapi를 못 해먹을 것 같아

그나마 했을 때 재미가 있었고 제가 했을 때 어느정도 성과가 나왔던 

압생트 봇 재개로 merge 시켜버릴 예정입니다.

(#code는 예정대로 진행될 예정입니다.)

 

 

본론으로 넘어가서

일단 무조건 이기기 위해서 4드론을 할려고 하였으나...

상대 역시 4드론일 경우 간혹 패배하는 경우가 생겨

차라리 상대가 4드론일 때는 패배한다는 것을 염두해둔채로

9드론을 하기로 결정하였다.

 

일단 지난에 작성하였던 프로토스보다는 잘 적용이 되지않겠지만

그래도 나름 성공할 가능성을 높인 9드론을 개발하였습니다.

먼저 BaseInfo로 각 베이스구역을 지정하기 위해 따로 클래스를 만들어 관리하였습니다.

public class BaseInfo {

    public ArrayList<Unit> mineralsWorkers;
    public ArrayList<Unit> gasWorkers;
    public ArrayList<Unit> minerals;
    public ArrayList<Unit> geysers;
    public ArrayList<Unit> buildings;

    public boolean spawn;
    public boolean owned;
    public boolean constructing;
    public boolean possible;
    
    public Unit mainDepot;
    public Position loc;
    public Unit scout;

    public BaseInfo(boolean spawn, boolean owned, boolean constructing, boolean possible, Unit scout) {
        this.mineralsWorkers = new ArrayList<>();
        this.gasWorkers = new ArrayList<>();
        this.minerals = new ArrayList<>();
        this.geysers = new ArrayList<>();
        this.buildings = new ArrayList<>();
        this.spawn = spawn;
        this.owned = owned;
        this.constructing = constructing;
        this.possible = possible;
        this.scout = scout;
        this.mainDepot = null;
        this.loc = Position.Unknown;
    }

}

 

 

그런다음 UnitPriorities 클래스로 건물 공격 우선순위를 지정하였다.

public class UnitPriorities {

    public HashMap<UnitType, Integer> structures;

    public void onStart() {
        structures = new HashMap<>();

        for (UnitType t : UnitType.values()) {
            structures.put(t, 1);
            if (t == UnitType.Terran_Supply_Depot
                    || t == UnitType.Zerg_Overlord
                    || t == UnitType.Protoss_Pylon) {
                structures.put(t, 2);
            }
            if (t.isResourceDepot()) {
                structures.put(t, 3);
            }
            if (t.isWorker()) {
                structures.put(t, 4);
            }
            if (t.canProduce()) {
                structures.put(t, 5);
            }
            if (t == UnitType.Zerg_Hydralisk_Den
                    || t == UnitType.Zerg_Spawning_Pool
                    || t == UnitType.Zerg_Spire
                    || t == UnitType.Zerg_Greater_Spire
                    || t == UnitType.Protoss_Forge
                    || t == UnitType.Protoss_Cybernetics_Core
                    || t == UnitType.Terran_Academy) {
                structures.put(t, 6);
            }
            if (t == UnitType.Terran_Bunker
                    || t == UnitType.Protoss_Photon_Cannon
                    || t == UnitType.Zerg_Spore_Colony
                    || t == UnitType.Zerg_Creep_Colony
                    || t == UnitType.Zerg_Sunken_Colony) {
                structures.put(t, 7);
            }
        }
    }

}

그후 Squad(스쿼드)클래스를 만들어 리더를 설정하고 해당 리더를 나머지 유닛들이 따라 움직이며 공격가도록 구현하였습니다.

public class Squad {

    public ArrayList<UnitInfo> units = new ArrayList<>();
    public double strength;
    public int order;

    public Squad(UnitInfo unitInfo) {
        this.strength = 0;
        this.order = 0;
        addToSquad(unitInfo);
    }

    public boolean containsUnit(UnitInfo u) {
        for (UnitInfo ui : units) {
            return (ui == u);
        }
        return false;
    }

    public void addToSquad(UnitInfo u) {
        units.add(units.size(), u);
    }

    public void removeFromSquad(UnitInfo u) {
        for (UnitInfo ui : units) {
            if (u == ui) {
                units.remove(ui);
                return;
            }
        }
    }

}

그후 스쿼드 클래스만 있다고 공격할 수 있는 것은 아니기에

Fighter 클래스를 구현할 것인데.. 그전에 UnitInfo 클래스를 구현하였습니다.

 

UnitInfo는 보다 더 효율적으로 사용하기 위해 개발한 클래스입니다.

public class UnitInfo {

    public Unit unit;
    public UnitType unitType;

    public int id = -1;
    public int health = 0;
    public int shields = 0;
    public int energy = 0;
    public int resources = 0;

    public Position position = Position.None;
    public Position target = Position.None;
    public Player owner = null;

    public UnitInfo(Unit unit) {
        if (unit == null) {
            return;
        }

        if (!unit.exists()) {
            return;
        }

        this.unit = unit;
        this.unitType = unit.getType();
        this.health = unit.getHitPoints();
        this.shields = unit.getShields();
        this.energy = unit.getEnergy();
        this.position = unit.getPosition();
        if (unitType.isResourceContainer()) {
            this.resources = unit.getResources();
        }
        this.owner = unit.getPlayer();
        this.id = unit.getID();
    }

    public void update() {
        if (unit == null) {
            return;
        }

        health = unit.getHitPoints();
        if (unit.getType() != unitType) {
            unitType = unit.getType();
        }

        shields = unit.getShields();
        energy = unit.getEnergy();
        position = unit.getPosition();
        if (!unit.isIdle()) {
            target = unit.getTargetPosition();
        }
        if (unit.getPlayer() != owner) {
            owner = unit.getPlayer();
        }
    }
}
public class Fighter {

    public UnitPriorities priorities = new UnitPriorities();
    public HashMap<UnitType, Squad> squads = new HashMap<>();

    public void onStart() {
        squads.clear();
        priorities.onStart();
    }

    public void drawInfo() {
        ExampleBot.BroodWar.drawTextScreen(2, 80, "temp: " + squads.size() + " squads managed");
        for (Map.Entry<UnitType, Squad> s : squads.entrySet()) {
            ExampleBot.BroodWar.drawTextScreen(2, 90, "Type: " + s.getKey() + ", unitCount: " + s.getValue().units.size());
            for (int i = 0; i < s.getValue().units.size(); i++) {
                if (i == 0) {
                    ExampleBot.BroodWar.drawCircleMap(s.getValue().units.get(0).position, 5, ExampleBot.BroodWar.self().getColor());
                }
                else {
                    ExampleBot.BroodWar.drawLineMap(s.getValue().units.get(i).position, s.getValue().units.get(0).position, Color.Teal);
                }
            }
        }
    }

    public Squad getSquad(UnitInfo unitInfo) {
        for (Map.Entry<UnitType, Squad> s : squads.entrySet()) {
            if (s.getKey() == unitInfo.unitType) {
                return s.getValue();
            }
        }
        return null;
    }

    public void assignedSquad(UnitInfo unitInfo) {
        Squad squad = getSquad(unitInfo);

        if (squad != null) {
            squad.addToSquad(unitInfo);
            return;
        }

        squads.put(unitInfo.unitType, new Squad(unitInfo));
    }

    public void onUnitDestroy(UnitInfo unitInfo) {
        Squad squad = getSquad(unitInfo);

        if (squad != null) {
            squad.removeFromSquad(unitInfo);
        }
    }
}

그이후 ExampleBot 클래스를 개발하여 Main 및 스타크래프트에 연동시킵니다.

하지만 상당히 비효율적으로 개발하였기때문에

참고로만 봐주세요.

https://github.com/iqpizza6349/StarCraft_BroodWar_BasicBot/blob/master/src/main/java/com/tistory/workshop6349/examplebotZ/ExampleBot.java

 

GitHub - iqpizza6349/StarCraft_BroodWar_BasicBot: 스타봇 동아리에서 사용될 소스코드입니다.

스타봇 동아리에서 사용될 소스코드입니다. Contribute to iqpizza6349/StarCraft_BroodWar_BasicBot development by creating an account on GitHub.

github.com

아래는 시연 영상입니다.

 

Zergling.rep
0.09MB