Merge lp:~widelands-dev/widelands/ai_sched_speedup into lp:widelands
- ai_sched_speedup
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email:
|
Commit message
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
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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
TiborB (tiborb95) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
GunChleoc (gunchleoc) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
TiborB (tiborb95) wrote : | # |
I believe all your comments were incorporated...
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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; |
oh, I found some mathematical issues here, I will push new version tonight