Merge lp:~widelands-dev/widelands/ai_sched_speedup into lp:widelands

Proposed by TiborB
Status: Merged
Merged at revision: 7633
Proposed branch: lp:~widelands-dev/widelands/ai_sched_speedup
Merge into: lp:widelands
Diff against target: 768 lines (+339/-235)
3 files modified
src/ai/ai_help_structs.h (+39/-0)
src/ai/defaultai.cc (+288/-209)
src/ai/defaultai.h (+12/-26)
To merge this branch: bzr merge lp:~widelands-dev/widelands/ai_sched_speedup
Reviewer Review Type Date Requested Status
GunChleoc Approve
Review via email: mp+277296@code.launchpad.net

Description of the change

Currently AI runs only one job per call, and this change allows up to 4 jobs per call, depending on the delay of most delayed job. Moreover, once jobs to run are identified, they are sorted by own priority, because there are dependencies between them, like check_buildable_fields should be done before construct_building and so on...

The goal is to allow AI cope better with higher speeds of game. However warnings to terminal are preserved, so it is usefull to watch for them when testing/playing...

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

oh, I found some mathematical issues here, I will push new version tonight

Revision history for this message
GunChleoc (gunchleoc) wrote :
Revision history for this message
TiborB (tiborb95) wrote :

I believe all your comments were incorporated...

Revision history for this message
GunChleoc (gunchleoc) wrote :

Yes, go ahead and merge :)

