Merge lp:~widelands-dev/widelands/trainingsites_and_teams into lp:widelands
- trainingsites_and_teams
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 7495 |
Proposed branch: | lp:~widelands-dev/widelands/trainingsites_and_teams |
Merge into: | lp:widelands |
Diff against target: |
1798 lines (+748/-292) 13 files modified
src/ai/ai_help_structs.h (+19/-10) src/ai/defaultai.cc (+667/-273) src/ai/defaultai.h (+33/-2) src/logic/trainingsite.cc (+3/-1) src/logic/trainingsite.h (+18/-0) src/notifications/note_ids.h (+1/-0) tribes/atlanteans/fish_breeders_house/conf (+1/-1) tribes/atlanteans/sawmill/conf (+1/-1) tribes/atlanteans/smokery/conf (+1/-1) tribes/atlanteans/spidercloth/conf (+1/-1) tribes/atlanteans/spiderfarm/conf (+1/-1) tribes/atlanteans/weaving-mill/conf (+1/-1) tribes/barbarians/farm/conf (+1/-0) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/trainingsites_and_teams |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email:
|
Commit message
Description of the change
Another bunch of changes to AI
- better management of trainingsites
- better expansion policy (building farms and other internal paramenters)
- better calculation of military strength of soldiers
- better management of rangers
- multiple smaller changes
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
TiborB (tiborb95) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
TiborB (tiborb95) wrote : | # |
I fixed what I wanted (7477). I tried regression tests but "test cancel when port space was reached two ships" failed - obviously the same problem as in the other branch.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
GunChleoc (gunchleoc) wrote : | # |
I have gone through the code and added my usual nitpicking. I haven't gotten around to any testing yet.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
TiborB (tiborb95) wrote : | # |
I will incorporate your comments of course, thanks for review, and I added one answer to your comment in the diff...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
GunChleoc (gunchleoc) wrote : | # |
I have added a comment to your comment ;)
The regression tests now also will go through in trunk (SirVer fixed it) but not here - maybe merging trunk will fix this.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
TiborB (tiborb95) wrote : | # |
I incorporated the comments, I belive all of them, though I was unsure about iterators, there is a lot of places where iterators are used here... Maybe it will be reworked gradually.
regression tests passed OK....
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
GunChleoc (gunchleoc) wrote : | # |
Thanks for fixing - this is good to go.
There are still iterators all over the code from before we switched to C++11. We should get rid of as many as them as possible, because this makes the code easier to read. So, I'm trying to avoid adding new ones. I actually once had a go at removing the iterators from the AI, but I screwed something up, so I need to start from scratch.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
TiborB (tiborb95) wrote : | # |
Thanks, I am glad it is done :)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
GunChleoc (gunchleoc) wrote : | # |
Yes, it took me too long to find the time for a review :(
Preview Diff
1 | === modified file 'src/ai/ai_help_structs.h' |
2 | --- src/ai/ai_help_structs.h 2015-05-05 20:00:21 +0000 |
3 | +++ src/ai/ai_help_structs.h 2015-07-28 19:26:44 +0000 |
4 | @@ -382,6 +382,15 @@ |
5 | std::vector<int16_t> inputs_; |
6 | std::vector<int16_t> outputs_; |
7 | std::vector<Widelands::WareIndex> critical_built_mat_; |
8 | + |
9 | + bool upgrade_substitutes_; |
10 | + |
11 | + // It seems that fish and meat are subsitutes (for trainingsites), so |
12 | + // when testing if a trainingsite is supplied enough |
13 | + // we count the wares together |
14 | + std::unordered_set<Widelands::WareIndex> substitute_inputs_; |
15 | + int32_t substitutes_count_; |
16 | + |
17 | int16_t production_hint_; |
18 | |
19 | int32_t cnt_built_; |
20 | @@ -449,16 +458,16 @@ |
21 | uint8_t preciousness_; |
22 | }; |
23 | |
24 | -//Computer player does not get notification messages about enemy militarysites |
25 | -//and warehouses, so following is collected based on observation |
26 | -//It is conventient to have some information preserved, like nearby minefields, |
27 | -//when it was attacked, whether it is warehouse and so on |
28 | -//Also AI test more such targets when considering attack and calculated score is |
29 | -//is stored in the observer |
30 | +// Computer player does not get notification messages about enemy militarysites |
31 | +// and warehouses, so following is collected based on observation |
32 | +// It is conventient to have some information preserved, like nearby minefields, |
33 | +// when it was attacked, whether it is warehouse and so on |
34 | +// Also AI test more such targets when considering attack and calculated score is |
35 | +// is stored in the observer |
36 | struct EnemySiteObserver { |
37 | bool warehouse_; |
38 | - uint8_t attack_soldiers; |
39 | - uint8_t defenders; |
40 | + int32_t attack_soldiers_strength; |
41 | + int32_t defenders_strength; |
42 | uint8_t stationed_soldiers; |
43 | uint32_t last_time_attackable; |
44 | uint32_t last_tested; |
45 | @@ -468,8 +477,8 @@ |
46 | |
47 | EnemySiteObserver() |
48 | : warehouse_(false), |
49 | - attack_soldiers(0), |
50 | - defenders(0), |
51 | + attack_soldiers_strength(0), |
52 | + defenders_strength(0), |
53 | stationed_soldiers(0), |
54 | last_time_attackable(std::numeric_limits<uint32_t>::max()), |
55 | last_tested(0), |
56 | |
57 | === modified file 'src/ai/defaultai.cc' |
58 | --- src/ai/defaultai.cc 2015-05-07 20:46:32 +0000 |
59 | +++ src/ai/defaultai.cc 2015-07-28 19:26:44 +0000 |
60 | @@ -44,6 +44,7 @@ |
61 | #include "logic/playercommand.h" |
62 | #include "logic/productionsite.h" |
63 | #include "logic/ship.h" |
64 | +#include "logic/soldier.h" |
65 | #include "logic/trainingsite.h" |
66 | #include "logic/tribe.h" |
67 | #include "logic/warehouse.h" |
68 | @@ -62,7 +63,7 @@ |
69 | constexpr int kMinMFCheckInterval = 19 * 1000; |
70 | constexpr int kShipCheckInterval = 5 * 1000; |
71 | constexpr int kMarineDecisionInterval = 20 * 1000; |
72 | -constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000; |
73 | +constexpr int kTrainingSitesCheckInterval = 45 * 1000; |
74 | |
75 | // this is intended for map developers, by default should be off |
76 | constexpr bool kPrintStats = false; |
77 | @@ -90,6 +91,7 @@ |
78 | num_ports(0), |
79 | last_attacked_player_(std::numeric_limits<uint16_t>::max()), |
80 | enemysites_check_delay_(60), |
81 | + wood_policy_(WoodPolicy::kStartRangers), |
82 | next_ai_think_(0), |
83 | next_mine_construction_due_(0), |
84 | inhibit_road_building_(0), |
85 | @@ -112,7 +114,8 @@ |
86 | ts_basic_const_count_(0), |
87 | ts_advanced_count_(0), |
88 | ts_advanced_const_count_(0), |
89 | - ts_without_trainers_(0) { |
90 | + ts_without_trainers_(0), |
91 | + scheduler_delay_counter_(0) { |
92 | |
93 | // Subscribe to NoteFieldPossession. |
94 | field_possession_subscriber_ = |
95 | @@ -152,6 +155,18 @@ |
96 | |
97 | }); |
98 | |
99 | + // Subscribe to TrainingSiteSoldierTrained. |
100 | + soldiertrained_subscriber_ = Notifications::subscribe<NoteTrainingSiteSoldierTrained>( |
101 | + [this](const NoteTrainingSiteSoldierTrained& note) { |
102 | + if (note.ts->owner().player_number() != player_->player_number()) { |
103 | + return; |
104 | + } |
105 | + |
106 | + soldier_trained(*note.ts); |
107 | + |
108 | + }); |
109 | + |
110 | + |
111 | // Subscribe to ShipNotes. |
112 | shipnotes_subscriber_ = |
113 | Notifications::subscribe<NoteShipMessage>([this](const NoteShipMessage& note) { |
114 | @@ -253,8 +268,18 @@ |
115 | if (check_economies()) { // is a must |
116 | return; |
117 | }; |
118 | - taskDue[ScheduleTasks::kRoadCheck] = gametime + 400; |
119 | - improve_roads(gametime); |
120 | + taskDue[ScheduleTasks::kRoadCheck] = gametime + 1000; |
121 | + // testing 5 roads |
122 | + { |
123 | + const int32_t roads_to_check = (roads.size() + 1 < 5) ? roads.size() + 1 : 5; |
124 | + for (int i = 0; i < roads_to_check; i += 1) { |
125 | + // improve_roads function will test one road and rotate roads vector |
126 | + if (improve_roads(gametime)) { |
127 | + // if significant change takes place do not go on |
128 | + break; |
129 | + }; |
130 | + } |
131 | + } |
132 | break; |
133 | case ScheduleTasks::kUnbuildableFCheck : |
134 | taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000; |
135 | @@ -280,8 +305,18 @@ |
136 | if (check_economies()) { // economies must be consistent |
137 | return; |
138 | } |
139 | - check_productionsites(gametime); |
140 | - taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000; |
141 | + { |
142 | + // testing 5 productionsites (if there are 5 of them) |
143 | + int32_t ps_to_check = (productionsites.size() < 5) ? productionsites.size() : 5; |
144 | + for (int i = 0; i < ps_to_check; i += 1) { |
145 | + // one productionsite per one check_productionsites() call |
146 | + if (check_productionsites(gametime)) { |
147 | + // if significant change takes place do not go on |
148 | + break; |
149 | + }; |
150 | + } |
151 | + } |
152 | + taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 15000; |
153 | break; |
154 | case ScheduleTasks::kCheckShips : |
155 | check_ships(gametime); |
156 | @@ -293,8 +328,18 @@ |
157 | if (check_economies()) { // economies must be consistent |
158 | return; |
159 | } |
160 | - taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough |
161 | - check_mines_(gametime); |
162 | + taskDue[ScheduleTasks::kCheckMines] = gametime + 15000; |
163 | + // checking 3 mines if possible |
164 | + { |
165 | + int32_t mines_to_check = (mines_.size() < 5) ? mines_.size() : 5; |
166 | + for (int i = 0; i < mines_to_check; i += 1) { |
167 | + // every run of check_mines_() checks one mine |
168 | + if (check_mines_(gametime)) { |
169 | + // if significant change takes place do not go on |
170 | + break; |
171 | + }; |
172 | + } |
173 | + } |
174 | break; |
175 | case ScheduleTasks::kCheckMilitarysites : |
176 | check_militarysites(gametime); |
177 | @@ -304,7 +349,7 @@ |
178 | break; |
179 | case ScheduleTasks::kCountMilitaryVacant : |
180 | count_military_vacant_positions(); |
181 | - taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000; |
182 | + taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 60 * 1000; |
183 | break; |
184 | case ScheduleTasks::kWareReview : |
185 | if (check_economies()) { // economies must be consistent |
186 | @@ -391,6 +436,7 @@ |
187 | bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds |
188 | bo.is_port_ = bld.get_isport(); |
189 | bo.trainingsite_type_ = TrainingSiteType::kNoTS; |
190 | + bo.upgrade_substitutes_ = false; |
191 | |
192 | if (bh.renews_map_resource()) { |
193 | bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource()); |
194 | @@ -448,6 +494,38 @@ |
195 | bo.is_shipyard_ = false; |
196 | } |
197 | |
198 | + // now we find out if the upgrade of the building is a full substitution |
199 | + // (produces all wares as current one) |
200 | + const BuildingIndex enhancement = bld.enhancement(); |
201 | + if (enhancement != INVALID_INDEX && bo.type == BuildingObserver::PRODUCTIONSITE) { |
202 | + std::unordered_set<WareIndex> enh_outputs; |
203 | + const ProductionSiteDescr& enh_prod |
204 | + = |
205 | + dynamic_cast<const ProductionSiteDescr&>(*tribe_->get_building_descr(enhancement)); |
206 | + |
207 | + // collecting wares that are produced in enhanced building |
208 | + for (const WareIndex& ware : enh_prod.output_ware_types()) { |
209 | + enh_outputs.insert(ware); |
210 | + } |
211 | + // now testing outputs of current building |
212 | + // and comparing |
213 | + bo.upgrade_substitutes_ = true; |
214 | + for (WareIndex ware : bo.outputs_) { |
215 | + if (enh_outputs.count(ware) == 0) { |
216 | + bo.upgrade_substitutes_ = false; |
217 | + break; |
218 | + } |
219 | + } |
220 | + } |
221 | + |
222 | + // plus some manually picked buildings, |
223 | + // see preferred_upgrade list |
224 | + for (const char* pb : preferred_upgrade) { |
225 | + if (strcmp(bld.name().c_str(), pb) == 0) { |
226 | + bo.upgrade_substitutes_ = true; |
227 | + } |
228 | + } |
229 | + |
230 | continue; |
231 | } |
232 | |
233 | @@ -482,7 +560,16 @@ |
234 | const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld); |
235 | for (const WareAmount& temp_input : train.inputs()) { |
236 | bo.inputs_.push_back(temp_input.first); |
237 | + |
238 | + // collecting subsitutes |
239 | + if (tribe_->ware_index("meat") == temp_input.first || |
240 | + tribe_->ware_index("fish") == temp_input.first || |
241 | + tribe_->ware_index("smoked_meat") == temp_input.first || |
242 | + tribe_->ware_index("smoked_fish") == temp_input.first) { |
243 | + bo.substitute_inputs_.insert(temp_input.first); |
244 | + } |
245 | } |
246 | + |
247 | bo.trainingsite_type_ = bh.get_trainingsite_type(); |
248 | // it would behave badly if no type was set |
249 | // make sure all TS have its type set properly in conf files |
250 | @@ -496,6 +583,8 @@ |
251 | } |
252 | } |
253 | |
254 | + |
255 | + |
256 | // atlanteans they consider water as a resource |
257 | // (together with mines, stones and wood) |
258 | if (tribe_->name() == "atlanteans") { |
259 | @@ -592,7 +681,7 @@ |
260 | taskDue[ScheduleTasks::kCheckShips] = 30 * 1000; |
261 | taskDue[ScheduleTasks::kCheckEconomies] = 1000; |
262 | taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000; |
263 | - taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000; |
264 | + taskDue[ScheduleTasks::kCheckTrainingsites] = 2 * 60 * 1000; |
265 | taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000; |
266 | taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000; |
267 | taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000; |
268 | @@ -870,7 +959,6 @@ |
269 | if (player_->is_hostile(player_immovable->owner())) { |
270 | field.enemy_nearby_ = true; |
271 | } |
272 | - enemy_last_seen_ = gametime; |
273 | |
274 | continue; |
275 | } |
276 | @@ -1132,23 +1220,23 @@ |
277 | // scores every combination and one with highest and positive score |
278 | // is built. |
279 | // * Buildings are split into categories |
280 | -// * The logic is complex but aproximatelly: |
281 | -// - buildings producing building material are preffered |
282 | -// - buildings identified as basic are preffered |
283 | -// - first bulding of a type is preffered |
284 | -// - buildings identified as 'direct food supplier' as built after 15 min. |
285 | +// * The logic is complex but approximately: |
286 | +// - buildings producing building material are preferred |
287 | +// - buildings identified as basic are preferred |
288 | +// - first bulding of a type is preferred |
289 | +// - buildings identified as 'direct food supplier' are built after 15 min. |
290 | // from game start |
291 | -// - if a bulding is upgradeable, second building is also preffered |
292 | +// - if a building is upgradeable, second building is also preferred |
293 | // (there should be no upgrade when there are not two buildings of the same type) |
294 | -// - algorigthm is trying to take into account actual utlization of buildings |
295 | +// - algorithm is trying to take into account actual utlization of buildings |
296 | // (the one shown in GUI/game is not reliable, it calculates own statistics) |
297 | // * military buildings have own strategy, split into two situations: |
298 | // - there is no enemy |
299 | // - there is an enemy |
300 | -// Currently more military buildings are built then needed |
301 | -// and "optimalization" (dismantling not needed buildings) is done afterwards |
302 | +// Currently more military buildings are built than needed |
303 | +// and "optimization" (dismantling not needed buildings) is done afterwards |
304 | bool DefaultAI::construct_building(uint32_t gametime) { |
305 | - // Just used for easy checking whether a mine or something else was built. |
306 | + // Just used for easy checking whether a mine or something else was built. |
307 | bool mine = false; |
308 | bool field_blocked = false; |
309 | uint32_t consumers_nearby_count = 0; |
310 | @@ -1172,6 +1260,21 @@ |
311 | new_buildings_stop_ = false; |
312 | MilitaryStrategy expansion_mode = MilitaryStrategy::kResourcesOrDefense; |
313 | |
314 | + // helper variable - we need some proportion of free spots vs productionsites |
315 | + // the proportion depends on size of economy |
316 | + // this proportion defines how dense the buildings will be |
317 | + // it is degressive (allows high density on the beginning) |
318 | + int32_t needed_spots = 0; |
319 | + if (productionsites.size() < 50) { |
320 | + needed_spots = productionsites.size(); |
321 | + } else if (productionsites.size() < 100) { |
322 | + needed_spots = 50 + (productionsites.size() - 50) * 5; |
323 | + } else if (productionsites.size() < 200) { |
324 | + needed_spots = 300 + (productionsites.size() - 100) * 10; |
325 | + } else { |
326 | + needed_spots = 1300 + (productionsites.size() - 200) * 20; |
327 | + } |
328 | + |
329 | // there are many reasons why to stop building production buildings |
330 | // (note there are numerous exceptions) |
331 | // 1. to not have too many constructionsites |
332 | @@ -1179,12 +1282,12 @@ |
333 | new_buildings_stop_ = true; |
334 | } |
335 | // 2. to not exhaust all free spots |
336 | - if (spots_ * 3 / 2 + 5 < static_cast<int32_t>(productionsites.size())) { |
337 | + if (spots_ < needed_spots) { |
338 | new_buildings_stop_ = true; |
339 | } |
340 | // 3. too keep some proportions production sites vs military sites |
341 | if ((num_prod_constructionsites + productionsites.size()) > |
342 | - (num_milit_constructionsites + militarysites.size()) * 3) { |
343 | + (num_milit_constructionsites + militarysites.size()) * 5) { |
344 | new_buildings_stop_ = true; |
345 | } |
346 | // 4. if we do not have 3 mines at least |
347 | @@ -1192,26 +1295,26 @@ |
348 | new_buildings_stop_ = true; |
349 | } |
350 | // BUT if enemy is nearby, we cancel above stop |
351 | - if (new_buildings_stop_ && enemy_last_seen_ + 2 * 60 * 1000 > gametime) { |
352 | + if (new_buildings_stop_ && enemy_last_seen_ + 10 * 60 * 1000 > gametime) { |
353 | new_buildings_stop_ = false; |
354 | } |
355 | |
356 | - // sometimes there is too many military buildings in construction, so we must |
357 | - // prevent initialization of further buildings start |
358 | - const int32_t vacant_plus_in_construction_minus_prod = |
359 | - vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7; |
360 | - if (vacant_plus_in_construction_minus_prod > 20) { |
361 | - expansion_mode = MilitaryStrategy::kNoNewMilitary; |
362 | - } else if (vacant_plus_in_construction_minus_prod > 13) { |
363 | - expansion_mode = MilitaryStrategy::kDefenseOnly; |
364 | - } else if (vacant_plus_in_construction_minus_prod > 6) { |
365 | - expansion_mode = MilitaryStrategy::kResourcesOrDefense; |
366 | + // we must calculate wood policy |
367 | + const WareIndex wood_index = tribe_->safe_ware_index("log"); |
368 | + // stocked wood is to be in some propotion to productionsites and |
369 | + // constructionsites (this proportion is bit artifical, or we can say |
370 | + // it is proportion to the size of economy). Plus some positive 'margin' |
371 | + const int32_t stocked_wood_margin = get_warehoused_stock(wood_index) - |
372 | + productionsites.size() * 2 - |
373 | + num_prod_constructionsites; |
374 | + if (stocked_wood_margin > 80) { |
375 | + wood_policy_ = WoodPolicy::kDismantleRangers; |
376 | + } else if (stocked_wood_margin > 25) { |
377 | + wood_policy_ = WoodPolicy::kStopRangers; |
378 | + } else if (stocked_wood_margin > 10) { |
379 | + wood_policy_ = WoodPolicy::kStartRangers; |
380 | } else { |
381 | - if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) { |
382 | - expansion_mode = MilitaryStrategy::kExpansion; |
383 | - } else { |
384 | - expansion_mode = MilitaryStrategy::kPushExpansion; |
385 | - } |
386 | + wood_policy_ = WoodPolicy::kBuildRangers; |
387 | } |
388 | |
389 | // we must consider need for mines |
390 | @@ -1243,6 +1346,27 @@ |
391 | } |
392 | } |
393 | |
394 | + // this controls a speed and willingness to expand the teritorry |
395 | + const int32_t vacant_plus_in_construction_minus_prod = |
396 | + vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 7; |
397 | + if (vacant_plus_in_construction_minus_prod > 20) { |
398 | + expansion_mode = MilitaryStrategy::kNoNewMilitary; |
399 | + } else if (vacant_plus_in_construction_minus_prod > 13) { |
400 | + expansion_mode = MilitaryStrategy::kDefenseOnly; |
401 | + } else if (vacant_plus_in_construction_minus_prod > 6) { |
402 | + expansion_mode = MilitaryStrategy::kResourcesOrDefense; |
403 | + } else { |
404 | + // this is intended for initial phase of game when the player still has enough soldiers |
405 | + // but we still want to force it to follow resources instead for plain expansion |
406 | + if (virtual_mines <= 2 && (unstationed_milit_buildings_ + num_milit_constructionsites) > 2) { |
407 | + expansion_mode = MilitaryStrategy::kResourcesOrDefense; |
408 | + } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) { |
409 | + expansion_mode = MilitaryStrategy::kExpansion; |
410 | + } else { |
411 | + expansion_mode = MilitaryStrategy::kPushExpansion; |
412 | + } |
413 | + } |
414 | + |
415 | BuildingObserver* best_building = nullptr; |
416 | int32_t proposed_priority = 0; |
417 | Coords proposed_coords; |
418 | @@ -1559,12 +1683,6 @@ |
419 | continue; |
420 | } |
421 | |
422 | - if (bo.stocklevel_time < game().get_gametime() - 5 * 1000) { |
423 | - bo.stocklevel_ = |
424 | - get_stocklevel_by_hint(static_cast<size_t>(bo.production_hint_)); |
425 | - bo.stocklevel_time = game().get_gametime(); |
426 | - } |
427 | - |
428 | if (bo.total_count() == 0) { |
429 | prio = 200; |
430 | } |
431 | @@ -1572,9 +1690,10 @@ |
432 | continue; |
433 | } |
434 | // we can go above target if there is shortage of logs on stock |
435 | - else if (bo.total_count() >= bo.cnt_target_ && |
436 | - bo.stocklevel_ > 40 + productionsites.size() * 2) { |
437 | - continue; |
438 | + else if (bo.total_count() >= bo.cnt_target_) { |
439 | + if (wood_policy_ != WoodPolicy::kBuildRangers) { |
440 | + continue; |
441 | + } |
442 | } |
443 | |
444 | // considering near trees and producers |
445 | @@ -1625,15 +1744,29 @@ |
446 | // this will depend on number of mines_ and productionsites |
447 | if (static_cast<int32_t>((productionsites.size() + mines_.size()) / 30) > |
448 | bo.total_count() && |
449 | - bo.cnt_under_construction_ == 0) |
450 | - prio = 4 + kDefaultPrioBoost; |
451 | + (bo.cnt_under_construction_ + bo.unoccupied_) == 0 && |
452 | + // but only if current buildings are utilized enough |
453 | + (bo.total_count() == 0 || bo.current_stats_ > 60)) { |
454 | + prio = 4 + kDefaultPrioBoost; |
455 | + } |
456 | } else { // finally normal productionsites |
457 | if (bo.production_hint_ >= 0) { |
458 | continue; |
459 | } |
460 | |
461 | - if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) { |
462 | - continue; |
463 | + // generally we allow 1 building in construction, but if |
464 | + // preciousness of missing ware is >=10 and it is farm-like building |
465 | + // we allow 2 in construction |
466 | + if (max_needed_preciousness >= 10 |
467 | + && bo.inputs_.empty() |
468 | + && gametime > 30 * 60 * 1000) { |
469 | + if ((bo.cnt_under_construction_ + bo.unoccupied_) > 1) { |
470 | + continue; |
471 | + } |
472 | + } else { |
473 | + if ((bo.cnt_under_construction_ + bo.unoccupied_) > 0) { |
474 | + continue; |
475 | + } |
476 | } |
477 | |
478 | if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) { |
479 | @@ -1653,9 +1786,9 @@ |
480 | prio += kDefaultPrioBoost; |
481 | } else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) { |
482 | prio -= kDefaultPrioBoost * (new_buildings_stop_); |
483 | - } else if (new_buildings_stop_) |
484 | + } else if (new_buildings_stop_) { |
485 | continue; |
486 | - |
487 | + } |
488 | // we check separatelly buildings with no inputs and some inputs |
489 | if (bo.inputs_.empty()) { |
490 | |
491 | @@ -1683,7 +1816,6 @@ |
492 | if (!bo.space_consumer_) { |
493 | prio -= bf->producers_nearby_.at(bo.outputs_.at(0)) * 20; |
494 | } // leave some free space between them |
495 | - |
496 | } |
497 | |
498 | else if (bo.is_shipyard_) { |
499 | @@ -1699,12 +1831,9 @@ |
500 | } |
501 | if ((bo.cnt_built_ - bo.unconnected_) > 0 |
502 | && |
503 | - //due to very badly designed statistics and the way how |
504 | - //productionsites are working we must distinguish how many |
505 | - //outputs the site has. |
506 | - ((bo.outputs_.size() == 1 && bo.current_stats_ > 75) |
507 | - || |
508 | - (bo.outputs_.size() > 1 && bo.current_stats_ > 55))) { |
509 | + is_productionsite_needed(bo.outputs_.size(), |
510 | + bo.current_stats_, |
511 | + PerfEvaluation::kForConstruction)) { |
512 | prio += max_needed_preciousness + kDefaultPrioBoost - 3 + |
513 | (bo.current_stats_ - 55) / 8; |
514 | } |
515 | @@ -1714,6 +1843,15 @@ |
516 | continue; |
517 | } |
518 | |
519 | + // bonus for big buildings if shortage of big fields |
520 | + if (spots_avail.at(BUILDCAPS_BIG) <= 5 && bo.desc->get_size() == 3) { |
521 | + prio += 10; |
522 | + } |
523 | + |
524 | + if (spots_avail.at(BUILDCAPS_MEDIUM) <= 5 && bo.desc->get_size() == 2) { |
525 | + prio += 5; |
526 | + } |
527 | + |
528 | //+1 if any consumers_ are nearby |
529 | consumers_nearby_count = 0; |
530 | |
531 | @@ -1724,6 +1862,21 @@ |
532 | prio += 1; |
533 | } |
534 | } |
535 | + |
536 | + // consider borders (for medium + big buildings and ones with input) |
537 | + // =>decreasing the score |
538 | + // but only if we have enough free spots to built on |
539 | + // otherwise it will slow down the expansion - small buildings would be preferred |
540 | + if (spots_avail.at(BUILDCAPS_MEDIUM) > 40 |
541 | + && |
542 | + spots_avail.at(BUILDCAPS_BIG) > 20 |
543 | + && |
544 | + (bo.desc->get_size() == 2 || |
545 | + bo.desc->get_size() == 3 || |
546 | + !bo.inputs_.empty())) { |
547 | + prio = recalc_with_border_range(*bf, prio); |
548 | + } |
549 | + |
550 | } // production sites done |
551 | else if (bo.type == BuildingObserver::MILITARYSITE) { |
552 | |
553 | @@ -1901,115 +2054,62 @@ |
554 | // Militarysites are slightly important as well, to have a bigger |
555 | // chance for a warehouses (containing waiting soldiers or wares |
556 | // needed for soldier training) near the frontier. |
557 | - if ((static_cast<int32_t>(productionsites.size() + mines_.size()) + 20) / 35 > |
558 | - static_cast<int32_t>(numof_warehouses_) && |
559 | - bo.cnt_under_construction_ == 0) { |
560 | - prio = 20; |
561 | - warehouse_needed = true; |
562 | - } |
563 | - |
564 | - // but generally we prefer ports |
565 | - if (bo.is_port_) { |
566 | - prio += 10; |
567 | - } |
568 | - |
569 | - // special boost for first port |
570 | - if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 && |
571 | - !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) { |
572 | - prio += kDefaultPrioBoost + productionsites.size(); |
573 | - warehouse_needed = true; |
574 | - } |
575 | - |
576 | - if (!warehouse_needed) { |
577 | - continue; |
578 | - } |
579 | - |
580 | - // iterating over current warehouses and testing a distance |
581 | - // getting distance to nearest warehouse and adding it to a score |
582 | - uint16_t nearest_distance = std::numeric_limits<uint16_t>::max(); |
583 | - for (const WarehouseSiteObserver& wh_obs : warehousesites) { |
584 | - const uint16_t actual_distance = |
585 | - map.calc_distance(bf->coords, wh_obs.site->get_position()); |
586 | - nearest_distance = std::min(nearest_distance, actual_distance); |
587 | - } |
588 | - // but limit to 15 |
589 | - const uint16_t max_distance_considered = 15; |
590 | - nearest_distance = std::min(nearest_distance, max_distance_considered); |
591 | - prio += nearest_distance; |
592 | - |
593 | - // take care about and enemies |
594 | - if (bf->enemy_nearby_) { |
595 | - prio /= 4; |
596 | - } |
597 | - |
598 | - if (bf->unowned_land_nearby_ && !bo.is_port_) { |
599 | - prio /= 2; |
600 | - } |
601 | - |
602 | - } else if (bo.type == BuildingObserver::WAREHOUSE) { |
603 | - |
604 | - // exclude spots on border |
605 | - if (bf->near_border_ && !bo.is_port_) { |
606 | - continue; |
607 | - } |
608 | - |
609 | - if (!bf->is_portspace_ && bo.is_port_) { |
610 | - continue; |
611 | - } |
612 | - |
613 | - if (bo.cnt_under_construction_ > 0) { |
614 | - continue; |
615 | - } |
616 | - |
617 | - bool warehouse_needed = false; |
618 | - |
619 | - // Build one warehouse for ~every 35 productionsites and mines_. |
620 | - // Militarysites are slightly important as well, to have a bigger |
621 | - // chance for a warehouses (containing waiting soldiers or wares |
622 | - // needed for soldier training) near the frontier. |
623 | - if ((static_cast<int32_t>(productionsites.size() + mines_.size()) + 20) / 35 > |
624 | - static_cast<int32_t>(numof_warehouses_) && |
625 | - bo.cnt_under_construction_ == 0) { |
626 | - prio = 20; |
627 | - warehouse_needed = true; |
628 | - } |
629 | - |
630 | - // but generally we prefer ports |
631 | - if (bo.is_port_) { |
632 | - prio += 10; |
633 | - } |
634 | - |
635 | - // special boost for first port |
636 | - if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 && |
637 | - !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) { |
638 | - prio += kDefaultPrioBoost + productionsites.size(); |
639 | - warehouse_needed = true; |
640 | - } |
641 | - |
642 | - if (!warehouse_needed) { |
643 | - continue; |
644 | - } |
645 | - |
646 | - // iterating over current warehouses and testing a distance |
647 | - // getting distance to nearest warehouse and adding it to a score |
648 | - uint16_t nearest_distance = std::numeric_limits<uint16_t>::max(); |
649 | - for (const WarehouseSiteObserver& wh_obs : warehousesites) { |
650 | - const uint16_t actual_distance = |
651 | - map.calc_distance(bf->coords, wh_obs.site->get_position()); |
652 | - nearest_distance = std::min(nearest_distance, actual_distance); |
653 | - } |
654 | - // but limit to 15 |
655 | - const uint16_t max_distance_considered = 15; |
656 | - nearest_distance = std::min(nearest_distance, max_distance_considered); |
657 | - prio += nearest_distance; |
658 | - |
659 | - // take care about and enemies |
660 | - if (bf->enemy_nearby_) { |
661 | - prio /= 4; |
662 | - } |
663 | - |
664 | - if (bf->unowned_land_nearby_ && !bo.is_port_) { |
665 | - prio /= 2; |
666 | + prio = static_cast<int32_t>(productionsites.size() + mines_.size()) + 20 |
667 | + - |
668 | + 35 * static_cast<int32_t>(numof_warehouses_); |
669 | + if (prio > 0) { |
670 | + warehouse_needed = true; |
671 | + } else { |
672 | + prio = 0; |
673 | + } |
674 | + |
675 | + // But we still can built a port if it is first one |
676 | + if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 && |
677 | + !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) { |
678 | + prio += kDefaultPrioBoost + productionsites.size(); |
679 | + warehouse_needed = true; |
680 | + } |
681 | + |
682 | + if (!warehouse_needed) { |
683 | + continue; |
684 | + } |
685 | + |
686 | + // we prefer ports to a normal warehouse |
687 | + if (bo.is_port_) { |
688 | + prio += 15; |
689 | + } |
690 | + |
691 | + // it is good to have more then 1 warehouse |
692 | + if (numof_warehouses_ == 1) { |
693 | + prio += 10; |
694 | + } |
695 | + |
696 | + // iterating over current warehouses and testing a distance |
697 | + // getting distance to nearest warehouse and adding it to a score |
698 | + uint16_t nearest_distance = std::numeric_limits<uint16_t>::max(); |
699 | + for (const WarehouseSiteObserver& wh_obs : warehousesites) { |
700 | + const uint16_t actual_distance = |
701 | + map.calc_distance(bf->coords, wh_obs.site->get_position()); |
702 | + nearest_distance = std::min(nearest_distance, actual_distance); |
703 | + } |
704 | + // but limit to 30 |
705 | + const uint16_t max_distance_considered = 30; |
706 | + nearest_distance = std::min(nearest_distance, max_distance_considered); |
707 | + prio += nearest_distance - 10; |
708 | + |
709 | + // dont be close to enemies |
710 | + if (bf->enemy_nearby_){ |
711 | + if (bo.is_port_) { |
712 | + prio -= 10; |
713 | + } else { |
714 | + prio -= 40; |
715 | + } |
716 | + } |
717 | + |
718 | + // being too close to a border is not good either |
719 | + if (bf->unowned_land_nearby_ && !bo.is_port_ && prio > 0) { |
720 | + prio /= 2; |
721 | + prio -= 10; |
722 | } |
723 | |
724 | } else if (bo.type == BuildingObserver::TRAININGSITE) { |
725 | @@ -2029,14 +2129,14 @@ |
726 | continue; |
727 | } |
728 | |
729 | - // we build one training site for 100 militarysites |
730 | + // we build one basic training site for 50 militarysites |
731 | if (bo.trainingsite_type_ == TrainingSiteType::kBasic && |
732 | - militarysites.size() / 100 < static_cast<int32_t>(ts_basic_count_)) { |
733 | + militarysites.size() / 50 < static_cast<int32_t>(ts_basic_count_)) { |
734 | continue; |
735 | } |
736 | - // we build one training site for 100 militarysites |
737 | + // we build one advanced training site for 75 militarysites |
738 | if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced && |
739 | - militarysites.size() / 100 < static_cast<int32_t>(ts_advanced_count_)) { |
740 | + militarysites.size() / 75 < static_cast<int32_t>(ts_advanced_count_)) { |
741 | continue; |
742 | } |
743 | |
744 | @@ -2080,9 +2180,9 @@ |
745 | } |
746 | |
747 | // Prefer road side fields |
748 | - prio += bf->preferred_ ? 1 : 0; |
749 | + prio += bf->preferred_ ? 5 : 0; |
750 | // don't waste good land for small huts |
751 | - prio -= (maxsize - bo.desc->get_size()) * 5; |
752 | + prio -= (maxsize - bo.desc->get_size()) * 20; |
753 | |
754 | // prefer vicinity of ports (with exemption of warehouses) |
755 | if (bf->port_nearby_ && bo.type == BuildingObserver::MILITARYSITE) { |
756 | @@ -2148,7 +2248,7 @@ |
757 | 0) { |
758 | nearness_penalty = 0; |
759 | } else { |
760 | - nearness_penalty = 30; |
761 | + nearness_penalty = 40; |
762 | } |
763 | |
764 | // bonus score to prefer if too few mines |
765 | @@ -2338,12 +2438,13 @@ |
766 | } |
767 | |
768 | if (inhibit_road_building_ >= gametime) { |
769 | - return false; |
770 | + return true; |
771 | } |
772 | |
773 | // now we rotate economies and flags to get one flag to go on with |
774 | if (economies.empty()) { |
775 | - return check_economies(); |
776 | + check_economies(); |
777 | + return false; |
778 | } |
779 | |
780 | if (economies.size() >= 2) { // rotating economies |
781 | @@ -2353,7 +2454,8 @@ |
782 | |
783 | EconomyObserver* eco = economies.front(); |
784 | if (eco->flags.empty()) { |
785 | - return check_economies(); |
786 | + check_economies(); |
787 | + return false; |
788 | } |
789 | if (eco->flags.size() > 1) { |
790 | eco->flags.push_back(eco->flags.front()); |
791 | @@ -2389,9 +2491,11 @@ |
792 | } else if (flag.current_wares() > 5) { |
793 | create_shortcut_road(flag, 9, -2, gametime); |
794 | inhibit_road_building_ = gametime + 400; |
795 | + } else { |
796 | + return false; |
797 | } |
798 | |
799 | - return false; |
800 | + return true; |
801 | } |
802 | |
803 | // the function takes a road (road is smallest section of roads with two flags on the ends) |
804 | @@ -2469,6 +2573,27 @@ |
805 | return false; |
806 | } |
807 | |
808 | +// is productionsite needed |
809 | +// used for building new buildings or dismantle of old, intended for ones |
810 | +// that have inputs |
811 | +bool DefaultAI::is_productionsite_needed(int32_t outputs, |
812 | + int32_t performance, |
813 | + PerfEvaluation purpose) { |
814 | + int32_t expected_performance = 0; |
815 | + if (outputs > 0) { |
816 | + expected_performance = 10 + 70 / outputs; |
817 | + } else { |
818 | + expected_performance = 80; |
819 | + } |
820 | + if (purpose == PerfEvaluation::kForDismantle) { |
821 | + expected_performance /= 2; |
822 | + } |
823 | + if (performance > expected_performance) { |
824 | + return true; |
825 | + } |
826 | + return false; |
827 | +} |
828 | + |
829 | // trying to connect the flag to another one, be it from own economy |
830 | // or other economy |
831 | bool DefaultAI::create_shortcut_road(const Flag& flag, |
832 | @@ -2811,7 +2936,6 @@ |
833 | return false; |
834 | } |
835 | |
836 | - bool changed = false; |
837 | // Reorder and set new values; - better now because there are multiple returns in the function |
838 | productionsites.push_back(productionsites.front()); |
839 | productionsites.pop_front(); |
840 | @@ -2850,14 +2974,18 @@ |
841 | // available, one is to be enhanced |
842 | // b) if there are two buildings |
843 | // statistics percents are decisive |
844 | + // c) yet there are buildings that might be upgraded, even when |
845 | + // there is no second buiding of the kind (flag upgrade_substitutes_) |
846 | + |
847 | const BuildingIndex enhancement = site.site->descr().enhancement(); |
848 | if (connected_to_wh && enhancement != INVALID_INDEX && |
849 | - (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) { |
850 | + ((site.bo->cnt_built_ - site.bo->unoccupied_) > 1 || |
851 | + site.bo->upgrade_substitutes_)) { |
852 | |
853 | BuildingIndex enbld = INVALID_INDEX; // to get rid of this |
854 | |
855 | // Only enhance buildings that are allowed (scenario mode) |
856 | - // do not do decisions to fast |
857 | + // do not do decisions too fast |
858 | if (player_->is_building_type_allowed(enhancement)) { |
859 | |
860 | const BuildingDescr& bld = *tribe_->get_building_descr(enhancement); |
861 | @@ -2871,7 +2999,7 @@ |
862 | if (site.site->has_workers(enhancement, game())) { |
863 | |
864 | // forcing first upgrade |
865 | - if (en_bo.cnt_built_ == 0 && !mines_.empty()) { |
866 | + if (en_bo.cnt_built_ == 0) { |
867 | enbld = enhancement; |
868 | bestbld = &en_bo; |
869 | } |
870 | @@ -3079,9 +3207,12 @@ |
871 | } |
872 | |
873 | // buildings with inputs_, checking if we can a dismantle some due to low performance |
874 | - if (!site.bo->inputs_.empty() && (site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 && |
875 | + if (!site.bo->inputs_.empty() && |
876 | + (site.bo->cnt_built_ - site.bo->unoccupied_) >= 3 && |
877 | site.site->can_start_working() && |
878 | - site.site->get_statistics_percent() < 20 && // statistics for the building |
879 | + !is_productionsite_needed(site.bo->outputs_.size(), |
880 | + site.site->get_statistics_percent(), |
881 | + PerfEvaluation::kForDismantle) && |
882 | site.bo->current_stats_ < 30 && // overall statistics |
883 | (game().get_gametime() - site.unoccupied_till_) > 10 * 60 * 1000) { |
884 | |
885 | @@ -3117,16 +3248,8 @@ |
886 | // stop/start them based on stock avaiable |
887 | if (site.bo->production_hint_ >= 0) { |
888 | |
889 | - if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) { |
890 | - site.bo->stocklevel_ = get_stocklevel_by_hint(site.bo->production_hint_); |
891 | - site.bo->stocklevel_time = game().get_gametime(); |
892 | - } |
893 | - |
894 | - // logs can be stored also in productionsites, they are counted as on stock |
895 | - // but are no available for random production site |
896 | - int16_t score = site.bo->stocklevel_ - productionsites.size() * 2; |
897 | - |
898 | - if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) { |
899 | + // dismantling the rangers hut, but only if we have them above a target |
900 | + if (wood_policy_ == WoodPolicy::kDismantleRangers && site.bo->cnt_built_ > site.bo->cnt_target_) { |
901 | |
902 | site.bo->last_dismantle_time_ = game().get_gametime(); |
903 | flags_to_be_removed.push_back(site.site->base_flag().get_position()); |
904 | @@ -3138,27 +3261,38 @@ |
905 | return true; |
906 | } |
907 | |
908 | - if (score > 120 && !site.site->is_stopped()) { |
909 | + // stopping a ranger (sometimes the policy can be kDismantleRangers, |
910 | + // but we still preserve some rangers for sure) |
911 | + if ((wood_policy_ == WoodPolicy::kStopRangers |
912 | + || |
913 | + wood_policy_ == WoodPolicy::kDismantleRangers) |
914 | + && |
915 | + !site.site->is_stopped()) { |
916 | |
917 | game().send_player_start_stop_building(*site.site); |
918 | - return true; |
919 | + return false; |
920 | } |
921 | + |
922 | const uint32_t trees_in_vicinity = |
923 | map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5), |
924 | nullptr, |
925 | FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree"))); |
926 | |
927 | + // stop ranger if enough trees around regardless of policy |
928 | if (trees_in_vicinity > 25) { |
929 | if (!site.site->is_stopped()) { |
930 | game().send_player_start_stop_building(*site.site); |
931 | } |
932 | - } else if (score < 80 && site.site->is_stopped()) { |
933 | - |
934 | - game().send_player_start_stop_building(*site.site); |
935 | + // if not enough trees nearby, we can start them if required |
936 | + } else if ((wood_policy_ == WoodPolicy::kStartRangers || |
937 | + wood_policy_ == WoodPolicy::kBuildRangers) |
938 | + && |
939 | + site.site->is_stopped()) { |
940 | + game().send_player_start_stop_building(*site.site); |
941 | } |
942 | } |
943 | |
944 | - return changed; |
945 | + return false; |
946 | } |
947 | |
948 | // This function scans current situation with shipyards, ports, ships, ongoing expeditions |
949 | @@ -3562,15 +3696,15 @@ |
950 | // counting vacant positions |
951 | vacant_mil_positions_ = 0; |
952 | for (TrainingSiteObserver tso : trainingsites) { |
953 | - vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size(); |
954 | + vacant_mil_positions_ += 10 * (tso.site->soldier_capacity() - tso.site->stationed_soldiers().size()); |
955 | } |
956 | for (MilitarySiteObserver mso : militarysites) { |
957 | vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size(); |
958 | } |
959 | } |
960 | |
961 | -// this function only manipulates with trainingsites' inputs priority |
962 | -// decreases it when too many unoccupied military buildings |
963 | +// this function only check with trainingsites |
964 | +// manipulates input queues and soldier capacity |
965 | bool DefaultAI::check_trainingsites(uint32_t gametime) { |
966 | if (taskDue[ScheduleTasks::kCheckTrainingsites] > gametime) { |
967 | return false; |
968 | @@ -3582,6 +3716,9 @@ |
969 | return false; |
970 | } |
971 | |
972 | + trainingsites.push_back(trainingsites.front()); |
973 | + trainingsites.pop_front(); |
974 | + |
975 | TrainingSite* ts = trainingsites.front().site; |
976 | TrainingSiteObserver& tso = trainingsites.front(); |
977 | |
978 | @@ -3595,12 +3732,76 @@ |
979 | } |
980 | } |
981 | |
982 | - trainingsites.push_back(trainingsites.front()); |
983 | - trainingsites.pop_front(); |
984 | - |
985 | - // changing capacity |
986 | - if (tso.site->soldier_capacity() != 2) { |
987 | - game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity()); |
988 | + // changing capacity to 0 - this will happen only once..... |
989 | + if (tso.site->soldier_capacity() > 1) { |
990 | + game().send_player_change_soldier_capacity(*ts, - tso.site->soldier_capacity()); |
991 | + return true; |
992 | + } |
993 | + |
994 | + // reducing ware queues |
995 | + // - for armours and weapons to 1 |
996 | + // - for others to 6 |
997 | + std::vector<WaresQueue*> const warequeues1 = tso.site->warequeues(); |
998 | + size_t nr_warequeues = warequeues1.size(); |
999 | + for (size_t i = 0; i < nr_warequeues; ++i) { |
1000 | + |
1001 | + // if it was decreased yet |
1002 | + if (warequeues1[i]->get_max_fill() <= 1) { |
1003 | + continue;} |
1004 | + |
1005 | + // now modifying max_fill of armors and weapons |
1006 | + for (std::string pattern : armors_and_weapons) { |
1007 | + |
1008 | + if (tribe_->get_ware_descr(warequeues1[i]->get_ware())->name().find(pattern) != std::string::npos) { |
1009 | + if (warequeues1[i]->get_max_fill() > 1) { |
1010 | + game().send_player_set_ware_max_fill(*ts, warequeues1[i]->get_ware(), 1); |
1011 | + continue; |
1012 | + } |
1013 | + } |
1014 | + } |
1015 | + } |
1016 | + |
1017 | + // changing priority if basic |
1018 | + if (tso.bo->trainingsite_type_ == TrainingSiteType::kBasic) { |
1019 | + for (uint32_t k = 0; k < tso.bo->inputs_.size(); ++k) { |
1020 | + game().send_player_set_ware_priority( |
1021 | + *ts, wwWARE, tso.bo->inputs_.at(k), HIGH_PRIORITY); |
1022 | + } |
1023 | + } |
1024 | + |
1025 | + // if soldier capacity is set to 0, we need to find out if the site is |
1026 | + // supplied enough to incrase the capacity to 1 |
1027 | + if (tso.site->soldier_capacity() == 0) { |
1028 | + |
1029 | + // First subsitute wares |
1030 | + int32_t filled = 0; |
1031 | + bool supplied_enough = true; |
1032 | + std::vector<WaresQueue*> const warequeues2 = tso.site->warequeues(); |
1033 | + nr_warequeues = warequeues2.size(); |
1034 | + for (size_t i = 0; i < nr_warequeues; ++i) { |
1035 | + if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) > 0) { |
1036 | + filled += warequeues2[i]->get_filled(); |
1037 | + } |
1038 | + } |
1039 | + if (filled < 5) { |
1040 | + supplied_enough = false; |
1041 | + } |
1042 | + |
1043 | + // checking non subsitutes |
1044 | + for (size_t i = 0; i < nr_warequeues; ++i) { |
1045 | + if (tso.bo->substitute_inputs_.count(warequeues2[i]->get_ware()) == 0) { |
1046 | + const uint32_t required_amount |
1047 | + = |
1048 | + (warequeues2[i]->get_max_fill() < 5) ? warequeues2[i]->get_max_fill() : 5; |
1049 | + if (warequeues2[i]->get_filled() < required_amount) { |
1050 | + supplied_enough = false; |
1051 | + } |
1052 | + } |
1053 | + } |
1054 | + |
1055 | + if (supplied_enough) { |
1056 | + game().send_player_change_soldier_capacity(*ts, 1); |
1057 | + } |
1058 | } |
1059 | |
1060 | ts_without_trainers_ = 0; // zeroing |
1061 | @@ -3713,12 +3914,12 @@ |
1062 | int32_t unused1 = 0; |
1063 | uint16_t unused2 = 0; |
1064 | |
1065 | - mso.enemies_nearby_ = true; |
1066 | + mso.enemies_nearby_ = false; |
1067 | |
1068 | // yes enemy is nearby, but still we must distinguish whether |
1069 | // he is accessible (over the land) |
1070 | if (other_player_accessible( |
1071 | - vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) { |
1072 | + vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kEnemy)) { |
1073 | |
1074 | uint32_t const total_capacity = ms->max_soldier_capacity(); |
1075 | uint32_t const target_capacity = ms->soldier_capacity(); |
1076 | @@ -3732,6 +3933,9 @@ |
1077 | *ms, MilitarySite::kPrefersHeroes); |
1078 | changed = true; |
1079 | } |
1080 | + |
1081 | + mso.enemies_nearby_ = true; |
1082 | + enemy_last_seen_ = gametime; |
1083 | } else { // otherwise decrease soldiers |
1084 | uint32_t const j = ms->soldier_capacity(); |
1085 | |
1086 | @@ -3753,8 +3957,8 @@ |
1087 | |
1088 | /** |
1089 | * This function takes care about the unowned and opposing territory and |
1090 | - * recalculates the priority for none military buildings depending on the |
1091 | - * initialisation type of a defaultAI |
1092 | + * recalculates the priority for non-military buildings |
1093 | + * The goal is to minimize losses when teritory is lost |
1094 | * |
1095 | * \arg bf = BuildableField to be checked |
1096 | * \arg prio = priority until now. |
1097 | @@ -3762,18 +3966,26 @@ |
1098 | * \returns the recalculated priority |
1099 | */ |
1100 | int32_t DefaultAI::recalc_with_border_range(const BuildableField& bf, int32_t prio) { |
1101 | - // Prefer building space in the inner land. |
1102 | - |
1103 | + |
1104 | + // no change when priority is not positive number |
1105 | + if (prio <= 0) { |
1106 | + return prio; |
1107 | + } |
1108 | + |
1109 | + // in unowned teritory, decreasing to 2/3 |
1110 | if (bf.unowned_land_nearby_ > 15) { |
1111 | - prio -= (bf.unowned_land_nearby_ - 15); |
1112 | - } |
1113 | - |
1114 | - // Especially places near the frontier to the enemies are unlikely |
1115 | - // NOTE take care about the type of computer player_. The more |
1116 | - // NOTE aggressive a computer player_ is, the more important is |
1117 | - // NOTE this check. So we add \var type as bonus. |
1118 | - if (bf.enemy_nearby_ && prio > 0) { |
1119 | - prio /= (3 + type_); |
1120 | + prio *= 2; |
1121 | + prio /= 3; |
1122 | + } |
1123 | + |
1124 | + // to preserve positive score |
1125 | + if (prio == 0) { |
1126 | + prio = 1; |
1127 | + } |
1128 | + |
1129 | + // Further decrease the score if enemy nearby |
1130 | + if (bf.enemy_nearby_) { |
1131 | + prio /= 2; |
1132 | } |
1133 | |
1134 | return prio; |
1135 | @@ -3952,6 +4164,25 @@ |
1136 | } |
1137 | } |
1138 | |
1139 | +// This is called when soldier left the trainingsite |
1140 | +// the purpose is to set soldier capacity to 0 |
1141 | +// (AI will then wait till training site is stocked) |
1142 | +void DefaultAI::soldier_trained(const TrainingSite& site) { |
1143 | + |
1144 | + for (TrainingSiteObserver & trainingsite_obs : trainingsites) { |
1145 | + if (trainingsite_obs.site == &site) { |
1146 | + if (trainingsite_obs.site->soldier_capacity() > 0) { |
1147 | + game().send_player_change_soldier_capacity(*trainingsite_obs.site, |
1148 | + - trainingsite_obs.site->soldier_capacity()); |
1149 | + } |
1150 | + return; |
1151 | + } |
1152 | + } |
1153 | + |
1154 | + log (" %d: Computer player error - trainingsite not found\n", |
1155 | + player_number()); |
1156 | +} |
1157 | + |
1158 | // walk and search for teritorry controlled by other player |
1159 | // usually scanning radius is enough but sometimes we must walk to |
1160 | // verify that an enemy teritory is really accessible by land |
1161 | @@ -3993,10 +4224,34 @@ |
1162 | // a port location), but when testing (starting from) own military building |
1163 | // we must ignore own teritory, of course |
1164 | if (f->get_owned_by() > 0) { |
1165 | - if (type == WalkSearch::kAnyPlayer || |
1166 | - (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn)) { |
1167 | - *tested_fields = done.size(); |
1168 | - return true; |
1169 | + |
1170 | + // if field is owned by anybody |
1171 | + if (type == WalkSearch::kAnyPlayer) { |
1172 | + *tested_fields = done.size(); |
1173 | + return true; |
1174 | + } |
1175 | + |
1176 | + // if anybody but not me |
1177 | + if (type == WalkSearch::kOtherPlayers && f->get_owned_by() != pn) { |
1178 | + *tested_fields = done.size(); |
1179 | + return true; |
1180 | + } |
1181 | + |
1182 | + // if owned by enemy |
1183 | + if (type == WalkSearch::kEnemy && f->get_owned_by() != pn) { |
1184 | + // for case I am not member of a team |
1185 | + if (player_->team_number() == 0) { |
1186 | + *tested_fields = done.size(); |
1187 | + return true; |
1188 | + // if I am in team, testing if the same team |
1189 | + } else if (player_->team_number() > 0 |
1190 | + && |
1191 | + player_->team_number() |
1192 | + != |
1193 | + game().get_player(f->get_owned_by())->team_number()) { |
1194 | + *tested_fields = done.size(); |
1195 | + return true; |
1196 | + } |
1197 | } |
1198 | } |
1199 | |
1200 | @@ -4421,6 +4676,64 @@ |
1201 | return supplied == bo.inputs_.size(); |
1202 | } |
1203 | |
1204 | +// This calculates strength of vector of soldiers, f.e. soldiers in a building or |
1205 | +// ones ready to attack |
1206 | +int32_t DefaultAI::calculate_strength(const std::vector<Widelands::Soldier*> soldiers) { |
1207 | + |
1208 | + if (soldiers.empty()) { |
1209 | + return 0; |
1210 | + } |
1211 | + |
1212 | + Tribes tribe = Tribes::kNone; |
1213 | + |
1214 | + if (soldiers.at(0)->get_owner()->tribe().name() == "atlanteans") { |
1215 | + tribe = Tribes::kAtlanteans; |
1216 | + } else if (soldiers.at(0)->get_owner()->tribe().name() == "barbarians") { |
1217 | + tribe = Tribes::kBarbarians; |
1218 | + } else if (soldiers.at(0)->get_owner()->tribe().name() == "empire") { |
1219 | + tribe = Tribes::kEmpire; |
1220 | + } else { |
1221 | + throw wexception("AI warning: Unable to calculate strenght for player of tribe %s", |
1222 | + soldiers.at(0)->get_owner()->tribe().name().c_str()); |
1223 | + } |
1224 | + |
1225 | + float hp = 0; |
1226 | + float al = 0; |
1227 | + float dl = 0; |
1228 | + float el = 0; |
1229 | + float final = 0; |
1230 | + |
1231 | + for (Soldier * soldier : soldiers) { |
1232 | + switch (tribe) { |
1233 | + case (Tribes::kAtlanteans): |
1234 | + hp = 135 + 40 * soldier->get_hp_level(); |
1235 | + al = 14 + 8 * soldier->get_attack_level(); |
1236 | + dl = static_cast<float>(94 - 8 * soldier->get_defense_level()) / 100; |
1237 | + el = static_cast<float>(70 - 17 * soldier->get_evade_level()) / 100; |
1238 | + break; |
1239 | + case (Tribes::kBarbarians): |
1240 | + hp += 130 + 28 * soldier->get_hp_level(); |
1241 | + al += 14 + 7 * soldier->get_attack_level(); |
1242 | + dl += static_cast<float>(97 - 8 * soldier->get_defense_level()) / 100; |
1243 | + el += static_cast<float>(75 - 15 * soldier->get_evade_level()) / 100; |
1244 | + break; |
1245 | + case (Tribes::kEmpire): |
1246 | + hp += 130 + 21 * soldier->get_hp_level(); |
1247 | + al += 14 + 8 * soldier->get_attack_level(); |
1248 | + dl += static_cast<float>(95 - 8 * soldier->get_defense_level()) / 100; |
1249 | + el += static_cast<float>(70 - 16 * soldier->get_evade_level()) / 100; |
1250 | + break; |
1251 | + default: |
1252 | + assert (false); |
1253 | + } |
1254 | + |
1255 | + final += (al * hp) / (dl * el); |
1256 | + } |
1257 | + |
1258 | + // 2500 is aproximate strength of one unpromoted soldier |
1259 | + return static_cast<int32_t>(final / 2500); |
1260 | +} |
1261 | + |
1262 | bool DefaultAI::check_enemy_sites(uint32_t const gametime) { |
1263 | |
1264 | Map& map = game().map(); |
1265 | @@ -4437,6 +4750,27 @@ |
1266 | // receiving games statistics and parsing it (reading latest entry) |
1267 | const Game::GeneralStatsVector& genstats = game().get_general_statistics(); |
1268 | |
1269 | + // summing team power, creating team_power std::map of team_number:strength |
1270 | + std::map<TeamNumber, uint32_t> team_power; |
1271 | + for (uint8_t j = 1; j <= plr_in_game; ++j) { |
1272 | + TeamNumber const tm = game().get_player(j)->team_number(); |
1273 | + if (tm == 0) { |
1274 | + continue; |
1275 | + } |
1276 | + // for case this is new team |
1277 | + if (team_power.count(tm) == 0) { |
1278 | + // adding this team (number) to vector |
1279 | + team_power[tm] = 0; |
1280 | + } |
1281 | + try { |
1282 | + team_power[tm] += genstats.at(j - 1).miltary_strength.back(); |
1283 | + } catch (const std::out_of_range&) { |
1284 | + log("ComputerPlayer(%d): genstats entry missing - size :%d\n", |
1285 | + player_number(), |
1286 | + static_cast<unsigned int>(genstats.size())); |
1287 | + } |
1288 | + } |
1289 | + |
1290 | // defining treshold ratio of own_strenght/enemy's_strength |
1291 | uint32_t treshold_ratio = 100; |
1292 | if (type_ == AGGRESSIVE) { |
1293 | @@ -4446,25 +4780,49 @@ |
1294 | treshold_ratio = 120; |
1295 | } |
1296 | |
1297 | - // now we test all players which one are 'attackable' |
1298 | + uint32_t my_power = 0; |
1299 | + try { |
1300 | + my_power = genstats.at(pn - 1).miltary_strength.back(); |
1301 | + } catch (const std::out_of_range&) { |
1302 | + log("ComputerPlayer(%d): genstats entry missing - size :%d\n", |
1303 | + player_number(), |
1304 | + static_cast<unsigned int>(genstats.size())); |
1305 | + } |
1306 | + // adding power of team (minus my power) divided by 2 |
1307 | + // (if I am a part of a team of course) |
1308 | + if (game().get_player(pn)->team_number() > 0) { |
1309 | + my_power += (team_power[game().get_player(pn)->team_number()] - my_power) / 2; |
1310 | + } |
1311 | + |
1312 | + // now we test all players to identify 'attackable' ones |
1313 | for (uint8_t j = 1; j <= plr_in_game; ++j) { |
1314 | - if (pn == j) { // its me |
1315 | + // if it's me |
1316 | + if (pn == j) { |
1317 | + player_attackable[j - 1] = false; |
1318 | + continue; |
1319 | + } |
1320 | + // if we are the same team |
1321 | + if (game().get_player(pn)->team_number() > 0 && |
1322 | + game().get_player(pn)->team_number() == game().get_player(j)->team_number()) { |
1323 | player_attackable[j - 1] = false; |
1324 | continue; |
1325 | } |
1326 | |
1327 | + // now we compare strength |
1328 | try { |
1329 | - // It seems that under some circumstances genstats can be empty. |
1330 | - // So, to avoid crash, the AI tests its content first. |
1331 | - if (genstats.at(j - 1).miltary_strength.empty()) { |
1332 | - log("ComputerPlayer(%d): miltary_strength is empty\n", player_number()); |
1333 | - player_attackable.at(j - 1) = false; |
1334 | - // Avoid division by zero |
1335 | - } else if (genstats.at(j - 1).miltary_strength.back() == 0) { |
1336 | + // strength of the other player |
1337 | + uint32_t players_power = 0; |
1338 | + if (!genstats.at(j - 1).miltary_strength.empty()) { |
1339 | + players_power += genstats.at(j - 1).miltary_strength.back(); |
1340 | + } |
1341 | + // +power of team (if member of a team) |
1342 | + if (game().get_player(j)->team_number() > 0) { |
1343 | + players_power += (team_power[game().get_player(j)->team_number()] - players_power) / 2; |
1344 | + } |
1345 | + |
1346 | + if (players_power == 0) { |
1347 | player_attackable.at(j - 1) = true; |
1348 | - // Check threshold |
1349 | - } else if ((genstats.at(pn - 1).miltary_strength.back() * 100 / |
1350 | - genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) { |
1351 | + } else if (my_power * 100 / players_power > treshold_ratio) { |
1352 | player_attackable.at(j - 1) = true; |
1353 | } else { |
1354 | player_attackable.at(j - 1) = false; |
1355 | @@ -4532,10 +4890,10 @@ |
1356 | count += 1; |
1357 | |
1358 | site->second.last_tested = gametime; |
1359 | - uint8_t defenders = 0; |
1360 | + uint8_t defenders_strength = 0; |
1361 | bool is_warehouse = false; |
1362 | bool is_attackable = false; |
1363 | - uint16_t onwer_number = 100; |
1364 | + uint16_t owner_number = 100; |
1365 | |
1366 | // testing if we can attack the building - result is a flag |
1367 | // if we dont get a flag, we remove the building from observers list |
1368 | @@ -4544,23 +4902,30 @@ |
1369 | Flag* flag = nullptr; |
1370 | if (upcast(MilitarySite, bld, f.field->get_immovable())) { |
1371 | if (player_->is_hostile(bld->owner())) { |
1372 | - defenders = bld->present_soldiers().size(); |
1373 | + std::vector<Soldier *> defenders; |
1374 | + defenders = bld->present_soldiers(); |
1375 | + defenders_strength = calculate_strength(defenders); |
1376 | + |
1377 | flag = &bld->base_flag(); |
1378 | if (bld->can_attack()) { |
1379 | is_attackable = true; |
1380 | } |
1381 | - onwer_number = bld->owner().player_number(); |
1382 | + owner_number = bld->owner().player_number(); |
1383 | } |
1384 | } |
1385 | if (upcast(Warehouse, Wh, f.field->get_immovable())) { |
1386 | if (player_->is_hostile(Wh->owner())) { |
1387 | - defenders = Wh->present_soldiers().size(); |
1388 | + |
1389 | + std::vector<Soldier *> defenders; |
1390 | + defenders = Wh->present_soldiers(); |
1391 | + defenders_strength = calculate_strength(defenders); |
1392 | + |
1393 | flag = &Wh->base_flag(); |
1394 | is_warehouse = true; |
1395 | if (Wh->can_attack()) { |
1396 | is_attackable = true; |
1397 | } |
1398 | - onwer_number = Wh->owner().player_number(); |
1399 | + owner_number = Wh->owner().player_number(); |
1400 | } |
1401 | } |
1402 | |
1403 | @@ -4588,61 +4953,75 @@ |
1404 | |
1405 | // can we attack: |
1406 | if (is_attackable) { |
1407 | - site->second.attack_soldiers = player_->find_attack_soldiers(*flag); |
1408 | + std::vector<Soldier *> attackers; |
1409 | + player_->find_attack_soldiers(*flag, &attackers); |
1410 | + int32_t strength = calculate_strength(attackers); |
1411 | + |
1412 | + site->second.attack_soldiers_strength = strength; |
1413 | } else { |
1414 | - site->second.attack_soldiers = 0; |
1415 | + site->second.attack_soldiers_strength = 0; |
1416 | } |
1417 | |
1418 | - site->second.defenders = defenders; |
1419 | - |
1420 | - if (site->second.attack_soldiers > 0) { |
1421 | - site->second.score = site->second.attack_soldiers - site->second.defenders / 2; |
1422 | - |
1423 | - if (!is_warehouse) |
1424 | - site->second.score -= 1; |
1425 | + site->second.defenders_strength = defenders_strength; |
1426 | + |
1427 | + if (site->second.attack_soldiers_strength > 0 |
1428 | + && |
1429 | + player_attackable[owner_number - 1]) { |
1430 | + site->second.score = site->second.attack_soldiers_strength - site->second.defenders_strength / 2; |
1431 | + |
1432 | + if (is_warehouse) { |
1433 | + site->second.score += 2; |
1434 | + } else { |
1435 | + site->second.score -= 2; |
1436 | + } |
1437 | |
1438 | // here is some differentiation based on "character" of a player |
1439 | if (type_ == NORMAL) { |
1440 | - site->second.score -= 1; |
1441 | - site->second.score -= vacant_mil_positions_ / 10; |
1442 | + site->second.score -= 3; |
1443 | + site->second.score -= vacant_mil_positions_ / 8; |
1444 | } else if (type_ == DEFENSIVE) { |
1445 | - site->second.score -= 2; |
1446 | - site->second.score -= vacant_mil_positions_ / 5; |
1447 | + site->second.score -= 6; |
1448 | + site->second.score -= vacant_mil_positions_ / 4; |
1449 | } else { //=AGRESSIVE |
1450 | - site->second.score -= vacant_mil_positions_ / 15; |
1451 | + site->second.score -= vacant_mil_positions_ / 16; |
1452 | } |
1453 | if (site->second.mines_nearby == ExtendedBool::kFalse) { |
1454 | site->second.score -= 1; |
1455 | + } else { |
1456 | + site->second.score += 1; |
1457 | } |
1458 | // we dont want to attack multiple players at the same time too eagerly |
1459 | - if (onwer_number != last_attacked_player_) { |
1460 | + if (owner_number != last_attacked_player_) { |
1461 | site->second.score -= 3; |
1462 | } |
1463 | // if we dont have mines yet |
1464 | if (mines_.size() <= 2) { |
1465 | - site->second.score -= 2; |
1466 | + site->second.score -= 8; |
1467 | } |
1468 | // also we should have at least some training sites |
1469 | if ((ts_basic_count_ + ts_advanced_count_) == 0) { |
1470 | - site->second.score -= 2; |
1471 | + site->second.score -= 4; |
1472 | } |
1473 | // treating no attack score |
1474 | if (site->second.no_attack_counter < 0) { |
1475 | + // we cannot attack yet |
1476 | site->second.score = 0; |
1477 | + // but increase the counter by 1 |
1478 | site->second.no_attack_counter += 1; |
1479 | } |
1480 | + |
1481 | } else { |
1482 | site->second.score = 0; |
1483 | } // or the score will remain 0 |
1484 | |
1485 | - if (site->second.score > 0 && player_attackable[onwer_number - 1]) { |
1486 | + if (site->second.score > 0) { |
1487 | if (site->second.score > best_score) { |
1488 | best_score = site->second.score; |
1489 | best_target = site->first; |
1490 | } |
1491 | } |
1492 | |
1493 | - if (site->second.attack_soldiers > 0) { |
1494 | + if (site->second.attack_soldiers_strength > 0) { |
1495 | site->second.last_time_attackable = gametime; |
1496 | } |
1497 | if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) { |
1498 | @@ -4743,6 +5122,19 @@ |
1499 | DueTask = task.first; |
1500 | } |
1501 | } |
1502 | + if ((gametime - oldestTaskTime) > 5000) { |
1503 | + scheduler_delay_counter_ += 1; |
1504 | + } else { |
1505 | + scheduler_delay_counter_ = 0; |
1506 | + } |
1507 | + |
1508 | + if (scheduler_delay_counter_ > 10) { |
1509 | + log(" %d: AI: game speed too high, jobs are too late (now %2d seconds)\n", |
1510 | + player_number(), |
1511 | + static_cast<int32_t>((gametime - oldestTaskTime) / 1000)); |
1512 | + scheduler_delay_counter_ = 0; |
1513 | + } |
1514 | + |
1515 | return DueTask; |
1516 | } |
1517 | |
1518 | @@ -4766,6 +5158,7 @@ |
1519 | const std::vector<std::string> materials = {"coal", |
1520 | "log", |
1521 | "ironore", |
1522 | + "iron", |
1523 | "marble", |
1524 | "plank", |
1525 | "water", |
1526 | @@ -4791,12 +5184,13 @@ |
1527 | } |
1528 | summary = summary + materials.at(j) + ", "; |
1529 | } |
1530 | - log(" %1d: Buildings: Pr:%3lu, Ml:%3lu, Mi:%2lu, Wh:%2lu, Po:%2u. Missing: %s\n", |
1531 | + |
1532 | + log(" %1d: Buildings: Pr:%3u, Ml:%3u, Mi:%2u, Wh:%2u, Po:%u. Missing: %s\n", |
1533 | pn, |
1534 | - productionsites.size(), |
1535 | - militarysites.size(), |
1536 | - mines_.size(), |
1537 | - warehousesites.size() - num_ports, |
1538 | + static_cast<uint32_t>(productionsites.size()), |
1539 | + static_cast<uint32_t>(militarysites.size()), |
1540 | + static_cast<uint32_t>(mines_.size()), |
1541 | + static_cast<uint32_t>(warehousesites.size() - num_ports), |
1542 | num_ports, |
1543 | summary.c_str()); |
1544 | } |
1545 | |
1546 | === modified file 'src/ai/defaultai.h' |
1547 | --- src/ai/defaultai.h 2015-05-05 20:00:21 +0000 |
1548 | +++ src/ai/defaultai.h 2015-07-28 19:26:44 +0000 |
1549 | @@ -30,6 +30,8 @@ |
1550 | #include "base/i18n.h" |
1551 | #include "logic/immovable.h" |
1552 | #include "logic/ship.h" |
1553 | +#include "logic/soldier.h" |
1554 | +#include "logic/trainingsite.h" |
1555 | |
1556 | namespace Widelands { |
1557 | struct Road; |
1558 | @@ -79,8 +81,10 @@ |
1559 | DEFENSIVE = 0, |
1560 | }; |
1561 | |
1562 | - enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers}; |
1563 | + enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers, kEnemy}; |
1564 | + enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kStartRangers, kBuildRangers}; |
1565 | enum class NewShip : uint8_t {kBuilt, kFoundOnLoad}; |
1566 | + enum class PerfEvaluation : uint8_t {kForConstruction, kForDismantle}; |
1567 | enum class ScheduleTasks : uint8_t { |
1568 | kBbuildableFieldsCheck, |
1569 | kMineableFieldsCheck, |
1570 | @@ -108,6 +112,12 @@ |
1571 | kExpansion, |
1572 | kPushExpansion |
1573 | }; |
1574 | + enum class Tribes : uint8_t { |
1575 | + kNone, |
1576 | + kBarbarians, |
1577 | + kAtlanteans, |
1578 | + kEmpire |
1579 | + }; |
1580 | |
1581 | /// Implementation for Aggressive |
1582 | struct AggressiveImpl : public ComputerPlayer::Implementation { |
1583 | @@ -199,6 +209,8 @@ |
1584 | bool check_ships(uint32_t); |
1585 | bool check_enemy_sites(uint32_t); |
1586 | void print_stats(uint32_t); |
1587 | + // return single number of strength of vector of soldiers |
1588 | + int32_t calculate_strength(const std::vector<Widelands::Soldier*>); |
1589 | uint32_t get_stocklevel_by_hint(size_t); |
1590 | uint32_t get_stocklevel(BuildingObserver&); |
1591 | uint32_t get_warehoused_stock(Widelands::WareIndex wt); |
1592 | @@ -233,6 +245,10 @@ |
1593 | void gain_ship(Widelands::Ship&, NewShip); |
1594 | void expedition_management(ShipObserver&); |
1595 | void out_of_resources_site(const Widelands::ProductionSite&); |
1596 | + void soldier_trained(const Widelands::TrainingSite&); |
1597 | + bool is_productionsite_needed(int32_t outputs, |
1598 | + int32_t performance, |
1599 | + PerfEvaluation purpose); |
1600 | |
1601 | bool check_supply(const BuildingObserver&); |
1602 | |
1603 | @@ -260,6 +276,8 @@ |
1604 | // check ms in this interval - will auto-adjust |
1605 | uint32_t enemysites_check_delay_; |
1606 | |
1607 | + WoodPolicy wood_policy_; |
1608 | + |
1609 | std::list<Widelands::FCoords> unusable_fields; |
1610 | std::list<BuildableField*> buildable_fields; |
1611 | std::list<BlockedField> blocked_fields; |
1612 | @@ -311,13 +329,24 @@ |
1613 | // it decreases with failed scans |
1614 | int32_t spots_; // sum of buildable fields |
1615 | int32_t vacant_mil_positions_; // sum of vacant positions in militarysites and training sites |
1616 | - //statistics for training sites per type |
1617 | + // statistics for training sites per type |
1618 | uint8_t ts_basic_count_; |
1619 | uint8_t ts_basic_const_count_; |
1620 | uint8_t ts_advanced_count_; |
1621 | uint8_t ts_advanced_const_count_; |
1622 | uint8_t ts_without_trainers_; |
1623 | |
1624 | + // this is helping counter to track how many scheduler tasks are too delayed |
1625 | + // the purpose is to print out a warning that the game is pacing too fast |
1626 | + int32_t scheduler_delay_counter_; |
1627 | + |
1628 | + // this is a bunch of patterns that have to identify weapons and armors for input queues of trainingsites |
1629 | + std::vector<std::string> const armors_and_weapons = |
1630 | + {"ax", "lance", "armor", "helm", "lance", "trident", "tabard", "shield", "mask"}; |
1631 | + // some buildings can be upgraded even when they are only one |
1632 | + // now only microbrewery get this special treatment |
1633 | + const char* preferred_upgrade[1] = {"micro-brewery"}; |
1634 | + |
1635 | enum {kReprioritize, kStopShipyard, kStapShipyard}; |
1636 | |
1637 | std::vector<int16_t> marineTaskQueue_; |
1638 | @@ -327,6 +356,8 @@ |
1639 | std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_; |
1640 | std::unique_ptr<Notifications::Subscriber<Widelands::NoteProductionSiteOutOfResources>> |
1641 | outofresource_subscriber_; |
1642 | + std::unique_ptr<Notifications::Subscriber<Widelands::NoteTrainingSiteSoldierTrained>> |
1643 | + soldiertrained_subscriber_; |
1644 | std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipMessage>> shipnotes_subscriber_; |
1645 | }; |
1646 | |
1647 | |
1648 | === modified file 'src/logic/trainingsite.cc' |
1649 | --- src/logic/trainingsite.cc 2015-05-03 11:39:11 +0000 |
1650 | +++ src/logic/trainingsite.cc 2015-07-28 19:26:44 +0000 |
1651 | @@ -320,8 +320,9 @@ |
1652 | delete m_soldier_request; |
1653 | m_soldier_request = nullptr; |
1654 | |
1655 | - while (m_soldiers.size() > m_capacity) |
1656 | + while (m_soldiers.size() > m_capacity) { |
1657 | drop_soldier(**m_soldiers.rbegin()); |
1658 | + } |
1659 | } |
1660 | } |
1661 | |
1662 | @@ -432,6 +433,7 @@ |
1663 | |
1664 | // Schedule, so that we can call new soldiers on next act() |
1665 | schedule_act(game, 100); |
1666 | + Notifications::publish(NoteTrainingSiteSoldierTrained(this, get_owner())); |
1667 | } |
1668 | |
1669 | |
1670 | |
1671 | === modified file 'src/logic/trainingsite.h' |
1672 | --- src/logic/trainingsite.h 2014-09-19 12:54:54 +0000 |
1673 | +++ src/logic/trainingsite.h 2015-07-28 19:26:44 +0000 |
1674 | @@ -218,6 +218,24 @@ |
1675 | |
1676 | }; |
1677 | |
1678 | +/** |
1679 | + * Note to be published when a soldier is leaving the training center |
1680 | + */ |
1681 | +// A note we're using to notify the AI |
1682 | +struct NoteTrainingSiteSoldierTrained { |
1683 | + CAN_BE_SENT_AS_NOTE(NoteId::TrainingSiteSoldierTrained) |
1684 | + |
1685 | + // The trainingsite from where soldier is leaving. |
1686 | + TrainingSite* ts; |
1687 | + |
1688 | + // The player that owns the ttraining site. |
1689 | + Player * player; |
1690 | + |
1691 | + NoteTrainingSiteSoldierTrained(TrainingSite* const init_ts, Player* init_player) |
1692 | + : ts(init_ts), player(init_player) { |
1693 | + } |
1694 | +}; |
1695 | + |
1696 | } |
1697 | |
1698 | #endif // end of include guard: WL_LOGIC_TRAININGSITE_H |
1699 | |
1700 | === modified file 'src/notifications/note_ids.h' |
1701 | --- src/notifications/note_ids.h 2015-03-01 09:21:20 +0000 |
1702 | +++ src/notifications/note_ids.h 2015-07-28 19:26:44 +0000 |
1703 | @@ -32,6 +32,7 @@ |
1704 | FieldPossession, |
1705 | FieldTransformed, |
1706 | ProductionSiteOutOfResources, |
1707 | + TrainingSiteSoldierTrained, |
1708 | ShipMessage, |
1709 | GraphicResolutionChanged, |
1710 | |
1711 | |
1712 | === modified file 'tribes/atlanteans/fish_breeders_house/conf' |
1713 | --- tribes/atlanteans/fish_breeders_house/conf 2014-08-02 19:55:23 +0000 |
1714 | +++ tribes/atlanteans/fish_breeders_house/conf 2015-07-28 19:26:44 +0000 |
1715 | @@ -3,7 +3,7 @@ |
1716 | [aihints] |
1717 | needs_water=true |
1718 | renews_map_resource=fish |
1719 | -prohibited_till=900 |
1720 | +prohibited_till=300 |
1721 | |
1722 | [buildcost] |
1723 | log=1 |
1724 | |
1725 | === modified file 'tribes/atlanteans/sawmill/conf' |
1726 | --- tribes/atlanteans/sawmill/conf 2014-08-02 19:55:23 +0000 |
1727 | +++ tribes/atlanteans/sawmill/conf 2015-07-28 19:26:44 +0000 |
1728 | @@ -2,7 +2,7 @@ |
1729 | output=planks |
1730 | |
1731 | [aihints] |
1732 | -forced_after=300 |
1733 | +forced_after=120 |
1734 | prohibited_till=60 |
1735 | |
1736 | [buildcost] |
1737 | |
1738 | === modified file 'tribes/atlanteans/smokery/conf' |
1739 | --- tribes/atlanteans/smokery/conf 2014-08-02 19:55:23 +0000 |
1740 | +++ tribes/atlanteans/smokery/conf 2015-07-28 19:26:44 +0000 |
1741 | @@ -4,7 +4,7 @@ |
1742 | |
1743 | [aihints] |
1744 | forced_after=900 |
1745 | -prohibited_till=60 |
1746 | +prohibited_till=180 |
1747 | |
1748 | [buildcost] |
1749 | log=1 |
1750 | |
1751 | === modified file 'tribes/atlanteans/spidercloth/conf' |
1752 | --- tribes/atlanteans/spidercloth/conf 2014-07-29 20:48:19 +0000 |
1753 | +++ tribes/atlanteans/spidercloth/conf 2015-07-28 19:26:44 +0000 |
1754 | @@ -1,7 +1,7 @@ |
1755 | help=_Spidercloth is made out of spideryarn in a weaving mill. It is used in the toolsmithy and the shipyard. Also some higher developed buildings need spidercloth for their construction. |
1756 | |
1757 | default_target_quantity=20 |
1758 | -preciousness=5 |
1759 | +preciousness=7 |
1760 | |
1761 | [idle] |
1762 | pics=idle.png |
1763 | |
1764 | === modified file 'tribes/atlanteans/spiderfarm/conf' |
1765 | --- tribes/atlanteans/spiderfarm/conf 2014-08-02 19:55:23 +0000 |
1766 | +++ tribes/atlanteans/spiderfarm/conf 2015-07-28 19:26:44 +0000 |
1767 | @@ -37,5 +37,5 @@ |
1768 | hotspot=87 75 |
1769 | |
1770 | [aihints] |
1771 | -forced_after=60 |
1772 | +forced_after=150 |
1773 | prohibited_till=60 |
1774 | |
1775 | === modified file 'tribes/atlanteans/weaving-mill/conf' |
1776 | --- tribes/atlanteans/weaving-mill/conf 2014-08-02 19:55:23 +0000 |
1777 | +++ tribes/atlanteans/weaving-mill/conf 2015-07-28 19:26:44 +0000 |
1778 | @@ -4,7 +4,7 @@ |
1779 | output=golden_tabard |
1780 | |
1781 | [aihints] |
1782 | -forced_after=60 |
1783 | +forced_after=150 |
1784 | prohibited_till=60 |
1785 | |
1786 | [buildcost] |
1787 | |
1788 | === modified file 'tribes/barbarians/farm/conf' |
1789 | --- tribes/barbarians/farm/conf 2015-06-10 07:00:10 +0000 |
1790 | +++ tribes/barbarians/farm/conf 2015-07-28 19:26:44 +0000 |
1791 | @@ -3,6 +3,7 @@ |
1792 | |
1793 | [aihints] |
1794 | space_consumer=true |
1795 | +forced_after=900 |
1796 | |
1797 | [buildcost] |
1798 | log=4 |
oooops, I found small problem in defaultai.cc (duplicite sections of code), I am going to fix it...