Passed
Push — master ( 3da424...a0c806 )
by Marek
01:50
created

AIs.ais_pirate.Pirate.fleets_manager()   C

Complexity

Conditions 9

Size

Total Lines 20
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 17
nop 1
dl 0
loc 20
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 ai 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
                    break
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
                    break
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
            planet = self.db[planet_id]
114
            if system_info.dens[planet_id] < 1 or possiblePrisons < 1:
115
                # there was a Den, but it is not there anymore, try another planet
116
                continue
117
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
118
                Rules.Tech.PIRATEPRISON, 1, planet_id,
119
                Rules.Tech.PIRATEBASE < 1000, 0, Rules.Tech.PIRATEDEN)
120
            system_info.idle_planets.remove(planet_id)
121
            system_info.dens[planet_id] -= 1
122
            possiblePrisons -= 1
123
124
    def _build_shipyard(self, system_info):
125
        slots = 0
126
        for planet_id in system_info.system.planets:
127
            slots += self.db[planet_id].plSlots
128
        if slots <= 10 or system_info.shipyards:
129
            return
130
        for planet_id in copy.copy(system_info.idle_planets) & set(system_info.dens.keys()):
131
            planet = self.db[planet_id]
132
            if system_info.dens[planet_id] < 1:
133
                # there was a Den, but it is not there anymore, try another planet
134
                continue
135
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
136
                Rules.Tech.PIRATESD, 1, planet_id,
137
                Rules.Tech.PIRATEBASE < 1000, 0, Rules.Tech.PIRATEDEN)
138
            system_info.idle_planets.remove(planet_id)
139
            system_info.dens[planet_id] -= 1
140
            system_info.shipyards = 1
141
            return
142
143
    def _expand_slots(self, system_info):
144
        if Rules.Tech.ADDSLOT3 not in self.player.techs:
145
            return
146
        for planet_id in copy.copy(system_info.idle_planets):
147
            planet = self.db[planet_id]
148
            if planet.plSlots < planet.plMaxSlots:
149
                planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
150
                    Rules.Tech.ADDSLOT3, 1, planet_id,
151
                    Rules.Tech.ADDSLOT3 < 1000, 0, Const.OID_NONE)
152
                planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id,
153
                    Rules.Tech.PIRATEDEN, 1, planet_id,
154
                    Rules.Tech.PIRATEDEN < 1000, 0, Const.OID_NONE)
155
                system_info.idle_planets.remove(planet_id)
156
157
    def _condensePlanet(self, planet, target):
158
        if Rules.Tech.PLCOND5 in self.player.techs:
159
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
160
                Rules.Tech.PLCOND5, 1, target.oid,
161
                Rules.Tech.PLCOND5 < 1000, 0, Const.OID_NONE)
162
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
163
                Rules.Tech.PIRATEBASE, 1, target.oid,
164
                Rules.Tech.PIRATEBASE < 1000, 0, Const.OID_NONE)
165
            self.data.nonhabPlanets.remove(target.oid)
166
167
    def _assemblePlanet(self, planet, target):
168
        if Rules.Tech.PLASSEMBL5 in self.player.techs:
169
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
170
                Rules.Tech.PLASSEMBL5, 1, target.oid,
171
                Rules.Tech.PLASSEMBL5 < 1000, 0, Const.OID_NONE)
172
            planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet.oid,
173
                Rules.Tech.PIRATEBASE, 1, target.oid,
174
                Rules.Tech.PIRATEBASE < 1000, 0, Const.OID_NONE)
175
            self.data.nonhabPlanets.remove(target.oid)
176
177
    def _create_planets(self, system_info):
178
        for target_id in copy.copy(self.data.nonhabPlanets & set(system_info.system.planets)):
179
            target = self.db[target_id]
180
            try:
181
                planet_id = system_info.idle_planets.pop()
182
            except KeyError:
183
                return
184
            planet = self.db[planet_id]
185
            if target.plType == u'G':
186
                self._condensePlanet(planet, target)
187
            elif target.plType == u'A':
188
                self._assemblePlanet(planet, target)
189
190
    def _build_ships(self, system_info):
191
        system_fleets = getattr(system_info.system, 'fleets', [])
192
        has_scout = False
193
        for fleet_id in system_fleets:
194
            fleet = self.db[fleet_id]
195
            if getattr(fleet, 'owner', Const.OID_NONE) == self.player.oid:
196
                if tool.fleetContains(fleet, {4:1}):
197
                    has_scout = True
198
        for planet_id in system_info.idle_planets:
199
            planet = self.db[planet_id]
200
            if not has_scout:
201
                planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 4, 1, planet_id, True, False, Const.OID_NONE)
202
            else:
203
                dice = random.randint(1, 3)
204
                if dice == 1:
205
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 1, 3, planet_id, True, False, Const.OID_NONE)
206
                elif dice == 2:
207
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 2, 3, planet_id, True, False, Const.OID_NONE)
208
                else:
209
                    planet.prodQueue, self.player.stratRes = self.client.cmdProxy.startConstruction(planet_id, 3, 2, planet_id, True, False, Const.OID_NONE)
210
211
    def _get_system_info(self, system):
212
        system_info = IDataHolder()
213
        # my planets in the system
214
        system_info.system = system
215
        system_info.breweries = 0
216
        system_info.shipyards = 0
217
        system_info.prisons = 0
218
        system_info.dens = {}
219
        system_info.bases = {}
220
        system_info.other_struct_id = {}
221
        system_info.idle_planets = self.data.myPlanets & set(system.planets)
222
        for planet_id in copy.copy(system_info.idle_planets):
223
            planet = self.db[planet_id]
224
            system_info.bases[planet_id] = 0
225
            system_info.other_struct_id[planet_id] = None
226
            system_info.dens[planet_id] = 0
227
            for struct in planet.slots:
228
                if struct[0] == Rules.Tech.PIRATEBASE:
229
                    system_info.bases[planet_id] += 1
230
                elif struct[0] == Rules.Tech.PIRATEBREWERY:
231
                    system_info.breweries += 1
232
                elif struct[0] == Rules.Tech.PIRATEDEN:
233
                    system_info.dens[planet_id] += 1
234
                elif struct[0] == Rules.Tech.PIRATESD:
235
                    system_info.shipyards += 1
236
                elif struct[0] == Rules.Tech.PIRATEPRISON:
237
                    system_info.prisons += 1
238
                else:
239
                    system_info.other_struct_id[planet_id] = struct[0]
240
            if getattr(planet, 'prodQueue', None):
241
                # something is in the production queue, account it and do next
242
                for task in planet.prodQueue:
243
                    if task.techID == Rules.Tech.PIRATEBREWERY:
244
                        system_info.breweries += task.quantity
245
                    elif task.techID == Rules.Tech.PIRATESD:
246
                        system_info.shipyards += task.quantity
247
                    elif task.techID == Rules.Tech.PIRATEPRISON:
248
                        system_info.prisons += task.quantity
249
                    elif task.techID in (Rules.Tech.PLCOND5, Rules.Tech.PLASSEMBL5):
250
                        self.data.nonhabPlanets.remove(task.targetID)
251
                system_info.idle_planets.remove(planet_id)
252
                continue
253
        return system_info
254
255
    def _retreat_fleet(self, fleet):
256
        max_range = tool.subfleetMaxRange(self.client, self.db, None, fleet.oid)
257
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.mySystems)
258
        if len(nearest_sys_ids):
259
            nearest_sys_id = nearest_sys_ids[0]
260
            target = self.db[nearest_sys_id]
261
            distance = math.hypot(target.x-fleet.x, target.y-fleet.y)
262
            if distance >= max_range:
263
                tool.orderFleet(self.client, self.db, fleet.oid, Const.FLACTION_MOVE, nearest_sys_id, None)
264
265
    def _return_fleet(self, fleet):
266
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.mySystems)
267
        if len(nearest_sys_ids):
268
            nearest_sys_id = nearest_sys_ids[0]
269
            tool.orderFleet(self.client, self.db, fleet.oid, Const.FLACTION_MOVE, nearest_sys_id, None)
270
271
    def _commence_attack(self, fleet):
272
        sheet = tool.getFleetSheet(fleet)
273
        ships = {}
274
        ships[3] = min(sheet[1], sheet[2], sheet[3])
275
        ships[1] = ships[2] = ships[3]
276
        ships[4] = 1
277
        max_range = tool.subfleetMaxRange(self.client, self.db, ships, fleet.oid)
278
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.otherSystems & self.data.relevantSystems, max_range * 0.45)
279
        if len(nearest_sys_ids):
280
            nearestSys = nearest_sys_ids[0]
281
            tool.orderPartFleet(self.client, self.db, ships, False, fleet.oid, Const.FLACTION_MOVE, nearestSys, None)
282
283
    def _followup_attack(self, fleet):
284
        max_range = tool.subfleetMaxRange(self.client, self.db, None, fleet.oid)
285
        nearest_sys_ids = tool.findNearest(self.db, fleet, self.data.otherSystems & self.data.relevantSystems, max_range)
286
        if len(nearest_sys_ids):
287
            nearest_sys_id = nearest_sys_ids[0]
288
            tool.orderFleet(self.client, self.db, fleet.oid, Const.FLACTION_MOVE, nearest_sys_id, None)
289
        else:
290
            self._return_fleet(fleet)
291
292
    def fleets_manager(self):
293
        attack_fleets = set()
294
        attack_minimum = {1:10, 2:10, 3:10, 4:1}
295
        for fleet_id in self.data.myFleets & self.data.idleFleets:
296
            fleet = self.db.get(fleet_id, None)
297
            if fleet.combatCounter and fleet.orbiting not in self.data.mySystems:
298
                self._retreat_fleet(fleet)
299
            elif not fleet.orbiting in self.data.mySystems:
300
                if tool.fleetContains(fleet, attack_minimum):
301
                    attack_fleets.add(fleet)
302
                else:
303
                    self._return_fleet(fleet)
304
            else:
305
                if tool.fleetContains(fleet, attack_minimum):
306
                    attack_fleets.add(fleet)
307
        for fleet in attack_fleets:
308
            if fleet.orbiting in self.data.mySystems:
309
                self._commence_attack(fleet)
310
            else:
311
                self._followup_attack(fleet)
312
313
    def planet_manager(self):
314
        for planet_id in self.data.myPlanets:
315
            tool.sortStructures(self.client, self.db, planet_id)
316
        for systemID in self.data.mySystems:
317
            system = self.db[systemID]
318
            system_info = self._get_system_info(system)
319
            self._fill_with_dens(system_info)
320
            self._build_defensive_bases(system_info)
321
            self._build_bases(system_info)
322
            self._build_brewery(system_info)
323
            self._build_prisons(system_info)
324
            self._build_shipyard(system_info)
325
            self._expand_slots(system_info)
326
            self._create_planets(system_info)
327
            self._build_ships(system_info)
328
329
    def ship_design_manager(self):
330
        if len(self.player.shipDesigns.keys()) < 4:
331
            self.client.cmdProxy.addShipDesign(self.player.oid, 'Brawler',
332
                    Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
333
                    Rules.Tech.CANNON1:2, Rules.Tech.PIRATEFTLENG:3})
334
            self.client.cmdProxy.addShipDesign(self.player.oid, "Enforcer",
335
                    Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
336
                    Rules.Tech.SSROCKET2:2, Rules.Tech.PIRATEFTLENG:3})
337
            self.client.cmdProxy.addShipDesign(self.player.oid, "Raider",
338
                     Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
339
                     Rules.Tech.CONBOMB1:1, Rules.Tech.PIRATEFTLENG:3})
340
            self.client.cmdProxy.addShipDesign(self.player.oid, "Squeal",
341
                    Rules.Tech.SMALLHULL1, {Rules.Tech.SCOCKPIT1:1,
342
                    Rules.Tech.SCANNERMOD1:1, Rules.Tech.PIRATEFTLENG:3})
343
344
    def offense_manager(self):
345
        self.ship_design_manager()
346
        self.fleets_manager()
347
348
    def economy_manager(self):
349
        self.planet_manager()
350
351
    def run(self):
352
        self.offense_manager()
353
        self.economy_manager()
354
355
def run(aclient):
356
    ai = Pirate(aclient)
357
    ai.run()
358
    aclient.saveDB()
359
360
361
362
363
364