스타봇

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

오잎 클로버 2021. 7. 12. 11:56
728x90

스타크래프트에서 무엇보다도 가장 중요한 것은 '정찰'이다.

정찰을 통해 현재 적의 전략, 그리고 적의 위치등을 파악을 하기위해서는 정찰이 필수이다.

 

BasicBot에도 충분히 정찰 기능이 존재하나

추가적으로 더 효율적인 정찰 기능을 추가하고자

오버로드 첫 정찰과

드론의 정찰을 업그레이드하고자한다.

저그는 게임 시작시, 오버로드 1기를 주어 빠르게 정찰을 시작할 수 있다.

하지만 오버로드의 속도가 매우 느리기에 스포닝풀을 올리고 나서도 기지를 겨우 하나 돌 정도로 느리다.

그러기에 스포닝풀이 지어졌는 데도 적군 기지를 찾지 못하였다면 드론을 정찰을 보내고자한다.

 

먼저 GameCommander에서 onStart메소드에서 오버로드 찾아 정찰기로 지정한다.

for (Unit unit : MyBotModule.Broodwar.self().getUnits()) {
	if (unit.getType() == UnitType.Zerg_Overlord) {
		WorkerManager.Instance().setScoutWorker(unit);
		ScoutManager.currentScoutUnit = unit;
		return;
	}
}

StrategyManager(이하 Strategy라 칭한다)의 update에 오버로드를 정찰을 꾸준히 하게 도와줄 메소드와 더불어

드론을 정찰시키도록하는 메소드도 작성한다.

public static List<BaseLocation> bases = new ArrayList<BaseLocation>();
public static boolean gotoExpansion = false;
public static List<Unit> checked = new ArrayList<Unit>();
public static int check = 0;
...
private void overloadScout() {
	if (MyBotModule.Broodwar.getFrameCount() == 10) {
		for (BaseLocation startLocation : BWTA.getStartLocations()) {
			if (startLocation == null)
				continue;

			if (startLocation.getX() == ScoutManager.pos.getX() && startLocation.getY() == ScoutManager.pos.getY())
				continue;

			if (startLocation.getX() == InformationManager.Instance()
					.getMainBaseLocation(MyBotModule.Broodwar.self()).getX()
					&& startLocation.getY() == InformationManager.Instance()
					.getMainBaseLocation(MyBotModule.Broodwar.self()).getY())
				continue;

			bases.add(startLocation);
		}

		if(bases.get(0).getGroundDistance(InformationManager.Instance().getMainBaseLocation(
				MyBotModule.Broodwar.self())) >=
				bases.get(1).getGroundDistance(InformationManager.Instance().getMainBaseLocation(
						MyBotModule.Broodwar.self())))
			bases.remove(0);
	}

	if (MyBotModule.Broodwar.getFrameCount() >= 10 && MyBotModule.Broodwar.getFrameCount() % 24 == 0) {
		if(!gotoExpansion)
			commandUtil.move(ScoutManager.currentScoutUnit, ScoutManager.pos);

		else
			commandUtil.move(ScoutManager.currentScoutUnit, InformationManager.Instance().getFirstExpansionLocation(
					MyBotModule.Broodwar.enemy()).getPosition());
	}
	if (MyBotModule.Broodwar.self().completedUnitCount(UnitType.Zerg_Spawning_Pool) == 1)
		check = 1;

	if (check >= 1 && MyBotModule.Broodwar.getFrameCount() % 24 == 0)
		scoutMore();
}

기본적으로 ScoutManagercurrentScoutUnitprivate처리가 되어있으나 public static처리로 사용을 허가해주고

추가로 위치를 확인하는 public static Position pos를 추가한다.

 

또, 사용하지 않을 메소드인 assignScoutIfNeeded 메소드는 주석처리 해준다.

pos에 위치를 저장시키기 위해 

아래와 같이 추가해준다.

if (closestBaseLocation != null) {
	// assign a scout to go scout it
	pos = closestBaseLocation.getPosition();	// pos에 위치 저장함
	commandUtil.move(currentScoutUnit, closestBaseLocation.getPosition());
	currentScoutTargetBaseLocation = closestBaseLocation;
}

