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
=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h 2015-02-19 19:37:09 +0000
+++ src/ai/ai_help_structs.h 2015-03-06 20:13:14 +0000
@@ -204,7 +204,7 @@
204204
205struct BlockedField {205struct BlockedField {
206 Widelands::FCoords coords;206 Widelands::FCoords coords;
207 int32_t blocked_until_;207 uint32_t blocked_until_;
208208
209 BlockedField(Widelands::FCoords c, int32_t until) : coords(c), blocked_until_(until) {209 BlockedField(Widelands::FCoords c, int32_t until) : coords(c), blocked_until_(until) {
210 }210 }
@@ -213,7 +213,7 @@
213struct BuildableField {213struct BuildableField {
214 Widelands::FCoords coords;214 Widelands::FCoords coords;
215215
216 int32_t next_update_due_;216 uint32_t next_update_due_;
217217
218 bool preferred_;218 bool preferred_;
219 bool enemy_nearby_;219 bool enemy_nearby_;
@@ -292,7 +292,7 @@
292struct MineableField {292struct MineableField {
293 Widelands::FCoords coords;293 Widelands::FCoords coords;
294294
295 int32_t next_update_due_;295 uint32_t next_update_due_;
296296
297 bool preferred_;297 bool preferred_;
298298
@@ -350,8 +350,8 @@
350 bool expansion_type_; // military building used that can be used to control area350 bool expansion_type_; // military building used that can be used to control area
351 bool fighting_type_; // military building built near enemies351 bool fighting_type_; // military building built near enemies
352 bool mountain_conqueror_; // military building built near mountains352 bool mountain_conqueror_; // military building built near mountains
353 int32_t prohibited_till_; // do not build before (ms)353 uint32_t prohibited_till_; // do not build before (ms)
354 int32_t forced_after_; // do not wait until ware is needed354 uint32_t forced_after_; // do not wait until ware is needed
355355
356 bool unoccupied_; //356 bool unoccupied_; //
357357
@@ -385,8 +385,8 @@
385385
386struct ProductionSiteObserver {386struct ProductionSiteObserver {
387 Widelands::ProductionSite* site;387 Widelands::ProductionSite* site;
388 int32_t built_time_;388 uint32_t built_time_;
389 int32_t unoccupied_till_;389 uint32_t unoccupied_till_;
390 uint8_t stats_zero_;390 uint8_t stats_zero_;
391 uint8_t no_resources_count;391 uint8_t no_resources_count;
392 BuildingObserver* bo;392 BuildingObserver* bo;
393393
=== modified file 'src/ai/ai_hints.cc'
--- src/ai/ai_hints.cc 2015-01-27 20:35:26 +0000
+++ src/ai/ai_hints.cc 2015-03-06 20:13:14 +0000
@@ -31,8 +31,8 @@
31 expansion_(section ? section->get_bool("expansion") : false),31 expansion_(section ? section->get_bool("expansion") : false),
32 fighting_(section ? section->get_bool("fighting") : false),32 fighting_(section ? section->get_bool("fighting") : false),
33 mountain_conqueror_(section ? section->get_bool("mountain_conqueror") : false),33 mountain_conqueror_(section ? section->get_bool("mountain_conqueror") : false),
34 prohibited_till_(section ? section->get_int("prohibited_till", 0) : 0),34 prohibited_till_(section ? section->get_natural("prohibited_till", 0) : 0),
35 forced_after_(section ? section->get_int("forced_after", 864000) : 0), // 10 days default35 forced_after_(section ? section->get_natural("forced_after", 864000) : 0), // 10 days default
36 mines_percent_(section ? section->get_int("mines_percent", 100) : 0)36 mines_percent_(section ? section->get_int("mines_percent", 100) : 0)
37{37{
38 if (section) {38 if (section) {
3939
=== modified file 'src/ai/ai_hints.h'
--- src/ai/ai_hints.h 2015-01-27 20:35:26 +0000
+++ src/ai/ai_hints.h 2015-03-06 20:13:14 +0000
@@ -82,11 +82,11 @@
82 return mountain_conqueror_;82 return mountain_conqueror_;
83 }83 }
8484
85 int32_t get_prohibited_till() const {85 uint32_t get_prohibited_till() const {
86 return prohibited_till_;86 return prohibited_till_;
87 }87 }
8888
89 int32_t get_forced_after() const {89 uint32_t get_forced_after() const {
90 return forced_after_;90 return forced_after_;
91 }91 }
9292
9393
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2015-02-16 20:23:15 +0000
+++ src/ai/defaultai.cc 2015-03-06 20:13:14 +0000
@@ -50,12 +50,6 @@
50#include "logic/world/world.h"50#include "logic/world/world.h"
51#include "profile/profile.h"51#include "profile/profile.h"
5252
53// Building of new military buildings can be restricted
54constexpr int kPushExpansion = 1;
55constexpr int kResourcesOrDefense = 2;
56constexpr int kDefenseOnly = 3;
57constexpr int kNoNewMilitary = 4;
58
59// following is in miliseconds (widelands counts time in ms)53// following is in miliseconds (widelands counts time in ms)
60constexpr int kFieldUpdateInterval = 2000;54constexpr int kFieldUpdateInterval = 2000;
61constexpr int kIdleMineUpdateInterval = 22000;55constexpr int kIdleMineUpdateInterval = 22000;
@@ -65,7 +59,7 @@
65constexpr int kMinBFCheckInterval = 5 * 1000;59constexpr int kMinBFCheckInterval = 5 * 1000;
66constexpr int kShipCheckInterval = 5 * 1000;60constexpr int kShipCheckInterval = 5 * 1000;
67constexpr int kMarineDecisionInterval = 20 * 1000;61constexpr int kMarineDecisionInterval = 20 * 1000;
68constexpr int kTrainingSitesCheckInterval = 30 * 1000;62constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
6963
70// this is intended for map developers, by default should be off64// this is intended for map developers, by default should be off
71constexpr bool kPrintStats = false;65constexpr bool kPrintStats = false;
@@ -85,31 +79,17 @@
85DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, uint8_t const t)79DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, uint8_t const t)
86 : ComputerPlayer(ggame, pid),80 : ComputerPlayer(ggame, pid),
87 type_(t),81 type_(t),
88 m_buildable_changed(true),
89 m_mineable_changed(true),
90 player_(nullptr),82 player_(nullptr),
91 tribe_(nullptr),83 tribe_(nullptr),
92 num_constructionsites_(0),84 num_constructionsites_(0),
93 num_milit_constructionsites(0),85 num_milit_constructionsites(0),
94 num_prod_constructionsites(0),86 num_prod_constructionsites(0),
95 num_ports(0),87 num_ports(0),
96 next_road_due_(2000),88 next_ai_think_(0),
97 next_stats_update_due_(30000),
98 next_construction_due_(1000),
99 next_mine_construction_due_(0),89 next_mine_construction_due_(0),
100 next_productionsite_check_due_(0),
101 next_mine_check_due_(0),
102 next_militarysite_check_due_(0),
103 next_ship_check_due(30 * 1000),
104 next_marine_decisions_due(30 * 1000),
105 next_attack_consideration_due_(300000),
106 next_trainingsites_check_due_(15 * 60 * 1000),
107 next_bf_check_due_(1000),
108 next_wares_review_due_(15 * 60 * 1000),
109 next_statistics_report_(30 * 60 * 1000),
110 inhibit_road_building_(0),90 inhibit_road_building_(0),
111 time_of_last_construction_(0),91 time_of_last_construction_(0),
112 enemy_last_seen_(-2 * 60 * 1000),92 enemy_last_seen_(0),
113 numof_warehouses_(0),93 numof_warehouses_(0),
114 new_buildings_stop_(false),94 new_buildings_stop_(false),
115 resource_necessity_territory_(255),95 resource_necessity_territory_(255),
@@ -119,7 +99,7 @@
119 resource_necessity_water_needed_(false),99 resource_necessity_water_needed_(false),
120 unstationed_milit_buildings_(0),100 unstationed_milit_buildings_(0),
121 military_last_dismantle_(0),101 military_last_dismantle_(0),
122 military_last_build_(-60 * 1000),102 military_last_build_(0),
123 last_attack_target_(103 last_attack_target_(
124 std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max()),104 std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max()),
125 next_attack_waittime_(10),105 next_attack_waittime_(10),
@@ -202,7 +182,7 @@
202 }182 }
203 }183 }
204 break;184 break;
205 default:185 default:
206 ;186 ;
207 }187 }
208 });188 });
@@ -236,113 +216,81 @@
236 late_initialization();216 late_initialization();
237 }217 }
238218
239 const int32_t gametime = game().get_gametime();219 const uint32_t gametime = static_cast<uint32_t>(game().get_gametime());
240220
241 if (m_buildable_changed || next_bf_check_due_ < gametime) {221 if (next_ai_think_ > gametime) {
242 // update statistics about buildable fields222 return;
223 }
224
225 // AI now thinks twice in a seccond, if the game engine allows this
226 // if too busy, the period can be many seconds.
227 next_ai_think_ = gametime + 500;
228 ScheduleTasks DueTask = ScheduleTasks::kIdle;
229 DueTask = get_oldest_task(gametime);
230 schedStat[static_cast<uint32_t>(DueTask)] += 1;
231
232 // now AI runs a job selected above to be performed in this turn
233 // (only one but some of them needs to run check_economies() to
234 // guarantee consistency)
235 // job names are selfexplanatory
236 if (DueTask == ScheduleTasks::kBbuildableFieldsCheck) {
243 update_all_buildable_fields(gametime);237 update_all_buildable_fields(gametime);
244 next_bf_check_due_ = gametime + kMinBFCheckInterval;238 taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
245 }239 } else if (DueTask == ScheduleTasks::kRoadCheck) {
246240 if (check_economies()) { // is a must
247 m_buildable_changed = false;
248
249 // perpetually tries to improve roads
250 if (next_road_due_ <= gametime) {
251 next_road_due_ = gametime + 1000;
252
253 if (improve_roads(gametime)) {
254 m_buildable_changed = true;
255 return;241 return;
256 }242 };
257 } else {243 taskDue[ScheduleTasks::kRoadCheck] = gametime + 400;
258 // only go on, after defaultAI tried to improve roads.244 improve_roads(gametime);
259 return;245 } else if (DueTask == ScheduleTasks::kUnbuildableFCheck) {
260 }246 taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
261247 update_all_not_buildable_fields();
262 // NOTE Because of the check above, the following parts of think() are used248 } else if (DueTask == ScheduleTasks::kConsiderAttack) {
263 // NOTE only once every second at maximum. This increases performance and as
264 // NOTE human player_s can not even react that fast, it should not be a
265 // NOTE disadvantage for the defaultAI.
266 // This must be checked every time as changes of bobs in AI area aren't
267 // handled by the AI itself.
268 update_all_not_buildable_fields();
269
270 // considering attack
271 if (next_attack_consideration_due_ <= gametime) {
272 consider_attack(gametime);249 consider_attack(gametime);
273 }250 } else if (DueTask == ScheduleTasks::kCheckEconomies) {
274251 check_economies();
275 // check if anything in the economies changed.252 taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
276 // This needs to be done before new buildings are placed, to ensure that no253 } else if (DueTask == ScheduleTasks::kProductionsitesStats) {
277 // empty economy is left.
278 if (check_economies()) {
279 return;
280 }
281
282 // Before thinking about a new construction, update current stats, to have
283 // a better view on current economy.
284 if (next_stats_update_due_ <= gametime) {
285 update_productionsite_stats(gametime);254 update_productionsite_stats(gametime);
286 }255 } else if (DueTask == ScheduleTasks::kConstructBuilding) {
287256 if (check_economies()) { // economies must be consistent
288 // Now try to build something if possible257 return;
289 if (next_construction_due_ <= gametime) {258 }
290 next_construction_due_ = gametime + 2000;259 taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000;
291
292 if (construct_building(gametime)) {260 if (construct_building(gametime)) {
293 time_of_last_construction_ = gametime;261 time_of_last_construction_ = gametime;
294 m_buildable_changed = true;262 }
295 return;263 } else if (DueTask == ScheduleTasks::kCheckProductionsites) {
296 }264 if (check_economies()) { // economies must be consistent
297 }265 return;
298266 }
299 // verify that our production sites are doing well267 check_productionsites(gametime);
300 if (check_productionsites(gametime)) {268 taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 5000;
301 return;269 } else if (DueTask == ScheduleTasks::kCheckShips) {
302 }270 check_ships(gametime);
303271 } else if (DueTask == ScheduleTasks::KMarineDecisions) {
304 if (check_ships(gametime)) {272 marine_main_decisions(gametime);
305 return;273 } else if (DueTask == ScheduleTasks::kCheckMines) {
306 }274 if (check_economies()) { // economies must be consistent
307275 return;
308 if (marine_main_decisions(gametime)) {276 }
309 return;277 taskDue[ScheduleTasks::kCheckMines] = gametime + 7000; // 7 seconds is enough
310 }278 check_mines_(gametime);
311279 } else if (DueTask == ScheduleTasks::kCheckMilitarysites) {
312 // Check the mines and consider upgrading or destroying one280 check_militarysites(gametime);
313 if (check_mines_(gametime)) {281 } else if (DueTask == ScheduleTasks::kCheckTrainingsites) {
314 return;282 check_trainingsites(gametime);
315 }283 } else if (DueTask == ScheduleTasks::kWareReview) {
316284 if (check_economies()) { // economies must be consistent
317 // consider whether a change of the soldier capacity of some militarysites285 return;
318 // would make sense.286 }
319 if (check_militarysites(gametime)) {287 taskDue[ScheduleTasks::kWareReview] = gametime + 15 * 60 * 1000;
320 return;
321 }
322
323 if (check_trainingsites(gametime)) {
324 return;
325 }
326
327 // improve existing roads!
328 // main part of this improvment is creation 'shortcut roads'
329 // this includes also connection of new buildings
330 if (improve_roads(gametime)) {
331 m_buildable_changed = true;
332 m_mineable_changed = true;
333 return;
334 }
335
336 // once in 15 minutes we increase(or decrease) targets for wares
337 if (next_wares_review_due_ <= gametime) {
338 next_wares_review_due_ = gametime + 15 * 60 * 1000;
339 review_wares_targets(gametime);288 review_wares_targets(gametime);
340 }289 } else if (DueTask == ScheduleTasks::kPrintStats) {
341290 if (check_economies()) { // economies must be consistent
342 // print statistics291 return;
343 if (kPrintStats && next_statistics_report_ <= gametime) {292 }
344 print_stats();293 print_stats(gametime);
345 next_statistics_report_ += 60 * 60 * 1000;
346 }294 }
347}295}
348296
@@ -500,13 +448,6 @@
500 }448 }
501 }449 }
502450
503 num_constructionsites_ = 0;
504 num_milit_constructionsites = 0;
505 num_prod_constructionsites = 0;
506 next_construction_due_ = 0;
507 next_road_due_ = 1000;
508 next_productionsite_check_due_ = 0;
509 inhibit_road_building_ = 0;
510 // atlanteans they consider water as a resource451 // atlanteans they consider water as a resource
511 // (together with mines, stones and wood)452 // (together with mines, stones and wood)
512 if (tribe_->name() == "atlanteans") {453 if (tribe_->name() == "atlanteans") {
@@ -524,7 +465,7 @@
524 port_reserved_coords.insert(hash);465 port_reserved_coords.insert(hash);
525 } while (mr.advance(map));466 } while (mr.advance(map));
526467
527 //the same for NW neighbour of a field468 // the same for NW neighbour of a field
528 Coords c_nw;469 Coords c_nw;
529 map.get_tln(c, &c_nw);470 map.get_tln(c, &c_nw);
530 MapRegion<Area<FCoords>> mr_nw(map, Area<FCoords>(map.get_fcoords(c_nw), 3));471 MapRegion<Area<FCoords>> mr_nw(map, Area<FCoords>(map.get_fcoords(c_nw), 3));
@@ -593,6 +534,22 @@
593 } while (mr.advance(map));534 } while (mr.advance(map));
594 }535 }
595 }536 }
537
538 taskDue[ScheduleTasks::kConstructBuilding] = 0;
539 taskDue[ScheduleTasks::kRoadCheck] = 1000;
540 taskDue[ScheduleTasks::kCheckProductionsites] = 15 * 1000;
541 taskDue[ScheduleTasks::kProductionsitesStats] = 30000;
542 taskDue[ScheduleTasks::kCheckMines] = 30 * 1000;
543 taskDue[ScheduleTasks::kCheckMilitarysites] = 0;
544 taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
545 taskDue[ScheduleTasks::kCheckEconomies] = 1000;
546 taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
547 taskDue[ScheduleTasks::kConsiderAttack] = 300000;
548 taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
549 taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
550 taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
551 taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000;
552 taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
596}553}
597554
598/**555/**
@@ -601,12 +558,12 @@
601 * this shouldn't be used often, as it might hang the game for some 100558 * this shouldn't be used often, as it might hang the game for some 100
602 * milliseconds if the area the computer owns is big.559 * milliseconds if the area the computer owns is big.
603 */560 */
604void DefaultAI::update_all_buildable_fields(const int32_t gametime) {561void DefaultAI::update_all_buildable_fields(const uint32_t gametime) {
605562
606 uint16_t i = 0;563 uint16_t i = 0;
607564
608 while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&565 while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&
609 i < 25) {566 i < 40) {
610 BuildableField& bf = *buildable_fields.front();567 BuildableField& bf = *buildable_fields.front();
611568
612 // check whether we lost ownership of the node569 // check whether we lost ownership of the node
@@ -639,7 +596,7 @@
639 * this shouldn't be used often, as it might hang the game for some 100596 * this shouldn't be used often, as it might hang the game for some 100
640 * milliseconds if the area the computer owns is big.597 * milliseconds if the area the computer owns is big.
641 */598 */
642void DefaultAI::update_all_mineable_fields(const int32_t gametime) {599void DefaultAI::update_all_mineable_fields(const uint32_t gametime) {
643600
644 uint16_t i = 0; // counter, used to track # of checked fields601 uint16_t i = 0; // counter, used to track # of checked fields
645602
@@ -753,7 +710,7 @@
753 }710 }
754 }711 }
755712
756 //testing for near porspaces713 // testing for near porspaces
757 if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {714 if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {
758 field.portspace_nearby_ = ExtendedBool::kFalse;715 field.portspace_nearby_ = ExtendedBool::kFalse;
759 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));716 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));
@@ -1010,9 +967,9 @@
1010}967}
1011968
1012/// Updates the production and MINE sites statistics needed for construction decision.969/// Updates the production and MINE sites statistics needed for construction decision.
1013void DefaultAI::update_productionsite_stats(int32_t const gametime) {970void DefaultAI::update_productionsite_stats(uint32_t const gametime) {
1014 // Updating the stats every 10 seconds should be enough971 // Updating the stats every 10 seconds should be enough
1015 next_stats_update_due_ = gametime + 10000;972 taskDue[ScheduleTasks::kProductionsitesStats] = gametime + 10000;
1016 uint16_t fishers_count = 0; // used for atlanteans only973 uint16_t fishers_count = 0; // used for atlanteans only
1017974
1018 // Reset statistics for all buildings975 // Reset statistics for all buildings
@@ -1092,7 +1049,7 @@
1092// - there is an enemy1049// - there is an enemy
1093// Currently more military buildings are built then needed1050// Currently more military buildings are built then needed
1094// and "optimalization" (dismantling not needed buildings) is done afterwards1051// and "optimalization" (dismantling not needed buildings) is done afterwards
1095bool DefaultAI::construct_building(int32_t gametime) {1052bool DefaultAI::construct_building(uint32_t gametime) {
1096 // Just used for easy checking whether a mine or something else was built.1053 // Just used for easy checking whether a mine or something else was built.
1097 bool mine = false;1054 bool mine = false;
1098 bool field_blocked = false;1055 bool field_blocked = false;
@@ -1115,7 +1072,7 @@
11151072
1116 // here we possible stop building of new buildings1073 // here we possible stop building of new buildings
1117 new_buildings_stop_ = false;1074 new_buildings_stop_ = false;
1118 uint8_t expansion_mode = kResourcesOrDefense;1075 MilitaryStrategy expansion_mode = MilitaryStrategy::kResourcesOrDefense;
11191076
1120 // there are many reasons why to stop building production buildings1077 // there are many reasons why to stop building production buildings
1121 // (note there are numerous exceptions)1078 // (note there are numerous exceptions)
@@ -1146,13 +1103,15 @@
1146 const uint32_t treshold = militarysites.size() / 40 + 2;1103 const uint32_t treshold = militarysites.size() / 40 + 2;
11471104
1148 if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) {1105 if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) {
1149 expansion_mode = kNoNewMilitary;1106 expansion_mode = MilitaryStrategy::kNoNewMilitary;
1150 } else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) {1107 } else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) {
1151 expansion_mode = kDefenseOnly;1108 expansion_mode = MilitaryStrategy::kDefenseOnly;
1109 } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= treshold - 1) {
1110 expansion_mode = MilitaryStrategy::kResourcesOrDefense;
1152 } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {1111 } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
1153 expansion_mode = kResourcesOrDefense;1112 expansion_mode = MilitaryStrategy::kExpansion;
1154 } else {1113 } else {
1155 expansion_mode = kPushExpansion;1114 expansion_mode = MilitaryStrategy::kPushExpansion;
1156 }1115 }
11571116
1158 // we must consider need for mines1117 // we must consider need for mines
@@ -1163,9 +1122,9 @@
1163 if (virtual_mines <= 7) {1122 if (virtual_mines <= 7) {
1164 resource_necessity_mines_ = std::numeric_limits<uint8_t>::max();1123 resource_necessity_mines_ = std::numeric_limits<uint8_t>::max();
1165 } else if (virtual_mines > 19) {1124 } else if (virtual_mines > 19) {
1166 resource_necessity_mines_ = 0;1125 resource_necessity_mines_ = 50;
1167 } else {1126 } else {
1168 const uint32_t tmp = ((18 - virtual_mines) * 255) / 12;1127 const uint32_t tmp = (24 - virtual_mines) * 10;
1169 resource_necessity_mines_ = tmp;1128 resource_necessity_mines_ = tmp;
1170 }1129 }
11711130
@@ -1198,7 +1157,7 @@
11981157
1199 // Remove outdated fields from blocker list1158 // Remove outdated fields from blocker list
1200 for (std::list<BlockedField>::iterator i = blocked_fields.begin(); i != blocked_fields.end();)1159 for (std::list<BlockedField>::iterator i = blocked_fields.begin(); i != blocked_fields.end();)
1201 if (i->blocked_until_ < game().get_gametime()) {1160 if (i->blocked_until_ < gametime) {
1202 i = blocked_fields.erase(i);1161 i = blocked_fields.erase(i);
1203 } else {1162 } else {
1204 ++i;1163 ++i;
@@ -1246,7 +1205,7 @@
1246 ++i) {1205 ++i) {
1247 BuildableField* const bf = *i;1206 BuildableField* const bf = *i;
12481207
1249 if (bf->next_update_due_ < gametime - 8000) {1208 if (bf->next_update_due_ < gametime - 10000) {
1250 continue;1209 continue;
1251 }1210 }
12521211
@@ -1518,7 +1477,7 @@
1518 }1477 }
1519 // we can go above target if there is shortage of logs on stock1478 // we can go above target if there is shortage of logs on stock
1520 else if (bo.total_count() >= bo.cnt_target_ &&1479 else if (bo.total_count() >= bo.cnt_target_ &&
1521 bo.stocklevel_ > 40 + productionsites.size() * 5) {1480 bo.stocklevel_ > 40 + productionsites.size() * 2) {
1522 continue;1481 continue;
1523 }1482 }
15241483
@@ -1679,11 +1638,18 @@
1679 continue;1638 continue;
1680 }1639 }
16811640
1682 if (expansion_mode == kNoNewMilitary) {1641 if (expansion_mode == MilitaryStrategy::kNoNewMilitary) {
1683 continue;1642 continue;
1684 }1643 }
16851644
1686 if (expansion_mode == kDefenseOnly && !bf->enemy_nearby_) {1645 if (expansion_mode == MilitaryStrategy::kDefenseOnly && !bf->enemy_nearby_) {
1646 continue;
1647 }
1648
1649 if (expansion_mode == MilitaryStrategy::kResourcesOrDefense &&
1650 !(bf->unowned_mines_pots_nearby_ || bf->stones_nearby_ || bf->water_nearby_ ||
1651 (bf->distant_water_ && resource_necessity_water_needed_) ||
1652 bf->enemy_nearby_)) {
1687 continue;1653 continue;
1688 }1654 }
16891655
@@ -1694,19 +1660,14 @@
1694 (bo.mountain_conqueror_ || bo.expansion_type_)) {1660 (bo.mountain_conqueror_ || bo.expansion_type_)) {
1695 ;1661 ;
1696 } // it is ok, go on1662 } // it is ok, go on
1697 else if (bf->unowned_land_nearby_ && bo.expansion_type_ &&1663 else if (bo.expansion_type_) {
1698 num_milit_constructionsites <= 1) {
1699 ; // we allow big buildings now
1700 } else if (bf->unowned_land_nearby_ &&
1701 bo.expansion_type_) { // decreasing probability for big buidlings
1702 if (bo.desc->get_size() == 2 && gametime % 2 >= 1) {1664 if (bo.desc->get_size() == 2 && gametime % 2 >= 1) {
1703 continue;1665 continue;
1704 }1666 }
1705 if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {1667 if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {
1706 continue;1668 continue;
1707 }1669 };
1708 }1670 }
1709 // it is ok, go on
1710 else {1671 else {
1711 continue;1672 continue;
1712 } // the building is not suitable for situation1673 } // the building is not suitable for situation
@@ -1719,22 +1680,22 @@
17191680
1720 // a boost to prevent an expansion halt1681 // a boost to prevent an expansion halt
1721 int32_t local_boost = 0;1682 int32_t local_boost = 0;
1722 if (expansion_mode == kPushExpansion) {1683 if (expansion_mode == MilitaryStrategy::kPushExpansion) {
1723 local_boost = 200;1684 local_boost = 200;
1724 }1685 }
17251686
1726 prio = (bf->unowned_land_nearby_ * 2 * resource_necessity_territory_ / 255 +1687 prio = ((bf->unowned_land_nearby_ * 2 * resource_necessity_territory_) / 255 +
1727 bf->unowned_mines_pots_nearby_ * resource_necessity_mines_ / 255 +1688 (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 255 +
1728 bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost +1689 bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost +
1729 bf->water_nearby_ * resource_necessity_water_ / 255);1690 (bf->water_nearby_ * resource_necessity_water_) / 255);
17301691
1731 // special bonus due to remote water for atlanteans1692 // special bonus due to remote water for atlanteans
1732 if (resource_necessity_water_needed_)1693 if (resource_necessity_water_needed_)
1733 prio += bf->distant_water_ * resource_necessity_water_ / 255;1694 prio += (bf->distant_water_ * resource_necessity_water_) / 255;
17341695
1735 //special bonus if a portspace is close1696 // special bonus if a portspace is close
1736 if (bf->portspace_nearby_ == ExtendedBool::kTrue) {1697 if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
1737 if (num_ports == 0){1698 if (num_ports == 0) {
1738 prio += 25;1699 prio += 25;
1739 } else {1700 } else {
1740 prio += 5;1701 prio += 5;
@@ -2044,16 +2005,13 @@
2044 // set the type of update that is needed2005 // set the type of update that is needed
2045 if (mine) {2006 if (mine) {
2046 next_mine_construction_due_ = gametime + kBusyMineUpdateInterval;2007 next_mine_construction_due_ = gametime + kBusyMineUpdateInterval;
2047
2048 } else {
2049 m_buildable_changed = true;
2050 }2008 }
20512009
2052 return true;2010 return true;
2053}2011}
20542012
2055// improves current road system2013// improves current road system
2056bool DefaultAI::improve_roads(int32_t gametime) {2014bool DefaultAI::improve_roads(uint32_t gametime) {
20572015
2058 // first force a split on roads that are longer than 3 parts2016 // first force a split on roads that are longer than 3 parts
2059 // with exemption when there is too few building spots2017 // with exemption when there is too few building spots
@@ -2093,7 +2051,7 @@
2093 roads.pop_front();2051 roads.pop_front();
20942052
2095 // occasionaly we test if the road can be dismounted2053 // occasionaly we test if the road can be dismounted
2096 if (gametime % 25 == 0) {2054 if (gametime % 5 == 0) {
2097 const Road& road = *roads.front();2055 const Road& road = *roads.front();
2098 if (dispensable_road_test(*const_cast<Road*>(&road))) {2056 if (dispensable_road_test(*const_cast<Road*>(&road))) {
2099 game().send_player_bulldoze(*const_cast<Road*>(&road));2057 game().send_player_bulldoze(*const_cast<Road*>(&road));
@@ -2134,14 +2092,25 @@
2134 return true;2092 return true;
2135 }2093 }
21362094
2095 bool is_warehouse = false;
2096 if (Building* b = flag.get_building()) {
2097 BuildingObserver& bo = get_building_observer(b->descr().name().c_str());
2098 if (bo.type == BuildingObserver::WAREHOUSE) {
2099 is_warehouse = true;
2100 }
2101 }
2102
2137 // if this is end flag (or sole building) or just randomly2103 // if this is end flag (or sole building) or just randomly
2138 if (flag.nr_of_roads() <= 1 || gametime % 200 == 0) {2104 if (flag.nr_of_roads() <= 1 || gametime % 10 == 0) {
2139 create_shortcut_road(flag, 13, 20, gametime);2105 create_shortcut_road(flag, 11, 20, gametime);
2140 inhibit_road_building_ = gametime + 800;2106 inhibit_road_building_ = gametime + 800;
2141 }2107 // a warehouse with 3 or less roads
2142 // this is when a flag is full2108 } else if (is_warehouse && flag.nr_of_roads() <= 3) {
2143 else if (flag.current_wares() > 6 && gametime % 10 == 0) {2109 create_shortcut_road(flag, 9, -1, gametime);
2144 create_shortcut_road(flag, 9, 0, gametime);2110 inhibit_road_building_ = gametime + 400;
2111 // and when a flag is full with wares
2112 } else if (flag.current_wares() > 5) {
2113 create_shortcut_road(flag, 9, -2, gametime);
2145 inhibit_road_building_ = gametime + 400;2114 inhibit_road_building_ = gametime + 400;
2146 }2115 }
21472116
@@ -2227,7 +2196,7 @@
2227// or other economy2196// or other economy
2228bool DefaultAI::create_shortcut_road(const Flag& flag,2197bool DefaultAI::create_shortcut_road(const Flag& flag,
2229 uint16_t checkradius,2198 uint16_t checkradius,
2230 uint16_t minred,2199 int16_t min_reduction,
2231 int32_t gametime) {2200 int32_t gametime) {
22322201
2233 // Increasing the failed_connection_tries counter2202 // Increasing the failed_connection_tries counter
@@ -2246,15 +2215,27 @@
22462215
2247 // first we deal with situations when this is economy with no warehouses2216 // first we deal with situations when this is economy with no warehouses
2248 // and this is a flag belonging to a building/constructionsite2217 // and this is a flag belonging to a building/constructionsite
2218 // such economy must get dismantle grace time (if not set yet)
2219 // end sometimes extended checkradius
2249 if (flag.get_economy()->warehouses().empty() && flag.get_building()) {2220 if (flag.get_economy()->warehouses().empty() && flag.get_building()) {
22502221
2222 // occupied military buildings get special treatment
2223 //(extended grace time + longer radius)
2224 bool occupied_military_ = false;
2225 Building* b = flag.get_building();
2226 if (upcast(MilitarySite, militb, b)) {
2227 if (militb->stationed_soldiers().size() > 0) {
2228 occupied_military_ = true;
2229 }
2230 }
2231
2251 // if we are within grace time, it is OK, just go on2232 // if we are within grace time, it is OK, just go on
2252 if (eco->dismantle_grace_time_ > gametime &&2233 if (eco->dismantle_grace_time_ > gametime &&
2253 eco->dismantle_grace_time_ != std::numeric_limits<int32_t>::max()) {2234 eco->dismantle_grace_time_ != std::numeric_limits<int32_t>::max()) {
2254 ;2235 ;
22552236
2256 // if grace time is not set, this is probably first time without a warehouse and we must2237 // if grace time is not set, this is probably first time without a warehouse and we must
2257 // set it2238 // set it
2258 } else if (eco->dismantle_grace_time_ == std::numeric_limits<int32_t>::max()) {2239 } else if (eco->dismantle_grace_time_ == std::numeric_limits<int32_t>::max()) {
22592240
2260 // constructionsites2241 // constructionsites
@@ -2267,27 +2248,16 @@
2267 eco->dismantle_grace_time_ = gametime + 60 * 60 * 1000; // one hour should be enough2248 eco->dismantle_grace_time_ = gametime + 60 * 60 * 1000; // one hour should be enough
2268 } else { // other constructionsites, usually new (standalone) constructionsites2249 } else { // other constructionsites, usually new (standalone) constructionsites
2269 eco->dismantle_grace_time_ =2250 eco->dismantle_grace_time_ =
2270 gametime + 30 * 1000 + // very shot time is enough2251 gametime + 30 * 1000 + // very shot time is enough
2271 (eco->flags.size() * 30 * 1000); // + 30 seconds for every flag in economy2252 (eco->flags.size() * 30 * 1000); // + 30 seconds for every flag in economy
2272 }2253 }
22732254
2274 // buildings2255 // buildings
2275 } else {2256 } else {
22762257
2277 //occupied military buildings get special treatment
2278 //(extended grace time)
2279 bool occupied_military_ = false;
2280 Building* b = flag.get_building();
2281 if (upcast(MilitarySite, militb, b)) {
2282 if (militb->stationed_soldiers().size() > 0) {
2283 occupied_military_ = true;
2284 }
2285 }
2286
2287 if (occupied_military_) {2258 if (occupied_military_) {
2288 eco->dismantle_grace_time_ =2259 eco->dismantle_grace_time_ =
2289 (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);2260 (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
2290 checkradius += 3;
22912261
2292 } else { // for other normal buildings2262 } else { // for other normal buildings
2293 eco->dismantle_grace_time_ =2263 eco->dismantle_grace_time_ =
@@ -2298,9 +2268,17 @@
2298 // we have passed grace_time - it is time to dismantle2268 // we have passed grace_time - it is time to dismantle
2299 } else {2269 } else {
2300 last_attempt_ = true;2270 last_attempt_ = true;
2301 //we increase a check radius in last attempt2271 // we increase a check radius in last attempt
2302 checkradius += 2;2272 checkradius += 2;
2303 }2273 }
2274
2275 // and bonus for occupied military buildings:
2276 if (occupied_military_) {
2277 checkradius += 4;
2278 }
2279
2280 // and generally increase radius for unconnected buildings
2281 checkradius += 2;
2304 }2282 }
23052283
2306 Map& map = game().map();2284 Map& map = game().map();
@@ -2456,7 +2434,7 @@
2456 NearFlag& nf = nearflags.at(i);2434 NearFlag& nf = nearflags.at(i);
24572435
2458 // terminating looping if reduction is too low (nearflags are sorted by reduction)2436 // terminating looping if reduction is too low (nearflags are sorted by reduction)
2459 if ((nf.cost_ - nf.distance_) < minred) {2437 if ((nf.cost_ - nf.distance_) < min_reduction) {
2460 return false;2438 return false;
2461 }2439 }
24622440
@@ -2464,7 +2442,7 @@
2464 // usually we allow connecting only if both flags are closer then 'checkradius-2'2442 // usually we allow connecting only if both flags are closer then 'checkradius-2'
2465 // with exeption the flag belongs to a small economy (typically a new building not connected2443 // with exeption the flag belongs to a small economy (typically a new building not connected
2466 // yet)2444 // yet)
2467 if ((nf.cost_ - nf.distance_) >= minred && nf.distance_ >= 2 &&2445 if ((nf.cost_ - nf.distance_) >= min_reduction && nf.distance_ >= 2 &&
2468 nf.distance_ < checkradius - 2) {2446 nf.distance_ < checkradius - 2) {
24692447
2470 // sometimes the shortest road is not the buildable, even if map.findpath claims so2448 // sometimes the shortest road is not the buildable, even if map.findpath claims so
@@ -2481,7 +2459,7 @@
2481 map.findpath(flag.get_position(), nf.flag->get_position(), 0, path, check);2459 map.findpath(flag.get_position(), nf.flag->get_position(), 0, path, check);
24822460
2483 if (pathcost >= 0) {2461 if (pathcost >= 0) {
2484 if (static_cast<int32_t>(nf.cost_ - path.get_nsteps()) > minred) {2462 if (static_cast<int32_t>(nf.cost_ - path.get_nsteps()) > min_reduction) {
2485 game().send_player_build_road(player_number(), path);2463 game().send_player_build_road(player_number(), path);
2486 return true;2464 return true;
2487 }2465 }
@@ -2551,13 +2529,11 @@
2551 *2529 *
2552 * \returns true, if something was changed.2530 * \returns true, if something was changed.
2553 */2531 */
2554bool DefaultAI::check_productionsites(int32_t gametime) {2532bool DefaultAI::check_productionsites(uint32_t gametime) {
2555 if ((next_productionsite_check_due_ > gametime) || productionsites.empty()) {2533 if (productionsites.empty()) {
2556 return false;2534 return false;
2557 }2535 }
25582536
2559 next_productionsite_check_due_ = gametime + 4000;
2560
2561 bool changed = false;2537 bool changed = false;
2562 // Reorder and set new values; - better now because there are multiple returns in the function2538 // Reorder and set new values; - better now because there are multiple returns in the function
2563 productionsites.push_back(productionsites.front());2539 productionsites.push_back(productionsites.front());
@@ -2695,9 +2671,9 @@
26952671
2696 // Wells handling2672 // Wells handling
2697 if (site.bo->mines_water_) {2673 if (site.bo->mines_water_) {
2698 if (site.unoccupied_till_ + 6 * 60 * 1000 < game().get_gametime() &&2674 if (site.unoccupied_till_ + 6 * 60 * 1000 < gametime &&
2699 site.site->get_statistics_percent() == 0) {2675 site.site->get_statistics_percent() == 0) {
2700 site.bo->last_dismantle_time_ = game().get_gametime();2676 site.bo->last_dismantle_time_ = gametime;
2701 flags_to_be_removed.push_back(site.site->base_flag().get_position());2677 flags_to_be_removed.push_back(site.site->base_flag().get_position());
2702 game().send_player_dismantle(*site.site);2678 game().send_player_dismantle(*site.site);
27032679
@@ -2742,7 +2718,7 @@
2742 return true;2718 return true;
2743 }2719 }
27442720
2745 if (site.unoccupied_till_ + 6 * 60 * 1000 < game().get_gametime() &&2721 if (site.unoccupied_till_ + 6 * 60 * 1000 < gametime &&
2746 site.site->get_statistics_percent() == 0) {2722 site.site->get_statistics_percent() == 0) {
2747 // it is possible that there are stones but quary is not able to mine them2723 // it is possible that there are stones but quary is not able to mine them
2748 site.bo->last_dismantle_time_ = game().get_gametime();2724 site.bo->last_dismantle_time_ = game().get_gametime();
@@ -2760,7 +2736,7 @@
2760 &&2736 &&
2761 site.bo->production_hint_ == -1 // not a renewing building (forester...)2737 site.bo->production_hint_ == -1 // not a renewing building (forester...)
2762 &&2738 &&
2763 site.unoccupied_till_ + 10 * 60 * 1000 < game().get_gametime() // > 10 minutes old2739 site.unoccupied_till_ + 10 * 60 * 1000 < gametime // > 10 minutes old
2764 &&2740 &&
2765 site.site->can_start_working() // building is occupied2741 site.site->can_start_working() // building is occupied
2766 &&2742 &&
@@ -2831,7 +2807,7 @@
28312807
2832 // logs can be stored also in productionsites, they are counted as on stock2808 // logs can be stored also in productionsites, they are counted as on stock
2833 // but are no available for random production site2809 // but are no available for random production site
2834 int16_t score = site.bo->stocklevel_ - productionsites.size() * 5;2810 int16_t score = site.bo->stocklevel_ - productionsites.size() * 2;
28352811
2836 if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) {2812 if (score > 200 && site.bo->cnt_built_ > site.bo->cnt_target_) {
28372813
@@ -2860,15 +2836,17 @@
2860// - build a ship2836// - build a ship
2861// - start preparation for expedition2837// - start preparation for expedition
2862bool DefaultAI::marine_main_decisions(uint32_t const gametime) {2838bool DefaultAI::marine_main_decisions(uint32_t const gametime) {
2863 if (gametime < next_marine_decisions_due) {2839 if (gametime < taskDue[ScheduleTasks::KMarineDecisions]) {
2864 return false;2840 return false;
2865 }2841 }
2866 next_marine_decisions_due = gametime + kMarineDecisionInterval;
28672842
2868 if (!seafaring_economy) {2843 if (!seafaring_economy) {
2844 taskDue[ScheduleTasks::KMarineDecisions] = std::numeric_limits<uint32_t>::max();
2869 return false;2845 return false;
2870 }2846 }
28712847
2848 taskDue[ScheduleTasks::KMarineDecisions] = gametime + kMarineDecisionInterval;
2849
2872 // getting some base statistics2850 // getting some base statistics
2873 player_ = game().get_player(player_number());2851 player_ = game().get_player(player_number());
2874 uint16_t ports_count = 0;2852 uint16_t ports_count = 0;
@@ -2918,7 +2896,7 @@
2918 }2896 }
2919 }2897 }
29202898
2921 enum class FleetStatus : uint8_t {kNeedShip = 0, kEnoughShips = 1, kDoNothing = 2 };2899 enum class FleetStatus : uint8_t {kNeedShip = 0, kEnoughShips = 1, kDoNothing = 2};
29222900
2923 // now we must compare ports vs ships to decide if new ship is needed or new expedition can start2901 // now we must compare ports vs ships to decide if new ship is needed or new expedition can start
2924 FleetStatus enough_ships = FleetStatus::kDoNothing;2902 FleetStatus enough_ships = FleetStatus::kDoNothing;
@@ -2970,16 +2948,17 @@
29702948
2971// This identifies ships that are waiting for command2949// This identifies ships that are waiting for command
2972bool DefaultAI::check_ships(uint32_t const gametime) {2950bool DefaultAI::check_ships(uint32_t const gametime) {
2973 if (gametime < next_ship_check_due) {2951 if (gametime < taskDue[ScheduleTasks::kCheckShips]) {
2974 return false;2952 return false;
2975 }2953 }
29762954
2977 next_ship_check_due = gametime + kShipCheckInterval;
2978
2979 if (!seafaring_economy) {2955 if (!seafaring_economy) {
2956 taskDue[ScheduleTasks::kCheckShips] = std::numeric_limits<int32_t>::max();
2980 return false;2957 return false;
2981 }2958 }
29822959
2960 bool action_taken = false;
2961
2983 if (!allships.empty()) {2962 if (!allships.empty()) {
2984 // iterating over ships and executing what is needed2963 // iterating over ships and executing what is needed
2985 for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {2964 for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
@@ -3001,6 +2980,7 @@
3001 // if ships is waiting for command2980 // if ships is waiting for command
3002 if (i->waiting_for_command_) {2981 if (i->waiting_for_command_) {
3003 expedition_management(*i);2982 expedition_management(*i);
2983 action_taken = true;
3004 }2984 }
3005 }2985 }
3006 }2986 }
@@ -3036,6 +3016,12 @@
3036 marineTaskQueue_.pop_back();3016 marineTaskQueue_.pop_back();
3037 }3017 }
30383018
3019 if (action_taken) {
3020 taskDue[ScheduleTasks::kCheckShips] = gametime + kShipCheckInterval;
3021 } else {
3022 taskDue[ScheduleTasks::kCheckShips] = gametime + 3 * kShipCheckInterval;
3023 }
3024
3039 return true;3025 return true;
3040}3026}
30413027
@@ -3045,11 +3031,11 @@
3045 *3031 *
3046 * \returns true, if something was changed.3032 * \returns true, if something was changed.
3047 */3033 */
3048bool DefaultAI::check_mines_(int32_t const gametime) {3034bool DefaultAI::check_mines_(uint32_t const gametime) {
3049 if ((next_mine_check_due_ > gametime) || mines_.empty())3035 if (mines_.empty()) {
3050 return false;3036 return false;
3037 }
30513038
3052 next_mine_check_due_ = gametime + 7000; // 7 seconds is enough
3053 // Reorder and set new values; - due to returns within the function3039 // Reorder and set new values; - due to returns within the function
3054 mines_.push_back(mines_.front());3040 mines_.push_back(mines_.front());
3055 mines_.pop_front();3041 mines_.pop_front();
@@ -3228,14 +3214,14 @@
32283214
3229// this function only manipulates with trainingsites' inputs priority3215// this function only manipulates with trainingsites' inputs priority
3230// decreases it when too many unoccupied military buildings3216// decreases it when too many unoccupied military buildings
3231bool DefaultAI::check_trainingsites(int32_t gametime) {3217bool DefaultAI::check_trainingsites(uint32_t gametime) {
3232 if (next_trainingsites_check_due_ > gametime) {3218 if (taskDue[ScheduleTasks::kCheckTrainingsites] > gametime) {
3233 return false;3219 return false;
3234 }3220 }
3235 if (!trainingsites.empty()) {3221 if (!trainingsites.empty()) {
3236 next_trainingsites_check_due_ = gametime + kTrainingSitesCheckInterval;3222 taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval;
3237 } else {3223 } else {
3238 next_trainingsites_check_due_ = gametime + 3 * kTrainingSitesCheckInterval;3224 taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 5 * kTrainingSitesCheckInterval;
3239 }3225 }
32403226
3241 uint8_t new_priority = DEFAULT_PRIORITY;3227 uint8_t new_priority = DEFAULT_PRIORITY;
@@ -3264,13 +3250,13 @@
3264 *3250 *
3265 * \returns true if something was changed3251 * \returns true if something was changed
3266 */3252 */
3267bool DefaultAI::check_militarysites(int32_t gametime) {3253bool DefaultAI::check_militarysites(uint32_t gametime) {
3268 if (next_militarysite_check_due_ > gametime) {3254 if (taskDue[ScheduleTasks::kCheckMilitarysites] > gametime) {
3269 return false;3255 return false;
3270 }3256 }
32713257
3272 // just to be sure the value is reset3258 // just to be sure the value is reset
3273 next_militarysite_check_due_ = gametime + 4 * 1000; // 4 seconds is really fine3259 taskDue[ScheduleTasks::kCheckMilitarysites] = gametime + 4 * 1000; // 4 seconds is really fine
3274 // even if there are no finished & attended military sites, probably there are ones just in3260 // even if there are no finished & attended military sites, probably there are ones just in
3275 // construction3261 // construction
3276 unstationed_milit_buildings_ = 0;3262 unstationed_milit_buildings_ = 0;
@@ -3385,7 +3371,7 @@
3385 // reorder:;3371 // reorder:;
3386 militarysites.push_back(militarysites.front());3372 militarysites.push_back(militarysites.front());
3387 militarysites.pop_front();3373 militarysites.pop_front();
3388 next_militarysite_check_due_ = gametime + 5 * 1000; // 10 seconds is really fine3374 taskDue[ScheduleTasks::kCheckMilitarysites] = gametime + 5 * 1000; // 10 seconds is really fine
3389 return changed;3375 return changed;
3390}3376}
33913377
@@ -3833,7 +3819,7 @@
3833 }3819 }
38343820
3835 // Let defaultAI try to directly connect the constructionsite3821 // Let defaultAI try to directly connect the constructionsite
3836 next_road_due_ = game().get_gametime();3822 taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime();
3837 } else {3823 } else {
3838 ++bo.cnt_built_;3824 ++bo.cnt_built_;
38393825
@@ -3989,9 +3975,6 @@
3989 }3975 }
3990 }3976 }
3991 }3977 }
3992
3993 m_buildable_changed = true;
3994 m_mineable_changed = true;
3995}3978}
39963979
3997// Checks that supply line exists for given building.3980// Checks that supply line exists for given building.
@@ -4043,7 +4026,7 @@
40434026
4044 // Only useable, if it owns at least one militarysite4027 // Only useable, if it owns at least one militarysite
4045 if (militarysites.empty()) {4028 if (militarysites.empty()) {
4046 next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;4029 taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
4047 return false;4030 return false;
4048 }4031 }
40494032
@@ -4092,7 +4075,7 @@
4092 if (current_margin < needed_margin) { // no attacking!4075 if (current_margin < needed_margin) { // no attacking!
4093 last_attack_target_.x = std::numeric_limits<uint16_t>::max();4076 last_attack_target_.x = std::numeric_limits<uint16_t>::max();
4094 last_attack_target_.y = std::numeric_limits<uint16_t>::max();4077 last_attack_target_.y = std::numeric_limits<uint16_t>::max();
4095 next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;4078 taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
4096 return false;4079 return false;
4097 }4080 }
40984081
@@ -4131,7 +4114,7 @@
41314114
4132 // if we cannot attack anybody, terminating...4115 // if we cannot attack anybody, terminating...
4133 if (!any_attackable) {4116 if (!any_attackable) {
4134 next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;4117 taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
4135 last_attack_target_.x = std::numeric_limits<uint16_t>::max();4118 last_attack_target_.x = std::numeric_limits<uint16_t>::max();
4136 last_attack_target_.y = std::numeric_limits<uint16_t>::max();4119 last_attack_target_.y = std::numeric_limits<uint16_t>::max();
4137 return false;4120 return false;
@@ -4245,7 +4228,7 @@
42454228
4246 game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers);4229 game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers);
4247 last_attack_target_ = best_wh_target->get_position();4230 last_attack_target_ = best_wh_target->get_position();
4248 next_attack_consideration_due_ = (gametime % 10 + 10) * 1000 + gametime;4231 taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
4249 next_attack_waittime_ = 10;4232 next_attack_waittime_ = 10;
4250 return true;4233 return true;
42514234
@@ -4261,11 +4244,11 @@
42614244
4262 game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers);4245 game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers);
4263 last_attack_target_ = best_ms_target->get_position();4246 last_attack_target_ = best_ms_target->get_position();
4264 next_attack_consideration_due_ = (gametime % 10 + 10) * 1000 + gametime;4247 taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
4265 next_attack_waittime_ = 10;4248 next_attack_waittime_ = 10;
4266 return true;4249 return true;
4267 } else {4250 } else {
4268 next_attack_consideration_due_ = next_attack_waittime_ * 1000 + gametime;4251 taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
4269 last_attack_target_.x = std::numeric_limits<uint16_t>::max();4252 last_attack_target_.x = std::numeric_limits<uint16_t>::max();
4270 last_attack_target_.y = std::numeric_limits<uint16_t>::max();4253 last_attack_target_.y = std::numeric_limits<uint16_t>::max();
4271 return false;4254 return false;
@@ -4274,7 +4257,7 @@
42744257
4275// This runs once in 15 minutes, and adjust wares targets based on number of4258// This runs once in 15 minutes, and adjust wares targets based on number of
4276// productionsites and ports4259// productionsites and ports
4277void DefaultAI::review_wares_targets(int32_t const gametime) {4260void DefaultAI::review_wares_targets(uint32_t const gametime) {
42784261
4279 player_ = game().get_player(player_number());4262 player_ = game().get_player(player_number());
4280 tribe_ = &player_->tribe();4263 tribe_ = &player_->tribe();
@@ -4300,13 +4283,35 @@
4300 }4283 }
4301}4284}
43024285
4286// run over dueTasks map and returns task with lower duetime
4287DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) {
4288
4289 uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
4290 ScheduleTasks DueTask = ScheduleTasks::kIdle; // default
4291 taskDue[ScheduleTasks::kIdle] = gametime;
4292
4293 for (std::pair<ScheduleTasks, uint32_t> task : taskDue) {
4294 if (task.second < oldestTaskTime) {
4295 oldestTaskTime = task.second;
4296 DueTask = task.first;
4297 }
4298 }
4299 return DueTask;
4300}
4301
4303// This prints some basic statistics during a game to the command line -4302// This prints some basic statistics during a game to the command line -
4304// missing materials and counts of different types of buildings.4303// missing materials and counts of different types of buildings.
4305// The main purpose of this is when a game creator needs to finetune a map4304// The main purpose of this is when a game creator needs to finetune a map
4306// and needs to know what resourcess are missing for which player and so on.4305// and needs to know what resourcess are missing for which player and so on.
4307// By default it is off (see kPrintStats)4306// By default it is off (see kPrintStats)
4308// TODO(tiborb ?): - it would be nice to have this activated by a command line switch4307// TODO(tiborb ?): - it would be nice to have this activated by a command line switch
4309void DefaultAI::print_stats() {4308void DefaultAI::print_stats(uint32_t const gametime) {
4309
4310 if (!kPrintStats) {
4311 taskDue[ScheduleTasks::kPrintStats] = std::numeric_limits<int32_t>::max();
4312 return;
4313 }
4314 taskDue[ScheduleTasks::kPrintStats] = gametime + 30 * 60 * 1000;
43104315
4311 PlayerNumber const pn = player_number();4316 PlayerNumber const pn = player_number();
43124317
43134318
=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h 2015-02-16 21:19:59 +0000
+++ src/ai/defaultai.h 2015-03-06 20:13:14 +0000
@@ -78,8 +78,33 @@
78 DEFENSIVE = 0,78 DEFENSIVE = 0,
79 };79 };
8080
81 enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers };81 enum class WalkSearch : uint8_t {kAnyPlayer, kOtherPlayers};
82 enum class NewShip : uint8_t {kBuilt, kFoundOnLoad };82 enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
83 enum class ScheduleTasks : uint8_t {
84 kBbuildableFieldsCheck,
85 kRoadCheck,
86 kUnbuildableFCheck,
87 kConsiderAttack,
88 kCheckEconomies,
89 kProductionsitesStats,
90 kConstructBuilding,
91 kCheckProductionsites,
92 kCheckShips,
93 KMarineDecisions,
94 kCheckMines,
95 kWareReview,
96 kPrintStats,
97 kIdle,
98 kCheckMilitarysites,
99 kCheckTrainingsites
100 };
101 enum class MilitaryStrategy : uint8_t {
102 kNoNewMilitary,
103 kDefenseOnly,
104 kResourcesOrDefense,
105 kExpansion,
106 kPushExpansion
107 };
83108
84 /// Implementation for Aggressive109 /// Implementation for Aggressive
85 struct AggressiveImpl : public ComputerPlayer::Implementation {110 struct AggressiveImpl : public ComputerPlayer::Implementation {
@@ -122,21 +147,24 @@
122private:147private:
123 void late_initialization();148 void late_initialization();
124149
125 void update_all_buildable_fields(int32_t);150 void update_all_buildable_fields(uint32_t);
126 void update_all_mineable_fields(int32_t);151 void update_all_mineable_fields(uint32_t);
127 void update_all_not_buildable_fields();152 void update_all_not_buildable_fields();
128153
129 void update_buildable_field(BuildableField&, uint16_t = 6, bool = false);154 void update_buildable_field(BuildableField&, uint16_t = 6, bool = false);
130 void update_mineable_field(MineableField&);155 void update_mineable_field(MineableField&);
131156
132 void update_productionsite_stats(int32_t);157 void update_productionsite_stats(uint32_t);
133158
134 void check_ware_necessity(BuildingObserver& bo,159 void check_ware_necessity(BuildingObserver& bo,
135 bool* output_is_needed,160 bool* output_is_needed,
136 int16_t* max_preciousness,161 int16_t* max_preciousness,
137 int16_t* max_needed_preciousness);162 int16_t* max_needed_preciousness);
138163
139 bool construct_building(int32_t);164
165 ScheduleTasks get_oldest_task(uint32_t);
166
167 bool construct_building(uint32_t);
140168
141 uint32_t coords_hash(Widelands::Coords coords) {169 uint32_t coords_hash(Widelands::Coords coords) {
142 uint32_t hash = coords.x << 16 | coords.y;170 uint32_t hash = coords.x << 16 | coords.y;
@@ -153,27 +181,26 @@
153 // all road management is invoked by function improve_roads()181 // all road management is invoked by function improve_roads()
154 // if needed it calls create_shortcut_road() with a flag from which182 // if needed it calls create_shortcut_road() with a flag from which
155 // new road should be considered (or is needed)183 // new road should be considered (or is needed)
156 bool improve_roads(int32_t);184 bool improve_roads(uint32_t);
157 bool create_shortcut_road(const Widelands::Flag&,185 bool create_shortcut_road(const Widelands::Flag&,
158 uint16_t maxcheckradius,186 uint16_t maxcheckradius,
159 uint16_t minred,187 int16_t minReduction,
160 const int32_t gametime);188 const int32_t gametime);
161 // trying to identify roads that might be removed189 // trying to identify roads that might be removed
162 bool dispensable_road_test(Widelands::Road&);190 bool dispensable_road_test(Widelands::Road&);
163 bool check_economies();191 bool check_economies();
164 bool check_productionsites(int32_t);192 bool check_productionsites(uint32_t);
165 bool check_trainingsites(int32_t);193 bool check_trainingsites(uint32_t);
166 bool check_mines_(int32_t);194 bool check_mines_(uint32_t);
167 bool check_militarysites(int32_t);195 bool check_militarysites(uint32_t);
168 bool marine_main_decisions(uint32_t);196 bool marine_main_decisions(uint32_t);
169 bool check_ships(uint32_t);197 bool check_ships(uint32_t);
170 void print_stats();198 void print_stats(uint32_t);
171 uint32_t get_stocklevel_by_hint(size_t);199 uint32_t get_stocklevel_by_hint(size_t);
172 uint32_t get_stocklevel(BuildingObserver&);200 uint32_t get_stocklevel(BuildingObserver&);
173 uint32_t get_warehoused_stock(Widelands::WareIndex wt);201 uint32_t get_warehoused_stock(Widelands::WareIndex wt);
174 uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_202 uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_
175 void check_helpersites(int32_t);203 void review_wares_targets(uint32_t);
176 void review_wares_targets(int32_t);
177204
178 // sometimes scanning an area in radius gives inappropriate results, so this is to verify that205 // sometimes scanning an area in radius gives inappropriate results, so this is to verify that
179 // other player is accessible206 // other player is accessible
@@ -213,8 +240,8 @@
213 // Variables of default AI240 // Variables of default AI
214 uint8_t type_;241 uint8_t type_;
215242
216 bool m_buildable_changed;243 // collect statistics on how many times which job was run
217 bool m_mineable_changed;244 uint32_t schedStat[20] = {0};
218245
219 Widelands::Player* player_;246 Widelands::Player* player_;
220 Widelands::TribeDescr const* tribe_;247 Widelands::TribeDescr const* tribe_;
@@ -240,26 +267,15 @@
240 std::list<WarehouseSiteObserver> warehousesites;267 std::list<WarehouseSiteObserver> warehousesites;
241 std::list<TrainingSiteObserver> trainingsites;268 std::list<TrainingSiteObserver> trainingsites;
242 std::list<ShipObserver> allships;269 std::list<ShipObserver> allships;
270 std::map<ScheduleTasks, uint32_t> taskDue;
243271
244 std::vector<WareObserver> wares;272 std::vector<WareObserver> wares;
245273
246 int32_t next_road_due_;274 uint32_t next_ai_think_;
247 int32_t next_stats_update_due_;275 uint32_t next_mine_construction_due_;
248 int32_t next_construction_due_;276 uint32_t inhibit_road_building_;
249 int32_t next_mine_construction_due_;277 uint32_t time_of_last_construction_;
250 int32_t next_productionsite_check_due_;278 uint32_t enemy_last_seen_;
251 int32_t next_mine_check_due_;
252 int32_t next_militarysite_check_due_;
253 uint32_t next_ship_check_due;
254 uint32_t next_marine_decisions_due;
255 int32_t next_attack_consideration_due_;
256 int32_t next_trainingsites_check_due_;
257 int32_t next_bf_check_due_;
258 int32_t next_wares_review_due_;
259 int32_t next_statistics_report_;
260 int32_t inhibit_road_building_;
261 int32_t time_of_last_construction_;
262 int32_t enemy_last_seen_;
263279
264 uint16_t numof_warehouses_;280 uint16_t numof_warehouses_;
265281
@@ -278,17 +294,17 @@
278 // is belogning to)294 // is belogning to)
279 uint16_t military_under_constr_;295 uint16_t military_under_constr_;
280 uint16_t military_last_dismantle_;296 uint16_t military_last_dismantle_;
281 int32_t military_last_build_; // sometimes expansions just stops, this is time of last military297 uint32_t military_last_build_; // sometimes expansions just stops, this is time of last military
282 // building build298 // building build
283 Widelands::Coords299 Widelands::Coords
284 last_attack_target_; // flag to abuilding (position) that was attacked last time300 last_attack_target_; // flag to abuilding (position) that was attacked last time
285 int32_t next_attack_waittime_; // second till the next attack consideration301 uint32_t next_attack_waittime_; // second till the next attack consideration
286 bool seafaring_economy; // false by default, until first port space is found302 bool seafaring_economy; // false by default, until first port space is found
287 uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory303 uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory
288 // it decreases with failed scans304 // it decreases with failed scans
289 int32_t spots_; // sum of buildable fields305 int32_t spots_; // sum of buildable fields
290306
291 enum {kReprioritize, kStopShipyard, kStapShipyard };307 enum {kReprioritize, kStopShipyard, kStapShipyard};
292308
293 std::vector<int16_t> marineTaskQueue_;309 std::vector<int16_t> marineTaskQueue_;
294310

Subscribers

People subscribed via source and target branches

to status/vote changes: