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