| Total Complexity | 95 |
| Total Lines | 321 |
| Duplicated Lines | 4.36 % |
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like AIs.ais_rebel often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # |
||
| 2 | # Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/] |
||
| 3 | # |
||
| 4 | # This file is part of Outer Space. |
||
| 5 | # |
||
| 6 | # Outer Space is free software; you can redistribute it and/or modify |
||
| 7 | # it under the terms of the GNU General Public License as published by |
||
| 8 | # the Free Software Foundation; either version 2 of the License, or |
||
| 9 | # (at your option) any later version. |
||
| 10 | # |
||
| 11 | # Outer Space is distributed in the hope that it will be useful, |
||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 14 | # GNU General Public License for more details. |
||
| 15 | # |
||
| 16 | # You should have received a copy of the GNU General Public License |
||
| 17 | # along with Outer Space; if not, write to the Free Software |
||
| 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||
| 19 | # |
||
| 20 | from ige import log |
||
| 21 | from ige.ospace import Const |
||
| 22 | from ige.ospace import Rules |
||
| 23 | from ige.ospace import Utils |
||
| 24 | from ige.ospace import TechHandlers |
||
| 25 | |||
| 26 | import ai_tools as tool |
||
| 27 | from ais_base import AI |
||
| 28 | |||
| 29 | import copy, random, math |
||
| 30 | |||
| 31 | class Rebel(AI): |
||
| 32 | def __init__(self, client): |
||
| 33 | super(Rebel, self).__init__(client) |
||
| 34 | tool.doRelevance(self.data, self.client, self.db, 10) |
||
| 35 | self.designs = {} |
||
| 36 | |||
| 37 | def offense_manager(self): |
||
| 38 | # rebel is not suited for attacks, yet |
||
| 39 | pass |
||
| 40 | |||
| 41 | def _get_idle_planets(self, system): |
||
| 42 | idle_planets = set() |
||
| 43 | for planet_id in self.data.myProdPlanets & set(system.planets): |
||
| 44 | planet = self.db[planet_id] |
||
| 45 | if not getattr(planet, 'prodQueue', None): |
||
| 46 | idle_planets.add(planet_id) |
||
| 47 | continue |
||
| 48 | return idle_planets |
||
| 49 | |||
| 50 | def _fill_planets(self, system, idle_planets): |
||
| 51 | system_stats = tool.getSystemStructStats(self.data, self.client, self.db, system.oid) |
||
| 52 | for planet_id in copy.copy(idle_planets): |
||
| 53 | planet = self.db[planet_id] |
||
| 54 | prod_tech_id = Rules.Tech.RESCENTRE1 if planet.plMin < 100 else Rules.Tech.FACTORY1 |
||
| 55 | prod_tech = self.client.getFullTechInfo(prod_tech_id) |
||
| 56 | # now we ignore all already build structures, and try to satisfy |
||
| 57 | # outpost/fact or outpost/labs ration [on free slots] |
||
| 58 | if planet.plSlots > len(planet.slots): |
||
| 59 | if system_stats.en > prod_tech.operEn and system_stats.bio > prod_tech.operWorkers / 100: |
||
| 60 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid, |
||
| 61 | prod_tech.id, 1, planet_id, prod_tech.id < 1000, 0, Const.OID_NONE) |
||
| 62 | idle_planets.remove(planet_id) |
||
| 63 | else: |
||
| 64 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, |
||
| 65 | Rules.Tech.OUTPOST1, 1, planet_id, Rules.Tech.OUTPOST1 < 1000, 0, Const.OID_NONE) |
||
| 66 | idle_planets.remove(planet_id) |
||
| 67 | |||
| 68 | def _colonize_planets(self, system, idle_planets): |
||
| 69 | toColonize = self.data.freePlanets & set(system.planets) |
||
| 70 | # colonize remaining planets |
||
| 71 | for planet_id in copy.copy(idle_planets): |
||
| 72 | planet = self.db[planet_id] |
||
| 73 | if toColonize: |
||
| 74 | targetID = toColonize.pop() |
||
| 75 | target = self.db[targetID] |
||
| 76 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, |
||
| 77 | Rules.Tech.OUTPOST1, 1, |
||
| 78 | targetID, Rules.Tech.OUTPOST1 < 1000, 0, Const.OID_NONE) |
||
| 79 | idle_planets.remove(planet_id) |
||
| 80 | |||
| 81 | def _build_ships(self, system, idle_planets): |
||
| 82 | for planet_id in copy.copy(idle_planets): |
||
| 83 | planet = self.db[planet_id] |
||
| 84 | systemFleets = getattr(system, 'fleets', []) |
||
| 85 | has_colony = False |
||
| 86 | has_scouts = False |
||
| 87 | shared_system = len(set(system.planets) & self.data.otherPlanets) > 0 |
||
| 88 | for fleetID in systemFleets: |
||
| 89 | fleet = self.db[fleetID] |
||
| 90 | if getattr(fleet, 'owner', Const.OID_NONE) == self.player.oid: |
||
| 91 | has_colony |= tool.fleetContains(fleet, {self.designs["colony"]:1}) |
||
| 92 | has_scouts |= tool.fleetContains(fleet, {self.designs["scout"]:1}) |
||
| 93 | if not has_colony: |
||
| 94 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, |
||
| 95 | self.designs["colony"], 1, planet_id, self.designs["colony"] < 1000, 0, Const.OID_NONE) |
||
| 96 | elif not has_scouts: |
||
| 97 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, |
||
| 98 | self.designs["scout"], 1, planet_id, self.designs["scout"] < 1000, 0, Const.OID_NONE) |
||
| 99 | elif not shared_system: |
||
| 100 | # build fighters |
||
| 101 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, |
||
| 102 | self.designs["fighter"], 1, planet_id, self.designs["fighter"] < 1000, 0, Const.OID_NONE) |
||
| 103 | |||
| 104 | def _planet_manager(self): |
||
| 105 | for planet_id in self.data.myPlanets: |
||
| 106 | tool.sortStructures(self.client, self.db, planet_id) |
||
| 107 | for system_id in self.data.mySystems: |
||
| 108 | system = self.db[system_id] |
||
| 109 | idle_planets = self._get_idle_planets(system) |
||
| 110 | # build production buildings if nothing is needed, or outposts |
||
| 111 | self._fill_planets(system, idle_planets) |
||
| 112 | self._colonize_planets(system, idle_planets) |
||
| 113 | self._build_ships(system, idle_planets) |
||
| 114 | |||
| 115 | def _place_gov_center(self, candidate_planets): |
||
| 116 | PLACEHOLDERS = (Rules.Tech.RESCENTRE1, Rules.Tech.FACTORY1) |
||
| 117 | for planet_id in candidate_planets: |
||
| 118 | planet = self.db[planet_id] |
||
| 119 | if planet.prodProd == 0: |
||
| 120 | continue |
||
| 121 | gov_placeholder = None |
||
| 122 | for struct in [x for x in planet.slots if x[0] in PLACEHOLDERS]: |
||
| 123 | if gov_placeholder is not None: |
||
| 124 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, |
||
| 125 | Rules.Tech.GOVCENTER1, 1, planet_id, Rules.Tech.GOVCENTER1 < 1000, 0, gov_placeholder) |
||
| 126 | planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, |
||
| 127 | Rules.Tech.OUTPOST1, 1, planet_id, Rules.Tech.OUTPOST1 < 1000, 0, struct[0]) |
||
| 128 | return |
||
| 129 | gov_placeholder = struct[0] |
||
| 130 | |||
| 131 | def _cancel_gov_tasks(self, planet_ids): |
||
| 132 | # cancel all tasks |
||
| 133 | for planet_id in planet_ids: |
||
| 134 | planet = self.db[planet_id] |
||
| 135 | indexes = [] |
||
| 136 | i = 0 |
||
| 137 | for task in planet.prodQueue: |
||
| 138 | if task.techID in set([1000, 3010, 3011]): |
||
| 139 | indexes.append(i) |
||
| 140 | i += 1 |
||
| 141 | indexes.reverse() |
||
| 142 | for index in indexes: |
||
| 143 | self.client.cmdProxy.abortConstruction(planet_id, index) |
||
| 144 | |||
| 145 | def _get_current_gov(self): |
||
| 146 | gov_position = Const.OID_NONE |
||
| 147 | gov_productions = [] |
||
| 148 | for planet_id in self.data.myPlanets: |
||
| 149 | # find build gov center |
||
| 150 | planet = self.db[planet_id] |
||
| 151 | for struct in planet.slots: |
||
| 152 | if struct[0] in set([1000, 3010, 3011]): |
||
| 153 | gov_position = planet_id |
||
| 154 | break |
||
| 155 | for task in getattr(planet, 'prodQueue', []): |
||
| 156 | if task.techID in set([1000, 3010, 3011]): |
||
| 157 | gov_productions.append(planet_id) |
||
| 158 | break |
||
| 159 | return gov_position, gov_productions |
||
| 160 | |||
| 161 | def _empire_manager(self): |
||
| 162 | if not Rules.Tech.GOVCENTER1 in self.player.techs.keys(): |
||
| 163 | return |
||
| 164 | candidates = tool.findPopCenterPlanets(self.db, self.data.myPlanets) |
||
| 165 | candidate_planets = candidates[:10] |
||
| 166 | gov_position, gov_productions = self._get_current_gov() |
||
| 167 | if not set(candidate_planets) & (set([gov_position]) | set(gov_productions)): |
||
| 168 | self._cancel_gov_tasks(gov_productions) |
||
| 169 | self._place_gov_center(candidate_planets) |
||
| 170 | return |
||
| 171 | |||
| 172 | def _expansion_manager(self): |
||
| 173 | should_repeat = True |
||
| 174 | pirate_influenced_systems = tool.findInfluence(self.data, self.client, self.db, Rules.pirateInfluenceRange, self.data.pirateSystems) |
||
| 175 | while should_repeat: |
||
| 176 | should_repeat = False |
||
| 177 | should_repeat |= self._explore(self.designs["scout"]) |
||
| 178 | safe_systems = (self.data.freeSystems & self.data.relevantSystems) - pirate_influenced_systems |
||
| 179 | should_repeat |= self._colonize_free_systems(safe_systems, self.designs["colony"]) |
||
| 180 | |||
| 181 | def _ship_design_manager(self): |
||
| 182 | for desID in self.player.shipDesigns: |
||
| 183 | design = self.player.shipDesigns[desID] |
||
| 184 | if design.name == 'Scout': |
||
| 185 | self.designs["scout"] = desID |
||
| 186 | elif design.name == 'Fighter': |
||
| 187 | self.designs["fighter"] = desID |
||
| 188 | elif design.name == 'Bomber': |
||
| 189 | self.designs["bomber"] = desID |
||
| 190 | elif design.name == 'Colony Ship': |
||
| 191 | self.designs["colony"] = desID |
||
| 192 | if "scout" not in self.designs: |
||
| 193 | self.designs["scout"] = self.client.cmdProxy.addShipDesign(self.player.oid, 'Scout', |
||
| 194 | Rules.Tech.SMALLHULL0, {Rules.Tech.SCOCKPIT0:1, |
||
| 195 | Rules.Tech.SCANNERMOD0:1, Rules.Tech.FTLENG0:3}) |
||
| 196 | if "fighter" not in self.designs: |
||
| 197 | self.designs["fighter"] = self.client.cmdProxy.addShipDesign(self.player.oid, 'Fighter', |
||
| 198 | Rules.Tech.SMALLHULL0, {Rules.Tech.SCOCKPIT0:1, |
||
| 199 | Rules.Tech.CANNON0:2, Rules.Tech.FTLENG0:3}) |
||
| 200 | if "bomber" not in self.designs: |
||
| 201 | self.designs["bomber"] = self.client.cmdProxy.addShipDesign(self.player.oid, 'Bomber', |
||
| 202 | Rules.Tech.SMALLHULL0, {Rules.Tech.SCOCKPIT0:1, |
||
| 203 | Rules.Tech.CONBOMB0:1, Rules.Tech.FTLENG0:3}) |
||
| 204 | if "colony" not in self.designs: |
||
| 205 | self.designs["colony"] = self.client.cmdProxy.addShipDesign(self.player.oid, 'Colony Ship', |
||
| 206 | Rules.Tech.MEDIUMHULL0, {Rules.Tech.SCOCKPIT0:1, |
||
| 207 | Rules.Tech.COLONYMOD0:1, Rules.Tech.FTLENG0:4}) |
||
| 208 | |||
| 209 | def research_manager(self): |
||
| 210 | researchable = set() |
||
| 211 | if len(self.player.rsrchQueue) < 2: |
||
| 212 | for tech_id in self.player.techs.keys(): |
||
| 213 | tech = self.client.getTechInfo(tech_id) |
||
| 214 | improvement = self.player.techs[tech_id] |
||
| 215 | if improvement < Rules.techMaxImprovement and\ |
||
| 216 | improvement < tech.maxImprovement: |
||
| 217 | researchable.add(tech_id) |
||
| 218 | for tech_id in self.client.getAllTechIDs(): |
||
| 219 | tech = self.client.getTechInfo(tech_id) |
||
| 220 | if not hasattr(tech, "partialData") or not hasattr(tech, 'researchMod'): |
||
| 221 | continue |
||
| 222 | else: |
||
| 223 | researchable.add(tech_id) |
||
| 224 | for task in self.player.rsrchQueue: |
||
| 225 | researchable -= set([task.techID]) |
||
| 226 | # some less useful technologies for AI - deprioritize |
||
| 227 | lessTechs = set([1102, 1104, 1107, 1110, 1112, 1404, 1510, 1800, 1801, 1802]) |
||
| 228 | if len(researchable - (lessTechs | set([1990, 1991, 1992]))) > 0: |
||
| 229 | researchable -= lessTechs |
||
| 230 | # do not advance, for now |
||
| 231 | researchable -= set([1990, 1991, 1992]) |
||
| 232 | if len(researchable) > 0: |
||
| 233 | if Rules.Tech.OUTPOST1 in researchable: |
||
| 234 | self.player.rsrchQueue = self.client.cmdProxy.startResearch(self.player.oid, Rules.Tech.OUTPOST1) |
||
| 235 | return |
||
| 236 | possibilities = list(researchable) |
||
| 237 | random.shuffle(possibilities) |
||
| 238 | tech_id = possibilities[0] |
||
| 239 | self.player.rsrchQueue = self.client.cmdProxy.startResearch(self.player.oid, tech_id) |
||
| 240 | |||
| 241 | View Code Duplication | def diplomacy_manager(self): |
|
|
|
|||
| 242 | for contact_id in self.player.diplomacyRels: |
||
| 243 | dipl = self.client.getDiplomacyWith(contact_id) |
||
| 244 | for pact_id in [Const.PACT_ALLOW_CIVILIAN_SHIPS, Const.PACT_ALLOW_TANKING, Const.PACT_MINOR_SCI_COOP, Const.PACT_MAJOR_SCI_COOP, Const.PACT_MINOR_CP_COOP, Const.PACT_MAJOR_CP_COOP]: |
||
| 245 | pactSpec = Rules.pactDescrs[pact_id] |
||
| 246 | if dipl.relation < pactSpec.validityInterval[0] or dipl.relation > pactSpec.validityInterval[1]: |
||
| 247 | # not friendly enough |
||
| 248 | continue |
||
| 249 | if pact_id in dipl.pacts and dipl.pacts[pact_id][0] in [Const.PACT_ACTIVE, Const.PACT_INACTIVE]: |
||
| 250 | # nothing more to do, move along |
||
| 251 | continue |
||
| 252 | # hey, we should enable this pact! |
||
| 253 | conditions = [pact_id] |
||
| 254 | self.player.diplomacyRels = self.client.cmdProxy.changePactCond(self.player.oid, contact_id, pact_id, Const.PACT_INACTIVE, conditions) |
||
| 255 | |||
| 256 | def _help_system(self): |
||
| 257 | tool.doDanger(self.data, self.client, self.db) |
||
| 258 | pirate_influenced_systems = tool.findInfluence(self.data, self.client, self.db, Rules.pirateInfluenceRange, self.data.pirateSystems) |
||
| 259 | one_fighter_mp = self.player.shipDesigns[self.designs["fighter"]].combatPwr |
||
| 260 | for system_id in set(self.data.endangeredSystems) - set(pirate_influenced_systems): |
||
| 261 | mil_pwr, shipQuantity = self.data.endangeredSystems[system_id] |
||
| 262 | mil_pwr = -mil_pwr |
||
| 263 | if system_id in self.data.myMPPerSystem: |
||
| 264 | mil_pwr += self.data.myMPPerSystem[system_id] |
||
| 265 | if mil_pwr < 0: |
||
| 266 | system = self.db[system_id] |
||
| 267 | nearest = tool.findNearest(self.db, system, self.data.mySystems, 99999, 20)[1:] |
||
| 268 | for temp_id in nearest: |
||
| 269 | if temp_id in self.data.myMPPerSystem: |
||
| 270 | temp_mp = self.data.myMPPerSystem[temp_id] |
||
| 271 | else: |
||
| 272 | temp_mp = 0 |
||
| 273 | self.data.myMPPerSystem[temp_id] = 0 |
||
| 274 | if temp_id in self.data.endangeredSystems: |
||
| 275 | a, b = self.data.endangeredSystems[temp_id] |
||
| 276 | temp_mp -= a * 1.5 |
||
| 277 | if temp_mp <= 0: continue |
||
| 278 | orig = temp_mp = min(-mil_pwr, temp_mp) * 1.25 |
||
| 279 | # this is just prototype, working only with Fighters |
||
| 280 | quantity = int(math.ceil(temp_mp / float(one_fighter_mp))) |
||
| 281 | if quantity == 0: |
||
| 282 | continue |
||
| 283 | ships_left, mil_pwrSend = tool.orderFromSystem(self.client, self.db, |
||
| 284 | {self.designs["fighter"]:quantity}, temp_id, Const.FLACTION_MOVE, system_id, None) |
||
| 285 | mil_pwr += mil_pwrSend |
||
| 286 | self.data.myMPPerSystem[temp_id] -= mil_pwrSend |
||
| 287 | if mil_pwr > 0: break |
||
| 288 | |||
| 289 | def _return_strays(self): |
||
| 290 | for fleetID in self.data.idleFleets: |
||
| 291 | fleet = self.db[fleetID] |
||
| 292 | # fleets orbiting in systems not belonging to the self.player |
||
| 293 | if fleet.orbiting and fleet.orbiting not in self.data.mySystems: |
||
| 294 | nearest = tool.findNearest(self.db, fleet, self.data.mySystems, 99999, 1) |
||
| 295 | if len(nearest): |
||
| 296 | targetID = nearest[0] |
||
| 297 | tool.orderFleet(self.client, self.db, fleetID, Const.FLACTION_MOVE, targetID, None) |
||
| 298 | |||
| 299 | def defense_manager(self): |
||
| 300 | self._help_system() |
||
| 301 | self._return_strays() |
||
| 302 | |||
| 303 | def economy_manager(self): |
||
| 304 | self._empire_manager() |
||
| 305 | self._planet_manager() |
||
| 306 | self._expansion_manager() |
||
| 307 | |||
| 308 | def run(self): |
||
| 309 | self._ship_design_manager() # this fills self.designs, needs to go first |
||
| 310 | self.research_manager() |
||
| 311 | self.diplomacy_manager() |
||
| 312 | self.economy_manager() |
||
| 313 | self.defense_manager() |
||
| 314 | self.offense_manager() |
||
| 315 | |||
| 316 | |||
| 317 | def run(aclient): |
||
| 318 | ai = Rebel(aclient) |
||
| 319 | ai.run() |
||
| 320 | aclient.saveDB() |
||
| 321 | |||
| 322 |