package ai.general;
import java.util.ArrayList;
/**
* \brief Manages the various productions that are needed
* @author Jeff Bernard
*
*/
public class ProductionManager extends TaskManager {
private static final int BUILD_TURN_WAIT = 100; /**< how many turns to wait before buildings can be made */
private static final int PRIORITY_HIGH = 0; /**< a quite high priority */
private static final int WORKER_PROB = 1; /**< out of HUNDRED_PERCENT; sometimes you want to build a worker */
private static final int HUNDRED_PERCENT = 10000;
public ArrayList<GeneralAIProduction> units_possible; /**< units we could build */
public ArrayList<GeneralAIProduction> buildings_possible; /**< buildings we could build */
private ArrayList<GeneralAIProduction> units_wanted; /**< units we want to build */
public ArrayList<GeneralAIProduction> buildings_wanted; /**< buildings we want to build */
public int workers_wanted; /**< how many workers we want to scout, but not don't exist */
public int workers_queued; /**< how many workers are queued to be built */
/**
* Constructs a new production manager
* @param ai the parent ai
*/
public ProductionManager(GeneralAI ai) {
super();
units_possible = new ArrayList<GeneralAIProduction>();
buildings_possible = new ArrayList<GeneralAIProduction>();
units_wanted = new ArrayList<GeneralAIProduction>();
buildings_wanted = new ArrayList<GeneralAIProduction>();
for (int i = 0; i < ai.state.getUnitList().size(); i++) {
units_possible.add(new GeneralAIProduction(ai.state.getUnitList().get(i), i));
}
for (int i = 0; i < ai.state.getBuildingList().size(); i++) {
buildings_possible.add(new GeneralAIProduction(ai.state.getBuildingList().get(i), i));
}
workers_wanted = 0;
workers_queued = 0;
}
@Override
/**
* Requests units to produce more units
* @param ai the parent ai
*/
public void manage_units(GeneralAI ai) {
// determine what we want to build
for (int i = 0; i < buildings_possible.size(); i++) {
GeneralAIProduction production = (GeneralAIProduction)buildings_possible.get(i);
for (int j = 0; j < units_possible.size(); j++) {
if (units_possible.get(j).priority != GeneralAI.DISTANCE_IGNORE && production.def.produces.contains(j)) {
production.priority -= units_possible.get(j).priority;
}
}
if (production.priority != GeneralAI.DISTANCE_IGNORE) {
for (int j = 0; j < ai.town_manager.towns.size(); j++) {
if (ai.town_manager.towns.get(j).owner == -1 || ai.town_manager.towns.get(j).owner == ai.player_id) {
int wish_location = ai.town_manager.towns.get(j).get_location(production, ai);
if (wish_location != GeneralAITown.NO_VACANCY) {
buildings_wanted.add(new GeneralAIProduction(production, wish_location%ai.state.getMapWidth(), wish_location/ai.state.getMapWidth(), production.def.is_stockpile_building ? PRIORITY_HIGH : production.priority));
}
}
}
}
}
workers_wanted = Math.max(workers_wanted, ai.farm_manager.workers_wanted);
workers_wanted -= workers_queued;
if (ai.state.isFog()) {
int xpct = (int)(Math.random()*HUNDRED_PERCENT);
if (xpct < WORKER_PROB) {
workers_wanted++;
}
}
for (int i = 0; i < units_possible.size(); i++) {
GeneralAIProduction production = (GeneralAIProduction)units_possible.get(i);
// if (production.def.is_worker && !(ai.workers/2 < ai.farm_manager.owned_farms.size())) {
// continue;
// }
// if (!production.def.is_worker) {
// if (((ai.getLesion()&GeneralAI.LESION_ONLY_RANGE) != 0 && production.def.attack_range <= 1) ||
// ((ai.getLesion()&GeneralAI.LESION_NO_RANGE) != 0 && production.def.attack_range > 1) ||
// ((ai.getLesion()&GeneralAI.LESION_ONLY_FLYING) != 0 && !production.def.is_flying) ||
// ((ai.getLesion()&GeneralAI.LESION_NO_FLYING) != 0 && production.def.is_flying)) {
// continue;
// }
// }
if ((production.priority != GeneralAI.DISTANCE_IGNORE && !production.def.is_worker) || (production.def.is_worker && (workers_wanted > 0))) {
// we can only build units near buildings
for (int j = 0; j < units.size(); j++) {
GeneralAIUnit building = units.get(j);
if (building.stats.isBuilding() && building.stats.getProduce().contains(production.id)) {
// maybe we just build units whereever?
ArrayList<Integer> locations = building.adjacent_build_locations(production, ai);
int location = locations.get((int)(Math.random()*locations.size()));
units_wanted.add(new GeneralAIProduction(production, location%ai.state.getMapWidth(), location/ai.state.getMapHeight(), production.priority));
if (production.def.is_worker) {
workers_queued++;
workers_wanted--;
if (workers_wanted <= 0) {
break;
}
}
}
}
}
}
// assign units to to do these builds
// check if any of the scouted units have become available
for (int i = 0; i < units_scouted.size(); i++) {
GeneralAIUnit unit = units_scouted.get(i);
if (unit.strategy == GeneralAI.STRATEGY_NONE) {
// free agent!!
unit.strategy = GeneralAI.STRATEGY_BUILD;
unit.wanted_strategy = GeneralAI.STRATEGY_NONE;
for (int j = 0; j < buildings_wanted.size(); j++) {
GeneralAIProduction building = buildings_wanted.get(j);
if (building.builder == unit.stats.getID()) {
for (int k = 0; k < building.def.cost.size(); k++) {
ai.money.set(k, ai.money.get(k)-building.def.cost.get(k));
}
unit.object = building;
buildings_wanted.remove(j);
break;
}
}
units.add(unit);
units_scouted.remove(i--);
} else if (unit.wanted_strategy == GeneralAI.STRATEGY_NONE) {
unit.wanted_strategy = GeneralAI.STRATEGY_BUILD;
}
}
// update what we're doing with each of the unit's we've got
for (int i = 0; i < units.size(); i++) {
GeneralAIUnit unit = units.get(i);
if (unit.object == null) {
if (!unit.stats.isBuilding()) {
// drop the unit, we'll pick him up later if he's what we want for another task
unit.strategy = GeneralAI.STRATEGY_NONE;
units.remove(i--);
} else {
unit.strategy = GeneralAI.STRATEGY_BUILD;
// check if the building can build anything
int distance = GeneralAI.DISTANCE_IGNORE;
int index = -1;
for (int j = 0; j < units_wanted.size(); j++) {
int d = units_wanted.get(j).distance(unit, ai);
if (d != GeneralAI.DISTANCE_IGNORE && (distance == GeneralAI.DISTANCE_IGNORE || d < distance)) {
unit.object = units_wanted.get(j);
distance = d;
index = j;
break;
}
}
if (unit.object != null) {
for (int k = 0; k < units_wanted.get(index).def.cost.size(); k++) {
ai.money.set(k, ai.money.get(k)-units_wanted.get(index).def.cost.get(k));
}
units_wanted.remove(index);
}
}
}
}
// figure out how units we want to scout
workers_wanted = 0;
if (ai.current_turn > BUILD_TURN_WAIT) {
for (int i = 0; i < buildings_wanted.size(); i++) {
GeneralAIProduction building = buildings_wanted.get(i);
if (building.builder == -1) {
GeneralAIUnit closest_unit = null;
int distance = GeneralAI.DISTANCE_IGNORE;
for (int j = 0; j < ai.units.size(); j++) {
GeneralAIUnit unit = ai.units.get(j);
if (unit.stats.isWorker() && unit.wanted_strategy != GeneralAI.STRATEGY_BUILD && unit.strategy != GeneralAI.STRATEGY_BUILD) { // make sure not a unit already scouted
int d = building.distance(unit, ai);
if (d != GeneralAI.DISTANCE_IGNORE && (distance == GeneralAI.DISTANCE_IGNORE || d < distance)) {
distance = d;
closest_unit = unit;
}
}
}
if (closest_unit != null) {
units_scouted.add(closest_unit);
closest_unit.wanted_strategy = GeneralAI.STRATEGY_BUILD;
building.builder = closest_unit.stats.getID();
} else {
workers_wanted++;
}
}
}
}
}
@Override
/**
* Updates the data on possible productions
*@param ai the parent ai
*/
public void update(GeneralAI ai) {
// in case a unit defintion ever changes (ie- an upgrade occurs)
for (int i = 0; i < units_possible.size(); i++) {
GeneralAIProduction production = (GeneralAIProduction)units_possible.get(i);
production.def = ai.state.getUnitList().get(production.id);
//production.priority = GeneralAI.DISTANCE_IGNORE;
}
for (int i = 0; i < buildings_possible.size(); i++) {
GeneralAIProduction production = (GeneralAIProduction)buildings_possible.get(i);
production.def = ai.state.getBuildingList().get(production.id);
production.priority = GeneralAI.DISTANCE_IGNORE;
}
}
/**
* Changes the location of what we want to build, if the other player is blocking
* @param unit the uni doing the building
* @param ai the parent ai
*/
public void change_build_location(GeneralAIUnit unit, GeneralAI ai) {
GeneralAIProduction product = (GeneralAIProduction)unit.object;
if (product.def.is_building) {
// need to call the town planner
for (int j = 0; j < ai.town_manager.towns.size(); j++) {
if (ai.town_manager.towns.get(j).in_limits(product.x+product.y*ai.state.getMapWidth(), ai.state.getMapWidth())) {
int wish_location = ai.town_manager.towns.get(j).get_location(product, ai);
if (wish_location != GeneralAITown.NO_VACANCY) {
unit.object = new GeneralAIProduction(product, wish_location%ai.state.getMapWidth(), wish_location/ai.state.getMapWidth(), product.priority);
break;
}
unit.object = null;
break;
}
}
} else {
// consider somewheres else adjacent
ArrayList<Integer> locations = unit.adjacent_build_locations(product, ai);
int location = locations.get((int)(Math.random()*locations.size()));
unit.object = new GeneralAIProduction(product, location%ai.state.getMapWidth(), location/ai.state.getMapHeight(), product.priority);
}
}
}