/*******************************************************************************
* Copyright (c) 2015 - 2017
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.ai.highlevel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jsettlers.ai.army.ArmyGeneral;
import jsettlers.ai.construction.BestConstructionPositionFinderFactory;
import jsettlers.ai.economy.EconomyMinister;
import jsettlers.ai.highlevel.pioneers.PioneerAi;
import jsettlers.ai.highlevel.pioneers.PioneerGroup;
import jsettlers.ai.highlevel.pioneers.target.SameBlockedPartitionLikePlayerFilter;
import jsettlers.ai.highlevel.pioneers.target.SurroundedByResourcesFilter;
import jsettlers.common.buildings.EBuildingType;
import jsettlers.common.landscape.EResourceType;
import jsettlers.common.material.EMaterialType;
import jsettlers.common.movable.EMovableType;
import jsettlers.common.movable.IMovable;
import jsettlers.common.position.ShortPoint2D;
import jsettlers.input.tasks.ConstructBuildingTask;
import jsettlers.input.tasks.ConvertGuiTask;
import jsettlers.input.tasks.DestroyBuildingGuiTask;
import jsettlers.input.tasks.EGuiAction;
import jsettlers.input.tasks.MoveToGuiTask;
import jsettlers.input.tasks.WorkAreaGuiTask;
import jsettlers.logic.buildings.Building;
import jsettlers.logic.buildings.military.OccupyingBuilding;
import jsettlers.logic.map.grid.MainGrid;
import jsettlers.logic.movable.interfaces.ILogicMovable;
import jsettlers.network.client.interfaces.ITaskScheduler;
import static jsettlers.ai.highlevel.AiBuildingConstants.COAL_MINE_TO_IRONORE_MINE_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.COAL_MINE_TO_SMITH_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.FARM_TO_BAKER_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.FARM_TO_MILL_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.FARM_TO_PIG_FARM_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.FARM_TO_SLAUGHTER_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.FARM_TO_WATERWORKS_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.IRONMELT_TO_WEAPON_SMITH_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.IRON_MINE_TO_IRONMELT_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.LUMBERJACK_TO_FORESTER_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.LUMBERJACK_TO_SAWMILL_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.WEAPON_SMITH_TO_BARRACKS_RATIO;
import static jsettlers.ai.highlevel.AiBuildingConstants.WINEGROWER_TO_TEMPLE_RATIO;
import static jsettlers.common.buildings.EBuildingType.BAKER;
import static jsettlers.common.buildings.EBuildingType.BARRACK;
import static jsettlers.common.buildings.EBuildingType.BIG_LIVINGHOUSE;
import static jsettlers.common.buildings.EBuildingType.BIG_TEMPLE;
import static jsettlers.common.buildings.EBuildingType.COALMINE;
import static jsettlers.common.buildings.EBuildingType.FARM;
import static jsettlers.common.buildings.EBuildingType.FORESTER;
import static jsettlers.common.buildings.EBuildingType.GOLDMELT;
import static jsettlers.common.buildings.EBuildingType.IRONMELT;
import static jsettlers.common.buildings.EBuildingType.IRONMINE;
import static jsettlers.common.buildings.EBuildingType.LUMBERJACK;
import static jsettlers.common.buildings.EBuildingType.MEDIUM_LIVINGHOUSE;
import static jsettlers.common.buildings.EBuildingType.MILL;
import static jsettlers.common.buildings.EBuildingType.PIG_FARM;
import static jsettlers.common.buildings.EBuildingType.SAWMILL;
import static jsettlers.common.buildings.EBuildingType.SLAUGHTERHOUSE;
import static jsettlers.common.buildings.EBuildingType.SMALL_LIVINGHOUSE;
import static jsettlers.common.buildings.EBuildingType.STOCK;
import static jsettlers.common.buildings.EBuildingType.STONECUTTER;
import static jsettlers.common.buildings.EBuildingType.TEMPLE;
import static jsettlers.common.buildings.EBuildingType.TOWER;
import static jsettlers.common.buildings.EBuildingType.WATERWORKS;
import static jsettlers.common.buildings.EBuildingType.WEAPONSMITH;
import static jsettlers.common.buildings.EBuildingType.WINEGROWER;
import static jsettlers.common.material.EMaterialType.GOLD;
import static jsettlers.logic.constants.Constants.TOWER_SEARCH_RADIUS;
/**
* This WhatToDoAi is a high level KI. It delegates the decision which building is build next to its economy minister. However this WhatToDoAi takes
* care against lack of settlers and it builds a toolsmith when needed but as late as possible. Furthermore it builds towers continously to spread the
* land. It destroys not needed living houses and stonecutters to get back building materials. Which soldiers to levy and to command the soldiers is
* delegated to its armay general.
*
* @author codingberling
*/
public class WhatToDoAi implements IWhatToDoAi {
public static final int NUMBER_OF_SMALL_LIVINGHOUSE_BEDS = 10;
public static final int NUMBER_OF_MEDIUM_LIVINGHOUSE_BEDS = 30;
public static final int NUMBER_OF_BIG_LIVINGHOUSE_BEDS = 100;
public static final int MINIMUM_NUMBER_OF_BEARERS = 10;
public static final int NUMBER_OF_BEARERSS_PER_HOUSE = 3;
public static final int MAXIMUM_STONECUTTER_WORK_RADIUS_FACTOR = 2;
public static final float WEAPON_SMITH_FACTOR = 7F;
public static final int RESOURCE_PIONEER_GROUP_COUNT = 20;
public static final int BROADEN_PIONEER_GROUP_COUNT = 40;
private final MainGrid mainGrid;
private final byte playerId;
private final ITaskScheduler taskScheduler;
private final AiStatistics aiStatistics;
private final ArmyGeneral armyGeneral;
private final BestConstructionPositionFinderFactory bestConstructionPositionFinderFactory;
private final EconomyMinister economyMinister;
private final PioneerAi pioneerAi;
private boolean isEndGame = false;
private ArrayList<Object> failedConstructingBuildings;
private PioneerGroup resourcePioneers;
private PioneerGroup broadenerPioneers;
private AiPositions.AiPositionFilter[] geologistFilters = new AiPositions.AiPositionFilter[EResourceType.values().length];
public WhatToDoAi(byte playerId, AiStatistics aiStatistics, EconomyMinister economyMinister, ArmyGeneral armyGeneral, MainGrid mainGrid,
ITaskScheduler taskScheduler) {
this.playerId = playerId;
this.mainGrid = mainGrid;
this.taskScheduler = taskScheduler;
this.aiStatistics = aiStatistics;
this.armyGeneral = armyGeneral;
this.economyMinister = economyMinister;
this.pioneerAi = new PioneerAi(aiStatistics, playerId);
bestConstructionPositionFinderFactory = new BestConstructionPositionFinderFactory();
resourcePioneers = new PioneerGroup(RESOURCE_PIONEER_GROUP_COUNT);
broadenerPioneers = new PioneerGroup(BROADEN_PIONEER_GROUP_COUNT);
for (EResourceType resourceType : EResourceType.VALUES) {
geologistFilters[resourceType.ordinal] = new AiPositions.CombinedAiPositionFilter(
new SurroundedByResourcesFilter(mainGrid, mainGrid.getLandscapeGrid(), resourceType),
new SameBlockedPartitionLikePlayerFilter(aiStatistics, playerId));
}
}
@Override
public void applyRules() {
if (aiStatistics.isAlive(playerId)) {
economyMinister.update();
isEndGame = economyMinister.isEndGame();
failedConstructingBuildings = new ArrayList<>();
destroyBuildings();
commandPioneers();
buildBuildings();
Set<Integer> soldiersWithOrders = occupyMilitaryBuildings();
armyGeneral.levyUnits();
armyGeneral.commandTroops(soldiersWithOrders);
sendGeologists();
}
}
private void sendGeologists() {
int geologistsCount = aiStatistics.getMovablePositionsByTypeForPlayer(EMovableType.GEOLOGIST, playerId).size();
List<ShortPoint2D> bearersPositions = aiStatistics.getMovablePositionsByTypeForPlayer(EMovableType.BEARER, playerId);
int bearersCount = bearersPositions.size();
int stoneCutterCount = aiStatistics.getNumberOfBuildingTypeForPlayer(STONECUTTER, playerId);
if (geologistsCount == 0 && stoneCutterCount >= 1 && bearersCount - 3 > MINIMUM_NUMBER_OF_BEARERS) {
ILogicMovable coalGeologist = getBearerAt(bearersPositions.get(0));
ILogicMovable ironGeologist = getBearerAt(bearersPositions.get(1));
ILogicMovable goldGeologist = getBearerAt(bearersPositions.get(2));
List<Integer> targetGeologists = new ArrayList<>();
targetGeologists.add(coalGeologist.getID());
targetGeologists.add(ironGeologist.getID());
targetGeologists.add(goldGeologist.getID());
taskScheduler.scheduleTask(new ConvertGuiTask(playerId, targetGeologists, EMovableType.GEOLOGIST));
sendGeologistToNearest(coalGeologist, EResourceType.COAL);
sendGeologistToNearest(ironGeologist, EResourceType.IRONORE);
sendGeologistToNearest(goldGeologist, EResourceType.GOLDORE);
}
}
private void sendGeologistToNearest(ILogicMovable geologist, EResourceType resourceType) {
ShortPoint2D resourcePoint = aiStatistics.getNearestResourcePointForPlayer(aiStatistics.getPositionOfPartition(playerId), resourceType,
playerId, Integer.MAX_VALUE, geologistFilters[resourceType.ordinal]);
if (resourcePoint == null) {
resourcePoint = aiStatistics.getNearestResourcePointInDefaultPartitionFor(
aiStatistics.getPositionOfPartition(playerId), resourceType, Integer.MAX_VALUE, geologistFilters[resourceType.ordinal]);
}
if (resourcePoint != null) {
sendMovableTo(geologist, resourcePoint);
}
}
private ILogicMovable getBearerAt(ShortPoint2D point) {
return mainGrid.getMovableGrid().getMovableAt(point.x, point.y);
}
private Set<Integer> occupyMilitaryBuildings() {
Set<Integer> soldiersWithOrders = new HashSet<>();
for (ShortPoint2D militaryBuildingPosition : aiStatistics.getBuildingPositionsOfTypesForPlayer(
EBuildingType.getMilitaryBuildings(), playerId)) {
OccupyingBuilding militaryBuilding = (OccupyingBuilding) aiStatistics.getBuildingAt(militaryBuildingPosition);
if (!militaryBuilding.isOccupied()) {
ShortPoint2D door = militaryBuilding.getDoor();
IMovable soldier = aiStatistics.getNearestSwordsmanOf(door, playerId);
if (soldier != null && militaryBuilding.getPos().getOnGridDistTo(soldier.getPos()) > TOWER_SEARCH_RADIUS) {
soldiersWithOrders.add(soldier.getID());
sendMovableTo(soldier, door);
}
}
}
return soldiersWithOrders;
}
private void sendMovableTo(IMovable movable, ShortPoint2D target) {
if (movable != null) {
taskScheduler.scheduleTask(new MoveToGuiTask(playerId, target, Collections.singletonList(movable.getID())));
}
}
private void destroyBuildings() {
// destroy stonecutters or set their work areas
for (ShortPoint2D stoneCutterPosition : aiStatistics.getBuildingPositionsOfTypeForPlayer(STONECUTTER, playerId)) {
if (aiStatistics.getBuildingAt(stoneCutterPosition).cannotWork()) {
int numberOfStoneCutters = aiStatistics.getNumberOfBuildingTypeForPlayer(STONECUTTER, playerId);
ShortPoint2D nearestStone = aiStatistics.getStonesForPlayer(playerId)
.getNearestPoint(stoneCutterPosition, STONECUTTER.getWorkRadius() * MAXIMUM_STONECUTTER_WORK_RADIUS_FACTOR, null);
if (nearestStone != null && numberOfStoneCutters < economyMinister.getMidGameNumberOfStoneCutters()) {
taskScheduler.scheduleTask(new WorkAreaGuiTask(EGuiAction.SET_WORK_AREA, playerId, nearestStone, stoneCutterPosition));
} else {
taskScheduler.scheduleTask(new DestroyBuildingGuiTask(playerId, stoneCutterPosition));
break; // destroy only one stone cutter
}
}
}
// destroy livinghouses
if (economyMinister.automaticLivingHousesEnabled()) {
int numberOfFreeBeds = aiStatistics.getNumberOfBuildingTypeForPlayer(EBuildingType.SMALL_LIVINGHOUSE, playerId)
* NUMBER_OF_SMALL_LIVINGHOUSE_BEDS
+ aiStatistics.getNumberOfBuildingTypeForPlayer(EBuildingType.MEDIUM_LIVINGHOUSE, playerId) * NUMBER_OF_MEDIUM_LIVINGHOUSE_BEDS
+ aiStatistics.getNumberOfBuildingTypeForPlayer(EBuildingType.BIG_LIVINGHOUSE, playerId) * NUMBER_OF_BIG_LIVINGHOUSE_BEDS
- aiStatistics.getMovablePositionsByTypeForPlayer(EMovableType.BEARER, playerId).size();
if (numberOfFreeBeds >= NUMBER_OF_SMALL_LIVINGHOUSE_BEDS + 1 && !destroyLivingHouse(SMALL_LIVINGHOUSE)) {
if (numberOfFreeBeds >= NUMBER_OF_MEDIUM_LIVINGHOUSE_BEDS + 1 && !destroyLivingHouse(MEDIUM_LIVINGHOUSE)) {
if (numberOfFreeBeds >= NUMBER_OF_BIG_LIVINGHOUSE_BEDS + 1) {
destroyLivingHouse(BIG_LIVINGHOUSE);
}
}
}
}
// destroy not necessary buildings to get enough space for livinghouses in end-game
if (isEndGame && isWoodJam()) {
List<ShortPoint2D> foresters = aiStatistics.getBuildingPositionsOfTypeForPlayer(FORESTER, playerId);
if (foresters.size() > 1) {
for (int i = 1; i < foresters.size(); i++) {
taskScheduler.scheduleTask(new DestroyBuildingGuiTask(playerId, foresters.get(i)));
}
}
for (ShortPoint2D lumberJackPosition : aiStatistics.getBuildingPositionsOfTypeForPlayer(LUMBERJACK, playerId)) {
if (aiStatistics.getBuildingAt(lumberJackPosition).cannotWork()) {
taskScheduler.scheduleTask(new DestroyBuildingGuiTask(playerId, lumberJackPosition));
}
}
if ((aiStatistics.getNumberOfBuildingTypeForPlayer(SAWMILL, playerId) * 3 - 2) > aiStatistics.getNumberOfBuildingTypeForPlayer(
LUMBERJACK, playerId)) {
taskScheduler.scheduleTask(
new DestroyBuildingGuiTask(playerId, aiStatistics.getBuildingPositionsOfTypeForPlayer(SAWMILL, playerId).get(0)));
}
for (ShortPoint2D bigTemple : aiStatistics.getBuildingPositionsOfTypeForPlayer(BIG_TEMPLE, playerId)) {
taskScheduler.scheduleTask(new DestroyBuildingGuiTask(playerId, bigTemple));
}
}
}
private boolean destroyLivingHouse(EBuildingType livingHouseType) {
for (ShortPoint2D livingHousePosition : aiStatistics.getBuildingPositionsOfTypeForPlayer(livingHouseType, playerId)) {
if (aiStatistics.getBuildingAt(livingHousePosition).cannotWork()) {
taskScheduler.scheduleTask(new DestroyBuildingGuiTask(playerId, livingHousePosition));
return true;
}
}
return false;
}
private boolean isWoodJam() {
return aiStatistics.getNumberOfMaterialTypeForPlayer(EMaterialType.TRUNK, playerId) > aiStatistics.getNumberOfBuildingTypeForPlayer(LUMBERJACK, playerId) * 2;
}
private void buildBuildings() {
if (aiStatistics.getNumberOfNotFinishedBuildingsForPlayer(playerId) < economyMinister.getNumberOfParallelConstructionSites()) {
if (economyMinister.automaticLivingHousesEnabled() && buildLivingHouse())
return;
if (isEndGame) {
return;
}
if (buildTower()) {
return;
}
if (buildStock())
return;
buildEconomy();
}
}
private boolean buildTower() {
for (ShortPoint2D towerPosition : aiStatistics.getBuildingPositionsOfTypeForPlayer(TOWER, playerId)) {
Building tower = aiStatistics.getBuildingAt(towerPosition);
if (!tower.isConstructionFinished() || !tower.isOccupied()) {
return false;
}
}
List<ShortPoint2D> threatenedBorder = aiStatistics.threatenedBorderOf(playerId);
if (threatenedBorder.size() == 0) {
return false;
}
ShortPoint2D position = bestConstructionPositionFinderFactory
.getBorderDefenceConstructionPosition(threatenedBorder)
.findBestConstructionPosition(aiStatistics, mainGrid.getConstructionMarksGrid(), playerId);
if (position != null) {
taskScheduler.scheduleTask(new ConstructBuildingTask(EGuiAction.BUILD, playerId, position, TOWER));
sendSwordsmenToTower(position);
return true;
}
return false;
}
private void sendSwordsmenToTower(ShortPoint2D position) {
IMovable soldier = aiStatistics.getNearestSwordsmanOf(position, playerId);
if (soldier != null) {
sendMovableTo(soldier, position);
}
}
private boolean buildStock() {
if (aiStatistics.getTotalNumberOfBuildingTypeForPlayer(GOLDMELT, playerId) < 1) {
return false;
}
int stockCount = aiStatistics.getTotalNumberOfBuildingTypeForPlayer(STOCK, playerId);
int goldCount = aiStatistics.getNumberOfMaterialTypeForPlayer(GOLD, playerId);
if (stockCount * 6 * 8 - 32 < goldCount) {
return construct(STOCK);
}
return false;
}
private void buildEconomy() {
Map<EBuildingType, Integer> playerBuildingPlan = new HashMap<EBuildingType, Integer>();
for (EBuildingType currentBuildingType : economyMinister.getBuildingsToBuild()) {
addBuildingCountToBuildingPlan(currentBuildingType, playerBuildingPlan);
if (buildingNeedsToBeBuild(playerBuildingPlan, currentBuildingType)
&& buildingDependenciesAreFulfilled(currentBuildingType)
&& construct(currentBuildingType)) {
return;
}
}
}
private boolean buildingDependenciesAreFulfilled(EBuildingType targetBuilding) {
switch (targetBuilding) {
case IRONMINE:
return ratioFits(COALMINE, COAL_MINE_TO_IRONORE_MINE_RATIO, IRONMINE);
case WEAPONSMITH:
return ratioFits(IRONMELT, IRONMELT_TO_WEAPON_SMITH_RATIO, WEAPONSMITH);
case IRONMELT:
return ratioFits(COALMINE, COAL_MINE_TO_SMITH_RATIO, IRONMELT)
&& ratioFits(IRONMINE, IRON_MINE_TO_IRONMELT_RATIO, IRONMELT);
case BARRACK:
return ratioFits(WEAPONSMITH, WEAPON_SMITH_TO_BARRACKS_RATIO, BARRACK);
case MILL:
return ratioFits(FARM, FARM_TO_MILL_RATIO, MILL);
case BAKER:
return ratioFits(FARM, FARM_TO_BAKER_RATIO, BAKER);
case WATERWORKS:
return ratioFits(FARM, FARM_TO_WATERWORKS_RATIO, WATERWORKS);
case SLAUGHTERHOUSE:
return ratioFits(FARM, FARM_TO_SLAUGHTER_RATIO, SLAUGHTERHOUSE);
case PIG_FARM:
return ratioFits(FARM, FARM_TO_PIG_FARM_RATIO, PIG_FARM);
case TEMPLE:
return ratioFits(WINEGROWER, WINEGROWER_TO_TEMPLE_RATIO, TEMPLE);
case SAWMILL:
return ratioFits(LUMBERJACK, LUMBERJACK_TO_SAWMILL_RATIO, SAWMILL);
case FORESTER:
return ratioFits(LUMBERJACK, LUMBERJACK_TO_FORESTER_RATIO, FORESTER);
default:
return true;
}
}
private boolean ratioFits(EBuildingType leftBuilding, double leftToRightBuildingRatio, EBuildingType rightBuilding) {
return aiStatistics.getTotalNumberOfBuildingTypeForPlayer(leftBuilding,
playerId) >= (double) aiStatistics.getTotalNumberOfBuildingTypeForPlayer(rightBuilding, playerId) * leftToRightBuildingRatio;
}
private boolean buildingNeedsToBeBuild(Map<EBuildingType, Integer> playerBuildingPlan, EBuildingType currentBuildingType) {
int currentNumberOfBuildings = aiStatistics.getTotalNumberOfBuildingTypeForPlayer(currentBuildingType, playerId);
int targetNumberOfBuildings = playerBuildingPlan.get(currentBuildingType);
return currentNumberOfBuildings < targetNumberOfBuildings;
}
private void addBuildingCountToBuildingPlan(EBuildingType buildingType, Map<EBuildingType, Integer> playerBuildingPlan) {
if (!playerBuildingPlan.containsKey(buildingType)) {
playerBuildingPlan.put(buildingType, 0);
}
playerBuildingPlan.put(buildingType, playerBuildingPlan.get(buildingType) + 1);
}
private void commandPioneers() {
if (aiStatistics.getBorderOf(playerId).size() == 0 || aiStatistics.getEnemiesInTownOf(playerId).size() > 0) {
releaseAllPioneers();
} else if (aiStatistics.getNumberOfTotalBuildingsForPlayer(playerId) >= 4) {
sendOutPioneers();
}
}
private void releaseAllPioneers() {
broadenerPioneers.clear();
resourcePioneers.clear();
List<ShortPoint2D> pioneers = aiStatistics.getMovablePositionsByTypeForPlayer(EMovableType.PIONEER, playerId);
if (!pioneers.isEmpty()) {
List<Integer> pioneerIds = new ArrayList<>(pioneers.size());
for (ShortPoint2D pioneerPosition : pioneers) {
pioneerIds.add(mainGrid.getMovableGrid().getMovableAt(pioneerPosition.x, pioneerPosition.y).getID());
}
taskScheduler.scheduleTask(new ConvertGuiTask(playerId, pioneerIds, EMovableType.BEARER));
// pioneers which can not be converted shall walk into player's land to be converted the next tic
taskScheduler.scheduleTask(new MoveToGuiTask(playerId, aiStatistics.getPositionOfPartition(playerId), pioneerIds));
}
}
private void sendOutPioneers() {
resourcePioneers.removeDeadPioneers();
broadenerPioneers.removeDeadPioneers();
if (!resourcePioneers.isFull()) {
fill(resourcePioneers);
} else if (!broadenerPioneers.isFull()) {
fill(broadenerPioneers);
}
setNewTargetForResourcePioneers();
setNewTargetForBroadenerPioneers();
}
private void setNewTargetForBroadenerPioneers() {
if (broadenerPioneers.isNotEmpty()) {
PioneerGroup pioneersWithNoAction = broadenerPioneers.getPioneersWithNoAction();
ShortPoint2D broadenTarget = pioneerAi.findBroadenTarget();
if (broadenTarget != null) {
taskScheduler.scheduleTask(new MoveToGuiTask(playerId, broadenTarget, pioneersWithNoAction.getPioneerIds()));
}
}
}
private void setNewTargetForResourcePioneers() {
if (resourcePioneers.isNotEmpty()) {
ShortPoint2D resourceTarget = pioneerAi.findResourceTarget();
if (resourceTarget != null) {
taskScheduler.scheduleTask(new MoveToGuiTask(playerId, resourceTarget, resourcePioneers.getPioneerIds()));
}
}
}
private void fill(PioneerGroup pioneerGroup) {
List<ShortPoint2D> bearers = aiStatistics.getMovablePositionsByTypeForPlayer(EMovableType.BEARER, playerId);
int maxNewPioneersCount = bearers.size() - Math.max(
MINIMUM_NUMBER_OF_BEARERS, aiStatistics.getNumberOfTotalBuildingsForPlayer(playerId) * NUMBER_OF_BEARERSS_PER_HOUSE);
if (maxNewPioneersCount > 0) {
pioneerGroup.fill(taskScheduler, aiStatistics, playerId, maxNewPioneersCount);
}
}
private boolean buildLivingHouse() {
int futureNumberOfBearers = aiStatistics.getMovablePositionsByTypeForPlayer(EMovableType.BEARER, playerId).size()
+ aiStatistics.getNumberOfNotFinishedBuildingTypesForPlayer(BIG_LIVINGHOUSE, playerId) * NUMBER_OF_BIG_LIVINGHOUSE_BEDS
+ aiStatistics.getNumberOfNotFinishedBuildingTypesForPlayer(SMALL_LIVINGHOUSE, playerId) * NUMBER_OF_SMALL_LIVINGHOUSE_BEDS
+ aiStatistics.getNumberOfNotFinishedBuildingTypesForPlayer(MEDIUM_LIVINGHOUSE, playerId) * NUMBER_OF_MEDIUM_LIVINGHOUSE_BEDS;
if (futureNumberOfBearers < MINIMUM_NUMBER_OF_BEARERS
|| (aiStatistics.getNumberOfTotalBuildingsForPlayer(playerId) + aiStatistics.getNumberOfBuildingTypeForPlayer(WEAPONSMITH,
playerId) * WEAPON_SMITH_FACTOR) * NUMBER_OF_BEARERSS_PER_HOUSE > futureNumberOfBearers) {
if (aiStatistics.getTotalNumberOfBuildingTypeForPlayer(STONECUTTER, playerId) < 1
|| aiStatistics.getTotalNumberOfBuildingTypeForPlayer(LUMBERJACK, playerId) < 1) {
return construct(SMALL_LIVINGHOUSE);
} else if (aiStatistics.getTotalNumberOfBuildingTypeForPlayer(WEAPONSMITH, playerId) < 2) {
return construct(MEDIUM_LIVINGHOUSE);
} else {
return construct(BIG_LIVINGHOUSE);
}
}
return false;
}
private boolean construct(EBuildingType type) {
if (failedConstructingBuildings.size() > 1 && failedConstructingBuildings.contains(type)) {
return false;
}
ShortPoint2D position = bestConstructionPositionFinderFactory
.getBestConstructionPositionFinderFor(type)
.findBestConstructionPosition(aiStatistics, mainGrid.getConstructionMarksGrid(), playerId);
if (position != null) {
taskScheduler.scheduleTask(new ConstructBuildingTask(EGuiAction.BUILD, playerId, position, type));
if (type.isMilitaryBuilding()) {
sendSwordsmenToTower(position);
}
return true;
}
failedConstructingBuildings.add(type);
return false;
}
@Override
public String toString() {
return "Player " + playerId + " with " + economyMinister.toString() + " and " + armyGeneral.toString();
}
}