스타크래프트2 봇

스타크래프트 2 봇 개발 일지-1(Ocraft)

오잎 클로버 2022. 1. 1. 13:00
728x90

지난 번에는 기본적으로 스타크래프트2 봇을 테스트를 해보았습니다.

 

이번에는 일꾼 생산 및 보급고 건설을 해볼 겁니다.

일단 일꾼 생산 같은 경우에는

onUnitIdle 메소드를 오버라이딩하여

일하는 유닛들을 가져옵니다.

UnitInPool 이라는 클래스가 어떤 클래스인지 그리고 어떤 방식으로 진행이 되어가는 지 

잘 파악하지 못하였기때문에

원리를 제가 이해한 방식대로 한다면

UnitPool에 저장되고, UnitInPool에 인스턴스화된 것들을 가지고 오는 것

같습니다.

 

SCV 생산

@Override
public void onUnitIdle(UnitInPool unitInPool) {
    Unit unit = unitInPool.unit(); // 아직도 이해 못함 원리를
    switch ((Units) unit.getType()) {
        case TERRAN_COMMAND_CENTER:
            actions().unitCommand(unit, Abilities.TRAIN_SCV, false);
            break;
        default:
            break;
    }
}

일하지 않고 놀고 있는 유닛을 가져와서 해당 유닛이 CommandCenter이라면

TRAIN_SCV (SCV 생산)을 행동하는 것 같습니다.

 

 

보급고 건설

private boolean tryBuildSupplyDepot() {
    if (observation().getFoodUsed() <= observation().getFoodCap() - 2) {
        return false;
    }

    return tryBuildStructure(Abilities.BUILD_SUPPLY_DEPOT, Units.TERRAN_SCV);
}

먼저 인구수를 늘릴 필요가 있는 지부터 확인을 합니다.

전체 인구수를 가져오는 getFoodCap에서 2만큼을 뺀 값이

사용된 인구수보다 작다면 요구되지 않습니다.

 

bwapi는 2배수로 계산해야하는 데에 반해

Ocraft 같은 경우에는 굳이 그럴 필요가 없습니다.

private boolean tryBuildStructure(Abilities abilities, UnitType type) {
    if (!observation().getUnits(Alliance.SELF, doesBuildWith(abilities)).isEmpty()) {
        return false;
    }

    Optional<UnitInPool> unitInPool = getRandomUnit(type);
    if (unitInPool.isPresent()) {
        Unit unit = unitInPool.get().unit();
        actions().unitCommand(
                unit,
                abilities,
                unit.getPosition().toPoint2d().add(Point2d.of(getRandomScalar(), getRandomScalar()).mul(15f)),
                false
        );
        return true;
    }
    else {
        return false;
    }
}

인구수 확장을 해야한다면

이미 보급고를 건설 중이라면 굳이 또 건설할 필요가 없기때문에

false를 반환합니다.

랜덤한 위치에서 SCV를 가져와서

해당 SCV에게 보급고 건설 명령을 하달합니다.

private Predicate<UnitInPool> doesBuildWith(Ability ability) {
    return unitInPool -> unitInPool.unit()
            .getOrders()
            .stream()
            .anyMatch(unitOrder -> ability.equals(unitOrder.getAbility()));
}

해당 행동을 수행 중인지를 파악합니다.

private Optional<UnitInPool> getRandomUnit(UnitType type) {
    List<UnitInPool> units = observation().getUnits(Alliance.SELF, UnitInPool.isUnit(type));
    return units.isEmpty()
            ? Optional.empty()
            : Optional.of(units.get(ThreadLocalRandom.current().nextInt(units.size())));
}

랜덤한 유닛을 가져옵니다.

private float getRandomScalar() {
    return ThreadLocalRandom.current().nextFloat() * 2 - 1;
}

랜덤한 숫자를 반합환니다.

 

 

새 비디오 만들기.mp4
5.59MB

이상입니다.