moveScoutUnit메소드에 있는 코드 중 한 부분인데 해당 영역에서 

pos = closestBaseLocation.getPosition();

을 추가한 것이 전부이다.

 

그런 다음 InformationManager(이하 Information으로 칭한다)의 여러 메소드들 가운데

베이스를 관리하는 메소드인 updateBaseLocationInfo메소드에 Strategy에서 관리하는 checked 리스트와

check 변수를 변경하도록한다.

아래와 같은 곳에 코드를 추가하여 관리한다.

// enemy 의 startLocation을 아직 모르는 경우
if (mainBaseLocations.get(enemyPlayer) == null) {
	// how many start locations have we explored
	int exploredStartLocations = 0;
	boolean enemyStartLocationFound = false;

	// an unexplored base location holder
	BaseLocation unexplored = null;

	for (BaseLocation startLocation : BWTA.getStartLocations()) {
		if (existsPlayerBuildingInRegion(BWTA.getRegion(startLocation.getTilePosition()), enemyPlayer)) {
			if (!enemyStartLocationFound) {
				enemyStartLocationFound = true;
				mainBaseLocations.put(enemyPlayer, startLocation);
				mainBaseLocationChanged.put(enemyPlayer, new Boolean(true));

				for(int i = 0; i < StrategyManager.checked.size(); i++)
					StrategyManager.checked.remove(i);

					StrategyManager.check = 1;
					System.out.println("changed");
				}
			}

			if (MyBotModule.Broodwar.isExplored(startLocation.getTilePosition())) {
				// if it's explored, increment
				exploredStartLocations++;
				if(exploredStartLocations == 3 && StrategyManager.bases.size() == 2) {
					StrategyManager.bases.remove(0);
				}
			} else {
				// otherwise set it as unexplored base
				unexplored = startLocation;
			}
		}

		// if we've explored every start location except one, it's the enemy
		if (!enemyStartLocationFound && exploredStartLocations == ((int) BWTA.getStartLocations().size() - 1)) {
			enemyStartLocationFound = true;
			mainBaseLocations.put(enemyPlayer, unexplored);
			mainBaseLocationChanged.put(enemyPlayer, new Boolean(true));

			for(int i = 0; i < StrategyManager.checked.size(); i++)
				StrategyManager.checked.remove(i);

				StrategyManager.check = 1;
				System.out.println("changed");

				// C++ : _occupiedBaseLocations[_enemy].push_back(unexplored);
				if(occupiedBaseLocations.get(enemyPlayer) == null)
					occupiedBaseLocations.put(enemyPlayer, new ArrayList<BaseLocation>()); 
				occupiedBaseLocations.get(enemyPlayer).add(unexplored);
			}
		}
 ...
updateChokePointAndExpansionLocation();
if(getFirstExpansionLocation(MyBotModule.Broodwar.enemy()) != null && !StrategyManager.gotoExpansion)
	StrategyManager.gotoExpansion = true;

위 코드를 동일하게 하였다면 오버로드 정찰을 잘 작동할 것이다.

 

이제 작성할 코드는 드론 정찰이다.

다시 Strategy로 돌아와 scoutMore메소드를 작성한다.

scoutMore메소드와 overloadScout메소드는 단순히 적군 위치를 알아내기위한 메소드에 불과하다.

private void scoutMore() {
	if (InformationManager.Instance().getMainBaseLocation(MyBotModule.Broodwar.enemy()) == null) {
		for (Unit unit : MyBotModule.Broodwar.self().getUnits()) {
			if (unit == null)
				continue;

			if (checked.contains(unit))
				continue;

			else {
				if (unit.getType() == UnitType.Zerg_Drone) {
					if (check == 1)
						commandUtil.move(unit, bases.get(0).getPosition());
					check++;
				}
				checked.add(unit);
			}
		}
	}
}

드론을 하나만 보내기위해 check을 1일때만 작동하도록 작성했다.

이것으로 오버로드 정찰과 드론 정찰은 마무리 되었다.

2021-07-12