Merge lp:~widelands-dev/widelands/ai-scheduler into lp:widelands

Proposed by TiborB
Status: Merged
Merged at revision: 7428
Proposed branch: lp:~widelands-dev/widelands/ai-scheduler
Merge into: lp:widelands
Diff against target: 1233 lines (+307/-286)
5 files modified
src/ai/ai_help_structs.h (+7/-7)
src/ai/ai_hints.cc (+2/-2)
src/ai/ai_hints.h (+2/-2)
src/ai/defaultai.cc (+243/-238)
src/ai/defaultai.h (+53/-37)
To merge this branch: bzr merge lp:~widelands-dev/widelands/ai-scheduler
Reviewer Review Type Date Requested Status
GunChleoc Approve
Review via email: mp+251327@code.launchpad.net

Description of the change

This is rework of DefaultAI::think() function. In itself it does not bring a lost of visible effects but reorganizes the logic how AI defines what "job" (one of about 15 AI functions) are to be run in this turn.

Previously AI used to run once in 0.1 second something "small" and once in 1 second more functions. It was pretty chaotic and unclear. Now it runs twice in a second and runs only one function. (with slight exemptions).

And this is meaning of a 'scheduler' - pick the only one function and be fair. Longest overdue first. And sometimes nothing as no function is overdue yet.

I think performance is improved but I have no 'metric' to quantify it.

There were some other slight changes to the code as I noticed the AI behaves bit differently.

There are still few nocoms and prints but I will remove as soon as you say it can go.

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

I have an idea: how about putting all the tasks into a set, making sure that the due time is unique:

http://www.cplusplus.com/reference/set/set/

You could then get and erase the task from front(), emplace a new task of the same type (making sure that due time is unique) and work the current task. This way, we wouldn't have the long chain of scheduler_review calls.

Revision history for this message
TiborB (tiborb95) wrote :

You are right the code is not the most elegant as it can be...

but sets are not fine, because we need something that takes pairs duetime:job, and ideally that would re-order itself after each entry, so the the job with oldest duetime is on the bottom or top....

As for identical duetime - it would be even nice if such structure could cope with identical duetimes as well, it would save me a few lines of code and for game it does not matter much which such job is preferred.

Revision history for this message
TiborB (tiborb95) wrote :

I am going to investigate priority_queue :)

Revision history for this message
TiborB (tiborb95) wrote :

Well it is not so simple.

I thought priority_queue + pairs would do the trick, but I realized that I dont need to remove and add items into queue, instead, all items would be stable, only second member of pair would be changing. And this is not possible with priority_queue.

But I could create pairs under name f.e. dueTimes as job:dueTime, and all these variables like next_economies_check_due_, would be replaced with 'dueTime.at(kCheckEconomies)' and the oldest item (=first member of a pair) in dueTime would be get with some primitive function "out of sight"

Revision history for this message
GunChleoc (gunchleoc) wrote :

Set elements are pairs of <key, value>. Just use the duetime as the key and the job type as the value, and your entries will automatically be sorted by duetime. Pop the next job off the front and insert a new check of the same job type into the set with the new duetime and you will have everything you need.

What you need to watch out for is when two jobs get sceduled at the same time, because they would have the same key. To avoid this, you can use the set's count() function to check and increment the duetime if needed.

Revision history for this message
TiborB (tiborb95) wrote :

This is not ideal. These due times should be treated as variables - everytime defined; with the queue there is a chance they can be not present in queue, or put into queue multiple times.

Also sorting entire structure is a bit overkill - I just need to find the oldest. No sense to re-sort structure of 15 pairs everytime "scheduler" runs. (Not that would do much harm to CPU)

Dumb pairs seems the best to me. I will at least save one set of variables (all these *_check_due variables)

Revision history for this message
SirVer (sirver) wrote :

Sounds more like a priority queue is needed

Also, widelands already has a command queue. How about just making a new command for each action you want to perform and use this to be called back? You would not need to reinvent the scheduling logic.

The class is Game::commandqueue or something along the lines. I am on my mobile and cannot check right now.

> Am 03.03.2015 um 04:12 schrieb GunChleoc <email address hidden>:
>
> Set elements are pairs of <key, value>. Just use the duetime as the key and the job type as the value, and your entries will automatically be sorted by duetime. Pop the next job off the front and insert a new check of the same job type into the set with the new duetime and you will have everything you need.
>
> What you need to watch out for is when two jobs get sceduled at the same time, because they would have the same key. To avoid this, you can use the set's count() function to check and increment the duetime if needed.
> --
> https://code.launchpad.net/~widelands-dev/widelands/ai-scheduler/+merge/251327
> You are subscribed to branch lp:widelands.

Revision history for this message
TiborB (tiborb95) wrote :

I would like to keep this stuff separated.

You understand this is not a queue in a FIFO meaning. When actual job is running it is not obvious which one will be run afterwards. Might be the same job again. And it is not important to know order of subsequent jobs.

It is merely to provide equal chances for every job (function) to be run.

Revision history for this message
TiborB (tiborb95) wrote :

When using simple std::pair<ScheduleTasks, DueTime>, I would completely eliminate the scheduler_review() function. It would made the code shorter and a bunch of variables would disappear.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Shorter code sounds good :)

Revision history for this message
GunChleoc (gunchleoc) wrote :

I was thinking, if all you need is an equal chance of all jobs types being run, you could list the job types in a const array and walk through it round-robin in a fixed time interval.

Revision history for this message
TiborB (tiborb95) wrote :

no, I dont need "fixed time interval".

Current design is many ifs => do some task and return. So tasks listed lower in that if - if - if - if scheme got lower changes to be run at all...

Revision history for this message
TiborB (tiborb95) wrote :

I reworked this +- as we discussed (my way :) ), it is yours now :)

Revision history for this message
GunChleoc (gunchleoc) wrote :

Looks like a good solution to me :)

I have added a bunch of small nits with the comments function.

Revision history for this message
TiborB (tiborb95) wrote :

uf, not a big problem, I am just afraid that some of these variables can achieve (or start with) negative values now - but I will test it all...

also a side question: game().get_gametime() returns int32_t - that means range of gametime -24 - +24 days. I hope game stops itself after 24 days of gametime..., is there some kind of protection there?

Revision history for this message
GunChleoc (gunchleoc) wrote :

game().get_gametime() should return uint32_t. We just haven't gotten around to fixing it.

https://bugs.launchpad.net/widelands/+bug/1380058

Revision history for this message
TiborB (tiborb95) wrote :

OK, the time reworked - I hope without hidden negative consequencies

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks. LGTM :)

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

great, may I merge this?

Revision history for this message
GunChleoc (gunchleoc) wrote :

Yes, please do :)

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-02-19 19:37:09 +0000
3+++ src/ai/ai_help_structs.h 2015-03-06 20:13:14 +0000
4@@ -204,7 +204,7 @@
5
6 struct BlockedField {
7 Widelands::FCoords coords;
8- int32_t blocked_until_;
9+ uint32_t blocked_until_;
10
11 BlockedField(Widelands::FCoords c, int32_t until) : coords(c), blocked_until_(until) {
12 }
13@@ -213,7 +213,7 @@
14 struct BuildableField {
15 Widelands::FCoords coords;
16
17- int32_t next_update_due_;
18+ uint32_t next_update_due_;
19
20 bool preferred_;
21 bool enemy_nearby_;
22@@ -292,7 +292,7 @@
23 struct MineableField {
24 Widelands::FCoords coords;
25
26- int32_t next_update_due_;
27+ uint32_t next_update_due_;
28
29 bool preferred_;
30
31@@ -350,8 +350,8 @@
32 bool expansion_type_; // military building used that can be used to control area
33 bool fighting_type_; // military building built near enemies
34 bool mountain_conqueror_; // military building built near mountains
35- int32_t prohibited_till_; // do not build before (ms)
36- int32_t forced_after_; // do not wait until ware is needed
37+ uint32_t prohibited_till_; // do not build before (ms)
38+ uint32_t forced_after_; // do not wait until ware is needed
39
40 bool unoccupied_; //
41
42@@ -385,8 +385,8 @@
43
44 struct ProductionSiteObserver {
45 Widelands::ProductionSite* site;
46- int32_t built_time_;
47- int32_t unoccupied_till_;
48+ uint32_t built_time_;
49+ uint32_t unoccupied_till_;
50 uint8_t stats_zero_;
51 uint8_t no_resources_count;
52 BuildingObserver* bo;
53
54=== modified file 'src/ai/ai_hints.cc'
55--- src/ai/ai_hints.cc 2015-01-27 20:35:26 +0000
56+++ src/ai/ai_hints.cc 2015-03-06 20:13:14 +0000
57@@ -31,8 +31,8 @@
58 expansion_(section ? section->get_bool("expansion") : false),
59 fighting_(section ? section->get_bool("fighting") : false),
60 mountain_conqueror_(section ? section->get_bool("mountain_conqueror") : false),
61- prohibited_till_(section ? section->get_int("prohibited_till", 0) : 0),
62- forced_after_(section ? section->get_int("forced_after", 864000) : 0), // 10 days default
63+ prohibited_till_(section ? section->get_natural("prohibited_till", 0) : 0),
64+ forced_after_(section ? section->get_natural("forced_after", 864000) : 0), // 10 days default
65 mines_percent_(section ? section->get_int("mines_percent", 100) : 0)
66 {
67 if (section) {
68
69=== modified file 'src/ai/ai_hints.h'
70--- src/ai/ai_hints.h 2015-01-27 20:35:26 +0000
71+++ src/ai/ai_hints.h 2015-03-06 20:13:14 +0000
72@@ -82,11 +82,11 @@
73 return mountain_conqueror_;
74 }
75
76- int32_t get_prohibited_till() const {
77+ uint32_t get_prohibited_till() const {
78 return prohibited_till_;
79 }
80
81- int32_t get_forced_after() const {
82+ uint32_t get_forced_after() const {
83 return forced_after_;
84 }
85
86
87=== modified file 'src/ai/defaultai.cc'
88--- src/ai/defaultai.cc 2015-02-16 20:23:15 +0000
89+++ src/ai/defaultai.cc 2015-03-06 20:13:14 +0000
90@@ -50,12 +50,6 @@
91 #include "logic/world/world.h"
92 #include "profile/profile.h"
93
94-// Building of new military buildings can be restricted
95-constexpr int kPushExpansion = 1;
96-constexpr int kResourcesOrDefense = 2;
97-constexpr int kDefenseOnly = 3;
98-constexpr int kNoNewMilitary = 4;
99-
100 // following is in miliseconds (widelands counts time in ms)
101 constexpr int kFieldUpdateInterval = 2000;
102 constexpr int kIdleMineUpdateInterval = 22000;
103@@ -65,7 +59,7 @@
104 constexpr int kMinBFCheckInterval = 5 * 1000;
105 constexpr int kShipCheckInterval = 5 * 1000;
106 constexpr int kMarineDecisionInterval = 20 * 1000;
107-constexpr int kTrainingSitesCheckInterval = 30 * 1000;
108+constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
109
110 // this is intended for map developers, by default should be off
111 constexpr bool kPrintStats = false;
112@@ -85,31 +79,17 @@
113 DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, uint8_t const t)
114 : ComputerPlayer(ggame, pid),
115 type_(t),
116- m_buildable_changed(true),
117- m_mineable_changed(true),
118 player_(nullptr),
119 tribe_(nullptr),
120 num_constructionsites_(0),
121 num_milit_constructionsites(0),
122 num_prod_constructionsites(0),
123 num_ports(0),
124- next_road_due_(2000),
125- next_stats_update_due_(30000),
126- next_construction_due_(1000),
127+ next_ai_think_(0),
128 next_mine_construction_due_(0),
129- next_productionsite_check_due_(0),
130- next_mine_check_due_(0),
131- next_militarysite_check_due_(0),
132- next_ship_check_due(30 * 1000),
133- next_marine_decisions_due(30 * 1000),
134- next_attack_consideration_due_(300000),
135- next_trainingsites_check_due_(15 * 60 * 1000),
136- next_bf_check_due_(1000),
137- next_wares_review_due_(15 * 60 * 1000),
138- next_statistics_report_(30 * 60 * 1000),
139 inhibit_road_building_(0),
140 time_of_last_construction_(0),
141- enemy_last_seen_(-2 * 60 * 1000),
142+ enemy_last_seen_(0),
143 numof_warehouses_(0),
144 new_buildings_stop_(false),
145 resource_necessity_territory_(255),
146@@ -119,7 +99,7 @@
147 resource_necessity_water_needed_(false),
148 unstationed_milit_buildings_(0),
149 military_last_dismantle_(0),
150- military_last_build_(-60 * 1000),
151+ military_last_build_(0),
152 last_attack_target_(
153 std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max()),
154 next_attack_waittime_(10),
155@@ -202,7 +182,7 @@
156 }
157 }
158 break;
159- default:
160+ default:
161 ;
162 }
163 });
164@@ -236,113 +216,81 @@
165 late_initialization();
166 }
167
168- const int32_t gametime = game().get_gametime();
169-
170- if (m_buildable_changed || next_bf_check_due_ < gametime) {
171- // update statistics about buildable fields
172+ const uint32_t gametime = static_cast<uint32_t>(game().get_gametime());
173+
174+ if (next_ai_think_ > gametime) {
175+ return;
176+ }
177+
178+ // AI now thinks twice in a seccond, if the game engine allows this
179+ // if too busy, the period can be many seconds.
180+ next_ai_think_ = gametime + 500;
181+ ScheduleTasks DueTask = ScheduleTasks::kIdle;
182+ DueTask = get_oldest_task(gametime);
183+ schedStat[static_cast<uint32_t>(DueTask)] += 1;
184+
185+ // now AI runs a job selected above to be performed in this turn
186+ // (only one but some of them needs to run check_economies() to
187+ // guarantee consistency)
188+ // job names are selfexplanatory
189+ if (DueTask == ScheduleTasks::kBbuildableFieldsCheck) {
190 update_all_buildable_fields(gametime);
191- next_bf_check_due_ = gametime + kMinBFCheckInterval;
192- }
193-
194- m_buildable_changed = false;
195-
196- // perpetually tries to improve roads
197- if (next_road_due_ <= gametime) {
198- next_road_due_ = gametime + 1000;
199-
200- if (improve_roads(gametime)) {
201- m_buildable_changed = true;
202+ taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
203+ } else if (DueTask == ScheduleTasks::kRoadCheck) {
204+ if (check_economies()) { // is a must
205 return;
206- }
207- } else {
208- // only go on, after defaultAI tried to improve roads.
209- return;
210- }
211-
212- // NOTE Because of the check above, the following parts of think() are used
213- // NOTE only once every second at maximum. This increases performance and as
214- // NOTE human player_s can not even react that fast, it should not be a
215- // NOTE disadvantage for the defaultAI.
216- // This must be checked every time as changes of bobs in AI area aren't
217- // handled by the AI itself.
218- update_all_not_buildable_fields();
219-
220- // considering attack
221- if (next_attack_consideration_due_ <= gametime) {
222+ };
223+ taskDue[ScheduleTasks::kRoadCheck] = gametime + 400;
224+ improve_roads(gametime);
225+ } else if (DueTask == ScheduleTasks::kUnbuildableFCheck) {
226+ taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
227+ update_all_not_buildable_fields();
228+ } else if (DueTask == ScheduleTasks::kConsiderAttack) {
229 consider_attack(gametime);
230- }
231-
232- // check if anything in the economies changed.
233- // This needs to be done before new buildings are placed, to ensure that no
234- // empty economy is left.
235- if (check_economies()) {
236- return;
237- }
238-
239- // Before thinking about a new construction, update current stats, to have
240- // a better view on current economy.
241- if (next_stats_update_due_ <= gametime) {
242+ } else if (DueTask == ScheduleTasks::kCheckEconomies) {
243+ check_economies();
244+ taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
245+ } else if (DueTask == ScheduleTasks::kProductionsitesStats) {
246 update_productionsite_stats(gametime);
247- }
248-
249- // Now try to build something if possible
250- if (next_construction_due_ <= gametime) {
251- next_construction_due_ = gametime + 2000;
252-
253+ } else if (DueTask == ScheduleTasks::kConstructBuilding) {
254+ if (check_economies()) { // economies must be consistent
255+ return;
256+ }
257+ taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000;
258 if (construct_building(gametime)) {
259 time_of_last_construction_ = gametime;
260- m_buildable_changed = true;
261- return;
262- }
263- }
264-
265- // verify that our production sites are doing well
266- if (check_productionsites(gametime)) {
267- return;
268- }
269-
270- if (check_ships(gametime)) {
271- return;
272- }
273-
274- if (marine_main_decisions(gametime)) {
275- return;
276- }
277-
278- // Check the mines and consider upgrading or destroying one
279- if (check_mines_(gametime)) {
280- return;
281- }
282-
283- // consider whether a change of the soldier capacity of some militarysites
284- // would make sense.
285- if (check_militarysites(gametime)) {
286- return;
287- }
288-
289- if (check_trainingsites(gametime)) {
290- return;
291- }
292-
293- // improve existing roads!
294- // main part of this improvment is creation 'shortcut roads'
295- // this includes also connection of new buildings
296- if (improve_roads(gametime)) {
297- m_buildable_changed = true;
298- m_mineable_changed = true;
299- return;
300- }
301-
302- // once in 15 minutes we increase(or decrease) targets for wares
303- if (next_wares_review_due_ <= gametime) {
304- next_wares_review_due_ = gametime + 15 * 60 * 1000;
305+ }
306+ } else if (DueTask == ScheduleTasks::kCheckProductionsites) {
307+ if (check_economies()) { // economies must be consistent
308+ return;
309+ }
310+ check_productionsites(gametime);
311+ taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000;
312+ } else if (DueTask == ScheduleTasks::kCheckShips) {
313+ check_ships(gametime);
314+ } else if (DueTask == ScheduleTasks::KMarineDecisions) {
315+ marine_main_decisions(gametime);
316+ } else if (DueTask == ScheduleTasks::kCheckMines) {
317+ if (check_economies()) { // economies must be consistent
318+ return;
319+ }
320+ taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough
321+ check_mines_(gametime);
322+ } else if (DueTask == ScheduleTasks::kCheckMilitarysites) {
323+ check_militarysites(gametime);
324+ } else if (DueTask == ScheduleTasks::kCheckTrainingsites) {
325+ check_trainingsites(gametime);
326+ } else if (DueTask == ScheduleTasks::kWareReview) {
327+ if (check_economies()) { // economies must be consistent
328+ return;
329+ }
330+ taskDue[ScheduleTasks::kWareReview] = gametime + 15 * 60 * 1000;
331 review_wares_targets(gametime);
332- }
333-
334- // print statistics
335- if (kPrintStats && next_statistics_report_ <= gametime) {
336- print_stats();
337- next_statistics_report_ += 60 * 60 * 1000;
338+ } else if (DueTask == ScheduleTasks::kPrintStats) {
339+ if (check_economies()) { // economies must be consistent
340+ return;
341+ }
342+ print_stats(gametime);
343 }
344 }
345
346@@ -500,13 +448,6 @@
347 }
348 }
349
350- num_constructionsites_ = 0;
351- num_milit_constructionsites = 0;
352- num_prod_constructionsites = 0;
353- next_construction_due_ = 0;
354- next_road_due_ = 1000;
355- next_productionsite_check_due_ = 0;
356- inhibit_road_building_ = 0;
357 // atlanteans they consider water as a resource
358 // (together with mines, stones and wood)
359 if (tribe_->name() == "atlanteans") {
360@@ -524,7 +465,7 @@
361 port_reserved_coords.insert(hash);
362 } while (mr.advance(map));
363
364- //the same for NW neighbour of a field
365+ // the same for NW neighbour of a field
366 Coords c_nw;
367 map.get_tln(c, &c_nw);
368 MapRegion<Area<FCoords>> mr_nw(map, Area<FCoords>(map.get_fcoords(c_nw), 3));
369@@ -593,6 +534,22 @@
370 } while (mr.advance(map));
371 }
372 }
373+
374+ taskDue[ScheduleTasks::kConstructBuilding] = 0;
375+ taskDue[ScheduleTasks::kRoadCheck] = 1000;
376+ taskDue[ScheduleTasks::kCheckProductionsites] = 15 * 1000;
377+ taskDue[ScheduleTasks::kProductionsitesStats] = 30000;
378+ taskDue[ScheduleTasks::kCheckMines] = 30 * 1000;
379+ taskDue[ScheduleTasks::kCheckMilitarysites] = 0;
380+ taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
381+ taskDue[ScheduleTasks::kCheckEconomies] = 1000;
382+ taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
383+ taskDue[ScheduleTasks::kConsiderAttack] = 300000;
384+ taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
385+ taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
386+ taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
387+ taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000;
388+ taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
389 }
390
391 /**
392@@ -601,12 +558,12 @@
393 * this shouldn't be used often, as it might hang the game for some 100
394 * milliseconds if the area the computer owns is big.
395 */
396-void DefaultAI::update_all_buildable_fields(const int32_t gametime) {
397+void DefaultAI::update_all_buildable_fields(const uint32_t gametime) {
398
399 uint16_t i = 0;
400
401 while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&
402- i < 25) {
403+ i < 40) {
404 BuildableField& bf = *buildable_fields.front();
405
406 // check whether we lost ownership of the node
407@@ -639,7 +596,7 @@
408 * this shouldn't be used often, as it might hang the game for some 100
409 * milliseconds if the area the computer owns is big.
410 */
411-void DefaultAI::update_all_mineable_fields(const int32_t gametime) {
412+void DefaultAI::update_all_mineable_fields(const uint32_t gametime) {
413
414 uint16_t i = 0; // counter, used to track # of checked fields
415
416@@ -753,7 +710,7 @@
417 }
418 }
419
420- //testing for near porspaces
421+ // testing for near porspaces
422 if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {
423 field.portspace_nearby_ = ExtendedBool::kFalse;
424 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));
425@@ -1010,9 +967,9 @@
426 }
427
428 /// Updates the production and MINE sites statistics needed for construction decision.
429-void DefaultAI::update_productionsite_stats(int32_t const gametime) {
430+void DefaultAI::update_productionsite_stats(uint32_t const gametime) {
431 // Updating the stats every 10 seconds should be enough
432- next_stats_update_due_ = gametime + 10000;
433+ taskDue[ScheduleTasks::kProductionsitesStats] = gametime + 10000;
434 uint16_t fishers_count = 0; // used for atlanteans only
435
436 // Reset statistics for all buildings
437@@ -1092,7 +1049,7 @@
438 // - there is an enemy
439 // Currently more military buildings are built then needed
440 // and "optimalization" (dismantling not needed buildings) is done afterwards
441-bool DefaultAI::construct_building(int32_t gametime) {
442+bool DefaultAI::construct_building(uint32_t gametime) {
443 // Just used for easy checking whether a mine or something else was built.
444 bool mine = false;
445 bool field_blocked = false;
446@@ -1115,7 +1072,7 @@
447
448 // here we possible stop building of new buildings
449 new_buildings_stop_ = false;
450- uint8_t expansion_mode = kResourcesOrDefense;
451+ MilitaryStrategy expansion_mode = MilitaryStrategy::kResourcesOrDefense;
452
453 // there are many reasons why to stop building production buildings
454 // (note there are numerous exceptions)
455@@ -1146,13 +1103,15 @@
456 const uint32_t treshold = militarysites.size() / 40 + 2;
457
458 if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) {
459- expansion_mode = kNoNewMilitary;
460+ expansion_mode = MilitaryStrategy::kNoNewMilitary;
461 } else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) {
462- expansion_mode = kDefenseOnly;
463+ expansion_mode = MilitaryStrategy::kDefenseOnly;
464+ } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= treshold - 1) {
465+ expansion_mode = MilitaryStrategy::kResourcesOrDefense;
466 } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
467- expansion_mode = kResourcesOrDefense;
468+ expansion_mode = MilitaryStrategy::kExpansion;
469 } else {
470- expansion_mode = kPushExpansion;
471+ expansion_mode = MilitaryStrategy::kPushExpansion;
472 }
473
474 // we must consider need for mines
475@@ -1163,9 +1122,9 @@
476 if (virtual_mines <= 7) {
477 resource_necessity_mines_ = std::numeric_limits<uint8_t>::max();
478 } else if (virtual_mines > 19) {
479- resource_necessity_mines_ = 0;
480+ resource_necessity_mines_ = 50;
481 } else {
482- const uint32_t tmp = ((18 - virtual_mines) * 255) / 12;
483+ const uint32_t tmp = (24 - virtual_mines) * 10;
484 resource_necessity_mines_ = tmp;
485 }
486
487@@ -1198,7 +1157,7 @@
488
489 // Remove outdated fields from blocker list
490 for (std::list<BlockedField>::iterator i = blocked_fields.begin(); i != blocked_fields.end();)
491- if (i->blocked_until_ < game().get_gametime()) {
492+ if (i->blocked_until_ < gametime) {
493 i = blocked_fields.erase(i);
494 } else {
495 ++i;
496@@ -1246,7 +1205,7 @@
497 ++i) {
498 BuildableField* const bf = *i;
499
500- if (bf->next_update_due_ < gametime - 8000) {
501+ if (bf->next_update_due_ < gametime - 10000) {
502 continue;
503 }
504
505@@ -1518,7 +1477,7 @@
506 }
507 // we can go above target if there is shortage of logs on stock
508 else if (bo.total_count() >= bo.cnt_target_ &&
509- bo.stocklevel_ > 40 + productionsites.size() * 5) {
510+ bo.stocklevel_ > 40 + productionsites.size() * 2) {
511 continue;
512 }
513
514@@ -1679,11 +1638,18 @@
515 continue;
516 }
517
518- if (expansion_mode == kNoNewMilitary) {
519- continue;
520- }
521-
522- if (expansion_mode == kDefenseOnly && !bf->enemy_nearby_) {
523+ if (expansion_mode == MilitaryStrategy::kNoNewMilitary) {
524+ continue;
525+ }
526+
527+ if (expansion_mode == MilitaryStrategy::kDefenseOnly && !bf->enemy_nearby_) {
528+ continue;
529+ }
530+
531+ if (expansion_mode == MilitaryStrategy::kResourcesOrDefense &&
532+ !(bf->unowned_mines_pots_nearby_ || bf->stones_nearby_ || bf->water_nearby_ ||
533+ (bf->distant_water_ && resource_necessity_water_needed_) ||
534+ bf->enemy_nearby_)) {
535 continue;
536 }
537
538@@ -1694,19 +1660,14 @@
539 (bo.mountain_conqueror_ || bo.expansion_type_)) {
540 ;
541 } // it is ok, go on
542- else if (bf->unowned_land_nearby_ && bo.expansion_type_ &&
543- num_milit_constructionsites <= 1) {
544- ; // we allow big buildings now
545- } else if (bf->unowned_land_nearby_ &&
546- bo.expansion_type_) { // decreasing probability for big buidlings
547+ else if (bo.expansion_type_) {
548 if (bo.desc->get_size() == 2 && gametime % 2 >= 1) {
549 continue;
550 }
551 if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {
552 continue;
553- }
554+ };
555 }
556- // it is ok, go on
557 else {
558 continue;
559 } // the building is not suitable for situation
560@@ -1719,22 +1680,22 @@
561
562 // a boost to prevent an expansion halt
563 int32_t local_boost = 0;
564- if (expansion_mode == kPushExpansion) {
565+ if (expansion_mode == MilitaryStrategy::kPushExpansion) {
566 local_boost = 200;
567 }
568
569- prio = (bf->unowned_land_nearby_ * 2 * resource_necessity_territory_ / 255 +
570- bf->unowned_mines_pots_nearby_ * resource_necessity_mines_ / 255 +
571+ prio = ((bf->unowned_land_nearby_ * 2 * resource_necessity_territory_) / 255 +
572+ (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 255 +
573 bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost +
574- bf->water_nearby_ * resource_necessity_water_ / 255);
575+ (bf->water_nearby_ * resource_necessity_water_) / 255);
576
577 // special bonus due to remote water for atlanteans
578 if (resource_necessity_water_needed_)
579- prio += bf->distant_water_ * resource_necessity_water_ / 255;
580+ prio += (bf->distant_water_ * resource_necessity_water_) / 255;
581
582- //special bonus if a portspace is close
583+ // special bonus if a portspace is close
584 if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
585- if (num_ports == 0){
586+ if (num_ports == 0) {
587 prio += 25;
588 } else {
589 prio += 5;
590@@ -2044,16 +2005,13 @@
591 // set the type of update that is needed
592 if (mine) {
593 next_mine_construction_due_ = gametime + kBusyMineUpdateInterval;
594-
595- } else {
596- m_buildable_changed = true;
597 }
598
599 return true;
600 }
601
602 // improves current road system
603-bool DefaultAI::improve_roads(int32_t gametime) {
604+bool DefaultAI::improve_roads(uint32_t gametime) {
605
606 // first force a split on roads that are longer than 3 parts
607 // with exemption when there is too few building spots
608@@ -2093,7 +2051,7 @@
609 roads.pop_front();
610
611 // occasionaly we test if the road can be dismounted
612- if (gametime % 25 == 0) {
613+ if (gametime % 5 == 0) {
614 const Road& road = *roads.front();
615 if (dispensable_road_test(*const_cast<Road*>(&road))) {
616 game().send_player_bulldoze(*const_cast<Road*>(&road));
617@@ -2134,14 +2092,25 @@
618 return true;
619 }
620
621+ bool is_warehouse = false;
622+ if (Building* b = flag.get_building()) {
623+ BuildingObserver& bo = get_building_observer(b->descr().name().c_str());
624+ if (bo.type == BuildingObserver::WAREHOUSE) {
625+ is_warehouse = true;
626+ }
627+ }
628+
629 // if this is end flag (or sole building) or just randomly
630- if (flag.nr_of_roads() <= 1 || gametime % 200 == 0) {
631- create_shortcut_road(flag, 13, 20, gametime);
632+ if (flag.nr_of_roads() <= 1 || gametime % 10 == 0) {
633+ create_shortcut_road(flag, 11, 20, gametime);
634 inhibit_road_building_ = gametime + 800;
635- }
636- // this is when a flag is full
637- else if (flag.current_wares() > 6 && gametime % 10 == 0) {
638- create_shortcut_road(flag, 9, 0, gametime);
639+ // a warehouse with 3 or less roads
640+ } else if (is_warehouse && flag.nr_of_roads() <= 3) {
641+ create_shortcut_road(flag, 9, -1, gametime);
642+ inhibit_road_building_ = gametime + 400;
643+ // and when a flag is full with wares
644+ } else if (flag.current_wares() > 5) {
645+ create_shortcut_road(flag, 9, -2, gametime);
646 inhibit_road_building_ = gametime + 400;
647 }
648
649@@ -2227,7 +2196,7 @@
650 // or other economy
651 bool DefaultAI::create_shortcut_road(const Flag& flag,
652 uint16_t checkradius,
653- uint16_t minred,
654+ int16_t min_reduction,
655 int32_t gametime) {
656
657 // Increasing the failed_connection_tries counter
658@@ -2246,15 +2215,27 @@
659
660 // first we deal with situations when this is economy with no warehouses
661 // and this is a flag belonging to a building/constructionsite
662+ // such economy must get dismantle grace time (if not set yet)
663+ // end sometimes extended checkradius
664 if (flag.get_economy()->warehouses().empty() && flag.get_building()) {
665
666+ // occupied military buildings get special treatment
667+ //(extended grace time + longer radius)
668+ bool occupied_military_ = false;
669+ Building* b = flag.get_building();
670+ if (upcast(MilitarySite, militb, b)) {
671+ if (militb->stationed_soldiers().size() > 0) {
672+ occupied_military_ = true;
673+ }
674+ }
675+
676 // if we are within grace time, it is OK, just go on
677 if (eco->dismantle_grace_time_ > gametime &&
678 eco->dismantle_grace_time_ != std::numeric_limits<int32_t>::max()) {
679 ;
680
681- // if grace time is not set, this is probably first time without a warehouse and we must
682- // set it
683+ // if grace time is not set, this is probably first time without a warehouse and we must
684+ // set it
685 } else if (eco->dismantle_grace_time_ == std::numeric_limits<int32_t>::max()) {
686
687 // constructionsites
688@@ -2267,27 +2248,16 @@
689 eco->dismantle_grace_time_ = gametime + 60 * 60 * 1000; // one hour should be enough
690 } else { // other constructionsites, usually new (standalone) constructionsites
691 eco->dismantle_grace_time_ =
692- gametime + 30 * 1000 + // very shot time is enough
693+ gametime + 30 * 1000 + // very shot time is enough
694 (eco->flags.size() * 30 * 1000); // + 30 seconds for every flag in economy
695 }
696
697- // buildings
698+ // buildings
699 } else {
700
701- //occupied military buildings get special treatment
702- //(extended grace time)
703- bool occupied_military_ = false;
704- Building* b = flag.get_building();
705- if (upcast(MilitarySite, militb, b)) {
706- if (militb->stationed_soldiers().size() > 0) {
707- occupied_military_ = true;
708- }
709- }
710-
711 if (occupied_military_) {
712 eco->dismantle_grace_time_ =
713 (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
714- checkradius += 3;
715
716 } else { // for other normal buildings
717 eco->dismantle_grace_time_ =
718@@ -2298,9 +2268,17 @@
719 // we have passed grace_time - it is time to dismantle
720 } else {
721 last_attempt_ = true;
722- //we increase a check radius in last attempt
723+ // we increase a check radius in last attempt
724 checkradius += 2;
725 }
726+
727+ // and bonus for occupied military buildings:
728+ if (occupied_military_) {
729+ checkradius += 4;
730+ }
731+
732+ // and generally increase radius for unconnected buildings
733+ checkradius += 2;
734 }
735
736 Map& map = game().map();
737@@ -2456,7 +2434,7 @@
738 NearFlag& nf = nearflags.at(i);
739
740 // terminating looping if reduction is too low (nearflags are sorted by reduction)
741- if ((nf.cost_ - nf.distance_) < minred) {
742+ if ((nf.cost_ - nf.distance_) < min_reduction) {
743 return false;
744 }
745
746@@ -2464,7 +2442,7 @@
747 // usually we allow connecting only if both flags are closer then 'checkradius-2'
748 // with exeption the flag belongs to a small economy (typically a new building not connected
749 // yet)
750- if ((nf.cost_ - nf.distance_) >= minred && nf.distance_ >= 2 &&
751+ if ((nf.cost_ - nf.distance_) >= min_reduction && nf.distance_ >= 2 &&
752 nf.distance_ < checkradius - 2) {
753
754 // sometimes the shortest road is not the buildable, even if map.findpath claims so
755@@ -2481,7 +2459,7 @@
756 map.findpath(flag.get_position(), nf.flag->get_position(), 0, path, check);
757
758 if (pathcost >= 0) {
759- if (static_cast<int32_t>(nf.cost_ - path.get_nsteps()) > minred) {
760+ if (static_cast<int32_t>(nf.cost_ - path.get_nsteps()) > min_reduction) {
761 game().send_player_build_road(player_number(), path);
762 return true;
763 }
764@@ -2551,13 +2529,11 @@
765 *
766 * \returns true, if something was changed.
767 */
768-bool DefaultAI::check_productionsites(int32_t gametime) {
769- if ((next_productionsite_check_due_ > gametime) || productionsites.empty()) {
770+bool DefaultAI::check_productionsites(uint32_t gametime) {
771+ if (productionsites.empty()) {
772 return false;
773 }
774
775- next_productionsite_check_due_ = gametime + 4000;
776-
777 bool changed = false;
778 // Reorder and set new values; - better now because there are multiple returns in the function
779 productionsites.push_back(productionsites.front());
780@@ -2695,9 +2671,9 @@
781
782 // Wells handling
783 if (site.bo->mines_water_) {
784- if (site.unoccupied_till_ + 6 * 60 * 1000 < game().get_gametime() &&
785+ if (site.unoccupied_till_ + 6 * 60 * 1000 < gametime &&
786 site.site->get_statistics_percent() == 0) {
787- site.bo->last_dismantle_time_ = game().get_gametime();
788+ site.bo->last_dismantle_time_ = gametime;
789 flags_to_be_removed.push_back(site.site->base_flag().get_position());
790 game().send_player_dismantle(*site.site);
791
792@@ -2742,7 +2718,7 @@
793 return true;
794 }
795
796- if (site.unoccupied_till_ + 6 * 60 * 1000 < game().get_gametime() &&
797+ if (site.unoccupied_till_ + 6 * 60 * 1000 < gametime &&
798 site.site->get_statistics_percent() == 0) {
799 // it is possible that there are stones but quary is not able to mine them
800 site.bo->last_dismantle_time_ = game().get_gametime();
801@@ -2760,7 +2736,7 @@
802 &&
803 site.bo->production_hint_ == -1 // not a renewing building (forester...)
804 &&
805- site.unoccupied_till_ + 10 * 60 * 1000 < game().get_gametime() // > 10 minutes old
806+ site.unoccupied_till_ + 10 * 60 * 1000 < gametime // > 10 minutes old
807 &&
808 site.site->can_start_working() // building is occupied
809 &&
810@@ -2831,7 +2807,7 @@
811
812 // logs can be stored also in productionsites, they are counted as on stock
813 // but are no available for random production site
814- int16_t score = site.bo->stocklevel_ - productionsites.size() * 5;
815+ int16_t score = site.bo->stocklevel_ - productionsites.size() * 2;
816
817 if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) {
818
819@@ -2860,15 +2836,17 @@
820 // - build a ship
821 // - start preparation for expedition
822 bool DefaultAI::marine_main_decisions(uint32_t const gametime) {
823- if (gametime < next_marine_decisions_due) {
824+ if (gametime < taskDue[ScheduleTasks::KMarineDecisions]) {
825 return false;
826 }
827- next_marine_decisions_due = gametime + kMarineDecisionInterval;
828
829 if (!seafaring_economy) {
830+ taskDue[ScheduleTasks::KMarineDecisions] = std::numeric_limits<uint32_t>::max();
831 return false;
832 }
833
834+ taskDue[ScheduleTasks::KMarineDecisions] = gametime + kMarineDecisionInterval;
835+
836 // getting some base statistics
837 player_ = game().get_player(player_number());
838 uint16_t ports_count = 0;
839@@ -2918,7 +2896,7 @@
840 }
841 }
842
843- enum class FleetStatus : uint8_t {kNeedShip = 0, kEnoughShips = 1, kDoNothing = 2 };
844+ enum class FleetStatus : uint8_t {kNeedShip = 0, kEnoughShips = 1, kDoNothing = 2};
845
846 // now we must compare ports vs ships to decide if new ship is needed or new expedition can start
847 FleetStatus enough_ships = FleetStatus::kDoNothing;
848@@ -2970,16 +2948,17 @@
849
850 // This identifies ships that are waiting for command
851 bool DefaultAI::check_ships(uint32_t const gametime) {
852- if (gametime < next_ship_check_due) {
853+ if (gametime < taskDue[ScheduleTasks::kCheckShips]) {
854 return false;
855 }
856
857- next_ship_check_due = gametime + kShipCheckInterval;
858-
859 if (!seafaring_economy) {
860+ taskDue[ScheduleTasks::kCheckShips] = std::numeric_limits<int32_t>::max();
861 return false;
862 }
863
864+ bool action_taken = false;
865+
866 if (!allships.empty()) {
867 // iterating over ships and executing what is needed
868 for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
869@@ -3001,6 +2980,7 @@
870 // if ships is waiting for command
871 if (i->waiting_for_command_) {
872 expedition_management(*i);
873+ action_taken = true;
874 }
875 }
876 }
877@@ -3036,6 +3016,12 @@
878 marineTaskQueue_.pop_back();
879 }
880
881+ if (action_taken) {
882+ taskDue[ScheduleTasks::kCheckShips] = gametime + kShipCheckInterval;
883+ } else {
884+ taskDue[ScheduleTasks::kCheckShips] = gametime + 3 * kShipCheckInterval;
885+ }
886+
887 return true;
888 }
889
890@@ -3045,11 +3031,11 @@
891 *
892 * \returns true, if something was changed.
893 */
894-bool DefaultAI::check_mines_(int32_t const gametime) {
895- if ((next_mine_check_due_ > gametime) || mines_.empty())
896+bool DefaultAI::check_mines_(uint32_t const gametime) {
897+ if (mines_.empty()) {
898 return false;
899+ }
900
901- next_mine_check_due_ = gametime + 7000; // 7 seconds is enough
902 // Reorder and set new values; - due to returns within the function
903 mines_.push_back(mines_.front());
904 mines_.pop_front();
905@@ -3228,14 +3214,14 @@
906
907 // this function only manipulates with trainingsites' inputs priority
908 // decreases it when too many unoccupied military buildings
909-bool DefaultAI::check_trainingsites(int32_t gametime) {
910- if (next_trainingsites_check_due_ > gametime) {
911+bool DefaultAI::check_trainingsites(uint32_t gametime) {
912+ if (taskDue[ScheduleTasks::kCheckTrainingsites] > gametime) {
913 return false;
914 }
915 if (!trainingsites.empty()) {
916- next_trainingsites_check_due_ = gametime + kTrainingSitesCheckInterval;
917+ taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval;
918 } else {
919- next_trainingsites_check_due_ = gametime + 3 * kTrainingSitesCheckInterval;
920+ taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 5 * kTrainingSitesCheckInterval;
921 }
922
923 uint8_t new_priority = DEFAULT_PRIORITY;
924@@ -3264,13 +3250,13 @@
925 *
926 * \returns true if something was changed
927 */
928-bool DefaultAI::check_militarysites(int32_t gametime) {
929- if (next_militarysite_check_due_ > gametime) {
930+bool DefaultAI::check_militarysites(uint32_t gametime) {
931+ if (taskDue[ScheduleTasks::kCheckMilitarysites] > gametime) {
932 return false;
933 }
934
935 // just to be sure the value is reset
936- next_militarysite_check_due_ = gametime + 4 * 1000; // 4 seconds is really fine
937+ taskDue[ScheduleTasks::kCheckMilitarysites] = gametime + 4 * 1000; // 4 seconds is really fine
938 // even if there are no finished & attended military sites, probably there are ones just in
939 // construction
940 unstationed_milit_buildings_ = 0;
941@@ -3385,7 +3371,7 @@
942 // reorder:;
943 militarysites.push_back(militarysites.front());
944 militarysites.pop_front();
945- next_militarysite_check_due_ = gametime + 5 * 1000; // 10 seconds is really fine
946+ taskDue[ScheduleTasks::kCheckMilitarysites] = gametime + 5 * 1000; // 10 seconds is really fine
947 return changed;
948 }
949
950@@ -3833,7 +3819,7 @@
951 }
952
953 // Let defaultAI try to directly connect the constructionsite
954- next_road_due_ = game().get_gametime();
955+ taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime();
956 } else {
957 ++bo.cnt_built_;
958
959@@ -3989,9 +3975,6 @@
960 }
961 }
962 }
963-
964- m_buildable_changed = true;
965- m_mineable_changed = true;
966 }
967
968 // Checks that supply line exists for given building.
969@@ -4043,7 +4026,7 @@
970
971 // Only useable, if it owns at least one militarysite
972 if (militarysites.empty()) {
973- next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;
974+ taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
975 return false;
976 }
977
978@@ -4092,7 +4075,7 @@
979 if (current_margin < needed_margin) { // no attacking!
980 last_attack_target_.x = std::numeric_limits<uint16_t>::max();
981 last_attack_target_.y = std::numeric_limits<uint16_t>::max();
982- next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;
983+ taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
984 return false;
985 }
986
987@@ -4131,7 +4114,7 @@
988
989 // if we cannot attack anybody, terminating...
990 if (!any_attackable) {
991- next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;
992+ taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
993 last_attack_target_.x = std::numeric_limits<uint16_t>::max();
994 last_attack_target_.y = std::numeric_limits<uint16_t>::max();
995 return false;
996@@ -4245,7 +4228,7 @@
997
998 game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers);
999 last_attack_target_ = best_wh_target->get_position();
1000- next_attack_consideration_due_ = (gametime % 10 + 10) * 1000 + gametime;
1001+ taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
1002 next_attack_waittime_ = 10;
1003 return true;
1004
1005@@ -4261,11 +4244,11 @@
1006
1007 game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers);
1008 last_attack_target_ = best_ms_target->get_position();
1009- next_attack_consideration_due_ = (gametime % 10 + 10) * 1000 + gametime;
1010+ taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
1011 next_attack_waittime_ = 10;
1012 return true;
1013 } else {
1014- next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;
1015+ taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
1016 last_attack_target_.x = std::numeric_limits<uint16_t>::max();
1017 last_attack_target_.y = std::numeric_limits<uint16_t>::max();
1018 return false;
1019@@ -4274,7 +4257,7 @@
1020
1021 // This runs once in 15 minutes, and adjust wares targets based on number of
1022 // productionsites and ports
1023-void DefaultAI::review_wares_targets(int32_t const gametime) {
1024+void DefaultAI::review_wares_targets(uint32_t const gametime) {
1025
1026 player_ = game().get_player(player_number());
1027 tribe_ = &player_->tribe();
1028@@ -4300,13 +4283,35 @@
1029 }
1030 }
1031
1032+// run over dueTasks map and returns task with lower duetime
1033+DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) {
1034+
1035+ uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
1036+ ScheduleTasks DueTask = ScheduleTasks::kIdle; // default
1037+ taskDue[ScheduleTasks::kIdle] = gametime;
1038+
1039+ for (std::pair<ScheduleTasks, uint32_t> task : taskDue) {
1040+ if (task.second < oldestTaskTime) {
1041+ oldestTaskTime = task.second;
1042+ DueTask = task.first;
1043+ }
1044+ }
1045+ return DueTask;
1046+}
1047+
1048 // This prints some basic statistics during a game to the command line -
1049 // missing materials and counts of different types of buildings.
1050 // The main purpose of this is when a game creator needs to finetune a map
1051 // and needs to know what resourcess are missing for which player and so on.
1052 // By default it is off (see kPrintStats)
1053 // TODO(tiborb ?): - it would be nice to have this activated by a command line switch
1054-void DefaultAI::print_stats() {
1055+void DefaultAI::print_stats(uint32_t const gametime) {
1056+
1057+ if (!kPrintStats) {
1058+ taskDue[ScheduleTasks::kPrintStats] = std::numeric_limits<int32_t>::max();
1059+ return;
1060+ }
1061+ taskDue[ScheduleTasks::kPrintStats] = gametime + 30 * 60 * 1000;
1062
1063 PlayerNumber const pn = player_number();
1064
1065
1066=== modified file 'src/ai/defaultai.h'
1067--- src/ai/defaultai.h 2015-02-16 21:19:59 +0000
1068+++ src/ai/defaultai.h 2015-03-06 20:13:14 +0000
1069@@ -78,8 +78,33 @@
1070 DEFENSIVE = 0,
1071 };
1072
1073- enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers };
1074- enum class NewShip : uint8_t {kBuilt, kFoundOnLoad };
1075+ enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers};
1076+ enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
1077+ enum class ScheduleTasks : uint8_t {
1078+ kBbuildableFieldsCheck,
1079+ kRoadCheck,
1080+ kUnbuildableFCheck,
1081+ kConsiderAttack,
1082+ kCheckEconomies,
1083+ kProductionsitesStats,
1084+ kConstructBuilding,
1085+ kCheckProductionsites,
1086+ kCheckShips,
1087+ KMarineDecisions,
1088+ kCheckMines,
1089+ kWareReview,
1090+ kPrintStats,
1091+ kIdle,
1092+ kCheckMilitarysites,
1093+ kCheckTrainingsites
1094+ };
1095+ enum class MilitaryStrategy : uint8_t {
1096+ kNoNewMilitary,
1097+ kDefenseOnly,
1098+ kResourcesOrDefense,
1099+ kExpansion,
1100+ kPushExpansion
1101+ };
1102
1103 /// Implementation for Aggressive
1104 struct AggressiveImpl : public ComputerPlayer::Implementation {
1105@@ -122,21 +147,24 @@
1106 private:
1107 void late_initialization();
1108
1109- void update_all_buildable_fields(int32_t);
1110- void update_all_mineable_fields(int32_t);
1111+ void update_all_buildable_fields(uint32_t);
1112+ void update_all_mineable_fields(uint32_t);
1113 void update_all_not_buildable_fields();
1114
1115 void update_buildable_field(BuildableField&, uint16_t = 6, bool = false);
1116 void update_mineable_field(MineableField&);
1117
1118- void update_productionsite_stats(int32_t);
1119+ void update_productionsite_stats(uint32_t);
1120
1121 void check_ware_necessity(BuildingObserver& bo,
1122 bool* output_is_needed,
1123 int16_t* max_preciousness,
1124 int16_t* max_needed_preciousness);
1125
1126- bool construct_building(int32_t);
1127+
1128+ ScheduleTasks get_oldest_task(uint32_t);
1129+
1130+ bool construct_building(uint32_t);
1131
1132 uint32_t coords_hash(Widelands::Coords coords) {
1133 uint32_t hash = coords.x << 16 | coords.y;
1134@@ -153,27 +181,26 @@
1135 // all road management is invoked by function improve_roads()
1136 // if needed it calls create_shortcut_road() with a flag from which
1137 // new road should be considered (or is needed)
1138- bool improve_roads(int32_t);
1139+ bool improve_roads(uint32_t);
1140 bool create_shortcut_road(const Widelands::Flag&,
1141 uint16_t maxcheckradius,
1142- uint16_t minred,
1143+ int16_t minReduction,
1144 const int32_t gametime);
1145 // trying to identify roads that might be removed
1146 bool dispensable_road_test(Widelands::Road&);
1147 bool check_economies();
1148- bool check_productionsites(int32_t);
1149- bool check_trainingsites(int32_t);
1150- bool check_mines_(int32_t);
1151- bool check_militarysites(int32_t);
1152+ bool check_productionsites(uint32_t);
1153+ bool check_trainingsites(uint32_t);
1154+ bool check_mines_(uint32_t);
1155+ bool check_militarysites(uint32_t);
1156 bool marine_main_decisions(uint32_t);
1157 bool check_ships(uint32_t);
1158- void print_stats();
1159+ void print_stats(uint32_t);
1160 uint32_t get_stocklevel_by_hint(size_t);
1161 uint32_t get_stocklevel(BuildingObserver&);
1162 uint32_t get_warehoused_stock(Widelands::WareIndex wt);
1163 uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_
1164- void check_helpersites(int32_t);
1165- void review_wares_targets(int32_t);
1166+ void review_wares_targets(uint32_t);
1167
1168 // sometimes scanning an area in radius gives inappropriate results, so this is to verify that
1169 // other player is accessible
1170@@ -213,8 +240,8 @@
1171 // Variables of default AI
1172 uint8_t type_;
1173
1174- bool m_buildable_changed;
1175- bool m_mineable_changed;
1176+ // collect statistics on how many times which job was run
1177+ uint32_t schedStat[20] = {0};
1178
1179 Widelands::Player* player_;
1180 Widelands::TribeDescr const* tribe_;
1181@@ -240,26 +267,15 @@
1182 std::list<WarehouseSiteObserver> warehousesites;
1183 std::list<TrainingSiteObserver> trainingsites;
1184 std::list<ShipObserver> allships;
1185+ std::map<ScheduleTasks, uint32_t> taskDue;
1186
1187 std::vector<WareObserver> wares;
1188
1189- int32_t next_road_due_;
1190- int32_t next_stats_update_due_;
1191- int32_t next_construction_due_;
1192- int32_t next_mine_construction_due_;
1193- int32_t next_productionsite_check_due_;
1194- int32_t next_mine_check_due_;
1195- int32_t next_militarysite_check_due_;
1196- uint32_t next_ship_check_due;
1197- uint32_t next_marine_decisions_due;
1198- int32_t next_attack_consideration_due_;
1199- int32_t next_trainingsites_check_due_;
1200- int32_t next_bf_check_due_;
1201- int32_t next_wares_review_due_;
1202- int32_t next_statistics_report_;
1203- int32_t inhibit_road_building_;
1204- int32_t time_of_last_construction_;
1205- int32_t enemy_last_seen_;
1206+ uint32_t next_ai_think_;
1207+ uint32_t next_mine_construction_due_;
1208+ uint32_t inhibit_road_building_;
1209+ uint32_t time_of_last_construction_;
1210+ uint32_t enemy_last_seen_;
1211
1212 uint16_t numof_warehouses_;
1213
1214@@ -278,17 +294,17 @@
1215 // is belogning to)
1216 uint16_t military_under_constr_;
1217 uint16_t military_last_dismantle_;
1218- int32_t military_last_build_; // sometimes expansions just stops, this is time of last military
1219+ uint32_t military_last_build_; // sometimes expansions just stops, this is time of last military
1220 // building build
1221 Widelands::Coords
1222 last_attack_target_; // flag to abuilding (position) that was attacked last time
1223- int32_t next_attack_waittime_; // second till the next attack consideration
1224+ uint32_t next_attack_waittime_; // second till the next attack consideration
1225 bool seafaring_economy; // false by default, until first port space is found
1226 uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory
1227 // it decreases with failed scans
1228 int32_t spots_; // sum of buildable fields
1229
1230- enum {kReprioritize, kStopShipyard, kStapShipyard };
1231+ enum {kReprioritize, kStopShipyard, kStapShipyard};
1232
1233 std::vector<int16_t> marineTaskQueue_;
1234

Subscribers

People subscribed via source and target branches

to status/vote changes: