Completed
Pull Request — master (#242)
by Marek
02:10
created

AIs.ai.AI._get_researchable()   B

Complexity

Conditions 8

Size

Total Lines 18
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 16
nop 1
dl 0
loc 18
rs 7.3333
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 copy
21
import random
22
from ige import log
23
from ige.ospace import Const
24
from ige.ospace import Rules
25
from ige.ospace import Utils
26
27
import ai_tools as tool
28
29
class AI(object):
30
    def __init__(self, client, enemyTypes=None):
31
        if enemyTypes is None:
32
            enemyTypes = []
33
        self.client = client
34
        self.db = client.db
35
        self.player = client.getPlayer()
36
37
        self.data = tool.tool_parseDB(self.client, self.db, enemyTypes)
38
39
    def economy_manager(self):
40
        raise NotImplementedError
41
42
    def defense_manager(self):
43
        raise NotImplementedError
44
45
    def offense_manager(self):
46
        raise NotImplementedError
47
48
    def _filter_res_requirements(self, techs):
49
        for tech_id in copy.copy(techs):
50
            tech = self.client.getTechInfo(tech_id)
51
            failed_requirements = False
52
            for req_tech, req_tech_improv in tech.researchRequires:
53
                if req_tech not in self.player.techs or self.player.techs[req_tech] < req_tech_improv:
54
                    failed_requirements = True
55
                    break
56
            if failed_requirements:
57
                techs.remove(tech_id)
58
59
    def _get_researchable(self):
60
        researchable = set()
61
        # already available
62
        for tech_id in self.player.techs.keys():
63
            tech = self.client.getTechInfo(tech_id)
64
            improvement = self.player.techs[tech_id]
65
            if improvement < Rules.techMaxImprovement and\
66
                    improvement < tech.maxImprovement:
67
                researchable.add(tech_id)
68
        # new tech
69
        for tech_id in self.client.getAllTechIDs():
70
            tech = self.client.getTechInfo(tech_id)
71
            if not hasattr(tech, "partialData") or not hasattr(tech, 'researchMod'):
72
                continue
73
        for task in self.player.rsrchQueue:
74
            researchable -= set([task.techID])
75
        self._filter_res_requirements(researchable)
76
        return researchable
77
78
    def research_manager(self, weighted_techs):
79
        """ weighted_techs is dictionary, with weight:list_of_techs pairs
80
        """
81
        if len(self.player.rsrchQueue) >= 2:
82
            return
83
        researchable = self._get_researchable()
84
        weights = []
85
        tech_choices = []
86
        for weight, techs in weighted_techs.iteritems():
87
            for tech in researchable.intersection(techs):
88
                weights.append(weight)
89
                tech_choices.append(tech)
90
        if sum(weights):
91
            tech = Utils.weightedRandom(tech_choices, weights)
92
            self.player.rsrchQueue = self.client.cmdProxy.startResearch(self.player.oid, tech)
93
94
    def _find_best_planet(self, planet_ids):
95
        # right now, it's simply the largest one
96
        max_slots = 0
97
        largest_planet_id = None
98
        for planet_id in planet_ids:
99
            planet = self.db[planet_id]
100
            if max_slots < planet.plSlots:
101
                max_slots = planet.plSlots
102
                largest_planet_id = planet_id
103
        return largest_planet_id
104
105
    def _explore(self, explorer_design_id):
106
        should_repeat = False
107
        explorer_fleets = self.data.myFleetsWithDesign.get(explorer_design_id, set())
108
        for fleet_id in copy.copy(explorer_fleets & self.data.idleFleets):
109
            max_range = tool.subfleetMaxRange(self.client, self.db, {explorer_design_id:1}, fleet_id)
110
            nearest = tool.findNearest(self.db, self.db[fleet_id], self.data.unknownSystems, max_range)
111
            if len(nearest) > 0:
112
                system_id = nearest[0]
113
                # send the fleet
114
                fleet, new_fleet, my_fleets = tool.orderPartFleet(self.client, self.db,
115
                    {explorer_design_id:1}, True, fleet_id, Const.FLACTION_MOVE, system_id, None)
116
                self.data.myFleetSheets[fleet_id][explorer_design_id] -= 1
117
                if self.data.myFleetSheets[fleet_id][explorer_design_id] == 0:
118
                    del self.data.myFleetSheets[fleet_id][explorer_design_id]
119
                    explorer_fleets.remove(fleet_id)
120
                else:
121
                    should_repeat = True
122
                self.data.unknownSystems.remove(system_id)
123
        return should_repeat
124
125
    def _colonize_free_systems(self, valid_systems, colony_design_id):
126
        should_repeat = False
127
        colony_fleets = self.data.myFleetsWithDesign.get(colony_design_id, set())
128
        for fleet_id in copy.copy(colony_fleets & self.data.idleFleets):
129
            max_range = tool.subfleetMaxRange(self.client, self.db, {colony_design_id:1}, fleet_id)
130
            nearest = tool.findNearest(self.db, self.db[fleet_id], valid_systems, max_range)
131
            if len(nearest) > 0:
132
                system_id = nearest[0]
133
                system = self.db[system_id]
134
                target_id = self._find_best_planet(system.planets)
135
                fleet, new_fleet, my_fleets = tool.orderPartFleet(self.client, self.db,
136
                    {colony_design_id:1}, True, fleet_id, Const.FLACTION_DEPLOY, target_id, colony_design_id)
137
                self.data.myFleetSheets[fleet_id][colony_design_id] -= 1
138
                if self.data.myFleetSheets[fleet_id][colony_design_id] == 0:
139
                    del self.data.myFleetSheets[fleet_id][colony_design_id]
140
                    colony_fleets.remove(fleet_id)
141
                else:
142
                    should_repeat = True
143
                self.data.freeSystems.remove(system_id)
144
                valid_systems.remove(system_id)
145
        return should_repeat
146
147
    def diplomacy_manager(self, friendly_types = None, pacts = None):
148
        if friendly_types is None:
149
            return
150
        if pacts is None:
151
            return
152
        for contact_id in self.player.diplomacyRels:
153
            contact = self.client.get(contact_id, publicOnly = True)
154
            if contact.type not in friendly_types:
155
                continue
156
            dipl = self.client.getDiplomacyWith(contact_id)
157
            for pact_id in pacts:
158
                pactSpec = Rules.pactDescrs[pact_id]
159
                if dipl.relation < pactSpec.validityInterval[0] or dipl.relation > pactSpec.validityInterval[1]:
160
                    # not friendly enough
161
                    continue
162
                if pact_id in dipl.pacts and dipl.pacts[pact_id][0] in [Const.PACT_ACTIVE, Const.PACT_INACTIVE]:
163
                    # nothing more to do, move along
164
                    continue
165
                # hey, we should enable this pact!
166
                conditions = [pact_id]
167
                self.player.diplomacyRels = self.client.cmdProxy.changePactCond(self.player.oid, contact_id, pact_id, Const.PACT_INACTIVE, conditions)
168
169
    def run(self):
170
        self.economy_manager()
171
        self.defense_manager()
172
        self.offense_manager()
173
        self.research_manager()
174
        self.diplomacy_manager()
175
176