AIs.ais_pirate.Pirate._get_system_info()   F
last analyzed

Complexity

Conditions 14

Size

Total Lines 43
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 40
nop 2
dl 0
loc 43
rs 3.6
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like AIs.ais_pirate.Pirate._get_system_info() 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
import random, copy, math
21
22
from ige import log
23
from ige.ospace import Const
24
from ige.ospace import Rules
25
from ige.ospace import Utils
26
from ige.IDataHolder import IDataHolder
27
28
import ai_tools as tool
29
from ai import AI
30
31
class Pirate(AI):
32
    def __init__(self, client):
33
        enemy_types = [Const.T_PLAYER, Const.T_AIPLAYER, Const.T_AIRENPLAYER, Const.T_AIMUTPLAYER]
34
        super(Pirate, self).__init__(client, enemy_types)
35
        tool.doRelevance(self.data, self.client, self.db, Rules.pirateInfluenceRange)
36
37
    def _fill_with_dens(self, system_info):
38
        for planet_id in copy.copy(system_info.idle_planets):
39
            planet = self.db[planet_id]
40
            if planet.plSlots > len(planet.slots):
41
                log.debug(self.player.oid, "PIRATEAI - building pirate den", planet.oid)
42
                dens_to_build = planet.plSlots - len(planet.slots)
43
                planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
44
                    Rules.Tech.PIRATEDEN, dens_to_build,
45
                    planet.oid, Rules.Tech.PIRATEDEN < 1000, 0, Const.OID_NONE)
46
                system_info.idle_planets.remove(planet_id)
47
                system_info.dens[planet_id] += dens_to_build
48
                continue
49
50
    def _build_defensive_bases(self, system_info):
51
        for planet_id in copy.copy(system_info.idle_planets):
52
            planet = self.db[planet_id]
53
            if system_info.bases[planet_id] < 2 and planet.plSlots >= 2:
54
                dens_sum = sum(system_info.dens.values())
55
                # build on the other structure [something nonpiratish :)] or
56
                # pirate den
57
                if system_info.other_struct_id[planet_id]:
58
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
59
                        Rules.Tech.PIRATEBASE, 1, planet.oid, Rules.Tech.PIRATEBASE < 1000,
60
                        0, system_info.other_struct_id[planet_id])
61
                    system_info.idle_planets.remove(planet_id)
62
                    return
63
                elif dens_sum:
64
                    bases_to_build = min(dens_sum, 2 - system_info.bases[planet_id])
65
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
66
                        Rules.Tech.PIRATEBASE, bases_to_build,
67
                        planet.oid, Rules.Tech.PIRATEBASE < 1000, 0,
68
                        Rules.Tech.PIRATEDEN)
69
                    system_info.idle_planets.remove(planet_id)
70
                    system_info.dens[planet_id] -= bases_to_build
71
                    return
72
73
    def _build_bases(self, system_info):
74
        to_colonize = self.data.freePlanets & set(system_info.system.planets)
75
        for planet_id in copy.copy(system_info.idle_planets):
76
            planet = self.db[planet_id]
77
            for target_id in to_colonize:
78
                target = self.db[target_id]
79
                if target.owner == self.player.oid:
80
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
81
                        Rules.Tech.PIRATEDEN, 1,
82
                        target_id, Rules.Tech.PIRATEDEN < 1000, 0, Const.OID_NONE)
83
                    system_info.idle_planets.remove(planet_id)
84
                    system_info.dens[target_id] = 1
85
                    break
86
                else:
87
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
88
                        Rules.Tech.PIRATEBASE, 1,
89
                        target_id, Rules.Tech.PIRATEBASE < 1000, 0, Const.OID_NONE)
90
                    system_info.idle_planets.remove(planet_id)
91
                    break
92
93
    def _build_brewery(self, system_info):
94
        for planet_id in copy.copy(system_info.idle_planets) & set(system_info.dens.keys()):
95
            planet = self.db[planet_id]
96
            if system_info.dens[planet_id] < 1:
97
                # there was den, but it is not anymore, try another planet
98
                continue
99
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
100
                Rules.Tech.PIRATEBREWERY, 1, planet_id,
101
                Rules.Tech.PIRATEBASE < 1000, 0, Rules.Tech.PIRATEDEN)
102
            system_info.idle_planets.remove(planet_id)
103
            system_info.dens[planet_id] -= 1
104
            system_info.breweries += 1
105
            return
106
107
    def _build_prisons(self, system_info):
108
        sumOfDens = sum(system_info.dens.values())
109
        denTech = Rules.techs[Rules.Tech.PIRATEDEN]
110
        prisonTech = Rules.techs[Rules.Tech.PIRATEPRISON]
111
        energy = sumOfDens * denTech.prodEn * 1.25 - system_info.prisons * prisonTech.operEn
112
        possiblePrisons = math.floor(energy / (denTech.prodEn * 1.25 + prisonTech.operEn))
113
        for planet_id in copy.copy(system_info.idle_planets) & set(system_info.dens.keys()):
114
            planet = self.db[planet_id]
115
            if system_info.dens[planet_id] < 1 or possiblePrisons < 1:
116
                # there was a Den, but it is not there anymore, try another planet
117
                continue
118
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
119
                Rules.Tech.PIRATEPRISON, 1, planet_id,
120
                Rules.Tech.PIRATEBASE < 1000, 0, Rules.Tech.PIRATEDEN)
121
            system_info.idle_planets.remove(planet_id)
122
            system_info.dens[planet_id] -= 1
123
            possiblePrisons -= 1
124
125
    def _build_shipyard(self, system_info):
126
        slots = 0
127
        for planet_id in system_info.system.planets:
128
            slots += self.db[planet_id].plSlots
129
        if slots <= 10 or system_info.shipyards:
130
            return
131
        for planet_id in copy.copy(system_info.idle_planets) & set(system_info.dens.keys()):
132
            planet = self.db[planet_id]
133
            if system_info.dens[planet_id] < 1:
134
                # there was a Den, but it is not there anymore, try another planet
135
                continue
136
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
137
                Rules.Tech.PIRATESD, 1, planet_id,
138
                Rules.Tech.PIRATEBASE < 1000, 0, Rules.Tech.PIRATEDEN)
139
            system_info.idle_planets.remove(planet_id)
140
            system_info.dens[planet_id] -= 1
141
            system_info.shipyards = 1
142
            return
143
144
    def _expand_slots(self, system_info):
145
        if Rules.Tech.ADDSLOT3 not in self.player.techs:
146
            return
147
        for planet_id in copy.copy(system_info.idle_planets):
148
            planet = self.db[planet_id]
149
            if planet.plSlots < planet.plMaxSlots:
150
                planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
151
                    Rules.Tech.ADDSLOT3, 1, planet_id,
152
                    Rules.Tech.ADDSLOT3 < 1000, 0, Const.OID_NONE)
153
                planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
154
                    Rules.Tech.PIRATEDEN, 1, planet_id,
155
                    Rules.Tech.PIRATEDEN < 1000, 0, Const.OID_NONE)
156
                system_info.idle_planets.remove(planet_id)
157
158
    def _condensePlanet(self, planet, target):
159
        if Rules.Tech.PLCOND5 in self.player.techs:
160
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
161
                Rules.Tech.PLCOND5, 1, target.oid,
162
                Rules.Tech.PLCOND5 < 1000, 0, Const.OID_NONE)
163
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
164
                Rules.Tech.PIRATEBASE, 1, target.oid,
165
                Rules.Tech.PIRATEBASE < 1000, 0, Const.OID_NONE)
166
            self.data.nonhabPlanets.remove(target.oid)
167
168
    def _assemblePlanet(self, planet, target):
169
        if Rules.Tech.PLASSEMBL5 in self.player.techs:
170
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
171
                Rules.Tech.PLASSEMBL5, 1, target.oid,
172
                Rules.Tech.PLASSEMBL5 < 1000, 0, Const.OID_NONE)
173
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
174
                Rules.Tech.PIRATEBASE, 1, target.oid,
175
                Rules.Tech.PIRATEBASE < 1000, 0, Const.OID_NONE)
176
            self.data.nonhabPlanets.remove(target.oid)
177
178
    def _create_planets(self, system_info):
179
        for target_id in copy.copy(self.data.nonhabPlanets & set(system_info.system.planets)):
180
            target = self.db[target_id]
181
            try:
182
                planet_id = system_info.idle_planets.pop()
183
            except KeyError:
184
                return
185
            planet = self.db[planet_id]
186
            if target.plType == u'G':
187
                self._condensePlanet(planet, target)
188
            elif target.plType == u'A':
189
                self._assemblePlanet(planet, target)
190
191
    def _build_ships(self, system_info):
192
        system_fleets = getattr(system_info.system, 'fleets', [])
193
        has_scout = False
194
        for fleet_id in system_fleets:
195
            fleet = self.db[fleet_id]
196
            if getattr(fleet, 'owner', Const.OID_NONE) == self.player.oid:
197
                if tool.fleetContains(fleet, {4:1}):
198
                    has_scout = True
199
        for planet_id in system_info.idle_planets:
200
            planet = self.db[planet_id]
201
            if not has_scout:
202
                planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 4, 1, planet_id, True, False, Const.OID_NONE)
203
            else:
204
                dice = random.randint(1, 3)
205
                if dice == 1:
206
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 1, 3, planet_id, True, False, Const.OID_NONE)
207
                elif dice == 2:
208
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 2, 3, planet_id, True, False, Const.OID_NONE)
209
                else:
210
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 3, 2, planet_id, True, False, Const.OID_NONE)
211
212
    def _get_system_info(self, system):
213
        system_info = IDataHolder()
214
        # my planets in the system
215
        system_info.system = system
216
        system_info.breweries = 0
217
        system_info.shipyards = 0
218
        system_info.prisons = 0
219
        system_info.dens = {}
220
        system_info.bases = {}
221
        system_info.other_struct_id = {}
222
        system_info.idle_planets = self.data.myPlanets & set(system.planets)
223
        for planet_id in copy.copy(system_info.idle_planets):
224
            planet = self.db[planet_id]
225
            system_info.bases[planet_id] = 0
226
            system_info.other_struct_id[planet_id] = None
227
            system_info.dens[planet_id] = 0
228
            for struct in planet.slots:
229
                if struct[0] == Rules.Tech.PIRATEBASE:
230
                    system_info.bases[planet_id] += 1
231
                elif struct[0] == Rules.Tech.PIRATEBREWERY:
232
                    system_info.breweries += 1
233
                elif struct[0] == Rules.Tech.PIRATEDEN:
234
                    system_info.dens[planet_id] += 1
235
                elif struct[0] == Rules.Tech.PIRATESD:
236
                    system_info.shipyards += 1
237
                elif struct[0] == Rules.Tech.PIRATEPRISON:
238
                    system_info.prisons += 1
239
                else:
240
                    system_info.other_struct_id[planet_id] = struct[0]
241
            if getattr(planet, 'prodQueue', None):
242
                # something is in the production queue, account it and do next
243
                for task in planet.prodQueue:
244
                    if task.techID == Rules.Tech.PIRATEBREWERY:
245
                        system_info.breweries += task.quantity
246
                    elif task.techID == Rules.Tech.PIRATESD:
247
                        system_info.shipyards += task.quantity
248
                    elif task.techID == Rules.Tech.PIRATEPRISON:
249
                        system_info.prisons += task.quantity
250
                    elif task.techID in (Rules.Tech.PLCOND5, Rules.Tech.PLASSEMBL5):
251
                        self.data.nonhabPlanets.remove(task.targetID)
252
                system_info.idle_planets.remove(planet_id)
253
                continue
254
        return system_info
255
256
    def _retreat_fleet(self, fleet):
257
        max_range = tool.subfleetMaxRange(self.client, self.db, None, fleet.oid)
258
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.mySystems)
259
        if len(nearest_sys_ids):
260
            nearest_sys_id = nearest_sys_ids[0]
261
            target = self.db[nearest_sys_id]
262
            distance = math.hypot(target.x-fleet.x, target.y-fleet.y)
263
            if distance >= max_range:
264
                tool.orderFleet(self.client, self.db, fleet.oid, Const.FLACTION_MOVE, nearest_sys_id, None)
265
266
    def _return_fleet(self, fleet):
267
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.mySystems)
268
        if len(nearest_sys_ids):
269
            nearest_sys_id = nearest_sys_ids[0]
270
            tool.orderFleet(self.client, self.db, fleet.oid, Const.FLACTION_MOVE, nearest_sys_id, None)
