Merge lp:~widelands-dev/widelands/ai_flag_warehouse_distance into lp:widelands
- ai_flag_warehouse_distance
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 9172 |
Proposed branch: | lp:~widelands-dev/widelands/ai_flag_warehouse_distance |
Merge into: | lp:widelands |
Diff against target: |
1700 lines (+982/-511) 8 files modified
src/ai/CMakeLists.txt (+1/-0) src/ai/ai_help_structs.cc (+189/-119) src/ai/ai_help_structs.h (+106/-47) src/ai/defaultai.cc (+422/-344) src/ai/defaultai.h (+6/-1) src/ai/test/CMakeLists.txt (+9/-0) src/ai/test/ai_test_main.cc (+21/-0) src/ai/test/test_ai.cc (+228/-0) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/ai_flag_warehouse_distance |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
hessenfarmer | Approve | ||
Review via email: mp+368544@code.launchpad.net |
Commit message
Not ready for merge
Description of the change
This is just to trigger the build process
TiborB (tiborb95) wrote : | # |
hessenfarmer (stephan-lutz) wrote : | # |
The appveyor builds failed you need to merge trunk first.
TiborB (tiborb95) wrote : | # |
currently there is no new revision in trunk, so nothing to merge for now..
hessenfarmer (stephan-lutz) wrote : | # |
Sorry overlooked you already did the merge. Seems as if Bunnybot is down again.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 5169. State: failed. Details: https:/
Appveyor build 4951. State: failed. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 5174. State: failed. Details: https:/
Appveyor build 4952. State: success. Details: https:/
hessenfarmer (stephan-lutz) wrote : | # |
hi, I finally found some time to test this and I believe it to be an improvement over the previous code. So I would like to see it in the game.
GunChleoc (gunchleoc) wrote : | # |
I have done a preliminary code review - everything just small nits. Can you clean up the NOCOMs too and set a commit message?
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 5261. State: failed. Details: https:/
Appveyor build 5038. State: failed. Details: https:/
TiborB (tiborb95) wrote : | # |
I forgot some comments, so another try tonight...
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 5278. State: errored. Details: https:/
Appveyor build 5053. State: success. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 5280. State: failed. Details: https:/
Appveyor build 5055. State: success. Details: https:/
TiborB (tiborb95) wrote : | # |
Failed only because of some network issues
hessenfarmer (stephan-lutz) wrote : | # |
Technically I already approved this but I believe a C++ experet should do the final Code review. GunChleoc would be probably best to do this as she did the first round
TiborB (tiborb95) wrote : | # |
OK, let wait then...
GunChleoc (gunchleoc) wrote : | # |
Code review done. Mostly small tweaks to names and some efficiency stuff, and 1 real question.
TiborB (tiborb95) wrote : | # |
See my answer.
TiborB (tiborb95) wrote : | # |
Comments implemented except one 'const', where compiler complained, probably because one of arguments was a pointer
GunChleoc (gunchleoc) wrote : | # |
Looking good, let's have it :)
@bunnybot merge
bunnybot (widelandsofficial) wrote : | # |
Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.
Travis build 5295. State: failed. Details: https:/
hessenfarmer (stephan-lutz) wrote : | # |
transient failure on travis
@bunnybot merge force
Preview Diff
1 | === modified file 'src/ai/CMakeLists.txt' | |||
2 | --- src/ai/CMakeLists.txt 2017-11-22 19:00:09 +0000 | |||
3 | +++ src/ai/CMakeLists.txt 2019-07-31 19:53:54 +0000 | |||
4 | @@ -24,3 +24,4 @@ | |||
5 | 24 | logic_map_objects | 24 | logic_map_objects |
6 | 25 | scripting_lua_table | 25 | scripting_lua_table |
7 | 26 | ) | 26 | ) |
8 | 27 | add_subdirectory(test) | ||
9 | 27 | 28 | ||
10 | === modified file 'src/ai/ai_help_structs.cc' | |||
11 | --- src/ai/ai_help_structs.cc 2019-04-09 16:43:49 +0000 | |||
12 | +++ src/ai/ai_help_structs.cc 2019-07-31 19:53:54 +0000 | |||
13 | @@ -19,6 +19,8 @@ | |||
14 | 19 | 19 | ||
15 | 20 | #include "ai/ai_help_structs.h" | 20 | #include "ai/ai_help_structs.h" |
16 | 21 | 21 | ||
17 | 22 | #include <algorithm> | ||
18 | 23 | |||
19 | 22 | #include "base/macros.h" | 24 | #include "base/macros.h" |
20 | 23 | #include "base/time_string.h" | 25 | #include "base/time_string.h" |
21 | 24 | #include "logic/ai_dna_handler.h" | 26 | #include "logic/ai_dna_handler.h" |
22 | @@ -27,10 +29,6 @@ | |||
23 | 27 | 29 | ||
24 | 28 | namespace Widelands { | 30 | namespace Widelands { |
25 | 29 | 31 | ||
26 | 30 | // couple of constants for calculation of road interconnections | ||
27 | 31 | constexpr int kRoadPossiblyBuildable = 200; | ||
28 | 32 | constexpr int kConnectedByRoads = 400; | ||
29 | 33 | constexpr int kNotConnectedByRoads = 600; | ||
30 | 34 | constexpr int kNoAiTrainingMutation = 200; | 32 | constexpr int kNoAiTrainingMutation = 200; |
31 | 35 | constexpr int kUpperDefaultMutationLimit = 150; | 33 | constexpr int kUpperDefaultMutationLimit = 150; |
32 | 36 | constexpr int kLowerDefaultMutationLimit = 75; | 34 | constexpr int kLowerDefaultMutationLimit = 75; |
33 | @@ -307,7 +305,7 @@ | |||
34 | 307 | // count of rocks can only decrease, so amount of rocks | 305 | // count of rocks can only decrease, so amount of rocks |
35 | 308 | // is recalculated only when previous count is positive | 306 | // is recalculated only when previous count is positive |
36 | 309 | // count of water fields are stable, so if the current count is | 307 | // count of water fields are stable, so if the current count is |
38 | 310 | // non-negative, water is not recaldulated | 308 | // non-negative, water is not recalculated |
39 | 311 | rocks_nearby(1), | 309 | rocks_nearby(1), |
40 | 312 | water_nearby(-1), | 310 | water_nearby(-1), |
41 | 313 | open_water_nearby(-1), | 311 | open_water_nearby(-1), |
42 | @@ -974,120 +972,6 @@ | |||
43 | 974 | return (blocked_fields_.count(coords.hash()) != 0); | 972 | return (blocked_fields_.count(coords.hash()) != 0); |
44 | 975 | } | 973 | } |
45 | 976 | 974 | ||
46 | 977 | // As a policy, we just set some default value, that will be updated later on | ||
47 | 978 | FlagsForRoads::Candidate::Candidate(uint32_t coords, int32_t distance, bool different_economy) | ||
48 | 979 | : coords_hash(coords), air_distance(distance) { | ||
49 | 980 | new_road_possible = false; | ||
50 | 981 | // Just custom values | ||
51 | 982 | new_road_length = kRoadPossiblyBuildable; | ||
52 | 983 | current_road_length = (different_economy) ? kNotConnectedByRoads : kConnectedByRoads; | ||
53 | 984 | } | ||
54 | 985 | |||
55 | 986 | // Used when sorting cadidate flags from best one | ||
56 | 987 | bool FlagsForRoads::Candidate::operator<(const Candidate& other) const { | ||
57 | 988 | const int32_t other_rs = other.reduction_score(); | ||
58 | 989 | const int32_t this_rs = reduction_score(); | ||
59 | 990 | return std::tie(other.new_road_possible, other_rs) < std::tie(new_road_possible, this_rs); | ||
60 | 991 | } | ||
61 | 992 | |||
62 | 993 | bool FlagsForRoads::Candidate::operator==(const Candidate& other) const { | ||
63 | 994 | return coords_hash == other.coords_hash; | ||
64 | 995 | } | ||
65 | 996 | |||
66 | 997 | int32_t FlagsForRoads::Candidate::reduction_score() const { | ||
67 | 998 | return current_road_length - new_road_length - (new_road_length - air_distance) / 3; | ||
68 | 999 | } | ||
69 | 1000 | |||
70 | 1001 | void FlagsForRoads::print() { // this is for debugging and development purposes | ||
71 | 1002 | for (auto& candidate_flag : flags_queue) { | ||
72 | 1003 | log(" %starget: %3dx%3d, saving: %5d (%3d), air distance: %3d, new road: %6d, score: %5d " | ||
73 | 1004 | "%s\n", | ||
74 | 1005 | (candidate_flag.reduction_score() >= min_reduction && candidate_flag.new_road_possible) ? | ||
75 | 1006 | "+" : | ||
76 | 1007 | " ", | ||
77 | 1008 | Coords::unhash(candidate_flag.coords_hash).x, | ||
78 | 1009 | Coords::unhash(candidate_flag.coords_hash).y, | ||
79 | 1010 | candidate_flag.current_road_length - candidate_flag.new_road_length, min_reduction, | ||
80 | 1011 | candidate_flag.air_distance, candidate_flag.new_road_length, | ||
81 | 1012 | candidate_flag.reduction_score(), | ||
82 | 1013 | (candidate_flag.new_road_possible) ? ", new road possible" : " "); | ||
83 | 1014 | } | ||
84 | 1015 | } | ||
85 | 1016 | |||
86 | 1017 | // Queue is ordered but some target flags are only estimations so we take such a candidate_flag | ||
87 | 1018 | // first | ||
88 | 1019 | bool FlagsForRoads::get_best_uncalculated(uint32_t* winner) { | ||
89 | 1020 | for (auto& candidate_flag : flags_queue) { | ||
90 | 1021 | if (!candidate_flag.new_road_possible) { | ||
91 | 1022 | *winner = candidate_flag.coords_hash; | ||
92 | 1023 | return true; | ||
93 | 1024 | } | ||
94 | 1025 | } | ||
95 | 1026 | return false; | ||
96 | 1027 | } | ||
97 | 1028 | |||
98 | 1029 | // Road from starting flag to this flag can be built | ||
99 | 1030 | void FlagsForRoads::road_possible(Widelands::Coords coords, const uint32_t new_road) { | ||
100 | 1031 | for (auto& candidate_flag : flags_queue) { | ||
101 | 1032 | if (candidate_flag.coords_hash == coords.hash()) { | ||
102 | 1033 | candidate_flag.new_road_length = new_road; | ||
103 | 1034 | candidate_flag.new_road_possible = true; | ||
104 | 1035 | candidate_flag.reduction_score(); | ||
105 | 1036 | return; | ||
106 | 1037 | } | ||
107 | 1038 | } | ||
108 | 1039 | NEVER_HERE(); | ||
109 | 1040 | } | ||
110 | 1041 | |||
111 | 1042 | // find_reachable_fields returns duplicates so we deal with them | ||
112 | 1043 | bool FlagsForRoads::has_candidate(const uint32_t hash) { | ||
113 | 1044 | for (auto& candidate_flag : flags_queue) { | ||
114 | 1045 | if (candidate_flag.coords_hash == hash) { | ||
115 | 1046 | return true; | ||
116 | 1047 | } | ||
117 | 1048 | } | ||
118 | 1049 | return false; | ||
119 | 1050 | } | ||
120 | 1051 | |||
121 | 1052 | // Updating walking distance into flags_queue | ||
122 | 1053 | void FlagsForRoads::set_cur_road_distance(Widelands::Coords coords, int32_t cur_distance) { | ||
123 | 1054 | for (auto& candidate_flag : flags_queue) { | ||
124 | 1055 | if (candidate_flag.coords_hash == coords.hash()) { | ||
125 | 1056 | candidate_flag.current_road_length = cur_distance; | ||
126 | 1057 | candidate_flag.reduction_score(); | ||
127 | 1058 | return; | ||
128 | 1059 | } | ||
129 | 1060 | } | ||
130 | 1061 | } | ||
131 | 1062 | |||
132 | 1063 | // Returns mostly best candidate, as a result of sorting | ||
133 | 1064 | bool FlagsForRoads::get_winner(uint32_t* winner_hash) { | ||
134 | 1065 | // If AI can ask for 2nd position, but there is only one viable candidate | ||
135 | 1066 | // we return the first one of course | ||
136 | 1067 | bool has_winner = false; | ||
137 | 1068 | for (auto candidate_flag : flags_queue) { | ||
138 | 1069 | if (candidate_flag.reduction_score() < min_reduction || !candidate_flag.new_road_possible || | ||
139 | 1070 | candidate_flag.new_road_length * 2 > candidate_flag.current_road_length) { | ||
140 | 1071 | continue; | ||
141 | 1072 | } | ||
142 | 1073 | assert(candidate_flag.air_distance > 0); | ||
143 | 1074 | assert(candidate_flag.reduction_score() >= min_reduction); | ||
144 | 1075 | assert(candidate_flag.new_road_possible); | ||
145 | 1076 | *winner_hash = candidate_flag.coords_hash; | ||
146 | 1077 | has_winner = true; | ||
147 | 1078 | |||
148 | 1079 | if (std::rand() % 4 > 0) { | ||
149 | 1080 | // with probability of 3/4 we accept this flag | ||
150 | 1081 | return true; | ||
151 | 1082 | } | ||
152 | 1083 | } | ||
153 | 1084 | |||
154 | 1085 | if (has_winner) { | ||
155 | 1086 | return true; | ||
156 | 1087 | } | ||
157 | 1088 | return false; | ||
158 | 1089 | } | ||
159 | 1090 | |||
160 | 1091 | PlayersStrengths::PlayersStrengths() : update_time(0) { | 975 | PlayersStrengths::PlayersStrengths() : update_time(0) { |
161 | 1092 | } | 976 | } |
162 | 1093 | 977 | ||
163 | @@ -1417,4 +1301,190 @@ | |||
164 | 1417 | return update_time; | 1301 | return update_time; |
165 | 1418 | } | 1302 | } |
166 | 1419 | 1303 | ||
167 | 1304 | FlagWarehouseDistances::FlagInfo::FlagInfo(const uint32_t gametime, | ||
168 | 1305 | const uint16_t dist, | ||
169 | 1306 | const uint32_t wh) { | ||
170 | 1307 | expiry_time = gametime + kFlagDistanceExpirationPeriod; | ||
171 | 1308 | soft_expiry_time = gametime + kFlagDistanceExpirationPeriod / 2; | ||
172 | 1309 | distance = dist; | ||
173 | 1310 | nearest_warehouse = wh; | ||
174 | 1311 | new_road_prohibited_till = 0; | ||
175 | 1312 | } | ||
176 | 1313 | FlagWarehouseDistances::FlagInfo::FlagInfo() { | ||
177 | 1314 | expiry_time = 0; | ||
178 | 1315 | distance = 1000; | ||
179 | 1316 | new_road_prohibited_till = 0; | ||
180 | 1317 | } | ||
181 | 1318 | |||
182 | 1319 | // We are updating the distance info, but not all the time. | ||
183 | 1320 | // Always if after soft expiration period, but | ||
184 | 1321 | // if below expiration period only when the new value is lower than current one | ||
185 | 1322 | // In both cases new expiration times are calculated | ||
186 | 1323 | bool FlagWarehouseDistances::FlagInfo::update(const uint32_t gametime, | ||
187 | 1324 | const uint16_t new_distance, | ||
188 | 1325 | const uint32_t nearest_wh) { | ||
189 | 1326 | const uint32_t new_expiry_time = gametime + kFlagDistanceExpirationPeriod; | ||
190 | 1327 | |||
191 | 1328 | if (gametime > soft_expiry_time) { | ||
192 | 1329 | distance = new_distance; | ||
193 | 1330 | expiry_time = new_expiry_time; | ||
194 | 1331 | soft_expiry_time = gametime + kFlagDistanceExpirationPeriod / 2; | ||
195 | 1332 | nearest_warehouse = nearest_wh; | ||
196 | 1333 | return true; | ||
197 | 1334 | } else if (new_distance < distance || (new_distance == distance && | ||
198 | 1335 | expiry_time < new_expiry_time)) { | ||
199 | 1336 | distance = new_distance; | ||
200 | 1337 | expiry_time = new_expiry_time; | ||
201 | 1338 | nearest_warehouse = nearest_wh; | ||
202 | 1339 | return true; | ||
203 | 1340 | } | ||
204 | 1341 | return false; | ||
205 | 1342 | } | ||
206 | 1343 | |||
207 | 1344 | uint16_t FlagWarehouseDistances::FlagInfo::get(const uint32_t gametime, uint32_t* nw) const { | ||
208 | 1345 | *nw = nearest_warehouse; | ||
209 | 1346 | if (gametime <= expiry_time) { | ||
210 | 1347 | return distance; | ||
211 | 1348 | } | ||
212 | 1349 | return kFarButReachable; | ||
213 | 1350 | } | ||
214 | 1351 | |||
215 | 1352 | void FlagWarehouseDistances::FlagInfo::set_road_built(const uint32_t gametime) { | ||
216 | 1353 | // Prohibiting for next 60 seconds | ||
217 | 1354 | new_road_prohibited_till = gametime + 60 * 1000; | ||
218 | 1355 | } | ||
219 | 1356 | |||
220 | 1357 | bool FlagWarehouseDistances::FlagInfo::is_road_prohibited(const uint32_t gametime) const { | ||
221 | 1358 | return new_road_prohibited_till > gametime; | ||
222 | 1359 | } | ||
223 | 1360 | |||
224 | 1361 | bool FlagWarehouseDistances::set_distance(const uint32_t flag_coords, | ||
225 | 1362 | const uint16_t distance, | ||
226 | 1363 | uint32_t const gametime, | ||
227 | 1364 | uint32_t const nearest_warehouse) { | ||
228 | 1365 | if (flags_map.count(flag_coords) == 0) { | ||
229 | 1366 | flags_map[flag_coords] = | ||
230 | 1367 | FlagWarehouseDistances::FlagInfo(gametime, distance, nearest_warehouse); | ||
231 | 1368 | return true; | ||
232 | 1369 | } | ||
233 | 1370 | return flags_map[flag_coords].update(gametime, distance, nearest_warehouse); | ||
234 | 1371 | } | ||
235 | 1372 | |||
236 | 1373 | uint16_t FlagWarehouseDistances::count() const { | ||
237 | 1374 | return flags_map.size(); | ||
238 | 1375 | } | ||
239 | 1376 | |||
240 | 1377 | int16_t | ||
241 | 1378 | FlagWarehouseDistances::get_distance(const uint32_t flag_coords, uint32_t gametime, uint32_t* nw) { | ||
242 | 1379 | if (flags_map.count(flag_coords) == 0) { | ||
243 | 1380 | *nw = 0; | ||
244 | 1381 | return kFarButReachable; // this is to discourage to build second road from brand new flag... | ||
245 | 1382 | } else { | ||
246 | 1383 | return flags_map[flag_coords].get(gametime, nw); | ||
247 | 1384 | } | ||
248 | 1385 | } | ||
249 | 1386 | |||
250 | 1387 | void FlagWarehouseDistances::set_road_built(const uint32_t coords_hash, const uint32_t gametime) { | ||
251 | 1388 | if (flags_map.count(coords_hash) == 1) { | ||
252 | 1389 | flags_map[coords_hash].set_road_built(gametime); | ||
253 | 1390 | } | ||
254 | 1391 | } | ||
255 | 1392 | |||
256 | 1393 | bool FlagWarehouseDistances::is_road_prohibited(const uint32_t coords_hash, | ||
257 | 1394 | const uint32_t gametime) { | ||
258 | 1395 | if (flags_map.count(coords_hash) == 1) { | ||
259 | 1396 | return flags_map[coords_hash].is_road_prohibited(gametime); | ||
260 | 1397 | } | ||
261 | 1398 | return false; | ||
262 | 1399 | } | ||
263 | 1400 | |||
264 | 1401 | bool FlagWarehouseDistances::remove_old_flag(uint32_t gametime) { | ||
265 | 1402 | for (std::map<uint32_t, FlagWarehouseDistances::FlagInfo>::iterator it = flags_map.begin(); | ||
266 | 1403 | it != flags_map.end(); it++) { | ||
267 | 1404 | if (it->second.expiry_time + kOldFlagRemoveTime < gametime) { | ||
268 | 1405 | it = flags_map.erase(it); | ||
269 | 1406 | return true; | ||
270 | 1407 | } | ||
271 | 1408 | } | ||
272 | 1409 | return false; | ||
273 | 1410 | } | ||
274 | 1411 | |||
275 | 1412 | // Returns pointer to winning road, provided that the treshold is exceeded | ||
276 | 1413 | FlagCandidates::Candidate* FlagCandidates::get_winner(const int16_t threshold) { | ||
277 | 1414 | if (flags_.empty()) { | ||
278 | 1415 | return nullptr; | ||
279 | 1416 | } | ||
280 | 1417 | sort(); | ||
281 | 1418 | if (flags_[0].score() < threshold) { | ||
282 | 1419 | return nullptr; | ||
283 | 1420 | } | ||
284 | 1421 | if (!flags_[0].is_buildable()) { | ||
285 | 1422 | return nullptr; | ||
286 | 1423 | } | ||
287 | 1424 | return &flags_[0]; | ||
288 | 1425 | } | ||
289 | 1426 | |||
290 | 1427 | FlagCandidates::Candidate::Candidate( | ||
291 | 1428 | const uint32_t c_hash, bool different_eco, uint16_t start_f_dist, uint16_t act_dist_to_wh, uint16_t air_dst) { | ||
292 | 1429 | coords_hash = c_hash; | ||
293 | 1430 | different_economy = different_eco; | ||
294 | 1431 | start_flag_dist_to_wh = start_f_dist; | ||
295 | 1432 | flag_to_flag_road_distance = 0; | ||
296 | 1433 | possible_road_distance = kFarButReachable; | ||
297 | 1434 | cand_flag_distance_to_wh = act_dist_to_wh; | ||
298 | 1435 | air_distance = air_dst; | ||
299 | 1436 | } | ||
300 | 1437 | |||
301 | 1438 | int16_t FlagCandidates::Candidate::score() const { | ||
302 | 1439 | return different_economy * 2000 + (start_flag_dist_to_wh - cand_flag_distance_to_wh) + | ||
303 | 1440 | (flag_to_flag_road_distance - 2 * possible_road_distance); | ||
304 | 1441 | } | ||
305 | 1442 | |||
306 | 1443 | bool FlagCandidates::set_road_possible(const uint32_t coords_hash, const uint16_t steps) { | ||
307 | 1444 | for (auto& item : flags_) { | ||
308 | 1445 | if (item.coords_hash == coords_hash) { | ||
309 | 1446 | item.possible_road_distance = steps; | ||
310 | 1447 | return true; | ||
311 | 1448 | } | ||
312 | 1449 | } | ||
313 | 1450 | return false; | ||
314 | 1451 | } | ||
315 | 1452 | |||
316 | 1453 | bool FlagCandidates::set_cur_road_distance(const uint32_t coords, uint16_t dist) { | ||
317 | 1454 | for (auto& item : flags_) { | ||
318 | 1455 | if (item.coords_hash == coords) { | ||
319 | 1456 | item.flag_to_flag_road_distance = dist; | ||
320 | 1457 | return true; | ||
321 | 1458 | } | ||
322 | 1459 | } | ||
323 | 1460 | return false; | ||
324 | 1461 | } | ||
325 | 1462 | void FlagCandidates::sort() { | ||
326 | 1463 | std::sort(flags_.begin(), flags_.end()); | ||
327 | 1464 | } | ||
328 | 1465 | |||
329 | 1466 | void FlagCandidates::sort_by_air_distance() { | ||
330 | 1467 | std::sort(flags_.begin(), flags_.end(), | ||
331 | 1468 | [](const FlagCandidates::Candidate& lf, const FlagCandidates::Candidate& rf) { | ||
332 | 1469 | return lf.air_distance < rf.air_distance; | ||
333 | 1470 | }); | ||
334 | 1471 | } | ||
335 | 1472 | |||
336 | 1473 | void FlagCandidates::add_flag(const uint32_t coords, | ||
337 | 1474 | const bool different_economy, | ||
338 | 1475 | const uint16_t act_dist_to_wh, | ||
339 | 1476 | const uint16_t air_distance) { | ||
340 | 1477 | flags_.push_back( | ||
341 | 1478 | Candidate(coords, different_economy, start_flag_dist_to_wh, act_dist_to_wh, air_distance)); | ||
342 | 1479 | } | ||
343 | 1480 | |||
344 | 1481 | bool FlagCandidates::has_candidate(const uint32_t coords_hash) const { | ||
345 | 1482 | for (auto& item : flags_) { | ||
346 | 1483 | if (item.coords_hash == coords_hash) { | ||
347 | 1484 | return true; | ||
348 | 1485 | } | ||
349 | 1486 | } | ||
350 | 1487 | return false; | ||
351 | 1488 | } | ||
352 | 1489 | |||
353 | 1420 | } // namespace Widelands | 1490 | } // namespace Widelands |
354 | 1421 | 1491 | ||
355 | === modified file 'src/ai/ai_help_structs.h' | |||
356 | --- src/ai/ai_help_structs.h 2019-05-25 08:20:22 +0000 | |||
357 | +++ src/ai/ai_help_structs.h 2019-07-31 19:53:54 +0000 | |||
358 | @@ -20,6 +20,7 @@ | |||
359 | 20 | #ifndef WL_AI_AI_HELP_STRUCTS_H | 20 | #ifndef WL_AI_AI_HELP_STRUCTS_H |
360 | 21 | #define WL_AI_AI_HELP_STRUCTS_H | 21 | #define WL_AI_AI_HELP_STRUCTS_H |
361 | 22 | 22 | ||
362 | 23 | #include <algorithm> | ||
363 | 23 | #include <list> | 24 | #include <list> |
364 | 24 | #include <queue> | 25 | #include <queue> |
365 | 25 | #include <unordered_set> | 26 | #include <unordered_set> |
366 | @@ -107,6 +108,7 @@ | |||
367 | 107 | kCheckEnemySites, | 108 | kCheckEnemySites, |
368 | 108 | kManagementUpdate, | 109 | kManagementUpdate, |
369 | 109 | kUpdateStats, | 110 | kUpdateStats, |
370 | 111 | kWarehouseFlagDist, | ||
371 | 110 | kUnset | 112 | kUnset |
372 | 111 | }; | 113 | }; |
373 | 112 | 114 | ||
374 | @@ -122,9 +124,20 @@ | |||
375 | 122 | // TODO(tiborb): this should be replaced by command line switch | 124 | // TODO(tiborb): this should be replaced by command line switch |
376 | 123 | constexpr int kFNeuronBitSize = 32; | 125 | constexpr int kFNeuronBitSize = 32; |
377 | 124 | constexpr int kMutationRatePosition = 42; | 126 | constexpr int kMutationRatePosition = 42; |
378 | 127 | // This is expiration time for distance from a flag to nearest warehouse | ||
379 | 128 | constexpr int kFlagDistanceExpirationPeriod = 120 * 1000; | ||
380 | 129 | // If the distance of flag-warehouse was not updated for this time, we presume that the flag | ||
381 | 130 | // does not exist anymore and remove it | ||
382 | 131 | constexpr int kOldFlagRemoveTime = 5 * 60 * 1000; | ||
383 | 125 | 132 | ||
384 | 126 | constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max(); | 133 | constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max(); |
385 | 127 | 134 | ||
386 | 135 | constexpr uint32_t kNoField = std::numeric_limits<uint32_t>::max(); | ||
387 | 136 | |||
388 | 137 | constexpr uint32_t kOneMinute = 60 * 1000; | ||
389 | 138 | |||
390 | 139 | constexpr uint16_t kFarButReachable = 1000; | ||
391 | 140 | |||
392 | 128 | struct CheckStepRoadAI { | 141 | struct CheckStepRoadAI { |
393 | 129 | CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe); | 142 | CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe); |
394 | 130 | 143 | ||
395 | @@ -527,8 +540,10 @@ | |||
396 | 527 | }; | 540 | }; |
397 | 528 | 541 | ||
398 | 529 | struct WarehouseSiteObserver { | 542 | struct WarehouseSiteObserver { |
399 | 543 | |||
400 | 530 | Widelands::Warehouse* site; | 544 | Widelands::Warehouse* site; |
401 | 531 | BuildingObserver* bo; | 545 | BuildingObserver* bo; |
402 | 546 | uint32_t flag_distances_last_update; | ||
403 | 532 | }; | 547 | }; |
404 | 533 | 548 | ||
405 | 534 | struct ShipObserver { | 549 | struct ShipObserver { |
406 | @@ -766,53 +781,6 @@ | |||
407 | 766 | std::map<uint32_t, uint32_t> blocked_fields_; | 781 | std::map<uint32_t, uint32_t> blocked_fields_; |
408 | 767 | }; | 782 | }; |
409 | 768 | 783 | ||
410 | 769 | // list of candidate flags to build roads, with some additional logic | ||
411 | 770 | struct FlagsForRoads { | ||
412 | 771 | |||
413 | 772 | explicit FlagsForRoads(int32_t mr) : min_reduction(mr) { | ||
414 | 773 | } | ||
415 | 774 | |||
416 | 775 | struct Candidate { | ||
417 | 776 | Candidate(); | ||
418 | 777 | Candidate(uint32_t coords, int32_t distance, bool different_economy); | ||
419 | 778 | |||
420 | 779 | uint32_t coords_hash; | ||
421 | 780 | int32_t new_road_length; | ||
422 | 781 | int32_t current_road_length; | ||
423 | 782 | int32_t air_distance; | ||
424 | 783 | |||
425 | 784 | bool new_road_possible; | ||
426 | 785 | |||
427 | 786 | bool operator<(const Candidate& other) const; | ||
428 | 787 | bool operator==(const Candidate& other) const; | ||
429 | 788 | int32_t reduction_score() const; | ||
430 | 789 | }; | ||
431 | 790 | |||
432 | 791 | int32_t min_reduction; | ||
433 | 792 | // This is the core of this object - candidate flags to be ordered by air_distance | ||
434 | 793 | std::deque<Candidate> flags_queue; | ||
435 | 794 | |||
436 | 795 | void add_flag(Widelands::Coords coords, int32_t air_dist, bool different_economy) { | ||
437 | 796 | flags_queue.push_back(Candidate(coords.hash(), air_dist, different_economy)); | ||
438 | 797 | } | ||
439 | 798 | |||
440 | 799 | uint32_t count() { | ||
441 | 800 | return flags_queue.size(); | ||
442 | 801 | } | ||
443 | 802 | bool has_candidate(uint32_t hash); | ||
444 | 803 | |||
445 | 804 | // This is for debugging and development purposes | ||
446 | 805 | void print(); | ||
447 | 806 | // during processing we need to pick first one uprocessed flag (with best score so far) | ||
448 | 807 | bool get_best_uncalculated(uint32_t* winner); | ||
449 | 808 | // When we test candidate flag if road can be built to it, there are two possible outcomes: | ||
450 | 809 | void road_possible(Widelands::Coords coords, uint32_t new_road); | ||
451 | 810 | // Updating walking distance over existing roads | ||
452 | 811 | void set_cur_road_distance(Widelands::Coords coords, int32_t cur_distance); | ||
453 | 812 | // Finally we query the flag that we will build a road to | ||
454 | 813 | bool get_winner(uint32_t* winner_hash); | ||
455 | 814 | }; | ||
456 | 815 | |||
457 | 816 | // This is a struct that stores strength of players, info on teams and provides some outputs from | 784 | // This is a struct that stores strength of players, info on teams and provides some outputs from |
458 | 817 | // these data | 785 | // these data |
459 | 818 | struct PlayersStrengths { | 786 | struct PlayersStrengths { |
460 | @@ -891,6 +859,97 @@ | |||
461 | 891 | PlayerNumber this_player_number; | 859 | PlayerNumber this_player_number; |
462 | 892 | PlayerNumber this_player_team; | 860 | PlayerNumber this_player_team; |
463 | 893 | }; | 861 | }; |
464 | 862 | |||
465 | 863 | // This is a wrapper around map of <Flag coords hash:distance from flag to nearest warehouse> | ||
466 | 864 | // Flags does not have observers so this stuct server like "lazy" substitution for this, where | ||
467 | 865 | // keys are hash of flags positions | ||
468 | 866 | // This "lives" during entire "session", and is not saved, but is easily recalulated | ||
469 | 867 | struct FlagWarehouseDistances { | ||
470 | 868 | private: | ||
471 | 869 | struct FlagInfo { | ||
472 | 870 | FlagInfo(); | ||
473 | 871 | FlagInfo(uint32_t, uint16_t, uint32_t); | ||
474 | 872 | // Distance to a nearest warehouse expires, but in two stages | ||
475 | 873 | // This is complete expiration and such flag is treated as without distance to WH | ||
476 | 874 | uint32_t expiry_time; | ||
477 | 875 | // When recalculating new distance, if the current information is younger than | ||
478 | 876 | // soft_expiry_time it is only decreased if new value is smaller After soft_expiry_time is | ||
479 | 877 | // updated in any case | ||
480 | 878 | uint32_t soft_expiry_time; | ||
481 | 879 | uint16_t distance; | ||
482 | 880 | // This is coords hash of nearest warehouse, not used by now | ||
483 | 881 | uint32_t nearest_warehouse; | ||
484 | 882 | // after building of new road, the distance information is updated not immediately so | ||
485 | 883 | // we prohibit current flag from another road building... | ||
486 | 884 | uint32_t new_road_prohibited_till; | ||
487 | 885 | |||
488 | 886 | bool update(uint32_t, uint16_t, uint32_t); | ||
489 | 887 | // Saying the road was built and when | ||
490 | 888 | void set_road_built(uint32_t); | ||
491 | 889 | // Asking if road can be built from this flag (providing current gametime) | ||
492 | 890 | bool is_road_prohibited(uint32_t) const; | ||
493 | 891 | // get current distance (providing current gametime) | ||
494 | 892 | uint16_t get(uint32_t, uint32_t*) const; | ||
495 | 893 | }; | ||
496 | 894 | std::map<uint32_t, FlagInfo> flags_map; | ||
497 | 895 | |||
498 | 896 | public: | ||
499 | 897 | // All these function uses lookup in flags_map so first argument is usually flag coords hash | ||
500 | 898 | bool set_distance(uint32_t flag_coords, uint16_t distance, uint32_t gametime, uint32_t nearest_warehouse); | ||
501 | 899 | int16_t get_distance(uint32_t flag_coords, uint32_t gametime, uint32_t* nw); | ||
502 | 900 | void set_road_built(uint32_t coords_hash, uint32_t gametime); | ||
503 | 901 | bool is_road_prohibited(uint32_t coords_hash, uint32_t gametime); | ||
504 | 902 | uint16_t count() const; | ||
505 | 903 | bool remove_old_flag(uint32_t gametime); | ||
506 | 904 | }; | ||
507 | 905 | |||
508 | 906 | // This is one-time structure - initiated and filled up when investigating possible roads to be | ||
509 | 907 | // built to a flag. At the end the flags are scored based on gained info), ordered and if treshold is | ||
510 | 908 | // achieved the road is to be built | ||
511 | 909 | struct FlagCandidates { | ||
512 | 910 | |||
513 | 911 | explicit FlagCandidates(uint16_t wd) : start_flag_dist_to_wh(wd) { | ||
514 | 912 | } | ||
515 | 913 | |||
516 | 914 | struct Candidate { | ||
517 | 915 | Candidate() = delete; | ||
518 | 916 | Candidate(uint32_t, bool, uint16_t, uint16_t, uint16_t); | ||
519 | 917 | uint16_t air_distance; | ||
520 | 918 | uint32_t coords_hash; | ||
521 | 919 | bool different_economy; | ||
522 | 920 | uint16_t start_flag_dist_to_wh; | ||
523 | 921 | uint16_t flag_to_flag_road_distance; | ||
524 | 922 | uint16_t possible_road_distance; | ||
525 | 923 | uint16_t cand_flag_distance_to_wh; | ||
526 | 924 | // Scoring is considering multiple things | ||
527 | 925 | int16_t score() const; | ||
528 | 926 | bool is_buildable() { | ||
529 | 927 | return possible_road_distance > 0; | ||
530 | 928 | } | ||
531 | 929 | bool operator<(const Candidate& other) const { | ||
532 | 930 | return score() > other.score(); | ||
533 | 931 | } | ||
534 | 932 | }; | ||
535 | 933 | |||
536 | 934 | private: | ||
537 | 935 | std::vector<Candidate> flags_; | ||
538 | 936 | uint16_t start_flag_dist_to_wh; | ||
539 | 937 | |||
540 | 938 | public: | ||
541 | 939 | const std::vector<Candidate>& flags() const { | ||
542 | 940 | return flags_; | ||
543 | 941 | } | ||
544 | 942 | bool has_candidate(uint32_t) const; | ||
545 | 943 | void add_flag(uint32_t, bool, uint16_t, uint16_t); | ||
546 | 944 | bool set_cur_road_distance(uint32_t, uint16_t); | ||
547 | 945 | bool set_road_possible(uint32_t, uint16_t); | ||
548 | 946 | void sort(); | ||
549 | 947 | void sort_by_air_distance(); | ||
550 | 948 | uint32_t count() { | ||
551 | 949 | return flags_.size(); | ||
552 | 950 | } | ||
553 | 951 | FlagCandidates::Candidate* get_winner(const int16_t = 0); | ||
554 | 952 | }; | ||
555 | 894 | } // namespace Widelands | 953 | } // namespace Widelands |
556 | 895 | 954 | ||
557 | 896 | #endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H | 955 | #endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H |
558 | 897 | 956 | ||
559 | === modified file 'src/ai/defaultai.cc' | |||
560 | --- src/ai/defaultai.cc 2019-05-26 11:39:41 +0000 | |||
561 | +++ src/ai/defaultai.cc 2019-07-31 19:53:54 +0000 | |||
562 | @@ -331,7 +331,7 @@ | |||
563 | 331 | set_taskpool_task_time(gametime + 1000, SchedulerTaskId::kRoadCheck); | 331 | set_taskpool_task_time(gametime + 1000, SchedulerTaskId::kRoadCheck); |
564 | 332 | // testing 5 roads | 332 | // testing 5 roads |
565 | 333 | { | 333 | { |
567 | 334 | const int32_t roads_to_check = (roads.size() < 20) ? 1 : 3; | 334 | const int32_t roads_to_check = roads.size() / 30 + 1; |
568 | 335 | for (int j = 0; j < roads_to_check; j += 1) { | 335 | for (int j = 0; j < roads_to_check; j += 1) { |
569 | 336 | // improve_roads function will test one road and rotate roads vector | 336 | // improve_roads function will test one road and rotate roads vector |
570 | 337 | if (improve_roads(gametime)) { | 337 | if (improve_roads(gametime)) { |
571 | @@ -486,6 +486,10 @@ | |||
572 | 486 | update_player_stat(gametime); | 486 | update_player_stat(gametime); |
573 | 487 | set_taskpool_task_time(gametime + kStatUpdateInterval, SchedulerTaskId::kUpdateStats); | 487 | set_taskpool_task_time(gametime + kStatUpdateInterval, SchedulerTaskId::kUpdateStats); |
574 | 488 | break; | 488 | break; |
575 | 489 | case SchedulerTaskId::kWarehouseFlagDist: | ||
576 | 490 | check_flag_distances(gametime); | ||
577 | 491 | set_taskpool_task_time(gametime + kFlagWarehouseUpdInterval, SchedulerTaskId::kWarehouseFlagDist); | ||
578 | 492 | break; | ||
579 | 489 | case SchedulerTaskId::kUnset: | 493 | case SchedulerTaskId::kUnset: |
580 | 490 | NEVER_HERE(); | 494 | NEVER_HERE(); |
581 | 491 | } | 495 | } |
582 | @@ -958,6 +962,9 @@ | |||
583 | 958 | taskPool.push_back(SchedulerTask( | 962 | taskPool.push_back(SchedulerTask( |
584 | 959 | std::max<uint32_t>(gametime, 10 * 1000), SchedulerTaskId::kUpdateStats, 15, "review")); | 963 | std::max<uint32_t>(gametime, 10 * 1000), SchedulerTaskId::kUpdateStats, 15, "review")); |
585 | 960 | 964 | ||
586 | 965 | taskPool.push_back(SchedulerTask( | ||
587 | 966 | std::max<uint32_t>(gametime, 10 * 1000), SchedulerTaskId::kWarehouseFlagDist, 5, "Flag-Warehouse Update")); | ||
588 | 967 | |||
589 | 961 | const Map& map = game().map(); | 968 | const Map& map = game().map(); |
590 | 962 | 969 | ||
591 | 963 | // here we scan entire map for own ships | 970 | // here we scan entire map for own ships |
592 | @@ -1053,7 +1060,7 @@ | |||
593 | 1053 | kExpeditionMinDuration + | 1060 | kExpeditionMinDuration + |
594 | 1054 | static_cast<double>(off) * (kExpeditionMaxDuration - kExpeditionMinDuration) / scope; | 1061 | static_cast<double>(off) * (kExpeditionMaxDuration - kExpeditionMinDuration) / scope; |
595 | 1055 | log(" %d: expedition max duration = %u (%u minutes), map area root: %u\n", player_number(), | 1062 | log(" %d: expedition max duration = %u (%u minutes), map area root: %u\n", player_number(), |
597 | 1056 | expedition_max_duration / 1000, expedition_max_duration / 60000, map_area_root); | 1063 | expedition_max_duration / 1000, expedition_max_duration / kOneMinute, map_area_root); |
598 | 1057 | assert(expedition_max_duration >= kExpeditionMinDuration); | 1064 | assert(expedition_max_duration >= kExpeditionMinDuration); |
599 | 1058 | assert(expedition_max_duration <= kExpeditionMaxDuration); | 1065 | assert(expedition_max_duration <= kExpeditionMaxDuration); |
600 | 1059 | 1066 | ||
601 | @@ -3422,6 +3429,70 @@ | |||
602 | 3422 | return true; | 3429 | return true; |
603 | 3423 | } | 3430 | } |
604 | 3424 | 3431 | ||
605 | 3432 | |||
606 | 3433 | // Re-calculating warehouse to flag distances | ||
607 | 3434 | void DefaultAI::check_flag_distances(const uint32_t gametime) { | ||
608 | 3435 | for (WarehouseSiteObserver& wh_obs : warehousesites) { | ||
609 | 3436 | uint16_t checked_flags = 0; | ||
610 | 3437 | const uint32_t this_wh_hash = wh_obs.site->get_position().hash(); | ||
611 | 3438 | uint32_t highest_distance_set = 0; | ||
612 | 3439 | |||
613 | 3440 | std::queue<Widelands::Flag*> | ||
614 | 3441 | remaining_flags; // only used to collect flags reachable walk over roads | ||
615 | 3442 | remaining_flags.push(&wh_obs.site->base_flag()); | ||
616 | 3443 | flag_warehouse_distance.set_distance( | ||
617 | 3444 | wh_obs.site->base_flag().get_position().hash(), 0, gametime, this_wh_hash); | ||
618 | 3445 | uint32_t tmp_wh; | ||
619 | 3446 | assert(flag_warehouse_distance.get_distance( | ||
620 | 3447 | wh_obs.site->base_flag().get_position().hash(), gametime, &tmp_wh) == 0); | ||
621 | 3448 | |||
622 | 3449 | // Algorithm to walk on roads | ||
623 | 3450 | // All nodes are marked as to_be_checked == true first and once the node is checked it is | ||
624 | 3451 | // changed to false. Under some conditions, the same node can be checked twice, the | ||
625 | 3452 | // to_be_checked can be set back to true. Because less hoops (fewer flag-to-flag roads) does | ||
626 | 3453 | // not always mean shortest road. | ||
627 | 3454 | while (!remaining_flags.empty()) { | ||
628 | 3455 | ++checked_flags; | ||
629 | 3456 | // looking for a node with shortest existing road distance from starting flag and one that | ||
630 | 3457 | // has to be checked Now going over roads leading from this flag | ||
631 | 3458 | const uint16_t current_flag_distance = flag_warehouse_distance.get_distance( | ||
632 | 3459 | remaining_flags.front()->get_position().hash(), gametime, &tmp_wh); | ||
633 | 3460 | for (uint8_t i = 1; i <= 6; ++i) { | ||
634 | 3461 | Road* const road = remaining_flags.front()->get_road(i); | ||
635 | 3462 | |||
636 | 3463 | if (!road) { | ||
637 | 3464 | continue; | ||
638 | 3465 | } | ||
639 | 3466 | |||
640 | 3467 | Flag* endflag = &road->get_flag(Road::FlagStart); | ||
641 | 3468 | |||
642 | 3469 | if (endflag == remaining_flags.front()) { | ||
643 | 3470 | endflag = &road->get_flag(Road::FlagEnd); | ||
644 | 3471 | } | ||
645 | 3472 | const uint16_t steps_count = road->get_path().get_nsteps(); | ||
646 | 3473 | |||
647 | 3474 | // Calculated distance can be used or ignored if f.e. longer than via other route | ||
648 | 3475 | bool const updated = flag_warehouse_distance.set_distance( | ||
649 | 3476 | endflag->get_position().hash(), current_flag_distance + steps_count, gametime, | ||
650 | 3477 | this_wh_hash); | ||
651 | 3478 | |||
652 | 3479 | if (highest_distance_set < current_flag_distance + steps_count) { | ||
653 | 3480 | highest_distance_set = current_flag_distance + steps_count; | ||
654 | 3481 | } | ||
655 | 3482 | |||
656 | 3483 | if (updated) { | ||
657 | 3484 | remaining_flags.push(endflag); | ||
658 | 3485 | } | ||
659 | 3486 | } | ||
660 | 3487 | remaining_flags.pop(); | ||
661 | 3488 | } | ||
662 | 3489 | |||
663 | 3490 | } | ||
664 | 3491 | |||
665 | 3492 | // Now let do some lazy pruning - remove the flags that were not updated for long | ||
666 | 3493 | flag_warehouse_distance.remove_old_flag(gametime); | ||
667 | 3494 | } | ||
668 | 3495 | |||
669 | 3425 | // Here we pick about 25 roads and investigate them. If it is a dead end we dismantle it | 3496 | // Here we pick about 25 roads and investigate them. If it is a dead end we dismantle it |
670 | 3426 | bool DefaultAI::dismantle_dead_ends() { | 3497 | bool DefaultAI::dismantle_dead_ends() { |
671 | 3427 | bool road_dismantled = false; | 3498 | bool road_dismantled = false; |
672 | @@ -3538,50 +3609,36 @@ | |||
673 | 3538 | } | 3609 | } |
674 | 3539 | 3610 | ||
675 | 3540 | bool is_warehouse = false; | 3611 | bool is_warehouse = false; |
676 | 3541 | bool has_building = false; | ||
677 | 3542 | if (Building* b = flag.get_building()) { | 3612 | if (Building* b = flag.get_building()) { |
678 | 3543 | has_building = true; | ||
679 | 3544 | BuildingObserver& bo = get_building_observer(b->descr().name().c_str()); | 3613 | BuildingObserver& bo = get_building_observer(b->descr().name().c_str()); |
680 | 3545 | if (bo.type == BuildingObserver::Type::kWarehouse) { | 3614 | if (bo.type == BuildingObserver::Type::kWarehouse) { |
681 | 3546 | is_warehouse = true; | 3615 | is_warehouse = true; |
682 | 3547 | } | 3616 | } |
683 | 3548 | } | 3617 | } |
684 | 3618 | const uint32_t flag_coords_hash = flag.get_position().hash(); | ||
685 | 3549 | 3619 | ||
687 | 3550 | // is connected to a warehouse? | 3620 | if (flag_warehouse_distance.is_road_prohibited(flag_coords_hash, gametime)) {return false;} |
688 | 3551 | const bool needs_warehouse = flag.get_economy()->warehouses().empty(); | 3621 | const bool needs_warehouse = flag.get_economy()->warehouses().empty(); |
689 | 3552 | 3622 | ||
722 | 3553 | if (!has_building && flag.nr_of_roads() == 1) { | 3623 | uint32_t tmp_wh; |
723 | 3554 | return false; | 3624 | |
724 | 3555 | } else if (is_warehouse && flag.nr_of_roads() <= 3) { | 3625 | // when deciding if we attempt to build a road from here we use probability |
725 | 3556 | // Do nothing | 3626 | uint16_t probability_score = 0; |
726 | 3557 | } else if (needs_warehouse) { | 3627 | if (flag.nr_of_roads() == 1) {probability_score += 20;} |
727 | 3558 | // Do nothing | 3628 | if (is_warehouse && flag.nr_of_roads() <= 3) {probability_score += 20;} |
728 | 3559 | } else if (flag.current_wares() > 5) { | 3629 | probability_score += flag.current_wares() * 5; |
729 | 3560 | // Do nothing | 3630 | if (needs_warehouse) {probability_score += 500;} |
730 | 3561 | } else if (has_building && std::rand() % 3 == 0) { | 3631 | if (std::rand() % 10 == 0) { |
731 | 3562 | // Do nothing | 3632 | probability_score += flag_warehouse_distance.get_distance(flag_coords_hash, gametime, &tmp_wh); |
732 | 3563 | } else if (std::rand() % 10 == 0) { | 3633 | } |
733 | 3564 | // Do nothing | 3634 | |
734 | 3565 | } else { | 3635 | if (std::rand() % 200 < probability_score) { |
735 | 3566 | return false; | 3636 | create_shortcut_road(flag, 14, gametime); |
736 | 3567 | } | 3637 | return true; |
737 | 3568 | 3638 | } | |
738 | 3569 | int16_t expected_shortcut = 27; | 3639 | |
739 | 3570 | if (has_building) { | 3640 | return false; |
740 | 3571 | expected_shortcut -= 3; | 3641 | |
709 | 3572 | } | ||
710 | 3573 | expected_shortcut -= flag.current_wares() * 3; | ||
711 | 3574 | if (is_warehouse) { | ||
712 | 3575 | expected_shortcut -= 10; | ||
713 | 3576 | } | ||
714 | 3577 | if (std::rand() % 5 == 0) { | ||
715 | 3578 | expected_shortcut -= 10; | ||
716 | 3579 | } | ||
717 | 3580 | expected_shortcut -= 2 * flag.nr_of_roads(); | ||
718 | 3581 | |||
719 | 3582 | create_shortcut_road(flag, 14, expected_shortcut, gametime); | ||
720 | 3583 | |||
721 | 3584 | return true; | ||
741 | 3585 | } | 3642 | } |
742 | 3586 | 3643 | ||
743 | 3587 | // This function takes a road (road is smallest section of roads with two flags on the ends) | 3644 | // This function takes a road (road is smallest section of roads with two flags on the ends) |
744 | @@ -3756,318 +3813,339 @@ | |||
745 | 3756 | // connection is not known | 3813 | // connection is not known |
746 | 3757 | bool DefaultAI::create_shortcut_road(const Flag& flag, | 3814 | bool DefaultAI::create_shortcut_road(const Flag& flag, |
747 | 3758 | uint16_t checkradius, | 3815 | uint16_t checkradius, |
748 | 3759 | int16_t min_reduction, | ||
749 | 3760 | uint32_t gametime) { | 3816 | uint32_t gametime) { |
750 | 3761 | 3817 | ||
1015 | 3762 | // Increasing the failed_connection_tries counter | 3818 | // Increasing the failed_connection_tries counter |
1016 | 3763 | // At the same time it indicates a time an economy is without a warehouse | 3819 | // At the same time it indicates a time an economy is without a warehouse |
1017 | 3764 | EconomyObserver* eco = get_economy_observer(flag.economy()); | 3820 | EconomyObserver* eco = get_economy_observer(flag.economy()); |
1018 | 3765 | // if we passed grace time this will be last attempt and if it fails | 3821 | // if we passed grace time this will be last attempt and if it fails |
1019 | 3766 | // building is destroyes | 3822 | // building is destroyed |
1020 | 3767 | bool last_attempt_ = false; | 3823 | bool last_attempt_ = false; |
1021 | 3768 | 3824 | ||
1022 | 3769 | // this should not happen, but if the economy has a warehouse and a dismantle | 3825 | // this should not happen, but if the economy has a warehouse and a dismantle |
1023 | 3770 | // grace time set, we must 'zero' the dismantle grace time | 3826 | // grace time set, we must 'zero' the dismantle grace time |
1024 | 3771 | if (!flag.get_economy()->warehouses().empty() && | 3827 | if (!flag.get_economy()->warehouses().empty() && |
1025 | 3772 | eco->dismantle_grace_time != std::numeric_limits<uint32_t>::max()) { | 3828 | eco->dismantle_grace_time != kNever) { |
1026 | 3773 | eco->dismantle_grace_time = std::numeric_limits<uint32_t>::max(); | 3829 | eco->dismantle_grace_time = kNever; |
1027 | 3774 | } | 3830 | } |
1028 | 3775 | 3831 | ||
1029 | 3776 | // first we deal with situations when this is economy with no warehouses | 3832 | // first we deal with situations when this is economy with no warehouses |
1030 | 3777 | // and this is a flag belonging to a building/constructionsite | 3833 | // and this is a flag belonging to a building/constructionsite |
1031 | 3778 | // such economy must get dismantle grace time (if not set yet) | 3834 | // such economy must get dismantle grace time (if not set yet) |
1032 | 3779 | // end sometimes extended checkradius | 3835 | // end sometimes extended checkradius |
1033 | 3780 | if (flag.get_economy()->warehouses().empty() && flag.get_building()) { | 3836 | if (flag.get_economy()->warehouses().empty() && flag.get_building()) { |
1034 | 3781 | 3837 | ||
1035 | 3782 | // occupied military buildings get special treatment | 3838 | // occupied military buildings get special treatment |
1036 | 3783 | // (extended grace time + longer radius) | 3839 | // (extended grace time + longer radius) |
1037 | 3784 | bool occupied_military_ = false; | 3840 | bool occupied_military_ = false; |
1038 | 3785 | Building* b = flag.get_building(); | 3841 | Building* b = flag.get_building(); |
1039 | 3786 | if (upcast(MilitarySite, militb, b)) { | 3842 | if (upcast(MilitarySite, militb, b)) { |
1040 | 3787 | if (militb->soldier_control()->stationed_soldiers().size() > 0) { | 3843 | if (militb->soldier_control()->stationed_soldiers().size() > 0) { |
1041 | 3788 | occupied_military_ = true; | 3844 | occupied_military_ = true; |
1042 | 3789 | } | 3845 | } |
1043 | 3790 | } | 3846 | } |
1044 | 3791 | 3847 | ||
1045 | 3792 | // check if we are within grace time, if not or gracetime unset we need to do something | 3848 | // check if we are within grace time, if not or gracetime unset we need to do something |
1046 | 3793 | // if we are within gracetime we do nothing (this loop is skipped) | 3849 | // if we are within gracetime we do nothing (this loop is skipped) |
1047 | 3794 | 3850 | ||
1048 | 3795 | // if grace time is not set, this is probably first time without a warehouse and we must set | 3851 | // if grace time is not set, this is probably first time without a warehouse and we must set |
1049 | 3796 | // it | 3852 | // it |
1050 | 3797 | if (eco->dismantle_grace_time == std::numeric_limits<uint32_t>::max()) { | 3853 | if (eco->dismantle_grace_time == kNever) { |
1051 | 3798 | 3854 | ||
1052 | 3799 | // constructionsites | 3855 | // constructionsites |
1053 | 3800 | if (upcast(ConstructionSite const, constructionsite, flag.get_building())) { | 3856 | if (upcast(ConstructionSite const, constructionsite, flag.get_building())) { |
1054 | 3801 | BuildingObserver& bo = | 3857 | BuildingObserver& bo = |
1055 | 3802 | get_building_observer(constructionsite->building().name().c_str()); | 3858 | get_building_observer(constructionsite->building().name().c_str()); |
1056 | 3803 | // first very special case - a port (in the phase of constructionsite) | 3859 | // first very special case - a port (in the phase of constructionsite) |
1057 | 3804 | // this might be a new colonization port | 3860 | // this might be a new colonization port |
1058 | 3805 | if (bo.is(BuildingAttribute::kPort)) { | 3861 | if (bo.is(BuildingAttribute::kPort)) { |
1059 | 3806 | eco->dismantle_grace_time = gametime + 60 * 60 * 1000; // one hour should be enough | 3862 | eco->dismantle_grace_time = gametime + 60 * 60 * 1000; // one hour should be enough |
1060 | 3807 | } else { // other constructionsites, usually new (standalone) constructionsites | 3863 | } else { // other constructionsites, usually new (standalone) constructionsites |
1061 | 3808 | eco->dismantle_grace_time = | 3864 | eco->dismantle_grace_time = |
1062 | 3809 | gametime + 30 * 1000 + // very shot time is enough | 3865 | gametime + 30 * 1000 + // very shot time is enough |
1063 | 3810 | (eco->flags.size() * 30 * 1000); // + 30 seconds for every flag in economy | 3866 | (eco->flags.size() * 30 * 1000); // + 30 seconds for every flag in economy |
1064 | 3811 | } | 3867 | } |
1065 | 3812 | 3868 | ||
1066 | 3813 | // buildings | 3869 | // buildings |
1067 | 3814 | } else { | 3870 | } else { |
1068 | 3815 | 3871 | ||
1069 | 3816 | if (occupied_military_) { | 3872 | if (occupied_military_) { |
1070 | 3817 | eco->dismantle_grace_time = | 3873 | eco->dismantle_grace_time = |
1071 | 3818 | (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000); | 3874 | (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000); |
1072 | 3819 | 3875 | ||
1073 | 3820 | } else { // for other normal buildings | 3876 | } else { // for other normal buildings |
1074 | 3821 | eco->dismantle_grace_time = | 3877 | eco->dismantle_grace_time = |
1075 | 3822 | gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000); | 3878 | gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000); |
1076 | 3823 | } | 3879 | } |
1077 | 3824 | } | 3880 | } |
1078 | 3825 | 3881 | ||
1079 | 3826 | // we have passed grace_time - it is time to dismantle | 3882 | // we have passed grace_time - it is time to dismantle |
1080 | 3827 | } else if (eco->dismantle_grace_time <= gametime) { | 3883 | } else if (eco->dismantle_grace_time <= gametime) { |
1081 | 3828 | last_attempt_ = true; | 3884 | last_attempt_ = true; |
1082 | 3829 | // we increase a check radius in last attempt | 3885 | // we increase a check radius in last attempt |
1083 | 3830 | checkradius += 2; | 3886 | checkradius += 2; |
1084 | 3831 | } | 3887 | } |
1085 | 3832 | 3888 | ||
1086 | 3833 | // and bonus for occupied military buildings: | 3889 | // and bonus for occupied military buildings: |
1087 | 3834 | if (occupied_military_) { | 3890 | if (occupied_military_) { |
1088 | 3835 | checkradius += 4; | 3891 | checkradius += 4; |
1089 | 3836 | } | 3892 | } |
1090 | 3837 | 3893 | ||
1091 | 3838 | // and generally increase radius for unconnected buildings | 3894 | // and generally increase radius for unconnected buildings |
1092 | 3839 | checkradius += 2; | 3895 | checkradius += 2; |
1093 | 3840 | } | 3896 | } |
1094 | 3841 | 3897 | ||
1095 | 3842 | // Now own roadfinding stuff | 3898 | // Now own roadfinding stuff |
1096 | 3843 | const Map& map = game().map(); | 3899 | const Map& map = game().map(); |
1097 | 3844 | 3900 | ||
1098 | 3845 | // initializing new object of FlagsForRoads, we will push there all candidate flags | 3901 | // Initializing new object of FlagsForRoads, we will push there all candidate flags |
1099 | 3846 | // First we dont even know if a road can be built there (from current flag) | 3902 | // First we dont even know if a road can be built there (from current flag) |
1100 | 3847 | Widelands::FlagsForRoads RoadCandidates(min_reduction); | 3903 | // Adding also distance of this flag to nearest wh |
1101 | 3848 | 3904 | uint32_t tmp_wh; // This information is not used, but we need it | |
1102 | 3849 | FindNodeWithFlagOrRoad functor; | 3905 | const uint32_t current_flag_dist_to_wh = flag_warehouse_distance. |
1103 | 3850 | CheckStepRoadAI check(player_, MOVECAPS_WALK, true); | 3906 | get_distance(flag.get_position().hash(), gametime, &tmp_wh); |
1104 | 3851 | 3907 | ||
1105 | 3852 | // get all flags within radius | 3908 | FlagCandidates flag_candidates(current_flag_dist_to_wh); |
1106 | 3853 | std::vector<Coords> reachable; | 3909 | |
1107 | 3854 | map.find_reachable_fields( | 3910 | FindNodeWithFlagOrRoad functor; |
1108 | 3855 | Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius), &reachable, check, functor); | 3911 | CheckStepRoadAI check(player_, MOVECAPS_WALK, true); |
1109 | 3856 | 3912 | ||
1110 | 3857 | for (const Coords& reachable_coords : reachable) { | 3913 | // get all flags within radius |
1111 | 3858 | 3914 | std::vector<Coords> reachable; | |
1112 | 3859 | // ignore starting flag, of course | 3915 | map.find_reachable_fields( |
1113 | 3860 | if (reachable_coords == flag.get_position()) { | 3916 | Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius), &reachable, check, functor); |
1114 | 3861 | continue; | 3917 | |
1115 | 3862 | } | 3918 | for (const Coords& reachable_coords : reachable) { |
1116 | 3863 | 3919 | ||
1117 | 3864 | // first make sure there is an immovable (should be, but still) | 3920 | // ignore starting flag, of course |
1118 | 3865 | if (upcast(PlayerImmovable const, player_immovable, map[reachable_coords].get_immovable())) { | 3921 | if (reachable_coords == flag.get_position()) { |
1119 | 3866 | 3922 | continue; | |
1120 | 3867 | // if it is the road, make a flag there | 3923 | } |
1121 | 3868 | if (dynamic_cast<const Road*>(map[reachable_coords].get_immovable())) { | 3924 | |
1122 | 3869 | game().send_player_build_flag(player_number(), reachable_coords); | 3925 | const uint32_t reachable_coords_hash = reachable_coords.hash(); |
1123 | 3870 | } | 3926 | |
1124 | 3871 | 3927 | // first make sure there is an immovable (should be, but still) | |
1125 | 3872 | // do not go on if it is not a flag | 3928 | Widelands::BaseImmovable* this_immovable = map[reachable_coords].get_immovable(); |
1126 | 3873 | if (!dynamic_cast<const Flag*>(map[reachable_coords].get_immovable())) { | 3929 | if (upcast(PlayerImmovable const, player_immovable, this_immovable)) { |
1127 | 3874 | continue; | 3930 | |
1128 | 3875 | } | 3931 | // if it is the road, make a flag there |
1129 | 3876 | 3932 | if (this_immovable->descr().type() == MapObjectType::ROAD) { | |
1130 | 3877 | // testing if a flag/road's economy has a warehouse, if not we are not | 3933 | game().send_player_build_flag(player_number(), reachable_coords); |
1131 | 3878 | // interested to connect to it | 3934 | } |
1132 | 3879 | if (player_immovable->economy().warehouses().size() == 0) { | 3935 | |
1133 | 3880 | continue; | 3936 | // do not go on if it is not a flag |
1134 | 3881 | } | 3937 | if (this_immovable->descr().type() != MapObjectType::FLAG) { |
1135 | 3882 | 3938 | continue; | |
1136 | 3883 | // This is a candidate, sending all necessary info to RoadCandidates | 3939 | } |
1137 | 3884 | const bool different_economy = (player_immovable->get_economy() != flag.get_economy()); | 3940 | |
1138 | 3885 | const int32_t air_distance = map.calc_distance(flag.get_position(), reachable_coords); | 3941 | // testing if a flag/road's economy has a warehouse, if not we are not |
1139 | 3886 | if (!RoadCandidates.has_candidate(reachable_coords.hash())) { | 3942 | // interested to connect to it |
1140 | 3887 | RoadCandidates.add_flag(reachable_coords, air_distance, different_economy); | 3943 | if (player_immovable->economy().warehouses().size() == 0) { |
1141 | 3888 | } | 3944 | continue; |
1142 | 3889 | } | 3945 | } |
1143 | 3890 | } | 3946 | |
1144 | 3891 | 3947 | // This is a candidate, sending all necessary info to RoadCandidates | |
1145 | 3892 | // now we walk over roads and if field is reachable by roads, we change the distance assigned | 3948 | const bool is_different_economy = (player_immovable->get_economy() != flag.get_economy()); |
1146 | 3893 | // above | 3949 | const uint16_t air_distance = map.calc_distance(flag.get_position(), reachable_coords); |
1147 | 3894 | std::map<uint32_t, NearFlag> nearflags; // only used to collect flags reachable walk over roads | 3950 | |
1148 | 3895 | nearflags[flag.get_position().hash()] = NearFlag(&flag, 0); | 3951 | if (!flag_candidates.has_candidate(reachable_coords_hash) && !flag_warehouse_distance.is_road_prohibited(reachable_coords_hash, gametime)) { |
1149 | 3896 | 3952 | flag_candidates.add_flag(reachable_coords_hash, is_different_economy, | |
1150 | 3897 | // Algorithm to walk on roads | 3953 | flag_warehouse_distance.get_distance(reachable_coords_hash, gametime, &tmp_wh), air_distance); |
1151 | 3898 | // All nodes are marked as to_be_checked == true first and once the node is checked it is changed | 3954 | } |
1152 | 3899 | // to false. Under some conditions, the same node can be checked twice, the to_be_checked can | 3955 | } |
1153 | 3900 | // be set back to true. Because less hoops (fewer flag-to-flag roads) does not always mean | 3956 | } |
1154 | 3901 | // shortest | 3957 | |
1155 | 3902 | // road. | 3958 | // now we walk over roads and if field is reachable by roads, we change the distance assigned |
1156 | 3903 | for (;;) { | 3959 | // above |
1157 | 3904 | // looking for a node with shortest existing road distance from starting flag and one that has | 3960 | std::map<uint32_t, NearFlag> nearflags; // only used to collect flags reachable walk over roads |
1158 | 3905 | // to be checked | 3961 | nearflags[flag.get_position().hash()] = NearFlag(&flag, 0); |
1159 | 3906 | uint32_t start_field = std::numeric_limits<uint32_t>::max(); | 3962 | |
1160 | 3907 | uint32_t nearest_distance = 10000; | 3963 | collect_nearflags(nearflags, flag, checkradius); |
1161 | 3908 | for (auto item : nearflags) { | 3964 | |
1162 | 3909 | if (item.second.current_road_distance < nearest_distance && item.second.to_be_checked) { | 3965 | // Sending calculated walking costs from nearflags to RoadCandidates to update info on |
1163 | 3910 | nearest_distance = item.second.current_road_distance; | 3966 | // Candidate flags/roads |
1164 | 3911 | start_field = item.first; | 3967 | for (auto& nf_walk : nearflags) { |
1165 | 3912 | } | 3968 | const uint32_t nf_hash = nf_walk.second.flag->get_position().hash(); |
1166 | 3913 | } | 3969 | // NearFlag contains also flags beyond check radius, these are not relevant for us |
1167 | 3914 | // OK, we failed to find a NearFlag where to_be_checked == true, so quitting the loop now | 3970 | if (flag_candidates.has_candidate(nf_hash)) { |
1168 | 3915 | if (start_field == std::numeric_limits<uint32_t>::max()) { | 3971 | flag_candidates.set_cur_road_distance( |
1169 | 3916 | break; | 3972 | nf_hash, nf_walk.second.current_road_distance); |
1170 | 3917 | } | 3973 | } |
1171 | 3918 | 3974 | } | |
1172 | 3919 | nearflags[start_field].to_be_checked = false; | 3975 | |
1173 | 3920 | 3976 | // Here we must consider how much are buildable fields lacking | |
1174 | 3921 | // Now going over roads leading from this flag | 3977 | // the number will be transformed to a weight passed to findpath function |
1175 | 3922 | for (uint8_t i = 1; i <= 6; ++i) { | 3978 | int32_t fields_necessity = 0; |
1176 | 3923 | Road* const road = nearflags[start_field].flag->get_road(i); | 3979 | if (spots_ < kSpotsTooLittle) { |
1177 | 3924 | 3980 | fields_necessity += 10; | |
1178 | 3925 | if (!road) { | 3981 | } |
1179 | 3926 | continue; | 3982 | if (map_allows_seafaring_ && num_ports == 0) { |
1180 | 3927 | } | 3983 | fields_necessity += 10; |
1181 | 3928 | 3984 | } | |
1182 | 3929 | Flag* endflag = &road->get_flag(Road::FlagStart); | 3985 | if (num_ports < 4) { |
1183 | 3930 | 3986 | fields_necessity += 5; | |
1184 | 3931 | if (endflag == nearflags[start_field].flag) { | 3987 | } |
1185 | 3932 | endflag = &road->get_flag(Road::FlagEnd); | 3988 | if (spots_ < kSpotsEnough) { |
1186 | 3933 | } | 3989 | fields_necessity += 5; |
1187 | 3934 | 3990 | } | |
1188 | 3935 | const uint32_t endflag_hash = endflag->get_position().hash(); | 3991 | |
1189 | 3936 | 3992 | fields_necessity *= std::abs(management_data.get_military_number_at(64)) * 5; | |
1190 | 3937 | const int32_t dist = map.calc_distance(flag.get_position(), endflag->get_position()); | 3993 | |
1191 | 3938 | 3994 | // Now we calculate roads from here to few best looking RoadCandidates.... | |
1192 | 3939 | if (dist > checkradius + 2) { // Testing bigger vicinity then checkradius.... | 3995 | flag_candidates.sort_by_air_distance(); |
1193 | 3940 | continue; | 3996 | uint32_t possible_roads_count = 0; |
1194 | 3941 | } | 3997 | for (const auto &flag_candidate : flag_candidates.flags()) { |
1195 | 3942 | 3998 | if (possible_roads_count > 10) {break;} | |
1196 | 3943 | // There is few scenarios for this neighbour flag | 3999 | const Widelands::Coords coords = Coords::unhash(flag_candidate.coords_hash); |
1197 | 3944 | if (nearflags.count(endflag_hash) == 0) { | 4000 | Path path; |
1198 | 3945 | // This is brand new flag | 4001 | |
1199 | 3946 | nearflags[endflag_hash] = | 4002 | // value of pathcost is not important, it just indicates, that the path can be built |
1200 | 3947 | NearFlag(endflag, nearflags[start_field].current_road_distance + | 4003 | // We send this information to RoadCandidates, with length of possible road if applicable |
1201 | 3948 | road->get_path().get_nsteps()); | 4004 | const int32_t pathcost = |
1202 | 3949 | } else { | 4005 | map.findpath(flag.get_position(), coords, 0, path, check, 0, fields_necessity); |
1203 | 3950 | // We know about this flag already | 4006 | if (pathcost >= 0) { |
1204 | 3951 | if (nearflags[endflag_hash].current_road_distance > | 4007 | flag_candidates.set_road_possible(flag_candidate.coords_hash, path.get_nsteps()); |
1205 | 3952 | nearflags[start_field].current_road_distance + road->get_path().get_nsteps()) { | 4008 | ++possible_roads_count; |
1206 | 3953 | // ..but this current connection is shorter than one found before | 4009 | } |
1207 | 3954 | nearflags[endflag_hash].current_road_distance = | 4010 | } |
1208 | 3955 | nearflags[start_field].current_road_distance + road->get_path().get_nsteps(); | 4011 | |
1209 | 3956 | // So let re-check neighbours once more | 4012 | // re-sorting again now by default by a score |
1210 | 3957 | nearflags[endflag_hash].to_be_checked = true; | 4013 | flag_candidates.sort(); |
1211 | 3958 | } | 4014 | |
1212 | 3959 | } | 4015 | |
1213 | 3960 | } | 4016 | // Well and finally building the winning road (if any) |
1214 | 3961 | } | 4017 | const int32_t winner_min_score = (spots_ < kSpotsTooLittle) ? 50 : 25; |
1215 | 3962 | 4018 | ||
1216 | 3963 | // Sending calculated walking costs from nearflags to RoadCandidates to update info on | 4019 | FlagCandidates::Candidate* winner = flag_candidates.get_winner(winner_min_score); |
1217 | 3964 | // Candidate flags/roads | 4020 | if (winner) { |
1218 | 3965 | for (auto& nf_walk : nearflags) { | 4021 | const Widelands::Coords target_coords = Coords::unhash(winner->coords_hash); |
1219 | 3966 | // NearFlag contains also flags beyond check radius, these are not relevent for us | 4022 | |
1220 | 3967 | if (RoadCandidates.has_candidate(nf_walk.second.flag->get_position().hash())) { | 4023 | // This is to prohibit the flag for some time but with exemption of warehouse |
1221 | 3968 | RoadCandidates.set_cur_road_distance( | 4024 | if (flag_warehouse_distance.get_distance(winner->coords_hash, gametime, &tmp_wh) > 0) { |
1222 | 3969 | nf_walk.second.flag->get_position(), nf_walk.second.current_road_distance); | 4025 | flag_warehouse_distance.set_road_built(winner->coords_hash, gametime); |
1223 | 3970 | } | 4026 | } |
1224 | 3971 | } | 4027 | // and we straight away set distance of future flag |
1225 | 3972 | 4028 | flag_warehouse_distance.set_distance(flag.get_position().hash(), winner->start_flag_dist_to_wh + winner->possible_road_distance, | |
1226 | 3973 | // Here we must consider how much are buildable fields lacking | 4029 | gametime, 0); // faking the warehouse |
1227 | 3974 | // the number will be transformed to a weight passed to findpath function | 4030 | |
1228 | 3975 | int32_t fields_necessity = 0; | 4031 | Path& path = *new Path(); |
965 | 3976 | if (spots_ < kSpotsTooLittle) { | ||
966 | 3977 | fields_necessity += 10; | ||
967 | 3978 | } | ||
968 | 3979 | if (map_allows_seafaring_ && num_ports == 0) { | ||
969 | 3980 | fields_necessity += 10; | ||
970 | 3981 | } | ||
971 | 3982 | if (num_ports < 4) { | ||
972 | 3983 | fields_necessity += 5; | ||
973 | 3984 | } | ||
974 | 3985 | if (spots_ < kSpotsEnough) { | ||
975 | 3986 | fields_necessity += 5; | ||
976 | 3987 | } | ||
977 | 3988 | fields_necessity *= std::abs(management_data.get_military_number_at(64)) * 5; | ||
978 | 3989 | |||
979 | 3990 | // We need to sort these flags somehow, because we are not going to investigate all of them | ||
980 | 3991 | // so sorting first by current road length (that might contain fake values for flags that were | ||
981 | 3992 | // not reached over roads) and secondary by air_distance (nearer flags first) | ||
982 | 3993 | std::sort(std::begin(RoadCandidates.flags_queue), std::end(RoadCandidates.flags_queue), | ||
983 | 3994 | [](const FlagsForRoads::Candidate& a, const FlagsForRoads::Candidate& b) { | ||
984 | 3995 | // Here we are doing a kind of bucketing | ||
985 | 3996 | const int32_t a_length = a.current_road_length / 50; | ||
986 | 3997 | const int32_t b_length = b.current_road_length / 50; | ||
987 | 3998 | return std::tie(b_length, a.air_distance) < std::tie(a_length, b.air_distance); | ||
988 | 3999 | }); | ||
989 | 4000 | |||
990 | 4001 | // Now we calculate roads from here to few best looking RoadCandidates.... | ||
991 | 4002 | uint32_t possible_roads_count = 0; | ||
992 | 4003 | uint32_t current = 0; // hash of flag that we are going to calculate in the iteration | ||
993 | 4004 | while (possible_roads_count < 5 && RoadCandidates.get_best_uncalculated(¤t)) { | ||
994 | 4005 | const Widelands::Coords coords = Coords::unhash(current); | ||
995 | 4006 | Path path; | ||
996 | 4007 | |||
997 | 4008 | // value of pathcost is not important, it just indicates, that the path can be built | ||
998 | 4009 | // We send this information to RoadCandidates, with length of possible road if applicable | ||
999 | 4010 | const int32_t pathcost = | ||
1000 | 4011 | map.findpath(flag.get_position(), coords, 0, path, check, 0, fields_necessity); | ||
1001 | 4012 | if (pathcost >= 0) { | ||
1002 | 4013 | RoadCandidates.road_possible(coords, path.get_nsteps()); | ||
1003 | 4014 | possible_roads_count += 1; | ||
1004 | 4015 | } | ||
1005 | 4016 | } | ||
1006 | 4017 | |||
1007 | 4018 | // re-sorting again, now by reduction score (custom operator specified in .h file) | ||
1008 | 4019 | std::sort(std::begin(RoadCandidates.flags_queue), std::end(RoadCandidates.flags_queue)); | ||
1009 | 4020 | |||
1010 | 4021 | // Well and finally building the winning road (if any) | ||
1011 | 4022 | uint32_t winner_hash = 0; | ||
1012 | 4023 | if (RoadCandidates.get_winner(&winner_hash)) { | ||
1013 | 4024 | const Widelands::Coords target_coords = Coords::unhash(winner_hash); | ||
1014 | 4025 | Path& path = *new Path(); | ||
1229 | 4026 | #ifndef NDEBUG | 4032 | #ifndef NDEBUG |
1233 | 4027 | const int32_t pathcost = | 4033 | const int32_t pathcost = |
1234 | 4028 | map.findpath(flag.get_position(), target_coords, 0, path, check, 0, fields_necessity); | 4034 | map.findpath(flag.get_position(), target_coords, 0, path, check, 0, fields_necessity); |
1235 | 4029 | assert(pathcost >= 0); | 4035 | assert(pathcost >= 0); |
1236 | 4030 | #else | 4036 | #else |
1238 | 4031 | map.findpath(flag.get_position(), target_coords, 0, path, check, 0, fields_necessity); | 4037 | map.findpath(flag.get_position(), target_coords, 0, path, check, 0, fields_necessity); |
1239 | 4032 | #endif | 4038 | #endif |
1278 | 4033 | game().send_player_build_road(player_number(), path); | 4039 | game().send_player_build_road(player_number(), path); |
1279 | 4034 | return true; | 4040 | return true; |
1280 | 4035 | } | 4041 | } |
1281 | 4036 | // We can't build a road so let's block the vicinity as an indication this area is not | 4042 | // We can't build a road so let's block the vicinity as an indication this area is not |
1282 | 4037 | // connectible | 4043 | // connectible |
1283 | 4038 | // Usually we block for 2 minutes, but if it is a last attempt we block for 10 minutes | 4044 | // Usually we block for 2 minutes, but if it is a last attempt we block for 10 minutes |
1284 | 4039 | // Note: we block the vicinity only if this economy (usually a sole flag with a building) is not | 4045 | // Note: we block the vicinity only if this economy (usually a sole flag with a building) is not |
1285 | 4040 | // connected to a warehouse | 4046 | // connected to a warehouse |
1286 | 4041 | if (flag.get_economy()->warehouses().empty()) { | 4047 | if (flag.get_economy()->warehouses().empty()) { |
1287 | 4042 | 4048 | ||
1288 | 4043 | // blocking only if latest block was less then 60 seconds ago or it is last attempt | 4049 | // blocking only if latest block was less then 60 seconds ago or it is last attempt |
1289 | 4044 | if (eco->fields_block_last_time + 60000 < gametime || last_attempt_) { | 4050 | if (eco->fields_block_last_time + kOneMinute < gametime || last_attempt_) { |
1290 | 4045 | eco->fields_block_last_time = gametime; | 4051 | eco->fields_block_last_time = gametime; |
1291 | 4046 | 4052 | ||
1292 | 4047 | const uint32_t block_time = last_attempt_ ? 10 * 60 * 1000 : 2 * 60 * 1000; | 4053 | const uint32_t block_time = last_attempt_ ? 10 * kOneMinute : 2 * kOneMinute; |
1293 | 4048 | 4054 | ||
1294 | 4049 | FindNodeAcceptAll buildable_functor; | 4055 | FindNodeAcceptAll buildable_functor; |
1295 | 4050 | CheckStepOwnTerritory check_own(player_, MOVECAPS_WALK, true); | 4056 | CheckStepOwnTerritory check_own(player_, MOVECAPS_WALK, true); |
1296 | 4051 | 4057 | ||
1297 | 4052 | // get all flags within radius | 4058 | // get all flags within radius |
1298 | 4053 | std::vector<Coords> reachable_to_block; | 4059 | std::vector<Coords> reachable_to_block; |
1299 | 4054 | map.find_reachable_fields(Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius), | 4060 | map.find_reachable_fields(Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius), |
1300 | 4055 | &reachable_to_block, check_own, buildable_functor); | 4061 | &reachable_to_block, check_own, buildable_functor); |
1301 | 4056 | 4062 | ||
1302 | 4057 | for (auto coords : reachable_to_block) { | 4063 | for (auto coords : reachable_to_block) { |
1303 | 4058 | blocked_fields.add(coords, game().get_gametime() + block_time); | 4064 | blocked_fields.add(coords, game().get_gametime() + block_time); |
1304 | 4059 | } | 4065 | } |
1305 | 4060 | } | 4066 | } |
1306 | 4061 | 4067 | ||
1307 | 4062 | // If it last attempt we also destroy the flag (with a building if any attached) | 4068 | // If it last attempt we also destroy the flag (with a building if any attached) |
1308 | 4063 | if (last_attempt_) { | 4069 | if (last_attempt_) { |
1309 | 4064 | remove_from_dqueue<Widelands::Flag>(eco->flags, &flag); | 4070 | remove_from_dqueue<Widelands::Flag>(eco->flags, &flag); |
1310 | 4065 | game().send_player_bulldoze(*const_cast<Flag*>(&flag)); | 4071 | game().send_player_bulldoze(*const_cast<Flag*>(&flag)); |
1311 | 4066 | dead_ends_check_ = true; | 4072 | dead_ends_check_ = true; |
1312 | 4067 | return true; | 4073 | return true; |
1313 | 4068 | } | 4074 | } |
1314 | 4069 | } | 4075 | } |
1315 | 4070 | return false; | 4076 | return false; |
1316 | 4077 | } | ||
1317 | 4078 | |||
1318 | 4079 | void DefaultAI::collect_nearflags(std::map<uint32_t, NearFlag> &nearflags, const Flag& flag, const uint16_t checkradius) { | ||
1319 | 4080 | // Algorithm to walk on roads | ||
1320 | 4081 | // All nodes are marked as to_be_checked == true first and once the node is checked it is changed | ||
1321 | 4082 | // to false. Under some conditions, the same node can be checked twice, the to_be_checked can | ||
1322 | 4083 | // be set back to true. Because less hoops (fewer flag-to-flag roads) does not always mean | ||
1323 | 4084 | // shortest road. | ||
1324 | 4085 | |||
1325 | 4086 | const Map& map = game().map(); | ||
1326 | 4087 | |||
1327 | 4088 | for (;;) { | ||
1328 | 4089 | // looking for a node with shortest existing road distance from starting flag and one that has | ||
1329 | 4090 | // to be checked | ||
1330 | 4091 | uint32_t start_field = kNoField; | ||
1331 | 4092 | uint32_t nearest_distance = 10000; | ||
1332 | 4093 | for (auto item : nearflags) { | ||
1333 | 4094 | if (item.second.current_road_distance < nearest_distance && item.second.to_be_checked) { | ||
1334 | 4095 | nearest_distance = item.second.current_road_distance; | ||
1335 | 4096 | start_field = item.first; | ||
1336 | 4097 | } | ||
1337 | 4098 | } | ||
1338 | 4099 | // OK, we failed to find a NearFlag where to_be_checked == true, so quitting the loop now | ||
1339 | 4100 | if (start_field == kNoField) { | ||
1340 | 4101 | break; | ||
1341 | 4102 | } | ||
1342 | 4103 | |||
1343 | 4104 | nearflags[start_field].to_be_checked = false; | ||
1344 | 4105 | |||
1345 | 4106 | // Now going over roads leading from this flag | ||
1346 | 4107 | for (uint8_t i = 1; i <= 6; ++i) { | ||
1347 | 4108 | Road* const road = nearflags[start_field].flag->get_road(i); | ||
1348 | 4109 | |||
1349 | 4110 | if (!road) { | ||
1350 | 4111 | continue; | ||
1351 | 4112 | } | ||
1352 | 4113 | |||
1353 | 4114 | Flag* endflag = &road->get_flag(Road::FlagStart); | ||
1354 | 4115 | |||
1355 | 4116 | if (endflag == nearflags[start_field].flag) { | ||
1356 | 4117 | endflag = &road->get_flag(Road::FlagEnd); | ||
1357 | 4118 | } | ||
1358 | 4119 | |||
1359 | 4120 | const uint32_t endflag_hash = endflag->get_position().hash(); | ||
1360 | 4121 | |||
1361 | 4122 | const int32_t dist = map.calc_distance(flag.get_position(), endflag->get_position()); | ||
1362 | 4123 | |||
1363 | 4124 | if (dist > checkradius + 2) { // Testing bigger vicinity then checkradius.... | ||
1364 | 4125 | continue; | ||
1365 | 4126 | } | ||
1366 | 4127 | |||
1367 | 4128 | // There is few scenarios for this neighbour flag | ||
1368 | 4129 | if (nearflags.count(endflag_hash) == 0) { | ||
1369 | 4130 | // This is brand new flag | ||
1370 | 4131 | // calculating diff how much closer we will get to the flag | ||
1371 | 4132 | nearflags[endflag_hash] = | ||
1372 | 4133 | NearFlag(endflag, nearflags[start_field].current_road_distance + | ||
1373 | 4134 | road->get_path().get_nsteps()); | ||
1374 | 4135 | } else { | ||
1375 | 4136 | // We know about this flag already | ||
1376 | 4137 | if (nearflags[endflag_hash].current_road_distance > | ||
1377 | 4138 | nearflags[start_field].current_road_distance + road->get_path().get_nsteps()) { | ||
1378 | 4139 | // ..but this current connection is shorter than one found before | ||
1379 | 4140 | nearflags[endflag_hash].current_road_distance = | ||
1380 | 4141 | nearflags[start_field].current_road_distance + road->get_path().get_nsteps(); | ||
1381 | 4142 | // So let re-check neighbours once more | ||
1382 | 4143 | nearflags[endflag_hash].to_be_checked = true; | ||
1383 | 4144 | } | ||
1384 | 4145 | } | ||
1385 | 4146 | } | ||
1386 | 4147 | } | ||
1387 | 4148 | |||
1388 | 4071 | } | 4149 | } |
1389 | 4072 | 4150 | ||
1390 | 4073 | /** | 4151 | /** |
1391 | 4074 | 4152 | ||
1392 | === modified file 'src/ai/defaultai.h' | |||
1393 | --- src/ai/defaultai.h 2019-05-15 06:29:24 +0000 | |||
1394 | +++ src/ai/defaultai.h 2019-07-31 19:53:54 +0000 | |||
1395 | @@ -148,6 +148,7 @@ | |||
1396 | 148 | static constexpr int32_t kSpotsTooLittle = 15; | 148 | static constexpr int32_t kSpotsTooLittle = 15; |
1397 | 149 | static constexpr int kManagementUpdateInterval = 10 * 60 * 1000; | 149 | static constexpr int kManagementUpdateInterval = 10 * 60 * 1000; |
1398 | 150 | static constexpr int kStatUpdateInterval = 60 * 1000; | 150 | static constexpr int kStatUpdateInterval = 60 * 1000; |
1399 | 151 | static constexpr int kFlagWarehouseUpdInterval = 15 * 1000; | ||
1400 | 151 | 152 | ||
1401 | 152 | // For vision and scheduling | 153 | // For vision and scheduling |
1402 | 153 | static constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max(); | 154 | static constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max(); |
1403 | @@ -198,11 +199,14 @@ | |||
1404 | 198 | bool improve_roads(uint32_t); | 199 | bool improve_roads(uint32_t); |
1405 | 199 | bool create_shortcut_road(const Widelands::Flag&, | 200 | bool create_shortcut_road(const Widelands::Flag&, |
1406 | 200 | uint16_t maxcheckradius, | 201 | uint16_t maxcheckradius, |
1407 | 201 | int16_t minReduction, | ||
1408 | 202 | const uint32_t gametime); | 202 | const uint32_t gametime); |
1409 | 203 | // trying to identify roads that might be removed | 203 | // trying to identify roads that might be removed |
1410 | 204 | bool dispensable_road_test(const Widelands::Road&); | 204 | bool dispensable_road_test(const Widelands::Road&); |
1411 | 205 | bool dismantle_dead_ends(); | 205 | bool dismantle_dead_ends(); |
1412 | 206 | void collect_nearflags(std::map<uint32_t, Widelands::NearFlag> &, const Widelands::Flag&, const uint16_t); | ||
1413 | 207 | // calculating distances from local warehouse to flags | ||
1414 | 208 | void check_flag_distances(uint32_t); | ||
1415 | 209 | Widelands::FlagWarehouseDistances flag_warehouse_distance; | ||
1416 | 206 | 210 | ||
1417 | 207 | bool check_economies(); | 211 | bool check_economies(); |
1418 | 208 | bool check_productionsites(uint32_t); | 212 | bool check_productionsites(uint32_t); |
1419 | @@ -272,6 +276,7 @@ | |||
1420 | 272 | bool check_ships(uint32_t); | 276 | bool check_ships(uint32_t); |
1421 | 273 | bool attempt_escape(Widelands::ShipObserver& so); | 277 | bool attempt_escape(Widelands::ShipObserver& so); |
1422 | 274 | 278 | ||
1423 | 279 | |||
1424 | 275 | // finding and owner | 280 | // finding and owner |
1425 | 276 | Widelands::PlayerNumber get_land_owner(const Widelands::Map&, uint32_t); | 281 | Widelands::PlayerNumber get_land_owner(const Widelands::Map&, uint32_t); |
1426 | 277 | 282 | ||
1427 | 278 | 283 | ||
1428 | === added directory 'src/ai/test' | |||
1429 | === added file 'src/ai/test/CMakeLists.txt' | |||
1430 | --- src/ai/test/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
1431 | +++ src/ai/test/CMakeLists.txt 2019-07-31 19:53:54 +0000 | |||
1432 | @@ -0,0 +1,9 @@ | |||
1433 | 1 | wl_test(test_ai | ||
1434 | 2 | SRCS | ||
1435 | 3 | ai_test_main.cc | ||
1436 | 4 | test_ai.cc | ||
1437 | 5 | DEPENDS | ||
1438 | 6 | base_log | ||
1439 | 7 | base_macros | ||
1440 | 8 | ai | ||
1441 | 9 | ) | ||
1442 | 0 | 10 | ||
1443 | === added file 'src/ai/test/ai_test_main.cc' | |||
1444 | --- src/ai/test/ai_test_main.cc 1970-01-01 00:00:00 +0000 | |||
1445 | +++ src/ai/test/ai_test_main.cc 2019-07-31 19:53:54 +0000 | |||
1446 | @@ -0,0 +1,21 @@ | |||
1447 | 1 | /* | ||
1448 | 2 | * Copyright (C) 2007-2019 by the Widelands Development Team | ||
1449 | 3 | * | ||
1450 | 4 | * This program is free software; you can redistribute it and/or | ||
1451 | 5 | * modify it under the terms of the GNU General Public License | ||
1452 | 6 | * as published by the Free Software Foundation; either version 2 | ||
1453 | 7 | * of the License, or (at your option) any later version. | ||
1454 | 8 | * | ||
1455 | 9 | * This program is distributed in the hope that it will be useful, | ||
1456 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1457 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1458 | 12 | * GNU General Public License for more details. | ||
1459 | 13 | * | ||
1460 | 14 | * You should have received a copy of the GNU General Public License | ||
1461 | 15 | * along with this program; if not, write to the Free Software | ||
1462 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
1463 | 17 | * | ||
1464 | 18 | */ | ||
1465 | 19 | |||
1466 | 20 | #define BOOST_TEST_MODULE AI | ||
1467 | 21 | #include <boost/test/unit_test.hpp> | ||
1468 | 0 | 22 | ||
1469 | === added file 'src/ai/test/test_ai.cc' | |||
1470 | --- src/ai/test/test_ai.cc 1970-01-01 00:00:00 +0000 | |||
1471 | +++ src/ai/test/test_ai.cc 2019-07-31 19:53:54 +0000 | |||
1472 | @@ -0,0 +1,228 @@ | |||
1473 | 1 | /* | ||
1474 | 2 | * Copyright (C) 2007-2019 by the Widelands Development Team | ||
1475 | 3 | * | ||
1476 | 4 | * This program is free software; you can redistribute it and/or | ||
1477 | 5 | * modify it under the terms of the GNU General Public License | ||
1478 | 6 | * as published by the Free Software Foundation; either version 2 | ||
1479 | 7 | * of the License, or (at your option) any later version. | ||
1480 | 8 | * | ||
1481 | 9 | * This program is distributed in the hope that it will be useful, | ||
1482 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1483 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1484 | 12 | * GNU General Public License for more details. | ||
1485 | 13 | * | ||
1486 | 14 | * You should have received a copy of the GNU General Public License | ||
1487 | 15 | * along with this program; if not, write to the Free Software | ||
1488 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
1489 | 17 | * | ||
1490 | 18 | */ | ||
1491 | 19 | |||
1492 | 20 | #include <exception> | ||
1493 | 21 | |||
1494 | 22 | #include <boost/test/unit_test.hpp> | ||
1495 | 23 | |||
1496 | 24 | #ifdef _WIN32 | ||
1497 | 25 | #include "base/log.h" | ||
1498 | 26 | #endif | ||
1499 | 27 | #include "ai/ai_help_structs.h" | ||
1500 | 28 | #include "base/macros.h" | ||
1501 | 29 | |||
1502 | 30 | // Triggered by BOOST_AUTO_TEST_CASE | ||
1503 | 31 | CLANG_DIAG_OFF("-Wdisabled-macro-expansion") | ||
1504 | 32 | |||
1505 | 33 | namespace Widelands { // Needed? | ||
1506 | 34 | class World; | ||
1507 | 35 | } // namespace Widelands | ||
1508 | 36 | |||
1509 | 37 | using namespace Widelands; | ||
1510 | 38 | |||
1511 | 39 | BOOST_AUTO_TEST_SUITE(ai) | ||
1512 | 40 | |||
1513 | 41 | BOOST_AUTO_TEST_CASE(flag_distance_soft_expiry) { | ||
1514 | 42 | FlagWarehouseDistances fw; | ||
1515 | 43 | uint32_t tmp_wh; | ||
1516 | 44 | BOOST_CHECK_EQUAL(fw.get_distance(0, 0, &tmp_wh), 1000); | ||
1517 | 45 | BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); | ||
1518 | 46 | BOOST_CHECK_EQUAL(fw.get_distance(1, 2, &tmp_wh), 2); // distance now 2 | ||
1519 | 47 | BOOST_CHECK_EQUAL(tmp_wh, 3); | ||
1520 | 48 | |||
1521 | 49 | // setting longer distance below soft_expiry time | ||
1522 | 50 | BOOST_CHECK_EQUAL(fw.set_distance(1, 3, kFlagDistanceExpirationPeriod / 3, 4), false); | ||
1523 | 51 | // distance to 3 not updated | ||
1524 | 52 | BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod / 3, &tmp_wh), 2); | ||
1525 | 53 | BOOST_CHECK_EQUAL(tmp_wh, 3); | ||
1526 | 54 | |||
1527 | 55 | // now setting after soft expiry | ||
1528 | 56 | BOOST_CHECK_EQUAL( | ||
1529 | 57 | fw.set_distance(1, 1, kFlagDistanceExpirationPeriod / 3, 6), true); // distance set to 1 | ||
1530 | 58 | BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod / 3, &tmp_wh), 1); | ||
1531 | 59 | BOOST_CHECK_EQUAL(tmp_wh, 6); | ||
1532 | 60 | } | ||
1533 | 61 | BOOST_AUTO_TEST_CASE(flag_distance_below_expiry) | ||
1534 | 62 | /* Compare with void free_test_function() */ | ||
1535 | 63 | { | ||
1536 | 64 | FlagWarehouseDistances fw; | ||
1537 | 65 | uint32_t tmp_wh; | ||
1538 | 66 | BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); | ||
1539 | 67 | |||
1540 | 68 | // setting longer distance after soft but below expiry time | ||
1541 | 69 | BOOST_CHECK_EQUAL(fw.set_distance(1, 3, kFlagDistanceExpirationPeriod * 2 / 3, 5), true); | ||
1542 | 70 | BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod * 2 / 3, &tmp_wh), 3); | ||
1543 | 71 | BOOST_CHECK_EQUAL(tmp_wh, 5); | ||
1544 | 72 | } | ||
1545 | 73 | |||
1546 | 74 | BOOST_AUTO_TEST_CASE(flag_distance_after_expiry) | ||
1547 | 75 | /* Compare with void free_test_function() */ | ||
1548 | 76 | { | ||
1549 | 77 | FlagWarehouseDistances fw; | ||
1550 | 78 | uint32_t tmp_wh; | ||
1551 | 79 | BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); | ||
1552 | 80 | |||
1553 | 81 | // setting longer distance below expiry time | ||
1554 | 82 | BOOST_CHECK_EQUAL(fw.set_distance(1, 3, 2 * kFlagDistanceExpirationPeriod, 5), true); | ||
1555 | 83 | BOOST_CHECK_EQUAL(fw.get_distance(1, 3, &tmp_wh), 3); | ||
1556 | 84 | BOOST_CHECK_EQUAL(tmp_wh, 5); | ||
1557 | 85 | } | ||
1558 | 86 | |||
1559 | 87 | BOOST_AUTO_TEST_CASE(flag_distance_expiration_extension) | ||
1560 | 88 | /* setting the same distance restart the expiry_period */ | ||
1561 | 89 | { | ||
1562 | 90 | FlagWarehouseDistances fw; | ||
1563 | 91 | uint32_t tmp_wh; | ||
1564 | 92 | BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); | ||
1565 | 93 | BOOST_CHECK_EQUAL( | ||
1566 | 94 | fw.set_distance(1, 2, 0, 3), false); // cannot reset the same distance in the same time | ||
1567 | 95 | |||
1568 | 96 | // Now we are after expiration time | ||
1569 | 97 | BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod + 3, &tmp_wh), 1000); | ||
1570 | 98 | |||
1571 | 99 | // setting distance 2 time shortly one after another | ||
1572 | 100 | BOOST_CHECK_EQUAL(fw.set_distance(1, 2, kFlagDistanceExpirationPeriod + 3, 5), true); | ||
1573 | 101 | BOOST_CHECK_EQUAL(fw.set_distance(1, 2, kFlagDistanceExpirationPeriod + 10, 5), true); | ||
1574 | 102 | // current expiry_time should be 2*kFlagDistanceExpirationPeriod + 10 | ||
1575 | 103 | BOOST_CHECK_EQUAL(fw.get_distance(1, 2 * kFlagDistanceExpirationPeriod, &tmp_wh), 2); | ||
1576 | 104 | BOOST_CHECK_EQUAL(tmp_wh, 5); | ||
1577 | 105 | BOOST_CHECK_EQUAL(fw.get_distance(1, 2 * kFlagDistanceExpirationPeriod + 15, &tmp_wh), 1000); | ||
1578 | 106 | } | ||
1579 | 107 | |||
1580 | 108 | BOOST_AUTO_TEST_CASE(flag_distance_road_builtexpiration_extension) | ||
1581 | 109 | /* setting the same distance restart the expiry_period */ | ||
1582 | 110 | { | ||
1583 | 111 | FlagWarehouseDistances fw; | ||
1584 | 112 | // No road built on fresh flag | ||
1585 | 113 | BOOST_CHECK_EQUAL(fw.is_road_prohibited(1, 1), false); | ||
1586 | 114 | // get_distance(const uint32_t flag_coords, uint32_t gametime, uint32_t* nw) | ||
1587 | 115 | |||
1588 | 116 | // setting road we dont know about | ||
1589 | 117 | fw.set_road_built(1, 0); | ||
1590 | 118 | BOOST_CHECK_EQUAL(fw.is_road_prohibited(1, 1), false); | ||
1591 | 119 | |||
1592 | 120 | // let fw knows about it | ||
1593 | 121 | fw.set_distance(1, 2, 0, 3); | ||
1594 | 122 | fw.set_road_built(1, 0); | ||
1595 | 123 | BOOST_CHECK_EQUAL(fw.is_road_prohibited(1, 1), true); | ||
1596 | 124 | BOOST_CHECK_EQUAL(fw.is_road_prohibited(1, 59999), true); | ||
1597 | 125 | BOOST_CHECK_EQUAL(fw.is_road_prohibited(1, 60001), false); | ||
1598 | 126 | |||
1599 | 127 | BOOST_CHECK_EQUAL(fw.is_road_prohibited(2, 60001), false); | ||
1600 | 128 | } | ||
1601 | 129 | |||
1602 | 130 | BOOST_AUTO_TEST_CASE(flag_distance_old_removal) | ||
1603 | 131 | /* setting the same distance restart the expiry_period */ | ||
1604 | 132 | { | ||
1605 | 133 | FlagWarehouseDistances fw; | ||
1606 | 134 | fw.set_distance(1, 2, 0, 3); | ||
1607 | 135 | BOOST_CHECK_EQUAL(fw.count(), 1); | ||
1608 | 136 | BOOST_CHECK_EQUAL(fw.remove_old_flag(kOldFlagRemoveTime + kFlagDistanceExpirationPeriod), false); | ||
1609 | 137 | BOOST_CHECK_EQUAL(fw.count(), 1); | ||
1610 | 138 | BOOST_CHECK_EQUAL( | ||
1611 | 139 | fw.remove_old_flag(kOldFlagRemoveTime + kFlagDistanceExpirationPeriod + 2), true); | ||
1612 | 140 | BOOST_CHECK_EQUAL(fw.count(), 0); | ||
1613 | 141 | } | ||
1614 | 142 | |||
1615 | 143 | BOOST_AUTO_TEST_CASE(new_flag_road_not_prohibited) | ||
1616 | 144 | /* setting the same distance restart the expiry_period */ | ||
1617 | 145 | { | ||
1618 | 146 | FlagWarehouseDistances fw; | ||
1619 | 147 | // let fw knows about it | ||
1620 | 148 | BOOST_CHECK_EQUAL(fw.count(), 0); | ||
1621 | 149 | fw.set_distance(1, 2, 0, 3); | ||
1622 | 150 | BOOST_CHECK_EQUAL(fw.count(), 1); | ||
1623 | 151 | BOOST_CHECK_EQUAL(fw.is_road_prohibited(1, 1), false); | ||
1624 | 152 | } | ||
1625 | 153 | |||
1626 | 154 | BOOST_AUTO_TEST_CASE(flag_candidate_init) | ||
1627 | 155 | /* setting the same distance restart the expiry_period */ | ||
1628 | 156 | { | ||
1629 | 157 | FlagCandidates fc = FlagCandidates(10); | ||
1630 | 158 | BOOST_CHECK_EQUAL(fc.count(), 0); | ||
1631 | 159 | } | ||
1632 | 160 | |||
1633 | 161 | BOOST_AUTO_TEST_CASE(flag_candidate_winner_score) { | ||
1634 | 162 | const uint16_t kCurFlDistToWh = 3; | ||
1635 | 163 | const uint16_t kStartFlagToWh = 10; | ||
1636 | 164 | |||
1637 | 165 | const uint16_t kPosRoadDist = 5; | ||
1638 | 166 | const uint16_t kCurRoadDistFlToFl = 17; | ||
1639 | 167 | |||
1640 | 168 | const uint32_t kTestedCoords = 11; | ||
1641 | 169 | |||
1642 | 170 | FlagCandidates fc = FlagCandidates(kStartFlagToWh); | ||
1643 | 171 | |||
1644 | 172 | // coord, different economy, distance to wh | ||
1645 | 173 | fc.add_flag(kTestedCoords, false, kCurFlDistToWh, 1); | ||
1646 | 174 | // setting coords, dist | ||
1647 | 175 | BOOST_CHECK_EQUAL(fc.set_cur_road_distance(kTestedCoords, kCurRoadDistFlToFl), true); | ||
1648 | 176 | BOOST_CHECK_EQUAL( | ||
1649 | 177 | fc.set_cur_road_distance(1, 5), false); // we cannot set distance to unknown flag | ||
1650 | 178 | BOOST_CHECK(!fc.get_winner()); // road not possible | ||
1651 | 179 | // set length of possible road | ||
1652 | 180 | BOOST_CHECK_EQUAL(fc.set_road_possible(kTestedCoords, kPosRoadDist), true); | ||
1653 | 181 | BOOST_VERIFY(fc.get_winner()); | ||
1654 | 182 | BOOST_CHECK_EQUAL(fc.get_winner()->start_flag_dist_to_wh, kStartFlagToWh); | ||
1655 | 183 | BOOST_CHECK_EQUAL(fc.get_winner()->cand_flag_distance_to_wh, kCurFlDistToWh); | ||
1656 | 184 | BOOST_CHECK_EQUAL(fc.get_winner()->flag_to_flag_road_distance, kCurRoadDistFlToFl); | ||
1657 | 185 | BOOST_CHECK_EQUAL(fc.get_winner()->possible_road_distance, kPosRoadDist); | ||
1658 | 186 | BOOST_CHECK_EQUAL(fc.get_winner()->coords_hash, kTestedCoords); | ||
1659 | 187 | BOOST_CHECK_EQUAL(fc.get_winner()->different_economy, false); | ||
1660 | 188 | |||
1661 | 189 | BOOST_CHECK_EQUAL(fc.get_winner()->score(), | ||
1662 | 190 | +(kStartFlagToWh - kCurFlDistToWh) + (kCurRoadDistFlToFl - 2 * kPosRoadDist)); | ||
1663 | 191 | } | ||
1664 | 192 | BOOST_AUTO_TEST_CASE(flag_candidates_sorting) { | ||
1665 | 193 | FlagCandidates fc = FlagCandidates(10); | ||
1666 | 194 | |||
1667 | 195 | fc.add_flag(0, false, 10, 1); | ||
1668 | 196 | fc.add_flag(1, false, 10, 1); | ||
1669 | 197 | fc.add_flag(2, false, 10, 1); | ||
1670 | 198 | BOOST_CHECK_EQUAL(fc.set_cur_road_distance(0, 5), true); | ||
1671 | 199 | BOOST_CHECK_EQUAL(fc.set_cur_road_distance(1, 5), true); | ||
1672 | 200 | BOOST_CHECK_EQUAL(fc.set_cur_road_distance(2, 5), true); | ||
1673 | 201 | BOOST_CHECK_EQUAL(fc.set_road_possible(0, 4), true); | ||
1674 | 202 | BOOST_CHECK_EQUAL(fc.set_road_possible(1, 2), true); | ||
1675 | 203 | BOOST_CHECK_EQUAL(fc.set_road_possible(2, 3), true); | ||
1676 | 204 | BOOST_CHECK_EQUAL(fc.get_winner()->coords_hash, 1); // sorted done automatically | ||
1677 | 205 | BOOST_CHECK_EQUAL(fc.count(), 3); | ||
1678 | 206 | } | ||
1679 | 207 | |||
1680 | 208 | BOOST_AUTO_TEST_CASE(flag_sort_by_air_distance) { | ||
1681 | 209 | FlagCandidates fc = FlagCandidates(10); | ||
1682 | 210 | |||
1683 | 211 | fc.add_flag(0, false, 10, 4); | ||
1684 | 212 | fc.add_flag(1, false, 10, 1); | ||
1685 | 213 | fc.add_flag(2, false, 10, 2); | ||
1686 | 214 | fc.sort_by_air_distance(); | ||
1687 | 215 | BOOST_CHECK_EQUAL(fc.flags()[0].air_distance, 1); | ||
1688 | 216 | } | ||
1689 | 217 | |||
1690 | 218 | BOOST_AUTO_TEST_CASE(flag_has_candidate) { | ||
1691 | 219 | FlagCandidates fc = FlagCandidates(10); | ||
1692 | 220 | |||
1693 | 221 | fc.add_flag(0, false, 10, 4); | ||
1694 | 222 | fc.add_flag(1, false, 10, 1); | ||
1695 | 223 | fc.add_flag(2, false, 10, 2); | ||
1696 | 224 | BOOST_CHECK_EQUAL(fc.has_candidate(1), true); | ||
1697 | 225 | BOOST_CHECK_EQUAL(fc.has_candidate(3), false); | ||
1698 | 226 | } | ||
1699 | 227 | |||
1700 | 228 | BOOST_AUTO_TEST_SUITE_END() |
I dont see any build here, why?