스타봇

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

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

또다른 정찰기로서 써먹으면 좋은 저그 유닛

'스커지'

※ 스커지, 스컬지, 스콜지 등등으로 많이 불리지만 저자는 스커지라고 작성하겠다.

스커지는 재빠르게 적에게 붙어 자폭하는 공중에서 잘못 만나면 공중 유닛하나 잃어버릴 수 있지만

우리는 스커지의 속도를 보고 나쁘지않음을 깨닫고 정찰로서 사용하도록한다.

물론 시야가 짧지만 만일 맵이 섬맵일 수 있기에 정찰을 하도록 개발하였다.

저글링과 코드가 흡사하기에 이코드로 저글링 정찰의 코드를 대체하도록한다.

하지만 다른 점이 있다면 모든 스컬지들을 정찰하도록한다.

굳이 한 마리만 보낼 필요는 없다고 판단.

물론 추후 정찰을 해제해야할 수도 있기에 코드는 추후 수정될 가능성이 높다.

static int count = 0;

static ArrayList<TilePosition> makeNewList() {

	BaseLocation bl1 = InformationManager.Instance().getMainBaseLocation(MyBotModule.Broodwar.self());
	BaseLocation bl2 = InformationManager.Instance().getFirstExpansionLocation(MyBotModule.Broodwar.self());
	BaseLocation bl3 = InformationManager.Instance().getMainBaseLocation(MyBotModule.Broodwar.enemy());
	BaseLocation bl4 = InformationManager.Instance().getFirstExpansionLocation(MyBotModule.Broodwar.enemy());

	if (bl1 == null | bl2 == null || bl3 == null || bl4 == null) {
		return new ArrayList<TilePosition>();
	}

	ArrayList<TilePosition> listTilePosition = new ArrayList<TilePosition>();
	if (listTilePosition.size() == 0) {

		List<BaseLocation> listBaseLocation = BWTA.getBaseLocations();
		if (bl3 != null && bl4 != null) {

			ArrayList<TilePosition> tmpList = new ArrayList<TilePosition>();

			for (BaseLocation bl : listBaseLocation) {
				tmpList.add(bl.getTilePosition());
			}
			Collections.sort(tmpList, new ComparatorBaseLocation());

			for (int i = tmpList.size() - 1; i >= 0; i--) {
				if (tmpList.get(i).getX() >= 50 && tmpList.get(i).getX() <= 70 && tmpList.get(i).getY() >= 50 && tmpList.get(i).getY() <= 70) {
					tmpList.remove(i);
				}
			}

			int indexB1 = 0;

			for (int i = 0; i < tmpList.size(); i++) {
				if (tmpList.get(i).equals(bl1.getTilePosition())) {
					indexB1 = i;
				}
			}

			int index = indexB1;
			boolean findEnemy = false;
			while (findEnemy == false) {
				if (tmpList.get(index).equals(bl3.getTilePosition()) || tmpList.get(index).equals(bl4.getTilePosition())) {
					findEnemy = true;
					break;
				}
				if (!tmpList.get(index).equals(bl1.getTilePosition()) && !tmpList.get(index).equals(bl2.getTilePosition()) && !tmpList.get(index).equals(bl3.getTilePosition()) && !tmpList.get(index).equals(bl4.getTilePosition())) {
					listTilePosition.add(tmpList.get(index));
				}
				index++;
				if (index >= tmpList.size()) {
					index = 0;
				}
			}
			index--;
			if (index < 0)
				index = tmpList.size() - 1;
			findEnemy = false;
			while (findEnemy == false) {
				if (tmpList.get(index).equals(bl3.getTilePosition()) || tmpList.get(index).equals(bl4.getTilePosition())) {
					findEnemy = true;
					break;
				}
				if (!tmpList.get(index).equals(bl1.getTilePosition()) && !tmpList.get(index).equals(bl2.getTilePosition()) && !tmpList.get(index).equals(bl3.getTilePosition()) && !tmpList.get(index).equals(bl4.getTilePosition())) {
					listTilePosition.add(tmpList.get(index));
				}
				index--;
				if (index < 0) {
					index = tmpList.size() - 1;
				}
			}
			index++;
			if (index > tmpList.size() - 1) {
				index = 0;
			}

			boolean findSelf = false;
			while (findSelf == false) {
				if (tmpList.get(index).equals(bl1.getTilePosition()) || tmpList.get(index).equals(bl2.getTilePosition())) {
					findSelf = true;
					break;
				}
				if (!tmpList.get(index).equals(bl1.getTilePosition()) && !tmpList.get(index).equals(bl2.getTilePosition()) && !tmpList.get(index).equals(bl3.getTilePosition()) && !tmpList.get(index).equals(bl4.getTilePosition())) {
					listTilePosition.add(tmpList.get(index));
				}
				index++;
				if (index >= tmpList.size()) {
					index = 0;
				}
			}
		}
	}

	count++;
	if (count % 2 == 0) {
		Collections.reverse(listTilePosition);
	}
	return listTilePosition;
}

위 메소드를 사용하여 정찰을 위치를 지정한다.

적군과 본인의 기본 기지 영역을 제외하여 나머지들을 저장한다.

 

static HashMap<Integer, ArrayList<TilePosition>> mapScourgePatrol = new HashMap<Integer, ArrayList<TilePosition>>();
	
void scourgeMove(Unit scourge) {
	if (!mapScourgePatrol.containsKey(scourge.getID()) || mapScourgePatrol.get(scourge.getID()).size() == 0)
		mapScourgePatrol.put(scourge.getID(), makeNewList());
	ArrayList<TilePosition> targetList = mapScourgePatrol.get(scourge.getID());
	if (targetList.size() > 0) {
		TilePosition target = targetList.get(0);
		if (InformationManager.distanceTilePosition(target, scourge.getTilePosition()) <= 2)
			targetList.remove(target);
		else
			commandUtil.attackMove(scourge, target.toPosition());
	}
}

InformationManager에서 distanceTilePosition 메소드는 없기에 직접 구현을 하였다.

static double distanceTilePosition(TilePosition a, TilePosition b) {
	return Math.sqrt((a.getX() - b.getX()) * (a.getX() - b.getX()) + (a.getY() - b.getY()) * (a.getY() - b.getY()));
}

그리고 종족에 따라 상황이 다르기때문에 작성하는 글에는 테란 코드만을 작성하겠다.

private void terranAction(Unit unit) {
	for (Unit turret : MyBotModule.Broodwar.enemy().getUnits()) {
		if (turret.getType() == UnitType.Terran_Missile_Turret) {
			Position turretPosition = turret.getPosition();
			if (InformationManager.distancePosition(unit.getPosition(), turretPosition) < 11 * 32) {
				int X2 = unit.getPosition().getX();
				int Y2 = unit.getPosition().getY();
				int X1 = turretPosition.getX();
				int Y1 = turretPosition.getY();

				Position position = new Position(2 * X2 - X1, 2 * Y2 - Y1);
				if (!position.isValid())
					commandUtil.move(unit, MyBotModule.Broodwar.self().getStartLocation().toPosition());
				else
					commandUtil.move(unit, position);
				setSpecialAction(unit, 0);
				return;
			}
		}
	}
	runAwayAction(unit);

	Unit vessel = getMostCloseEnemyUnit(UnitType.Terran_Science_Vessel, unit);
	if (vessel != null && unit.getDistance(vessel) < 400) {
		if (!checkGoliat(vessel)) {
			commandUtil.attackUnit(unit, vessel);
			return;
		}
	}
	scourgeMove(unit);
}

private void runAwayAction(Unit unit) {
	for (Unit goliath : MyBotModule.Broodwar.enemy().getUnits()) {
		if (goliath.getType() == UnitType.Terran_Goliath) {
			Position goliathPosition = goliath.getPosition();
			if (InformationManager.distancePosition(unit.getPosition(), goliathPosition) < 10 * 32) {
				int X2 = unit.getPosition().getX();
				int Y2 = unit.getPosition().getY();
				int X1 = goliathPosition.getX();
				int Y1 = goliathPosition.getY();

				commandUtil.move(unit, new Position(2 * X2 - X1 + r.nextInt(3), 2 * Y2 - Y1 + r.nextInt(3)));
				setSpecialAction(unit, 0);
				return;
			}
		}
	}
	Unit bunker = getMostCloseEnemyUnit(UnitType.Terran_Bunker, unit);
	if (bunker != null && distanceTilePosition(bunker.getTilePosition(), unit.getTilePosition()) < 7) {
		int X2 = unit.getPosition().getX();
		int Y2 = unit.getPosition().getY();
		int X1 = bunker.getPosition().getX();
		int Y1 = bunker.getPosition().getY();

		commandUtil.move(unit, new Position(2 * X2 - X1 + r.nextInt(3), 2 * Y2 - Y1 + r.nextInt(3)));
		setSpecialAction(unit, 0);
		return;
	}
	Unit valkyire = getMostCloseEnemyUnit(UnitType.Terran_Valkyrie, unit);
	if (valkyire != null && distanceTilePosition(valkyire.getTilePosition(), unit.getTilePosition()) < 9) {
		int X2 = unit.getPosition().getX();
		int Y2 = unit.getPosition().getY();
		int X1 = valkyire.getPosition().getX();
		int Y1 = valkyire.getPosition().getY();

		commandUtil.move(unit, new Position(2 * X2 - X1 + r.nextInt(3), 2 * Y2 - Y1 + r.nextInt(3)));
		setSpecialAction(unit, 0);
		return;
	}
}

boolean checkGoliat(Unit unit) {
	boolean goliatAround = false;
	for (Unit goliath : MyBotModule.Broodwar.enemy().getUnits()) {
		if (goliath.getType() == UnitType.Terran_Goliath) {
			Position tp = goliath.getPosition();
			if (InformationManager.distancePosition(unit.getPosition(), tp) <= 10 * 32)
				goliatAround = true;
		}
	}
	return goliatAround;
}
...
HashMap<Integer, Integer> mapSpecialActionTime = new HashMap<Integer, Integer>();

void setSpecialAction(Unit unit, int addTime) {
	mapSpecialActionTime.put(unit.getID(), MyBotModule.Broodwar.getFrameCount() + addTime);
}

InformationManagerdistancePosition메소드는 distanceTilePosition 메소드와 매우 유사하기에 직접 구현하기를 바란다.

HashMap<Integer, Boolean> mapMode = new HashMap<Integer, Boolean>();
Random r = new Random();
	
private void scourgeControl(Unit unit) {
	if (InformationManager.Instance().enemyRace == Race.Protoss)
    	protossAction(unit);
	else if (InformationManager.Instance().enemyRace == Race.Terran)
		terranAction(unit);
    else
    	zergAction(unit);
}

스커지에게 위협적이라면 도주하고 그렇지않는 배슬을 발견하면 즉시 공격하도록한다.

영상을 끝으로 글을 마무리하겠다.