271
272
    def _commence_attack(self, fleet):
273
        sheet = tool.getFleetSheet(fleet)
274
        ships = {}
275
        ships[3] = min(sheet[1], sheet[2], sheet[3])
276
        ships[1] = ships[2] = ships[3]
277
        ships[4] = 1
278
        max_range = tool.subfleetMaxRange(self.client, self.db, ships, fleet.oid)
279
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.enemySystems & self.data.relevantSystems, max_range * 0.45)
280
        if len(nearest_sys_ids):
281
            nearestSys = nearest_sys_ids[0]
282
            tool.orderPartFleet(self.client, self.db, ships, False, fleet.oid, Const.FLACTION_MOVE, nearestSys, None)
283
284
    def _followup_attack(self, fleet):
285
        max_range = tool.subfleetMaxRange(self.client, self.db, None, fleet.oid)
286
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.enemySystems & self.data.relevantSystems, max_range)
287
        if len(nearest_sys_ids):
288
            nearest_sys_id = nearest_sys_ids[0]
289
            tool.orderFleet(self.client, self.db, fleet.oid, Const.FLACTION_MOVE, nearest_sys_id, None)
290
        else:
291
            self._return_fleet(fleet)
292
293
    def fleets_manager(self):
294
        attack_fleets = set()
295
        attack_minimum = {1:10, 2:10, 3:10, 4:1}
296
        for fleet_id in self.data.myFleets & self.data.idleFleets:
297
            fleet = self.db.get(fleet_id, None)
298
            if fleet.combatCounter and fleet.orbiting not in self.data.mySystems:
299
                self._retreat_fleet(fleet)
300
            elif not fleet.orbiting in self.data.mySystems:
301
                if tool.fleetContains(fleet, attack_minimum):
302
                    attack_fleets.add(fleet)
303
                else:
304
                    self._return_fleet(fleet)
305
            else:
306
                if tool.fleetContains(fleet, attack_minimum):
307
                    attack_fleets.add(fleet)
308
        for fleet in attack_fleets:
309
            if fleet.orbiting in self.data.mySystems:
310
                self._commence_attack(fleet)
311
            else:
312
                self._followup_attack(fleet)
313
314
    def planet_manager(self):
315
        for planet_id in self.data.myPlanets:
316
            tool.sortStructures(self.client, self.db, planet_id)
317
        for systemID in self.data.mySystems:
318
            system = self.db[systemID]
319
            system_info = self._get_system_info(system)
320
            self._fill_with_dens(system_info)
321
            self._build_defensive_bases(system_info)
322
            self._build_bases(system_info)
323
            self._build_brewery(system_info)
324
            self._build_prisons(system_info)
325
            self._build_shipyard(system_info)
326
            self._expand_slots(system_info)
327
            self._create_planets(system_info)
328
            self._build_ships(system_info)
329
330
    def ship_design_manager(self):
331
        if len(self.player.shipDesigns.keys()) < 4:
332
            self.client.cmdProxy.addShipDesign(self.player.oid, 'Brawler',
333
                    Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
334
                    Rules.Tech.CANNON1:2, Rules.Tech.PIRATEFTLENG:3})
335
            self.client.cmdProxy.addShipDesign(self.player.oid, "Enforcer",
336
                    Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
337
                    Rules.Tech.SSROCKET2:2, Rules.Tech.PIRATEFTLENG:3})
338
            self.client.cmdProxy.addShipDesign(self.player.oid, "Raider",
339
                     Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
340
                     Rules.Tech.CONBOMB1:1, Rules.Tech.PIRATEFTLENG:3})
341
            self.client.cmdProxy.addShipDesign(self.player.oid, "Squeal",
342
                    Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
343
                    Rules.Tech.SCANNERMOD1:1, Rules.Tech.PIRATEFTLENG:3})
344
345
    def offense_manager(self):
346
        self.ship_design_manager()
347
        self.fleets_manager()
348
349
    def economy_manager(self):
350
        self.planet_manager()
351
352
    def run(self):
353
        self.offense_manager()
354
        self.economy_manager()
355
356
def run(aclient):
357
    ai = Pirate(aclient)
358
    ai.run()
359
    aclient.saveDB()
360
361
362
363
364
365