review: Approve

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-11-11 09:53:54 +0000
3+++ src/ai/ai_help_structs.h 2015-11-21 18:26:32 +0000
4@@ -44,6 +44,26 @@
5 enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse};
6 enum class BuildingNecessity : uint8_t
7 {kForced, kNeeded, kNotNeeded, kUnset, kNotBuildable, kAllowed, kNeededPending};
8+enum class SchedulerTaskId : uint8_t {
9+ kBbuildableFieldsCheck,
10+ kMineableFieldsCheck,
11+ kRoadCheck,
12+ kUnbuildableFCheck,
13+ kCheckEconomies,
14+ kProductionsitesStats,
15+ kConstructBuilding,
16+ kCheckProductionsites,
17+ kCheckShips,
18+ KMarineDecisions,
19+ kCheckMines,
20+ kWareReview,
21+ kPrintStats,
22+ kCheckMilitarysites,
23+ kCheckTrainingsites,
24+ kCountMilitaryVacant,
25+ kCheckEnemySites,
26+ kUnset
27+ };
28
29 struct CheckStepRoadAI {
30 CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
31@@ -541,4 +561,23 @@
32 }
33 };
34
35+// this represents a scheduler task
36+struct SchedulerTask {
37+ uint32_t due_time;
38+ Widelands::SchedulerTaskId id;
39+ // used to sort jobs when AI has to perform more jobs at once
40+ uint8_t priority;
41+ // used only for debug purposes
42+ std::string descr;
43+
44+ bool operator<(SchedulerTask other) const {
45+ return priority > other.priority;
46+ }
47+
48+ SchedulerTask
49+ (const uint32_t time, const Widelands::SchedulerTaskId t, const uint8_t p, const char* d):
50+ due_time(time), id(t), priority(p), descr(d){}
51+
52+};
53+
54 #endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H
55
56=== modified file 'src/ai/defaultai.cc'
57--- src/ai/defaultai.cc 2015-11-11 09:53:54 +0000
58+++ src/ai/defaultai.cc 2015-11-21 18:26:32 +0000
59@@ -86,6 +86,9 @@
60 // duration of military campaign
61 constexpr int kCampaignDuration = 15 * 60 * 1000;
62
63+// for scheduler
64+constexpr int kMaxJobs = 4;
65+
66 using namespace Widelands;
67
68 DefaultAI::AggressiveImpl DefaultAI::aggressiveImpl;
69@@ -266,136 +269,206 @@
70 // AI now thinks twice in a seccond, if the game engine allows this
71 // if too busy, the period can be many seconds.
72 next_ai_think_ = gametime + 500;
73- ScheduleTasks DueTask = ScheduleTasks::kIdle;
74- DueTask = get_oldest_task(gametime);
75- schedStat[static_cast<uint32_t>(DueTask)] += 1;
76-
77- // now AI runs a job selected above to be performed in this turn
78- // (only one but some of them needs to run check_economies() to
79- // guarantee consistency)
80- // job names are selfexplanatory
81- switch (DueTask) {
82- case ScheduleTasks::kBbuildableFieldsCheck :
83- update_all_buildable_fields(gametime);
84- taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
85- break;
86- case ScheduleTasks::kMineableFieldsCheck :
87- update_all_mineable_fields(gametime);
88- taskDue[ScheduleTasks::kMineableFieldsCheck] = gametime + kMinMFCheckInterval;
89- break;
90- case ScheduleTasks::kRoadCheck :
91- if (check_economies()) { // is a must
92- return;
93- };
94- taskDue[ScheduleTasks::kRoadCheck] = gametime + 1000;
95- // testing 5 roads
96- {
97- const int32_t roads_to_check = (roads.size() + 1 < 5) ? roads.size() + 1 : 5;
98- for (int i = 0; i < roads_to_check; i += 1) {
99- // improve_roads function will test one road and rotate roads vector
100- if (improve_roads(gametime)) {
101- // if significant change takes place do not go on
102- break;
103- };
104- }
105- }
106- break;
107- case ScheduleTasks::kUnbuildableFCheck :
108- taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
109- update_all_not_buildable_fields();
110- break;
111- case ScheduleTasks::kCheckEconomies :
112- check_economies();
113- taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
114- break;
115- case ScheduleTasks::kProductionsitesStats :
116- update_productionsite_stats(gametime);
117- break;
118- case ScheduleTasks::kConstructBuilding :
119- if (check_economies()) { // economies must be consistent
120- return;
121- }
122- if (gametime < 15000) { //more frequent on the beginning of game
123- taskDue[ScheduleTasks::kConstructBuilding] = gametime + 2000;
124- } else {
125- taskDue[ScheduleTasks::kConstructBuilding] = gametime + 6000;
126- }
127- if (construct_building(gametime)) {
128- time_of_last_construction_ = gametime;
129- }
130- break;
131- case ScheduleTasks::kCheckProductionsites :
132- if (check_economies()) { // economies must be consistent
133- return;
134- }
135- {
136- // testing 5 productionsites (if there are 5 of them)
137- int32_t ps_to_check = (productionsites.size() < 5) ? productionsites.size() : 5;
138- for (int i = 0; i < ps_to_check; i += 1) {
139- // one productionsite per one check_productionsites() call
140- if (check_productionsites(gametime)) {
141- // if significant change takes place do not go on
142- break;
143- };
144- }
145- }
146- taskDue[ScheduleTasks::kCheckProductionsites] = gametime + 15000;
147- break;
148- case ScheduleTasks::kCheckShips :
149- check_ships(gametime);
150- break;
151- case ScheduleTasks::KMarineDecisions :
152- marine_main_decisions(gametime);
153- break;
154- case ScheduleTasks::kCheckMines :
155- if (check_economies()) { // economies must be consistent
156- return;
157- }
158- taskDue[ScheduleTasks::kCheckMines] = gametime + 15000;
159- // checking 3 mines if possible
160- {
161- int32_t mines_to_check = (mines_.size() < 5) ? mines_.size() : 5;
162- for (int i = 0; i < mines_to_check; i += 1) {
163- // every run of check_mines_() checks one mine
164- if (check_mines_(gametime)) {
165- // if significant change takes place do not go on
166- break;
167- };
168- }
169- }
170- break;
171- case ScheduleTasks::kCheckMilitarysites :
172- check_militarysites(gametime);
173- break;
174- case ScheduleTasks::kCheckTrainingsites :
175- check_trainingsites(gametime);
176- break;
177- case ScheduleTasks::kCountMilitaryVacant :
178- count_military_vacant_positions();
179- taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 45 * 1000;
180- break;
181- case ScheduleTasks::kWareReview :
182- if (check_economies()) { // economies must be consistent
183- return;
184- }
185- taskDue[ScheduleTasks::kWareReview] = gametime + 15 * 60 * 1000;
186- review_wares_targets(gametime);
187- break;
188- case ScheduleTasks::kPrintStats :
189- if (check_economies()) { // economies must be consistent
190- return;
191- }
192- print_stats(gametime);
193- break;
194- case ScheduleTasks::kCheckEnemySites :
195- check_enemy_sites(gametime);
196- taskDue[ScheduleTasks::kCheckEnemySites] = gametime + 19 * 1000;
197- break;
198- case ScheduleTasks::kIdle :
199- break;
200- default:
201- ;
202+ SchedulerTaskId due_task = SchedulerTaskId::kUnset;
203+
204+ sort_task_pool();
205+
206+ const int32_t delay_time = gametime - taskPool.front().due_time;
207+
208+ // Here we decide how many jobs will be run now (none - 5)
209+ // in case no job is due now, it can be zero
210+ uint32_t jobs_to_run_count = (delay_time < 0) ? 0 : 1;
211+
212+ // Here we collect data for "too late ..." message
213+ if (delay_time > 5000) {
214+ scheduler_delay_counter_ += 1;
215+ } else {
216+ scheduler_delay_counter_ = 0;
217+ }
218+
219+ if (jobs_to_run_count == 0) {
220+ // well we have nothing to do now
221+ return;
222+ }
223+
224+ // And printing it now and resetting counter
225+ if (scheduler_delay_counter_ > 10) {
226+ log(" %d: AI: game speed too high, jobs are too late (now %2d seconds)\n",
227+ player_number(),
228+ static_cast<int32_t>(delay_time / 1000));
229+ scheduler_delay_counter_ = 0;
230+ }
231+
232+ // 400 provides that second job is run if delay time is longer then 1.6 sec
233+ if (delay_time / 400 > 1) {
234+ jobs_to_run_count = sqrt(static_cast<uint32_t>(delay_time / 500));
235+ }
236+
237+ jobs_to_run_count = (jobs_to_run_count > kMaxJobs) ? kMaxJobs : jobs_to_run_count;
238+ assert (jobs_to_run_count > 0 && jobs_to_run_count <= kMaxJobs);
239+ assert (jobs_to_run_count < taskPool.size());
240+
241+ // Pool of tasks to be executed this run. In ideal situation it will consist of one task only.
242+ std::vector<SchedulerTask> current_task_queue;
243+ // Here we push SchedulerTask members into the temporary queue, providing that a task is due now and
244+ // the limit (jobs_to_run_count) is not exceeded
245+ for (uint8_t i = 0; i < jobs_to_run_count; i += 1) {
246+ if (taskPool[i].due_time <= gametime) {
247+ current_task_queue.push_back(taskPool[i]);
248+ sort_task_pool();
249+ } else {
250+ break;
251 }
252+ }
253+
254+ assert (!current_task_queue.empty() && current_task_queue.size() <= jobs_to_run_count);
255+
256+ // Ordering temporary queue so that higher priority (lower number) is on the beginning
257+ std::sort(current_task_queue.begin(), current_task_queue.end());
258+
259+ // Performing tasks from temporary queue one by one
260+ for (uint8_t i = 0; i < current_task_queue.size(); ++i) {
261+
262+ due_task = current_task_queue[i].id;
263+
264+ sched_stat_[static_cast<uint32_t>(due_task)] += 1;
265+
266+ // Now AI runs a job selected above to be performed in this turn
267+ // (only one but some of them needs to run check_economies() to
268+ // guarantee consistency)
269+ // job names are selfexplanatory
270+ switch (due_task) {
271+ case SchedulerTaskId::kBbuildableFieldsCheck :
272+ update_all_buildable_fields(gametime);
273+ set_taskpool_task_time(gametime + kMinBFCheckInterval, SchedulerTaskId::kBbuildableFieldsCheck);
274+ break;
275+ case SchedulerTaskId::kMineableFieldsCheck :
276+ update_all_mineable_fields(gametime);
277+ set_taskpool_task_time(gametime + kMinMFCheckInterval, SchedulerTaskId::kMineableFieldsCheck);
278+ break;
279+ case SchedulerTaskId::kRoadCheck :
280+ if (check_economies()) { // is a must
281+ return;
282+ };
283+ set_taskpool_task_time(gametime + 1000, SchedulerTaskId::kRoadCheck);
284+ // testing 5 roads
285+ {
286+ const int32_t roads_to_check = (roads.size() + 1 < 5) ? roads.size() + 1 : 5;
287+ for (int j = 0; j < roads_to_check; j += 1) {
288+ // improve_roads function will test one road and rotate roads vector
289+ if (improve_roads(gametime)) {
290+ // if significant change takes place do not go on
291+ break;
292+ };
293+ }
294+ }
295+ break;
296+ case SchedulerTaskId::kUnbuildableFCheck :
297+ set_taskpool_task_time(gametime + 4000, SchedulerTaskId::kUnbuildableFCheck);
298+ update_all_not_buildable_fields();
299+ break;
300+ case SchedulerTaskId::kCheckEconomies :
301+ check_economies();
302+ set_taskpool_task_time(gametime + 8000, SchedulerTaskId::kCheckEconomies);
303+ break;
304+ case SchedulerTaskId::kProductionsitesStats :
305+ update_productionsite_stats();
306+ // Updating the stats every 10 seconds should be enough
307+ set_taskpool_task_time(gametime + 10000, SchedulerTaskId::kProductionsitesStats);
308+ break;
309+ case SchedulerTaskId::kConstructBuilding :
310+ if (check_economies()) { // economies must be consistent
311+ return;
312+ }
313+ if (gametime < 15000) { //more frequent on the beginning of game
314+ set_taskpool_task_time(gametime + 2000, SchedulerTaskId::kConstructBuilding);
315+ } else {
316+ set_taskpool_task_time(gametime + 6000, SchedulerTaskId::kConstructBuilding);
317+ }
318+ if (construct_building(gametime)) {
319+ time_of_last_construction_ = gametime;
320+ }
321+ break;
322+ case SchedulerTaskId::kCheckProductionsites :
323+ if (check_economies()) { // economies must be consistent
324+ return;
325+ }
326+ {
327+ set_taskpool_task_time(gametime + 15000, SchedulerTaskId::kCheckProductionsites);
328+ // testing 5 productionsites (if there are 5 of them)
329+ int32_t ps_to_check = (productionsites.size() < 5) ? productionsites.size() : 5;
330+ for (int j = 0; j < ps_to_check; j += 1) {
331+ // one productionsite per one check_productionsites() call
332+ if (check_productionsites(gametime)) {
333+ // if significant change takes place do not go on
334+ break;
335+ };
336+ }
337+ }
338+ break;
339+ case SchedulerTaskId::kCheckShips :
340+ set_taskpool_task_time(gametime + 3 * kShipCheckInterval, SchedulerTaskId::kCheckShips);
341+ check_ships(gametime);
342+ break;
343+ case SchedulerTaskId::KMarineDecisions :
344+ set_taskpool_task_time(gametime + kMarineDecisionInterval, SchedulerTaskId::KMarineDecisions);
345+ marine_main_decisions();
346+ break;
347+ case SchedulerTaskId::kCheckMines :
348+ if (check_economies()) { // economies must be consistent
349+ return;
350+ }
351+ set_taskpool_task_time(gametime + 15000, SchedulerTaskId::kCheckMines);
352+ // checking 3 mines if possible
353+ {
354+ int32_t mines_to_check = (mines_.size() < 5) ? mines_.size() : 5;
355+ for (int j = 0; j < mines_to_check; j += 1) {
356+ // every run of check_mines_() checks one mine
357+ if (check_mines_(gametime)) {
358+ // if significant change takes place do not go on
359+ break;
360+ };
361+ }
362+ }
363+ break;
364+ case SchedulerTaskId::kCheckMilitarysites :
365+ // just to be sure the value is reset
366+ // 4 seconds is really fine
367+ set_taskpool_task_time(gametime + 4 * 1000, SchedulerTaskId::kCheckMilitarysites);
368+ check_militarysites(gametime);
369+ break;
370+ case SchedulerTaskId::kCheckTrainingsites :
371+ set_taskpool_task_time(
372+ gametime + kTrainingSitesCheckInterval, SchedulerTaskId::kCheckTrainingsites);
373+ check_trainingsites(gametime);
374+ break;
375+ case SchedulerTaskId::kCountMilitaryVacant :
376+ count_military_vacant_positions();
377+ set_taskpool_task_time(gametime + 45 * 1000, SchedulerTaskId::kCountMilitaryVacant);
378+ break;
379+ case SchedulerTaskId::kWareReview :
380+ if (check_economies()) { // economies must be consistent
381+ return;
382+ }
383+ set_taskpool_task_time(gametime + 15 * 60 * 1000, SchedulerTaskId::kWareReview);
384+ review_wares_targets(gametime);
385+ break;
386+ case SchedulerTaskId::kPrintStats :
387+ if (check_economies()) { // economies must be consistent
388+ return;
389+ }
390+ set_taskpool_task_time(gametime + 30 * 60 * 1000, SchedulerTaskId::kPrintStats);
391+ print_stats();
392+ break;
393+ case SchedulerTaskId::kCheckEnemySites :
394+ check_enemy_sites(gametime);
395+ set_taskpool_task_time(gametime + 19 * 1000, SchedulerTaskId::kCheckEnemySites);
396+ break;
397+ default:
398+ assert(false);
399+ ;
400+ }
401+ }
402 }
403
404 /**
405@@ -407,6 +480,8 @@
406 void DefaultAI::late_initialization() {
407 player_ = game().get_player(player_number());
408 tribe_ = &player_->tribe();
409+ const uint32_t gametime = game().get_gametime();
410+
411 log("ComputerPlayer(%d): initializing (%u)\n", player_number(), type_);
412
413 wares.resize(game().tribes().nrwares());
414@@ -638,6 +713,42 @@
415 resource_necessity_water_needed_ = true;
416 }
417
418+ // Populating taskPool with all AI jobs and their starting times
419+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 0),
420+ SchedulerTaskId::kConstructBuilding, 6, "construct a building"));
421+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
422+ SchedulerTaskId::kRoadCheck, 2, "roads check"));
423+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 15 * 1000),
424+ SchedulerTaskId::kCheckProductionsites, 5, "productionsites check"));
425+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 1000),
426+ SchedulerTaskId::kProductionsitesStats, 1, "productionsites statistics"));
427+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 1000),
428+ SchedulerTaskId::kCheckMines, 5, "check mines"));
429+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 0 * 1000),
430+ SchedulerTaskId::kCheckMilitarysites, 5, "check militarysites"));
431+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 1000),
432+ SchedulerTaskId::kCheckShips, 5, "check ships"));
433+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
434+ SchedulerTaskId::kCheckEconomies, 1, "check economies"));
435+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 1000),
436+ SchedulerTaskId::KMarineDecisions, 5, "marine decisions"));
437+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 2 * 60 * 1000),
438+ SchedulerTaskId::kCheckTrainingsites, 5, "check training sites"));
439+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
440+ SchedulerTaskId::kBbuildableFieldsCheck, 2, "check buildable fields"));
441+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
442+ SchedulerTaskId::kMineableFieldsCheck, 2, "check mineable fields"));
443+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
444+ SchedulerTaskId::kUnbuildableFCheck, 1, "check unbuildable fields"));
445+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 15 * 60 * 1000),
446+ SchedulerTaskId::kWareReview, 9, "wares review"));
447+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 60 * 1000),
448+ SchedulerTaskId::kPrintStats, 9, "print statistics"));
449+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 60 * 1000),
450+ SchedulerTaskId::kCountMilitaryVacant, 2, "count military vacant"));
451+ taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
452+ SchedulerTaskId::kCheckEnemySites, 6, "check enemy sites"));
453+
454 Map& map = game().map();
455
456 // here we generate list of all ports and their vicinity from entire map
457@@ -719,24 +830,6 @@
458 }
459 }
460
461- taskDue[ScheduleTasks::kConstructBuilding] = 0;
462- taskDue[ScheduleTasks::kRoadCheck] = 1000;
463- taskDue[ScheduleTasks::kCheckProductionsites] = 15 * 1000;
464- taskDue[ScheduleTasks::kProductionsitesStats] = 30000;
465- taskDue[ScheduleTasks::kCheckMines] = 30 * 1000;
466- taskDue[ScheduleTasks::kCheckMilitarysites] = 0;
467- taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
468- taskDue[ScheduleTasks::kCheckEconomies] = 1000;
469- taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
470- taskDue[ScheduleTasks::kCheckTrainingsites] = 2 * 60 * 1000;
471- taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
472- taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
473- taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
474- taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000;
475- taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
476- taskDue[ScheduleTasks::kCountMilitaryVacant] = 1 * 60 * 1000;
477- taskDue[ScheduleTasks::kCheckEnemySites] = 10 * 60 * 1000;
478-
479 // Here the AI persistent data either exists - then they are read
480 // or does not exist, then they are created and saved
481 int16_t persistent_data_exists_ = 0;
482@@ -1265,9 +1358,7 @@
483 }
484
485 /// Updates the production and MINE sites statistics needed for construction decision.
486-void DefaultAI::update_productionsite_stats(uint32_t const gametime) {
487- // Updating the stats every 10 seconds should be enough
488- taskDue[ScheduleTasks::kProductionsitesStats] = gametime + 10000;
489+void DefaultAI::update_productionsite_stats() {
490 uint16_t fishers_count = 0; // used for atlanteans only
491
492 // Reset statistics for all buildings
493@@ -3339,18 +3430,13 @@
494 // and makes two decisions:
495 // - build a ship
496 // - start preparation for expedition
497-bool DefaultAI::marine_main_decisions(uint32_t const gametime) {
498- if (gametime < taskDue[ScheduleTasks::KMarineDecisions]) {
499- return false;
500- }
501+bool DefaultAI::marine_main_decisions() {
502
503 if (!seafaring_economy) {
504- taskDue[ScheduleTasks::KMarineDecisions] = std::numeric_limits<uint32_t>::max();
505+ set_taskpool_task_time(std::numeric_limits<uint32_t>::max(), SchedulerTaskId::KMarineDecisions);
506 return false;
507 }
508
509- taskDue[ScheduleTasks::KMarineDecisions] = gametime + kMarineDecisionInterval;
510-
511 // getting some base statistics
512 player_ = game().get_player(player_number());
513 uint16_t ports_count = 0;
514@@ -3449,12 +3535,9 @@
515
516 // This identifies ships that are waiting for command
517 bool DefaultAI::check_ships(uint32_t const gametime) {
518- if (gametime < taskDue[ScheduleTasks::kCheckShips]) {
519- return false;
520- }
521
522 if (!seafaring_economy) {
523- taskDue[ScheduleTasks::kCheckShips] = std::numeric_limits<int32_t>::max();
524+ set_taskpool_task_time(std::numeric_limits<int32_t>::max(), SchedulerTaskId::kCheckShips);
525 return false;
526 }
527
528@@ -3518,11 +3601,8 @@
529 }
530
531 if (action_taken) {
532- taskDue[ScheduleTasks::kCheckShips] = gametime + kShipCheckInterval;
533- } else {
534- taskDue[ScheduleTasks::kCheckShips] = gametime + 3 * kShipCheckInterval;
535+ set_taskpool_task_time(gametime + kShipCheckInterval, SchedulerTaskId::kCheckShips);
536 }
537-
538 return true;
539 }
540
541@@ -4020,13 +4100,12 @@
542 // this function only check with trainingsites
543 // manipulates input queues and soldier capacity
544 bool DefaultAI::check_trainingsites(uint32_t gametime) {
545- if (taskDue[ScheduleTasks::kCheckTrainingsites] > gametime) {
546+ if (get_taskpool_task_time(SchedulerTaskId::kCheckTrainingsites) > gametime) {
547 return false;
548 }
549- if (!trainingsites.empty()) {
550- taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval;
551- } else {
552- taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 2 * kTrainingSitesCheckInterval;
553+ if (trainingsites.empty()) {
554+ set_taskpool_task_time(
555+ gametime + 2 * kTrainingSitesCheckInterval, SchedulerTaskId::kCheckTrainingsites);
556 return false;
557 }
558
559@@ -4139,12 +4218,6 @@
560 * \returns true if something was changed
561 */
562 bool DefaultAI::check_militarysites(uint32_t gametime) {
563- if (taskDue[ScheduleTasks::kCheckMilitarysites] > gametime) {
564- return false;
565- }
566-
567- // just to be sure the value is reset
568- taskDue[ScheduleTasks::kCheckMilitarysites] = gametime + 4 * 1000; // 4 seconds is really fine
569
570 // Only useable, if defaultAI owns at least one militarysite
571 if (militarysites.empty()) {
572@@ -4254,7 +4327,6 @@
573 // reorder:;
574 militarysites.push_back(militarysites.front());
575 militarysites.pop_front();
576- taskDue[ScheduleTasks::kCheckMilitarysites] = gametime + 5 * 1000; // 10 seconds is really fine
577 return changed;
578 }
579
580@@ -4728,8 +4800,8 @@
581 }
582 }
583
584- // Let defaultAI try to directly connect the constructionsite
585- taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime();
586+ set_taskpool_task_time(game().get_gametime(), SchedulerTaskId::kRoadCheck);
587+
588 } else {
589 ++bo.cnt_built_;
590
591@@ -5430,33 +5502,39 @@
592 }
593 }
594
595-// run over dueTasks map and returns task with lower duetime
596-DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) {
597-
598- uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
599- ScheduleTasks DueTask = ScheduleTasks::kIdle; // default
600- taskDue[ScheduleTasks::kIdle] = gametime;
601-
602- for (std::pair<ScheduleTasks, uint32_t> task : taskDue) {
603- if (task.second < oldestTaskTime) {
604- oldestTaskTime = task.second;
605- DueTask = task.first;
606- }
607- }
608- if ((gametime - oldestTaskTime) > 5000) {
609- scheduler_delay_counter_ += 1;
610- } else {
611- scheduler_delay_counter_ = 0;
612- }
613-
614- if (scheduler_delay_counter_ > 10) {
615- log(" %d: AI: game speed too high, jobs are too late (now %2d seconds)\n",
616- player_number(),
617- static_cast<int32_t>((gametime - oldestTaskTime) / 1000));
618- scheduler_delay_counter_ = 0;
619- }
620-
621- return DueTask;
622+// Sets due_time based on job ID
623+void DefaultAI::set_taskpool_task_time(const uint32_t gametime, const Widelands::SchedulerTaskId task) {
624+
625+ for (auto& item : taskPool) {
626+ if (item.id == task) {
627+ item.due_time = gametime;
628+ return;
629+ }
630+ }
631+
632+ assert(false);
633+}
634+
635+// Retrieves due time of the task based on its ID
636+uint32_t DefaultAI::get_taskpool_task_time(const Widelands::SchedulerTaskId task) {
637+ for (const auto& item : taskPool) {
638+ if (item.id == task) {
639+ return item.due_time;
640+ }
641+ }
642+
643+ assert (false);
644+}
645+
646+// This performs one "iteration" of sorting based on due_time
647+// We by design do not need full sorting...
648+void DefaultAI::sort_task_pool() {
649+ assert(!taskPool.empty());
650+ for (int8_t i = taskPool.size() - 1; i > 0; --i) {
651+ if (taskPool[i - 1].due_time > taskPool[i].due_time) {
652+ std::iter_swap(taskPool.begin() + i - 1, taskPool.begin() + i);
653+ }
654+ }
655 }
656
657 // following two functions count mines of the same type (same output,
658@@ -5468,6 +5546,7 @@
659 }
660 return count;
661 }
662+
663 uint32_t DefaultAI::mines_built() const{
664 uint32_t count = 0;
665 for (const std::pair<const int, MineTypesObserver> m : mines_per_type) {
666@@ -5499,13 +5578,13 @@
667 // and needs to know what resourcess are missing for which player and so on.
668 // By default it is off (see kPrintStats)
669 // TODO(tiborb ?): - it would be nice to have this activated by a command line switch
670-void DefaultAI::print_stats(uint32_t const gametime) {
671+void DefaultAI::print_stats() {
672
673 if (!kPrintStats) {
674- taskDue[ScheduleTasks::kPrintStats] = std::numeric_limits<int32_t>::max();
675+ set_taskpool_task_time(std::numeric_limits<int32_t>::max(), SchedulerTaskId::kPrintStats);
676 return;
677 }
678- taskDue[ScheduleTasks::kPrintStats] = gametime + 30 * 60 * 1000;
679+
680
681 PlayerNumber const pn = player_number();
682
683
684=== modified file 'src/ai/defaultai.h'
685--- src/ai/defaultai.h 2015-11-11 09:52:55 +0000
686+++ src/ai/defaultai.h 2015-11-21 18:26:32 +0000
687@@ -85,26 +85,7 @@
688 enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kAllowRangers};
689 enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
690 enum class PerfEvaluation : uint8_t {kForConstruction, kForDismantle};
691- enum class ScheduleTasks : uint8_t {
692- kBbuildableFieldsCheck,
693- kMineableFieldsCheck,
694- kRoadCheck,
695- kUnbuildableFCheck,
696- kCheckEconomies,
697- kProductionsitesStats,
698- kConstructBuilding,
699- kCheckProductionsites,
700- kCheckShips,
701- KMarineDecisions,
702- kCheckMines,
703- kWareReview,
704- kPrintStats,
705- kIdle,
706- kCheckMilitarysites,
707- kCheckTrainingsites,
708- kCountMilitaryVacant,
709- kCheckEnemySites
710- };
711+
712 enum class Tribes : uint8_t {
713 kNone,
714 kBarbarians,
715@@ -161,7 +142,7 @@
716 void update_buildable_field(BuildableField&, uint16_t = 6, bool = false);
717 void update_mineable_field(MineableField&);
718
719- void update_productionsite_stats(uint32_t);
720+ void update_productionsite_stats();
721
722 // for productionsites
723 Widelands::BuildingNecessity check_building_necessity
724@@ -170,7 +151,10 @@
725 Widelands::BuildingNecessity check_building_necessity
726 (uint8_t, uint32_t);
727
728- ScheduleTasks get_oldest_task(uint32_t);
729+ void sort_task_pool();
730+ void sort_by_priority();
731+ void set_taskpool_task_time(uint32_t, Widelands::SchedulerTaskId);
732+ uint32_t get_taskpool_task_time(Widelands::SchedulerTaskId);
733
734 bool construct_building(uint32_t);
735
736@@ -201,10 +185,10 @@
737 bool check_trainingsites(uint32_t);
738 bool check_mines_(uint32_t);
739 bool check_militarysites(uint32_t);
740- bool marine_main_decisions(uint32_t);
741+ bool marine_main_decisions();
742 bool check_ships(uint32_t);
743 bool check_enemy_sites(uint32_t);
744- void print_stats(uint32_t);
745+ void print_stats();
746 // return single number of strength of vector of soldiers
747 int32_t calculate_strength(const std::vector<Widelands::Soldier*>);
748 uint32_t get_stocklevel_by_hint(size_t);
749@@ -256,7 +240,7 @@
750 uint8_t type_;
751
752 // collect statistics on how many times which job was run
753- uint32_t schedStat[20] = {0};
754+ uint32_t sched_stat_[20] = {0};
755
756 Widelands::Player* player_;
757 Widelands::TribeDescr const* tribe_;
758@@ -291,7 +275,9 @@
759 std::list<WarehouseSiteObserver> warehousesites;
760 std::list<TrainingSiteObserver> trainingsites;
761 std::list<ShipObserver> allships;
762- std::map<ScheduleTasks, uint32_t> taskDue;
763+ // This is a vector that is filled up on initiatlization
764+ // and no items are added/removed afterwards
765+ std::vector<SchedulerTask> taskPool;
766 std::map<uint32_t, EnemySiteObserver> enemy_sites;
767 // it will map mined material to observer
768 std::map<int32_t, MineTypesObserver> mines_per_type;

Subscribers

People subscribed via source and target branches

to status/vote changes: