Merge lp:~widelands-dev/widelands/ai-military-changes into lp:widelands

Proposed by TiborB
Status: Merged
Merged at revision: 7436
Proposed branch: lp:~widelands-dev/widelands/ai-military-changes
Merge into: lp:widelands
Diff against target: 2528 lines (+937/-492)
13 files modified
src/ai/ai_help_structs.h (+76/-16)
src/ai/ai_hints.cc (+14/-0)
src/ai/ai_hints.h (+8/-0)
src/ai/defaultai.cc (+795/-460)
src/ai/defaultai.h (+28/-13)
tribes/atlanteans/dungeon/conf (+3/-0)
tribes/atlanteans/labyrinth/conf (+1/-1)
tribes/barbarians/battlearena/conf (+3/-0)
tribes/barbarians/trainingcamp/conf (+1/-1)
tribes/empire/arena/conf (+3/-0)
tribes/empire/colosseum/conf (+3/-0)
tribes/empire/piggery/conf (+1/-0)
tribes/empire/trainingcamp/conf (+1/-1)
To merge this branch: bzr merge lp:~widelands-dev/widelands/ai-military-changes
Reviewer Review Type Date Requested Status
SirVer Approve
Review via email: mp+253881@code.launchpad.net

Description of the change

Another bunch of AI changes, this time only files within src/ai/ were changed. Reworked were:
- attacking
- training sites
- mines
- further small things

To post a comment you must log in.
Revision history for this message
SirVer (sirver) wrote :

Please change the AI hint from ts_type=2 to something that is more understandable outside of the AI context, maybe trainingssite_type="no_bread_just_meat" or something along these lines. Numbers are fully opaque to everybody that looks at the conf files.

review: Needs Fixing
Revision history for this message
TiborB (tiborb95) wrote :

re type of trainingsites: type 1 needs less resources (to build and to operate) and type 2 needs more (including gold, weapons), so type 1 can and should be built sooner. But I can rename this classification to "basic" and "advanced", though this is not ideal neither.

Revision history for this message
GunChleoc (gunchleoc) wrote :

How about "needs_few_wares" and "needs_gold_and_weapons"?

Revision history for this message
TiborB (tiborb95) wrote :

@GunChleoc - you undestand that this will be completely hidden form everybody except handful of developers who will ever look at code. And perhaps few other peoples who will tinker with config files...

I already reworked this so I am not eager much to rework this once more, but if you really want this... :)

Revision history for this message
GunChleoc (gunchleoc) wrote :

Just imagine that you get hit by a bus tomorrow and that in 10 year's time another developer is trying to understand your code. So yes, just a handful of developers is important, which is why I posted my idea of how they might be named ;)

Revision history for this message
TiborB (tiborb95) wrote :

:) let make an agreement: If there will be another requests to change something I will change also this, can be?

Revision history for this message
SirVer (sirver) wrote :

> :) let make an agreement: If there will be another requests to change something I will change also this, can be?

Pfffff. I changed ts_type to trainingsite_type - I took the time, it took me 36 seconds. Should not have taken you much longer than typing this comment.

I think basic and advanced is fine - it allows for other types to be added in non-awkwardly later and it gives more possibility to grep for it through the source code which will make it easier for future devs to pick up on it.

lgtm.

review: Approve
Revision history for this message
TiborB (tiborb95) wrote :

> I changed ts_type to trainingsite_type - I took the time, it took me 36 seconds.

ts_type is definitely "safe" string to use sed (or other tool) to replace all occurrences, but to use sed to replace strings 'basic' and 'advanced' - it would be quite different

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/ai/ai_help_structs.h'
2--- src/ai/ai_help_structs.h 2015-03-05 20:57:07 +0000
3+++ src/ai/ai_help_structs.h 2015-03-26 20:42:28 +0000
4@@ -23,6 +23,7 @@
5 #include <list>
6 #include <unordered_set>
7
8+#include "ai/ai_hints.h"
9 #include "economy/flag.h"
10 #include "economy/road.h"
11 #include "logic/checkstep.h"
12@@ -40,7 +41,7 @@
13 class ProductionSite;
14 class MilitarySite;
15
16-enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse };
17+enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse};
18
19 struct CheckStepRoadAI {
20 CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
21@@ -110,6 +111,22 @@
22 }
23 };
24
25+// Sometimes we need to know how many nodes our allies owns
26+struct FindNodeAllyOwned {
27+ bool accept(const Map&, const FCoords& fc) const {
28+ return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() != 0) &&
29+ (fc.field->get_owned_by() != pn) &&
30+ !player_->is_hostile(*game.get_player(fc.field->get_owned_by()));
31+ }
32+
33+ Player* player_;
34+ Game& game;
35+ PlayerNumber pn;
36+
37+ FindNodeAllyOwned(Player* p, Game& g, PlayerNumber n) : player_(p), game(g), pn(n) {
38+ }
39+};
40+
41 // When looking for unowned terrain to acquire, we must
42 // pay speciall attention to fields where mines can be built.
43 // Fields should be completely unowned
44@@ -148,8 +165,9 @@
45
46 bool accept(const Map& /* map */, const FCoords& coord) const {
47 return (world_.terrain_descr(coord.field->terrain_d()).get_is() &
48- TerrainDescription::Type::kWater) ||
49- (world_.terrain_descr(coord.field->terrain_r()).get_is() & TerrainDescription::Type::kWater);
50+ TerrainDescription::Type::kWater) ||
51+ (world_.terrain_descr(coord.field->terrain_r()).get_is() &
52+ TerrainDescription::Type::kWater);
53 }
54
55 private:
56@@ -213,7 +231,7 @@
57 struct BuildableField {
58 Widelands::FCoords coords;
59
60- uint32_t next_update_due_;
61+ uint32_t field_info_expiration_;
62
63 bool preferred_;
64 bool enemy_nearby_;
65@@ -232,7 +250,7 @@
66 uint8_t space_consumers_nearby_;
67 // to manage the military better following variables exists:
68 // capacity of nearby buildings:
69- int16_t military_capacity_;
70+ int16_t area_military_capacity_;
71 // distance to near buldings:
72 int16_t military_loneliness_;
73 // count of military buildings in construction
74@@ -241,7 +259,7 @@
75 // are construction sites that will change this once they are built
76 int16_t military_in_constr_nearby_;
77 // actual count of soldiers in nearby buldings
78- int16_t military_presence_;
79+ int16_t area_military_presence_;
80 // stationed (manned) military buildings nearby
81 int16_t military_stationed_;
82 // stationed (manned) military buildings nearby
83@@ -256,7 +274,7 @@
84
85 BuildableField(const Widelands::FCoords& fc)
86 : coords(fc),
87- next_update_due_(0),
88+ field_info_expiration_(20000),
89 preferred_(false),
90 enemy_nearby_(0),
91 unowned_land_nearby_(0),
92@@ -277,10 +295,10 @@
93 critters_nearby_(-1),
94 ground_water_(1),
95 space_consumers_nearby_(0),
96- military_capacity_(0),
97+ area_military_capacity_(0),
98 military_loneliness_(1000),
99 military_in_constr_nearby_(0),
100- military_presence_(0),
101+ area_military_presence_(0),
102 military_stationed_(0),
103 military_unstationed_(0),
104 is_portspace_(false),
105@@ -292,7 +310,7 @@
106 struct MineableField {
107 Widelands::FCoords coords;
108
109- uint32_t next_update_due_;
110+ uint32_t field_info_expiration_;
111
112 bool preferred_;
113
114@@ -302,7 +320,7 @@
115
116 MineableField(const Widelands::FCoords& fc)
117 : coords(fc),
118- next_update_due_(0),
119+ field_info_expiration_(20000),
120 preferred_(false),
121 mines_nearby_(0),
122 same_mine_fields_nearby_(0) {
123@@ -346,14 +364,16 @@
124 bool is_fisher_; // need to identify fishers
125 bool is_port_;
126 bool is_shipyard_;
127- bool space_consumer_; // farm, vineyard... = true
128- bool expansion_type_; // military building used that can be used to control area
129- bool fighting_type_; // military building built near enemies
130- bool mountain_conqueror_; // military building built near mountains
131+ bool space_consumer_; // farm, vineyard... = true
132+ bool expansion_type_; // military building used that can be used to control area
133+ bool fighting_type_; // military building built near enemies
134+ bool mountain_conqueror_; // military building built near mountains
135 uint32_t prohibited_till_; // do not build before (ms)
136 uint32_t forced_after_; // do not wait until ware is needed
137+ TrainingSiteType trainingsite_type_;
138
139- bool unoccupied_; //
140+ bool unoccupied_;
141+ uint16_t unconnected_; // to any warehouse (count of such buildings)
142
143 int32_t mines_; // type of resource it mines_
144 uint16_t mines_percent_; // % of res it can mine
145@@ -429,4 +449,44 @@
146 uint8_t preciousness_;
147 };
148
149+//Computer player does not get notification messages about enemy militarysites
150+//and warehouses, so following is collected based on observation
151+//It is conventient to have some information preserved, like nearby minefields,
152+//when it was attacked, whether it is warehouse and so on
153+//Also AI test more such targets when considering attack and calculated score is
154+//is stored in the observer
155+struct EnemySiteObserver {
156+ bool warehouse_;
157+ uint8_t attack_soldiers;
158+ uint8_t defenders;
159+ uint8_t stationed_soldiers;
160+ uint32_t last_time_attackable;
161+ uint32_t last_tested;
162+ int16_t score;
163+ bool warehouse;
164+ Widelands::ExtendedBool mines_nearby;
165+ int16_t no_attack_counter;
166+
167+ EnemySiteObserver()
168+ : warehouse_(false),
169+ attack_soldiers(0),
170+ stationed_soldiers(0),
171+ last_time_attackable(std::numeric_limits<uint32_t>::max()),
172+ last_tested(0),
173+ score(0),
174+ mines_nearby(Widelands::ExtendedBool::kUnset),
175+ no_attack_counter(0) {
176+ }
177+};
178+
179+// as all mines have 3 levels, AI does not know total count of mines per mined material
180+// so this observer will be used for this
181+struct MineTypesObserver {
182+ uint16_t in_construction;
183+ uint16_t finished;
184+
185+ MineTypesObserver() : in_construction(0), finished(0) {
186+ }
187+};
188+
189 #endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H
190
191=== modified file 'src/ai/ai_hints.cc'
192--- src/ai/ai_hints.cc 2015-03-05 20:57:07 +0000
193+++ src/ai/ai_hints.cc 2015-03-26 20:42:28 +0000
194@@ -34,6 +34,7 @@
195 prohibited_till_(section ? section->get_natural("prohibited_till", 0) : 0),
196 forced_after_(section ? section->get_natural("forced_after", 864000) : 0), // 10 days default
197 mines_percent_(section ? section->get_int("mines_percent", 100) : 0)
198+
199 {
200 if (section) {
201 if (section->has_val("renews_map_resource"))
202@@ -41,4 +42,17 @@
203 if (section->has_val("mines"))
204 mines_ = section->get_string("mines");
205 }
206+ if (section) {
207+ if (!section->has_val("trainingsite_type")) {
208+ trainingsite_type_ = TrainingSiteType::kNoTS;
209+ } else {
210+ if (!strcmp(section ? section->get_string("trainingsite_type", "basic") : "basic", "basic")) {
211+ trainingsite_type_ = TrainingSiteType::kBasic;
212+ } else if (!strcmp(section ? section->get_string("trainingsite_type", "basic") : "basic", "advanced")) {
213+ trainingsite_type_ = TrainingSiteType::kAdvanced;
214+ } else {
215+ trainingsite_type_ = TrainingSiteType::kNoTS;
216+ }
217+ }
218+ }
219 }
220
221=== modified file 'src/ai/ai_hints.h'
222--- src/ai/ai_hints.h 2015-03-05 20:57:07 +0000
223+++ src/ai/ai_hints.h 2015-03-26 20:42:28 +0000
224@@ -24,9 +24,12 @@
225 #include <string>
226
227 #include "base/macros.h"
228+#include "base/log.h"
229
230 class Section;
231
232+enum class TrainingSiteType : uint8_t {kNoTS = 0, kBasic = 1, kAdvanced = 2};
233+
234 /// This struct is used to read out the data given in [aihints] section of a
235 /// buildings conf file. It is used to tell the computer player about the
236 /// special properties of a building.
237@@ -94,6 +97,10 @@
238 return mines_percent_;
239 }
240
241+ TrainingSiteType get_trainingsite_type() const {
242+ return trainingsite_type_;
243+ }
244+
245 private:
246 std::string renews_map_resource_;
247 std::string mines_;
248@@ -109,6 +116,7 @@
249 int32_t prohibited_till_;
250 int32_t forced_after_;
251 uint8_t mines_percent_;
252+ TrainingSiteType trainingsite_type_;
253
254 DISALLOW_COPY_AND_ASSIGN(BuildingHints);
255 };
256
257=== modified file 'src/ai/defaultai.cc'
258--- src/ai/defaultai.cc 2015-03-06 07:36:40 +0000
259+++ src/ai/defaultai.cc 2015-03-26 20:42:28 +0000
260@@ -51,12 +51,15 @@
261 #include "profile/profile.h"
262
263 // following is in miliseconds (widelands counts time in ms)
264-constexpr int kFieldUpdateInterval = 2000;
265-constexpr int kIdleMineUpdateInterval = 22000;
266+// constexpr int kFieldUpdateInterval = 2000;
267+constexpr int kFieldInfoExpiration = 12 * 1000;
268+constexpr int kMineFieldInfoExpiration = 20 * 1000;
269+constexpr int kNewMineConstInterval = 19000;
270 constexpr int kBusyMineUpdateInterval = 2000;
271 // building of the same building can be started after 25s at earliest
272 constexpr int kBuildingMinInterval = 25 * 1000;
273 constexpr int kMinBFCheckInterval = 5 * 1000;
274+constexpr int kMinMFCheckInterval = 19 * 1000;
275 constexpr int kShipCheckInterval = 5 * 1000;
276 constexpr int kMarineDecisionInterval = 20 * 1000;
277 constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
278@@ -85,6 +88,8 @@
279 num_milit_constructionsites(0),
280 num_prod_constructionsites(0),
281 num_ports(0),
282+ last_attacked_player_(std::numeric_limits<uint16_t>::max()),
283+ enemysites_check_delay_(60),
284 next_ai_think_(0),
285 next_mine_construction_due_(0),
286 inhibit_road_building_(0),
287@@ -92,20 +97,22 @@
288 enemy_last_seen_(0),
289 numof_warehouses_(0),
290 new_buildings_stop_(false),
291- resource_necessity_territory_(255),
292- resource_necessity_mines_(255),
293- resource_necessity_stones_(255),
294+ resource_necessity_territory_(100),
295+ resource_necessity_mines_(100),
296 resource_necessity_water_(0),
297 resource_necessity_water_needed_(false),
298 unstationed_milit_buildings_(0),
299 military_last_dismantle_(0),
300 military_last_build_(0),
301- last_attack_target_(
302- std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max()),
303- next_attack_waittime_(10),
304 seafaring_economy(false),
305 colony_scan_area_(35),
306- spots_(0) {
307+ spots_(0),
308+ vacant_mil_positions_(0),
309+ ts_basic_count_(0),
310+ ts_basic_const_count_(0),
311+ ts_advanced_count_(0),
312+ ts_advanced_const_count_(0),
313+ ts_without_trainers_(0) {
314
315 // Subscribe to NoteFieldPossession.
316 field_possession_subscriber_ =
317@@ -166,23 +173,23 @@
318 break;
319
320 case NoteShipMessage::Message::kLost:
321- for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
322+ for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
323 if (i->ship == note.ship) {
324 allships.erase(i);
325 break;
326 }
327 }
328- break;
329+ break;
330
331 case NoteShipMessage::Message::kWaitingForCommand:
332- for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
333+ for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
334 if (i->ship == note.ship) {
335 i->waiting_for_command_ = true;
336 break;
337 }
338 }
339- break;
340- default:
341+ break;
342+ default:
343 ;
344 }
345 });
346@@ -233,65 +240,94 @@
347 // (only one but some of them needs to run check_economies() to
348 // guarantee consistency)
349 // job names are selfexplanatory
350- if (DueTask == ScheduleTasks::kBbuildableFieldsCheck) {
351- update_all_buildable_fields(gametime);
352- taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
353- } else if (DueTask == ScheduleTasks::kRoadCheck) {
354- if (check_economies()) { // is a must
355- return;
356- };
357- taskDue[ScheduleTasks::kRoadCheck] = gametime + 400;
358- improve_roads(gametime);
359- } else if (DueTask == ScheduleTasks::kUnbuildableFCheck) {
360- taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
361- update_all_not_buildable_fields();
362- } else if (DueTask == ScheduleTasks::kConsiderAttack) {
363- consider_attack(gametime);
364- } else if (DueTask == ScheduleTasks::kCheckEconomies) {
365- check_economies();
366- taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
367- } else if (DueTask == ScheduleTasks::kProductionsitesStats) {
368- update_productionsite_stats(gametime);
369- } else if (DueTask == ScheduleTasks::kConstructBuilding) {
370- if (check_economies()) { // economies must be consistent
371- return;
372- }
373- taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000;
374- if (construct_building(gametime)) {
375- time_of_last_construction_ = gametime;
376- }
377- } else if (DueTask == ScheduleTasks::kCheckProductionsites) {
378- if (check_economies()) { // economies must be consistent
379- return;
380- }
381- check_productionsites(gametime);
382- taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000;
383- } else if (DueTask == ScheduleTasks::kCheckShips) {
384- check_ships(gametime);
385- } else if (DueTask == ScheduleTasks::KMarineDecisions) {
386- marine_main_decisions(gametime);
387- } else if (DueTask == ScheduleTasks::kCheckMines) {
388- if (check_economies()) { // economies must be consistent
389- return;
390- }
391- taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough
392- check_mines_(gametime);
393- } else if (DueTask == ScheduleTasks::kCheckMilitarysites) {
394- check_militarysites(gametime);
395- } else if (DueTask == ScheduleTasks::kCheckTrainingsites) {
396- check_trainingsites(gametime);
397- } else if (DueTask == ScheduleTasks::kWareReview) {
398- if (check_economies()) { // economies must be consistent
399- return;
400- }
401- taskDue[ScheduleTasks::kWareReview] = gametime + 15 * 60 * 1000;
402- review_wares_targets(gametime);
403- } else if (DueTask == ScheduleTasks::kPrintStats) {
404- if (check_economies()) { // economies must be consistent
405- return;
406- }
407- print_stats(gametime);
408- }
409+ switch (DueTask) {
410+ case ScheduleTasks::kBbuildableFieldsCheck :
411+ update_all_buildable_fields(gametime);
412+ taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
413+ break;
414+ case ScheduleTasks::kMineableFieldsCheck :
415+ update_all_mineable_fields(gametime);
416+ taskDue[ScheduleTasks::kMineableFieldsCheck] = gametime + kMinMFCheckInterval;
417+ break;
418+ case ScheduleTasks::kRoadCheck :
419+ if (check_economies()) { // is a must
420+ return;
421+ };
422+ taskDue[ScheduleTasks::kRoadCheck] = gametime + 400;
423+ improve_roads(gametime);
424+ break;
425+ case ScheduleTasks::kUnbuildableFCheck :
426+ taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
427+ update_all_not_buildable_fields();
428+ break;
429+ case ScheduleTasks::kCheckEconomies :
430+ check_economies();
431+ taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
432+ break;
433+ case ScheduleTasks::kProductionsitesStats :
434+ update_productionsite_stats(gametime);
435+ break;
436+ case ScheduleTasks::kConstructBuilding :
437+ if (check_economies()) { // economies must be consistent
438+ return;
439+ }
440+ taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000;
441+ if (construct_building(gametime)) {
442+ time_of_last_construction_ = gametime;
443+ }
444+ break;
445+ case ScheduleTasks::kCheckProductionsites :
446+ if (check_economies()) { // economies must be consistent
447+ return;
448+ }
449+ check_productionsites(gametime);
450+ taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000;
451+ break;
452+ case ScheduleTasks::kCheckShips :
453+ check_ships(gametime);
454+ break;
455+ case ScheduleTasks::KMarineDecisions :
456+ marine_main_decisions(gametime);
457+ break;
458+ case ScheduleTasks::kCheckMines :
459+ if (check_economies()) { // economies must be consistent
460+ return;
461+ }
462+ taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough
463+ check_mines_(gametime);
464+ break;
465+ case ScheduleTasks::kCheckMilitarysites :
466+ check_militarysites(gametime);
467+ break;
468+ case ScheduleTasks::kCheckTrainingsites :
469+ check_trainingsites(gametime);
470+ break;
471+ case ScheduleTasks::kCountMilitaryVacant :
472+ count_military_vacant_positions();
473+ taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000;
474+ break;
475+ case ScheduleTasks::kWareReview :
476+ if (check_economies()) { // economies must be consistent
477+ return;
478+ }
479+ taskDue[ScheduleTasks::kWareReview] = gametime + 15 * 60 * 1000;
480+ review_wares_targets(gametime);
481+ break;
482+ case ScheduleTasks::kPrintStats :
483+ if (check_economies()) { // economies must be consistent
484+ return;
485+ }
486+ print_stats(gametime);
487+ break;
488+ case ScheduleTasks::kCheckEnemySites :
489+ check_enemy_sites(gametime);
490+ taskDue[ScheduleTasks::kCheckEnemySites] = gametime + 19 * 1000;
491+ break;
492+ case ScheduleTasks::kIdle :
493+ break;
494+ default:
495+ ;
496+ }
497 }
498
499 /**
500@@ -340,6 +376,7 @@
501 bo.production_hint_ = -1;
502 bo.current_stats_ = 0;
503 bo.unoccupied_ = false;
504+ bo.unconnected_ = 0;
505 bo.is_buildable_ = bld.is_buildable();
506 bo.need_trees_ = bh.is_logproducer();
507 bo.need_stones_ = bh.is_stoneproducer();
508@@ -353,6 +390,8 @@
509 bo.prohibited_till_ = bh.get_prohibited_till() * 1000; // value in conf is in seconds
510 bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds
511 bo.is_port_ = bld.get_isport();
512+ bo.trainingsite_type_ = TrainingSiteType::kNoTS;
513+
514 if (bh.renews_map_resource()) {
515 bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource());
516 }
517@@ -382,6 +421,11 @@
518 }
519
520 bo.mines_percent_ = bh.get_mines_percent();
521+
522+ // populating mines_per_type map
523+ if (mines_per_type.count(bo.mines_) == 0) {
524+ mines_per_type[bo.mines_] = MineTypesObserver();
525+ }
526 }
527
528 // here we identify hunters
529@@ -439,6 +483,10 @@
530 for (const WareAmount& temp_input : train.inputs()) {
531 bo.inputs_.push_back(temp_input.first);
532 }
533+ bo.trainingsite_type_ = bh.get_trainingsite_type();
534+ // it would behave badly if no type was set
535+ // make sure all TS have its type set properly in conf files
536+ assert(bo.trainingsite_type_ != TrainingSiteType::kNoTS);
537 continue;
538 }
539
540@@ -544,12 +592,14 @@
541 taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
542 taskDue[ScheduleTasks::kCheckEconomies] = 1000;
543 taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
544- taskDue[ScheduleTasks::kConsiderAttack] = 300000;
545 taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
546 taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
547+ taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
548 taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
549 taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000;
550 taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
551+ taskDue[ScheduleTasks::kCountMilitaryVacant] = 10 * 60 * 1000;
552+ taskDue[ScheduleTasks::kCheckEnemySites] = 10 * 60 * 1000;
553 }
554
555 /**
556@@ -562,7 +612,10 @@
557
558 uint16_t i = 0;
559
560- while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&
561+ // we test 40 fields that were update more than 1 seconds ago
562+ while (!buildable_fields.empty() &&
563+ (buildable_fields.front()->field_info_expiration_ - kFieldInfoExpiration + 1000) <=
564+ gametime &&
565 i < 40) {
566 BuildableField& bf = *buildable_fields.front();
567
568@@ -582,7 +635,7 @@
569 }
570
571 update_buildable_field(bf);
572- bf.next_update_due_ = gametime + kFieldUpdateInterval;
573+ bf.field_info_expiration_ = gametime + kFieldInfoExpiration;
574 buildable_fields.push_back(&bf);
575 buildable_fields.pop_front();
576
577@@ -600,8 +653,12 @@
578
579 uint16_t i = 0; // counter, used to track # of checked fields
580
581- while (!mineable_fields.empty() && mineable_fields.front()->next_update_due_ <= gametime &&
582- i < 40) {
583+ // we test 30 fields that were updated more than 1 seconds ago
584+ // to avoid re-test of the same field twice
585+ while (!mineable_fields.empty() &&
586+ (mineable_fields.front()->field_info_expiration_ - kMineFieldInfoExpiration + 1000) <=
587+ gametime &&
588+ i < 30) {
589 MineableField* mf = mineable_fields.front();
590
591 // check whether we lost ownership of the node
592@@ -620,7 +677,7 @@
593 }
594
595 update_mineable_field(*mf);
596- mf->next_update_due_ = gametime + kFieldUpdateInterval; // in fact this has very small effect
597+ mf->field_info_expiration_ = gametime + kMineFieldInfoExpiration;
598 mineable_fields.push_back(mf);
599 mineable_fields.pop_front();
600
601@@ -672,32 +729,38 @@
602 void DefaultAI::update_buildable_field(BuildableField& field, uint16_t range, bool military) {
603 // look if there is any unowned land nearby
604 Map& map = game().map();
605+ const int32_t gametime = game().get_gametime();
606 FindNodeUnowned find_unowned(player_, game());
607 FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
608 PlayerNumber const pn = player_->player_number();
609 const World& world = game().world();
610 field.unowned_land_nearby_ =
611 map.find_fields(Area<FCoords>(field.coords, range), nullptr, find_unowned);
612+ FindNodeAllyOwned find_ally(player_, game(), player_number());
613+ const int32_t AllyOwnedFields =
614+ map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally);
615
616 field.near_border_ = false;
617- if (field.unowned_land_nearby_ > 0) {
618+ if (AllyOwnedFields > 0) {
619+ field.near_border_ = true;
620+ } else if (field.unowned_land_nearby_ > 0) {
621 if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned) > 0) {
622 field.near_border_ = true;
623 }
624 }
625
626 // to save some CPU
627- if ((mines_.size() > 8 && game().get_gametime() % 3 > 0) || field.unowned_land_nearby_ == 0) {
628+ if ((mines_.size() > 8 && gametime % 3 > 0) || field.unowned_land_nearby_ == 0) {
629 field.unowned_mines_pots_nearby_ = 0;
630 } else {
631 uint32_t close_mines =
632 map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_mines_pots);
633 uint32_t distant_mines =
634- map.find_fields(Area<FCoords>(field.coords, (range + 6 < 12) ? 12 : range + 6),
635+ map.find_fields(Area<FCoords>(field.coords, (range + 6 < 14) ? 14 : range + 6),
636 nullptr,
637 find_unowned_mines_pots);
638 distant_mines = distant_mines - close_mines;
639- field.unowned_mines_pots_nearby_ = 3 * close_mines + distant_mines / 2;
640+ field.unowned_mines_pots_nearby_ = 4 * close_mines + distant_mines / 2;
641 if (distant_mines > 0) {
642 field.unowned_mines_pots_nearby_ += 15;
643 }
644@@ -710,7 +773,7 @@
645 }
646 }
647
648- // testing for near porspaces
649+ // testing for near portspaces
650 if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {
651 field.portspace_nearby_ = ExtendedBool::kFalse;
652 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));
653@@ -745,9 +808,9 @@
654 int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
655 field.preferred_ = false;
656 field.enemy_nearby_ = false;
657- field.military_capacity_ = 0;
658+ field.area_military_capacity_ = 0;
659 field.military_loneliness_ = 1000; // instead of floats(v-
660- field.military_presence_ = 0;
661+ field.area_military_presence_ = 0;
662 field.military_stationed_ = 0;
663 field.trees_nearby_ = 0;
664 field.space_consumers_nearby_ = 0;
665@@ -772,8 +835,7 @@
666 }
667
668 // counting fields with fish
669- if (field.water_nearby_ > 0 &&
670- (field.fish_nearby_ == -1 || game().get_gametime() % 10 == 0)) {
671+ if (field.water_nearby_ > 0 && (field.fish_nearby_ == -1 || gametime % 10 == 0)) {
672 map.find_fields(Area<FCoords>(field.coords, 6),
673 &resource_list,
674 FindNodeResource(world.get_resource("fish")));
675@@ -782,7 +844,7 @@
676
677 // counting fields with critters (game)
678 // not doing this always, this does not change fast
679- if (game().get_gametime() % 10 == 0) {
680+ if (gametime % 10 == 0) {
681 map.find_bobs(Area<FCoords>(field.coords, 6), &critters_list, FindBobCritter());
682 field.critters_nearby_ = critters_list.size();
683 }
684@@ -808,7 +870,7 @@
685 if (player_->is_hostile(player_immovable->owner())) {
686 field.enemy_nearby_ = true;
687 }
688- enemy_last_seen_ = game().get_gametime();
689+ enemy_last_seen_ = gametime;
690
691 continue;
692 }
693@@ -840,7 +902,7 @@
694 }
695
696 // stones are not renewable, we will count them only if previous state si nonzero
697- if (field.stones_nearby_ > 0) {
698+ if (field.stones_nearby_ > 0 && gametime % 3 == 0) {
699
700 int32_t const stone_attr = MapObjectDescr::get_attribute_id("granite");
701 field.stones_nearby_ = 0;
702@@ -863,13 +925,13 @@
703
704 // we get immovables with higher radius
705 immovables.clear();
706- map.find_immovables(Area<FCoords>(field.coords, (range < 10) ? 10 : range), &immovables);
707+ map.find_immovables(Area<FCoords>(field.coords, (range < 11) ? 11 : range), &immovables);
708 field.military_stationed_ = 0;
709 field.military_unstationed_ = 0;
710 field.military_in_constr_nearby_ = 0;
711- field.military_capacity_ = 0;
712+ field.area_military_capacity_ = 0;
713 field.military_loneliness_ = 1000;
714- field.military_presence_ = 0;
715+ field.area_military_presence_ = 0;
716
717 for (uint32_t i = 0; i < immovables.size(); ++i) {
718
719@@ -890,6 +952,7 @@
720 }
721 }
722
723+ // if we are here, immovable is ours
724 if (upcast(Building const, building, &base_immovable)) {
725 if (upcast(ConstructionSite const, constructionsite, building)) {
726 const BuildingDescr& target_descr = constructionsite->building();
727@@ -899,7 +962,8 @@
728 const int32_t radius = target_ms_d->get_conquers() + 4;
729
730 if (radius > dist) {
731- field.military_capacity_ += target_ms_d->get_max_number_of_soldiers() / 2 + 1;
732+ field.area_military_capacity_ +=
733+ target_ms_d->get_max_number_of_soldiers() / 2 + 1;
734 field.military_loneliness_ *= static_cast<double_t>(dist) / radius;
735 field.military_in_constr_nearby_ += 1;
736 }
737@@ -912,8 +976,8 @@
738
739 if (radius > dist) {
740
741- field.military_capacity_ += militarysite->max_soldier_capacity();
742- field.military_presence_ += militarysite->stationed_soldiers().size();
743+ field.area_military_capacity_ += militarysite->max_soldier_capacity();
744+ field.area_military_presence_ += militarysite->stationed_soldiers().size();
745
746 if (militarysite->stationed_soldiers().empty()) {
747 field.military_unstationed_ += 1;
748@@ -935,7 +999,7 @@
749 Map& map = game().map();
750 map.find_immovables(Area<FCoords>(field.coords, 5), &immovables);
751 field.preferred_ = false;
752- field.mines_nearby_ = 1;
753+ field.mines_nearby_ = 0;
754 FCoords fse;
755 map.get_brn(field.coords, &fse);
756
757@@ -948,11 +1012,20 @@
758
759 for (const ImmovableFound& temp_immovable : immovables) {
760 if (upcast(Building const, bld, temp_immovable.object)) {
761+ if (player_number() != bld->owner().player_number()) {
762+ continue;
763+ }
764 if (bld->descr().get_ismine()) {
765- ++field.mines_nearby_;
766+ if (get_building_observer(bld->descr().name().c_str()).mines_ ==
767+ field.coords.field->get_resources()) {
768+ ++field.mines_nearby_;
769+ }
770 } else if (upcast(ConstructionSite const, cs, bld)) {
771 if (cs->building().get_ismine()) {
772- ++field.mines_nearby_;
773+ if (get_building_observer(cs->building().name().c_str()).mines_ ==
774+ field.coords.field->get_resources()) {
775+ ++field.mines_nearby_;
776+ }
777 }
778 }
779 }
780@@ -976,23 +1049,34 @@
781 for (uint32_t i = 0; i < buildings_.size(); ++i) {
782 buildings_.at(i).current_stats_ = 0;
783 buildings_.at(i).unoccupied_ = false;
784+ buildings_.at(i).unconnected_ = 0;
785 }
786
787 // Check all available productionsites
788 for (uint32_t i = 0; i < productionsites.size(); ++i) {
789 assert(productionsites.front().bo->cnt_built_ > 0);
790- // Add statistics value
791- productionsites.front().bo->current_stats_ +=
792- productionsites.front().site->get_crude_statistics();
793-
794- // counting fishers
795- if (productionsites.front().bo->is_fisher_) {
796- fishers_count += 1;
797+ // is connected
798+ const bool connected_to_wh =
799+ !productionsites.front().site->get_economy()->warehouses().empty();
800+
801+ // unconnected buildings are excluded from statistics review
802+ if (connected_to_wh) {
803+ // Add statistics value
804+ productionsites.front().bo->current_stats_ +=
805+ productionsites.front().site->get_crude_statistics();
806+
807+ // counting fishers
808+ if (productionsites.front().bo->is_fisher_) {
809+ fishers_count += 1;
810+ }
811+
812+ // Check whether this building is completely occupied
813+ productionsites.front().bo->unoccupied_ |=
814+ !productionsites.front().site->can_start_working();
815+ } else {
816+ productionsites.front().bo->unconnected_ += 1;
817 }
818
819- // Check whether this building is completely occupied
820- productionsites.front().bo->unoccupied_ |= !productionsites.front().site->can_start_working();
821-
822 // Now reorder the buildings
823 productionsites.push_back(productionsites.front());
824 productionsites.pop_front();
825@@ -1000,11 +1084,11 @@
826
827 if (resource_necessity_water_needed_) {
828 if (fishers_count == 0) {
829- resource_necessity_water_ = 255;
830+ resource_necessity_water_ = 100;
831 } else if (fishers_count == 1) {
832- resource_necessity_water_ = 150;
833+ resource_necessity_water_ = 50;
834 } else {
835- resource_necessity_water_ = 18;
836+ resource_necessity_water_ = 10;
837 }
838 }
839
840@@ -1012,10 +1096,20 @@
841 // Check all available mines
842 for (uint32_t i = 0; i < mines_.size(); ++i) {
843 assert(mines_.front().bo->cnt_built_ > 0);
844- // Add statistics value
845- mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent();
846- // Check whether this building is completely occupied
847- mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working();
848+
849+ const bool connected_to_wh =
850+ !productionsites.front().site->get_economy()->warehouses().empty();
851+
852+ // unconnected mines are excluded from statistics review
853+ if (connected_to_wh) {
854+ // Add statistics value
855+ mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent();
856+ // Check whether this building is completely occupied
857+ mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working();
858+ } else {
859+ buildings_.at(i).unconnected_ += 1;
860+ }
861+
862 // Now reorder the buildings
863 mines_.push_back(mines_.front());
864 mines_.pop_front();
865@@ -1023,8 +1117,9 @@
866
867 // Scale statistics down
868 for (uint32_t i = 0; i < buildings_.size(); ++i) {
869- if (buildings_.at(i).cnt_built_ > 0) {
870- buildings_.at(i).current_stats_ /= buildings_.at(i).cnt_built_;
871+ if ((buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_) > 0) {
872+ buildings_.at(i).current_stats_ /=
873+ (buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_);
874 }
875 }
876 }
877@@ -1100,54 +1195,48 @@
878
879 // sometimes there is too many military buildings in construction, so we must
880 // prevent initialization of further buildings start
881- const uint32_t treshold = militarysites.size() / 40 + 2;
882-
883- if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) {
884+ const int32_t vacant_plus_in_construction_minus_prod =
885+ vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 15;
886+ if (vacant_plus_in_construction_minus_prod > 20) {
887 expansion_mode = MilitaryStrategy::kNoNewMilitary;
888- } else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) {
889+ } else if (vacant_plus_in_construction_minus_prod > 13) {
890 expansion_mode = MilitaryStrategy::kDefenseOnly;
891- } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= treshold - 1) {
892+ } else if (vacant_plus_in_construction_minus_prod > 6) {
893 expansion_mode = MilitaryStrategy::kResourcesOrDefense;
894- } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
895- expansion_mode = MilitaryStrategy::kExpansion;
896 } else {
897- expansion_mode = MilitaryStrategy::kPushExpansion;
898+ if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
899+ expansion_mode = MilitaryStrategy::kExpansion;
900+ } else {
901+ expansion_mode = MilitaryStrategy::kPushExpansion;
902+ }
903 }
904
905 // we must consider need for mines
906 // set necessity for mines
907 // we use 'virtual mines', because also mine spots can be changed
908 // to mines when AI decides so
909- const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15;
910- if (virtual_mines <= 7) {
911- resource_necessity_mines_ = std::numeric_limits<uint8_t>::max();
912- } else if (virtual_mines > 19) {
913- resource_necessity_mines_ = 50;
914+ const int32_t virtual_mines =
915+ mines_.size() + mineable_fields.size() / 15 - productionsites.size() / 25;
916+ resource_necessity_mines_ = 100 * (15 - virtual_mines) / 15;
917+ resource_necessity_mines_ = (resource_necessity_mines_ > 100) ? 100 : resource_necessity_mines_;
918+ resource_necessity_mines_ = (resource_necessity_mines_ < 20) ? 10 : resource_necessity_mines_;
919+
920+ // here we calculate how badly we need to expand, result is number (0-100)
921+ // like a percent
922+ if (spots_ == 0) {
923+ resource_necessity_territory_ = 100;
924 } else {
925- const uint32_t tmp = (24 - virtual_mines) * 10;
926- resource_necessity_mines_ = tmp;
927- }
928-
929- // here we calculate a need for expansion and reduce necessity for new land
930- // the game has two stages:
931- // First: virtual mines<=5 - stage of building the economics
932- // Second: virtual mines>5 - teritorial expansion
933- if (virtual_mines <= 5) {
934- if (spots_avail.at(BUILDCAPS_BIG) <= 4) {
935- resource_necessity_territory_ = 255;
936- } else {
937- resource_necessity_territory_ = 0;
938+ resource_necessity_territory_ = 100 * 5 * (productionsites.size() + 5) / spots_;
939+ resource_necessity_territory_ =
940+ (resource_necessity_territory_ > 100) ? 100 : resource_necessity_territory_;
941+ resource_necessity_territory_ =
942+ (resource_necessity_territory_ < 10) ? 10 : resource_necessity_territory_;
943+ // alse we need at lest 4 big spots
944+ if (spots_avail.at(BUILDCAPS_BIG) < 2) {
945+ resource_necessity_territory_ = 100;
946 }
947- } else { // or we have enough mines and regulate speed of expansion
948- if (spots_ == 0) {
949- resource_necessity_territory_ = 255;
950- } else {
951- const uint32_t tmp = 255 * 4 * productionsites.size() / spots_;
952- if (tmp > 255) {
953- resource_necessity_territory_ = 255;
954- } else {
955- resource_necessity_territory_ = tmp;
956- }
957+ if (spots_avail.at(BUILDCAPS_MEDIUM) < 4) {
958+ resource_necessity_territory_ = 100;
959 }
960 }
961
962@@ -1205,7 +1294,7 @@
963 ++i) {
964 BuildableField* const bf = *i;
965
966- if (bf->next_update_due_ < gametime - 10000) {
967+ if (bf->field_info_expiration_ < gametime) {
968 continue;
969 }
970
971@@ -1369,7 +1458,7 @@
972 continue;
973 }
974
975- if (bo.total_count() == 0) {
976+ if (bo.total_count() - bo.unconnected_ == 0) {
977 prio += 150;
978 }
979
980@@ -1394,12 +1483,15 @@
981 continue;
982 }
983
984- if (new_buildings_stop_) {
985+ if (gametime > 5 * 60 * 1000 &&
986+ (bo.total_count() - bo.unconnected_ == 0)) {
987+ prio += 10;
988+ } else if (new_buildings_stop_) {
989 continue;
990 }
991
992 prio +=
993- (bf->critters_nearby_ * 2) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
994+ (bf->critters_nearby_ * 3) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
995
996 } else if (bo.is_fisher_) { // fisher
997
998@@ -1490,7 +1582,7 @@
999 prio -= bf->space_consumers_nearby_ * 5;
1000
1001 } else { // FISH BREEDERS and GAME KEEPERS
1002- if (new_buildings_stop_ && bo.total_count() > 0) {
1003+ if (new_buildings_stop_ && (bo.total_count() - bo.unconnected_) > 0) {
1004 continue;
1005 }
1006
1007@@ -1502,7 +1594,7 @@
1008 prio += bf->water_nearby_ / 5;
1009 }
1010
1011- if (bo.total_count() > bo.cnt_target_) {
1012+ if ((bo.total_count() - bo.unconnected_) > bo.cnt_target_) {
1013 continue;
1014 }
1015
1016@@ -1543,9 +1635,10 @@
1017 continue;
1018 }
1019
1020- if (bo.forced_after_ < gametime && bo.total_count() == 0) {
1021+ if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
1022 prio += 150;
1023- } else if (bo.cnt_built_ == 1 && game().get_gametime() > 40 * 60 * 1000 &&
1024+ } else if ((bo.cnt_built_ - bo.unconnected_) == 1 &&
1025+ game().get_gametime() > 40 * 60 * 1000 &&
1026 bo.desc->enhancement() != INVALID_INDEX && !mines_.empty()) {
1027 prio += 10;
1028 } else if (bo.is_shipyard_) {
1029@@ -1554,9 +1647,10 @@
1030 }
1031 } else if (!output_is_needed) {
1032 continue;
1033- } else if (bo.cnt_built_ == 0 && game().get_gametime() > 40 * 60 * 1000) {
1034+ } else if ((bo.cnt_built_ - bo.unconnected_) == 0 &&
1035+ game().get_gametime() > 40 * 60 * 1000) {
1036 prio += kDefaultPrioBoost;
1037- } else if (bo.cnt_built_ > 1 && bo.current_stats_ > 97) {
1038+ } else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
1039 prio -= kDefaultPrioBoost * (new_buildings_stop_);
1040 } else if (new_buildings_stop_)
1041 continue;
1042@@ -1593,17 +1687,25 @@
1043
1044 else if (bo.is_shipyard_) {
1045 // for now AI builds only one shipyard
1046- if (bf->water_nearby_ > 3 && bo.total_count() == 0 && seafaring_economy) {
1047+ if (bf->water_nearby_ > 3 && (bo.total_count() - bo.unconnected_) == 0 &&
1048+ seafaring_economy) {
1049 prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_;
1050 }
1051
1052 } else if (!bo.inputs_.empty()) {
1053- if (bo.total_count() == 0) {
1054+ if ((bo.total_count() - bo.unconnected_ == 0)) {
1055 prio += max_needed_preciousness + kDefaultPrioBoost;
1056 }
1057- if (bo.cnt_built_ > 0 && bo.current_stats_ > 70) {
1058+ if ((bo.cnt_built_ - bo.unconnected_) > 0
1059+ &&
1060+ //due to very badly designed statistics and the way how
1061+ //productionsites are working we must distinguish how many
1062+ //outputs the site has.
1063+ ((bo.outputs_.size() == 1 && bo.current_stats_ > 75)
1064+ ||
1065+ (bo.outputs_.size() > 1 && bo.current_stats_ > 55))) {
1066 prio += max_needed_preciousness + kDefaultPrioBoost - 3 +
1067- (bo.current_stats_ - 70) / 5;
1068+ (bo.current_stats_ - 55) / 8;
1069 }
1070 }
1071
1072@@ -1623,7 +1725,6 @@
1073 }
1074 } // production sites done
1075 else if (bo.type == BuildingObserver::MILITARYSITE) {
1076-
1077 // we allow 1 exemption from big buildings prohibition
1078 if (bo.build_material_shortage_ &&
1079 (bo.cnt_under_construction_ > 0 || !(bf->enemy_nearby_))) {
1080@@ -1631,6 +1732,7 @@
1081 }
1082
1083 if (!bf->unowned_land_nearby_) {
1084+
1085 continue;
1086 }
1087
1088@@ -1648,8 +1750,7 @@
1089
1090 if (expansion_mode == MilitaryStrategy::kResourcesOrDefense &&
1091 !(bf->unowned_mines_pots_nearby_ || bf->stones_nearby_ || bf->water_nearby_ ||
1092- (bf->distant_water_ && resource_necessity_water_needed_) ||
1093- bf->enemy_nearby_)) {
1094+ (bf->distant_water_ && resource_necessity_water_needed_) || bf->enemy_nearby_)) {
1095 continue;
1096 }
1097
1098@@ -1667,31 +1768,40 @@
1099 if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {
1100 continue;
1101 };
1102- }
1103- else {
1104+ } else {
1105 continue;
1106 } // the building is not suitable for situation
1107-
1108 // not to build so many military buildings nearby
1109 if (!bf->enemy_nearby_ &&
1110 (bf->military_in_constr_nearby_ + bf->military_unstationed_) > 0) {
1111 continue;
1112 }
1113-
1114 // a boost to prevent an expansion halt
1115 int32_t local_boost = 0;
1116 if (expansion_mode == MilitaryStrategy::kPushExpansion) {
1117 local_boost = 200;
1118 }
1119
1120- prio = ((bf->unowned_land_nearby_ * 2 * resource_necessity_territory_) / 255 +
1121- (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 255 +
1122- bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost +
1123- (bf->water_nearby_ * resource_necessity_water_) / 255);
1124+ // priority based on basic resources
1125+ prio = ((bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 100 +
1126+ bf->stones_nearby_ + bf->military_loneliness_ / 10 - 40 + local_boost +
1127+ (bf->water_nearby_ * resource_necessity_water_) / 100);
1128+
1129+ // Depending on wheter resource only are considered or no
1130+ if (expansion_mode == MilitaryStrategy::kResourcesOrDefense) {
1131+ prio *= 2;
1132+ prio += bf->unowned_land_nearby_ * resource_necessity_territory_ / 100 / 2;
1133+ } else { // addding score for territory
1134+ prio += (bf->unowned_land_nearby_ * resource_necessity_territory_) / 100 * 3 / 2;
1135+ }
1136+
1137+ // adding score for distance to other military sites
1138+ prio += bf->military_loneliness_ / 10 - 40;
1139
1140 // special bonus due to remote water for atlanteans
1141- if (resource_necessity_water_needed_)
1142- prio += (bf->distant_water_ * resource_necessity_water_) / 255;
1143+ if (resource_necessity_water_needed_) {
1144+ prio += (bf->distant_water_ * resource_necessity_water_) / 100 / 2;
1145+ }
1146
1147 // special bonus if a portspace is close
1148 if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
1149@@ -1702,6 +1812,11 @@
1150 }
1151 }
1152
1153+ //special bonus for bigger buildings if enemy is nearby
1154+ if (bf->enemy_nearby_) {
1155+ prio += (bo.desc->get_size() - 1) * 25;
1156+ }
1157+
1158 if (bo.desc->get_size() < maxsize) {
1159 prio = prio - 5;
1160 } // penalty
1161@@ -1713,12 +1828,12 @@
1162 // for expansion)
1163 const int16_t bottom_treshold =
1164 15 - ((virtual_mines <= 4) ? (5 - virtual_mines) * 2 : 0);
1165- if (bf->enemy_nearby_ && bf->military_capacity_ < bottom_treshold) {
1166- prio += 50 + (bottom_treshold - bf->military_capacity_) * 20;
1167+ if (bf->enemy_nearby_ && bf->area_military_capacity_ < bottom_treshold) {
1168+ prio += 50 + (bottom_treshold - bf->area_military_capacity_) * 20;
1169 }
1170
1171- if (bf->enemy_nearby_ && bf->military_capacity_ > bottom_treshold + 4) {
1172- prio -= (bf->military_capacity_ - (bottom_treshold + 4)) * 5;
1173+ if (bf->enemy_nearby_ && bf->area_military_capacity_ > bottom_treshold + 4) {
1174+ prio -= (bf->area_military_capacity_ - (bottom_treshold + 4)) * 5;
1175 }
1176
1177 } else if (bo.type == BuildingObserver::WAREHOUSE) {
1178@@ -1780,7 +1895,7 @@
1179
1180 // take care about and enemies
1181 if (bf->enemy_nearby_) {
1182- prio /= 2;
1183+ prio /= 4;
1184 }
1185
1186 if (bf->unowned_land_nearby_ && !bo.is_port_) {
1187@@ -1789,24 +1904,43 @@
1188
1189 } else if (bo.type == BuildingObserver::TRAININGSITE) {
1190
1191- if (virtual_mines < 5) {
1192- continue;
1193- }
1194-
1195 // exclude spots on border
1196 if (bf->near_border_) {
1197 continue;
1198 }
1199
1200- if (virtual_mines < 3) {
1201- continue;
1202- }
1203-
1204- // build after 20 production sites and then after each 50 production site
1205- if (static_cast<int32_t>((productionsites.size() + 40) / 60) > bo.total_count() &&
1206- bo.cnt_under_construction_ == 0) {
1207- prio = 4 + kDefaultPrioBoost;
1208- }
1209+ // it is a bit difficult to get a new trainer.....
1210+ if (ts_without_trainers_) {
1211+ continue;
1212+ }
1213+
1214+ // target is only one for both types
1215+ if ((ts_basic_const_count_ + ts_advanced_const_count_) > 0) {
1216+ continue;
1217+ }
1218+
1219+ // we build one training site for 100 militarysites
1220+ if (bo.trainingsite_type_ == TrainingSiteType::kBasic &&
1221+ militarysites.size() / 100 < static_cast<int32_t>(ts_basic_count_)) {
1222+ continue;
1223+ }
1224+ // we build one training site for 100 militarysites
1225+ if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced &&
1226+ militarysites.size() / 100 < static_cast<int32_t>(ts_advanced_count_)) {
1227+ continue;
1228+ }
1229+
1230+ // for type1 we need 15 productionsties
1231+ if (bo.trainingsite_type_ == TrainingSiteType::kBasic && productionsites.size() < 15) {
1232+ continue;
1233+ }
1234+
1235+ // for type2 we need 4 mines
1236+ if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced && virtual_mines < 4) {
1237+ continue;
1238+ }
1239+
1240+ prio = 4 + kDefaultPrioBoost;
1241
1242 // take care about borders and enemies
1243 if (bf->enemy_nearby_) {
1244@@ -1854,8 +1988,9 @@
1245 // then try all mines_ - as soon as basic economy is build up.
1246 if (gametime > next_mine_construction_due_) {
1247
1248- update_all_mineable_fields(gametime);
1249- next_mine_construction_due_ = gametime + kIdleMineUpdateInterval;
1250+ // not done here
1251+ // update_all_mineable_fields(gametime);
1252+ next_mine_construction_due_ = gametime + kNewMineConstInterval;
1253
1254 if (!mineable_fields.empty()) {
1255
1256@@ -1885,22 +2020,33 @@
1257 check_ware_necessity(
1258 bo, &output_is_needed, &max_preciousness, &max_needed_preciousness);
1259
1260- if (!output_is_needed && bo.total_count() > 0) {
1261+ if (!output_is_needed && (bo.total_count() - bo.unconnected_) > 0) {
1262 continue;
1263 }
1264
1265 // if current one(s) are performing badly
1266- if (bo.total_count() >= 1 && bo.current_stats_ < 50) {
1267+ if ((bo.total_count() - bo.unconnected_) >= 1 && bo.current_stats_ < 50) {
1268 continue;
1269 }
1270
1271 // this is penalty if there are existing mines too close
1272 // it is treated as multiplicator for count of near mines
1273 uint32_t nearness_penalty = 0;
1274- if ((bo.cnt_built_ + bo.cnt_under_construction_) == 0) {
1275+ if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
1276+ 0) {
1277 nearness_penalty = 0;
1278 } else {
1279- nearness_penalty = 10;
1280+ nearness_penalty = 30;
1281+ }
1282+
1283+ // bonus score to prefer if too few mines
1284+ uint32_t bonus_score = 0;
1285+ if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
1286+ 0) {
1287+ bonus_score = 15;
1288+ } else if ((mines_per_type[bo.mines_].in_construction +
1289+ mines_per_type[bo.mines_].finished) == 1) {
1290+ bonus_score = 5;
1291 }
1292
1293 // iterating over fields
1294@@ -1908,26 +2054,46 @@
1295 j != mineable_fields.end();
1296 ++j) {
1297
1298- if ((*j)->coords.field->get_resources() != bo.mines_) {
1299- continue;
1300- }
1301-
1302- int32_t prio = (*j)->coords.field->get_resources_amount();
1303+ MineableField* const mf = *j;
1304+
1305+ if (mf->field_info_expiration_ <= gametime) {
1306+ continue;
1307+ }
1308+
1309+ if (mf->coords.field->get_resources() != bo.mines_) {
1310+ continue;
1311+ }
1312+
1313+ int32_t prio = 0;
1314+ MapRegion<Area<FCoords>> mr(map, Area<FCoords>(mf->coords, 2));
1315+ do {
1316+ if (bo.mines_ == mr.location().field->get_resources()) {
1317+ prio += mr.location().field->get_resources_amount();
1318+ }
1319+ } while (mr.advance(map));
1320+
1321+ prio /= 10;
1322+
1323+ // Only build mines_ on locations where some material can be mined
1324+ if (prio < 1) {
1325+ continue;
1326+ }
1327
1328 // applying nearnes penalty
1329- prio = prio - (*j)->mines_nearby_ * nearness_penalty;
1330-
1331- // Only build mines_ on locations where some material can be mined
1332- if (prio < 2) {
1333- continue;
1334- }
1335+ prio -= mf->mines_nearby_ * nearness_penalty;
1336+
1337+ // applying bonus score
1338+ prio += bonus_score;
1339+
1340+ // applying max needed
1341+ prio += max_needed_preciousness * 3;
1342
1343 // prefer mines in the middle of mine fields of the
1344 // same type, so we add a small bonus here
1345 // depending on count of same mines nearby,
1346 // though this does not reflects how many resources
1347 // are (left) in nearby mines
1348- prio += (*j)->same_mine_fields_nearby_ / 3;
1349+ prio += mf->same_mine_fields_nearby_;
1350
1351 // Continue if field is blocked at the moment
1352 bool blocked = false;
1353@@ -1946,13 +2112,12 @@
1354 }
1355
1356 // Prefer road side fields
1357- prio += (*j)->preferred_ ? 1 : 0;
1358+ prio += mf->preferred_ ? 1 : 0;
1359
1360 if (prio > proposed_priority) {
1361- // proposed_building = bo.id;
1362 best_building = &bo;
1363 proposed_priority = prio;
1364- proposed_coords = (*j)->coords;
1365+ proposed_coords = mf->coords;
1366 mine = true;
1367 }
1368 } // end of evaluation of field
1369@@ -2257,11 +2422,11 @@
1370
1371 if (occupied_military_) {
1372 eco->dismantle_grace_time_ =
1373- (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
1374+ (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
1375
1376 } else { // for other normal buildings
1377 eco->dismantle_grace_time_ =
1378- gametime + (5 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
1379+ gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
1380 }
1381 }
1382
1383@@ -2547,6 +2712,9 @@
1384 site.unoccupied_till_ = game().get_gametime();
1385 }
1386
1387+ // is it connected to wh at all?
1388+ const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
1389+
1390 // do not dismantle or upgrade the same type of building too soon - to give some time to update
1391 // statistics
1392 if (site.bo->last_dismantle_time_ > game().get_gametime() - 30 * 1000) {
1393@@ -2571,7 +2739,8 @@
1394 // b) if there are two buildings
1395 // statistics percents are decisive
1396 const BuildingIndex enhancement = site.site->descr().enhancement();
1397- if (enhancement != INVALID_INDEX && (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
1398+ if (connected_to_wh && enhancement != INVALID_INDEX &&
1399+ (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
1400
1401 BuildingIndex enbld = INVALID_INDEX; // to get rid of this
1402
1403@@ -2664,7 +2833,11 @@
1404 // so finally we dismantle the lumberjac
1405 site.bo->last_dismantle_time_ = game().get_gametime();
1406 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1407- game().send_player_dismantle(*site.site);
1408+ if (connected_to_wh) {
1409+ game().send_player_dismantle(*site.site);
1410+ } else {
1411+ game().send_player_bulldoze(*site.site);
1412+ }
1413
1414 return true;
1415 }
1416@@ -2675,7 +2848,11 @@
1417 site.site->get_statistics_percent() == 0) {
1418 site.bo->last_dismantle_time_ = gametime;
1419 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1420- game().send_player_dismantle(*site.site);
1421+ if (connected_to_wh) {
1422+ game().send_player_dismantle(*site.site);
1423+ } else {
1424+ game().send_player_bulldoze(*site.site);
1425+ }
1426
1427 return true;
1428 }
1429@@ -2695,7 +2872,11 @@
1430 if (site.bo->stocklevel_ > 250 + productionsites.size() * 5) { // dismantle
1431 site.bo->last_dismantle_time_ = game().get_gametime();
1432 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1433- game().send_player_dismantle(*site.site);
1434+ if (connected_to_wh) {
1435+ game().send_player_dismantle(*site.site);
1436+ } else {
1437+ game().send_player_bulldoze(*site.site);
1438+ }
1439 return true;
1440 }
1441
1442@@ -2706,7 +2887,7 @@
1443 if (site.bo->need_stones_) {
1444
1445 if (map.find_immovables(
1446- Area<FCoords>(map.get_fcoords(site.site->get_position()), radius),
1447+ Area<FCoords>(map.get_fcoords(site.site->get_position()), 6),
1448 nullptr,
1449
1450 FindImmovableAttribute(MapObjectDescr::get_attribute_id("granite"))) == 0) {
1451@@ -2714,7 +2895,11 @@
1452 // the destruction of the flag avoids that defaultAI will have too many
1453 // unused roads - if needed the road will be rebuild directly.
1454 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1455- game().send_player_dismantle(*site.site);
1456+ if (connected_to_wh) {
1457+ game().send_player_dismantle(*site.site);
1458+ } else {
1459+ game().send_player_bulldoze(*site.site);
1460+ }
1461 return true;
1462 }
1463
1464@@ -2723,7 +2908,11 @@
1465 // it is possible that there are stones but quary is not able to mine them
1466 site.bo->last_dismantle_time_ = game().get_gametime();
1467 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1468- game().send_player_dismantle(*site.site);
1469+ if (connected_to_wh) {
1470+ game().send_player_dismantle(*site.site);
1471+ } else {
1472+ game().send_player_bulldoze(*site.site);
1473+ }
1474
1475 return true;
1476 }
1477@@ -2743,7 +2932,7 @@
1478 site.bo->space_consumer_ && !site.bo->plants_trees_) {
1479
1480 // if we have more buildings then target
1481- if (site.bo->cnt_built_ > site.bo->cnt_target_) {
1482+ if ((site.bo->cnt_built_ - site.bo->unconnected_) > site.bo->cnt_target_) {
1483 if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
1484 site.bo->stocklevel_ = get_stocklevel(*site.bo);
1485 site.bo->stocklevel_time = game().get_gametime();
1486@@ -2753,7 +2942,11 @@
1487 site.bo->stocklevel_ > 100) { // production stats == 0%
1488 site.bo->last_dismantle_time_ = game().get_gametime();
1489 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1490- game().send_player_dismantle(*site.site);
1491+ if (connected_to_wh) {
1492+ game().send_player_dismantle(*site.site);
1493+ } else {
1494+ game().send_player_bulldoze(*site.site);
1495+ }
1496 return true;
1497 }
1498 }
1499@@ -2762,7 +2955,11 @@
1500 if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built_ > 1) {
1501
1502 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1503- game().send_player_dismantle(*site.site);
1504+ if (connected_to_wh) {
1505+ game().send_player_dismantle(*site.site);
1506+ } else {
1507+ game().send_player_bulldoze(*site.site);
1508+ }
1509 return true;
1510 }
1511
1512@@ -2778,7 +2975,11 @@
1513
1514 site.bo->last_dismantle_time_ = game().get_gametime();
1515 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1516- game().send_player_dismantle(*site.site);
1517+ if (connected_to_wh) {
1518+ game().send_player_dismantle(*site.site);
1519+ } else {
1520+ game().send_player_bulldoze(*site.site);
1521+ }
1522 return true;
1523 }
1524
1525@@ -2792,7 +2993,11 @@
1526
1527 site.bo->last_dismantle_time_ = game().get_gametime();
1528 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1529- game().send_player_dismantle(*site.site);
1530+ if (connected_to_wh) {
1531+ game().send_player_dismantle(*site.site);
1532+ } else {
1533+ game().send_player_bulldoze(*site.site);
1534+ }
1535 return true;
1536 }
1537
1538@@ -2813,16 +3018,29 @@
1539
1540 site.bo->last_dismantle_time_ = game().get_gametime();
1541 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1542- game().send_player_dismantle(*site.site);
1543+ if (connected_to_wh) {
1544+ game().send_player_dismantle(*site.site);
1545+ } else {
1546+ game().send_player_bulldoze(*site.site);
1547+ }
1548 return true;
1549 }
1550
1551 if (score > 120 && !site.site->is_stopped()) {
1552
1553 game().send_player_start_stop_building(*site.site);
1554+ return true;
1555 }
1556+ const uint32_t trees_in_vicinity =
1557+ map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5),
1558+ nullptr,
1559+ FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
1560
1561- if (score < 80 && site.site->is_stopped()) {
1562+ if (trees_in_vicinity > 25) {
1563+ if (!site.site->is_stopped()) {
1564+ game().send_player_start_stop_building(*site.site);
1565+ }
1566+ } else if (score < 80 && site.site->is_stopped()) {
1567
1568 game().send_player_start_stop_building(*site.site);
1569 }
1570@@ -3043,11 +3261,17 @@
1571 // Get link to productionsite that should be checked
1572 ProductionSiteObserver& site = mines_.front();
1573
1574+ const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
1575+
1576 // first get rid of mines that are missing workers for some time (6 minutes),
1577 // released worker (if any) can be usefull elsewhere !
1578 if (site.built_time_ + 6 * 60 * 1000 < gametime && !site.site->can_start_working()) {
1579 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1580- game().send_player_dismantle(*site.site);
1581+ if (connected_to_wh) {
1582+ game().send_player_dismantle(*site.site);
1583+ } else {
1584+ game().send_player_bulldoze(*site.site);
1585+ }
1586 return true;
1587 }
1588
1589@@ -3059,7 +3283,11 @@
1590 // dismantling when the failed count is too high
1591 if (site.no_resources_count > 12) {
1592 flags_to_be_removed.push_back(site.site->base_flag().get_position());
1593- game().send_player_dismantle(*site.site);
1594+ if (connected_to_wh) {
1595+ game().send_player_dismantle(*site.site);
1596+ } else {
1597+ game().send_player_bulldoze(*site.site);
1598+ }
1599 site.bo->construction_decision_time_ = gametime;
1600 return true;
1601 }
1602@@ -3084,6 +3312,11 @@
1603 return false;
1604 }
1605
1606+ if (!connected_to_wh) {
1607+ // no enhancement possible
1608+ return false;
1609+ }
1610+
1611 bool changed = false;
1612 if (player_->is_building_type_allowed(enhancement)) {
1613 // first exclude possibility there are enhancements in construction or unoccupied_
1614@@ -3212,6 +3445,18 @@
1615 return count;
1616 }
1617
1618+// this just counts free positions in military and training sites
1619+void DefaultAI::count_military_vacant_positions() {
1620+ // counting vacant positions
1621+ vacant_mil_positions_ = 0;
1622+ for (TrainingSiteObserver tso : trainingsites) {
1623+ vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size();
1624+ }
1625+ for (MilitarySiteObserver mso : militarysites) {
1626+ vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
1627+ }
1628+}
1629+
1630 // this function only manipulates with trainingsites' inputs priority
1631 // decreases it when too many unoccupied military buildings
1632 bool DefaultAI::check_trainingsites(uint32_t gametime) {
1633@@ -3221,22 +3466,38 @@
1634 if (!trainingsites.empty()) {
1635 taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval;
1636 } else {
1637- taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 5 * kTrainingSitesCheckInterval;
1638- }
1639-
1640- uint8_t new_priority = DEFAULT_PRIORITY;
1641- if (unstationed_milit_buildings_ > 2) {
1642- new_priority = LOW_PRIORITY;
1643- } else {
1644- new_priority = DEFAULT_PRIORITY;
1645- }
1646+ taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 2 * kTrainingSitesCheckInterval;
1647+ return false;
1648+ }
1649+
1650+ TrainingSite* ts = trainingsites.front().site;
1651+ TrainingSiteObserver& tso = trainingsites.front();
1652+
1653+ const BuildingIndex enhancement = ts->descr().enhancement();
1654+
1655+ if (enhancement != INVALID_INDEX && ts_without_trainers_ == 0 && mines_.size() > 3 &&
1656+ (ts_basic_const_count_ + ts_advanced_const_count_) == 0 && ts_advanced_count_ > 0) {
1657+
1658+ if (player_->is_building_type_allowed(enhancement)) {
1659+ game().send_player_enhance_building(*tso.site, enhancement);
1660+ }
1661+ }
1662+
1663+ trainingsites.push_back(trainingsites.front());
1664+ trainingsites.pop_front();
1665+
1666+ // changing capacity
1667+ if (tso.site->soldier_capacity() != 2) {
1668+ game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity());
1669+ }
1670+
1671+ ts_without_trainers_ = 0; // zeroing
1672 for (std::list<TrainingSiteObserver>::iterator site = trainingsites.begin();
1673 site != trainingsites.end();
1674 ++site) {
1675
1676- for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) {
1677- game().send_player_set_ware_priority(
1678- *site->site, wwWARE, site->bo->inputs_.at(k), new_priority);
1679+ if (!site->site->can_start_working()) {
1680+ ts_without_trainers_ += 1;
1681 }
1682 }
1683 return true;
1684@@ -3310,14 +3571,18 @@
1685 BuildableField bf(f);
1686 update_buildable_field(bf, vision, true);
1687 const int32_t size_penalty = ms->get_size() - 1;
1688+ FindNodeAllyOwned find_ally(player_, game(), player_number());
1689+ const int32_t allyOwnedFields =
1690+ map.find_fields(Area<FCoords>(f, vision), nullptr, find_ally);
1691
1692 int16_t score = 0;
1693- score += (bf.military_capacity_ > 5);
1694- score += (bf.military_presence_ > 3);
1695+ score += (bf.area_military_capacity_ > 6);
1696+ score += (bf.area_military_capacity_ > 18);
1697+ score += (bf.area_military_presence_ > 4);
1698 score += (bf.military_loneliness_ < 180);
1699- score += (bf.military_stationed_ > (2 + size_penalty));
1700- score -= (ms->soldier_capacity() * 2 > static_cast<uint32_t>(bf.military_capacity_));
1701- score += (bf.unowned_land_nearby_ < 10);
1702+ score += (bf.military_stationed_ > 2);
1703+ score -= size_penalty;
1704+ score += ((bf.unowned_land_nearby_ + allyOwnedFields) < 10);
1705
1706 if (score >= 4) {
1707 if (ms->get_playercaps() & Widelands::Building::PCap_Dismantle) {
1708@@ -3340,7 +3605,6 @@
1709
1710 // yes enemy is nearby, but still we must distinguish whether
1711 // he is accessible (over the land)
1712-
1713 if (other_player_accessible(
1714 vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) {
1715
1716@@ -3502,7 +3766,10 @@
1717 }
1718 }
1719
1720- throw wexception("Help: I do not know what to do with a %s", name);
1721+ throw wexception("Help: I (player %d / tribe %s) do not know what to do with a %s",
1722+ player_number(),
1723+ tribe_->name().c_str(),
1724+ name);
1725 }
1726
1727 // this is called whenever we gain ownership of a PlayerImmovable
1728@@ -3804,6 +4071,7 @@
1729
1730 // this is called whenever we gain a new building
1731 void DefaultAI::gain_building(Building& b) {
1732+
1733 BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
1734
1735 if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
1736@@ -3817,6 +4085,17 @@
1737 if (target_bo.type == BuildingObserver::MILITARYSITE) {
1738 ++num_milit_constructionsites;
1739 }
1740+ if (target_bo.type == BuildingObserver::MINE) {
1741+ mines_per_type[target_bo.mines_].in_construction += 1;
1742+ }
1743+ if (target_bo.type == BuildingObserver::TRAININGSITE) {
1744+ if (target_bo.trainingsite_type_ == TrainingSiteType::kBasic) {
1745+ ts_basic_const_count_ += 1;
1746+ }
1747+ if (target_bo.trainingsite_type_ == TrainingSiteType::kAdvanced) {
1748+ ts_advanced_const_count_ += 1;
1749+ }
1750+ }
1751
1752 // Let defaultAI try to directly connect the constructionsite
1753 taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime();
1754@@ -3852,6 +4131,9 @@
1755
1756 for (uint32_t i = 0; i < bo.inputs_.size(); ++i)
1757 ++wares.at(bo.inputs_.at(i)).consumers_;
1758+
1759+ mines_per_type[bo.mines_].finished += 1;
1760+
1761 } else if (bo.type == BuildingObserver::MILITARYSITE) {
1762 militarysites.push_back(MilitarySiteObserver());
1763 militarysites.back().site = &dynamic_cast<MilitarySite&>(b);
1764@@ -3860,9 +4142,16 @@
1765 militarysites.back().enemies_nearby_ = true;
1766
1767 } else if (bo.type == BuildingObserver::TRAININGSITE) {
1768+ ts_without_trainers_ += 1;
1769 trainingsites.push_back(TrainingSiteObserver());
1770 trainingsites.back().site = &dynamic_cast<TrainingSite&>(b);
1771 trainingsites.back().bo = &bo;
1772+ if (bo.trainingsite_type_ == TrainingSiteType::kBasic) {
1773+ ts_basic_count_ += 1;
1774+ }
1775+ if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced) {
1776+ ts_advanced_count_ += 1;
1777+ }
1778
1779 } else if (bo.type == BuildingObserver::WAREHOUSE) {
1780 ++numof_warehouses_;
1781@@ -3888,6 +4177,7 @@
1782
1783 // this is called whenever we lose a building
1784 void DefaultAI::lose_building(const Building& b) {
1785+
1786 BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
1787
1788 if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
1789@@ -3901,6 +4191,17 @@
1790 if (target_bo.type == BuildingObserver::MILITARYSITE) {
1791 --num_milit_constructionsites;
1792 }
1793+ if (target_bo.type == BuildingObserver::MINE) {
1794+ mines_per_type[target_bo.mines_].in_construction -= 1;
1795+ }
1796+ if (target_bo.type == BuildingObserver::TRAININGSITE) {
1797+ if (target_bo.trainingsite_type_ == TrainingSiteType::kBasic) {
1798+ ts_basic_const_count_ -= 1;
1799+ }
1800+ if (target_bo.trainingsite_type_ == TrainingSiteType::kAdvanced) {
1801+ ts_advanced_const_count_ -= 1;
1802+ }
1803+ }
1804
1805 } else {
1806 --bo.cnt_built_;
1807@@ -3938,6 +4239,9 @@
1808 for (uint32_t i = 0; i < bo.inputs_.size(); ++i) {
1809 --wares.at(bo.inputs_.at(i)).consumers_;
1810 }
1811+
1812+ mines_per_type[bo.mines_].finished -= 1;
1813+
1814 } else if (bo.type == BuildingObserver::MILITARYSITE) {
1815
1816 for (std::list<MilitarySiteObserver>::iterator i = militarysites.begin();
1817@@ -3955,6 +4259,12 @@
1818 ++i) {
1819 if (i->site == &b) {
1820 trainingsites.erase(i);
1821+ if (bo.trainingsite_type_ == TrainingSiteType::kBasic) {
1822+ ts_basic_count_ -= 1;
1823+ }
1824+ if (bo.trainingsite_type_ == TrainingSiteType::kAdvanced) {
1825+ ts_advanced_count_ -= 1;
1826+ }
1827 break;
1828 }
1829 }
1830@@ -3999,49 +4309,21 @@
1831 return supplied == bo.inputs_.size();
1832 }
1833
1834-/**
1835- * The defaultAi "considers" via this function whether to attack an
1836- * enemy, if opposing military buildings are in sight. In case of an attack it
1837- * sends all available forces.
1838- *
1839- * \returns true, if attack was started.
1840- */
1841-
1842-bool DefaultAI::consider_attack(int32_t const gametime) {
1843-
1844- // we assume that we are not attacking so we extend waitperiod
1845- // in case of attack the variable will be decreased below
1846- // this is intended to save some CPU and add randomness in attacking
1847- // and also differentiate according to type
1848- next_attack_waittime_ += gametime % 30;
1849- if (next_attack_waittime_ > 600 && type_ == DEFENSIVE) {
1850- next_attack_waittime_ = 20;
1851- }
1852- if (next_attack_waittime_ > 450 && type_ == NORMAL) {
1853- next_attack_waittime_ = 20;
1854- }
1855- if (next_attack_waittime_ > 300 && type_ == AGGRESSIVE) {
1856- next_attack_waittime_ = 20;
1857- }
1858-
1859- // Only useable, if it owns at least one militarysite
1860- if (militarysites.empty()) {
1861- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
1862- return false;
1863- }
1864-
1865- // First we iterate over all players and define which ones (if any)
1866- // are attackable (comparing overal strength)
1867- // counting players in game
1868- uint32_t plr_in_game = 0;
1869+bool DefaultAI::check_enemy_sites(uint32_t const gametime) {
1870+
1871+ Map& map = game().map();
1872+
1873+ // define which players are attackable
1874 std::vector<bool> player_attackable;
1875- PlayerNumber const nr_players = game().map().get_nrplayers();
1876+ PlayerNumber const nr_players = map.get_nrplayers();
1877 player_attackable.resize(nr_players);
1878- bool any_attackable = false;
1879+ uint32_t plr_in_game = 0;
1880 uint16_t const pn = player_number();
1881- std::unordered_set<uint32_t> irrelevant_immovables;
1882-
1883- std::vector<ImmovableFound> target_buildings;
1884+
1885+ iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
1886+
1887+ // receiving games statistics and parsing it (reading latest entry)
1888+ const Game::GeneralStatsVector& genstats = game().get_general_statistics();
1889
1890 // defining treshold ratio of own_strenght/enemy's_strength
1891 uint32_t treshold_ratio = 100;
1892@@ -4052,37 +4334,10 @@
1893 treshold_ratio = 120;
1894 }
1895
1896- iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
1897-
1898- // receiving games statistics and parsing it (reading latest entry)
1899- const Game::GeneralStatsVector& genstats = game().get_general_statistics();
1900-
1901- // first we try to prevent exhaustion of military forces (soldiers)
1902- // via excessive attacking
1903- // before building an economy with mines.
1904- // 'Margin' is an difference between count of actual soldiers and
1905- // military sites to be manned.
1906- // If we have no mines yet, we need to preserve some soldiers for further
1907- // expansion (if enemy allows this)
1908- // TODO(sirver): this next line is completely unreadable and maybe even wrong given the
1909- // precedence of ?: and +. Replace through some if/else.
1910- int32_t needed_margin = (mines_.size() < 6) ?
1911- ((6 - mines_.size()) * 3) :
1912- 0 + 2 + ((type_ == NORMAL) ? 4 : 0 + ((type_ == DEFENSIVE) ? 8 : 0));
1913- const int32_t current_margin =
1914- genstats[pn - 1].miltary_strength.back() - militarysites.size() - num_milit_constructionsites;
1915-
1916- if (current_margin < needed_margin) { // no attacking!
1917- last_attack_target_.x = std::numeric_limits<uint16_t>::max();
1918- last_attack_target_.y = std::numeric_limits<uint16_t>::max();
1919- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
1920- return false;
1921- }
1922-
1923 // now we test all players which one are 'attackable'
1924 for (uint8_t j = 1; j <= plr_in_game; ++j) {
1925- if (pn == j) {
1926- player_attackable.at(j - 1) = false;
1927+ if (pn == j) { // its me
1928+ player_attackable[j - 1] = false;
1929 continue;
1930 }
1931
1932@@ -4095,12 +4350,10 @@
1933 // Avoid division by zero
1934 } else if (genstats.at(j - 1).miltary_strength.back() == 0) {
1935 player_attackable.at(j - 1) = true;
1936- any_attackable = true;
1937 // Check threshold
1938 } else if ((genstats.at(pn - 1).miltary_strength.back() * 100 /
1939 genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) {
1940 player_attackable.at(j - 1) = true;
1941- any_attackable = true;
1942 } else {
1943 player_attackable.at(j - 1) = false;
1944 }
1945@@ -4112,147 +4365,229 @@
1946 }
1947 }
1948
1949- // if we cannot attack anybody, terminating...
1950- if (!any_attackable) {
1951- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
1952- last_attack_target_.x = std::numeric_limits<uint16_t>::max();
1953- last_attack_target_.y = std::numeric_limits<uint16_t>::max();
1954- return false;
1955- }
1956-
1957- // the logic of attacking is to pick n own military buildings - random ones
1958- // and test the vicinity for attackable buildings
1959- // candidates are put into target_buildings vector for later processing
1960- const uint16_t test_every = 4;
1961- Map& map = game().map();
1962- MilitarySite* best_ms_target = nullptr;
1963- Warehouse* best_wh_target = nullptr;
1964- int32_t best_ms_score = 0;
1965- int32_t best_wh_score = 0;
1966- const int8_t minimal_difference = 2;
1967-
1968- for (uint32_t position = gametime % test_every; position < militarysites.size();
1969- position += test_every) {
1970-
1971- std::list<MilitarySiteObserver>::iterator mso = militarysites.begin();
1972- std::advance(mso, position);
1973-
1974- MilitarySite* ms = mso->site;
1975-
1976- if (!mso->enemies_nearby_) {
1977- continue;
1978- }
1979-
1980+ // first we scan vicitnity of couple of militarysites to get new enemy sites
1981+ // militarysites rotate
1982+ int32_t i = 0;
1983+ for (MilitarySiteObserver mso : militarysites) {
1984+ i += 1;
1985+ if (i % 4 == 0)
1986+ continue;
1987+ if (i > 20)
1988+ continue;
1989+
1990+ MilitarySite* ms = mso.site;
1991 uint32_t const vision = ms->descr().vision_range();
1992 FCoords f = map.get_fcoords(ms->get_position());
1993
1994 // get list of immovable around this our military site
1995 std::vector<ImmovableFound> immovables;
1996- map.find_immovables(Area<FCoords>(f, vision + 3), &immovables, FindImmovableAttackable());
1997+ map.find_immovables(Area<FCoords>(f, (vision + 3 < 13) ? 13 : vision + 3),
1998+ &immovables,
1999+ FindImmovableAttackable());
2000
2001 for (uint32_t j = 0; j < immovables.size(); ++j) {
2002-
2003- // skip if in irrelevant_immovables
2004- const uint32_t hash = coords_hash(immovables.at(j).coords);
2005- if (irrelevant_immovables.count(hash) == 0) {
2006- irrelevant_immovables.insert(hash);
2007- }
2008-
2009- // maybe these are not good candidates to attack
2010- if (upcast(MilitarySite, bld, immovables.at(j).object)) {
2011-
2012- if (!player_attackable[bld->owner().player_number() - 1]) {
2013- continue;
2014- }
2015-
2016- // in case this is the same building as previously attacked
2017- if (last_attack_target_ == bld->get_position()) {
2018- continue;
2019- }
2020-
2021+ if (upcast(MilitarySite const, bld, immovables.at(j).object)) {
2022+ if (player_->is_hostile(bld->owner())) {
2023+ if (enemy_sites.count(coords_hash(bld->get_position())) == 0) {
2024+ enemy_sites[coords_hash(bld->get_position())] = EnemySiteObserver();
2025+ }
2026+ }
2027+ }
2028+ if (upcast(Warehouse const, wh, immovables.at(j).object)) {
2029+ if (player_->is_hostile(wh->owner())) {
2030+ if (enemy_sites.count(coords_hash(wh->get_position())) == 0) {
2031+ enemy_sites[coords_hash(wh->get_position())] = EnemySiteObserver();
2032+ }
2033+ }
2034+ }
2035+ }
2036+ }
2037+
2038+ // now we update some of them
2039+ uint32_t best_target = std::numeric_limits<uint32_t>::max();
2040+ uint8_t best_score = 0;
2041+ uint32_t count = 0;
2042+
2043+ for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
2044+ site != enemy_sites.end();
2045+ ++site) {
2046+
2047+ // we test max 12 sites and prefer ones tested more then 1 min ago
2048+ if (((site->second.last_tested + (enemysites_check_delay_ * 1000)) > gametime && count > 4) ||
2049+ count > 12) {
2050+ continue;
2051+ }
2052+ count += 1;
2053+
2054+ site->second.last_tested = gametime;
2055+ uint8_t defenders = 0;
2056+ bool is_warehouse = false;
2057+ bool is_attackable = false;
2058+ uint16_t onwer_number = 100;
2059+
2060+ // testing if we can attack the building - result is a flag
2061+ // if we dont get a flag, we remove the building from observers list
2062+ FCoords f = map.get_fcoords(coords_unhash(site->first));
2063+ uint32_t site_to_be_removed = std::numeric_limits<uint32_t>::max();
2064+ Flag* flag = nullptr;
2065+ if (upcast(MilitarySite, bld, f.field->get_immovable())) {
2066+ if (player_->is_hostile(bld->owner())) {
2067+ defenders = bld->present_soldiers().size();
2068+ flag = &bld->base_flag();
2069 if (bld->can_attack()) {
2070-
2071- int32_t attack_soldiers = player_->find_attack_soldiers(bld->base_flag());
2072- if (attack_soldiers < 1) {
2073- continue;
2074- }
2075-
2076- const int32_t soldiers_difference =
2077- player_->find_attack_soldiers(bld->base_flag()) - bld->present_soldiers().size();
2078-
2079- if (soldiers_difference < minimal_difference)
2080- continue;
2081- if (soldiers_difference <= best_ms_score)
2082- continue;
2083-
2084- best_ms_target = bld;
2085- best_ms_score = soldiers_difference;
2086- continue;
2087- }
2088- } else if (upcast(Warehouse, wh, immovables.at(j).object)) {
2089- if (!player_->is_hostile(wh->owner())) {
2090- continue;
2091- }
2092-
2093- // in case this is the same building as previously attacked
2094- if (last_attack_target_ == wh->get_position()) {
2095- continue;
2096- }
2097-
2098- if (wh->can_attack()) {
2099- int32_t attack_soldiers = player_->find_attack_soldiers(wh->base_flag());
2100- if (attack_soldiers < 1) {
2101- continue;
2102- }
2103-
2104- const int32_t soldiers_difference = player_->find_attack_soldiers(wh->base_flag()) -
2105- wh->present_soldiers().size() +
2106- 3; //+3 is to boost attack here
2107-
2108- if (soldiers_difference < minimal_difference)
2109- continue;
2110- if (soldiers_difference <= best_wh_score)
2111- continue;
2112-
2113- best_wh_target = wh;
2114- best_wh_score = soldiers_difference;
2115- }
2116- }
2117- }
2118- }
2119-
2120- // we always try to attack warehouse first
2121- if (best_wh_target != nullptr && gametime % 2 == 0) {
2122- // attacking with all attack-ready soldiers
2123- int32_t attackers = player_->find_attack_soldiers(best_wh_target->base_flag());
2124-
2125- game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers);
2126- last_attack_target_ = best_wh_target->get_position();
2127- taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
2128- next_attack_waittime_ = 10;
2129- return true;
2130-
2131- } else if (best_ms_target != nullptr) {
2132-
2133- // attacking with defenders + 6 soldiers
2134- int32_t attackers = player_->find_attack_soldiers(best_ms_target->base_flag());
2135- const int32_t defenders = best_ms_target->present_soldiers().size();
2136- if (attackers > defenders + 10) { // we need to leave meaningful count of soldiers
2137- // for next attack
2138- attackers = defenders + 6;
2139- }
2140-
2141- game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers);
2142- last_attack_target_ = best_ms_target->get_position();
2143- taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
2144- next_attack_waittime_ = 10;
2145- return true;
2146+ is_attackable = true;
2147+ }
2148+ onwer_number = bld->owner().player_number();
2149+ }
2150+ }
2151+ if (upcast(Warehouse, Wh, f.field->get_immovable())) {
2152+ if (player_->is_hostile(Wh->owner())) {
2153+ defenders = Wh->present_soldiers().size();
2154+ flag = &Wh->base_flag();
2155+ is_warehouse = true;
2156+ if (Wh->can_attack()) {
2157+ is_attackable = true;
2158+ }
2159+ onwer_number = Wh->owner().player_number();
2160+ }
2161+ }
2162+
2163+ // if flag is defined it is a good taget
2164+ if (flag) {
2165+ // updating some info
2166+ // updating info on mines nearby if needed
2167+ if (site->second.mines_nearby == ExtendedBool::kUnset) {
2168+ FindNodeMineable find_mines_spots_nearby(game(), f.field->get_resources());
2169+ const int32_t minescount =
2170+ map.find_fields(Area<FCoords>(f, 6), nullptr, find_mines_spots_nearby);
2171+ if (minescount > 0) {
2172+ site->second.mines_nearby = ExtendedBool::kTrue;
2173+ } else {
2174+ site->second.mines_nearby = ExtendedBool::kFalse;
2175+ }
2176+ }
2177+
2178+ site->second.warehouse = is_warehouse;
2179+
2180+ // getting rid of default
2181+ if (site->second.last_time_attackable == std::numeric_limits<uint32_t>::max()) {
2182+ site->second.last_time_attackable = gametime;
2183+ }
2184+
2185+ // can we attack:
2186+ if (is_attackable) {
2187+ site->second.attack_soldiers = player_->find_attack_soldiers(*flag);
2188+ } else {
2189+ site->second.attack_soldiers = 0;
2190+ }
2191+
2192+ site->second.defenders = defenders;
2193+
2194+ if (site->second.attack_soldiers > 0) {
2195+ site->second.score = site->second.attack_soldiers - site->second.defenders / 2;
2196+
2197+ if (!is_warehouse)
2198+ site->second.score -= 1;
2199+
2200+ // here is some differentiation based on "character" of a player
2201+ if (type_ == NORMAL) {
2202+ site->second.score -= 1;
2203+ site->second.score -= vacant_mil_positions_ / 10;
2204+ } else if (type_ == DEFENSIVE) {
2205+ site->second.score -= 2;
2206+ site->second.score -= vacant_mil_positions_ / 5;
2207+ } else { //=AGRESSIVE
2208+ site->second.score -= vacant_mil_positions_ / 15;
2209+ }
2210+ if (site->second.mines_nearby == ExtendedBool::kFalse) {
2211+ site->second.score -= 1;
2212+ }
2213+ // we dont want to attack multiple players at the same time too eagerly
2214+ if (onwer_number != last_attacked_player_) {
2215+ site->second.score -= 3;
2216+ }
2217+ // if we dont have mines yet
2218+ if (mines_.size() <= 2) {
2219+ site->second.score -= 2;
2220+ }
2221+ // also we should have at least some training sites
2222+ if ((ts_basic_count_ + ts_advanced_count_) == 0) {
2223+ site->second.score -= 2;
2224+ }
2225+ // treating no attack score
2226+ if (site->second.no_attack_counter < 0) {
2227+ site->second.score = 0;
2228+ site->second.no_attack_counter += 1;
2229+ }
2230+ } else {
2231+ site->second.score = 0;
2232+ } // or the score will remain 0
2233+
2234+ if (site->second.score > 0 && player_attackable[onwer_number - 1]) {
2235+ if (site->second.score > best_score) {
2236+ best_score = site->second.score;
2237+ best_target = site->first;
2238+ }
2239+ }
2240+
2241+ if (site->second.attack_soldiers > 0) {
2242+ site->second.last_time_attackable = gametime;
2243+ }
2244+ if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) {
2245+ site_to_be_removed = site->first;
2246+ }
2247+ } else { // we dont have a flag, let remove the site from out observer list
2248+ site_to_be_removed = site->first;
2249+ }
2250+
2251+ if (site_to_be_removed < std::numeric_limits<uint32_t>::max()) {
2252+ enemy_sites.erase(site_to_be_removed);
2253+ continue;
2254+ }
2255+ }
2256+
2257+ // modifying enemysites_check_delay_,this depends on the count
2258+ // of enemysites in observer
2259+ if (count >= 13 && enemysites_check_delay_ < 180) {
2260+ enemysites_check_delay_ += 3;
2261+ }
2262+ if (count < 10 && enemysites_check_delay_ > 45) {
2263+ enemysites_check_delay_ -= 2;
2264+ }
2265+
2266+ // if coordinates hash is not set
2267+ if (best_target == std::numeric_limits<uint32_t>::max()) {
2268+ return false;
2269+ }
2270+
2271+ // attacking
2272+ FCoords f = map.get_fcoords(coords_unhash(best_target));
2273+ // setting no attack counter here
2274+ // this gauranties that it will not be attacked in next 4
2275+ // turns
2276+ enemy_sites[best_target].no_attack_counter = -4;
2277+
2278+ Flag* flag = nullptr; // flag of a building to be attacked
2279+ if (upcast(MilitarySite, bld, f.field->get_immovable())) {
2280+ flag = &bld->base_flag();
2281+ } else if (upcast(Warehouse, Wh, f.field->get_immovable())) {
2282+ flag = &Wh->base_flag();
2283 } else {
2284- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
2285- last_attack_target_.x = std::numeric_limits<uint16_t>::max();
2286- last_attack_target_.y = std::numeric_limits<uint16_t>::max();
2287+ return false; // this should not happen
2288+ }
2289+
2290+ // how many attack soldiers we can send?
2291+ uint32_t attackers = player_->find_attack_soldiers(*flag);
2292+ // Just add some randomness
2293+ attackers -= gametime % 3;
2294+ if (attackers <= 0) {
2295 return false;
2296 }
2297+
2298+ game().send_player_enemyflagaction(*flag, player_number(), attackers);
2299+ last_attacked_player_ = flag->owner().player_number();
2300+
2301+ return true;
2302 }
2303
2304 // This runs once in 15 minutes, and adjust wares targets based on number of
2305@@ -4286,7 +4621,7 @@
2306 // run over dueTasks map and returns task with lower duetime
2307 DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) {
2308
2309- uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
2310+ uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
2311 ScheduleTasks DueTask = ScheduleTasks::kIdle; // default
2312 taskDue[ScheduleTasks::kIdle] = gametime;
2313
2314
2315=== modified file 'src/ai/defaultai.h'
2316--- src/ai/defaultai.h 2015-03-05 20:57:07 +0000
2317+++ src/ai/defaultai.h 2015-03-26 20:42:28 +0000
2318@@ -9,6 +9,7 @@
2319 * This program is distributed in the hope that it will be useful,
2320 * but WITHOUT ANY WARRANTY; without even the implied warranty of
2321 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2322+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2323 * GNU General Public License for more details.
2324 *
2325 * You should have received a copy of the GNU General Public License
2326@@ -82,9 +83,9 @@
2327 enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
2328 enum class ScheduleTasks : uint8_t {
2329 kBbuildableFieldsCheck,
2330+ kMineableFieldsCheck,
2331 kRoadCheck,
2332 kUnbuildableFCheck,
2333- kConsiderAttack,
2334 kCheckEconomies,
2335 kProductionsitesStats,
2336 kConstructBuilding,
2337@@ -96,7 +97,9 @@
2338 kPrintStats,
2339 kIdle,
2340 kCheckMilitarysites,
2341- kCheckTrainingsites
2342+ kCheckTrainingsites,
2343+ kCountMilitaryVacant,
2344+ kCheckEnemySites
2345 };
2346 enum class MilitaryStrategy : uint8_t {
2347 kNoNewMilitary,
2348@@ -161,7 +164,6 @@
2349 int16_t* max_preciousness,
2350 int16_t* max_needed_preciousness);
2351
2352-
2353 ScheduleTasks get_oldest_task(uint32_t);
2354
2355 bool construct_building(uint32_t);
2356@@ -195,12 +197,14 @@
2357 bool check_militarysites(uint32_t);
2358 bool marine_main_decisions(uint32_t);
2359 bool check_ships(uint32_t);
2360+ bool check_enemy_sites(uint32_t);
2361 void print_stats(uint32_t);
2362 uint32_t get_stocklevel_by_hint(size_t);
2363 uint32_t get_stocklevel(BuildingObserver&);
2364 uint32_t get_warehoused_stock(Widelands::WareIndex wt);
2365 uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_
2366 void review_wares_targets(uint32_t);
2367+ void count_military_vacant_positions();
2368
2369 // sometimes scanning an area in radius gives inappropriate results, so this is to verify that
2370 // other player is accessible
2371@@ -232,7 +236,7 @@
2372
2373 bool check_supply(const BuildingObserver&);
2374
2375- bool consider_attack(int32_t);
2376+ // bool consider_attack(int32_t);
2377
2378 void print_land_stats();
2379
2380@@ -252,6 +256,10 @@
2381 uint32_t num_prod_constructionsites;
2382 uint32_t num_ports;
2383
2384+ uint16_t last_attacked_player_;
2385+ // check ms in this interval - will auto-adjust
2386+ uint32_t enemysites_check_delay_;
2387+
2388 std::list<Widelands::FCoords> unusable_fields;
2389 std::list<BuildableField*> buildable_fields;
2390 std::list<BlockedField> blocked_fields;
2391@@ -268,6 +276,9 @@
2392 std::list<TrainingSiteObserver> trainingsites;
2393 std::list<ShipObserver> allships;
2394 std::map<ScheduleTasks, uint32_t> taskDue;
2395+ std::map<uint32_t, EnemySiteObserver> enemy_sites;
2396+ // it will map mined material to observer
2397+ std::map<int32_t, MineTypesObserver> mines_per_type;
2398
2399 std::vector<WareObserver> wares;
2400
2401@@ -284,10 +295,9 @@
2402 // when territory is expanded for every candidate field benefits are calculated
2403 // but need for water, space, mines can vary
2404 // so if 255 = resource is needed, 0 = not needed
2405- uint8_t resource_necessity_territory_;
2406- uint8_t resource_necessity_mines_;
2407- uint8_t resource_necessity_stones_;
2408- uint8_t resource_necessity_water_;
2409+ int32_t resource_necessity_territory_;
2410+ int32_t resource_necessity_mines_;
2411+ int32_t resource_necessity_water_;
2412 bool resource_necessity_water_needed_; // unless atlanteans
2413
2414 uint16_t unstationed_milit_buildings_; // counts empty military buildings (ones where no soldier
2415@@ -295,14 +305,19 @@
2416 uint16_t military_under_constr_;
2417 uint16_t military_last_dismantle_;
2418 uint32_t military_last_build_; // sometimes expansions just stops, this is time of last military
2419- // building build
2420- Widelands::Coords
2421- last_attack_target_; // flag to abuilding (position) that was attacked last time
2422- uint32_t next_attack_waittime_; // second till the next attack consideration
2423- bool seafaring_economy; // false by default, until first port space is found
2424+ // building build
2425+
2426+ bool seafaring_economy; // false by default, until first port space is found
2427 uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory
2428 // it decreases with failed scans
2429 int32_t spots_; // sum of buildable fields
2430+ int32_t vacant_mil_positions_; // sum of vacant positions in militarysites and training sites
2431+ //statistics for training sites per type
2432+ uint8_t ts_basic_count_;
2433+ uint8_t ts_basic_const_count_;
2434+ uint8_t ts_advanced_count_;
2435+ uint8_t ts_advanced_const_count_;
2436+ uint8_t ts_without_trainers_;
2437
2438 enum {kReprioritize, kStopShipyard, kStapShipyard};
2439
2440
2441=== modified file 'tribes/atlanteans/dungeon/conf'
2442--- tribes/atlanteans/dungeon/conf 2014-03-17 17:23:26 +0000
2443+++ tribes/atlanteans/dungeon/conf 2015-03-26 20:42:28 +0000
2444@@ -77,3 +77,6 @@
2445 [idle]
2446 pics=dungeon_i_??.png # ???
2447 hotspot=47 48
2448+
2449+[aihints]
2450+trainingsite_type=advanced
2451
2452=== modified file 'tribes/atlanteans/labyrinth/conf'
2453--- tribes/atlanteans/labyrinth/conf 2014-10-07 20:06:46 +0000
2454+++ tribes/atlanteans/labyrinth/conf 2015-03-26 20:42:28 +0000
2455@@ -99,4 +99,4 @@
2456 hotspot=80 88
2457
2458 [aihints]
2459-prohibited_till=2700
2460+trainingsite_type=basic
2461
2462=== modified file 'tribes/barbarians/battlearena/conf'
2463--- tribes/barbarians/battlearena/conf 2014-07-29 09:27:08 +0000
2464+++ tribes/barbarians/battlearena/conf 2015-03-26 20:42:28 +0000
2465@@ -74,3 +74,6 @@
2466 pics=battlearena_w_??.png # ???
2467 hotspot=110 72
2468 fps=10
2469+
2470+[aihints]
2471+trainingsite_type=basic
2472
2473=== modified file 'tribes/barbarians/trainingcamp/conf'
2474--- tribes/barbarians/trainingcamp/conf 2014-09-24 21:06:00 +0000
2475+++ tribes/barbarians/trainingcamp/conf 2015-03-26 20:42:28 +0000
2476@@ -36,7 +36,7 @@
2477 max_level=4
2478
2479 [aihints]
2480-prohibited_till=2700
2481+trainingsite_type=advanced
2482
2483 [soldier hp]
2484 min_level=0
2485
2486=== modified file 'tribes/empire/arena/conf'
2487--- tribes/empire/arena/conf 2014-09-30 08:05:35 +0000
2488+++ tribes/empire/arena/conf 2015-03-26 20:42:28 +0000
2489@@ -54,3 +54,6 @@
2490 [build]
2491 pics=arena_b_??.png # ???
2492 hotspot=82 83
2493+
2494+[aihints]
2495+trainingsite_type=basic
2496
2497=== modified file 'tribes/empire/colosseum/conf'
2498--- tribes/empire/colosseum/conf 2014-09-30 08:05:35 +0000
2499+++ tribes/empire/colosseum/conf 2015-03-26 20:42:28 +0000
2500@@ -60,3 +60,6 @@
2501 [idle]
2502 pics=colosseum_i_??.png # ???
2503 hotspot=81 106
2504+
2505+[aihints]
2506+trainingsite_type=basic
2507
2508=== modified file 'tribes/empire/piggery/conf'
2509--- tribes/empire/piggery/conf 2014-08-02 19:55:23 +0000
2510+++ tribes/empire/piggery/conf 2015-03-26 20:42:28 +0000
2511@@ -2,6 +2,7 @@
2512 output=meat
2513
2514 [aihints]
2515+forced_after=9000
2516
2517 [buildcost]
2518 log=2
2519
2520=== modified file 'tribes/empire/trainingcamp/conf'
2521--- tribes/empire/trainingcamp/conf 2014-08-02 19:55:23 +0000
2522+++ tribes/empire/trainingcamp/conf 2015-03-26 20:42:28 +0000
2523@@ -127,4 +127,4 @@
2524 hotspot=82 105
2525
2526 [aihints]
2527-prohibited_till=2700
2528+trainingsite_type=advanced

Subscribers

People subscribed via source and target branches

to status/vote changes: