스타봇

스타크래프트 봇 동아리, 내전용 봇 개발 (#13)

오잎 클로버 2021. 7. 24. 12:00
728x90

스타크래프트의 자원 줄을 계속 늘려주는 것은 병력을 쌓는 것 다음으로 중요하다는 것은 누구나 아는 사실이다.

그래서 내전용 봇에게 영역 확장을 하는 코드를 넣고자 한다.

먼저 종족이 저그인만큼 해처리를 늘리는 데에는 두 가지 이유가 있다.

1) 보다 많은 라바를 채우기 위해서

2) 자원 줄

3) 인구수 확보

 

먼저 영역 확장에 대해서부터 작성하도록하겠다.

※다른 종족에 비해 앞마당을 빨리 먹는 저그이기에 앞마당을 확장은 영역확장에 포함하지않았습니다.

유의해주세요. 물론 타 종족에서도 사용할 수 있게 코드상으로는 앞마당 확장은 영역확장에 포함하였습니다.

 

영역확장은 시간이 조금 지난 후, 중반쯤되어있을 때부터 확장을 하는 일반적이다.

또, 자원이 어느정도 확보가 되어있어야한다. 필자는 본진으로부터 가장 가깝지만 아직 아무도 확장하지않은 곳을 찾아 확장해야한다.

말로만 설명한다면 어렵기에 그림판으로 설명을 하고자한다.

맵은 투혼으로 설명하겠다.

 

투혼 맵

 

만일 파란색이 자신의 본진으로, 빨간색을 적 본진이라고 가정을 하자면 위 그림과 같이 된다.

물론 상황에 따라 달라질 수 있다.

가운데 역시 확장 할 수 있으나, 큰 부담이 가기에 확장을 하지않도록하였다.

 

boolean checkNeedToBuild(UnitType unitType, int cnt) {
	boolean result = false;
	if (cnt > 0 && MyBotModule.Broodwar.self().allUnitCount(unitType) < cnt && BuildManager.Instance().getBuildQueue().getItemCount(unitType) == 0 && ConstructionManager.Instance().getConstructionQueueItemCount(unitType, null) == 0) {
		result = true;
	}
	return result;
}


