1 // Copyright (C) 2004 John Farrell
2 // Copyright (C) 2004, 2005, 2006 Ulf Lorenz
3 // Copyright (C) 2006 Andrea Paternesi
4 // Copyright (C) 2009, 2014 Ben Asselstine
5 //
6 // This program 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 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program 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 Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 // 02110-1301, USA.
20
21 #include <iostream>
22 #include "AI_Analysis.h"
23 #include "citylist.h"
24 #include "Threatlist.h"
25 #include "Threat.h"
26 #include "playerlist.h"
27 #include "stackreflist.h"
28 #include "stacklist.h"
29 #include "ruinlist.h"
30 #include "army.h"
31 #include "city.h"
32 #include "AICityInfo.h"
33 #include "armysetlist.h"
34
35 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::flush<<std::endl;}
36 #define debug(x)
37
38 //this instance is just needed in case one of the observed stacks dies during
39 //the analysis (and the following actions).
40 AI_Analysis* AI_Analysis::instance = 0;
41
AI_Analysis(Player * owner)42 AI_Analysis::AI_Analysis(Player *owner)
43 :d_owner(owner)
44 {
45 d_stacks = 0;
46 d_threats = new Threatlist();
47 d_stacks = new StackReflist(owner->getStacklist());
48
49 examineCities();
50 examineRuins();
51 examineStacks();
52 calculateDanger();
53
54 instance = this;
55 }
56
~AI_Analysis()57 AI_Analysis::~AI_Analysis()
58 {
59 instance = 0;
60
61 delete d_threats;
62 delete d_stacks;
63
64 while (!d_cityInfo.empty())
65 {
66 delete (*d_cityInfo.begin()).second;
67 d_cityInfo.erase(d_cityInfo.begin());
68 }
69 }
70
deleteStack(guint32 id)71 void AI_Analysis::deleteStack(guint32 id)
72 {
73 if (instance)
74 {
75 instance->d_threats->deleteStack(id);
76 instance->d_stacks->removeStack(id);
77 }
78 }
79
deleteStack(Stack * s)80 void AI_Analysis::deleteStack(Stack* s)
81 {
82 if (instance)
83 {
84 debug("delete stack from ai_analysis")
85 instance->d_threats->deleteStack(s->getId());
86 instance->d_stacks->removeStack(s->getId());
87 debug("stack " << s << " died")
88 }
89 }
90
assessArmyStrength(const Army * army)91 float AI_Analysis::assessArmyStrength(const Army *army)
92 {
93 return (float)army->getStat(Army::STRENGTH);
94 }
95
assessStackStrength(const Stack * stack)96 float AI_Analysis::assessStackStrength(const Stack *stack)
97 {
98 if (!instance)
99 return stack->size() * 5.0;
100
101 if (stack->getOwner() == instance->d_owner)
102 {
103 // our stack, so we can look inside it
104 float total = 0.0;
105 for (Stack::const_iterator it = stack->begin(); it != stack->end(); it++)
106 total += assessArmyStrength(*it);
107
108
109 return total;
110 }
111 else
112 {
113 // enemy stack, no cheating!
114 // if we were smarter, we would remember all stacks we had seen before and return a better number here.
115 // We don't assume a too high average strength
116 guint32 as = stack->getOwner()->getArmyset();
117 guint32 type_id = stack->getStrongestArmy()->getTypeId();
118 ArmyProto *strongest = Armysetlist::getInstance()->getArmy(as, type_id);
119 //if the strongest army has a strength of 4 or less,
120 //we assume that all army units in the stack have the same strength.
121 if (strongest->getStrength() < 5)
122 return stack->size() * strongest->getStrength();
123 //otherwise we round everything down to an average of 5 strength.
124 return stack->size() * 5.0;
125 }
126 }
127
getThreatsInOrder()128 const Threatlist* AI_Analysis::getThreatsInOrder()
129 {
130 d_threats->sortByValue();
131 return d_threats;
132 }
133
getThreatsInOrder(Vector<int> pos)134 const Threatlist* AI_Analysis::getThreatsInOrder(Vector<int> pos)
135 {
136 d_threats->sortByDistance(pos);
137 return d_threats;
138 }
139
140
getCityWorstDangers(float dangers[3])141 void AI_Analysis::getCityWorstDangers(float dangers[3])
142 {
143 std::map<guint32, AICityInfo *>::iterator it;
144
145
146 // i wanto to have a result array with the first worst dangers
147 for (int i=0;i<3;i++)
148 {
149 float tmp=0.0;
150
151 for (it=d_cityInfo.begin();it!=d_cityInfo.end();it++)
152 {
153 tmp=(*it->second).getDanger();
154
155 if (dangers[i] < tmp)
156 {
157 if (i>0) // If The iteration is not the first we do want to avoid to store
158 // already stored worst dangers
159 {
160 if(tmp < dangers[i-1])
161 dangers[i]=tmp;
162 }
163 else
164 {
165 dangers[i]=tmp; // The first iteration we get the real worst Danger
166 }
167 }
168 }
169 }
170
171 return;
172 }
173
getNumberOfDefendersInCity(City * city)174 int AI_Analysis::getNumberOfDefendersInCity(City *city)
175 {
176 AICityMap::iterator it = d_cityInfo.find(city->getId());
177 if (it == d_cityInfo.end())
178 return 0;
179
180 return (*it).second->getDefenderCount();
181 }
182
getCityDanger(City * city)183 float AI_Analysis::getCityDanger(City *city)
184 {
185 AICityMap::iterator it = d_cityInfo.find(city->getId());
186 // city does not exist in the map
187 if (it == d_cityInfo.end())
188 return 0.0;
189
190 debug("Threats to " << city->getName() << " are " << d_cityInfo[city->getId()]->getThreats()->toString())
191 return (*it).second->getDanger();
192 }
193
reinforce(City * city,Stack * stack,int movesToArrive)194 void AI_Analysis::reinforce(City *city, Stack *stack, int movesToArrive)
195 {
196 AICityMap::iterator it = d_cityInfo.find(city->getId()) ;
197 if (it == d_cityInfo.end())
198 return;
199
200 (*it).second->addReinforcements(assessStackStrength(stack) / (float) movesToArrive);
201 }
202
reinforcementsNeeded(City * city)203 float AI_Analysis::reinforcementsNeeded(City *city)
204 {
205 AICityMap::iterator it = d_cityInfo.find(city->getId());
206 if (it == d_cityInfo.end())
207 return -1000.0;
208
209 return (*it).second->getDanger() - (*it).second->getReinforcements();
210 }
211
examineCities()212 void AI_Analysis::examineCities()
213 {
214 for (auto city: *Citylist::getInstance())
215 if (!city->isFriend(d_owner) && !city->isBurnt())
216 d_threats->push_back(new Threat(city));
217 }
218
examineStacks()219 void AI_Analysis::examineStacks()
220 {
221 // add all enemy stacks to the list of threats
222 for (auto player: *Playerlist::getInstance())
223 {
224 if (player == d_owner)
225 continue;
226
227 Stacklist *sl = player->getStacklist();
228 for (Stacklist::iterator sit = sl->begin(); sit != sl->end(); ++sit)
229 d_threats->addStack(*sit);
230 }
231 }
232
examineRuins()233 void AI_Analysis::examineRuins()
234 {
235 // enable the searching of ruins before you start to regard them as threats
236 /* Ruinlist *ruins = Ruinlist::getInstance();
237 for (Ruinlist::iterator it = ruins->begin(); it != ruins->end(); ++it)
238 {
239 Ruin ruin = *it;
240 if (!ruin->isSearched())
241 {
242 d_threats->push_back(new Threat(ruin));
243 }
244 }
245 */
246 }
247
calculateDanger()248 void AI_Analysis::calculateDanger()
249 {
250 for (auto city: *Citylist::getInstance())
251 {
252 if (city->isFriend(d_owner))
253 {
254 AICityInfo *info = new AICityInfo(city);
255 d_threats->findThreats(info);
256 d_cityInfo[city->getId()] = info;
257 }
258 }
259 }
260
changeOwnership(Player * old_player,Player * new_player)261 void AI_Analysis::changeOwnership (Player * old_player, Player * new_player)
262 {
263 if (instance)
264 instance->d_threats->changeOwnership(old_player, new_player);
265 }
266
267 // End of file
268