package ai.general; import java.util.ArrayList; import java.util.HashMap; import ai.AI; import rts.GameState; import rts.units.Unit; import rts.units.UnitAction; /** * \package ai.general * \brief The GeneralAI implemented to play generic RTS games * @author Jeff Bernard */ /** * \brief General AI built to play any generically defined RTS game * @author Jeff Bernard */ public class GeneralAI extends AI { public static final int LESION_NONE = 0; public static final int LESION_WORKER_ARMY = 1; public static final int LESION_NO_DEFENSE = 2; public static final int LESION_ONLY_RANGE = 4; public static final int LESION_NO_RANGE = 8; public static final int LESION_ONLY_FLYING = 16; public static final int LESION_NO_FLYING = 32; public static final int LESION_CHEAPEST_ARMY = 64; public static final int LESION_EXPENSIVE_ARMY = 128; public static final int LESION_WEAKEST_ARMY = 256; public static final int LESION_STRONGEST_ARMY = 512; public static final int STRATEGY_NONE = 0; /**< no strategy */ public static final int STRATEGY_BUILD = 1; /**< build */ public static final int STRATEGY_FARM = 2; /**< gather resources */ public static final int STRATEGY_ATTACK = 3; /**< attack enemy */ public static final int STRATEGY_EXPLORE = 4; /**< explore the map strat */ public static final int STRATEGY_DEFENSE = 5; /**< defend a position */ public static final int DISTANCE_IGNORE = -1; /**< ignore this distance */ private boolean init; /**< whether or not we have init */ private long turn_start; /**< when the turn started */ private long turn_limit; /**< maximum length for turn */ public TrafficMap traffic_map; /**< a map of our unit traffic */ public ExplorationManager exploration_manager; /**< exploration manager */ public ProductionManager production_manager; /**< production manager */ public AttackManager attack_manager; /**< attack manager */ public FarmManager farm_manager; /**< farm manager */ public TownManager town_manager; /**< town manager */ //private int worker_count; /**< how many workers we have */ public GameState state; /**< the game state */ public ArrayList<Integer> money; /**< money */ public ArrayList<GeneralAIUnit> units; /**< my units */ public int current_turn; /**< what turn is it */ public int player_id; /**< the id of the player this ai belongs to */ private int last_unit; /**< the last unit to make a move */ /** * Constructs the ai */ public GeneralAI() { super(); init = false; current_turn = 0; last_unit = 0; //worker_count = 0; player_id = -1; farm_manager = new FarmManager(); town_manager = new TownManager(); money = new ArrayList<Integer>(); units = new ArrayList<GeneralAIUnit>(); } public GeneralAI(int _lesion) { this(); setLesion(_lesion); } /** * The real constructor, basically */ private void initialize() { traffic_map = new TrafficMap(state.getMap().length); // I must own a unit... player_id = state.getMyUnits().get(0).getPlayer(); // bookkeeping need to run first exploration_manager = new ExplorationManager(this); production_manager = new ProductionManager(this); attack_manager = new AttackManager(production_manager.units_possible); for (int i = 0; i < state.getResourceTypes(); i++) { money.add(state.getResources(i)); } init = true; } @Override /** * Issues actions to units *@param gs the game state *@param time_limit how much time is given for this turn (msec) */ public void getAction(GameState gs, int time_limit) { turn_start = System.currentTimeMillis(); turn_limit = time_limit; state = gs; current_turn++; if (!init) { initialize(); } update_unit_list(); traffic_map.update(current_turn); production_manager.update(this); farm_manager.update(this); attack_manager.update(this); exploration_manager.update(this); town_manager.update(this); production_manager.manage_units(this); farm_manager.manage_units(this); town_manager.manage_units(this); attack_manager.manage_units(this); exploration_manager.manage_units(this); // execute unit orders boolean finished = true; boolean builders = false; for (int i = last_unit; i < units.size(); i++) { if (System.currentTimeMillis()-turn_start > turn_limit) { // System.out.println(player_id+" out of time on turn "+current_turn); last_unit = i; builders = false; finished = false; break; // out of time } units.get(i).act(this); if (units.get(i).stats.hasAction() && units.get(i).stats.getAction().getType() == UnitAction.BUILD) { builders = true; } } if (finished) { last_unit = 0; } if (!builders) { for (int i = 0; i < money.size(); i++) { money.set(i, state.getResources(i)); } } } /** * Updates the unit list */ private void update_unit_list() { for (int j = 0; j < units.size(); j++) { units.get(j).exists = false; units.get(j).wanted_strategy = STRATEGY_NONE; } for (int i = 0; i < state.getMyUnits().size(); i++) { boolean found = false; for (int j = 0; j < units.size(); j++) { if (units.get(j).equals(state.getMyUnits().get(i))) { units.get(j).exists = true; found = true; break; } } if (!found) { GeneralAIUnit unit = new GeneralAIUnit(state.getMyUnits().get(i), this); units.add(unit); if (unit.stats.isBuilding()) { // add to a town boolean added = false; for (int j = 0; j < town_manager.towns.size(); j++) { if (town_manager.towns.get(j).add(unit)) { added = true; break; } } if (!added) { // start a new town based around this building town_manager.towns.add(new GeneralAITown(unit, production_manager.buildings_possible.size())); } production_manager.units.add(unit); } else if (unit.stats.isWorker() && production_manager.workers_queued > 0) { production_manager.workers_queued--; } } } for (int j = 0; j < units.size(); j++) { if (!units.get(j).exists) { if (units.get(j).object != null) { units.get(j).remove(traffic_map); units.get(j).object.remove(units.get(j), this); } if (units.get(j).stats.isBuilding()) { // need to remove from the town for (int i = 0; i < town_manager.towns.size(); i++) { if (town_manager.towns.get(i).remove(units.get(j))) { if (town_manager.towns.get(i).population() == 0) { town_manager.towns.remove(i); } break; } } production_manager.remove_unit(units.get(j).stats.getID()); } else { if (units.get(j).stats.isWorker()) { farm_manager.remove_unit(units.get(j).stats.getID()); } exploration_manager.remove_unit(units.get(j).stats.getID()); attack_manager.remove_unit(units.get(j)); town_manager.remove_unit(units.get(j).stats.getID()); } units.remove(j--); } } } /** * Checks whether or not a unit can enter a location * @param unit the unit * @param location the location * @return whether or not */ private boolean can_enter(Unit unit, int location, int turn_start, int turn_end) { if ((exploration_manager.map[location]&(GameState.MAP_NEUTRAL|GameState.MAP_NONPLAYER)) == 0 && ((exploration_manager.map[location]&GameState.MAP_WALL) == 0 || unit.isFlying()) && traffic_map.valid(location, turn_start, turn_end)) { return true; } return false; } /** * Calculates the h from start to (a) goal * @param start * @param goals * @return */ private int h_score(int start, ArrayList<Integer> goals) { int h = DISTANCE_IGNORE; for (int i = 0; i < goals.size(); i++) { int dx = goals.get(i)%state.getMapWidth()-start%state.getMapWidth(); int dy = goals.get(i)/state.getMapWidth()-start/state.getMapWidth(); int dh = dx*dx+dy*dy; if (h == DISTANCE_IGNORE || dh < h) { h = dh; } } return h; } /** * Gets the path to a location (reversed) * @param unit * @param destinations */ public ArrayList<Integer[]> get_path(Unit unit, int start, int turn_start, ArrayList<Integer> destinations) { // multi-destination A* ArrayList<Integer> closed = new ArrayList<Integer>(); ArrayList<Integer> open = new ArrayList<Integer>(); open.add(start); HashMap<Integer, Integer> came_from = new HashMap<Integer, Integer>(); HashMap<Integer, Integer> g_score = new HashMap<Integer, Integer>(); HashMap<Integer, Integer> f_score = new HashMap<Integer, Integer>(); g_score.put(start, 0); f_score.put(start, h_score(start, destinations)); while (open.size() > 0) { int m = 0; int current = open.get(m); for (int i = 1; i < open.size(); i++) { if (f_score.get(open.get(i)) < f_score.get(current)) { current = open.get(i); m = i; } } int time = g_score.get(current)*unit.getMoveSpeed()+turn_start; int end = time+unit.getMoveSpeed(); if (destinations.contains(current)) { ArrayList<Integer[]> path = new ArrayList<Integer[]>(); if (current == start) { return path; } path.add(new Integer[]{current, time}); while (came_from.get(current) != null && came_from.get(current) != start) { current = came_from.get(current); time = g_score.get(current)*unit.getMoveSpeed()+turn_start; path.add(new Integer[]{current, time}); } return path; } open.remove(m); closed.add(current); int next_g = g_score.get(current)+1; int cx = current%state.getMapWidth(); int cy = current/state.getMapWidth(); int next = current-1; if (cx > 0 && (destinations.contains(next) || (!closed.contains(next) && can_enter(unit, next, time, end)))) { // left exists if (!open.contains(next) || next_g < g_score.get(next)) { came_from.put(next, current); g_score.put(next, next_g); f_score.put(next, next_g+h_score(next, destinations)); if (!open.contains(next)) { open.add(next); } } } next = current+1; if (cx < state.getMapWidth()-1 && (destinations.contains(next) || (!closed.contains(next) && can_enter(unit, next, time, end)))) { // right exists if (!open.contains(next) || next_g < g_score.get(next)) { came_from.put(next, current); g_score.put(next, next_g); f_score.put(next, next_g+h_score(next, destinations)); if (!open.contains(next)) { open.add(next); } } } next = current-state.getMapWidth(); if (cy > 0 && (destinations.contains(next) || (!closed.contains(next) && can_enter(unit, next, time, end)))) { // up exists if (!open.contains(next) || next_g < g_score.get(next)) { came_from.put(next, current); g_score.put(next, next_g); f_score.put(next, next_g+h_score(next, destinations)); if (!open.contains(next)) { open.add(next); } } } next = current+state.getMapWidth(); if (cy < state.getMapHeight()-1 && (destinations.contains(next) || (!closed.contains(next) && can_enter(unit, next, time, end)))) { // down exists if (!open.contains(next) || next_g < g_score.get(next)) { came_from.put(next, current); g_score.put(next, next_g); f_score.put(next, next_g+h_score(next, destinations)); if (!open.contains(next)) { open.add(next); } } } } return null; } /** * Allows the agent to label itself * @return a label */ @Override public String getLabel() { String label = "gAI"; if ((getLesion()&LESION_WORKER_ARMY) != 0) { label += "+WAtk"; } if ((getLesion()&LESION_NO_DEFENSE) != 0) { label += "-Def"; } if ((getLesion()&LESION_ONLY_RANGE) != 0) { label += "+R"; } if ((getLesion()&LESION_NO_RANGE) != 0) { label += "-R"; } if ((getLesion()&LESION_ONLY_FLYING) != 0) { label += "+F"; } if ((getLesion()&LESION_NO_FLYING) != 0) { label += "-F"; } if ((getLesion()&LESION_CHEAPEST_ARMY) != 0) { label += "-X"; } if ((getLesion()&LESION_EXPENSIVE_ARMY) != 0) { label += "+X"; } if ((getLesion()&LESION_WEAKEST_ARMY) != 0) { label += "-x"; } if ((getLesion()&LESION_STRONGEST_ARMY) != 0) { label += "+x"; } return label; } }