Passed
Pull Request — master (#226)
by Marek
01:53
created

AIs.ais_pirate.Pirate.fleets_manager()   C

Complexity

Conditions 9

Size

Total Lines 21
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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