private void expansionManager() {
	int needHatchery = (MyBotModule.Broodwar.getFrameCount() + 1000) / 6000;

	if (MyBotModule.Broodwar.self().minerals() > 600 && BuildManager.Instance().buildQueue.getItemCount(UnitType.Zerg_Hatchery) < 1
		&& MyBotModule.Broodwar.self().incompleteUnitCount(UnitType.Zerg_Hatchery) < 1)
		needHatchery = MyBotModule.Broodwar.self().completedUnitCount(UnitType.Zerg_Hatchery) + 1;

	if (MyBotModule.Broodwar.self().completedUnitCount(UnitType.Zerg_Hatchery) < needHatchery && checkNeedToBuild(UnitType.Zerg_Hatchery, needHatchery)) {
		ArrayList<TilePosition> listTilePositionBase = new ArrayList<TilePosition>();
		List<BaseLocation> listBaseLocation = BWTA.getBaseLocations();
		BaseLocation bl1 = InformationManager.Instance().getMainBaseLocation(MyBotModule.Broodwar.self());
		BaseLocation bl3 = InformationManager.Instance().getMainBaseLocation(MyBotModule.Broodwar.enemy());
		BaseLocation bl4 = InformationManager.Instance().getFirstExpansionLocation(MyBotModule.Broodwar.enemy());
		if (bl3 != null && bl4 != null) {
			for (BaseLocation bl : listBaseLocation) {
				boolean hasCommandCenterThere = false;
				for (Unit unit : getSelfUnit(UnitType.Zerg_Hatchery)) {
					if (distanceTilePosition(unit.getTilePosition(), bl.getTilePosition()) < 5) {
						hasCommandCenterThere = true;
						break;
					}
				}
				if (!hasCommandCenterThere && !bl.getTilePosition().equals(bl1.getTilePosition()) && !bl.getTilePosition().equals(bl3.getTilePosition()) && !bl.getTilePosition().equals(bl4.getTilePosition())
					&& !InformationManager.mapSelfMainBuilding.contains(bl.getTilePosition()) && !InformationManager.mapEnemyMainBuilding.contains(bl.getTilePosition())) {
					// 가스를 지을 수 있는 곳에 확장한다.
					TilePosition tilePosition = ConstructionPlaceFinder.Instance().getRefineryPositionNear(bl.getTilePosition());
					if (tilePosition != null) {
						// 가운데 지역은 건설하지 않는다.
						if (tilePosition.getX() >= 50 && tilePosition.getX() <= 70 && tilePosition.getY() >= 50 && tilePosition.getY() <= 70) {
							continue;
						}
						listTilePositionBase.add(bl.getTilePosition());
					}
				}
			}
			Collections.sort(listTilePositionBase, new ComparatorBaseLocationClose());
		}
		if (listTilePositionBase.size() > 0) {
			BuildManager.Instance().buildQueue.queueAsLowestPriority(UnitType.Zerg_Hatchery, listTilePositionBase.get(0), false);
			return;
		}
	}
	// 초반에 바로 가스를 지으면 안되기 때무에 8마리 보다 크다는 조건을 넣었음
	if (countAttackUnit() > 8 || MyBotModule.Broodwar.getFrameCount() > 5000) {
		// Command Center 주위에 가스를 건설할수 있으면 추가한다.
		for (Unit unit : getSelfUnit(UnitType.Zerg_Hatchery)) {
			TilePosition tilePosition = ConstructionPlaceFinder.Instance().getRefineryPositionNear(unit.getTilePosition());
			if (tilePosition != null && !InformationManager.mapRefineryPosition.contains(tilePosition) && BuildManager.Instance().getBuildQueue().getItemCount(UnitType.Zerg_Extractor) == 0 && ConstructionManager.Instance().getConstructionQueueItemCount(UnitType.Zerg_Extractor, null) == 0) {
				BuildManager.Instance().buildQueue.queueAsHighestPriority(UnitType.Zerg_Extractor, tilePosition, false);
			}
		}
	}
}

필자의 이전에 작성하였던 스커지 정찰 코드 중 ComparatorBaseLocation클래스와 비슷한 맥락으로

이와 비슷한 클래스를 하나 더 만들어줄 것이다.

ComparatorBaseLocationClose 클래스를 만들 것이다.

 

코드는 아래와 같다

import bwapi.TilePosition;
import java.util.Comparator;

public class ComparatorBaseLocationClose implements Comparator<TilePosition> {

    @Override
    public int compare(TilePosition o1, TilePosition o2) {
        double temp = StrategyManager.distanceTilePosition(o1, MyBotModule.Broodwar.self().getStartLocation())
                - StrategyManager.distanceTilePosition(o2, MyBotModule.Broodwar.self().getStartLocation());
        if (temp > 0)
            return 1;
        else if (temp < 0)
            return -1;
        return 0;
    }
}

 

그리고 본진에 해처리를 늘리므로서 라바를 많이 모으기위한 메소드 역시 작성하였다.

// 본진에 해처리를 늘리기위한 메소드
private void expansionInMainBase() {
	if (MyBotModule.Broodwar.self().minerals() > 500 && BuildManager.Instance().buildQueue.getItemCount(UnitType.Zerg_Hatchery) < 1
	&& MyBotModule.Broodwar.self().incompleteUnitCount(UnitType.Zerg_Hatchery) < 1 && (
	getSelfUnit(UnitType.Zerg_Hatchery).size() == 2 || getSelfUnit(UnitType.Zerg_Hatchery).size() == 4 || getSelfUnit(UnitType.Zerg_Hatchery).size() == 6)) {
		BuildManager.Instance().buildQueue.queueAsLowestPriority(UnitType.Zerg_Hatchery,
			BuildOrderItem.SeedPositionStrategy.MainBaseLocation, false);
		return;
	}
}

이를 직접 테스트를 하여 시연 영상을 찍고자 하였으나, 시간이 너무 오래걸리기에 스킵하도록하겠다.