1 /*
2 * Copyright (C) 2004-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "ai/defaultai.h"
21
22 #include <algorithm>
23 #include <memory>
24
25 #include "ai/ai_hints.h"
26 #include "base/macros.h"
27 #include "base/time_string.h"
28 #include "base/wexception.h"
29 #include "economy/flag.h"
30 #include "economy/portdock.h"
31 #include "economy/road.h"
32 #include "economy/wares_queue.h"
33 #include "logic/map.h"
34 #include "logic/map_objects/findbob.h"
35 #include "logic/map_objects/findimmovable.h"
36 #include "logic/map_objects/findnode.h"
37 #include "logic/map_objects/tribes/constructionsite.h"
38 #include "logic/map_objects/tribes/militarysite.h"
39 #include "logic/map_objects/tribes/productionsite.h"
40 #include "logic/map_objects/tribes/ship.h"
41 #include "logic/map_objects/tribes/soldier.h"
42 #include "logic/map_objects/tribes/trainingsite.h"
43 #include "logic/map_objects/tribes/tribe_descr.h"
44 #include "logic/map_objects/tribes/tribes.h"
45 #include "logic/map_objects/tribes/warehouse.h"
46 #include "logic/map_objects/world/world.h"
47 #include "logic/player.h"
48 #include "logic/playercommand.h"
49
50 // following is in milliseconds (widelands counts time in ms)
51 constexpr int kFieldInfoExpiration = 12 * 1000;
52 constexpr int kMineFieldInfoExpiration = 20 * 1000;
53 constexpr int kNewMineConstInterval = 19000;
54 constexpr int kBusyMineUpdateInterval = 2000;
55 // building of the same building can be started after 25s at earliest
56 constexpr int kBuildingMinInterval = 25 * 1000;
57 constexpr int kMinBFCheckInterval = 5 * 1000;
58 constexpr int kMinMFCheckInterval = 19 * 1000;
59 constexpr int kMarineDecisionInterval = 20 * 1000;
60 constexpr int kRemainingBasicBuildingsResetTime = 1 * 60 * 1000;
61
62 // following two are used for roads management, for creating shortcuts and dismantling dispensable
63 // roads
64 constexpr int32_t kSpotsEnough = 25;
65
66 constexpr uint16_t kTargetQuantCap = 30;
67
68 // this is intended for map developers & testers, should be off by default
69 constexpr bool kPrintStats = false;
70
71 // for scheduler
72 constexpr int kMaxJobs = 4;
73
74 // Count of mine types / ground resources
75 constexpr int kMineTypes = 4;
76
77 using namespace Widelands;
78
79 DefaultAI::NormalImpl DefaultAI::normal_impl;
80 DefaultAI::WeakImpl DefaultAI::weak_impl;
81 DefaultAI::VeryWeakImpl DefaultAI::very_weak_impl;
82
83 uint32_t DefaultAI::last_seafaring_check_ = 0;
84 bool DefaultAI::map_allows_seafaring_ = false;
85
86 /// Constructor of DefaultAI
DefaultAI(Game & ggame,PlayerNumber const pid,Widelands::AiType const t)87 DefaultAI::DefaultAI(Game& ggame, PlayerNumber const pid, Widelands::AiType const t)
88 : ComputerPlayer(ggame, pid),
89 type_(t),
90 player_(nullptr),
91 tribe_(nullptr),
92 attackers_count_(0),
93 next_ai_think_(0),
94 scheduler_delay_counter_(0),
95 wood_policy_(WoodPolicy::kAllowRangers),
96 numof_psites_in_constr(0),
97 num_ports(0),
98 numof_warehouses_(0),
99 numof_warehouses_in_const_(0),
100 military_last_dismantle_(0),
101 military_last_build_(0),
102 time_of_last_construction_(0),
103 next_mine_construction_due_(0),
104 fishers_count_(0),
105 bakeries_count_(),
106 first_iron_mine_built(50 * 60 * 60 * 1000),
107 ts_finished_count_(0),
108 ts_in_const_count_(0),
109 ts_without_trainers_(0),
110 enemysites_check_delay_(30),
111 spots_(0),
112 resource_necessity_water_needed_(false),
113 highest_nonmil_prio_(0),
114 expedition_ship_(kNoShip) {
115
116 // Subscribe to NoteFieldPossession.
117 field_possession_subscriber_ =
118 Notifications::subscribe<NoteFieldPossession>([this](const NoteFieldPossession& note) {
119 if (note.player != player_) {
120 return;
121 }
122 if (note.ownership == NoteFieldPossession::Ownership::GAINED) {
123 unusable_fields.push_back(note.fc);
124 }
125 });
126
127 // Subscribe to NoteImmovables.
128 immovable_subscriber_ =
129 Notifications::subscribe<NoteImmovable>([this](const NoteImmovable& note) {
130 if (player_ == nullptr) {
131 return;
132 }
133 if (note.pi->owner().player_number() != player_->player_number()) {
134 return;
135 }
136 if (note.ownership == NoteImmovable::Ownership::GAINED) {
137 gain_immovable(*note.pi);
138 } else {
139 lose_immovable(*note.pi);
140 }
141 });
142
143 // Subscribe to ProductionSiteOutOfResources.
144 outofresource_subscriber_ = Notifications::subscribe<NoteProductionSiteOutOfResources>(
145 [this](const NoteProductionSiteOutOfResources& note) {
146 if (note.ps->owner().player_number() != player_->player_number()) {
147 return;
148 }
149
150 out_of_resources_site(*note.ps);
151 });
152
153 // Subscribe to TrainingSiteSoldierTrained.
154 soldiertrained_subscriber_ = Notifications::subscribe<NoteTrainingSiteSoldierTrained>(
155 [this](const NoteTrainingSiteSoldierTrained& note) {
156 if (note.ts->owner().player_number() != player_->player_number()) {
157 return;
158 }
159
160 soldier_trained(*note.ts);
161 });
162
163 // Subscribe to ShipNotes.
164 shipnotes_subscriber_ = Notifications::subscribe<NoteShip>([this](const NoteShip& note) {
165 // in a short time between start and late_initialization the player
166 // can get notes that can not be processed.
167 // It seems that this causes no problem, at least no substantial
168 if (player_ == nullptr) {
169 return;
170 }
171 if (note.ship->get_owner()->player_number() != player_->player_number()) {
172 return;
173 }
174
175 switch (note.action) {
176
177 case NoteShip::Action::kGained:
178 gain_ship(*note.ship, NewShip::kBuilt);
179 break;
180
181 case NoteShip::Action::kLost:
182 for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
183 if (i->ship == note.ship) {
184 allships.erase(i);
185 break;
186 }
187 }
188 break;
189
190 case NoteShip::Action::kWaitingForCommand:
191 for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
192 if (i->ship == note.ship) {
193 i->waiting_for_command_ = true;
194 break;
195 }
196 }
197 break;
198 default:
199 // Do nothing
200 break;
201 }
202 });
203 }
204
~DefaultAI()205 DefaultAI::~DefaultAI() {
206 while (!buildable_fields.empty()) {
207 delete buildable_fields.back();
208 buildable_fields.pop_back();
209 }
210
211 while (!mineable_fields.empty()) {
212 delete mineable_fields.back();
213 mineable_fields.pop_back();
214 }
215
216 while (!economies.empty()) {
217 delete economies.back();
218 economies.pop_back();
219 }
220 }
221
222 /**
223 * Main loop of computer player_ "defaultAI"
224 *
225 * General behaviour is defined here.
226 */
think()227 void DefaultAI::think() {
228
229 if (tribe_ == nullptr) {
230 late_initialization();
231 }
232
233 const uint32_t gametime = static_cast<uint32_t>(game().get_gametime());
234
235 if (next_ai_think_ > gametime) {
236 return;
237 }
238
239 // AI now thinks twice in a seccond, if the game engine allows this
240 // if too busy, the period can be many seconds.
241 next_ai_think_ = gametime + 500;
242 SchedulerTaskId due_task = SchedulerTaskId::kUnset;
243
244 sort_task_pool();
245
246 const int32_t delay_time = gametime - taskPool.front().due_time;
247
248 // Here we decide how many jobs will be run now (none - 5)
249 // in case no job is due now, it can be zero
250 uint32_t jobs_to_run_count = (delay_time < 0) ? 0 : 1;
251
252 // Here we collect data for "too late ..." message
253 if (delay_time > 5000) {
254 ++scheduler_delay_counter_;
255 } else {
256 scheduler_delay_counter_ = 0;
257 }
258
259 if (jobs_to_run_count == 0) {
260 // well we have nothing to do now
261 return;
262 }
263
264 // And printing it now and resetting counter
265 if (scheduler_delay_counter_ > 10) {
266 log(" %d: AI: game speed too high, jobs are too late (now %2d seconds)\n", player_number(),
267 static_cast<int32_t>(delay_time / 1000));
268 scheduler_delay_counter_ = 0;
269 }
270
271 // 400 provides that second job is run if delay time is longer then 1.6 sec
272 if (delay_time / 400 > 1) {
273 jobs_to_run_count = sqrt(static_cast<uint32_t>(delay_time / 500));
274 }
275
276 jobs_to_run_count = (jobs_to_run_count > kMaxJobs) ? kMaxJobs : jobs_to_run_count;
277 assert(jobs_to_run_count > 0 && jobs_to_run_count <= kMaxJobs);
278 assert(jobs_to_run_count < taskPool.size());
279
280 // Pool of tasks to be executed this run. In ideal situation it will consist of one task only.
281 std::vector<SchedulerTask> current_task_queue;
282 assert(current_task_queue.empty());
283 // Here we push SchedulerTask members into the temporary queue, providing that a task is due now
284 // and
285 // the limit (jobs_to_run_count) is not exceeded
286 for (uint8_t i = 0; i < jobs_to_run_count; ++i) {
287 if (taskPool[i].due_time <= gametime) {
288 current_task_queue.push_back(taskPool[i]);
289 sort_task_pool();
290 } else {
291 break;
292 }
293 }
294
295 assert(!current_task_queue.empty() && current_task_queue.size() <= jobs_to_run_count);
296
297 // Ordering temporary queue so that higher priority (lower number) is on the beginning
298 std::sort(current_task_queue.begin(), current_task_queue.end());
299
300 // Performing tasks from temporary queue one by one
301 for (uint8_t i = 0; i < current_task_queue.size(); ++i) {
302
303 due_task = current_task_queue[i].id;
304
305 ++sched_stat_[static_cast<uint32_t>(due_task)];
306
307 // Now AI runs a job selected above to be performed in this turn
308 // (only one but some of them needs to run check_economies() to
309 // guarantee consistency)
310 // job names are selfexplanatory
311 switch (due_task) {
312 case SchedulerTaskId::kBbuildableFieldsCheck:
313 update_all_buildable_fields(gametime);
314 set_taskpool_task_time(
315 gametime + kMinBFCheckInterval, SchedulerTaskId::kBbuildableFieldsCheck);
316 break;
317 case SchedulerTaskId::kMineableFieldsCheck:
318 update_all_mineable_fields(gametime);
319 set_taskpool_task_time(
320 gametime + kMinMFCheckInterval, SchedulerTaskId::kMineableFieldsCheck);
321 break;
322 case SchedulerTaskId::kRoadCheck:
323 if (check_economies()) { // is a must
324 return;
325 }
326 set_taskpool_task_time(gametime + 1000, SchedulerTaskId::kRoadCheck);
327 // testing 5 roads
328 {
329 const int32_t roads_to_check = roads.size() / 30 + 1;
330 for (int j = 0; j < roads_to_check; ++j) {
331 // improve_roads function will test one road and rotate roads vector
332 if (improve_roads(gametime)) {
333 // if significant change takes place do not go on
334 break;
335 }
336 }
337 }
338 break;
339 case SchedulerTaskId::kUnbuildableFCheck:
340 set_taskpool_task_time(gametime + 4000, SchedulerTaskId::kUnbuildableFCheck);
341 update_all_not_buildable_fields();
342 break;
343 case SchedulerTaskId::kCheckEconomies:
344 check_economies();
345 set_taskpool_task_time(gametime + 8000, SchedulerTaskId::kCheckEconomies);
346 break;
347 case SchedulerTaskId::kProductionsitesStats:
348 update_productionsite_stats();
349 // Updating the stats every 10 seconds should be enough
350 set_taskpool_task_time(gametime + 10000, SchedulerTaskId::kProductionsitesStats);
351 break;
352 case SchedulerTaskId::kConstructBuilding:
353 if (check_economies()) { // economies must be consistent
354 return;
355 }
356 if (gametime < 15000) { // More frequent at the beginning of game
357 set_taskpool_task_time(gametime + 2000, SchedulerTaskId::kConstructBuilding);
358 } else {
359 set_taskpool_task_time(gametime + 6000, SchedulerTaskId::kConstructBuilding);
360 }
361 if (construct_building(gametime)) {
362 time_of_last_construction_ = gametime;
363 }
364 break;
365 case SchedulerTaskId::kCheckProductionsites:
366 if (check_economies()) { // economies must be consistent
367 return;
368 }
369 {
370 set_taskpool_task_time(gametime + 15000, SchedulerTaskId::kCheckProductionsites);
371 // testing 5 productionsites (if there are 5 of them)
372 int32_t ps_to_check = (productionsites.size() < 5) ? productionsites.size() : 5;
373 for (int j = 0; j < ps_to_check; ++j) {
374 // one productionsite per one check_productionsites() call
375 if (check_productionsites(gametime)) {
376 // if significant change takes place do not go on
377 break;
378 }
379 }
380 }
381 break;
382 case SchedulerTaskId::kCheckShips:
383 // if function returns false, we can postpone next call
384 {
385 const uint8_t wait_multiplier = (check_ships(gametime)) ? 1 : 5;
386 set_taskpool_task_time(
387 gametime + wait_multiplier * kShipCheckInterval, SchedulerTaskId::kCheckShips);
388 }
389 break;
390 case SchedulerTaskId::KMarineDecisions:
391 // if function returns false, we can postpone for next call
392 {
393 const uint8_t wait_multiplier = (marine_main_decisions(gametime)) ? 1 : 5;
394 set_taskpool_task_time(gametime + wait_multiplier * kMarineDecisionInterval,
395 SchedulerTaskId::KMarineDecisions);
396 }
397 break;
398 case SchedulerTaskId::kCheckMines:
399 if (check_economies()) { // economies must be consistent
400 return;
401 }
402 set_taskpool_task_time(gametime + 15000, SchedulerTaskId::kCheckMines);
403 // checking 3 mines if possible
404 {
405 int32_t mines_to_check = (mines_.size() < 5) ? mines_.size() : 5;
406 for (int j = 0; j < mines_to_check; ++j) {
407 // every run of check_mines_() checks one mine
408 if (check_mines_(gametime)) {
409 // if significant change takes place do not go on
410 break;
411 }
412 }
413 }
414 break;
415 case SchedulerTaskId::kCheckMilitarysites:
416 // just to be sure the value is reset
417 if (check_militarysites(gametime)) {
418 set_taskpool_task_time(gametime + 15 * 1000, SchedulerTaskId::kCheckMilitarysites);
419 } else {
420 set_taskpool_task_time(gametime + 4 * 1000, SchedulerTaskId::kCheckMilitarysites);
421 }
422 break;
423 case SchedulerTaskId::kCheckTrainingsites:
424 set_taskpool_task_time(
425 gametime + kTrainingSitesCheckInterval, SchedulerTaskId::kCheckTrainingsites);
426 check_trainingsites(gametime);
427 break;
428 case SchedulerTaskId::kCountMilitaryVacant:
429 count_military_vacant_positions();
430 set_taskpool_task_time(gametime + 25 * 1000, SchedulerTaskId::kCountMilitaryVacant);
431 break;
432 case SchedulerTaskId::kWareReview:
433 if (check_economies()) { // economies must be consistent
434 return;
435 }
436 set_taskpool_task_time(gametime + 15 * 60 * 1000, SchedulerTaskId::kWareReview);
437 review_wares_targets(gametime);
438 break;
439 case SchedulerTaskId::kPrintStats:
440 if (check_economies()) { // economies must be consistent
441 return;
442 }
443 set_taskpool_task_time(gametime + 10 * 60 * 1000, SchedulerTaskId::kPrintStats);
444 print_stats(gametime);
445 break;
446 case SchedulerTaskId::kCheckEnemySites:
447 check_enemy_sites(gametime);
448 set_taskpool_task_time(gametime + 19 * 1000, SchedulerTaskId::kCheckEnemySites);
449 break;
450 case SchedulerTaskId::kManagementUpdate:
451 // This task is used for training the AI, so it should be usually disabled
452 { // statistics for spotted warehouses
453 uint16_t conquered_wh = 0;
454 for (auto coords : enemy_warehouses) {
455 if (get_land_owner(game().map(), coords) == player_number()) {
456 ++conquered_wh;
457 }
458 }
459 if (!basic_economy_established) {
460 assert(!persistent_data->remaining_basic_buildings.empty());
461 log("%2d: Basic economy not achieved, %" PRIuS " building(s) missing, f.e.: %s\n",
462 player_number(), persistent_data->remaining_basic_buildings.size(),
463 get_building_observer(persistent_data->remaining_basic_buildings.begin()->first)
464 .name);
465 }
466 if (!enemy_warehouses.empty()) {
467 log(
468 "Conquered warehouses: %d / %" PRIuS "\n", conquered_wh, enemy_warehouses.size());
469 }
470 management_data.review(
471 gametime, player_number(), player_statistics.get_player_land(player_number()),
472 player_statistics.get_enemies_max_land(),
473 player_statistics.get_old60_player_land(player_number()), attackers_count_,
474 soldier_trained_log.count(gametime),
475 player_statistics.get_player_power(player_number()),
476 count_productionsites_without_buildings(), first_iron_mine_built);
477 set_taskpool_task_time(
478 gametime + kManagementUpdateInterval, SchedulerTaskId::kManagementUpdate);
479 }
480 break;
481 case SchedulerTaskId::kUpdateStats:
482 update_player_stat(gametime);
483 set_taskpool_task_time(gametime + kStatUpdateInterval, SchedulerTaskId::kUpdateStats);
484 break;
485 case SchedulerTaskId::kWarehouseFlagDist:
486 check_flag_distances(gametime);
487 set_taskpool_task_time(
488 gametime + kFlagWarehouseUpdInterval, SchedulerTaskId::kWarehouseFlagDist);
489 break;
490 case SchedulerTaskId::kUnset:
491 NEVER_HERE();
492 }
493 }
494 }
495
496 /**
497 * Cares for all variables not initialised during construction
498 *
499 * When DefaultAI is constructed, some information is not yet available (e.g.
500 * world), so this is done after complete loading of the map.
501 */
late_initialization()502 void DefaultAI::late_initialization() {
503 player_ = game().get_player(player_number());
504 tribe_ = &player_->tribe();
505 if (game().is_ai_training_mode()) {
506 ai_training_mode_ = true;
507 management_data.set_ai_training_mode();
508 }
509 const uint32_t gametime = game().get_gametime();
510
511 log("ComputerPlayer(%d): initializing as type %u%s\n", player_number(),
512 static_cast<unsigned int>(type_), (ai_training_mode_) ? ", in ai training mode" : "");
513 if (player_->team_number() > 0) {
514 log(" ... member of team %d\n", player_->team_number());
515 }
516
517 wares.resize(game().tribes().nrwares());
518 for (DescriptionIndex i = 0; i < static_cast<DescriptionIndex>(game().tribes().nrwares()); ++i) {
519 wares.at(i).preciousness =
520 game().tribes().get_ware_descr(i)->ai_hints().preciousness(tribe_->name());
521 }
522
523 const DescriptionIndex& nr_buildings = game().tribes().nrbuildings();
524
525 // The data struct below is owned by Player object, the purpose is to have them saved therein
526 persistent_data = player_->get_mutable_ai_persistent_state();
527 management_data.persistent_data = player_->get_mutable_ai_persistent_state();
528 const bool create_basic_buildings_list =
529 !persistent_data->initialized || (gametime < kRemainingBasicBuildingsResetTime);
530
531 if (!persistent_data->initialized) {
532 // As all data are initialized without given values, they must be populated with reasonable
533 // values first
534 persistent_data->initialize();
535
536 // AI's DNA population
537 management_data.new_dna_for_persistent(player_number(), type_);
538 management_data.copy_persistent_to_local();
539 management_data.mutate(player_number());
540 if (ai_training_mode_) {
541 management_data.dump_data(player_number());
542 }
543
544 management_data.test_consistency(true);
545 assert(management_data.get_military_number_at(42) ==
546 management_data.get_military_number_at(kMutationRatePosition));
547
548 } else {
549 // Doing some consistency checks
550 check_range<uint32_t>(
551 persistent_data->expedition_start_time, gametime, "expedition_start_time");
552 check_range<uint16_t>(persistent_data->ships_utilization, 0, 10000, "ships_utilization_");
553
554 // for backward consistency
555 if (persistent_data->ai_personality_mil_upper_limit <
556 persistent_data->target_military_score) {
557 persistent_data->ai_personality_mil_upper_limit = persistent_data->target_military_score;
558 }
559 if (persistent_data->least_military_score > persistent_data->target_military_score) {
560 persistent_data->least_military_score = persistent_data->target_military_score;
561 }
562
563 if (ai_training_mode_) {
564 log("%2d: reinitializing dna (kAITrainingMode set true)", player_number());
565 management_data.new_dna_for_persistent(player_number(), type_);
566 management_data.copy_persistent_to_local();
567 management_data.mutate(player_number());
568 management_data.dump_data(player_number());
569
570 } else {
571 management_data.copy_persistent_to_local();
572 }
573
574 management_data.test_consistency(true);
575
576 log(" %2d: %" PRIuS " basic buildings in savegame file. %s\n", player_number(),
577 persistent_data->remaining_basic_buildings.size(),
578 (create_basic_buildings_list) ?
579 "New list will be recreated though (kAITrainingMode is true)" :
580 "");
581 }
582
583 // Even if we have basic buildings from savefile, we ignore them and recreate them based
584 // on lua conf files
585 if (create_basic_buildings_list) {
586 persistent_data->remaining_basic_buildings.clear();
587 }
588
589 for (DescriptionIndex building_index = 0; building_index < nr_buildings; ++building_index) {
590 const BuildingDescr& bld = *tribe_->get_building_descr(building_index);
591 if (!tribe_->has_building(building_index) && bld.type() != MapObjectType::MILITARYSITE) {
592 continue;
593 }
594
595 const std::string& building_name = bld.name();
596 const BuildingHints& bh = bld.hints();
597 buildings_.resize(buildings_.size() + 1);
598 BuildingObserver& bo = buildings_.back();
599 bo.name = building_name.c_str();
600 bo.id = building_index;
601 bo.desc = &bld;
602 bo.type = BuildingObserver::Type::kBoring;
603 bo.cnt_built = 0;
604 bo.cnt_under_construction = 0;
605 bo.cnt_target = 1; // default for everything
606 bo.cnt_limit_by_aimode = std::numeric_limits<int32_t>::max();
607 bo.cnt_upgrade_pending = 0;
608 bo.stocklevel_count = 0;
609 bo.stocklevel_time = 0;
610 bo.last_dismantle_time = 0;
611 // this is set to negative number, otherwise the AI would wait 25 sec
612 // after game start not building anything
613 bo.construction_decision_time = -60 * 60 * 1000;
614 bo.last_building_built = kNever;
615 bo.build_material_shortage = false;
616 bo.current_stats = 0;
617 bo.unoccupied_count = 0;
618 bo.unconnected_count = 0;
619 bo.new_building_overdue = 0;
620 bo.primary_priority = 0;
621 if (bld.is_buildable()) {
622 bo.set_is(BuildingAttribute::kBuildable);
623 }
624 if (bld.needs_seafaring()) {
625 bo.set_is(BuildingAttribute::kNeedsSeafaring);
626 }
627 if (create_basic_buildings_list &&
628 bh.basic_amount() > 0) { // This is the very begining of the game
629 assert(persistent_data->remaining_basic_buildings.count(bo.id) == 0);
630 persistent_data->remaining_basic_buildings.emplace(
631 std::make_pair(bo.id, bh.basic_amount()));
632 }
633 bo.basic_amount = bh.basic_amount();
634 if (bh.get_needs_water()) {
635 bo.set_is(BuildingAttribute::kNeedsCoast);
636 }
637 if (bh.is_space_consumer()) {
638 bo.set_is(BuildingAttribute::kSpaceConsumer);
639 }
640 bo.expansion_type = bh.is_expansion_type();
641 bo.fighting_type = bh.is_fighting_type();
642 bo.mountain_conqueror = bh.is_mountain_conqueror();
643 bo.requires_supporters = bh.requires_supporters();
644 bo.set_collected_map_resource(*tribe_, bh.collects_ware_from_map());
645 if (bo.requires_supporters) {
646 log(" %d: %s strictly requires supporters\n", player_number(), bo.name);
647 }
648 bo.prohibited_till = bh.get_prohibited_till() * 1000; // value in conf is in seconds
649 bo.forced_after = bh.get_forced_after() * 1000; // value in conf is in seconds
650 if (bld.get_isport()) {
651 bo.set_is(BuildingAttribute::kPort);
652 }
653 bo.max_trainingsites_proportion = 100;
654 bo.initial_preciousness = 0;
655 bo.max_preciousness = 0;
656 bo.max_needed_preciousness = 0;
657
658 for (auto ph : bh.supported_production()) {
659 bo.production_hints.insert(tribe_->safe_ware_index(ph));
660 }
661 // I just presume cut wood is named "log" in the game
662 if (bo.production_hints.count(tribe_->safe_ware_index("log"))) {
663 bo.set_is(BuildingAttribute::kRanger);
664 }
665 // Is total count of this building limited by AI mode?
666 if (bh.get_ai_limit(type_) >= 0) {
667 bo.cnt_limit_by_aimode = bh.get_ai_limit(type_);
668 }
669
670 // Read all interesting data from ware producing buildings
671 if (bld.type() == MapObjectType::PRODUCTIONSITE) {
672 const ProductionSiteDescr& prod = dynamic_cast<const ProductionSiteDescr&>(bld);
673 bo.type = bld.get_ismine() ? BuildingObserver::Type::kMine :
674 BuildingObserver::Type::kProductionsite;
675 for (const auto& temp_input : prod.input_wares()) {
676 bo.inputs.push_back(temp_input.first);
677 }
678 for (const DescriptionIndex& temp_output : prod.output_ware_types()) {
679 bo.ware_outputs.push_back(temp_output);
680 if (tribe_->is_construction_material(temp_output) && !bo.inputs.empty()) {
681 wares.at(temp_output).refined_build_material = true;
682 }
683 }
684
685 // Read information about worker outputs
686 if (prod.output_worker_types().size() > 0) {
687 for (const DescriptionIndex& temp_output : prod.output_worker_types()) {
688 if (temp_output == tribe_->soldier()) {
689 bo.set_is(BuildingAttribute::kBarracks);
690 }
691 const WorkerHints* worker_hints = tribe_->get_worker_descr(temp_output)->ai_hints();
692 if (worker_hints != nullptr) {
693 const int worker_preciousness = worker_hints->preciousness(tribe_->name());
694 if (worker_preciousness != Widelands::kInvalidWare) {
695 bo.initial_preciousness += worker_preciousness;
696 }
697 }
698 }
699 if (!bo.is(BuildingAttribute::kBarracks) && bo.ware_outputs.empty()) {
700 bo.set_is(BuildingAttribute::kRecruitment);
701 }
702 }
703
704 for (const auto& temp_position : prod.working_positions()) {
705 for (uint8_t i = 0; i < temp_position.second; i++) {
706 bo.positions.push_back(temp_position.first);
707 }
708 }
709
710 // If this is a producer, does it act also as supporter?
711 if (!bo.ware_outputs.empty() && !bo.production_hints.empty()) {
712 bo.set_is(BuildingAttribute::kSupportingProducer);
713 }
714
715 iron_resource_id = game().world().resource_index("iron");
716 if (iron_resource_id == INVALID_INDEX) {
717 throw wexception("The AI needs the world to define the resource 'iron'");
718 }
719
720 if (bo.type == BuildingObserver::Type::kMine) {
721 // get the resource needed by the mine
722 if (bh.get_mines()) {
723 bo.mines = game().world().resource_index(bh.get_mines());
724 }
725
726 bo.mines_percent = bh.get_mines_percent();
727
728 // populating mines_per_type map
729 if (mines_per_type.count(bo.mines) == 0) {
730 mines_per_type[bo.mines] = MineTypesObserver();
731 }
732 // Identify iron mines based on mines value
733 if (bo.mines == iron_resource_id) {
734 mines_per_type[bo.mines].is_critical = true;
735 mine_fields_stat.add_critical_ore(bo.mines);
736 }
737 }
738
739 if (bh.is_shipyard()) {
740 bo.set_is(BuildingAttribute::kShipyard);
741 }
742 if (bh.supports_seafaring()) {
743 bo.set_is(BuildingAttribute::kSupportsSeafaring);
744 }
745
746 // now we find out if the upgrade of the building is a full substitution
747 // (produces all wares as current one)
748 const DescriptionIndex enhancement = bld.enhancement();
749 if (enhancement != INVALID_INDEX && bo.type == BuildingObserver::Type::kProductionsite) {
750 std::unordered_set<DescriptionIndex> enh_outputs;
751 const ProductionSiteDescr& enh_prod =
752 dynamic_cast<const ProductionSiteDescr&>(*tribe_->get_building_descr(enhancement));
753
754 // collecting wares that are produced in enhanced building
755 for (const DescriptionIndex& ware : enh_prod.output_ware_types()) {
756 enh_outputs.insert(ware);
757 }
758 // now testing outputs of current building
759 // and comparing
760 bo.set_is(BuildingAttribute::kUpgradeSubstitutes);
761 for (DescriptionIndex ware : bo.ware_outputs) {
762 if (enh_outputs.count(ware) == 0) {
763 bo.unset_is(BuildingAttribute::kUpgradeSubstitutes);
764 break;
765 }
766 }
767
768 std::unordered_set<DescriptionIndex> cur_outputs;
769 // collecting wares that are produced in enhanced building
770 for (const DescriptionIndex& ware : bo.ware_outputs) {
771 cur_outputs.insert(ware);
772 }
773 // Does upgraded building produce any different outputs?
774 for (DescriptionIndex ware : enh_outputs) {
775 if (cur_outputs.count(ware) == 0) {
776 bo.set_is(BuildingAttribute::kUpgradeExtends);
777 break;
778 }
779 }
780 }
781
782 // now we identify producers of critical build materials
783 for (DescriptionIndex ware : bo.ware_outputs) {
784 // building material except for trivial material
785 if (wares.at(ware).refined_build_material) {
786 bo.set_is(BuildingAttribute::kBuildingMatProducer);
787 if (bo.type == BuildingObserver::Type::kMine) {
788 mines_per_type[bo.mines].is_critical = true;
789 mine_fields_stat.add_critical_ore(bo.mines);
790 }
791 }
792 }
793
794 for (const auto& temp_buildcosts : prod.buildcost()) {
795 // building material except for trivial material
796 if (wares.at(temp_buildcosts.first).refined_build_material) {
797 bo.critical_building_material.push_back(temp_buildcosts.first);
798 }
799 }
800
801 // some important buildings are identified first the woodcutter/lumberjack
802 if (bh.collects_ware_from_map() == "log") {
803 bo.set_is(BuildingAttribute::kLumberjack);
804 }
805 // quarries
806 if (bh.collects_ware_from_map() == "granite") {
807 bo.set_is(BuildingAttribute::kNeedsRocks);
808 }
809 // wells
810 if (bh.collects_ware_from_map() == "water") {
811 bo.set_is(BuildingAttribute::kWell);
812 }
813 // here we identify hunters
814 if (bh.collects_ware_from_map() == "meat") {
815 bo.set_is(BuildingAttribute::kHunter);
816 }
817 // and fishers
818 if (bh.collects_ware_from_map() == "fish" && bo.inputs.empty()) {
819 bo.set_is(BuildingAttribute::kFisher);
820 }
821 // and collectors
822 if (bh.collects_ware_from_map() == "fruit") {
823 bo.set_is(BuildingAttribute::kNeedsBerry);
824 }
825
826 continue;
827 }
828
829 // now for every military building, we fill critical_building_material vector
830 // with critical construction wares
831 if (bld.type() == MapObjectType::MILITARYSITE) {
832 bo.type = BuildingObserver::Type::kMilitarysite;
833 const MilitarySiteDescr& milit = dynamic_cast<const MilitarySiteDescr&>(bld);
834 for (const auto& temp_buildcosts : milit.buildcost()) {
835 // Below are non-critical wares (wares produced without inputs)
836 if (wares.at(temp_buildcosts.first).refined_build_material) {
837 bo.critical_building_material.push_back(temp_buildcosts.first);
838 }
839 }
840 continue;
841 }
842
843 if (bld.type() == MapObjectType::WAREHOUSE) {
844 bo.type = BuildingObserver::Type::kWarehouse;
845 continue;
846 }
847
848 if (bld.type() == MapObjectType::TRAININGSITE) {
849 bo.type = BuildingObserver::Type::kTrainingsite;
850 bo.max_trainingsites_proportion = bh.trainingsites_max_percent();
851 assert(bo.max_trainingsites_proportion <= 100);
852 const TrainingSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
853 for (const auto& temp_input : train.input_wares()) {
854 bo.inputs.push_back(temp_input.first);
855
856 // collecting subsitutes
857 if (tribe_->ware_index("meat") == temp_input.first ||
858 tribe_->ware_index("fish") == temp_input.first ||
859 tribe_->ware_index("smoked_meat") == temp_input.first ||
860 tribe_->ware_index("smoked_fish") == temp_input.first) {
861 bo.substitute_inputs.insert(temp_input.first);
862 }
863 }
864 // Creating vector with critical material, to be used to discourage
865 // building of new sites if ware is lacking
866 for (const auto& temp_buildcosts : train.buildcost()) {
867 // building material except for trivial material
868 if (wares.at(temp_buildcosts.first).refined_build_material) {
869 bo.critical_building_material.push_back(temp_buildcosts.first);
870 }
871 }
872 continue;
873 }
874
875 if (bld.type() == MapObjectType::CONSTRUCTIONSITE) {
876 bo.type = BuildingObserver::Type::kConstructionsite;
877 continue;
878 }
879 }
880
881 // We must verify that some buildings has been identified
882 // Also note that the AI assumes that some buildings are unique, if you want to
883 // create e.g. two barracks or bakeries, the impact on the AI must be considered
884 if (count_buildings_with_attribute(BuildingAttribute::kBarracks) != 1) {
885 throw wexception("The AI needs the tribe '%s' to define 1 type of barracks building. "
886 "This is the building that produces the tribe's 'soldier' worker.",
887 tribe_->name().c_str());
888 }
889 if (count_buildings_with_attribute(BuildingAttribute::kRanger) != 1) {
890 throw wexception(
891 "The AI needs the tribe '%s' to define 1 type of ranger's building. "
892 "This is the building that has 'supports_production_of = { \"log\" }' in its AI hints.",
893 tribe_->name().c_str());
894 }
895 if (count_buildings_with_attribute(BuildingAttribute::kWell) != 1) {
896 throw wexception(
897 "The AI needs the tribe '%s' to define 1 type of well. "
898 "This is the building that has 'collects_ware_from_map = \"water\"' in its AI hints.",
899 tribe_->name().c_str());
900 }
901 if (count_buildings_with_attribute(BuildingAttribute::kLumberjack) != 1) {
902 throw wexception(
903 "The AI needs the tribe '%s' to define 1 type of lumberjack's building. "
904 "This is the building that has 'collects_ware_from_map = \"log\"' in its AI hints.",
905 tribe_->name().c_str());
906 }
907
908 if (count_buildings_with_attribute(BuildingAttribute::kHunter) != 0 &&
909 count_buildings_with_attribute(BuildingAttribute::kHunter) != 1) {
910 throw wexception(
911 "The AI needs the tribe '%s' to define 1 type of hunter's building at the most. "
912 "Hunters are buildings that have 'collects_ware_from_map = \"meat\"' in their AI hints.",
913 tribe_->name().c_str());
914 }
915
916 if (count_buildings_with_attribute(BuildingAttribute::kFisher) != 1) {
917 throw wexception(
918 "The AI needs the tribe '%s' to define 1 type of fisher's building. "
919 "This is the building that has 'collects_ware_from_map = \"fish\"' in its AI hints "
920 "and doesn't have any ware inputs.",
921 tribe_->name().c_str());
922 }
923
924 // atlanteans they consider water as a resource
925 // (together with mines, rocks and wood)
926 if (tribe_->name() == "atlanteans") {
927 resource_necessity_water_needed_ = true;
928 }
929
930 // Populating taskPool with all AI jobs and their starting times
931 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 0),
932 SchedulerTaskId::kConstructBuilding, 6,
933 "construct a building"));
934 taskPool.push_back(SchedulerTask(
935 std::max<uint32_t>(gametime, 1 * 1000), SchedulerTaskId::kRoadCheck, 2, "roads check"));
936 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 15 * 1000),
937 SchedulerTaskId::kCheckProductionsites, 5,
938 "productionsites check"));
939 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 1000),
940 SchedulerTaskId::kProductionsitesStats, 1,
941 "productionsites statistics"));
942 taskPool.push_back(SchedulerTask(
943 std::max<uint32_t>(gametime, 30 * 1000), SchedulerTaskId::kCheckMines, 5, "check mines"));
944 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 0 * 1000),
945 SchedulerTaskId::kCheckMilitarysites, 5,
946 "check militarysites"));
947 taskPool.push_back(SchedulerTask(
948 std::max<uint32_t>(gametime, 30 * 1000), SchedulerTaskId::kCheckShips, 5, "check ships"));
949 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
950 SchedulerTaskId::kCheckEconomies, 1, "check economies"));
951 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 30 * 1000),
952 SchedulerTaskId::KMarineDecisions, 5, "marine decisions"));
953 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 2 * 60 * 1000),
954 SchedulerTaskId::kCheckTrainingsites, 5,
955 "check training sites"));
956 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
957 SchedulerTaskId::kBbuildableFieldsCheck, 2,
958 "check buildable fields"));
959 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
960 SchedulerTaskId::kMineableFieldsCheck, 2,
961 "check mineable fields"));
962 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 1000),
963 SchedulerTaskId::kUnbuildableFCheck, 1,
964 "check unbuildable fields"));
965 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 15 * 60 * 1000),
966 SchedulerTaskId::kWareReview, 9, "wares review"));
967 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
968 SchedulerTaskId::kPrintStats, 9, "print statistics"));
969 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 1 * 60 * 1000),
970 SchedulerTaskId::kCountMilitaryVacant, 2,
971 "count military vacant"));
972 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 60 * 1000),
973 SchedulerTaskId::kCheckEnemySites, 6, "check enemy sites"));
974 if (ai_training_mode_) {
975 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 1000),
976 SchedulerTaskId::kManagementUpdate, 8, "reviewing"));
977 }
978 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 9 * 1000),
979 SchedulerTaskId::kUpdateStats, 6, "update player stats"));
980 taskPool.push_back(SchedulerTask(
981 std::max<uint32_t>(gametime, 10 * 1000), SchedulerTaskId::kUpdateStats, 15, "review"));
982
983 taskPool.push_back(SchedulerTask(std::max<uint32_t>(gametime, 10 * 1000),
984 SchedulerTaskId::kWarehouseFlagDist, 5,
985 "Flag-Warehouse Update"));
986
987 const Map& map = game().map();
988
989 // here we scan entire map for own ships
990 std::set<OPtr<Ship>> found_ships;
991 for (int16_t y = 0; y < map.get_height(); ++y) {
992 for (int16_t x = 0; x < map.get_width(); ++x) {
993 FCoords f = map.get_fcoords(Coords(x, y));
994 // there are too many bobs on the map so we investigate
995 // only bobs on water
996 if (f.field->nodecaps() & MOVECAPS_SWIM) {
997 for (Bob* bob = f.field->get_first_bob(); bob; bob = bob->get_next_on_field()) {
998 if (upcast(Ship, ship, bob)) {
999 if (ship->get_owner() == player_ && !found_ships.count(ship)) {
1000 found_ships.insert(ship);
1001 gain_ship(*ship, NewShip::kFoundOnLoad);
1002 }
1003 }
1004 }
1005 }
1006 }
1007 }
1008
1009 // here we scan entire map for owned unused fields and own buildings
1010 std::set<OPtr<PlayerImmovable>> found_immovables;
1011 for (int16_t y = 0; y < map.get_height(); ++y) {
1012 for (int16_t x = 0; x < map.get_width(); ++x) {
1013 FCoords f = map.get_fcoords(Coords(x, y));
1014
1015 if (f.field->get_owned_by() != player_number()) {
1016 continue;
1017 }
1018
1019 unusable_fields.push_back(f);
1020
1021 if (upcast(PlayerImmovable, imm, f.field->get_immovable())) {
1022 // Guard by a set - immovables might be on several nodes at once.
1023 if (&imm->owner() == player_ && !found_immovables.count(imm)) {
1024 found_immovables.insert(imm);
1025 gain_immovable(*imm, true);
1026 }
1027 }
1028 }
1029 }
1030
1031 // blocking space consumers vicinity (when reloading a game)
1032 for (const ProductionSiteObserver& ps_obs : productionsites) {
1033 if (ps_obs.bo->is(BuildingAttribute::kSpaceConsumer) &&
1034 !ps_obs.bo->is(BuildingAttribute::kRanger)) {
1035 MapRegion<Area<FCoords>> mr(
1036 map, Area<FCoords>(map.get_fcoords(ps_obs.site->get_position()), 4));
1037 do {
1038 blocked_fields.add(mr.location(), game().get_gametime() + 20 * 60 * 1000);
1039 } while (mr.advance(map));
1040 }
1041 }
1042
1043 // getting list of all fields nearby port space
1044 // TODO(tiborb): it seems port spaces can change over time so ports_vicinity needs to be
1045 // refreshed from
1046 // time to time
1047 for (const Coords& c : map.get_port_spaces()) {
1048 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(c), 3));
1049 do {
1050 const uint32_t hash = mr.location().hash();
1051 if (!ports_vicinity.count(hash)) {
1052 ports_vicinity.insert(hash);
1053 }
1054 } while (mr.advance(map));
1055 }
1056
1057 // printing identified basic buildings if we are in the basic economy mode
1058 basic_economy_established = persistent_data->remaining_basic_buildings.empty();
1059 if (!basic_economy_established) {
1060 log("%2d: Initializing in the basic economy mode, required buildings:\n", player_number());
1061 for (auto bb : persistent_data->remaining_basic_buildings) {
1062 log(" %3d / %-28s- target %d\n", bb.first, get_building_observer(bb.first).name,
1063 bb.second);
1064 }
1065 }
1066
1067 update_player_stat(gametime);
1068
1069 // Initialise the max duration of a single ship's expedition
1070 const uint32_t map_area = uint32_t(map.get_height()) * map.get_width();
1071 const uint32_t map_area_root = round(sqrt(map_area));
1072 int scope = 320 - 64;
1073 int off = map_area_root - 64;
1074 if (off < 0) {
1075 off = 0;
1076 }
1077 if (off > scope) {
1078 off = scope;
1079 }
1080 expedition_max_duration =
1081 kExpeditionMinDuration +
1082 static_cast<double>(off) * (kExpeditionMaxDuration - kExpeditionMinDuration) / scope;
1083 log(" %d: expedition max duration = %u (%u minutes), map area root: %u\n", player_number(),
1084 expedition_max_duration / 1000, expedition_max_duration / kOneMinute, map_area_root);
1085 assert(expedition_max_duration >= kExpeditionMinDuration);
1086 assert(expedition_max_duration <= kExpeditionMaxDuration);
1087
1088 // Sometimes there can be a ship in expedition, but expedition start time is not given
1089 // e.g. human player played this player before
1090 if (expedition_ship_ != kNoShip &&
1091 persistent_data->expedition_start_time == Player::AiPersistentState::kNoExpedition) {
1092 // Current gametime is better then 'Player::AiPersistentState::kNoExpedition'
1093 persistent_data->expedition_start_time = gametime;
1094 }
1095
1096 productionsites_ratio_ = management_data.get_military_number_at(86) / 10 + 12;
1097
1098 // Just to be initialized
1099 soldier_status_ = SoldiersStatus::kEnough;
1100 vacant_mil_positions_average_ = 0;
1101 spots_avail.resize(4);
1102 trees_nearby_treshold_ = 3 + std::abs(management_data.get_military_number_at(121)) / 2;
1103 last_road_dismantled_ = 0;
1104 dead_ends_check_ = true;
1105 }
1106
1107 /**
1108 * Checks PART of available buildable fields.
1109 *
1110 * this checks about 40-50 buildable fields. In big games, the player can have thousands
1111 * of them, so we rotate the buildable_fields container and check 35 fields, and in addition
1112 * we look for medium & big fields and near border fields if needed.
1113 */
update_all_buildable_fields(const uint32_t gametime)1114 void DefaultAI::update_all_buildable_fields(const uint32_t gametime) {
1115
1116 // Every call we try to check first 35 buildable fields
1117 constexpr uint16_t kMinimalFieldsCheck = 35;
1118
1119 uint16_t i = 0;
1120
1121 // To be sure we have some info about enemies we might see
1122 update_player_stat(gametime);
1123
1124 // Generally we check fields as they are in the container, but we need also given
1125 // number of "special" fields. So if given number of fields are not found within
1126 // "regular" check, we must go on and look also on other fields...
1127 uint8_t non_small_needed = 4;
1128 uint8_t near_border_needed = 10;
1129
1130 // we test 35 fields that were update more than 1 seconds ago
1131 while (!buildable_fields.empty() &&
1132 i < std::min<uint16_t>(kMinimalFieldsCheck, buildable_fields.size())) {
1133 BuildableField& bf = *buildable_fields.front();
1134
1135 if ((buildable_fields.front()->field_info_expiration - kFieldInfoExpiration + 1000) <=
1136 gametime) {
1137
1138 // check whether we lost ownership of the node
1139 if (bf.coords.field->get_owned_by() != player_number()) {
1140 delete &bf;
1141 buildable_fields.pop_front();
1142 continue;
1143 }
1144
1145 // check whether we can still construct regular buildings on the node
1146 if ((player_->get_buildcaps(bf.coords) & BUILDCAPS_SIZEMASK) == 0) {
1147 unusable_fields.push_back(bf.coords);
1148 delete &bf;
1149 buildable_fields.pop_front();
1150 continue;
1151 }
1152
1153 update_buildable_field(bf);
1154 if (non_small_needed > 0) {
1155 int32_t const maxsize = player_->get_buildcaps(bf.coords) & BUILDCAPS_SIZEMASK;
1156 if (maxsize > 1) {
1157 --non_small_needed;
1158 }
1159 }
1160 if (near_border_needed > 0) {
1161 if (bf.near_border) {
1162 --near_border_needed;
1163 }
1164 }
1165 }
1166 bf.field_info_expiration = gametime + kFieldInfoExpiration;
1167 buildable_fields.push_back(&bf);
1168 buildable_fields.pop_front();
1169
1170 ++i;
1171 }
1172
1173 // If needed we iterate once more and look for 'special' fields
1174 // starting in the middle of buildable_fields to skip fields tested lately
1175 // But not doing this if the count of buildable fields is too low
1176 // (no need to bother)
1177 if (buildable_fields.size() < kMinimalFieldsCheck * 3) {
1178 return;
1179 }
1180
1181 for (uint32_t j = buildable_fields.size() / 2; j < buildable_fields.size(); j++) {
1182 // If we dont need to iterate (anymore) ...
1183 if (non_small_needed + near_border_needed == 0) {
1184 break;
1185 }
1186
1187 // Skip if the field is not ours or was updated lately
1188 if (buildable_fields[j]->coords.field->get_owned_by() != player_number()) {
1189 continue;
1190 }
1191 // We are not interested in fields where info has expired less than 20s ago
1192 if (buildable_fields[j]->field_info_expiration + 20000 > gametime) {
1193 continue;
1194 }
1195
1196 // Continue if field is blocked at the moment
1197 if (blocked_fields.is_blocked(buildable_fields[j]->coords)) {
1198 continue;
1199 }
1200
1201 // Few constants to keep the code cleaner
1202 const int32_t field_maxsize =
1203 player_->get_buildcaps(buildable_fields[j]->coords) & BUILDCAPS_SIZEMASK;
1204 const bool field_near_border = buildable_fields[j]->near_border;
1205
1206 // Let decide if we need to update and for what reason
1207 const bool update_due_size = non_small_needed && field_maxsize > 1;
1208 const bool update_due_border = near_border_needed && field_near_border;
1209
1210 if (!(update_due_size || update_due_border)) {
1211 continue;
1212 }
1213
1214 // decreasing the counters
1215 if (update_due_size) {
1216 assert(non_small_needed > 0);
1217 --non_small_needed;
1218 }
1219 if (update_due_border) {
1220 assert(near_border_needed > 0);
1221 --near_border_needed;
1222 }
1223
1224 // and finnaly update the buildable field
1225 update_buildable_field(*buildable_fields[j]);
1226 buildable_fields[j]->field_info_expiration = gametime + kFieldInfoExpiration;
1227 }
1228 }
1229
1230 /**
1231 * Checks ALL available mineable fields.
1232 *
1233 * this shouldn't be used often, as it might hang the game for some 100
1234 * milliseconds if the area the computer owns is big.
1235 */
update_all_mineable_fields(const uint32_t gametime)1236 void DefaultAI::update_all_mineable_fields(const uint32_t gametime) {
1237
1238 uint16_t i = 0; // counter, used to track # of checked fields
1239
1240 // we test 30 fields that were updated more than 1 seconds ago
1241 // to avoid re-test of the same field twice
1242 while (!mineable_fields.empty() &&
1243 (mineable_fields.front()->field_info_expiration - kMineFieldInfoExpiration + 1000) <=
1244 gametime &&
1245 i < 30) {
1246 MineableField* mf = mineable_fields.front();
1247
1248 // check whether we lost ownership of the node
1249 if (mf->coords.field->get_owned_by() != player_number()) {
1250 delete mf;
1251 mineable_fields.pop_front();
1252 continue;
1253 }
1254
1255 // check whether we can still construct regular buildings on the node
1256 if ((player_->get_buildcaps(mf->coords) & BUILDCAPS_MINE) == 0) {
1257 unusable_fields.push_back(mf->coords);
1258 delete mf;
1259 mineable_fields.pop_front();
1260 continue;
1261 }
1262
1263 update_mineable_field(*mf);
1264 mf->field_info_expiration = gametime + kMineFieldInfoExpiration;
1265 mineable_fields.push_back(mf);
1266 mineable_fields.pop_front();
1267
1268 ++i;
1269 }
1270 // Updating overall statistics, first we flush the data and then iterate over all mine fields
1271 // ignoring fields that are blocked usually because they are not accessible
1272 mine_fields_stat.zero();
1273 for (const auto& mineable_field : mineable_fields) {
1274 if (mineable_field->coords.field->get_resources_amount() > 0 &&
1275 !blocked_fields.is_blocked(mineable_field->coords)) {
1276 mine_fields_stat.add(mineable_field->coords.field->get_resources());
1277 }
1278 }
1279
1280 // Following asserts presume that there are 1-3 critical mines
1281 if (mine_fields_stat.count_types() == kMineTypes) {
1282 assert(mine_fields_stat.has_critical_ore_fields());
1283 }
1284 if (mine_fields_stat.count_types() == 0) {
1285 assert(!mine_fields_stat.has_critical_ore_fields());
1286 }
1287 }
1288
1289 /**
1290 * Checks up to 50 fields that weren't buildable the last time.
1291 *
1292 * milliseconds if the area the computer owns is big.
1293 */
update_all_not_buildable_fields()1294 void DefaultAI::update_all_not_buildable_fields() {
1295 int32_t const pn = player_number();
1296
1297 // We are checking at least 5 unusable fields (or less if there are not 5 of them)
1298 // at once, but not more then 200...
1299 // The idea is to check each field at least once a minute, of course with big maps
1300 // it will take longer
1301 uint32_t maxchecks = unusable_fields.size();
1302 if (maxchecks > 5) {
1303 maxchecks = std::min<uint32_t>(5 + (unusable_fields.size() - 5) / 15, 200);
1304 }
1305
1306 for (uint32_t i = 0; i < maxchecks; ++i) {
1307 // check whether we lost ownership of the node
1308 if (unusable_fields.front().field->get_owned_by() != pn) {
1309 unusable_fields.pop_front();
1310 continue;
1311 }
1312
1313 // check whether building capabilities have improved
1314 if (player_->get_buildcaps(unusable_fields.front()) & BUILDCAPS_SIZEMASK) {
1315 buildable_fields.push_back(new BuildableField(unusable_fields.front()));
1316 unusable_fields.pop_front();
1317 update_buildable_field(*buildable_fields.back());
1318 continue;
1319 }
1320
1321 if (player_->get_buildcaps(unusable_fields.front()) & BUILDCAPS_MINE) {
1322 mineable_fields.push_back(new MineableField(unusable_fields.front()));
1323 unusable_fields.pop_front();
1324 update_mineable_field(*mineable_fields.back());
1325 continue;
1326 }
1327
1328 unusable_fields.push_back(unusable_fields.front());
1329 unusable_fields.pop_front();
1330 }
1331 }
1332
1333 /// Updates one buildable field
update_buildable_field(BuildableField & field)1334 void DefaultAI::update_buildable_field(BuildableField& field) {
1335 // look if there is any unowned land nearby
1336 const Map& map = game().map();
1337 const uint32_t gametime = game().get_gametime();
1338 FindNodeUnownedWalkable find_unowned_walkable(player_, game());
1339 FindEnemyNodeWalkable find_enemy_owned_walkable(player_, game());
1340 FindNodeUnownedBuildable find_unowned_buildable(player_, game());
1341 FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
1342 FindNodeUnownedMineable find_unowned_iron_mines(player_, game(), iron_resource_id);
1343 FindNodeAllyOwned find_ally(player_, game(), player_number());
1344 PlayerNumber const pn = player_->player_number();
1345 const World& world = game().world();
1346
1347 constexpr uint16_t kProductionArea = 6;
1348 constexpr uint16_t kBuildableSpotsCheckArea = 10;
1349 constexpr uint16_t kEnemyCheckArea = 16;
1350 const uint16_t ms_enemy_check_area =
1351 kEnemyCheckArea + std::abs(management_data.get_military_number_at(75)) / 10;
1352 constexpr uint16_t kDistantResourcesArea = 20;
1353
1354 uint16_t actual_enemy_check_area = kEnemyCheckArea;
1355 field.is_militarysite = false;
1356 if (field.coords.field->get_immovable()) {
1357 if (field.coords.field->get_immovable()->descr().type() ==
1358 Widelands::MapObjectType::MILITARYSITE) {
1359 field.is_militarysite = true;
1360 actual_enemy_check_area = ms_enemy_check_area;
1361 }
1362 }
1363
1364 field.unowned_land_nearby = map.find_fields(
1365 game(), Area<FCoords>(field.coords, actual_enemy_check_area), nullptr, find_unowned_walkable);
1366
1367 field.enemy_owned_land_nearby =
1368 map.find_fields(game(), Area<FCoords>(field.coords, actual_enemy_check_area), nullptr,
1369 find_enemy_owned_walkable);
1370
1371 field.nearest_buildable_spot_nearby = std::numeric_limits<uint16_t>::max();
1372 field.unowned_buildable_spots_nearby = 0;
1373 field.unowned_portspace_vicinity_nearby = 0;
1374 if (field.unowned_land_nearby > 0 ||
1375 (field.enemy_owned_land_nearby > 0 &&
1376 field.enemy_military_presence <
1377 std::abs(management_data.get_military_number_at(174)) / 10)) {
1378 std::vector<Coords> found_buildable_fields;
1379
1380 // first looking for unowned buildable spots
1381 field.unowned_buildable_spots_nearby =
1382 map.find_fields(game(), Area<FCoords>(field.coords, kBuildableSpotsCheckArea),
1383 &found_buildable_fields, find_unowned_buildable);
1384 field.unowned_buildable_spots_nearby +=
1385 map.find_fields(game(), Area<FCoords>(field.coords, kBuildableSpotsCheckArea),
1386 &found_buildable_fields, find_enemy_owned_walkable);
1387 // Now iterate over fields to collect statistics
1388 for (auto& coords : found_buildable_fields) {
1389 // We are not interested in blocked fields
1390 if (blocked_fields.is_blocked(coords)) {
1391 continue;
1392 }
1393 // And now looking for nearest field
1394 const uint32_t cur_distance = map.calc_distance(coords, field.coords);
1395 if (cur_distance < field.nearest_buildable_spot_nearby) {
1396 field.nearest_buildable_spot_nearby = cur_distance;
1397 }
1398 }
1399
1400 // now looking for unowned_portspace_vicinity_nearby
1401 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, kBuildableSpotsCheckArea));
1402 do {
1403
1404 if (mr.location().field->get_owned_by() == 0 &&
1405 ports_vicinity.count(mr.location().hash()) > 0) {
1406 ++field.unowned_portspace_vicinity_nearby;
1407 }
1408 } while (mr.advance(map));
1409 }
1410
1411 // Is this near the border? Get rid of fields owned by ally
1412 if (map.find_fields(game(), Area<FCoords>(field.coords, 3), nullptr, find_ally) ||
1413 map.find_fields(game(), Area<FCoords>(field.coords, 3), nullptr, find_unowned_walkable)) {
1414 field.near_border = true;
1415 } else {
1416 field.near_border = false;
1417 }
1418
1419 // are we going to count resources now?
1420 static bool resource_count_now = false;
1421 resource_count_now = false;
1422 // Testing in first 10 seconds or if last testing was more then 60 sec ago
1423 if (field.last_resources_check_time < 10000 ||
1424 field.last_resources_check_time - gametime > 60 * 1000) {
1425 resource_count_now = true;
1426 field.last_resources_check_time = gametime;
1427 }
1428
1429 // testing mines
1430 if (resource_count_now) {
1431 uint32_t close_mines = map.find_fields(
1432 game(), Area<FCoords>(field.coords, kProductionArea), nullptr, find_unowned_mines_pots);
1433 uint32_t distant_mines =
1434 map.find_fields(game(), Area<FCoords>(field.coords, kDistantResourcesArea), nullptr,
1435
1436 find_unowned_mines_pots);
1437 distant_mines = distant_mines - close_mines;
1438 field.unowned_mines_spots_nearby = 4 * close_mines + distant_mines / 2;
1439 if (distant_mines > 0) {
1440 field.unowned_mines_spots_nearby += 15;
1441 }
1442 if (field.unowned_mines_spots_nearby > 0 &&
1443 // for performance considerations we count iron nodes only if we have less than 2 iron
1444 // mines now...
1445 mines_per_type[iron_resource_id].total_count() <= 1) {
1446 // counting iron mines, if we have less than two iron mines
1447 field.unowned_iron_mines_nearby =
1448 map.find_fields(game(), Area<FCoords>(field.coords, kDistantResourcesArea), nullptr,
1449 find_unowned_iron_mines);
1450 } else {
1451 field.unowned_iron_mines_nearby = 0;
1452 }
1453 }
1454
1455 // identifying portspace fields
1456 if (player_->get_buildcaps(field.coords) & BUILDCAPS_PORT) {
1457 field.is_portspace = ExtendedBool::kTrue;
1458 } else {
1459 field.is_portspace = ExtendedBool::kFalse;
1460 }
1461
1462 // testing for near portspaces
1463 if (ports_vicinity.count(field.coords.hash()) > 0) {
1464 field.portspace_nearby = ExtendedBool::kTrue;
1465 } else {
1466 field.portspace_nearby = ExtendedBool::kFalse;
1467 }
1468
1469 // testing if a port is nearby, such field will get a priority boost
1470 if (resource_count_now) { // misusing a bit
1471 uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
1472 for (const WarehouseSiteObserver& wh_obs : warehousesites) {
1473 if (wh_obs.bo->is(BuildingAttribute::kPort)) {
1474 const uint16_t actual_distance =
1475 map.calc_distance(field.coords, wh_obs.site->get_position());
1476 nearest_distance = std::min(nearest_distance, actual_distance);
1477 }
1478 }
1479 if (nearest_distance < 15) {
1480 field.port_nearby = true;
1481 } else {
1482 field.port_nearby = false;
1483 }
1484 }
1485
1486 // testing fields in radius 1 to find biggest buildcaps.
1487 // This is to calculate capacity that will be lost if something is
1488 // built here
1489 field.max_buildcap_nearby = 0;
1490 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 1));
1491 do {
1492 if ((player_->get_buildcaps(mr.location()) & BUILDCAPS_SIZEMASK) >
1493 field.max_buildcap_nearby) {
1494 field.max_buildcap_nearby = player_->get_buildcaps(mr.location()) & BUILDCAPS_SIZEMASK;
1495 }
1496 } while (mr.advance(map));
1497
1498 assert((player_->get_buildcaps(field.coords) & BUILDCAPS_SIZEMASK) <= field.max_buildcap_nearby);
1499
1500 // Testing surface water (once only)
1501 // TODO(GunChleoc): We can change the terrain by scripting, so we should work with notifications
1502 // here.
1503 // Let's leave this as it is for now for performance reasons - terrain change of water is
1504 // currently only
1505 // used in the Atlantean scenario.
1506 if (field.water_nearby == kUncalculated) {
1507 assert(field.open_water_nearby == kUncalculated);
1508
1509 FindNodeWater find_water(game().world());
1510 field.water_nearby =
1511 map.find_fields(game(), Area<FCoords>(field.coords, kProductionArea), nullptr, find_water);
1512
1513 if (field.water_nearby > 0) {
1514 FindNodeOpenWater find_open_water(game().world());
1515 field.open_water_nearby = map.find_fields(
1516 game(), Area<FCoords>(field.coords, kProductionArea), nullptr, find_open_water);
1517 }
1518
1519 if (resource_necessity_water_needed_) { // for atlanteans
1520 field.distant_water =
1521 map.find_fields(
1522 game(), Area<FCoords>(field.coords, kDistantResourcesArea), nullptr, find_water) -
1523 field.water_nearby;
1524 assert(field.open_water_nearby <= field.water_nearby);
1525 }
1526 }
1527
1528 FCoords fse;
1529 map.get_neighbour(field.coords, WALK_SE, &fse);
1530 field.preferred = false;
1531 if (BaseImmovable const* const imm = fse.field->get_immovable()) {
1532 if (dynamic_cast<Flag const*>(imm) ||
1533 (dynamic_cast<Road const*>(imm) && (fse.field->nodecaps() & BUILDCAPS_FLAG))) {
1534 field.preferred = true;
1535 }
1536 }
1537
1538 // counting fields with fish, doing it roughly every 10-th minute is enough
1539 if (field.water_nearby > 0 &&
1540 (field.fish_nearby == kUncalculated || (resource_count_now && gametime % 10 == 0))) {
1541 CheckStepWalkOn fisher_cstep(MOVECAPS_WALK, true);
1542 static std::vector<Coords> fish_fields_list; // pity this contains duplicates
1543 fish_fields_list.clear();
1544 map.find_reachable_fields(game(), Area<FCoords>(field.coords, kProductionArea),
1545 &fish_fields_list, fisher_cstep,
1546 FindNodeResource(world.resource_index("fish")));
1547
1548 // This is "list" of unique fields in fish_fields_list we got above
1549 static std::set<Coords> counted_fields;
1550 counted_fields.clear();
1551 field.fish_nearby = 0;
1552 for (auto fish_coords : fish_fields_list) {
1553 if (counted_fields.insert(fish_coords).second) {
1554 field.fish_nearby += map.get_fcoords(fish_coords).field->get_resources_amount();
1555 }
1556 }
1557 }
1558
1559 // Counting resources that do not change fast
1560 if (resource_count_now) {
1561 // Counting fields with critters (game)
1562 field.critters_nearby = map.find_bobs(
1563 game(), Area<FCoords>(field.coords, kProductionArea), nullptr, FindBobCritter());
1564
1565 // Rocks are not renewable, we will count them only if previous state is nonzero
1566 if (field.rocks_nearby > 0) {
1567 field.rocks_nearby = map.find_immovables(
1568 game(), Area<FCoords>(map.get_fcoords(field.coords), kProductionArea), nullptr,
1569 FindImmovableAttribute(MapObjectDescr::get_attribute_id("rocks")));
1570
1571 // adding 5 if rocks found
1572 field.rocks_nearby = (field.rocks_nearby > 0) ? field.rocks_nearby + 2 : 0;
1573 }
1574
1575 // ground water is not renewable and its amount can only fall, we will count them only if
1576 // previous state is nonzero
1577 if (field.ground_water > 0) {
1578 field.ground_water = field.coords.field->get_resources_amount();
1579 }
1580
1581 // Counting trees nearby
1582 int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
1583 field.trees_nearby =
1584 map.find_immovables(game(), Area<FCoords>(map.get_fcoords(field.coords), kProductionArea),
1585 nullptr, FindImmovableAttribute(tree_attr));
1586
1587 // Counting bushes nearby
1588 int32_t const bush_attr = MapObjectDescr::get_attribute_id("ripe_bush");
1589 field.bushes_nearby =
1590 map.find_immovables(game(), Area<FCoords>(map.get_fcoords(field.coords), kProductionArea),
1591 nullptr, FindImmovableAttribute(bush_attr));
1592 }
1593
1594 // resetting some values
1595 field.enemy_nearby =
1596 (field.enemy_owned_land_nearby > std::abs(management_data.get_military_number_at(41) / 4)) ?
1597 true :
1598 false;
1599 if (field.enemy_owned_land_nearby == 0) {
1600 assert(!field.enemy_nearby);
1601 }
1602
1603 // resetting a bunch of values for the field
1604 field.ally_military_presence = 0;
1605 field.area_military_capacity = 0;
1606 field.consumers_nearby.clear();
1607 field.consumers_nearby.resize(wares.size());
1608 field.enemy_military_presence = 0;
1609 field.enemy_military_sites = 0;
1610 field.enemy_wh_nearby = false;
1611 field.military_in_constr_nearby = 0;
1612 field.military_loneliness = 1000;
1613 field.military_stationed = 0;
1614 field.military_unstationed = 0;
1615 field.own_military_presence = 0;
1616 field.own_non_military_nearby = 0;
1617 field.producers_nearby.clear();
1618 field.collecting_producers_nearby.clear();
1619 field.producers_nearby.resize(wares.size());
1620 field.collecting_producers_nearby.resize(wares.size());
1621 field.rangers_nearby = 0;
1622 field.space_consumers_nearby = 0;
1623 field.supporters_nearby.clear();
1624 field.supporters_nearby.resize(wares.size());
1625 field.unconnected_nearby = false;
1626
1627 // collect information about productionsites nearby
1628 static std::vector<ImmovableFound> immovables;
1629 immovables.reserve(50);
1630 immovables.clear();
1631 // Search in a radius of range
1632 map.find_immovables(game(), Area<FCoords>(field.coords, kProductionArea + 2), &immovables);
1633
1634 // function seems to return duplicates, so we will use serial numbers to filter them out
1635 static std::set<uint32_t> unique_serials;
1636 unique_serials.clear();
1637
1638 for (uint32_t i = 0; i < immovables.size(); ++i) {
1639 const BaseImmovable& base_immovable = *immovables.at(i).object;
1640 if (!unique_serials.insert(base_immovable.serial()).second) {
1641 continue; // serial was not inserted in the set, so this is a duplicate
1642 }
1643
1644 if (upcast(PlayerImmovable const, player_immovable, &base_immovable)) {
1645
1646 // TODO(unknown): Only continue if this is an opposing site
1647 // allied sites should be counted for military influence
1648 if (player_immovable->owner().player_number() != pn) {
1649 continue;
1650 }
1651 // here we identify the buiding (including expected building if constructionsite)
1652 // and calculate some statistics about nearby buildings
1653 if (player_immovable->descr().type() == MapObjectType::PRODUCTIONSITE) {
1654 BuildingObserver& bo = get_building_observer(player_immovable->descr().name().c_str());
1655 consider_productionsite_influence(field, immovables.at(i).coords, bo);
1656 } else if (upcast(ConstructionSite const, constructionsite, player_immovable)) {
1657 const BuildingDescr& target_descr = constructionsite->building();
1658 BuildingObserver& bo = get_building_observer(target_descr.name().c_str());
1659 consider_productionsite_influence(field, immovables.at(i).coords, bo);
1660 }
1661 }
1662 }
1663
1664 // Now testing military aspects
1665 immovables.clear();
1666 map.find_immovables(game(), Area<FCoords>(field.coords, actual_enemy_check_area), &immovables);
1667
1668 // We are interested in unconnected immovables, but we must be also close to connected ones
1669 static bool any_connected_imm = false;
1670 any_connected_imm = false;
1671 static bool any_unconnected_imm = false;
1672 any_unconnected_imm = false;
1673 unique_serials.clear();
1674
1675 for (uint32_t i = 0; i < immovables.size(); ++i) {
1676 const BaseImmovable& base_immovable = *immovables.at(i).object;
1677
1678 if (!unique_serials.insert(base_immovable.serial()).second) {
1679 continue; // serial was not inserted in the set, so this is duplicate
1680 }
1681
1682 // testing if immovable is owned by someone else and collecting some statistics
1683 if (upcast(Building const, building, &base_immovable)) {
1684
1685 const PlayerNumber bpn = building->owner().player_number();
1686 if (player_statistics.get_is_enemy(bpn)) { // owned by enemy
1687 assert(!player_statistics.players_in_same_team(bpn, pn));
1688 field.enemy_nearby = true;
1689 if (upcast(MilitarySite const, militarysite, building)) {
1690 field.enemy_military_presence +=
1691 militarysite->soldier_control()->stationed_soldiers().size();
1692 ++field.enemy_military_sites;
1693 }
1694 if (upcast(ConstructionSite const, constructionsite, building)) {
1695 const BuildingDescr& target_descr = constructionsite->building();
1696 if (target_descr.type() == MapObjectType::MILITARYSITE) {
1697 ++field.enemy_military_sites;
1698 }
1699 }
1700
1701 // Warehouses are counted here too as they can host soldiers as well
1702 if (upcast(Warehouse const, warehouse, building)) {
1703 field.enemy_military_presence +=
1704 warehouse->soldier_control()->stationed_soldiers().size();
1705 ++field.enemy_military_sites;
1706 field.enemy_wh_nearby = true;
1707 enemy_warehouses.insert(building->get_position().hash());
1708 }
1709 continue;
1710 } else if (bpn != pn) { // it is an ally
1711 assert(!player_statistics.get_is_enemy(bpn));
1712 if (upcast(MilitarySite const, militarysite, building)) {
1713 field.ally_military_presence +=
1714 militarysite->soldier_control()->stationed_soldiers().size();
1715 }
1716 continue;
1717 }
1718
1719 // if we are here, the immovable is ours
1720 assert(building->owner().player_number() == pn);
1721
1722 // connected to a warehouse
1723 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
1724 // ferries
1725 bool connected = !building->get_economy(wwWORKER)->warehouses().empty();
1726 if (connected) {
1727 any_connected_imm = true;
1728 }
1729
1730 if (upcast(ConstructionSite const, constructionsite, building)) {
1731 const BuildingDescr& target_descr = constructionsite->building();
1732
1733 if (upcast(MilitarySiteDescr const, target_ms_d, &target_descr)) {
1734 const int32_t dist = map.calc_distance(field.coords, immovables.at(i).coords);
1735 const int32_t radius = target_ms_d->get_conquers() + 4;
1736
1737 if (radius > dist) {
1738 field.area_military_capacity += target_ms_d->get_max_number_of_soldiers() / 2 + 1;
1739 if (field.coords != immovables.at(i).coords) {
1740 field.military_loneliness *= static_cast<double_t>(dist) / radius;
1741 }
1742 ++field.military_in_constr_nearby;
1743 }
1744 }
1745 } else if (!connected) {
1746 // we don't care about unconnected constructionsites
1747 any_unconnected_imm = true;
1748 }
1749
1750 if (upcast(MilitarySite const, militarysite, building)) {
1751 const int32_t dist = map.calc_distance(field.coords, immovables.at(i).coords);
1752 const int32_t radius = militarysite->descr().get_conquers() + 4;
1753
1754 if (radius > dist) {
1755 field.area_military_capacity +=
1756 militarysite->soldier_control()->max_soldier_capacity();
1757 field.own_military_presence +=
1758 militarysite->soldier_control()->stationed_soldiers().size();
1759
1760 if (militarysite->soldier_control()->stationed_soldiers().empty()) {
1761 ++field.military_unstationed;
1762 } else {
1763 ++field.military_stationed;
1764 }
1765
1766 if (field.coords != immovables.at(i).coords) {
1767 field.military_loneliness *= static_cast<double_t>(dist) / radius;
1768 }
1769 }
1770 } else {
1771 ++field.own_non_military_nearby;
1772 }
1773 }
1774 }
1775
1776 assert(field.military_loneliness <= 1000);
1777
1778 if (any_unconnected_imm && any_connected_imm && field.military_in_constr_nearby == 0) {
1779 field.unconnected_nearby = true;
1780 }
1781
1782 // if there is a militarysite on the field, we try to walk to enemy
1783 field.enemy_accessible_ = false;
1784 field.local_soldier_capacity = 0;
1785 if (field.is_militarysite) {
1786 if (upcast(MilitarySite, ms, field.coords.field->get_immovable())) {
1787 if (field.enemy_nearby) {
1788 uint32_t unused1 = 0;
1789 uint16_t unused2 = 0;
1790 field.enemy_accessible_ = other_player_accessible(
1791 actual_enemy_check_area + 3, &unused1, &unused2, field.coords, WalkSearch::kEnemy);
1792 }
1793 field.local_soldier_capacity = ms->soldier_control()->max_soldier_capacity();
1794 field.is_militarysite = true;
1795 } else {
1796 NEVER_HERE();
1797 }
1798 }
1799
1800 // Calculating field score
1801 field.military_score_ = 0;
1802 field.inland = false;
1803
1804 if (!(field.enemy_nearby || field.near_border)) {
1805 field.inland = true;
1806 }
1807
1808 const uint8_t score_parts_size = 69;
1809 int32_t score_parts[score_parts_size] = {0};
1810 if (field.enemy_owned_land_nearby) {
1811 score_parts[0] = 3 *
1812 management_data.neuron_pool[73].get_result_safe(
1813 field.enemy_owned_land_nearby / 5, kAbsValue);
1814 score_parts[1] =
1815 3 *
1816 management_data.neuron_pool[76].get_result_safe(field.enemy_owned_land_nearby, kAbsValue);
1817 score_parts[2] = 3 *
1818 management_data.neuron_pool[54].get_result_safe(
1819 field.enemy_military_presence * 2, kAbsValue);
1820 score_parts[3] = 3 *
1821 management_data.neuron_pool[61].get_result_safe(
1822 field.enemy_military_presence / 3, kAbsValue);
1823 score_parts[4] =
1824 (!field.enemy_accessible_) ? (-100 + management_data.get_military_number_at(55)) : 0;
1825 score_parts[5] =
1826 2 *
1827 management_data.neuron_pool[50].get_result_safe(field.enemy_owned_land_nearby, kAbsValue);
1828
1829 score_parts[6] =
1830 field.enemy_military_sites * std::abs(management_data.get_military_number_at(67) / 2);
1831 score_parts[7] =
1832 2 *
1833 management_data.neuron_pool[34].get_result_safe(field.enemy_military_sites * 2, kAbsValue);
1834 score_parts[8] = management_data.neuron_pool[56].get_result_safe(
1835 field.enemy_military_presence * 2, kAbsValue);
1836
1837 score_parts[9] = management_data.neuron_pool[65].get_result_safe(
1838 (field.unowned_land_nearby + field.enemy_owned_land_nearby) / 2, kAbsValue);
1839 score_parts[10] = (field.enemy_accessible_) ? management_data.get_military_number_at(80) : 0;
1840
1841 score_parts[11] =
1842 -3 *
1843 management_data.neuron_pool[8].get_result_safe(
1844 (field.military_in_constr_nearby + field.military_unstationed) * 3, kAbsValue);
1845 score_parts[12] =
1846 -3 *
1847 management_data.neuron_pool[74].get_result_safe(
1848 (field.military_in_constr_nearby + field.military_unstationed) * 5, kAbsValue);
1849 score_parts[13] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ?
1850 -std::abs(management_data.get_military_number_at(32)) :
1851 0;
1852 score_parts[14] = -1 * (field.military_in_constr_nearby + field.military_unstationed) *
1853 std::abs(management_data.get_military_number_at(12));
1854
1855 score_parts[15] =
1856 -2 * management_data.neuron_pool[75].get_result_safe(field.own_military_presence);
1857 score_parts[16] = -5 * std::min<int16_t>(field.area_military_capacity, 20);
1858 score_parts[17] = 3 * management_data.get_military_number_at(28);
1859 score_parts[18] =
1860 (field.enemy_nearby) ? 3 * std::abs(management_data.get_military_number_at(68)) : 0;
1861 score_parts[19] =
1862 (field.enemy_wh_nearby) ? 3 * std::abs(management_data.get_military_number_at(132)) : 0;
1863 score_parts[64] = (field.enemy_wh_nearby) ?
1864 std::abs(management_data.get_military_number_at(135)) :
1865 -std::abs(management_data.get_military_number_at(135));
1866
1867 } else { // for expansion or inner land
1868
1869 score_parts[20] = management_data.neuron_pool[22].get_result_safe(
1870 (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue);
1871 score_parts[21] = (field.unowned_mines_spots_nearby > 0) ?
1872 std::abs(management_data.get_military_number_at(58)) :
1873 0;
1874 if (expansion_type.get_expansion_type() == ExpansionMode::kResources) {
1875 score_parts[23] = 2 *
1876 management_data.neuron_pool[78].get_result_safe(
1877 (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue);
1878 }
1879
1880 score_parts[24] =
1881 (field.unowned_land_nearby) ?
1882 management_data.neuron_pool[25].get_result_safe(field.water_nearby / 2, kAbsValue) :
1883 0;
1884 score_parts[25] =
1885 (field.unowned_land_nearby) ?
1886 management_data.neuron_pool[27].get_result_safe(field.trees_nearby / 2, kAbsValue) :
1887 0;
1888
1889 if (resource_necessity_water_needed_) {
1890 score_parts[26] =
1891 (field.unowned_land_nearby) ?
1892 management_data.neuron_pool[15].get_result_safe(field.water_nearby, kAbsValue) :
1893 0;
1894 score_parts[27] =
1895 resource_necessity_water_needed_ *
1896 management_data.neuron_pool[17].get_result_safe(field.distant_water, kAbsValue) / 100;
1897 }
1898 score_parts[28] =
1899 (field.unowned_land_nearby) ?
1900 management_data.neuron_pool[33].get_result_safe(field.water_nearby, kAbsValue) :
1901 0;
1902 score_parts[29] =
1903 management_data.neuron_pool[10].get_result_safe(field.military_loneliness / 50, kAbsValue);
1904
1905 score_parts[30] =
1906 -10 *
1907 management_data.neuron_pool[8].get_result_safe(
1908 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue);
1909 score_parts[31] =
1910 -10 *
1911 management_data.neuron_pool[31].get_result_safe(
1912 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue);
1913 score_parts[32] = -4 * field.military_in_constr_nearby *
1914 std::abs(management_data.get_military_number_at(82));
1915 score_parts[33] = (field.military_in_constr_nearby > 0) ?
1916 -5 * management_data.get_military_number_at(85) :
1917 0;
1918
1919 score_parts[34] = -1 *
1920 management_data.neuron_pool[4].get_result_safe(
1921 (field.area_military_capacity + 4) / 5, kAbsValue);
1922 score_parts[35] = 3 * management_data.get_military_number_at(133);
1923
1924 if (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) {
1925 score_parts[36] = -100 - 4 * std::abs(management_data.get_military_number_at(139));
1926 } else if (expansion_type.get_expansion_type() == ExpansionMode::kResources ||
1927 expansion_type.get_expansion_type() == ExpansionMode::kSpace) {
1928 score_parts[37] =
1929 +100 + 4 * std::abs(management_data.get_military_number_at(139)); // The same as above
1930 }
1931 if (msites_in_constr() > 0 && field.max_buildcap_nearby == BUILDCAPS_BIG &&
1932 spots_avail.at(BUILDCAPS_BIG) <= 2) {
1933 score_parts[65] = -10 * std::abs(management_data.get_military_number_at(54));
1934 }
1935 }
1936
1937 // common inputs
1938 if (field.unowned_iron_mines_nearby > 0 && mines_per_type[iron_resource_id].total_count() == 0) {
1939 score_parts[40] = field.unowned_iron_mines_nearby *
1940 std::abs(management_data.get_military_number_at(92)) / 50;
1941 }
1942 if (field.unowned_iron_mines_nearby && mines_per_type[iron_resource_id].total_count() <= 1) {
1943 score_parts[41] = 3 * std::abs(management_data.get_military_number_at(93));
1944 }
1945
1946 score_parts[42] =
1947 (field.unowned_land_nearby) ?
1948 management_data.neuron_pool[18].get_result_safe(field.own_non_military_nearby, kAbsValue) :
1949 0;
1950
1951 score_parts[43] = 2 *
1952 management_data.neuron_pool[11].get_result_safe(
1953 field.unowned_buildable_spots_nearby, kAbsValue);
1954 score_parts[44] =
1955 management_data.neuron_pool[12].get_result_safe(field.unowned_mines_spots_nearby, kAbsValue);
1956 score_parts[45] =
1957 (field.unowned_land_nearby) ?
1958 field.military_loneliness * std::abs(management_data.get_military_number_at(53)) / 800 :
1959 0;
1960
1961 score_parts[46] =
1962 -1 * management_data.neuron_pool[55].get_result_safe(field.ally_military_presence, kAbsValue);
1963 score_parts[47] =
1964 -1 *
1965 management_data.neuron_pool[53].get_result_safe(2 * field.ally_military_presence, kAbsValue);
1966 score_parts[48] = -2 *
1967 management_data.neuron_pool[4].get_result_safe(
1968 (field.area_military_capacity + 4) / 5, kAbsValue);
1969 score_parts[49] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ?
1970 -std::abs(management_data.get_military_number_at(81)) :
1971 0;
1972 score_parts[55] = (field.military_loneliness < 10) ?
1973 2 * std::abs(management_data.get_military_number_at(141)) :
1974 0;
1975 score_parts[56] =
1976 (any_unconnected_imm) ? 2 * std::abs(management_data.get_military_number_at(23)) : 0;
1977 score_parts[57] = 1 *
1978 management_data.neuron_pool[18].get_result_safe(
1979 2 * field.unowned_portspace_vicinity_nearby, kAbsValue);
1980 score_parts[58] = 3 *
1981 management_data.neuron_pool[19].get_result_safe(
1982 5 * field.unowned_portspace_vicinity_nearby, kAbsValue);
1983 score_parts[59] = (field.unowned_portspace_vicinity_nearby) ?
1984 10 * std::abs(management_data.get_military_number_at(31)) :
1985 0;
1986 score_parts[60] = 3 *
1987 management_data.neuron_pool[21].get_result_safe(
1988 20 - field.nearest_buildable_spot_nearby, kAbsValue);
1989 score_parts[61] = (field.nearest_buildable_spot_nearby < 8) ?
1990 std::abs(management_data.get_military_number_at(153) * 2) :
1991 0;
1992 score_parts[62] = (field.nearest_buildable_spot_nearby > 20) ?
1993 -std::abs(management_data.get_military_number_at(154) * 2) :
1994 0;
1995 score_parts[63] = (field.nearest_buildable_spot_nearby < 4) ?
1996 std::abs(management_data.get_military_number_at(155) * 2) :
1997 0;
1998 // 64 and 65 are used above
1999 score_parts[66] =
2000 (field.unowned_mines_spots_nearby > 0 && !mine_fields_stat.has_critical_ore_fields()) ?
2001 std::abs(management_data.get_military_number_at(157)) :
2002 0;
2003 score_parts[67] = (field.unowned_mines_spots_nearby > 0 && mine_fields_stat.count_types() <= 4) ?
2004 std::abs(management_data.get_military_number_at(158)) :
2005 0;
2006 score_parts[68] =
2007 (field.unowned_mines_spots_nearby == 0 && mine_fields_stat.count_types() <= 4) ?
2008 -std::abs(management_data.get_military_number_at(159)) :
2009 0;
2010
2011 for (uint16_t i = 0; i < score_parts_size; i++) {
2012 field.military_score_ += score_parts[i];
2013 }
2014
2015 if (ai_training_mode_) {
2016 if (field.military_score_ < -5000 || field.military_score_ > 2000) {
2017 log("Warning field.military_score_ %5d, compounds: ", field.military_score_);
2018 for (uint16_t i = 0; i < score_parts_size; i++) {
2019 log("%d, ", score_parts[i]);
2020 }
2021 log("\n");
2022 }
2023 }
2024
2025 // is new site allowed at all here?
2026 field.defense_msite_allowed = false;
2027 static int16_t multiplicator = 10;
2028 multiplicator = 10;
2029 if (soldier_status_ == SoldiersStatus::kBadShortage) {
2030 multiplicator = 4;
2031 } else if (soldier_status_ == SoldiersStatus::kShortage) {
2032 multiplicator = 7;
2033 }
2034 if (field.area_military_capacity < field.enemy_military_presence * multiplicator / 10) {
2035 field.defense_msite_allowed = true;
2036 }
2037 }
2038
2039 /// Updates one mineable field
update_mineable_field(MineableField & field)2040 void DefaultAI::update_mineable_field(MineableField& field) {
2041 // collect information about resources in the area
2042 std::vector<ImmovableFound> immovables;
2043 const Map& map = game().map();
2044 map.find_immovables(game(), Area<FCoords>(field.coords, 5), &immovables);
2045 field.preferred = false;
2046 field.mines_nearby = 0;
2047 FCoords fse;
2048 map.get_brn(field.coords, &fse);
2049
2050 if (BaseImmovable const* const imm = fse.field->get_immovable()) {
2051 if (dynamic_cast<Flag const*>(imm) ||
2052 (dynamic_cast<Road const*>(imm) && (fse.field->nodecaps() & BUILDCAPS_FLAG))) {
2053 field.preferred = true;
2054 }
2055 }
2056
2057 for (const ImmovableFound& temp_immovable : immovables) {
2058 if (upcast(Building const, bld, temp_immovable.object)) {
2059 if (player_number() != bld->owner().player_number()) {
2060 continue;
2061 }
2062 if (bld->descr().get_ismine()) {
2063 if (get_building_observer(bld->descr().name().c_str()).mines ==
2064 field.coords.field->get_resources()) {
2065 ++field.mines_nearby;
2066 }
2067 } else if (upcast(ConstructionSite const, cs, bld)) {
2068 if (cs->building().get_ismine()) {
2069 if (get_building_observer(cs->building().name().c_str()).mines ==
2070 field.coords.field->get_resources()) {
2071 ++field.mines_nearby;
2072 }
2073 }
2074 }
2075 }
2076 }
2077
2078 // 0 is default, and thus indicates that counting must be done
2079 if (field.same_mine_fields_nearby == 0) {
2080 FindNodeMineable find_mines_spots_nearby(game(), field.coords.field->get_resources());
2081 field.same_mine_fields_nearby =
2082 map.find_fields(game(), Area<FCoords>(field.coords, 4), nullptr, find_mines_spots_nearby);
2083 }
2084 }
2085
2086 /// Updates the production and MINE sites statistics needed for construction decision.
update_productionsite_stats()2087 void DefaultAI::update_productionsite_stats() {
2088
2089 // Reset statistics for all buildings
2090 for (uint32_t i = 0; i < buildings_.size(); ++i) {
2091 buildings_.at(i).current_stats = 0;
2092 buildings_.at(i).unoccupied_count = 0;
2093 buildings_.at(i).unconnected_count = 0;
2094 }
2095
2096 // Check all available productionsites
2097 for (uint32_t i = 0; i < productionsites.size(); ++i) {
2098 assert(productionsites.front().bo->cnt_built > 0);
2099 // is connected
2100 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
2101 // ferries
2102 const bool connected_to_wh =
2103 !productionsites.front().site->get_economy(wwWORKER)->warehouses().empty();
2104
2105 // unconnected buildings are excluded from statistics review
2106 if (connected_to_wh) {
2107 // Add statistics value
2108 productionsites.front().bo->current_stats +=
2109 productionsites.front().site->get_statistics_percent();
2110
2111 // Check whether this building is completely occupied
2112 if (!productionsites.front().site->can_start_working()) {
2113 ++productionsites.front().bo->unoccupied_count;
2114 }
2115 } else {
2116 ++productionsites.front().bo->unconnected_count;
2117 }
2118
2119 // Now reorder the buildings
2120 productionsites.push_back(productionsites.front());
2121 productionsites.pop_front();
2122 }
2123
2124 // for mines_ also
2125 // Check all available mines
2126 for (uint32_t i = 0; i < mines_.size(); ++i) {
2127 assert(mines_.front().bo->cnt_built > 0);
2128
2129 const bool connected_to_wh =
2130 !mines_.front().site->get_economy(wwWORKER)->warehouses().empty();
2131
2132 // unconnected mines are excluded from statistics review
2133 if (connected_to_wh) {
2134 // Add statistics value
2135 mines_.front().bo->current_stats += mines_.front().site->get_statistics_percent();
2136 // Check whether this building is completely occupied
2137 if (!mines_.front().site->can_start_working()) {
2138 ++mines_.front().bo->unoccupied_count;
2139 }
2140 } else {
2141 ++mines_.front().bo->unconnected_count;
2142 }
2143
2144 // Now reorder the buildings
2145 mines_.push_back(mines_.front());
2146 mines_.pop_front();
2147 }
2148
2149 // Scale statistics down
2150 for (uint32_t i = 0; i < buildings_.size(); ++i) {
2151 if ((buildings_.at(i).cnt_built - buildings_.at(i).unconnected_count) > 0) {
2152 buildings_.at(i).current_stats /=
2153 (buildings_.at(i).cnt_built - buildings_.at(i).unconnected_count);
2154 }
2155 }
2156 }
2157
2158 // * Constructs the most needed building
2159 // algorithm goes over all available spots and all allowed buildings,
2160 // scores every combination and one with highest and positive score
2161 // is built.
2162 // * Buildings are split into categories
2163 // * The logic is complex but approximately:
2164 // - some buildings belong to "basic economy" - these are preferred
2165 // - some small huts are exempt from basic economy logic
2166 // - first bulding of a type is preferred
2167 // - algorithm is trying to take into account actual utlization of buildings
2168 // (the one shown in GUI/game is not reliable, it calculates own statistics)
2169 // * military buildings use genetic algorithm logic to score fields
2170 // Currently more military buildings are built than needed
2171 // so there are always some vacant positions
construct_building(uint32_t gametime)2172 bool DefaultAI::construct_building(uint32_t gametime) {
2173 if (buildable_fields.empty()) {
2174 return false;
2175 }
2176
2177 // Just used for easy checking whether a mine or something else was built.
2178 static bool mine = false;
2179 mine = false;
2180 static uint32_t consumers_nearby_count = 0;
2181 consumers_nearby_count = 0;
2182
2183 const Map& map = game().map();
2184
2185 if (gametime > last_seafaring_check_ + 20000U) {
2186 map_allows_seafaring_ = map.allows_seafaring();
2187 last_seafaring_check_ = gametime;
2188 }
2189
2190 for (int32_t i = 0; i < 4; ++i) {
2191 spots_avail.at(i) = 0;
2192 }
2193
2194 // We calculate owned buildable spots, of course ignoring ones that are blocked
2195 // for now
2196 for (std::deque<BuildableField*>::iterator i = buildable_fields.begin();
2197 i != buildable_fields.end(); ++i) {
2198 if (blocked_fields.is_blocked((*i)->coords)) {
2199 continue;
2200 }
2201 ++spots_avail.at((*i)->coords.field->nodecaps() & BUILDCAPS_SIZEMASK);
2202 }
2203
2204 spots_ = spots_avail.at(BUILDCAPS_SMALL);
2205 spots_ += spots_avail.at(BUILDCAPS_MEDIUM);
2206 spots_ += spots_avail.at(BUILDCAPS_BIG);
2207
2208 // helper variable - we need some proportion of free spots vs productionsites
2209 // the proportion depends on size of economy
2210 // this proportion defines how dense the buildings will be
2211 // it is degressive (allows high density on the beginning)
2212 static int32_t needed_spots = 0;
2213 if (productionsites.size() < 50) {
2214 needed_spots = productionsites.size();
2215 } else if (productionsites.size() < 100) {
2216 needed_spots = 50 + (productionsites.size() - 50) * 5;
2217 } else if (productionsites.size() < 200) {
2218 needed_spots = 300 + (productionsites.size() - 100) * 10;
2219 } else {
2220 needed_spots = 1300 + (productionsites.size() - 200) * 20;
2221 }
2222 const bool has_enough_space = (spots_ > needed_spots);
2223
2224 // Do we have basic economy established? Informing that we just left the basic economy mode.
2225 if (!basic_economy_established && persistent_data->remaining_basic_buildings.empty()) {
2226 log("%2d: Player has achieved the basic economy at %s\n", player_number(),
2227 gamestring_with_leading_zeros(gametime));
2228 basic_economy_established = true;
2229 assert(persistent_data->remaining_basic_buildings.empty());
2230 }
2231
2232 if (!basic_economy_established && player_statistics.any_enemy_seen_lately(gametime) &&
2233 management_data.f_neuron_pool[17].get_position(0)) {
2234 log("%2d: Player has not all buildings for basic economy yet (%" PRIuS
2235 " missing), but enemy is "
2236 "nearby, so quitting the mode at %s\n",
2237 player_number(), persistent_data->remaining_basic_buildings.size(),
2238 gamestring_with_leading_zeros(gametime));
2239 basic_economy_established = true;
2240 // Zeroing following to preserve consistency
2241 persistent_data->remaining_basic_buildings.clear();
2242 }
2243
2244 // *_military_scores are used as minimal score for a new military building
2245 // to be built. As AI does not traverse all building fields at once, these thresholds
2246 // are gradually going down until it finds a field&building that are above threshold
2247 // and this combination is used...
2248 // least_military_score is hardlimit, floating very slowly
2249 // target_military_score is always set according to latest best building (using the same
2250 // score) and quickly falling down until it reaches the least_military_score
2251 // this one (=target_military_score) is actually used to decide if building&field is allowed
2252 // candidate
2253
2254 const PlayerNumber pn = player_number();
2255
2256 // Genetic algorithm is used here
2257 static bool inputs[2 * kFNeuronBitSize] = {0};
2258 for (int i = 0; i < 2 * kFNeuronBitSize; i++) {
2259 inputs[i] = 0;
2260 }
2261 inputs[0] = (pow(msites_in_constr(), 2) > militarysites.size() + 2);
2262 inputs[1] = !(pow(msites_in_constr(), 2) > militarysites.size() + 2);
2263 inputs[2] =
2264 (highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(29) / 10));
2265 inputs[3] =
2266 !(highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(29) / 10));
2267 inputs[4] = (highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(48)));
2268 inputs[5] = !(highest_nonmil_prio_ > 18 + std::abs(management_data.get_military_number_at(49)));
2269 inputs[6] = ((numof_psites_in_constr + mines_in_constr()) >
2270 (productionsites.size() + mines_built()) / productionsites_ratio_);
2271 inputs[7] = !((numof_psites_in_constr + mines_in_constr()) >
2272 (productionsites.size() + mines_built()) / productionsites_ratio_);
2273
2274 inputs[8] = (has_enough_space);
2275 inputs[9] = !(has_enough_space);
2276 inputs[10] = (has_enough_space);
2277 inputs[11] = !(has_enough_space);
2278
2279 inputs[12] = (gametime > 45 * 60 * 1000);
2280 inputs[13] = !(gametime > 45 * 60 * 1000);
2281
2282 inputs[14] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy);
2283 inputs[15] = !(expansion_type.get_expansion_type() == ExpansionMode::kEconomy);
2284 inputs[16] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace);
2285 inputs[17] = !(expansion_type.get_expansion_type() == ExpansionMode::kSpace);
2286
2287 inputs[18] = (player_statistics.any_enemy_seen_lately(gametime));
2288 inputs[19] = !(player_statistics.any_enemy_seen_lately(gametime));
2289 inputs[20] = (player_statistics.get_player_power(pn) >
2290 player_statistics.get_old60_player_power(pn) +
2291 std::abs(management_data.get_military_number_at(130)) / 10);
2292 inputs[21] = !(player_statistics.get_player_power(pn) >
2293 player_statistics.get_old60_player_power(pn) +
2294 std::abs(management_data.get_military_number_at(131)) / 10);
2295 inputs[22] =
2296 (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
2297 inputs[23] =
2298 !(player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
2299 inputs[24] = (highest_nonmil_prio_ > 18 + management_data.get_military_number_at(65) / 10);
2300 inputs[25] = !(highest_nonmil_prio_ > 18 + management_data.get_military_number_at(65) / 10);
2301 inputs[26] = (player_statistics.get_modified_player_power(pn) >
2302 player_statistics.get_visible_enemies_power(pn));
2303 inputs[27] = (player_statistics.get_modified_player_power(pn) <=
2304 player_statistics.get_visible_enemies_power(pn));
2305 inputs[28] =
2306 (player_statistics.get_player_power(pn) > player_statistics.get_enemies_average_power());
2307 inputs[29] =
2308 !(player_statistics.get_player_power(pn) > player_statistics.get_enemies_average_power());
2309 inputs[30] =
2310 (player_statistics.get_player_power(pn) > player_statistics.get_enemies_max_power());
2311 inputs[31] =
2312 !(player_statistics.get_player_power(pn) > player_statistics.get_enemies_max_power());
2313
2314 inputs[32] = (persistent_data->least_military_score <
2315 persistent_data->ai_personality_mil_upper_limit *
2316 std::abs(management_data.get_military_number_at(69)) / 100);
2317 inputs[33] = !(persistent_data->least_military_score <
2318 persistent_data->ai_personality_mil_upper_limit *
2319 std::abs(management_data.get_military_number_at(69)) / 100);
2320 inputs[34] = player_statistics.strong_enough(pn);
2321 inputs[35] = !player_statistics.strong_enough(pn);
2322
2323 inputs[36] = (player_statistics.get_player_land(pn) < 500);
2324 inputs[37] = (player_statistics.get_player_land(pn) < 700);
2325 inputs[38] = (player_statistics.get_player_land(pn) < 900);
2326 inputs[39] = (player_statistics.get_player_land(pn) < 1100);
2327 inputs[40] = (player_statistics.get_player_land(pn) > 500);
2328 inputs[41] = (player_statistics.get_player_land(pn) > 700);
2329 inputs[42] = (player_statistics.get_player_land(pn) > 900);
2330 inputs[43] = (player_statistics.get_player_land(pn) > 1100);
2331 inputs[44] = (player_statistics.get_player_power(pn) >
2332 player_statistics.get_old60_player_power(pn) +
2333 std::abs(management_data.get_military_number_at(130)) / 10);
2334 inputs[45] = !(player_statistics.get_player_power(pn) >
2335 player_statistics.get_old60_player_power(pn) +
2336 std::abs(management_data.get_military_number_at(131)) / 10);
2337 inputs[46] =
2338 (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
2339 inputs[47] =
2340 !(player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn));
2341 inputs[48] = (bakeries_count_ == 0);
2342 inputs[49] = (bakeries_count_ <= 1);
2343 inputs[50] = (bakeries_count_ <= 1);
2344 inputs[51] = (numof_psites_in_constr > 8);
2345 inputs[52] = (numof_psites_in_constr < 8);
2346 inputs[53] = (mine_fields_stat.has_critical_ore_fields());
2347 inputs[54] = (!mine_fields_stat.has_critical_ore_fields());
2348 inputs[55] = (mine_fields_stat.count_types() == kMineTypes);
2349 inputs[56] = (mine_fields_stat.count_types() != kMineTypes);
2350 inputs[57] = (mine_fields_stat.has_critical_ore_fields());
2351 inputs[58] = (!mine_fields_stat.has_critical_ore_fields());
2352
2353 static int16_t needs_boost_economy_score = management_data.get_military_number_at(61) / 5;
2354 needs_boost_economy_score = management_data.get_military_number_at(61) / 5;
2355 static int16_t increase_score_limit_score = 0;
2356 increase_score_limit_score = 0;
2357
2358 for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
2359 if (management_data.f_neuron_pool[51].get_position(i)) {
2360 needs_boost_economy_score += (inputs[i]) ? 1 : -1;
2361 }
2362 if (management_data.f_neuron_pool[52].get_position(i)) {
2363 increase_score_limit_score += (inputs[i]) ? 1 : -1;
2364 }
2365 if (management_data.f_neuron_pool[21].get_position(i)) {
2366 needs_boost_economy_score += (inputs[kFNeuronBitSize + i]) ? 1 : -1;
2367 }
2368 if (management_data.f_neuron_pool[22].get_position(i)) {
2369 increase_score_limit_score += (inputs[kFNeuronBitSize + i]) ? 1 : -1;
2370 }
2371 }
2372
2373 // Finding expansion policy
2374 // Do we need basic resources? Do we have basic mines?
2375 const bool needs_fishers = resource_necessity_water_needed_ && fishers_count_ < 1;
2376
2377 if (!mine_fields_stat.has_critical_ore_fields() ||
2378 mines_per_type[iron_resource_id].total_count() < 1 || needs_fishers) {
2379 expansion_type.set_expantion_type(ExpansionMode::kResources);
2380 } else {
2381 // now we must decide if we go after spots or economy boost
2382 if (needs_boost_economy_score >= 3) {
2383 expansion_type.set_expantion_type(ExpansionMode::kEconomy);
2384 } else if (needs_boost_economy_score >= -2) {
2385 expansion_type.set_expantion_type(ExpansionMode::kBoth);
2386 } else {
2387 expansion_type.set_expantion_type(ExpansionMode::kSpace);
2388 }
2389 }
2390
2391 const bool increase_least_score_limit =
2392 (increase_score_limit_score > management_data.get_military_number_at(45) / 5);
2393
2394 static uint16_t concurent_ms_in_constr_no_enemy = 1;
2395 concurent_ms_in_constr_no_enemy = 1;
2396 static uint16_t concurent_ms_in_constr_enemy_nearby = 2;
2397 concurent_ms_in_constr_enemy_nearby = 2;
2398
2399 // resetting highest_nonmil_prio_ so it can be recalculated anew
2400 highest_nonmil_prio_ = 0;
2401
2402 if (increase_least_score_limit) {
2403 if (persistent_data->least_military_score <
2404 persistent_data
2405 ->ai_personality_mil_upper_limit) { // No sense in letting it grow too high
2406 persistent_data->least_military_score += 20;
2407 if (persistent_data->least_military_score > persistent_data->target_military_score) {
2408 persistent_data->target_military_score = persistent_data->least_military_score;
2409 }
2410 if (persistent_data->target_military_score >
2411 persistent_data->ai_personality_mil_upper_limit) {
2412 persistent_data->ai_personality_mil_upper_limit =
2413 persistent_data->target_military_score;
2414 }
2415 }
2416 } else {
2417
2418 uint16_t divider = 1; // this is to slow down decrementing the least military score
2419 switch (expansion_type.get_expansion_type()) {
2420 case ExpansionMode::kEconomy:
2421 divider = 3;
2422 break;
2423 case ExpansionMode::kBoth:
2424 divider = 2;
2425 break;
2426 default:
2427 divider = 1;
2428 }
2429
2430 // least_military_score is decreased, but depending on the size of territory
2431 switch (static_cast<uint32_t>(log10(buildable_fields.size()))) {
2432 case 0:
2433 persistent_data->least_military_score -= 10 / divider;
2434 break;
2435 case 1:
2436 persistent_data->least_military_score -= 8 / divider;
2437 break;
2438 case 2:
2439 persistent_data->least_military_score -= 5 / divider;
2440 break;
2441 case 3:
2442 persistent_data->least_military_score -= 3 / divider;
2443 break;
2444 default:
2445 persistent_data->least_military_score -= 2 / divider;
2446 }
2447 if (persistent_data->least_military_score < 0) {
2448 persistent_data->least_military_score = 0;
2449 }
2450 }
2451
2452 assert(persistent_data->least_military_score <= persistent_data->target_military_score);
2453 assert(persistent_data->target_military_score <=
2454 persistent_data->ai_personality_mil_upper_limit);
2455 persistent_data->target_military_score = 9 * persistent_data->target_military_score / 10;
2456 if (persistent_data->target_military_score < persistent_data->least_military_score) {
2457 persistent_data->target_military_score = persistent_data->least_military_score;
2458 }
2459 assert(persistent_data->target_military_score >= persistent_data->least_military_score);
2460
2461 // we must calculate wood policy
2462 const DescriptionIndex wood_index = tribe_->safe_ware_index("log");
2463 // stocked wood is to be in some propotion to productionsites and
2464 // constructionsites (this proportion is bit artifical, or we can say
2465 // it is proportion to the size of economy). Plus some positive 'margin'
2466 const int32_t stocked_wood_margin = calculate_stocklevel(wood_index) -
2467 productionsites.size() * 2 - numof_psites_in_constr +
2468 management_data.get_military_number_at(87) / 5;
2469 if (gametime < 15 * 60 * 1000) {
2470 wood_policy_ = WoodPolicy::kAllowRangers;
2471 } else if (stocked_wood_margin > 80) {
2472 wood_policy_ = WoodPolicy::kDismantleRangers;
2473 } else if (stocked_wood_margin > 25) {
2474 wood_policy_ = WoodPolicy::kStopRangers;
2475 } else {
2476 wood_policy_ = WoodPolicy::kAllowRangers;
2477 }
2478
2479 BuildingObserver* best_building = nullptr;
2480 int32_t proposed_priority = 0;
2481 Coords proposed_coords;
2482
2483 // Remove outdated fields from blocker list
2484 blocked_fields.remove_expired(gametime);
2485
2486 // testing big military buildings, whether critical construction
2487 // material is available (at least in amount of
2488 // 2/3 of default target amount)
2489 for (BuildingObserver& bo : buildings_) {
2490 if (!bo.buildable(*player_)) {
2491 continue;
2492 }
2493
2494 // not doing this for non-military buildins
2495 if (!(bo.type == BuildingObserver::Type::kMilitarysite ||
2496 bo.type == BuildingObserver::Type::kTrainingsite ||
2497 bo.type == BuildingObserver::Type::kProductionsite)) {
2498 continue;
2499 }
2500
2501 // and neither for small military buildings
2502 if (bo.type == BuildingObserver::Type::kMilitarysite &&
2503 bo.desc->get_size() == BaseImmovable::SMALL) {
2504 continue;
2505 }
2506
2507 bo.build_material_shortage = false;
2508
2509 // checking we have enough critical material on stock
2510 for (uint32_t m = 0; m < bo.critical_building_material.size(); ++m) {
2511 DescriptionIndex wt(static_cast<size_t>(bo.critical_building_material.at(m)));
2512 uint32_t treshold = 7;
2513 // generally trainingsites are more important
2514 if (bo.type == BuildingObserver::Type::kTrainingsite) {
2515 treshold = 4;
2516 }
2517
2518 if (bo.type == BuildingObserver::Type::kProductionsite) {
2519 treshold = 2;
2520 }
2521
2522 if (calculate_stocklevel(wt) <= treshold) {
2523 bo.build_material_shortage = true;
2524 break;
2525 }
2526 }
2527 }
2528
2529 // Calculating actual needness
2530 for (uint32_t j = 0; j < buildings_.size(); ++j) {
2531 BuildingObserver& bo = buildings_.at(j);
2532
2533 // we check if a previously not buildable Building of the basic economy is buildable again
2534 // If so and we don't have basic economy achieved we add readd it to basic buildings list
2535 // This should only happen in scenarios via scripting
2536 if (!basic_economy_established && bo.basic_amount > static_cast<uint32_t>(bo.total_count()) &&
2537 bo.buildable(*player_)) {
2538 persistent_data->remaining_basic_buildings.emplace(std::make_pair(bo.id, bo.basic_amount));
2539 }
2540 if (!bo.buildable(*player_)) {
2541 bo.new_building = BuildingNecessity::kNotNeeded;
2542 // This line removes buildings from basic economy if they are not allowed for the player
2543 // this should only happen by scripting.
2544 if (bo.basic_amount) {
2545 persistent_data->remaining_basic_buildings.erase(bo.id);
2546 }
2547 } else if (bo.type == BuildingObserver::Type::kProductionsite ||
2548 bo.type == BuildingObserver::Type::kMine) {
2549
2550 bo.new_building = check_building_necessity(bo, PerfEvaluation::kForConstruction, gametime);
2551
2552 if (bo.is(BuildingAttribute::kShipyard)) {
2553 assert(bo.new_building == BuildingNecessity::kAllowed ||
2554 bo.new_building == BuildingNecessity::kNeeded ||
2555 bo.new_building == BuildingNecessity::kForbidden);
2556 }
2557
2558 if (bo.new_building == BuildingNecessity::kAllowed) {
2559 bo.new_building_overdue = 0;
2560 }
2561
2562 // Now verifying that all 'buildable' buildings has positive max_needed_preciousness
2563 // if they have outputs, all other must have zero max_needed_preciousness
2564
2565 if (bo.new_building == BuildingNecessity::kForbidden) {
2566 bo.max_needed_preciousness = 0;
2567 } else if ((bo.new_building == BuildingNecessity::kNeeded ||
2568 bo.new_building == BuildingNecessity::kForced ||
2569 bo.new_building == BuildingNecessity::kAllowed ||
2570 bo.new_building == BuildingNecessity::kNeededPending) &&
2571 (!bo.ware_outputs.empty() ||
2572 bo.initial_preciousness >
2573 0)) { // bo.initial_preciousness signals that we have a worker output
2574 bo.max_needed_preciousness =
2575 std::max(bo.max_needed_preciousness, bo.initial_preciousness);
2576 bo.max_preciousness = std::max(bo.max_preciousness, bo.initial_preciousness);
2577
2578 if (bo.max_needed_preciousness <= 0) {
2579 throw wexception("AI: Max preciousness must not be <= 0 for building: %s",
2580 bo.desc->name().c_str());
2581 }
2582 } else {
2583 // For other situations we make sure max_needed_preciousness is zero
2584 assert(bo.max_needed_preciousness == 0);
2585 }
2586
2587 // Positive max_needed_preciousness says a building type is needed
2588 // here we increase or reset the counter
2589 // The counter is added to score when considering new building
2590 if (bo.max_needed_preciousness > 0) {
2591 ++bo.new_building_overdue;
2592 } else {
2593 bo.new_building_overdue = 0;
2594 }
2595
2596 // Here we consider a time how long a building needed
2597 // We calculate primary_priority used later in construct_building(),
2598 // it is basically max_needed_preciousness_ plus some 'bonus' for due time
2599 // Following scenarios are possible:
2600 // a) building is needed or forced: primary_priority grows with time
2601 // b) building is allowed: primary_priority = max_needed_preciousness (no time
2602 // consideration)
2603 // c) all other cases: primary_priority = 0;
2604 if (bo.max_needed_preciousness > 0) {
2605 if (bo.new_building == BuildingNecessity::kAllowed) {
2606 bo.primary_priority += bo.max_needed_preciousness;
2607 } else {
2608 bo.primary_priority += bo.primary_priority * bo.new_building_overdue *
2609 std::abs(management_data.get_military_number_at(120)) / 25;
2610 bo.primary_priority += bo.max_needed_preciousness +
2611 bo.max_needed_preciousness * bo.new_building_overdue *
2612 std::abs(management_data.get_military_number_at(70)) /
2613 100 +
2614 bo.new_building_overdue *
2615 std::abs(management_data.get_military_number_at(71)) / 10;
2616 if (bo.new_building == BuildingNecessity::kForced) {
2617 bo.primary_priority += bo.new_building_overdue *
2618 std::abs(management_data.get_military_number_at(119)) / 25;
2619 }
2620 }
2621 } else {
2622 bo.primary_priority = 0;
2623 }
2624
2625 } else if (bo.type == BuildingObserver::Type::kMilitarysite) {
2626 bo.new_building = check_building_necessity(bo, gametime);
2627 } else if (bo.type == BuildingObserver::Type::kTrainingsite) {
2628 bo.new_building = check_building_necessity(bo, PerfEvaluation::kForConstruction, gametime);
2629 } else if (bo.type == BuildingObserver::Type::kWarehouse) {
2630 bo.new_building = check_warehouse_necessity(bo, gametime);
2631 } else if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
2632 bo.new_building = BuildingNecessity::kNotNeeded;
2633 } else {
2634 bo.new_building = BuildingNecessity::kAllowed;
2635 bo.primary_priority = 0;
2636 }
2637
2638 const bool log_needed = (bo.new_building == BuildingNecessity::kAllowed ||
2639 bo.new_building == BuildingNecessity::kForced ||
2640 bo.new_building == BuildingNecessity::kNeeded);
2641 if (ai_training_mode_ && bo.type == BuildingObserver::Type::kProductionsite &&
2642 (gametime % 20 == 0 || log_needed)) {
2643 log("%2d: %-35s(%2d now) %-11s: max prec: %2d/%2d, primary priority: %4d, overdue: %3d\n",
2644 player_number(), bo.name, bo.total_count(), (log_needed) ? "needed" : "not needed",
2645 bo.max_needed_preciousness, bo.max_preciousness, bo.primary_priority,
2646 bo.new_building_overdue);
2647 }
2648 }
2649
2650 // first scan all buildable fields for regular buildings
2651 for (std::deque<BuildableField*>::iterator i = buildable_fields.begin();
2652 i != buildable_fields.end(); ++i) {
2653 BuildableField* const bf = *i;
2654
2655 if (bf->field_info_expiration < gametime) {
2656 continue;
2657 }
2658
2659 // Continue if field is blocked at the moment
2660 if (blocked_fields.is_blocked(bf->coords)) {
2661 continue;
2662 }
2663
2664 assert(player_);
2665 int32_t const maxsize = player_->get_buildcaps(bf->coords) & BUILDCAPS_SIZEMASK;
2666
2667 // For every field test all buildings
2668 for (BuildingObserver& bo : buildings_) {
2669 if (!bo.buildable(*player_)) {
2670 continue;
2671 }
2672
2673 if (bo.new_building == BuildingNecessity::kNotNeeded ||
2674 bo.new_building == BuildingNecessity::kNeededPending ||
2675 bo.new_building == BuildingNecessity::kForbidden) {
2676 continue;
2677 }
2678
2679 assert(bo.new_building == BuildingNecessity::kForced ||
2680 bo.new_building == BuildingNecessity::kNeeded ||
2681 bo.new_building == BuildingNecessity::kAllowed);
2682
2683 assert(bo.aimode_limit_status() == AiModeBuildings::kAnotherAllowed);
2684
2685 // if current field is not big enough
2686 if (bo.desc->get_size() > maxsize) {
2687 continue;
2688 }
2689
2690 if (std::rand() % 3 == 0 && bo.total_count() > 0) {
2691 continue;
2692 } // add randomnes and ease AI
2693
2694 if (bo.type == BuildingObserver::Type::kMine) {
2695 continue;
2696 }
2697
2698 // here we do an exemption for lumberjacks, mainly in early stages of game
2699 // sometimes the first one is not built and AI waits too long for second attempt
2700 if (gametime - bo.construction_decision_time < kBuildingMinInterval &&
2701 !bo.is(BuildingAttribute::kLumberjack)) {
2702 continue;
2703 }
2704
2705 if (!(bo.type == BuildingObserver::Type::kMilitarysite) &&
2706 bo.cnt_under_construction >= 2) {
2707 continue;
2708 }
2709
2710 int32_t prio = 0; // score of a bulding on a field
2711
2712 // testing for reserved ports
2713 if (!bo.is(BuildingAttribute::kPort)) {
2714 if (bf->portspace_nearby == ExtendedBool::kTrue) {
2715 if (num_ports == 0) {
2716 continue;
2717 }
2718 // If we have at least on port, we can perhaps build here something
2719 // but decreasing the score to discourage it
2720 prio -= 5 * std::abs(management_data.get_military_number_at(52));
2721 }
2722 }
2723
2724 if (bo.type == BuildingObserver::Type::kProductionsite) {
2725
2726 prio += management_data.neuron_pool[44].get_result_safe(bf->military_score_ / 20) / 5;
2727
2728 // Some productionsites strictly require supporting sites nearby
2729 if (bo.requires_supporters) {
2730 uint16_t supporters_nearby = 0;
2731 for (auto output : bo.ware_outputs) {
2732 supporters_nearby += bf->supporters_nearby.at(output);
2733 }
2734 if (supporters_nearby == 0) {
2735 continue;
2736 }
2737 }
2738
2739 // this can be only a well (as by now)
2740 if (bo.is(BuildingAttribute::kWell)) {
2741
2742 if (bo.new_building == BuildingNecessity::kForced) {
2743 assert(bo.total_count() - bo.unconnected_count == 0);
2744 }
2745
2746 if (bf->ground_water < 2) {
2747 continue;
2748 }
2749
2750 prio += bo.primary_priority;
2751
2752 // keep wells more distant
2753 if (bf->collecting_producers_nearby.at(bo.get_collected_map_resource()) > 2) {
2754 continue;
2755 }
2756
2757 // one well is forced
2758 if (bo.new_building == BuildingNecessity::kForced) {
2759 prio += 200;
2760 }
2761
2762 prio += -10 +
2763 std::abs(management_data.get_military_number_at(59) / 50) * bf->ground_water;
2764
2765 } else if (bo.is(BuildingAttribute::kLumberjack)) {
2766
2767 prio += bo.primary_priority;
2768
2769 if (bo.new_building == BuildingNecessity::kForced) {
2770 prio += 5 * std::abs(management_data.get_military_number_at(17));
2771 }
2772
2773 if (bf->trees_nearby < trees_nearby_treshold_ &&
2774 bo.new_building == BuildingNecessity::kAllowed) {
2775 continue;
2776 }
2777
2778 prio += std::abs(management_data.get_military_number_at(26)) *
2779 (bf->trees_nearby - trees_nearby_treshold_) / 10;
2780
2781 // consider cutters and rangers nearby
2782 prio += 2 * bf->supporters_nearby.at(bo.get_collected_map_resource()) *
2783 std::abs(management_data.get_military_number_at(25));
2784 prio -= bf->collecting_producers_nearby.at(bo.get_collected_map_resource()) *
2785 std::abs(management_data.get_military_number_at(36)) * 3;
2786
2787 } else if (bo.is(BuildingAttribute::kNeedsRocks)) {
2788
2789 // Quarries are generally to be built everywhere where rocks are
2790 // no matter the need for granite, as rocks are considered an obstacle
2791 // to expansion
2792 if (bf->rocks_nearby < 1) {
2793 continue;
2794 }
2795 prio += 2 * bf->rocks_nearby;
2796
2797 if (bf->rocks_nearby > 0 && bf->near_border) {
2798 prio += management_data.get_military_number_at(27) / 2;
2799 }
2800
2801 // value is initialized with 1 but minimal value that can be
2802 // calculated is 11
2803 if (prio <= 1) {
2804 continue;
2805 }
2806
2807 if (bo.total_count() - bo.unconnected_count == 0) {
2808 prio += 150;
2809 }
2810
2811 if (get_stocklevel(bo, gametime) == 0) {
2812 prio *= 2;
2813 }
2814
2815 // to prevent to many quaries on one spot
2816 prio =
2817 prio - 50 * bf->collecting_producers_nearby.at(bo.get_collected_map_resource());
2818
2819 } else if (bo.is(BuildingAttribute::kHunter)) {
2820
2821 if (bf->critters_nearby < 5) {
2822 continue;
2823 }
2824
2825 if (bo.new_building == BuildingNecessity::kForced) {
2826 prio += 20;
2827 }
2828
2829 // Overdue priority here
2830 prio += bo.primary_priority;
2831
2832 prio += bf->supporters_nearby.at(bo.get_collected_map_resource()) * 5;
2833
2834 prio += (bf->critters_nearby * 3) - 8 -
2835 5 * bf->collecting_producers_nearby.at(bo.get_collected_map_resource());
2836
2837 } else if (bo.is(BuildingAttribute::kFisher)) { // fisher
2838
2839 if (bf->fish_nearby <= 15) {
2840 continue;
2841 }
2842
2843 if (bo.new_building == BuildingNecessity::kForced) {
2844 prio += 200;
2845 }
2846
2847 // Overdue priority here
2848 prio += bo.primary_priority;
2849
2850 prio -= bf->collecting_producers_nearby.at(bo.get_collected_map_resource()) * 20;
2851 prio += bf->supporters_nearby.at(bo.get_collected_map_resource()) * 20;
2852
2853 prio +=
2854 -5 +
2855 bf->fish_nearby * (1 + std::abs(management_data.get_military_number_at(63) / 15));
2856 if (resource_necessity_water_needed_) {
2857 prio *= 3;
2858 }
2859
2860 } else if (!bo.production_hints.empty()) {
2861 if (bo.is(BuildingAttribute::kRanger)) {
2862 assert(bo.cnt_target > 0);
2863 }
2864
2865 prio += bo.primary_priority;
2866
2867 if (bo.is(BuildingAttribute::kRanger)) {
2868
2869 assert(bo.new_building == BuildingNecessity::kNeeded);
2870
2871 if (bo.total_count() == 0) {
2872 prio += 200;
2873 } else {
2874 prio += std::abs(management_data.get_military_number_at(66)) *
2875 (bo.cnt_target - bo.total_count());
2876 }
2877
2878 prio -= bf->water_nearby / 5;
2879
2880 for (auto ph : bo.production_hints) {
2881 assert(ph != INVALID_INDEX);
2882 prio += management_data.neuron_pool[67].get_result_safe(
2883 bf->collecting_producers_nearby.at(ph) * 5, kAbsValue) /
2884 2;
2885 }
2886
2887 prio +=
2888 management_data.neuron_pool[49].get_result_safe(bf->trees_nearby, kAbsValue) /
2889 5;
2890
2891 for (auto ph : bo.production_hints) {
2892 assert(ph != INVALID_INDEX);
2893 prio += bf->collecting_producers_nearby.at(ph) * 5 -
2894 (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) * 15 -
2895 bf->space_consumers_nearby *
2896 std::abs(management_data.get_military_number_at(102)) / 5 -
2897 bf->rocks_nearby / 3 + bf->supporters_nearby.at(ph) * 3;
2898 }
2899 // don't block port building spots with trees
2900 if (bf->unowned_portspace_vicinity_nearby > 0) {
2901 prio -= 500;
2902 }
2903 // frisian claypit and frisian farm
2904 } else if (bo.is(BuildingAttribute::kSupportingProducer)) {
2905 // we dont like trees nearby
2906 prio += 1 - bf->trees_nearby / 3;
2907 // and be far from rangers
2908 prio +=
2909 1 -
2910 bf->rangers_nearby * std::abs(management_data.get_military_number_at(102)) / 5;
2911
2912 // This is for a special case this is also supporter, it considers
2913 // producers nearby
2914 for (auto ph : bo.production_hints) {
2915 assert(ph != INVALID_INDEX);
2916 prio += management_data.neuron_pool[51].get_result_safe(
2917 bf->collecting_producers_nearby.at(ph) * 5, kAbsValue) /
2918 2;
2919 }
2920 // now we find out if the supporter is needed depending on output stocklevel
2921 // and supported stocklevel
2922 const uint32_t combined_stocklevel = (get_stocklevel(bo, gametime));
2923
2924 if (combined_stocklevel > 50 &&
2925 persistent_data->remaining_basic_buildings.count(bo.id) == 0) {
2926 continue;
2927 }
2928
2929 if (combined_stocklevel < 40) {
2930 prio += 5 *
2931 management_data.neuron_pool[23].get_result_safe(
2932 (40 - combined_stocklevel) / 2, kAbsValue);
2933 }
2934
2935 // taking into account the vicinity
2936 for (auto ph : bo.production_hints) {
2937 assert(ph != INVALID_INDEX);
2938 prio += bf->collecting_producers_nearby.at(ph) * 10;
2939 prio -= bf->supporters_nearby.at(ph) * 15;
2940 }
2941
2942 if (bf->enemy_nearby) { // not close to the enemy
2943 prio -= 20;
2944 }
2945
2946 // don't block port building spots with immovables
2947 if (bo.is(BuildingAttribute::kSpaceConsumer) &&
2948 bf->unowned_portspace_vicinity_nearby > 0) {
2949 prio -= 500;
2950 }
2951
2952 if (bo.is(BuildingAttribute::kSpaceConsumer) &&
2953 bf->water_nearby) { // not close to water
2954 prio -= std::abs(management_data.get_military_number_at(103)) / 5;
2955 }
2956
2957 if (bo.is(BuildingAttribute::kSpaceConsumer) &&
2958 bf->unowned_mines_spots_nearby) { // not close to mountains
2959 prio -= std::abs(management_data.get_military_number_at(104)) / 5;
2960 }
2961 // frisian berry farm
2962 } else if (bo.is(BuildingAttribute::kSpaceConsumer)) {
2963 // we dont like trees nearby
2964 prio += 1 - bf->trees_nearby / 4;
2965 // and be far from rangers
2966 prio +=
2967 1 -
2968 bf->rangers_nearby * std::abs(management_data.get_military_number_at(102)) / 5;
2969
2970 // now we find out if the supporter is needed depending on stocklevel
2971 const uint32_t current_stocklevel = (get_stocklevel(bo, gametime));
2972
2973 if (current_stocklevel > 50 &&
2974 persistent_data->remaining_basic_buildings.count(bo.id) == 0) {
2975 continue;
2976 }
2977
2978 if (current_stocklevel < 40) {
2979 prio += 5 *
2980 management_data.neuron_pool[23].get_result_safe(
2981 (40 - current_stocklevel) / 2, kAbsValue);
2982 }
2983 // taking into account the vicinity
2984 for (auto ph : bo.production_hints) {
2985 assert(ph != INVALID_INDEX);
2986 prio += bf->collecting_producers_nearby.at(ph) * 10;
2987 prio -= bf->supporters_nearby.at(ph) * 8;
2988 }
2989
2990 if (bf->enemy_nearby) { // not close to the enemy
2991 prio -= 20;
2992 }
2993
2994 // don't block port building spots with immovables
2995 if (bf->unowned_portspace_vicinity_nearby > 0) {
2996 prio -= 500;
2997 }
2998
2999 if (bf->water_nearby) { // not close to water
3000 prio -= std::abs(management_data.get_military_number_at(103)) / 5;
3001 }
3002
3003 if (bf->unowned_mines_spots_nearby) { // not close to mountains
3004 prio -= std::abs(management_data.get_military_number_at(104)) / 5;
3005 }
3006
3007 } else { // FISH BREEDERS and GAME KEEPERS
3008
3009 // especially for fish breeders
3010 if (bo.is(BuildingAttribute::kNeedsCoast) &&
3011 (bf->water_nearby < 6 || bf->fish_nearby < 6)) {
3012 continue;
3013 }
3014 if (bo.is(BuildingAttribute::kNeedsCoast)) {
3015 prio += (-6 + bf->water_nearby) / 3;
3016 prio += (-6 + bf->fish_nearby) / 3;
3017 }
3018
3019 const uint32_t current_stocklevel = (get_stocklevel(bo, gametime));
3020
3021 if (current_stocklevel > 50 &&
3022 persistent_data->remaining_basic_buildings.count(bo.id) == 0) {
3023 continue;
3024 }
3025
3026 if (current_stocklevel < 40) {
3027 prio += 5 *
3028 management_data.neuron_pool[23].get_result_safe(
3029 (40 - current_stocklevel) / 2, kAbsValue);
3030 }
3031
3032 for (auto ph : bo.production_hints) {
3033 assert(ph != INVALID_INDEX);
3034 prio += bf->collecting_producers_nearby.at(ph) * 10;
3035 prio -= bf->supporters_nearby.at(ph) * 20;
3036 }
3037
3038 if (bf->enemy_nearby) {
3039 prio -= 20;
3040 }
3041
3042 if (bf->unowned_portspace_vicinity_nearby > 0) {
3043 prio -= 500;
3044 }
3045 }
3046
3047 } else if (bo.is(BuildingAttribute::kRecruitment)) {
3048 prio += bo.primary_priority;
3049 prio -= bf->unowned_land_nearby * 2;
3050 prio -= (bf->enemy_nearby) * 100;
3051 prio -= (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) * 100;
3052 } else { // finally normal productionsites
3053 assert(bo.production_hints.empty());
3054
3055 if (bo.new_building == BuildingNecessity::kForced) {
3056 prio += 150;
3057 assert(!bo.is(BuildingAttribute::kShipyard));
3058 } else if (bo.is(BuildingAttribute::kShipyard)) {
3059 if (!map_allows_seafaring_) {
3060 continue;
3061 }
3062 } else {
3063 assert(bo.new_building == BuildingNecessity::kNeeded);
3064 }
3065
3066 // Overdue priority here
3067 prio += bo.primary_priority;
3068
3069 // we check separatelly buildings with no inputs and some inputs
3070 if (bo.inputs.empty()) {
3071
3072 assert(!bo.is(BuildingAttribute::kShipyard));
3073
3074 if (bo.is(BuildingAttribute::kSpaceConsumer)) { // e.g. farms
3075 // we dont like trees nearby
3076 prio += 1 - bf->trees_nearby / 4;
3077 // we attempt to cluster space consumers together
3078 prio += bf->space_consumers_nearby * 2;
3079 // and be far from rangers
3080 prio += 1 -
3081 bf->rangers_nearby *
3082 std::abs(management_data.get_military_number_at(102)) / 5;
3083 } else {
3084 // leave some free space between them
3085 prio -= bf->collecting_producers_nearby.at(bo.get_collected_map_resource()) *
3086 std::abs(management_data.get_military_number_at(108)) / 5;
3087 }
3088
3089 if (bo.is(BuildingAttribute::kSpaceConsumer) &&
3090 bf->water_nearby) { // not close to water
3091 prio -= std::abs(management_data.get_military_number_at(103)) / 5;
3092 }
3093
3094 if (bo.is(BuildingAttribute::kSpaceConsumer) &&
3095 bf->unowned_mines_spots_nearby) { // not close to mountains
3096 prio -= std::abs(management_data.get_military_number_at(104)) / 5;
3097 }
3098 if (bo.is(BuildingAttribute::kSpaceConsumer) &&
3099 bf->unowned_portspace_vicinity_nearby > 0) { // do not block Ports
3100 prio -= 500;
3101 }
3102 if (bo.is(BuildingAttribute::kNeedsBerry)) {
3103 prio += std::abs(management_data.get_military_number_at(13)) *
3104 bf->bushes_nearby / 12;
3105 }
3106 } else if (bo.is(BuildingAttribute::kShipyard)) {
3107 // for now AI builds only one shipyard
3108 assert(bo.total_count() == 0);
3109 if (bf->open_water_nearby > 3 && map_allows_seafaring_) {
3110 prio += productionsites.size() * 5 +
3111 bf->open_water_nearby *
3112 std::abs(management_data.get_military_number_at(109)) / 10;
3113 } else {
3114 continue;
3115 }
3116 }
3117
3118 // considering neededness depending on stocklevel
3119 const uint32_t current_stocklevel = (get_stocklevel(bo, gametime));
3120 if (current_stocklevel > 50 &&
3121 persistent_data->remaining_basic_buildings.count(bo.id) == 0) {
3122 continue;
3123 }
3124 if (current_stocklevel < 40) {
3125 prio += 5 *
3126 management_data.neuron_pool[23].get_result_safe(
3127 (40 - current_stocklevel) / 2, kAbsValue);
3128 }
3129 // This considers supporters nearby
3130 for (auto ph : bo.ware_outputs) {
3131 prio += management_data.neuron_pool[52].get_result_safe(
3132 bf->supporters_nearby.at(ph) * 5, kAbsValue) /
3133 2;
3134 }
3135
3136 if (prio <= 0) {
3137 continue;
3138 }
3139
3140 // bonus for big buildings if shortage of big fields
3141 if (spots_avail.at(BUILDCAPS_BIG) <= 5 && bo.desc->get_size() == 3) {
3142 prio += std::abs(management_data.get_military_number_at(105)) / 5;
3143 }
3144
3145 if (spots_avail.at(BUILDCAPS_MEDIUM) <= 5 && bo.desc->get_size() == 2) {
3146 prio += std::abs(management_data.get_military_number_at(106)) / 5;
3147 }
3148
3149 // +1 if any consumers_ are nearby
3150 consumers_nearby_count = 0;
3151
3152 for (size_t k = 0; k < bo.ware_outputs.size(); ++k) {
3153 consumers_nearby_count += bf->consumers_nearby.at(bo.ware_outputs.at(k));
3154 }
3155
3156 if (consumers_nearby_count > 0) {
3157 prio += std::abs(management_data.get_military_number_at(107)) / 3;
3158 }
3159 }
3160
3161 // Consider border with exemption of some huts
3162 if (!(bo.is(BuildingAttribute::kLumberjack) || bo.is(BuildingAttribute::kNeedsCoast) ||
3163 bo.is(BuildingAttribute::kFisher))) {
3164 prio += recalc_with_border_range(*bf, prio);
3165 } else if (bf->near_border && (bo.is(BuildingAttribute::kLumberjack) ||
3166 bo.is(BuildingAttribute::kNeedsCoast))) {
3167 prio /= 2;
3168 }
3169
3170 } // production sites done
3171 else if (bo.type == BuildingObserver::Type::kMilitarysite) {
3172
3173 prio += bo.primary_priority;
3174
3175 // Two possibilities why to construct militarysite here
3176 if (!bf->defense_msite_allowed &&
3177 (bf->nearest_buildable_spot_nearby < bo.desc->get_conquers() ||
3178 bf->unowned_portspace_vicinity_nearby > 0) &&
3179 (bf->military_in_constr_nearby + bf->military_unstationed) <
3180 concurent_ms_in_constr_no_enemy) {
3181 // it will conquer new buildable spots for buildings or mines
3182 } else if (bf->defense_msite_allowed &&
3183 (bf->military_in_constr_nearby + bf->military_unstationed) <
3184 concurent_ms_in_constr_enemy_nearby) {
3185 // we need it to increase capacity on the field
3186 if (bo.fighting_type) {
3187 prio += 5;
3188 }
3189 } else {
3190 continue;
3191 }
3192 if (bf->unowned_mines_spots_nearby > 2 && bo.mountain_conqueror) {
3193 prio += 5;
3194 }
3195 prio += std::abs(management_data.get_military_number_at(35)) / 5 *
3196 (static_cast<int16_t>(bo.desc->get_conquers()) -
3197 static_cast<int16_t>(bf->nearest_buildable_spot_nearby));
3198
3199 prio += bf->military_score_;
3200
3201 // if place+building is not good enough
3202 if (prio <= persistent_data->target_military_score) {
3203 continue;
3204 }
3205 if (prio > persistent_data->ai_personality_mil_upper_limit) {
3206 persistent_data->ai_personality_mil_upper_limit = prio;
3207 }
3208 } else if (bo.type == BuildingObserver::Type::kWarehouse) {
3209
3210 // exclude spots on border
3211 if (bf->near_border && !bo.is(BuildingAttribute::kPort)) {
3212 continue;
3213 }
3214 assert(bf->is_portspace != ExtendedBool::kUnset);
3215 if (bf->is_portspace != ExtendedBool::kTrue && bo.is(BuildingAttribute::kPort)) {
3216 continue;
3217 }
3218 prio += bo.primary_priority;
3219
3220 // iterating over current warehouses and testing a distance
3221 // getting distance to nearest warehouse and adding it to a score
3222 uint16_t nearest_distance = std::numeric_limits<uint16_t>::max();
3223 for (const WarehouseSiteObserver& wh_obs : warehousesites) {
3224 const uint16_t actual_distance =
3225 map.calc_distance(bf->coords, wh_obs.site->get_position());
3226 nearest_distance = std::min(nearest_distance, actual_distance);
3227 }
3228 // but limit to 30
3229 const uint16_t max_distance_considered = 30;
3230 nearest_distance = std::min(nearest_distance, max_distance_considered);
3231 if (nearest_distance < 13) {
3232 continue;
3233 }
3234 prio +=
3235 management_data.neuron_pool[47].get_result_safe(nearest_distance / 2, kAbsValue) / 2;
3236
3237 prio += bf->own_non_military_nearby * 3;
3238
3239 // dont be close to enemies
3240 if (bf->enemy_nearby) {
3241 prio -= 40;
3242 }
3243
3244 // being too close to a border is not good either
3245 if ((bf->unowned_land_nearby || bf->enemy_owned_land_nearby > 10) &&
3246 !bo.is(BuildingAttribute::kPort) && prio > 0) {
3247 prio /= 2;
3248 prio -= 10;
3249 }
3250
3251 } else if (bo.type == BuildingObserver::Type::kTrainingsite) {
3252
3253 // Even if a site is forced it has kNeeded necessity now
3254 assert(bo.primary_priority > 0 && bo.new_building == BuildingNecessity::kNeeded);
3255
3256 prio += bo.primary_priority;
3257
3258 // for spots close to a border
3259 if (bf->near_border) {
3260 prio -= 5;
3261 }
3262
3263 // take care about borders and enemies
3264 if (bf->enemy_nearby) {
3265 prio -= 20;
3266 }
3267
3268 if (bf->unowned_land_nearby || bf->enemy_owned_land_nearby) {
3269 prio -= 15;
3270 }
3271 }
3272
3273 // think of space consuming buildings nearby like farms or vineyards
3274 if (bo.type != BuildingObserver::Type::kMilitarysite) {
3275 prio -= bf->space_consumers_nearby * 10;
3276 }
3277
3278 // Stop here, if priority is 0 or less.
3279 if (prio <= 0) {
3280 continue;
3281 }
3282
3283 // Prefer road side fields
3284 prio += bf->preferred ? 5 : 0;
3285
3286 // don't waste good land for small huts
3287 const bool space_stress =
3288 (spots_avail.at(BUILDCAPS_MEDIUM) < 5 || spots_avail.at(BUILDCAPS_BIG) < 5);
3289
3290 if (space_stress && bo.type == BuildingObserver::Type::kMilitarysite) {
3291 prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 10;
3292 } else if (space_stress) {
3293 prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 30;
3294 } else {
3295 prio -= (bf->max_buildcap_nearby - bo.desc->get_size()) * 5;
3296 }
3297
3298 // prefer vicinity of ports (with exemption of warehouses)
3299 if (bf->port_nearby && bo.type == BuildingObserver::Type::kMilitarysite) {
3300 prio *= 2;
3301 }
3302
3303 if (bo.type != BuildingObserver::Type::kMilitarysite && highest_nonmil_prio_ < prio) {
3304 highest_nonmil_prio_ = prio;
3305 }
3306
3307 if (bo.type == BuildingObserver::Type::kMilitarysite) {
3308 if (prio <= persistent_data->target_military_score) {
3309 continue;
3310 }
3311 }
3312
3313 if (prio > proposed_priority) {
3314 best_building = &bo;
3315 proposed_priority = prio;
3316 proposed_coords = bf->coords;
3317 }
3318 } // ending loop over buildings
3319 } // ending loop over fields
3320
3321 // then try all mines_ - as soon as basic economy is build up.
3322 if (gametime > next_mine_construction_due_) {
3323
3324 // not done here
3325 // update_all_mineable_fields(gametime);
3326 next_mine_construction_due_ = gametime + kNewMineConstInterval;
3327
3328 if (!mineable_fields.empty()) {
3329
3330 for (BuildingObserver& bo : buildings_) {
3331 if (productionsites.size() <= 8) {
3332 break;
3333 }
3334
3335 if (bo.type != BuildingObserver::Type::kMine) {
3336 continue;
3337 }
3338
3339 assert(bo.new_building != BuildingNecessity::kAllowed);
3340
3341 // skip if a mine is not required
3342 if (!(bo.new_building == BuildingNecessity::kNeeded ||
3343 bo.new_building == BuildingNecessity::kForced)) {
3344 continue;
3345 }
3346
3347 // iterating over fields
3348 for (std::deque<MineableField*>::iterator j = mineable_fields.begin();
3349 j != mineable_fields.end(); ++j) {
3350
3351 MineableField* const mf = *j;
3352
3353 if (mf->field_info_expiration <= gametime) {
3354 continue;
3355 }
3356
3357 if (mf->coords.field->get_resources() != bo.mines) {
3358 continue;
3359 }
3360
3361 // Continue if field is blocked at the moment
3362 if (blocked_fields.is_blocked(mf->coords)) {
3363 continue;
3364 }
3365
3366 int32_t prio = 0;
3367 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(mf->coords, 2));
3368 do {
3369 if (bo.mines == mr.location().field->get_resources()) {
3370 prio += mr.location().field->get_resources_amount();
3371 }
3372 } while (mr.advance(map));
3373
3374 prio /= 10;
3375
3376 // Only build mines_ on locations where some material can be mined
3377 if (prio < 1) {
3378 continue;
3379 }
3380
3381 // applying nearnest penalty
3382 prio -= mf->mines_nearby * std::abs(management_data.get_military_number_at(126));
3383
3384 // applying max needed
3385 prio += bo.primary_priority;
3386
3387 // prefer mines in the middle of mine fields of the
3388 // same type, so we add a small bonus here
3389 // depending on count of same mines nearby,
3390 // though this does not reflects how many resources
3391 // are (left) in nearby mines
3392 prio += mf->same_mine_fields_nearby;
3393
3394 // Continue if field is blocked at the moment
3395 if (blocked_fields.is_blocked(mf->coords)) {
3396 continue;
3397 }
3398
3399 // Prefer road side fields
3400 prio += mf->preferred ? 1 : 0;
3401
3402 prio += bo.primary_priority;
3403
3404 if (prio > proposed_priority) {
3405 best_building = &bo;
3406 proposed_priority = prio;
3407 proposed_coords = mf->coords;
3408 mine = true;
3409 }
3410
3411 if (prio > highest_nonmil_prio_) {
3412 highest_nonmil_prio_ = prio;
3413 }
3414 } // end of evaluation of field
3415 }
3416
3417 } // section if mine size >0
3418 } // end of mines_ section
3419
3420 // if there is no winner:
3421 if (best_building == nullptr) {
3422 return false;
3423 }
3424
3425 if (best_building->type == BuildingObserver::Type::kMilitarysite) {
3426 assert(proposed_priority >= persistent_data->least_military_score);
3427 persistent_data->target_military_score = proposed_priority;
3428 if (persistent_data->target_military_score >
3429 persistent_data->ai_personality_mil_upper_limit) {
3430 persistent_data->ai_personality_mil_upper_limit = persistent_data->target_military_score;
3431 }
3432 assert(proposed_priority >= persistent_data->least_military_score);
3433 }
3434
3435 // send the command to construct a new building
3436 game().send_player_build(player_number(), proposed_coords, best_building->id);
3437 blocked_fields.add(proposed_coords, game().get_gametime() + 2 * 60 * 1000);
3438
3439 // resetting new_building_overdue
3440 best_building->new_building_overdue = 0;
3441
3442 // we block also nearby fields
3443 // if farms and so on, for quite a long time
3444 // if military sites only for short time for AI can update information on near buildable fields
3445 if ((best_building->is(BuildingAttribute::kSpaceConsumer) &&
3446 !best_building->is(BuildingAttribute::kRanger)) ||
3447 best_building->type == BuildingObserver::Type::kMilitarysite) {
3448 uint32_t block_time = 0;
3449 uint32_t block_area = 0;
3450 if (best_building->is(BuildingAttribute::kSpaceConsumer)) {
3451 if (spots_ > kSpotsEnough) {
3452 block_time = 45 * 60 * 1000;
3453 } else {
3454 block_time = 15 * 60 * 1000;
3455 }
3456 block_area = 5;
3457 } else { // militray buildings for a very short time
3458 block_time = 25 * 1000;
3459 block_area = 6;
3460 }
3461
3462 MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(proposed_coords), block_area));
3463 do {
3464 blocked_fields.add(mr.location(), game().get_gametime() + block_time);
3465 } while (mr.advance(map));
3466 }
3467
3468 if (best_building->is(BuildingAttribute::kRecruitment)) {
3469 log("%2d: Building a recruitment site: %s\n", player_number(), best_building->name);
3470 }
3471
3472 if (!(best_building->type == BuildingObserver::Type::kMilitarysite)) {
3473 best_building->construction_decision_time = gametime;
3474 } else {
3475 military_last_build_ = gametime;
3476 best_building->construction_decision_time = gametime - kBuildingMinInterval / 2;
3477 }
3478
3479 // set the type of update that is needed
3480 if (mine) {
3481 next_mine_construction_due_ = gametime + kBusyMineUpdateInterval;
3482 }
3483
3484 return true;
3485 }
3486
3487 // Re-calculating warehouse to flag distances
check_flag_distances(const uint32_t gametime)3488 void DefaultAI::check_flag_distances(const uint32_t gametime) {
3489 for (WarehouseSiteObserver& wh_obs : warehousesites) {
3490 uint16_t checked_flags = 0;
3491 const uint32_t this_wh_hash = wh_obs.site->get_position().hash();
3492 uint32_t highest_distance_set = 0;
3493
3494 std::queue<Widelands::Flag*>
3495 remaining_flags; // only used to collect flags reachable walk over roads
3496 remaining_flags.push(&wh_obs.site->base_flag());
3497 flag_warehouse_distance.set_distance(
3498 wh_obs.site->base_flag().get_position().hash(), 0, gametime, this_wh_hash);
3499 uint32_t tmp_wh;
3500 assert(flag_warehouse_distance.get_distance(
3501 wh_obs.site->base_flag().get_position().hash(), gametime, &tmp_wh) == 0);
3502
3503 // Algorithm to walk on roads
3504 // All nodes are marked as to_be_checked == true first and once the node is checked it is
3505 // changed to false. Under some conditions, the same node can be checked twice, the
3506 // to_be_checked can be set back to true. Because less hoops (fewer flag-to-flag roads) does
3507 // not always mean shortest road.
3508 while (!remaining_flags.empty()) {
3509 ++checked_flags;
3510 // looking for a node with shortest existing road distance from starting flag and one that
3511 // has to be checked Now going over roads leading from this flag
3512 const uint16_t current_flag_distance = flag_warehouse_distance.get_distance(
3513 remaining_flags.front()->get_position().hash(), gametime, &tmp_wh);
3514 for (uint8_t i = WalkingDir::FIRST_DIRECTION; i <= WalkingDir::LAST_DIRECTION; ++i) {
3515 Road* const road = remaining_flags.front()->get_road(i);
3516
3517 if (!road) {
3518 continue;
3519 }
3520
3521 Flag* endflag = &road->get_flag(Road::FlagStart);
3522
3523 if (endflag == remaining_flags.front()) {
3524 endflag = &road->get_flag(Road::FlagEnd);
3525 }
3526 const uint16_t steps_count = road->get_path().get_nsteps();
3527
3528 // Calculated distance can be used or ignored if f.e. longer than via other route
3529 bool const updated = flag_warehouse_distance.set_distance(
3530 endflag->get_position().hash(), current_flag_distance + steps_count, gametime,
3531 this_wh_hash);
3532
3533 if (highest_distance_set < current_flag_distance + steps_count) {
3534 highest_distance_set = current_flag_distance + steps_count;
3535 }
3536
3537 if (updated) {
3538 remaining_flags.push(endflag);
3539 }
3540 }
3541 remaining_flags.pop();
3542 }
3543 }
3544
3545 // Now let do some lazy pruning - remove the flags that were not updated for long
3546 flag_warehouse_distance.remove_old_flag(gametime);
3547 }
3548
3549 // Here we pick about 25 roads and investigate them. If it is a dead end we dismantle it
dismantle_dead_ends()3550 bool DefaultAI::dismantle_dead_ends() {
3551 bool road_dismantled = false;
3552 const uint16_t stepping = roads.size() / 25 + 1;
3553
3554 for (uint16_t i = 0; i < roads.size(); i += stepping) {
3555 const Flag& roadstartflag = roads[i]->get_flag(RoadBase::FlagStart);
3556 const Flag& roadendflag = roads[i]->get_flag(RoadBase::FlagEnd);
3557
3558 if (!roadstartflag.get_building() && roadstartflag.is_dead_end()) {
3559 game().send_player_bulldoze(*const_cast<Flag*>(&roadstartflag));
3560 road_dismantled = true;
3561 }
3562
3563 if (!roadendflag.get_building() && roadendflag.is_dead_end()) {
3564 game().send_player_bulldoze(*const_cast<Flag*>(&roadendflag));
3565 road_dismantled = true;
3566 }
3567 }
3568 return road_dismantled;
3569 }
3570
3571 // improves current road system
improve_roads(uint32_t gametime)3572 bool DefaultAI::improve_roads(uint32_t gametime) {
3573
3574 // First try to dismantle some dead ends on road
3575 if (dead_ends_check_ || gametime % 50 == 0) {
3576 if (dismantle_dead_ends()) {
3577 return true;
3578 }
3579 dead_ends_check_ = false;
3580 return false;
3581 }
3582
3583 if (!roads.empty()) {
3584 const Path& path = roads.front()->get_path();
3585
3586 // first force a split on roads that are longer than 3 parts
3587 if (path.get_nsteps() > 3 && spots_ > kSpotsEnough) {
3588 const Map& map = game().map();
3589 CoordPath cp(map, path);
3590 // try to split after two steps
3591 CoordPath::StepVector::size_type i = cp.get_nsteps() - 1, j = 1;
3592
3593 for (; i >= j; --i, ++j) {
3594 {
3595 const Coords c = cp.get_coords().at(i);
3596
3597 if (map[c].nodecaps() & BUILDCAPS_FLAG) {
3598 game().send_player_build_flag(player_number(), c);
3599 return true;
3600 }
3601 }
3602 {
3603 const Coords c = cp.get_coords().at(j);
3604
3605 if (map[c].nodecaps() & BUILDCAPS_FLAG) {
3606 game().send_player_build_flag(player_number(), c);
3607 return true;
3608 }
3609 }
3610 }
3611
3612 // Unable to set a flag - perhaps the road was build stupid
3613 game().send_player_bulldoze(*const_cast<Road*>(roads.front()));
3614 dead_ends_check_ = true;
3615 return true;
3616 }
3617
3618 roads.push_back(roads.front());
3619 roads.pop_front();
3620
3621 // Occasionaly (not more then once in 15 seconds) we test if the road can be dismantled
3622 // if there is shortage of spots we do it always
3623 if (last_road_dismantled_ + 15 * 1000 < gametime &&
3624 (gametime % 40 == 0 || spots_ <= kSpotsEnough)) {
3625 const Road& road = *roads.front();
3626 if (dispensable_road_test(*const_cast<Road*>(&road))) {
3627 game().send_player_bulldoze(*const_cast<Road*>(&road));
3628 last_road_dismantled_ = gametime;
3629 dead_ends_check_ = true;
3630 return true;
3631 }
3632 }
3633 }
3634 // now we rotate economies and flags to get one flag to go on with
3635 if (economies.empty()) {
3636 check_economies();
3637 return false;
3638 }
3639
3640 if (economies.size() >= 2) { // rotating economies
3641 economies.push_back(economies.front());
3642 economies.pop_front();
3643 }
3644
3645 EconomyObserver* eco = economies.front();
3646 if (eco->flags.empty()) {
3647 check_economies();
3648 return false;
3649 }
3650 if (eco->flags.size() > 1) {
3651 eco->flags.push_back(eco->flags.front());
3652 eco->flags.pop_front();
3653 }
3654
3655 const Flag& flag = *eco->flags.front();
3656
3657 // now we test if it is dead end flag, if yes, destroying it
3658 if (flag.is_dead_end() && flag.current_wares() == 0) {
3659 game().send_player_bulldoze(*const_cast<Flag*>(&flag));
3660 eco->flags.pop_front();
3661 return true;
3662 }
3663
3664 bool is_warehouse = false;
3665 if (Building* b = flag.get_building()) {
3666 BuildingObserver& bo = get_building_observer(b->descr().name().c_str());
3667 if (bo.type == BuildingObserver::Type::kWarehouse) {
3668 is_warehouse = true;
3669 }
3670 }
3671 const uint32_t flag_coords_hash = flag.get_position().hash();
3672
3673 if (flag_warehouse_distance.is_road_prohibited(flag_coords_hash, gametime)) {
3674 return false;
3675 }
3676 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
3677 // ferries
3678 const bool needs_warehouse = flag.get_economy(wwWORKER)->warehouses().empty();
3679
3680 uint32_t tmp_wh;
3681
3682 // when deciding if we attempt to build a road from here we use probability
3683 uint16_t probability_score = 0;
3684 if (flag.nr_of_roads() == 1) {
3685 probability_score += 20;
3686 }
3687 if (is_warehouse && flag.nr_of_roads() <= 3) {
3688 probability_score += 20;
3689 }
3690 probability_score += flag.current_wares() * 5;
3691 if (needs_warehouse) {
3692 probability_score += 500;
3693 }
3694 if (std::rand() % 10 == 0) {
3695 probability_score +=
3696 flag_warehouse_distance.get_distance(flag_coords_hash, gametime, &tmp_wh);
3697 }
3698
3699 if (std::rand() % 200 < probability_score) {
3700 create_shortcut_road(flag, 14, gametime);
3701 return true;
3702 }
3703
3704 return false;
3705 }
3706
3707 // This function takes a road (road is smallest section of roads with two flags on the ends)
3708 // look for longer section of road that starts and ends with building/road crossing
3709 // and tries to find alternative route from one end flag to another.
3710 // If route exists, it is not too long, and current road is not intensively used
3711 // the road can be dismantled
dispensable_road_test(const Widelands::Road & road)3712 bool DefaultAI::dispensable_road_test(const Widelands::Road& road) {
3713
3714 Flag& roadstartflag = road.get_flag(RoadBase::FlagStart);
3715 Flag& roadendflag = road.get_flag(RoadBase::FlagEnd);
3716
3717 // Calculating full road (from crossing/building to another crossing/building),
3718 // this means we calculate vector of all flags of the "full road"
3719 std::vector<Widelands::Flag*> full_road;
3720 full_road.push_back(&roadstartflag);
3721 full_road.push_back(&roadendflag);
3722
3723 uint16_t road_length = road.get_path().get_nsteps();
3724
3725 for (int j = 0; j < 2; ++j) {
3726 bool new_road_found = true;
3727 while (new_road_found && full_road.back()->nr_of_roads() <= 2 &&
3728 full_road.back()->get_building() == nullptr) {
3729 new_road_found = false;
3730 for (uint8_t i = WalkingDir::FIRST_DIRECTION; i <= WalkingDir::LAST_DIRECTION; ++i) {
3731 Road* const near_road = full_road.back()->get_road(i);
3732
3733 if (!near_road) {
3734 continue;
3735 }
3736
3737 Flag* other_end;
3738 if (near_road->get_flag(RoadBase::FlagStart).get_position().hash() ==
3739 full_road.back()->get_position().hash()) {
3740 other_end = &near_road->get_flag(RoadBase::FlagEnd);
3741 } else {
3742 other_end = &near_road->get_flag(RoadBase::FlagStart);
3743 }
3744
3745 // Have we already the end of road in our full_road?
3746 if (std::find(full_road.begin(), full_road.end(), other_end) == full_road.end()) {
3747 full_road.push_back(other_end);
3748 road_length += near_road->get_path().get_nsteps();
3749 new_road_found = true;
3750 break;
3751 }
3752 }
3753 }
3754 // we walked to one end, now let revert the content of full_road and repeat in opposite
3755 // direction
3756 std::reverse(full_road.begin(), full_road.end());
3757 }
3758
3759 // To make decision how intensively the road is used, we count wares on it, but we distinguish
3760 // situation when entire road has only 2 flags or is longer
3761 uint16_t wares_on_road = 0;
3762 assert(full_road.size() > 1);
3763 if (full_road.size() == 2) {
3764 wares_on_road = roadstartflag.current_wares() + roadendflag.current_wares();
3765 } else {
3766 // We count wares only on inner flags
3767 for (uint16_t k = 1; k < full_road.size() - 1; ++k) {
3768 wares_on_road += full_road[k]->current_wares();
3769 }
3770 }
3771
3772 // If it by chance starts or ends next to a warehouse...
3773 if (Building* b = full_road.front()->get_building()) {
3774 BuildingObserver& bo = get_building_observer(b->descr().name().c_str());
3775 if (bo.type == BuildingObserver::Type::kWarehouse) {
3776 return false;
3777 }
3778 }
3779 if (Building* b = full_road.back()->get_building()) {
3780 BuildingObserver& bo = get_building_observer(b->descr().name().c_str());
3781 if (bo.type == BuildingObserver::Type::kWarehouse) {
3782 return false;
3783 }
3784 }
3785
3786 if (spots_ > kSpotsEnough && wares_on_road > 5) {
3787 return false;
3788 } else if (wares_on_road > 8) {
3789 return false;
3790 }
3791
3792 std::priority_queue<NearFlag> queue;
3793 // only used to collect flags reachable walking over roads
3794 std::vector<NearFlag> reachableflags;
3795
3796 queue.push(NearFlag(full_road.front(), 0));
3797 uint16_t alternative_path = std::numeric_limits<uint16_t>::max();
3798 const uint8_t checkradius = 3 *
3799 game().map().calc_distance(full_road.front()->get_position(),
3800 full_road.back()->get_position());
3801
3802 // algorithm to walk on roads
3803 while (!queue.empty()) {
3804
3805 // Testing if we stand on the roadendflag... if yes, the alternative path is found, no reason
3806 // to go on
3807 if (full_road.back()->get_position().x == queue.top().flag->get_position().x &&
3808 full_road.back()->get_position().y == queue.top().flag->get_position().y) {
3809 alternative_path = queue.top().current_road_distance;
3810 break;
3811 }
3812
3813 // If we were here, do not evaluate the flag again
3814 std::vector<NearFlag>::iterator f =
3815 find(reachableflags.begin(), reachableflags.end(), queue.top().flag);
3816 if (f != reachableflags.end()) {
3817 queue.pop();
3818 continue;
3819 }
3820
3821 reachableflags.push_back(queue.top());
3822 queue.pop();
3823 NearFlag& nf = reachableflags.back();
3824
3825 // Now go over roads going from this flag
3826 for (uint8_t i = WalkingDir::FIRST_DIRECTION; i <= WalkingDir::LAST_DIRECTION; ++i) {
3827 Road* const near_road = nf.flag->get_road(i);
3828
3829 if (!near_road) {
3830 continue;
3831 }
3832
3833 // alternate road cannot lead via road to be dismantled
3834 if (near_road->serial() == road.serial()) {
3835 continue;
3836 }
3837
3838 Flag* endflag = &near_road->get_flag(RoadBase::FlagStart);
3839
3840 if (endflag == nf.flag) {
3841 endflag = &near_road->get_flag(RoadBase::FlagEnd);
3842 }
3843
3844 // When walking on nearby roads, we do not go too far from start and end of road
3845 const int32_t dist1 =
3846 game().map().calc_distance(full_road.front()->get_position(), endflag->get_position());
3847 const int32_t dist2 =
3848 game().map().calc_distance(full_road.back()->get_position(), endflag->get_position());
3849 if ((dist1 + dist2) > checkradius) {
3850 continue;
3851 }
3852
3853 const uint32_t new_length = nf.current_road_distance + near_road->get_path().get_nsteps();
3854 queue.push(NearFlag(endflag, new_length));
3855 }
3856 }
3857
3858 if (alternative_path + wares_on_road <= road_length + 12) {
3859 return true;
3860 }
3861
3862 return false;
3863 }
3864
3865 // Trying to connect the flag to another one, be it from own economy
3866 // or other economy
3867 // The procedure is:
3868 // - Collect all flags within checkradius into RoadCandidates, but first we dont even know if a road
3869 // can be built to them
3870 // - Walking over road network to collect info on flags that are accessible over road network
3871 // - Then merge info from NearFlags to RoadCandidates and consider roads to few best candidates from
3872 // RoadCandidates. We use score named "reduction" that is basically diff between connection over
3873 // existing roads minus possible road from starting flag to candidate flag. Of course there are two
3874 // special cases:
3875 // - the candidate flag does not belong to the same economy, so no road connection exists
3876 // - they are from same economy, but are connected beyond range of checkradius, so actual length of
3877 // connection is not known
create_shortcut_road(const Flag & flag,uint16_t checkradius,uint32_t gametime)3878 bool DefaultAI::create_shortcut_road(const Flag& flag, uint16_t checkradius, uint32_t gametime) {
3879
3880 // Increasing the failed_connection_tries counter
3881 // At the same time it indicates a time an economy is without a warehouse
3882 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
3883 // ferries
3884 EconomyObserver* eco = get_economy_observer(flag.economy(wwWORKER));
3885 // if we passed grace time this will be last attempt and if it fails
3886 // building is destroyed
3887 bool last_attempt_ = false;
3888
3889 // this should not happen, but if the economy has a warehouse and a dismantle
3890 // grace time set, we must 'zero' the dismantle grace time
3891 if (!flag.get_economy(wwWORKER)->warehouses().empty() && eco->dismantle_grace_time != kNever) {
3892 eco->dismantle_grace_time = kNever;
3893 }
3894
3895 // first we deal with situations when this is economy with no warehouses
3896 // and this is a flag belonging to a building/constructionsite
3897 // such economy must get dismantle grace time (if not set yet)
3898 // end sometimes extended checkradius
3899 if (flag.get_economy(wwWORKER)->warehouses().empty() && flag.get_building()) {
3900
3901 // occupied military buildings get special treatment
3902 // (extended grace time + longer radius)
3903 bool occupied_military_ = false;
3904 Building* b = flag.get_building();
3905 if (upcast(MilitarySite, militb, b)) {
3906 if (militb->soldier_control()->stationed_soldiers().size() > 0) {
3907 occupied_military_ = true;
3908 }
3909 }
3910
3911 // check if we are within grace time, if not or gracetime unset we need to do something
3912 // if we are within gracetime we do nothing (this loop is skipped)
3913
3914 // if grace time is not set, this is probably first time without a warehouse and we must set
3915 // it
3916 if (eco->dismantle_grace_time == kNever) {
3917
3918 // constructionsites
3919 if (upcast(ConstructionSite const, constructionsite, flag.get_building())) {
3920 BuildingObserver& bo =
3921 get_building_observer(constructionsite->building().name().c_str());
3922 // first very special case - a port (in the phase of constructionsite)
3923 // this might be a new colonization port
3924 if (bo.is(BuildingAttribute::kPort)) {
3925 eco->dismantle_grace_time = gametime + 60 * 60 * 1000; // one hour should be enough
3926 } else { // other constructionsites, usually new (standalone) constructionsites
3927 eco->dismantle_grace_time =
3928 gametime + 30 * 1000 + // very shot time is enough
3929 (eco->flags.size() * 30 * 1000); // + 30 seconds for every flag in economy
3930 }
3931
3932 // buildings
3933 } else {
3934
3935 if (occupied_military_) {
3936 eco->dismantle_grace_time =
3937 (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
3938
3939 } else { // for other normal buildings
3940 eco->dismantle_grace_time =
3941 gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
3942 }
3943 }
3944
3945 // we have passed grace_time - it is time to dismantle
3946 } else if (eco->dismantle_grace_time <= gametime) {
3947 last_attempt_ = true;
3948 // we increase a check radius in last attempt
3949 checkradius += 2;
3950 }
3951
3952 // and bonus for occupied military buildings:
3953 if (occupied_military_) {
3954 checkradius += 4;
3955 }
3956
3957 // and generally increase radius for unconnected buildings
3958 checkradius += 2;
3959 }
3960
3961 // Now own roadfinding stuff
3962 const Map& map = game().map();
3963
3964 // Initializing new object of FlagsForRoads, we will push there all candidate flags
3965 // First we dont even know if a road can be built there (from current flag)
3966 // Adding also distance of this flag to nearest wh
3967 uint32_t tmp_wh; // This information is not used, but we need it
3968 const uint32_t current_flag_dist_to_wh =
3969 flag_warehouse_distance.get_distance(flag.get_position().hash(), gametime, &tmp_wh);
3970
3971 FlagCandidates flag_candidates(current_flag_dist_to_wh);
3972
3973 FindNodeWithFlagOrRoad functor;
3974 CheckStepRoadAI check(player_, MOVECAPS_WALK, true);
3975
3976 // get all flags within radius
3977 std::vector<Coords> reachable;
3978 map.find_reachable_fields(game(),
3979 Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius),
3980 &reachable, check, functor);
3981
3982 for (const Coords& reachable_coords : reachable) {
3983
3984 // ignore starting flag, of course
3985 if (reachable_coords == flag.get_position()) {
3986 continue;
3987 }
3988
3989 const uint32_t reachable_coords_hash = reachable_coords.hash();
3990
3991 // first make sure there is an immovable (should be, but still)
3992 Widelands::BaseImmovable* this_immovable = map[reachable_coords].get_immovable();
3993 if (upcast(PlayerImmovable const, player_immovable, this_immovable)) {
3994
3995 // if it is the road, make a flag there
3996 if (this_immovable->descr().type() == MapObjectType::ROAD) {
3997 game().send_player_build_flag(player_number(), reachable_coords);
3998 }
3999
4000 // do not go on if it is not a flag
4001 if (this_immovable->descr().type() != MapObjectType::FLAG) {
4002 continue;
4003 }
4004
4005 // testing if a flag/road's economy has a warehouse, if not we are not
4006 // interested to connect to it
4007 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
4008 // ferries
4009 if (player_immovable->economy(wwWORKER).warehouses().size() == 0) {
4010 continue;
4011 }
4012
4013 // This is a candidate, sending all necessary info to RoadCandidates
4014 const bool is_different_economy =
4015 (player_immovable->get_economy(wwWORKER) != flag.get_economy(wwWORKER));
4016 const uint16_t air_distance = map.calc_distance(flag.get_position(), reachable_coords);
4017
4018 if (!flag_candidates.has_candidate(reachable_coords_hash) &&
4019 !flag_warehouse_distance.is_road_prohibited(reachable_coords_hash, gametime)) {
4020 flag_candidates.add_flag(
4021 reachable_coords_hash, is_different_economy,
4022 flag_warehouse_distance.get_distance(reachable_coords_hash, gametime, &tmp_wh),
4023 air_distance);
4024 }
4025 }
4026 }
4027
4028 // now we walk over roads and if field is reachable by roads, we change the distance assigned
4029 // above
4030 std::map<uint32_t, NearFlag> nearflags; // only used to collect flags reachable walk over roads
4031 nearflags[flag.get_position().hash()] = NearFlag(&flag, 0);
4032
4033 collect_nearflags(nearflags, flag, checkradius);
4034
4035 // Sending calculated walking costs from nearflags to RoadCandidates to update info on
4036 // Candidate flags/roads
4037 for (auto& nf_walk : nearflags) {
4038 const uint32_t nf_hash = nf_walk.second.flag->get_position().hash();
4039 // NearFlag contains also flags beyond check radius, these are not relevant for us
4040 if (flag_candidates.has_candidate(nf_hash)) {
4041 flag_candidates.set_cur_road_distance(nf_hash, nf_walk.second.current_road_distance);
4042 }
4043 }
4044
4045 // Here we must consider how much are buildable fields lacking
4046 // the number will be transformed to a weight passed to findpath function
4047 int32_t fields_necessity = 0;
4048 if (spots_ < kSpotsTooLittle) {
4049 fields_necessity += 10;
4050 }
4051 if (map_allows_seafaring_ && num_ports == 0) {
4052 fields_necessity += 10;
4053 }
4054 if (num_ports < 4) {
4055 fields_necessity += 5;
4056 }
4057 if (spots_ < kSpotsEnough) {
4058 fields_necessity += 5;
4059 }
4060
4061 fields_necessity *= std::abs(management_data.get_military_number_at(64)) * 5;
4062
4063 // Now we calculate roads from here to few best looking RoadCandidates....
4064 flag_candidates.sort_by_air_distance();
4065 uint32_t possible_roads_count = 0;
4066 for (const auto& flag_candidate : flag_candidates.flags()) {
4067 if (possible_roads_count > 10) {
4068 break;
4069 }
4070 const Widelands::Coords coords = Coords::unhash(flag_candidate.coords_hash);
4071 Path path;
4072
4073 // value of pathcost is not important, it just indicates, that the path can be built
4074 // We send this information to RoadCandidates, with length of possible road if applicable
4075 const int32_t pathcost =
4076 map.findpath(flag.get_position(), coords, 0, path, check, 0, fields_necessity);
4077 if (pathcost >= 0) {
4078 flag_candidates.set_road_possible(flag_candidate.coords_hash, path.get_nsteps());
4079 ++possible_roads_count;
4080 }
4081 }
4082
4083 // re-sorting again now by default by a score
4084 flag_candidates.sort();
4085
4086 // Well and finally building the winning road (if any)
4087 const int32_t winner_min_score = (spots_ < kSpotsTooLittle) ? 50 : 25;
4088
4089 FlagCandidates::Candidate* winner = flag_candidates.get_winner(winner_min_score);
4090 if (winner) {
4091 const Widelands::Coords target_coords = Coords::unhash(winner->coords_hash);
4092
4093 // This is to prohibit the flag for some time but with exemption of warehouse
4094 if (flag_warehouse_distance.get_distance(winner->coords_hash, gametime, &tmp_wh) > 0) {
4095 flag_warehouse_distance.set_road_built(winner->coords_hash, gametime);
4096 }
4097 // and we straight away set distance of future flag
4098 flag_warehouse_distance.set_distance(
4099 flag.get_position().hash(), winner->start_flag_dist_to_wh + winner->possible_road_distance,
4100 gametime, 0); // faking the warehouse
4101
4102 Path& path = *new Path();
4103 #ifndef NDEBUG
4104 const int32_t pathcost =
4105 map.findpath(flag.get_position(), target_coords, 0, path, check, 0, fields_necessity);
4106 assert(pathcost >= 0);
4107 #else
4108 map.findpath(flag.get_position(), target_coords, 0, path, check, 0, fields_necessity);
4109 #endif
4110 game().send_player_build_road(player_number(), path);
4111 return true;
4112 }
4113 // We can't build a road so let's block the vicinity as an indication this area is not
4114 // connectible
4115 // Usually we block for 2 minutes, but if it is a last attempt we block for 10 minutes
4116 // Note: we block the vicinity only if this economy (usually a sole flag with a building) is not
4117 // connected to a warehouse
4118 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
4119 // ferries
4120 if (flag.get_economy(wwWORKER)->warehouses().empty()) {
4121
4122 // blocking only if latest block was less then 60 seconds ago or it is last attempt
4123 if (eco->fields_block_last_time + kOneMinute < gametime || last_attempt_) {
4124 eco->fields_block_last_time = gametime;
4125
4126 const uint32_t block_time = last_attempt_ ? 10 * kOneMinute : 2 * kOneMinute;
4127
4128 FindNodeAcceptAll buildable_functor;
4129 CheckStepOwnTerritory check_own(player_, MOVECAPS_WALK, true);
4130
4131 // get all flags within radius
4132 std::vector<Coords> reachable_to_block;
4133 map.find_reachable_fields(game(),
4134 Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius),
4135 &reachable_to_block, check_own, buildable_functor);
4136
4137 for (auto coords : reachable_to_block) {
4138 blocked_fields.add(coords, game().get_gametime() + block_time);
4139 }
4140 }
4141
4142 // If it last attempt we also destroy the flag (with a building if any attached)
4143 if (last_attempt_) {
4144 remove_from_dqueue<Widelands::Flag>(eco->flags, &flag);
4145 game().send_player_bulldoze(*const_cast<Flag*>(&flag));
4146 dead_ends_check_ = true;
4147 return true;
4148 }
4149 }
4150 return false;
4151 }
4152
collect_nearflags(std::map<uint32_t,NearFlag> & nearflags,const Flag & flag,const uint16_t checkradius)4153 void DefaultAI::collect_nearflags(std::map<uint32_t, NearFlag>& nearflags,
4154 const Flag& flag,
4155 const uint16_t checkradius) {
4156 // Algorithm to walk on roads
4157 // All nodes are marked as to_be_checked == true first and once the node is checked it is changed
4158 // to false. Under some conditions, the same node can be checked twice, the to_be_checked can
4159 // be set back to true. Because less hoops (fewer flag-to-flag roads) does not always mean
4160 // shortest road.
4161
4162 const Map& map = game().map();
4163
4164 for (;;) {
4165 // looking for a node with shortest existing road distance from starting flag and one that has
4166 // to be checked
4167 uint32_t start_field = kNoField;
4168 uint32_t nearest_distance = 10000;
4169 for (auto item : nearflags) {
4170 if (item.second.current_road_distance < nearest_distance && item.second.to_be_checked) {
4171 nearest_distance = item.second.current_road_distance;
4172 start_field = item.first;
4173 }
4174 }
4175 // OK, we failed to find a NearFlag where to_be_checked == true, so quitting the loop now
4176 if (start_field == kNoField) {
4177 break;
4178 }
4179
4180 nearflags[start_field].to_be_checked = false;
4181
4182 // Now going over roads leading from this flag
4183 for (uint8_t i = WalkingDir::FIRST_DIRECTION; i <= WalkingDir::LAST_DIRECTION; ++i) {
4184 Road* const road = nearflags[start_field].flag->get_road(i);
4185
4186 if (!road) {
4187 continue;
4188 }
4189
4190 Flag* endflag = &road->get_flag(Road::FlagStart);
4191
4192 if (endflag == nearflags[start_field].flag) {
4193 endflag = &road->get_flag(Road::FlagEnd);
4194 }
4195
4196 const uint32_t endflag_hash = endflag->get_position().hash();
4197
4198 const int32_t dist = map.calc_distance(flag.get_position(), endflag->get_position());
4199
4200 if (dist > checkradius + 2) { // Testing bigger vicinity then checkradius....
4201 continue;
4202 }
4203
4204 // There is few scenarios for this neighbour flag
4205 if (nearflags.count(endflag_hash) == 0) {
4206 // This is brand new flag
4207 // calculating diff how much closer we will get to the flag
4208 nearflags[endflag_hash] =
4209 NearFlag(endflag, nearflags[start_field].current_road_distance +
4210 road->get_path().get_nsteps());
4211 } else {
4212 // We know about this flag already
4213 if (nearflags[endflag_hash].current_road_distance >
4214 nearflags[start_field].current_road_distance + road->get_path().get_nsteps()) {
4215 // ..but this current connection is shorter than one found before
4216 nearflags[endflag_hash].current_road_distance =
4217 nearflags[start_field].current_road_distance + road->get_path().get_nsteps();
4218 // So let re-check neighbours once more
4219 nearflags[endflag_hash].to_be_checked = true;
4220 }
4221 }
4222 }
4223 }
4224 }
4225
4226 /**
4227 * Checks if anything in one of the economies changed and takes care for these
4228 * changes.
4229 *
4230 * \returns true, if something was changed.
4231 */
check_economies()4232 bool DefaultAI::check_economies() {
4233 while (!new_flags.empty()) {
4234 const Flag& flag = *new_flags.front();
4235 new_flags.pop_front();
4236 // TODO(Nordfriese): Someone must urgently update the code since the big economy splitting for
4237 // the ferries
4238 get_economy_observer(flag.economy(wwWORKER))->flags.push_back(&flag);
4239 }
4240
4241 for (std::deque<EconomyObserver*>::iterator obs_iter = economies.begin();
4242 obs_iter != economies.end(); ++obs_iter) {
4243 // check if any flag has changed its economy
4244 std::deque<Flag const*>& fl = (*obs_iter)->flags;
4245
4246 for (std::deque<Flag const*>::iterator j = fl.begin(); j != fl.end();) {
4247 if (&(*obs_iter)->economy != &(*j)->economy(wwWORKER)) {
4248 // the flag belongs to other economy so we must assign it there
4249 get_economy_observer((*j)->economy(wwWORKER))->flags.push_back(*j);
4250 // and erase from this economy's observer
4251 j = fl.erase(j);
4252 } else {
4253 ++j;
4254 }
4255 }
4256
4257 // if there are no more flags in this economy,
4258 // we no longer need it's observer
4259 if ((*obs_iter)->flags.empty()) {
4260 delete *obs_iter;
4261 economies.erase(obs_iter);
4262 return true;
4263 }
4264 }
4265 return false;
4266 }
4267
4268 /**
4269 * checks the first productionsite in list, takes care if it runs out of
4270 * resources and finally reenqueues it at the end of the list.
4271 *
4272 * \returns true, if something was changed.
4273 */
check_productionsites(uint32_t gametime)4274 bool DefaultAI::check_productionsites(uint32_t gametime) {
4275 if (productionsites.empty()) {
4276 return false;
4277 }
4278
4279 // Reorder and set new values; - better now because there are multiple returns in the function
4280 productionsites.push_back(productionsites.front());
4281 productionsites.pop_front();
4282
4283 // Get link to productionsite that should be checked
4284 ProductionSiteObserver& site = productionsites.front();
4285
4286 // Inform if we are above ai type limit.
4287 if (site.bo->total_count() > site.bo->cnt_limit_by_aimode) {
4288 log("AI check_productionsites: Too many %s: %d, ai limit: %d\n", site.bo->name,
4289 site.bo->total_count(), site.bo->cnt_limit_by_aimode);
4290 }
4291
4292 // first we werify if site is working yet (can be unoccupied since the start)
4293 if (!site.site->can_start_working()) {
4294 site.unoccupied_till = game().get_gametime();
4295 }
4296
4297 // is it connected to wh at all?
4298 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
4299 // ferries
4300 const bool connected_to_wh = !site.site->get_economy(wwWORKER)->warehouses().empty();
4301
4302 // do not dismantle or upgrade the same type of building too soon - to give some time to update
4303 // statistics
4304 if (site.bo->last_dismantle_time >
4305 game().get_gametime() -
4306 (std::abs(management_data.get_military_number_at(164)) / 25 + 1) * 60 * 1000) {
4307 return false;
4308 }
4309
4310 // Get max radius of recursive workarea
4311 WorkareaInfo::size_type radius = 0;
4312 const WorkareaInfo& workarea_info = site.bo->desc->workarea_info_;
4313 for (const auto& temp_info : workarea_info) {
4314 if (radius < temp_info.first) {
4315 radius = temp_info.first;
4316 }
4317 }
4318
4319 const Map& map = game().map();
4320
4321 // First we check if we must release an experienced worker
4322 // iterate over all working positions of the actual productionsite
4323 for (uint8_t i = 0; i < site.site->descr().nr_working_positions(); i++) {
4324 // get the pointer to the worker assigned to the actual position
4325 const Worker* cw = site.site->working_positions()[i].worker;
4326 if (cw) { // a worker is assigned to the position
4327 // get the descritpion index of the worker assigned on this position
4328 DescriptionIndex current_worker = cw->descr().worker_index();
4329 // if description indexes of assigned worker and normal worker differ
4330 // (this means an experienced worker is assigned to the position)
4331 // and we have none of the experienced workers on stock
4332 if (current_worker != site.bo->positions.at(i) &&
4333 calculate_stocklevel(current_worker, WareWorker::kWorker) < 1) {
4334 // kick out the worker
4335 game().send_player_evict_worker(*site.site->working_positions()[i].worker);
4336 return true;
4337 }
4338 }
4339 }
4340
4341 // The code here is bit complicated
4342 // a) Either this site is pending for upgrade, if ready, order the upgrade
4343 // b) other site of type is pending for upgrade
4344 // c) if none of above, we can consider upgrade of this one
4345
4346 const DescriptionIndex enhancement = site.site->descr().enhancement();
4347
4348 bool considering_upgrade = enhancement != INVALID_INDEX;
4349
4350 if (!basic_economy_established && management_data.f_neuron_pool[17].get_position(2)) {
4351 considering_upgrade = false;
4352 }
4353
4354 // First we check for rare case when input wares are set to 0 but AI is not aware that
4355 // the site is pending for upgrade - one possible cause is this is a freshly loaded game
4356 if (!site.upgrade_pending) {
4357 bool resetting_wares = false;
4358 for (auto& queue : site.site->inputqueues()) {
4359 if (queue->get_max_fill() == 0) {
4360 resetting_wares = true;
4361 game().send_player_set_input_max_fill(
4362 *site.site, queue->get_index(), queue->get_type(), queue->get_max_size());
4363 }
4364 }
4365 if (resetting_wares) {
4366 log(" %d: AI: input queues were reset to max for %s (game just loaded?)\n",
4367 player_number(), site.bo->name);
4368 return true;
4369 }
4370 }
4371
4372 if (site.upgrade_pending) {
4373 // The site is in process of emptying its input queues
4374 // Do nothing when some wares are left, but do not wait more then 4 minutes
4375 if (site.bo->construction_decision_time + 4 * 60 * 1000 > gametime &&
4376 !set_inputs_to_zero(site)) {
4377 return false;
4378 }
4379 assert(site.bo->cnt_upgrade_pending == 1);
4380 assert(enhancement != INVALID_INDEX);
4381 game().send_player_enhance_building(*site.site, enhancement, true);
4382 return true;
4383 } else if (site.bo->cnt_upgrade_pending > 0) {
4384 // some other site of this type is in pending for upgrade
4385 assert(site.bo->cnt_upgrade_pending == 1);
4386 return false;
4387 }
4388 assert(site.bo->cnt_upgrade_pending == 0);
4389
4390 // Of course type of productionsite must be allowed
4391 if (considering_upgrade && !player_->is_building_type_allowed(enhancement)) {
4392 considering_upgrade = false;
4393 }
4394
4395 // Site must be connected to warehouse
4396 if (considering_upgrade && !connected_to_wh) {
4397 considering_upgrade = false;
4398 }
4399
4400 // If upgrade produces new outputs, we upgrade unless the site is younger
4401 // then 10 minutes. Otherwise the site must be older then 20 minutes and
4402 // gametime > 45 minutes.
4403 if (considering_upgrade) {
4404 if (site.bo->is(BuildingAttribute::kUpgradeExtends)) {
4405 if (gametime < site.built_time + 10 * 60 * 1000) {
4406 considering_upgrade = false;
4407 }
4408 } else {
4409 if (gametime < 45 * 60 * 1000 || gametime < site.built_time + 20 * 60 * 1000) {
4410 considering_upgrade = false;
4411 }
4412 }
4413 }
4414
4415 // No upgrade without proper workers
4416 if (considering_upgrade && !site.site->has_workers(enhancement, game())) {
4417 const BuildingDescr& bld = *tribe_->get_building_descr(enhancement);
4418 BuildingObserver& en_bo = get_building_observer(bld.name().c_str());
4419 if (get_stocklevel(en_bo, gametime, WareWorker::kWorker) < 1) {
4420 considering_upgrade = false;
4421 }
4422 }
4423
4424 if (considering_upgrade) {
4425
4426 const BuildingDescr& bld = *tribe_->get_building_descr(enhancement);
4427 BuildingObserver& en_bo = get_building_observer(bld.name().c_str());
4428 bool doing_upgrade = false;
4429
4430 // 10 minutes is a time to productions statics to settle
4431 if ((en_bo.last_building_built == kNever ||
4432 gametime - en_bo.last_building_built >= 10 * 60 * 1000) &&
4433 (en_bo.cnt_under_construction + en_bo.unoccupied_count) == 0) {
4434
4435 // forcing first upgrade
4436 if (en_bo.total_count() == 0 &&
4437 (site.bo->cnt_built > 1 || site.bo->is(BuildingAttribute::kUpgradeSubstitutes))) {
4438 doing_upgrade = true;
4439 }
4440
4441 if (en_bo.total_count() == 1 &&
4442 (site.bo->cnt_built > 1 || site.bo->is(BuildingAttribute::kUpgradeSubstitutes))) {
4443 if (en_bo.current_stats > 55) {
4444 doing_upgrade = true;
4445 }
4446 }
4447
4448 if (en_bo.total_count() > 1 &&
4449 (site.bo->cnt_built > 2 || site.bo->is(BuildingAttribute::kUpgradeSubstitutes))) {
4450 if (en_bo.current_stats > 75) {
4451 doing_upgrade = true;
4452 }
4453 }
4454
4455 // Don't forget about limitation of number of buildings
4456 if (en_bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
4457 doing_upgrade = false;
4458 }
4459 }
4460
4461 // Here we just restrict input wares to 0 and set flag 'upgrade_pending' to true
4462 if (doing_upgrade) {
4463 set_inputs_to_zero(site);
4464 site.bo->construction_decision_time = gametime;
4465 en_bo.construction_decision_time = gametime;
4466 site.upgrade_pending = true;
4467 ++site.bo->cnt_upgrade_pending;
4468 return true;
4469 }
4470 }
4471
4472 // Barracks
4473 if (site.bo->is(BuildingAttribute::kBarracks)) {
4474 // If we somehow have more than one barracks we will dismantle current one
4475 if (site.bo->total_count() > 1) {
4476 log("%2d: We have %d barracks, that is not supported by AI and if caused by AI it is an "
4477 "error; dismantling the barracks at %3dx%3d\n",
4478 player_number(), site.bo->total_count(), site.site->get_position().x,
4479 site.site->get_position().y);
4480 if (connected_to_wh) {
4481 game().send_player_dismantle(*site.site, true);
4482 } else {
4483 game().send_player_bulldoze(*site.site);
4484 }
4485 return true;
4486 }
4487
4488 assert(site.bo->total_count() == 1);
4489 for (auto& queue : site.site->inputqueues()) {
4490 if (queue->get_max_fill() > 4) {
4491 game().send_player_set_input_max_fill(
4492 *site.site, queue->get_index(), queue->get_type(), 4);
4493 }
4494 }
4495
4496 // AI takes multiple inputs into account and makes decision if barracks to be stopped/started
4497 int16_t tmp_score = 0;
4498 int16_t inputs[kFNeuronBitSize] = {0};
4499 const PlayerNumber pn = player_number();
4500 tmp_score += (soldier_status_ == SoldiersStatus::kBadShortage) * 8;
4501 tmp_score += (soldier_status_ == SoldiersStatus::kShortage) * 4;
4502 tmp_score += (soldier_status_ == SoldiersStatus::kEnough) * 2;
4503 tmp_score += (soldier_status_ == SoldiersStatus::kFull) * 1;
4504 inputs[2] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) * -1;
4505 inputs[3] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace) * 1;
4506 inputs[4] = -1;
4507 inputs[5] = -2;
4508 inputs[6] = -3;
4509 inputs[14] =
4510 (player_statistics.get_player_power(pn) < player_statistics.get_old_player_power(pn)) * 1;
4511 inputs[15] =
4512 (player_statistics.get_player_power(pn) < player_statistics.get_old60_player_power(pn)) *
4513 1;
4514 inputs[16] =
4515 (player_statistics.get_player_power(pn) > player_statistics.get_old_player_power(pn)) * 1;
4516 inputs[17] =
4517 (player_statistics.get_player_power(pn) > player_statistics.get_old60_player_power(pn)) *
4518 1;
4519 inputs[18] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace) * -1;
4520 inputs[19] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) * 1;
4521 inputs[20] = 1;
4522 inputs[21] = 2;
4523 inputs[22] = 3;
4524 inputs[23] = (ts_without_trainers_ > 0) ? -1 : 0;
4525 inputs[24] = (ts_without_trainers_ > 0) ? -2 : 0;
4526 inputs[25] = (ts_without_trainers_ > 0) ? -3 : 0;
4527 for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
4528 if (management_data.f_neuron_pool[24].get_position(i)) {
4529 tmp_score += inputs[i];
4530 }
4531 }
4532
4533 // starting the site
4534 if (site.site->is_stopped() && tmp_score >= 0) {
4535 game().send_player_start_stop_building(*site.site);
4536 for (auto& queue : site.site->inputqueues()) {
4537 game().send_player_set_input_max_fill(
4538 *site.site, queue->get_index(), queue->get_type(), 4);
4539 }
4540 }
4541 // stopping the site
4542 if (!site.site->is_stopped() && tmp_score < 0) {
4543 game().send_player_start_stop_building(*site.site);
4544 for (auto& queue : site.site->inputqueues()) {
4545 game().send_player_set_input_max_fill(
4546 *site.site, queue->get_index(), queue->get_type(), 2);
4547 }
4548 }
4549 }
4550
4551 // Lumberjack / Woodcutter handling
4552 if (site.bo->is(BuildingAttribute::kLumberjack)) {
4553
4554 // do not dismantle immediatelly
4555 if ((game().get_gametime() - site.built_time) < 6 * 60 * 1000) {
4556 return false;
4557 }
4558
4559 // Do not destruct the last few lumberjacks
4560 if (site.bo->cnt_built <= site.bo->cnt_target) {
4561 return false;
4562 }
4563
4564 const uint32_t remaining_trees = map.find_immovables(
4565 game(), Area<FCoords>(map.get_fcoords(site.site->get_position()), radius), nullptr,
4566 FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
4567
4568 if (site.site->get_statistics_percent() >
4569 std::abs(management_data.get_military_number_at(117)) / 2) {
4570 return false;
4571 }
4572
4573 if (remaining_trees > trees_nearby_treshold_ / 3) {
4574 return false;
4575 }
4576
4577 // so finally we dismantle the lumberjac
4578 site.bo->last_dismantle_time = game().get_gametime();
4579 if (connected_to_wh) {
4580 game().send_player_dismantle(*site.site, true);
4581 } else {
4582 game().send_player_bulldoze(*site.site);
4583 }
4584
4585 return true;
4586 }
4587
4588 // Wells handling
4589 if (site.bo->is(BuildingAttribute::kWell)) {
4590 // Never get below target count of wells
4591 if (site.bo->total_count() <= site.bo->cnt_target) {
4592 return false;
4593 }
4594
4595 if (site.unoccupied_till + 6 * 60 * 1000 < gametime &&
4596 site.site->get_statistics_percent() == 0) {
4597 site.bo->last_dismantle_time = gametime;
4598 if (connected_to_wh) {
4599 game().send_player_dismantle(*site.site, true);
4600 } else {
4601 game().send_player_bulldoze(*site.site);
4602 }
4603
4604 return true;
4605 }
4606
4607 // do not consider dismantling if we are under target
4608 if (site.bo->last_dismantle_time + 90 * 1000 > game().get_gametime()) {
4609 return false;
4610 }
4611
4612 // now we test the stocklevel and dismantle the well if we have enough water
4613 // but first we make sure we do not dismantle a well too soon
4614 // after dismantling previous one
4615 if (get_stocklevel(*site.bo, gametime) > 250 + productionsites.size() * 5) { // dismantle
4616 site.bo->last_dismantle_time = game().get_gametime();
4617 if (connected_to_wh) {
4618 game().send_player_dismantle(*site.site, true);
4619 } else {
4620 game().send_player_bulldoze(*site.site);
4621 }
4622 return true;
4623 }
4624
4625 return false;
4626 }
4627
4628 // Quarry handling
4629 if (site.bo->is(BuildingAttribute::kNeedsRocks)) {
4630
4631 if (map.find_immovables(
4632 game(), Area<FCoords>(map.get_fcoords(site.site->get_position()), 6), nullptr,
4633
4634 FindImmovableAttribute(MapObjectDescr::get_attribute_id("rocks"))) == 0) {
4635 // destruct the building and it's flag (via flag destruction)
4636 // the destruction of the flag avoids that defaultAI will have too many
4637 // unused roads - if needed the road will be rebuild directly.
4638 if (connected_to_wh) {
4639 game().send_player_dismantle(*site.site, true);
4640 } else {
4641 game().send_player_bulldoze(*site.site);
4642 }
4643 return true;
4644 }
4645
4646 if (site.unoccupied_till + 6 * 60 * 1000 < gametime &&
4647 site.site->get_statistics_percent() == 0) {
4648 // it is possible that there are rocks but quarry is not able to mine them
4649 site.bo->last_dismantle_time = game().get_gametime();
4650 if (connected_to_wh) {
4651 game().send_player_dismantle(*site.site, true);
4652 } else {
4653 game().send_player_bulldoze(*site.site);
4654 }
4655
4656 return true;
4657 }
4658
4659 return false;
4660 }
4661
4662 // All other SPACE_CONSUMERS without input and above target_count
4663 if (site.bo->inputs.empty() // does not consume anything
4664 && site.bo->production_hints.empty() // not a renewing building (forester...)
4665 && site.bo->is(BuildingAttribute::kSpaceConsumer) &&
4666 !site.bo->is(BuildingAttribute::kRanger)) {
4667
4668 // if we have more buildings then target
4669 if ((site.bo->cnt_built - site.bo->unconnected_count) > site.bo->cnt_target &&
4670 site.unoccupied_till +
4671 (std::abs(management_data.get_military_number_at(166)) / 5 + 1) * 60 * 1000 <
4672 gametime &&
4673 site.site->can_start_working()) {
4674
4675 if (site.site->get_statistics_percent() < 30 && get_stocklevel(*site.bo, gametime) > 100) {
4676 site.bo->last_dismantle_time = game().get_gametime();
4677 if (connected_to_wh) {
4678 game().send_player_dismantle(*site.site, true);
4679 } else {
4680 game().send_player_bulldoze(*site.site);
4681 }
4682 return true;
4683 }
4684 }
4685
4686 // a building can be dismanteld if it performs too bad, if it is not the last one
4687 if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built > 1 &&
4688 site.unoccupied_till +
4689 (std::abs(management_data.get_military_number_at(167)) / 5 + 1) * 60 * 1000 <
4690 gametime &&
4691 site.site->can_start_working() &&
4692 get_stocklevel(*site.bo, gametime) >
4693 static_cast<unsigned int>(
4694 (std::abs(management_data.get_military_number_at(168)) / 5))) {
4695
4696 if (connected_to_wh) {
4697 game().send_player_dismantle(*site.site, true);
4698 } else {
4699 game().send_player_bulldoze(*site.site);
4700 }
4701 return true;
4702 }
4703
4704 // Blocking the vicinity if too low performance and ware is still needed
4705 if (site.site->get_statistics_percent() <= 50 || get_stocklevel(*site.bo, gametime) < 5) {
4706 MapRegion<Area<FCoords>> mr(
4707 map, Area<FCoords>(map.get_fcoords(site.site->base_flag().get_position()), 5));
4708 do {
4709 blocked_fields.add(mr.location(), gametime + 5 * 60 * 100);
4710 } while (mr.advance(map));
4711 }
4712
4713 return false;
4714 }
4715
4716 // buildings with inputs, checking if we can a dismantle some due to low performance
4717 if (!site.bo->inputs.empty() && (site.bo->cnt_built - site.bo->unoccupied_count) >= 3 &&
4718 site.site->can_start_working() &&
4719 check_building_necessity(*site.bo, PerfEvaluation::kForDismantle, gametime) ==
4720 BuildingNecessity::kNotNeeded &&
4721 gametime - site.bo->last_dismantle_time >
4722 static_cast<uint32_t>((std::abs(management_data.get_military_number_at(169)) / 5 + 1) *
4723 60 * 1000) &&
4724
4725 site.bo->current_stats > site.site->get_statistics_percent() && // underperformer
4726 (game().get_gametime() - site.unoccupied_till) > 10 * 60 * 1000) {
4727
4728 site.bo->last_dismantle_time = game().get_gametime();
4729
4730 if (connected_to_wh) {
4731 game().send_player_dismantle(*site.site, true);
4732 } else {
4733 game().send_player_bulldoze(*site.site);
4734 }
4735 return true;
4736 }
4737
4738 // remaining buildings without inputs and not supporting ones (fishers only left probably and
4739 // hunters)
4740 if (site.bo->inputs.empty() && site.bo->production_hints.empty() &&
4741 site.site->can_start_working() && !site.bo->is(BuildingAttribute::kSpaceConsumer) &&
4742 site.site->get_statistics_percent() < 5 &&
4743 ((game().get_gametime() - site.built_time) > 10 * 60 * 1000)) {
4744
4745 site.bo->last_dismantle_time = game().get_gametime();
4746 if (connected_to_wh) {
4747 game().send_player_dismantle(*site.site, true);
4748 } else {
4749 game().send_player_bulldoze(*site.site);
4750 }
4751 return true;
4752 }
4753
4754 // supporting productionsites (rangers)
4755 // stop/start them based on stock avaiable
4756 if (!site.bo->production_hints.empty()) {
4757
4758 if (!site.bo->is(BuildingAttribute::kRanger)) {
4759 // other supporting sites, like fish breeders, gamekeepers are not dismantled at all
4760 return false;
4761 }
4762
4763 // dismantling the rangers hut, but only if we have them above a target
4764 if (wood_policy_ == WoodPolicy::kDismantleRangers &&
4765 site.bo->cnt_built > site.bo->cnt_target) {
4766
4767 site.bo->last_dismantle_time = game().get_gametime();
4768 if (connected_to_wh) {
4769 game().send_player_dismantle(*site.site, true);
4770 } else {
4771 game().send_player_bulldoze(*site.site);
4772 }
4773 return true;
4774 }
4775
4776 // stopping a ranger (sometimes the policy can be kDismantleRangers,
4777 // but we still preserve some rangers for sure)
4778 if ((wood_policy_ == WoodPolicy::kStopRangers ||
4779 wood_policy_ == WoodPolicy::kDismantleRangers) &&
4780 !site.site->is_stopped()) {
4781
4782 game().send_player_start_stop_building(*site.site);
4783 return false;
4784 }
4785
4786 const uint32_t trees_in_vicinity = map.find_immovables(
4787 game(), Area<FCoords>(map.get_fcoords(site.site->get_position()), 5), nullptr,
4788 FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
4789
4790 // stop ranger if enough trees around regardless of policy
4791 if (trees_in_vicinity > 25) {
4792 if (!site.site->is_stopped()) {
4793 game().send_player_start_stop_building(*site.site);
4794 }
4795 // if not enough trees nearby, we can start them if required
4796 } else if ((wood_policy_ == WoodPolicy::kAllowRangers) && site.site->is_stopped()) {
4797 game().send_player_start_stop_building(*site.site);
4798 }
4799 }
4800
4801 return false;
4802 }
4803
4804 /**
4805 * checks the first mine in list, takes care if it runs out of
4806 * resources and finally reenqueues it at the end of the list.
4807 *
4808 * \returns true, if something was changed.
4809 */
check_mines_(uint32_t const gametime)4810 bool DefaultAI::check_mines_(uint32_t const gametime) {
4811 if (mines_.empty()) {
4812 return false;
4813 }
4814
4815 // Reorder and set new values; - due to returns within the function
4816 mines_.push_back(mines_.front());
4817 mines_.pop_front();
4818
4819 // Get link to productionsite that should be checked
4820 ProductionSiteObserver& site = mines_.front();
4821
4822 // TODO(Nordfriese): Someone should update the code since the big economy splitting for the
4823 // ferries
4824 const bool connected_to_wh = !site.site->get_economy(wwWORKER)->warehouses().empty();
4825
4826 // First we dismantle mines that are marked as such, generally we wait till all wares all gone
4827 if (site.dismantle_pending_since != kNever) {
4828 assert(site.dismantle_pending_since <= gametime);
4829 if (set_inputs_to_zero(site) || site.dismantle_pending_since + 5 * 60 * 1000 < gametime) {
4830 if (connected_to_wh) {
4831 game().send_player_dismantle(*site.site, true);
4832 } else {
4833 game().send_player_bulldoze(*site.site);
4834 }
4835
4836 return true;
4837 } else if (site.dismantle_pending_since + 3 * 60 * 1000 < gametime) {
4838 stop_site(site);
4839 return false;
4840 } else {
4841 return false;
4842 }
4843 } else if (site.site->can_start_working()) {
4844 set_inputs_to_max(site);
4845 } else {
4846 set_inputs_to_zero(site);
4847 }
4848
4849 // First we check if we must release an experienced worker
4850 for (uint8_t i = 0; i < site.site->descr().nr_working_positions(); i++) {
4851 const Worker* cw = site.site->working_positions()[i].worker;
4852 if (cw) {
4853 DescriptionIndex current_worker = cw->descr().worker_index();
4854 if (current_worker != site.bo->positions.at(i) &&
4855 calculate_stocklevel(current_worker, WareWorker::kWorker) < 1) {
4856 game().send_player_evict_worker(*site.site->working_positions()[i].worker);
4857 return true;
4858 }
4859 }
4860 }
4861
4862 // Single _critical is a critical mine if it is the only one of its type, so it needs special
4863 // treatment
4864 bool single_critical = false;
4865 if ((site.bo->is(BuildingAttribute::kBuildingMatProducer) ||
4866 site.bo->mines == iron_resource_id) &&
4867 mines_per_type[site.bo->mines].finished == 1) {
4868 single_critical = true;
4869 }
4870
4871 // first get rid of mines that have been missing workers for some time (10 minutes),
4872 // released worker (if any) can be useful elsewhere !
4873 if (!single_critical && site.built_time + 10 * 60 * 1000 < gametime &&
4874 !site.site->can_start_working() && mines_per_type[site.bo->mines].total_count() > 2) {
4875 initiate_dismantling(site, gametime);
4876 return false;
4877 }
4878
4879 // to avoid problems with uint underflow, we discourage considerations below
4880 if (gametime < 10 * 60 * 1000) {
4881 return false;
4882 }
4883
4884 // After 15 minutes in existence we check whether a miner is needed for a critical unoccupied
4885 // mine elsewhere
4886 if (site.built_time + 15 * 60 * 1000 < gametime) {
4887 if (!mines_per_type[site.bo->mines].is_critical && critical_mine_unoccupied(gametime)) {
4888 for (uint8_t i = 0; i < site.site->descr().nr_working_positions(); i++) {
4889 const Worker* cw = site.site->working_positions()[i].worker;
4890 if (cw) {
4891 game().send_player_evict_worker(*site.site->working_positions()[i].worker);
4892 }
4893 }
4894 return true;
4895 }
4896 }
4897
4898 // if mine is working, doing nothing
4899 if (site.no_resources_since > gametime - 5 * 60 * 1000) {
4900 return false;
4901 }
4902
4903 // Out of resources, first check whether a mine is not needed for critical mine
4904 if (!mines_per_type[site.bo->mines].is_critical && critical_mine_unoccupied(gametime)) {
4905 initiate_dismantling(site, gametime);
4906 return true;
4907 }
4908
4909 // Check whether building is enhanceable. If yes consider an upgrade.
4910 const DescriptionIndex enhancement = site.site->descr().enhancement();
4911 bool has_upgrade = false;
4912 if (enhancement != INVALID_INDEX) {
4913 if (player_->is_building_type_allowed(enhancement)) {
4914 has_upgrade = true;
4915 }
4916 }
4917
4918 // every type of mine has minimal number of mines that are to be preserved
4919 // (we will not dismantle even if there are no mineable resources left for this level of mine
4920 // and output is not needed)
4921 bool forcing_upgrade = false;
4922 const uint16_t minimal_mines_count =
4923 (site.bo->is(BuildingAttribute::kBuildingMatProducer)) ? 2 : 1;
4924 if (has_upgrade && mines_per_type[site.bo->mines].total_count() <= minimal_mines_count) {
4925 forcing_upgrade = true;
4926 }
4927
4928 // dismantling a mine
4929 if (!has_upgrade) { // if no upgrade, now
4930 initiate_dismantling(site, gametime);
4931 return true;
4932 // if having an upgrade, after half hour
4933 } else if (site.no_resources_since < gametime - 30 * 60 * 1000 && !forcing_upgrade) {
4934 initiate_dismantling(site, gametime);
4935 return true;
4936 }
4937
4938 // if we are here, a mine is upgradeable
4939
4940 // if we don't need the output, and we have other buildings of the same type, the function
4941 // returns
4942 // and building will be dismantled later.
4943 check_building_necessity(*site.bo, PerfEvaluation::kForDismantle, gametime);
4944 if (site.bo->max_needed_preciousness == 0 && !forcing_upgrade) {
4945 return false;
4946 }
4947
4948 // again similarly, no upgrading if not connected, other parts of AI will dismantle it,
4949 // or connect to a warehouse
4950 if (!connected_to_wh) {
4951 return false;
4952 }
4953
4954 // don't upgrade now if other mines of the same type are right now in construction
4955 if (mines_per_type[site.bo->mines].in_construction > 0) {
4956 return false;
4957 }
4958
4959 bool changed = false;
4960
4961 // first exclude possibility there are enhancements in construction or unoccupied_count
4962 const BuildingDescr& bld = *tribe_->get_building_descr(enhancement);
4963 BuildingObserver& en_bo = get_building_observer(bld.name().c_str());
4964
4965 // Make sure we do not exceed limit given by AI mode
4966 if (en_bo.aimode_limit_status() == AiModeBuildings::kAnotherAllowed) {
4967
4968 // if it is too soon for enhancement
4969 if (gametime - en_bo.construction_decision_time >= kBuildingMinInterval) {
4970 // now verify that there are enough workers
4971 if (site.site->has_workers(enhancement, game())) { // enhancing
4972 game().send_player_enhance_building(*site.site, enhancement, true);
4973 if (site.bo->max_needed_preciousness == 0) {
4974 assert(mines_per_type[site.bo->mines].total_count() <= minimal_mines_count);
4975 }
4976 if (mines_per_type[site.bo->mines].total_count() > minimal_mines_count) {
4977 assert(site.bo->max_needed_preciousness > 0);
4978 }
4979 en_bo.construction_decision_time = gametime;
4980 changed = true;
4981 }
4982 }
4983 }
4984
4985 return changed;
4986 }
4987
check_warehouse_necessity(BuildingObserver & bo,const uint32_t gametime)4988 BuildingNecessity DefaultAI::check_warehouse_necessity(BuildingObserver& bo,
4989 const uint32_t gametime) {
4990 bo.primary_priority = 0;
4991
4992 // First there are situation when we cannot built the warehouse/port
4993 // a) This is port and map is not seafaring one
4994 if (bo.is(BuildingAttribute::kPort) && !map_allows_seafaring_) {
4995 bo.new_building_overdue = 0;
4996 return BuildingNecessity::kForbidden;
4997 }
4998
4999 // b) the site is prohibited
5000 if (bo.prohibited_till > gametime) {
5001 bo.new_building_overdue = 0;
5002 return BuildingNecessity::kForbidden;
5003 }
5004
5005 // If this is a port and is the first bo be built
5006 const bool first_port_allowed = (bo.is(BuildingAttribute::kPort) && bo.total_count() == 0);
5007
5008 // c) there are warehouses in construction (first port is exemption)
5009 if (numof_warehouses_in_const_ > 0 && !first_port_allowed) {
5010 bo.new_building_overdue = 0;
5011 return BuildingNecessity::kForbidden;
5012 }
5013
5014 // d) there is ai limit for this bulding
5015 if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
5016 bo.new_building_overdue = 0;
5017 return BuildingNecessity::kForbidden;
5018 }
5019
5020 // e) basic economy not established, but first port is an exemption
5021 if (!basic_economy_established && !first_port_allowed) {
5022 bo.new_building_overdue = 0;
5023 return BuildingNecessity::kForbidden;
5024 }
5025
5026 // Number of needed warehouses decides if new one is needed and also
5027 // converts to the score
5028 int32_t needed_count = 0;
5029 if (first_port_allowed) {
5030 needed_count += numof_warehouses_ + numof_warehouses_in_const_ + 1;
5031 } else {
5032 needed_count += static_cast<int32_t>(productionsites.size() + mines_.size()) /
5033 (40 + management_data.get_military_number_at(21) / 10) +
5034 1;
5035 }
5036
5037 assert(needed_count >= 0 &&
5038 needed_count <= (static_cast<uint16_t>(productionsites.size() + mines_.size()) / 10) + 2);
5039
5040 if (player_statistics.any_enemy_seen_lately(gametime) +
5041 (productionsites.size() + mines_.size()) >
5042 10) {
5043 ++needed_count;
5044 }
5045
5046 // Port should always have higher score than a warehouse
5047 if (bo.is(BuildingAttribute::kPort)) {
5048 ++needed_count;
5049 }
5050
5051 // suppres a warehouse if not enough spots
5052 if (spots_ < kSpotsEnough && !bo.is(BuildingAttribute::kPort)) {
5053 --needed_count;
5054 }
5055
5056 if (needed_count <= numof_warehouses_in_const_ + numof_warehouses_) {
5057 bo.new_building_overdue = 0;
5058 return BuildingNecessity::kForbidden;
5059 }
5060
5061 // So now we know the warehouse here is needed.
5062 bo.primary_priority = 1 +
5063 (needed_count - numof_warehouses_) *
5064 std::abs(management_data.get_military_number_at(22) * 20);
5065 ++bo.new_building_overdue;
5066 bo.primary_priority +=
5067 bo.new_building_overdue * std::abs(management_data.get_military_number_at(16));
5068 if (bo.is(BuildingAttribute::kPort) && spots_ < kSpotsTooLittle) {
5069 bo.primary_priority += std::abs(management_data.get_military_number_at(152)) * 10;
5070 }
5071 return BuildingNecessity::kAllowed;
5072 }
5073
5074 // this receives an building observer and have to decide if new/one of
5075 // current buildings of this type is needed
5076 // This is core of construct_building() function
5077 // This is run once when construct_building() is run, or when considering
5078 // dismantle
check_building_necessity(BuildingObserver & bo,const PerfEvaluation purpose,const uint32_t gametime)5079 BuildingNecessity DefaultAI::check_building_necessity(BuildingObserver& bo,
5080 const PerfEvaluation purpose,
5081 const uint32_t gametime) {
5082 bo.primary_priority = 0;
5083
5084 static BasicEconomyBuildingStatus site_needed_for_economy = BasicEconomyBuildingStatus::kNone;
5085 site_needed_for_economy = BasicEconomyBuildingStatus::kNone;
5086 if (gametime > 2 * 60 * 1000 && gametime < 120 * 60 * 1000 && !basic_economy_established) {
5087 if (persistent_data->remaining_basic_buildings.count(bo.id) &&
5088 bo.cnt_under_construction == 0) {
5089 assert(persistent_data->remaining_basic_buildings.count(bo.id) > 0);
5090 if (spots_ < kSpotsTooLittle && bo.type != BuildingObserver::Type::kMine) {
5091 site_needed_for_economy = BasicEconomyBuildingStatus::kNeutral;
5092 } else {
5093 site_needed_for_economy = BasicEconomyBuildingStatus::kEncouraged;
5094 }
5095
5096 } else {
5097 site_needed_for_economy = BasicEconomyBuildingStatus::kDiscouraged;
5098 }
5099 }
5100
5101 // Very first we finds if AI is allowed to build such building due to its mode
5102 if (purpose == PerfEvaluation::kForConstruction &&
5103 bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
5104 return BuildingNecessity::kForbidden;
5105 }
5106
5107 // Perhaps buildings are not allowed because the map is no seafaring
5108 if (purpose == PerfEvaluation::kForConstruction && !map_allows_seafaring_ &&
5109 bo.is(BuildingAttribute::kNeedsSeafaring)) {
5110 return BuildingNecessity::kForbidden;
5111 }
5112
5113 // First we deal with training sites, they are separate category
5114 if (bo.type == BuildingObserver::Type::kTrainingsite) {
5115
5116 if (!basic_economy_established && management_data.f_neuron_pool[17].get_position(1)) {
5117 return BuildingNecessity::kNotNeeded;
5118 } else if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) {
5119 return BuildingNecessity::kNotNeeded;
5120 } else if (ts_without_trainers_ > 0 || bo.cnt_under_construction > 0 ||
5121 ts_in_const_count_ > 1) {
5122 return BuildingNecessity::kNotNeeded;
5123 } else if (bo.prohibited_till > gametime) {
5124 return BuildingNecessity::kNotNeeded;
5125 } else if (ts_without_trainers_ > 1) {
5126 return BuildingNecessity::kNotNeeded;
5127 } else if (bo.total_count() > 0) {
5128 if (soldier_trained_log.count(gametime, bo.id) / bo.total_count() < 5) {
5129 return BuildingNecessity::kNotNeeded;
5130 }
5131 }
5132
5133 // It seems we might need it after all
5134 bo.primary_priority = -30;
5135 if (bo.build_material_shortage) {
5136 bo.primary_priority -= std::abs(management_data.get_military_number_at(72));
5137 }
5138
5139 if (bo.forced_after > gametime && bo.total_count() == 0) {
5140 bo.primary_priority += 50 + std::abs(management_data.get_military_number_at(112) / 5);
5141 }
5142
5143 // If we are close to enemy (was seen in last 15 minutes)
5144 if (player_statistics.any_enemy_seen_lately(gametime)) {
5145 bo.primary_priority += std::abs(management_data.get_military_number_at(57) / 2);
5146 }
5147
5148 // Do we own some minefields for each critical mine
5149 if (!mine_fields_stat.has_critical_ore_fields()) {
5150 bo.primary_priority -= std::abs(management_data.get_military_number_at(156));
5151 }
5152
5153 // We build one trainig site per X military sites
5154 // with some variations, of course
5155 int32_t target = 1 +
5156 static_cast<int32_t>(militarysites.size() + productionsites.size()) /
5157 (std::abs(management_data.get_military_number_at(113) / 2) + 1);
5158 assert(target > 0 && target < 500);
5159
5160 uint16_t current_proportion = 0;
5161 if (ts_finished_count_ + ts_in_const_count_ > 0) {
5162 current_proportion = bo.total_count() * 100 / (ts_finished_count_ + ts_in_const_count_);
5163 }
5164
5165 bo.primary_priority += (target - ts_finished_count_ - ts_in_const_count_) *
5166 std::abs(management_data.get_military_number_at(114) * 2);
5167 bo.primary_priority += (static_cast<int32_t>(militarysites.size() + productionsites.size()) -
5168 target * std::abs(management_data.get_military_number_at(78) / 4)) *
5169 3;
5170
5171 // Special bonus for very first site of type
5172 if (bo.total_count() == 0) {
5173 bo.primary_priority += std::abs(management_data.get_military_number_at(56)) +
5174 bo.max_trainingsites_proportion - current_proportion;
5175 } else if (bo.max_trainingsites_proportion < current_proportion) {
5176 bo.primary_priority -= std::abs(management_data.get_military_number_at(128) * 3);
5177 }
5178
5179 if (bo.primary_priority > 0) {
5180 return BuildingNecessity::kNeeded;
5181 } else {
5182 return BuildingNecessity::kNotNeeded;
5183 }
5184 }
5185
5186 if (bo.is(BuildingAttribute::kRecruitment)) {
5187 if (bo.total_count() > 1) {
5188 return BuildingNecessity::kForbidden;
5189 }
5190 if (critical_mine_unoccupied(gametime)) {
5191 return BuildingNecessity::kForbidden;
5192 }
5193 if (!basic_economy_established) {
5194 return BuildingNecessity::kForbidden;
5195 }
5196 const uint16_t min_roads_count =
5197 40 + std::abs(management_data.get_military_number_at(33)) / 2;
5198 if (roads.size() < static_cast<size_t>(min_roads_count * (1 + bo.total_count()))) {
5199 return BuildingNecessity::kForbidden;
5200 }
5201 bo.primary_priority += (roads.size() - min_roads_count * (1 + bo.total_count())) *
5202 (2 + std::abs(management_data.get_military_number_at(143)) / 5);
5203 return BuildingNecessity::kNeeded;
5204 }
5205
5206 // Let deal with productionsites now
5207 // First we iterate over outputs of building, count warehoused stock
5208 // and deciding if we have enough on stock (in warehouses)
5209
5210 // Calulate preciousness
5211 bo.max_preciousness = bo.initial_preciousness;
5212 bo.max_needed_preciousness = bo.initial_preciousness;
5213 for (uint32_t m = 0; m < bo.ware_outputs.size(); ++m) {
5214 DescriptionIndex wt(static_cast<size_t>(bo.ware_outputs.at(m)));
5215
5216 uint16_t target = tribe_->get_ware_descr(wt)->default_target_quantity(tribe_->name());
5217 if (target == Widelands::kInvalidWare) {
5218 target = kTargetQuantCap;
5219 }
5220 target /= 3;
5221
5222 // at least 1
5223 target = std::max<uint16_t>(target, 1);
5224
5225 // it seems there are wares with 0 preciousness (no entry in init files?), but we need
5226 // positive value here.
5227 // TODO(GunChleoc): Since we require in Tribes::postload() that this is set for all wares used
5228 // by a tribe, something seems to be wrong here. It should always be > 0.
5229 const uint16_t preciousness =
5230 std::max<uint16_t>(wares.at(bo.ware_outputs.at(m)).preciousness, 1);
5231
5232 if (calculate_stocklevel(wt) < target ||
5233 site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
5234 if (bo.max_needed_preciousness < preciousness) {
5235 bo.max_needed_preciousness = preciousness;
5236 }
5237 if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
5238 bo.max_needed_preciousness +=
5239 std::abs(management_data.get_military_number_at(144)) / 10;
5240 }
5241 }
5242
5243 if (bo.max_preciousness < preciousness) {
5244 bo.max_preciousness = preciousness;
5245 }
5246 }
5247
5248 // Do we have enough input materials on stock?
5249 bool inputs_on_stock = true;
5250 if (bo.type == BuildingObserver::Type::kProductionsite ||
5251 bo.type == BuildingObserver::Type::kMine) {
5252 for (auto input : bo.inputs) {
5253 if (calculate_stocklevel(input) < 2) {
5254 inputs_on_stock = false;
5255 break;
5256 }
5257 }
5258 }
5259
5260 // Do we have enough workers available in warehouses?
5261 bool workers_on_stock = true;
5262 if (bo.type == BuildingObserver::Type::kProductionsite ||
5263 bo.type == BuildingObserver::Type::kMine) {
5264 for (auto worker : bo.positions) {
5265 if (calculate_stocklevel(worker, WareWorker::kWorker) < 1) {
5266 workers_on_stock = false;
5267 break;
5268 }
5269 }
5270 }
5271
5272 // Do we have suppliers productionsites?
5273 const bool suppliers_exist = check_supply(bo);
5274
5275 if (!bo.ware_outputs.empty()) {
5276 assert(bo.max_preciousness > 0);
5277 }
5278
5279 if (bo.is(BuildingAttribute::kShipyard)) {
5280 assert(bo.max_preciousness == 0);
5281 }
5282
5283 // This flag is to be used when buildig is forced. AI will not build another building when
5284 // a substitution exists. F.e. mines or pairs like tavern-inn
5285 // To skip unnecessary calculation, we calculate this only if we have 0 count of the buildings
5286 bool has_substitution_building = false;
5287 if (bo.total_count() == 0 && bo.is(BuildingAttribute::kUpgradeSubstitutes) &&
5288 bo.type == BuildingObserver::Type::kProductionsite) {
5289 const DescriptionIndex enhancement = bo.desc->enhancement();
5290 BuildingObserver& en_bo =
5291 get_building_observer(tribe_->get_building_descr(enhancement)->name().c_str());
5292 if (en_bo.total_count() > 0) {
5293 has_substitution_building = true;
5294 }
5295 }
5296 if (bo.total_count() == 0 && bo.type == BuildingObserver::Type::kMine) {
5297 if (mines_per_type[bo.mines].total_count() > 0) {
5298 has_substitution_building = true;
5299 }
5300 }
5301
5302 // Some buildings are upgraded to ones that does not produce current output, so we need to have
5303 // two of current buildings to have at least one left after one of them is upgraded
5304 // Logic is: after 30th minute we need second building if there is no enhanced building yet,
5305 // and after 90th minute we want second building unconditionally
5306 bool needs_second_for_upgrade = false;
5307 if (gametime > 30 * 60 * 1000 && bo.cnt_built == 1 && bo.cnt_under_construction == 0 &&
5308 bo.is(BuildingAttribute::kUpgradeExtends) &&
5309 !bo.is(BuildingAttribute::kUpgradeSubstitutes) &&
5310 bo.type == BuildingObserver::Type::kProductionsite) {
5311 const DescriptionIndex enhancement = bo.desc->enhancement();
5312 BuildingObserver& en_bo =
5313 get_building_observer(tribe_->get_building_descr(enhancement)->name().c_str());
5314 if ((gametime > 30 * 60 * 1000 && en_bo.total_count() == 0) || gametime > 90 * 60 * 1000) {
5315 // We fake this
5316 bo.max_needed_preciousness = bo.max_preciousness * 2;
5317 needs_second_for_upgrade = true;
5318 }
5319 }
5320
5321 // And finally the 'core' of this function
5322 // First deal with construction of new sites
5323 if (purpose == PerfEvaluation::kForConstruction) {
5324 // Inform if we are above ai type limit.
5325 if (bo.total_count() > bo.cnt_limit_by_aimode) {
5326 log("AI check_building_necessity: Too many %s: %d, ai limit: %d\n", bo.name,
5327 bo.total_count(), bo.cnt_limit_by_aimode);
5328 }
5329
5330 if (bo.forced_after < gametime && bo.total_count() == 0 && !has_substitution_building) {
5331 bo.max_needed_preciousness = bo.max_preciousness;
5332 return BuildingNecessity::kForced;
5333 } else if (bo.prohibited_till > gametime) {
5334 return BuildingNecessity::kForbidden;
5335 } else if (bo.is(BuildingAttribute::kHunter) || bo.is(BuildingAttribute::kFisher) ||
5336 bo.is(BuildingAttribute::kWell)) {
5337
5338 bo.cnt_target = 1 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 25;
5339
5340 if (bo.cnt_under_construction + bo.unoccupied_count > 0) {
5341 return BuildingNecessity::kForbidden;
5342 }
5343
5344 static int16_t inputs[kFNeuronBitSize] = {0};
5345 // Reseting values as the variable is static
5346 for (int i = 0; i < kFNeuronBitSize; i++) {
5347 inputs[i] = 0;
5348 }
5349 inputs[0] = (bo.max_needed_preciousness == 0) ? -1 : 0;
5350 inputs[1] = (bo.max_needed_preciousness > 0) ? 2 : 0;
5351 inputs[2] = (bo.max_needed_preciousness == 0) ? -3 : 0;
5352 inputs[3] = (bo.total_count() > 0) ? -1 : 0;
5353 inputs[4] = (bo.total_count() > 1) ? -1 : 0;
5354 inputs[5] = (bo.total_count() > 0) ? -1 : 0;
5355 inputs[6] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? +1 : 0;
5356 inputs[7] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? +2 : 0;
5357 inputs[8] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? +3 : 0;
5358 inputs[9] = (expansion_type.get_expansion_type() == ExpansionMode::kBoth) ? +1 : 0;
5359 inputs[10] = (expansion_type.get_expansion_type() == ExpansionMode::kBoth) ? +1 : 0;
5360 inputs[11] = (bo.last_building_built + 5 * 60 * 100 < gametime) ? +1 : 0;
5361 inputs[12] = (bo.last_building_built + 10 * 60 * 100 < gametime) ? +1 : 0;
5362 inputs[13] = (bo.last_building_built + 20 * 60 * 100 < gametime) ? +1 : 0;
5363 inputs[14] = (bo.total_count() >= bo.cnt_target) ? -1 : 0;
5364 inputs[15] = (bo.total_count() >= bo.cnt_target) ? -2 : 0;
5365 inputs[16] = (bo.total_count() < bo.cnt_target) ? -1 : 0;
5366 inputs[17] = (bo.total_count() < bo.cnt_target) ? -2 : 0;
5367 inputs[18] = +1;
5368 inputs[19] = +2;
5369 inputs[20] = -1;
5370 inputs[21] = -2;
5371 inputs[22] = -3;
5372 inputs[23] = -4;
5373 inputs[24] = -5;
5374 inputs[25] = (basic_economy_established) ? 1 : -1;
5375 inputs[26] = (basic_economy_established) ? 1 : -1;
5376 inputs[27] = (bo.total_count() > 0 && spots_ < kSpotsEnough) ? -2 : 0;
5377 inputs[28] = (bo.total_count() > 0 && spots_ < kSpotsTooLittle) ? -2 : 0;
5378 inputs[29] = (spots_ < kSpotsEnough) ? -1 : 0;
5379 inputs[30] = (spots_ < kSpotsTooLittle) ? -1 : 0;
5380 int16_t tmp_score = 0;
5381 for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
5382 if (management_data.f_neuron_pool[53].get_position(i)) {
5383 tmp_score += inputs[i];
5384 }
5385 }
5386 if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
5387 tmp_score += 4;
5388 }
5389 if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
5390 tmp_score -= 2;
5391 }
5392
5393 if (tmp_score < 0) {
5394 return BuildingNecessity::kForbidden;
5395 } else {
5396 if (bo.max_needed_preciousness <= 0) {
5397 bo.max_needed_preciousness = 1;
5398 }
5399 bo.primary_priority =
5400 1 + tmp_score * std::abs(management_data.get_military_number_at(137) / 2);
5401 return BuildingNecessity::kNeeded;
5402 }
5403 } else if (bo.is(BuildingAttribute::kLumberjack)) {
5404 if (bo.total_count() > 1 && (bo.cnt_under_construction + bo.unoccupied_count > 0)) {
5405 return BuildingNecessity::kForbidden;
5406 }
5407 bo.cnt_target = 2;
5408 // adjusting/decreasing based on cnt_limit_by_aimode
5409 bo.cnt_target = std::min(bo.cnt_target, bo.cnt_limit_by_aimode);
5410
5411 // for case the wood is not needed yet, to avoid inconsistency later on
5412 bo.max_needed_preciousness = bo.max_preciousness;
5413
5414 bo.primary_priority = 0;
5415
5416 if (bo.total_count() < bo.cnt_target) {
5417 bo.primary_priority += 10 * std::abs(management_data.get_military_number_at(34));
5418 }
5419 if (get_stocklevel(bo, gametime) < 10) {
5420 bo.primary_priority += std::abs(management_data.get_military_number_at(118));
5421 }
5422 if (bo.total_count() < bo.cnt_target) {
5423 return BuildingNecessity::kNeeded;
5424 } else {
5425 return BuildingNecessity::kAllowed;
5426 }
5427 } else if (bo.is(BuildingAttribute::kRanger)) {
5428
5429 // making sure we have one completed lumberjack
5430 if (get_building_observer(BuildingAttribute::kLumberjack).cnt_built < 1) {
5431 return BuildingNecessity::kForbidden;
5432 }
5433
5434 // genetic algorithm to decide whether new rangers are needed
5435 static int16_t tmp_target = 2;
5436 tmp_target = 2;
5437 static int16_t inputs[2 * kFNeuronBitSize] = {0};
5438 // Reseting values as the variable is static
5439 for (int i = 0; i < 2 * kFNeuronBitSize; i++) {
5440 inputs[i] = 0;
5441 }
5442
5443 inputs[0] = (persistent_data->trees_around_cutters < 10) * 2;
5444 inputs[1] = (persistent_data->trees_around_cutters < 20) * 2;
5445 inputs[2] = (persistent_data->trees_around_cutters < 30) * 2;
5446 inputs[3] = (persistent_data->trees_around_cutters < 40) * 2;
5447 inputs[4] = (persistent_data->trees_around_cutters < 50) * 1;
5448 inputs[5] = (persistent_data->trees_around_cutters < 60) * 1;
5449 inputs[6] = (persistent_data->trees_around_cutters < 10) * 1;
5450 inputs[7] = (persistent_data->trees_around_cutters < 20) * 1;
5451 inputs[8] = (persistent_data->trees_around_cutters < 100) * 1;
5452 inputs[9] = (persistent_data->trees_around_cutters < 200) * 1;
5453 inputs[10] = (persistent_data->trees_around_cutters < 300) * 1;
5454 inputs[11] = (persistent_data->trees_around_cutters < 400) * 1;
5455 inputs[12] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
5456 inputs[13] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
5457 inputs[14] = (get_stocklevel(bo, gametime) < 10) * 1;
5458 inputs[15] = (get_stocklevel(bo, gametime) < 10) * 1;
5459 inputs[16] = (get_stocklevel(bo, gametime) < 2) * 1;
5460 if (gametime > 15 * 60) {
5461 inputs[17] = (get_stocklevel(bo, gametime) > 30) * -1;
5462 inputs[18] = (get_stocklevel(bo, gametime) > 20) * -1;
5463 inputs[19] = (get_stocklevel(bo, gametime) > 10) * -1;
5464 } else {
5465 inputs[20] = 1;
5466 inputs[21] = 1;
5467 }
5468 inputs[22] = (basic_economy_established) ? -1 : 1;
5469 inputs[23] = (msites_in_constr() > 0) ? 1 : -2;
5470 inputs[24] = (msites_in_constr() > 1) ? 1 : -2;
5471 inputs[25] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
5472 if (gametime > 90 * 60) {
5473 inputs[26] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
5474 inputs[27] = (persistent_data->trees_around_cutters < 20) * 1;
5475 }
5476 if (gametime > 45 * 60) {
5477 inputs[28] = (wood_policy_ != WoodPolicy::kAllowRangers) * 1;
5478 inputs[29] = (persistent_data->trees_around_cutters < 20) * 1;
5479 inputs[30] = (get_stocklevel(bo, gametime) > 30) * -1;
5480 }
5481 inputs[31] = (persistent_data->trees_around_cutters < 100) * 2;
5482 inputs[32] = (persistent_data->trees_around_cutters < 200) * 2;
5483 inputs[33] = (mines_per_type[iron_resource_id].total_count() <= 1) * -1;
5484 inputs[34] = (mines_per_type[iron_resource_id].total_count() <= 1) * -1;
5485 inputs[35] = (mines_per_type[iron_resource_id].total_count() == 0) * -1;
5486 inputs[36] = (mines_per_type[iron_resource_id].total_count() == 0) * -1;
5487 inputs[37] = -1;
5488 inputs[38] = -1;
5489 inputs[39] = -1;
5490 if (productionsites.size() / 3 > static_cast<uint32_t>(bo.total_count()) &&
5491 get_stocklevel(bo, gametime) < 20) {
5492 inputs[40] = (persistent_data->trees_around_cutters < 40) * 1;
5493 inputs[41] = (persistent_data->trees_around_cutters < 60) * 1;
5494 inputs[42] = (persistent_data->trees_around_cutters < 80) * 1;
5495 }
5496 if (productionsites.size() / 4 > static_cast<uint32_t>(bo.total_count()) &&
5497 get_stocklevel(bo, gametime) < 20) {
5498 inputs[43] = (persistent_data->trees_around_cutters < 40) * 2;
5499 inputs[44] = (persistent_data->trees_around_cutters < 60) * 2;
5500 inputs[45] = (persistent_data->trees_around_cutters < 80) * 2;
5501 }
5502
5503 if (productionsites.size() / 2 > static_cast<uint32_t>(bo.total_count()) &&
5504 get_stocklevel(bo, gametime) < 10) {
5505 inputs[46] = (persistent_data->trees_around_cutters < 20) * 1;
5506 inputs[47] = (persistent_data->trees_around_cutters < 40) * 1;
5507 inputs[48] = (persistent_data->trees_around_cutters < 60) * 1;
5508 inputs[49] = (persistent_data->trees_around_cutters < 80) * 1;
5509 }
5510 inputs[50] = (bo.last_building_built + 1 * 60 * 100 > gametime) * -2;
5511 inputs[51] = (bo.last_building_built + 2 * 60 * 100 > gametime) * -2;
5512 inputs[52] = (bo.last_building_built + 4 * 60 * 100 > gametime) * -2;
5513 inputs[53] = (bo.last_building_built + 6 * 60 * 100 > gametime) * -2;
5514 inputs[54] = (5 * 60 * 100 > gametime) * -2;
5515 inputs[55] = (6 * 60 * 100 > gametime) * -2;
5516 inputs[56] = (8 * 60 * 100 > gametime) * -2;
5517 inputs[57] = (10 * 60 * 100 > gametime) * -2;
5518 inputs[58] = (spots_ < kSpotsEnough) ? -2 : 0;
5519 inputs[59] = (spots_ < kSpotsTooLittle) ? -2 : 0;
5520 inputs[60] = (spots_ < kSpotsTooLittle) ? -2 : 0;
5521 inputs[61] = (spots_ < kSpotsTooLittle) ? -2 : 0;
5522 inputs[62] = (basic_economy_established) ? 0 : -2;
5523 inputs[63] = (spots_ < kSpotsTooLittle) ? 0 : -2;
5524
5525 for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
5526 if (management_data.f_neuron_pool[14].get_position(i)) {
5527 assert(inputs[i] >= -2 && inputs[i] <= 2);
5528 tmp_target += inputs[i];
5529 }
5530 if (management_data.f_neuron_pool[15].get_position(i)) {
5531 tmp_target += inputs[kFNeuronBitSize + i];
5532 assert(inputs[kFNeuronBitSize + i] >= -2 && inputs[kFNeuronBitSize + i] <= 2);
5533 }
5534 }
5535
5536 if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
5537 tmp_target -= std::abs(management_data.get_military_number_at(145) / 10);
5538 }
5539
5540 tmp_target = std::max<int16_t>(tmp_target, 2);
5541
5542 bo.cnt_target = tmp_target;
5543
5544 // adjusting/decreasing based on cnt_limit_by_aimode
5545 bo.cnt_target = std::min(bo.cnt_target, bo.cnt_limit_by_aimode);
5546
5547 assert(bo.cnt_target > 1 && bo.cnt_target < 1000);
5548
5549 if (wood_policy_ != WoodPolicy::kAllowRangers) {
5550 return BuildingNecessity::kForbidden;
5551 }
5552
5553 if (bo.total_count() > bo.cnt_target) {
5554 return BuildingNecessity::kForbidden;
5555 }
5556
5557 const bool parallel_construction = (bo.total_count() + 2 < bo.cnt_target);
5558 if (parallel_construction && (bo.cnt_under_construction + bo.unoccupied_count <= 1)) {
5559 return BuildingNecessity::kNeeded;
5560 } else if (bo.cnt_under_construction + bo.unoccupied_count == 0) {
5561 return BuildingNecessity::kNeeded;
5562 }
5563 return BuildingNecessity::kForbidden;
5564 } else if (bo.is(BuildingAttribute::kNeedsRocks) &&
5565 bo.cnt_under_construction + bo.unoccupied_count == 0) {
5566 bo.max_needed_preciousness = bo.max_preciousness; // even when rocks are not needed
5567 return BuildingNecessity::kAllowed;
5568 } else if (!bo.production_hints.empty() && !bo.is(BuildingAttribute::kSupportingProducer)) {
5569 // Pure supporting sites only
5570
5571 if (bo.cnt_under_construction + bo.unoccupied_count - bo.unconnected_count > 0) {
5572 return BuildingNecessity::kForbidden;
5573 }
5574
5575 // Rangers have been processed above
5576 assert(!bo.is(BuildingAttribute::kRanger));
5577
5578 bo.primary_priority = 0;
5579
5580 if (!basic_economy_established) {
5581 bo.cnt_target = bo.basic_amount;
5582 } else {
5583 bo.cnt_target = 1 + static_cast<int32_t>(mines_.size() + productionsites.size()) / 30;
5584 }
5585
5586 if (bo.total_count() > bo.cnt_target + 1) {
5587 return BuildingNecessity::kForbidden;
5588 }
5589
5590 // We allow another helper site if:
5591 // a) we are under target
5592 // b) if there is shortage of supported ware
5593 if (bo.total_count() < bo.cnt_target ||
5594 (get_stocklevel(bo, gametime) == 0 &&
5595 bo.last_building_built + 10 * 60 * 100 < gametime)) {
5596
5597 if (persistent_data->remaining_basic_buildings.count(bo.id)) {
5598 bo.primary_priority += std::abs(management_data.get_military_number_at(60) * 10);
5599 }
5600
5601 if (bo.total_count() < bo.cnt_target) {
5602 if (basic_economy_established) {
5603 bo.primary_priority += std::abs(management_data.get_military_number_at(51) * 6);
5604 } else if (persistent_data->remaining_basic_buildings.count(bo.id)) {
5605 bo.primary_priority += std::abs(management_data.get_military_number_at(146) * 6);
5606 } else {
5607 bo.primary_priority +=
5608 -200 + std::abs(management_data.get_military_number_at(147) * 8);
5609 }
5610 }
5611
5612 return BuildingNecessity::kAllowed;
5613 }
5614 return BuildingNecessity::kForbidden;
5615
5616 } else if (bo.is(BuildingAttribute::kBarracks)) {
5617 if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
5618 return BuildingNecessity::kForbidden;
5619 }
5620 if (gametime > 30 * 60 * 1000 && bo.total_count() == 0) {
5621
5622 int16_t tmp_score = 1;
5623 tmp_score += mines_per_type[iron_resource_id].total_count();
5624 tmp_score += (soldier_status_ == SoldiersStatus::kBadShortage) * 2;
5625 tmp_score += (soldier_status_ == SoldiersStatus::kShortage) * 2;
5626 tmp_score += (gametime / 60 / 1000 - 20) / 4;
5627 bo.max_needed_preciousness =
5628 1 + tmp_score * std::abs(management_data.get_military_number_at(134)) / 15;
5629 bo.max_preciousness = bo.max_needed_preciousness;
5630 return BuildingNecessity::kNeeded;
5631 } else {
5632 bo.max_needed_preciousness = 0;
5633 bo.max_preciousness = 0;
5634 return BuildingNecessity::kForbidden;
5635 }
5636 } else if (bo.type == BuildingObserver::Type::kMine) {
5637 bo.primary_priority = bo.max_needed_preciousness;
5638 if (mines_per_type[bo.mines].total_count() == 0 &&
5639 site_needed_for_economy != BasicEconomyBuildingStatus::kDiscouraged) {
5640 // unless a mine is prohibited, we want to have at least one of the kind
5641 bo.max_needed_preciousness = bo.max_preciousness;
5642 return BuildingNecessity::kNeeded;
5643 } else if (mines_per_type[bo.mines].finished == mines_per_type[bo.mines].total_count() &&
5644 bo.current_stats >
5645 static_cast<uint32_t>(
5646 85 + std::abs(management_data.get_military_number_at(129)) / 10) &&
5647 site_needed_for_economy != BasicEconomyBuildingStatus::kDiscouraged) {
5648 bo.max_needed_preciousness = bo.max_preciousness;
5649 return BuildingNecessity::kNeeded;
5650 }
5651 if (bo.max_needed_preciousness == 0) {
5652 return BuildingNecessity::kNotNeeded;
5653 }
5654 if (gametime - bo.construction_decision_time < kBuildingMinInterval) {
5655 return BuildingNecessity::kForbidden;
5656 }
5657 if (mines_per_type[bo.mines].in_construction > 0) {
5658 return BuildingNecessity::kForbidden;
5659 }
5660 if (mines_per_type[bo.mines].finished >= 1 && bo.current_stats < 50) {
5661 return BuildingNecessity::kForbidden;
5662 }
5663
5664 if (gametime < bo.last_building_built + 3 * 60 * 1000) {
5665 return BuildingNecessity::kForbidden;
5666 }
5667
5668 static int16_t inputs[kFNeuronBitSize] = {0};
5669 // Reseting values as the variable is static
5670 for (int i = 0; i < kFNeuronBitSize; i++) {
5671 inputs[i] = 0;
5672 }
5673 inputs[0] = (gametime < 15 * 60 * 1000) ? -2 : 0;
5674 inputs[1] = (gametime < 30 * 60 * 1000) ? -2 : 0;
5675 inputs[2] = (gametime < 45 * 60 * 1000) ? -2 : 0;
5676 inputs[3] = (mines_per_type[bo.mines].total_count() == 1) ? 3 : 0;
5677 inputs[4] = (mines_per_type[bo.mines].total_count() == 1) ? 2 : 0;
5678 inputs[5] = (bo.mines == iron_resource_id) ? 2 : 1;
5679 inputs[6] = (bo.current_stats - 50) / 10;
5680 inputs[7] = (gametime < 15 * 60 * 1000) ? -1 : 0;
5681 inputs[8] = (gametime < 30 * 60 * 1000) ? -1 : 0;
5682 inputs[9] = (gametime < 45 * 60 * 1000) ? -1 : 0;
5683 inputs[10] = (mines_per_type[bo.mines].total_count() == 1) ? 2 : 0;
5684 inputs[11] = (mines_per_type[bo.mines].total_count() == 1) ? 1 : 0;
5685 inputs[12] = (bo.mines == iron_resource_id) ? 2 : 0;
5686 inputs[13] = (bo.current_stats - 50) / 10;
5687 inputs[14] = (bo.current_stats - 50) / 10;
5688 inputs[15] = management_data.get_military_number_at(123) / 10;
5689 inputs[16] = 0;
5690 inputs[17] = (inputs_on_stock) ? 0 : -2;
5691 inputs[18] = (suppliers_exist) ? 0 : -3;
5692 inputs[19] = (inputs_on_stock) ? 0 : -4;
5693 inputs[20] = (mines_per_type[bo.mines].total_count() == 1) ? 3 : 0;
5694 inputs[21] = (mines_per_type[bo.mines].total_count() == 1) ? 2 : 0;
5695 inputs[22] = (bo.current_stats - 50) / 10;
5696 inputs[23] = (bo.current_stats - 50) / 20;
5697 inputs[24] = (suppliers_exist) ? 0 : -5;
5698 inputs[25] = (suppliers_exist) ? 0 : -2;
5699 inputs[26] = (workers_on_stock) ? 0 : -5;
5700 inputs[27] = (workers_on_stock) ? 0 : -2;
5701 inputs[28] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ? 1 : 0;
5702 inputs[29] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ? 3 : 0;
5703 inputs[30] = (mines_per_type[bo.mines].is_critical) ? 1 : -1;
5704
5705 int16_t tmp_score = management_data.get_military_number_at(83) / 5;
5706
5707 // Building productionsites above limit in Basic economy mode is strongly discouraged, but
5708 // still possible
5709 const int16_t basic_economy_score =
5710 25 + std::abs(management_data.get_military_number_at(122) * 2);
5711
5712 if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
5713 tmp_score += basic_economy_score;
5714 }
5715
5716 if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
5717 tmp_score -= basic_economy_score;
5718 }
5719
5720 for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
5721 if (management_data.f_neuron_pool[36].get_position(i)) {
5722 tmp_score += inputs[i];
5723 }
5724 }
5725 if (tmp_score < 0) {
5726 return BuildingNecessity::kNeededPending;
5727 } else {
5728 bo.primary_priority +=
5729 tmp_score * std::abs(management_data.get_military_number_at(127) / 5);
5730 return BuildingNecessity::kNeeded;
5731 }
5732
5733 } else if (bo.max_needed_preciousness > 0) {
5734
5735 static int16_t inputs[4 * kFNeuronBitSize] = {0};
5736 // Reseting values as the variable is static
5737 for (int i = 0; i < 4 * kFNeuronBitSize; i++) {
5738 inputs[i] = 0;
5739 }
5740 inputs[0] = (bo.total_count() <= 1) ?
5741 std::abs(management_data.get_military_number_at(110)) / 10 :
5742 0;
5743 inputs[1] = bo.total_count() * -3 / 2;
5744 inputs[2] =
5745 (bo.total_count() == 0) ? std::abs(management_data.get_military_number_at(0)) / 10 : 0;
5746 inputs[3] = (gametime >= 25 * 60 * 1000 && bo.inputs.empty()) ?
5747 management_data.get_military_number_at(1) / 10 :
5748 0;
5749 inputs[4] = (bo.max_needed_preciousness >= 10) ?
5750 std::abs(management_data.get_military_number_at(2)) / 10 :
5751 0;
5752 inputs[5] =
5753 (!bo.ware_outputs.empty() && bo.current_stats > 10 + 70 / bo.ware_outputs.size()) ?
5754 management_data.get_military_number_at(3) / 10 :
5755 0;
5756 inputs[6] = (needs_second_for_upgrade) ?
5757 std::abs(management_data.get_military_number_at(4)) / 5 :
5758 0;
5759 inputs[7] = (bo.cnt_under_construction + bo.unoccupied_count) * -1 *
5760 std::abs(management_data.get_military_number_at(9)) / 5;
5761 inputs[8] =
5762 (!bo.ware_outputs.empty() && bo.current_stats > 25 + 70 / bo.ware_outputs.size()) ?
5763 management_data.get_military_number_at(7) / 8 :
5764 0;
5765 inputs[9] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ?
5766 std::abs(management_data.get_military_number_at(10)) / 10 :
5767 0;
5768 inputs[10] =
5769 (bo.build_material_shortage) ? -management_data.get_military_number_at(39) / 10 : 0;
5770 inputs[11] = (wood_policy_ == WoodPolicy::kDismantleRangers ||
5771 wood_policy_ == WoodPolicy::kStopRangers) ?
5772 std::abs(management_data.get_military_number_at(15)) / 10 :
5773 0;
5774 inputs[12] = (gametime >= 15 * 60 * 1000) ?
5775 std::abs(management_data.get_military_number_at(94)) / 10 :
5776 0;
5777 inputs[13] = management_data.get_military_number_at(8) / 10;
5778 inputs[14] = (persistent_data->trees_around_cutters < 20) ?
5779 -1 * std::abs(management_data.get_military_number_at(95)) / 10 :
5780 0;
5781 inputs[15] = (persistent_data->trees_around_cutters > 100) ?
5782 std::abs(management_data.get_military_number_at(96)) / 10 :
5783 0;
5784 inputs[16] = (player_statistics.any_enemy_seen_lately(gametime)) ?
5785 management_data.get_military_number_at(97) / 10 :
5786 0;
5787 inputs[17] =
5788 (spots_ > kSpotsEnough) ? std::abs(management_data.get_military_number_at(74)) / 8 : 0;
5789 inputs[18] = management_data.get_military_number_at(98) / 10;
5790 inputs[19] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ?
5791 -1 * std::abs(management_data.get_military_number_at(40)) / 10 :
5792 0;
5793 inputs[20] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ?
5794 std::abs(management_data.get_military_number_at(50)) / 10 :
5795 0;
5796 inputs[21] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy ||
5797 expansion_type.get_expansion_type() == ExpansionMode::kBoth) ?
5798 3 :
5799 0;
5800 inputs[22] =
5801 (bo.total_count() == 0 && bo.is(BuildingAttribute::kBuildingMatProducer)) ? 3 : 0;
5802 if (bo.cnt_built > 0 && !bo.ware_outputs.empty()) {
5803 inputs[22] += bo.current_stats / 10;
5804 }
5805 inputs[23] = (!player_statistics.strong_enough(player_number())) ? 5 : 0;
5806 inputs[24] = (bo.inputs.empty()) ? 6 : 0;
5807 inputs[25] =
5808 (bo.total_count() == 0 && bo.is(BuildingAttribute::kBuildingMatProducer)) ? 4 : 0;
5809 inputs[26] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? 2 : 0;
5810 inputs[27] = (wood_policy_ == WoodPolicy::kDismantleRangers ||
5811 wood_policy_ == WoodPolicy::kStopRangers) ?
5812 4 :
5813 0;
5814 inputs[28] = (bo.max_needed_preciousness >= 10) ? 4 : 0;
5815 inputs[29] = (bo.inputs.empty() && bo.max_needed_preciousness >= 10) ? 3 : 0;
5816 inputs[30] = bo.max_needed_preciousness / 2;
5817 inputs[31] = ((bo.cnt_under_construction + bo.unoccupied_count) > 0) ? -5 : 0;
5818 inputs[32] = bo.max_needed_preciousness / 2;
5819 inputs[33] = -(bo.cnt_under_construction + bo.unoccupied_count) * 4;
5820 if (bo.cnt_built > 0 && !bo.ware_outputs.empty() && !bo.inputs.empty()) {
5821 inputs[34] +=
5822 bo.current_stats / (std::abs(management_data.get_military_number_at(192)) + 1) * 10;
5823 }
5824 inputs[35] = (!bo.ware_outputs.empty() && !bo.inputs.empty() &&
5825 bo.current_stats > 10 + 70 / bo.ware_outputs.size()) ?
5826 2 :
5827 0;
5828 inputs[36] = (!bo.ware_outputs.empty() && !bo.inputs.empty() &&
5829 bo.cnt_under_construction + bo.unoccupied_count == 0) ?
5830 bo.current_stats / 12 :
5831 0;
5832 if (bo.cnt_built > 0 && !bo.inputs.empty() && !bo.ware_outputs.empty() &&
5833 bo.current_stats < 20) {
5834 inputs[37] = -5;
5835 }
5836 inputs[38] = (bo.cnt_under_construction + bo.unoccupied_count > 0) ? -10 : 0;
5837 if (bo.cnt_built > 0 && !bo.ware_outputs.empty() && bo.current_stats < 15) {
5838 inputs[39] = -10;
5839 }
5840 inputs[40] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) ? 3 : 0;
5841 inputs[41] = (bo.build_material_shortage) ? -3 : 0;
5842 inputs[42] = (!player_statistics.strong_enough(player_number())) ? 2 : 0;
5843 inputs[43] = (bo.inputs.empty()) ? 3 : 0;
5844 inputs[44] = (bo.inputs.empty() && bo.max_needed_preciousness >= 10) ? 3 : 0;
5845 inputs[45] = bo.max_needed_preciousness / 2;
5846 inputs[46] =
5847 (!bo.ware_outputs.empty() && bo.current_stats > 10 + 70 / bo.ware_outputs.size()) ? 4 :
5848 0;
5849 inputs[47] = (!bo.ware_outputs.empty() && bo.current_stats > 85) ? 4 : 0;
5850 inputs[48] = (bo.max_needed_preciousness >= 10 &&
5851 (bo.cnt_under_construction + bo.unoccupied_count) == 1) ?
5852 5 :
5853 0;
5854 inputs[49] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -4 : 1;
5855 inputs[50] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -1 : 1;
5856 inputs[51] = (gametime < 20 * 60 * 1000) ? -4 : 0;
5857 inputs[52] = (bo.total_count() == 0) ? 4 : 0;
5858 inputs[53] = (bo.total_count() == 0) ? 2 : 0;
5859 inputs[54] = (spots_ < kSpotsEnough) ? -5 : 0;
5860 inputs[55] = (bo.max_needed_preciousness >= 10 &&
5861 (bo.cnt_under_construction + bo.unoccupied_count) == 1) ?
5862 3 :
5863 0;
5864 inputs[56] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -8 : 1;
5865 inputs[57] = (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? -6 : 1;
5866 inputs[58] = (bo.total_count() == 0 && inputs_on_stock) ? 4 : 0;
5867 inputs[59] = (bo.inputs.empty()) ? 5 : bo.current_stats / 10 - 5;
5868 inputs[60] = (spots_ < kSpotsTooLittle) ? -10 : 0;
5869 inputs[61] = (player_statistics.any_enemy_seen_lately(gametime)) ? 2 : 0;
5870 inputs[62] = (player_statistics.any_enemy_seen_lately(gametime) &&
5871 bo.cnt_under_construction + bo.unoccupied_count == 0) ?
5872 6 :
5873 0;
5874 inputs[63] = (!bo.ware_outputs.empty() && !bo.inputs.empty()) ? bo.current_stats / 10 : 0;
5875 inputs[64] = (gametime > 20 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
5876 inputs[65] = (gametime > 45 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
5877 inputs[66] = (gametime > 60 * 60 * 1000 && bo.total_count() <= 1) ? 3 : 0;
5878 inputs[67] = (gametime > 50 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
5879 inputs[68] =
5880 (bo.inputs.empty() && gametime > 50 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
5881 inputs[69] =
5882 (!bo.inputs.empty() && gametime > 50 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
5883 inputs[70] =
5884 (bo.inputs.empty() && gametime > 25 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
5885 inputs[71] =
5886 (!bo.inputs.empty() && gametime > 25 * 60 * 1000 && bo.total_count() <= 1) ? 2 : 0;
5887 if (bo.last_building_built != kNever) {
5888 inputs[72] = (gametime < bo.last_building_built + 3 * 60 * 1000) ? -4 : 0;
5889 inputs[73] = (gametime < bo.last_building_built + 5 * 60 * 1000) ? -2 : 0;
5890 inputs[74] = (gametime < bo.last_building_built + 2 * 60 * 1000) ? -5 : 0;
5891 inputs[75] = (gametime < bo.last_building_built + 10 * 60 * 1000) ? -2 : 0;
5892 inputs[76] = (gametime < bo.last_building_built + 20 * 60 * 1000) ? -2 : 0;
5893 }
5894 inputs[77] = (gametime > 35 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
5895 inputs[78] = (gametime > 60 * 60 * 1000 && bo.total_count() == 0) ? 3 : 0;
5896 inputs[79] = (expansion_type.get_expansion_type() == ExpansionMode::kResources ||
5897 expansion_type.get_expansion_type() == ExpansionMode::kSpace) *
5898 management_data.get_military_number_at(37) / 10;
5899 inputs[80] = (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) *
5900 management_data.get_military_number_at(38) / 10;
5901 inputs[81] = (expansion_type.get_expansion_type() == ExpansionMode::kSpace) *
5902 management_data.get_military_number_at(46) / 10;
5903 inputs[82] = (inputs_on_stock) ? 0 : -2;
5904 inputs[83] = (suppliers_exist) ? 0 : -2;
5905 inputs[84] = (inputs_on_stock) ? 0 : -4;
5906 inputs[85] = (suppliers_exist) ? 0 : -4;
5907 inputs[86] = (inputs_on_stock) ? 0 : -8;
5908 inputs[87] = (suppliers_exist) ? 0 : -8;
5909 inputs[88] = (workers_on_stock) ? 0 : -2;
5910 inputs[89] = (workers_on_stock) ? 0 : -6;
5911 inputs[90] = (bo.is(BuildingAttribute::kBuildingMatProducer)) ?
5912 std::abs(management_data.get_military_number_at(10)) / 10 :
5913 0;
5914 inputs[91] = (bo.build_material_shortage) ? -2 : 0;
5915 inputs[92] = (numof_psites_in_constr < 4) ? 3 : 0;
5916 inputs[93] = (numof_psites_in_constr < 8) ? 3 : 0;
5917 inputs[94] = (bo.inputs.empty()) ? 5 : 0;
5918 inputs[95] = (bo.inputs.empty()) ? 3 : 0;
5919 inputs[96] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -2 : 0;
5920 inputs[97] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -8 : 0;
5921 inputs[98] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -4 : 0;
5922 inputs[99] = (wood_policy_ == WoodPolicy::kAllowRangers) ? -1 : 0;
5923 inputs[100] = (bo.total_count() == 0) ? 3 : 0;
5924 inputs[101] = (bo.total_count() == 0) ? 6 : 0;
5925 if (bo.is(BuildingAttribute::kSupportingProducer)) {
5926 if (bo.total_count() == 0) {
5927 inputs[102] = 1;
5928 inputs[103] = 2;
5929 inputs[104] = -2;
5930 }
5931 inputs[105] = -3;
5932 inputs[106] = -2;
5933 }
5934 inputs[107] =
5935 std::abs(management_data.get_military_number_at(194)) - get_stocklevel(bo, gametime);
5936 inputs[108] =
5937 std::abs(management_data.get_military_number_at(191)) - get_stocklevel(bo, gametime);
5938 inputs[109] = (!bo.inputs.empty() && gametime > 50 * 60 * 1000 && bo.total_count() <= 1) ?
5939 std::abs(management_data.get_military_number_at(163)) / 10 :
5940 0;
5941 inputs[110] = (bo.ware_outputs.size() == 1) ?
5942 (tribe_->get_ware_descr(bo.ware_outputs.at(0))
5943 ->default_target_quantity(tribe_->name()) -
5944 get_stocklevel(bo, gametime)) *
5945 std::abs(management_data.get_military_number_at(165)) / 20 :
5946 0;
5947 inputs[111] = bo.current_stats / (bo.ware_outputs.size() + 1);
5948 // boost for buildings supporting seafaring
5949 if (bo.is(BuildingAttribute::kSupportsSeafaring) && map_allows_seafaring_) {
5950 inputs[112] = std::abs(management_data.get_military_number_at(170)) / 10;
5951 inputs[113] = 4;
5952 if (bo.total_count() == 0) {
5953 inputs[114] = std::abs(management_data.get_military_number_at(172)) / 10;
5954 inputs[115] = 4;
5955 }
5956 }
5957 inputs[116] = -(bo.unoccupied_count * bo.unoccupied_count);
5958 inputs[117] = -(2 * bo.unoccupied_count);
5959
5960 int16_t tmp_score = 0;
5961 for (uint8_t i = 0; i < kFNeuronBitSize; ++i) {
5962 if (management_data.f_neuron_pool[8].get_position(i)) {
5963 const int16_t partial_input = inputs[i];
5964 tmp_score += partial_input;
5965 }
5966 if (management_data.f_neuron_pool[11].get_position(i)) {
5967 const int16_t partial_input = inputs[i + kFNeuronBitSize];
5968 tmp_score += partial_input;
5969 }
5970 if (management_data.f_neuron_pool[59].get_position(i)) {
5971 const int16_t partial_input = inputs[i + 2 * kFNeuronBitSize];
5972 tmp_score += partial_input;
5973 }
5974 if (management_data.f_neuron_pool[12].get_position(i)) {
5975 const int16_t partial_input = inputs[i + 3 * kFNeuronBitSize];
5976 tmp_score += partial_input;
5977 }
5978 }
5979
5980 const int32_t base_economy_bonus =
5981 30 + std::abs(management_data.get_military_number_at(142));
5982 if (site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) {
5983 tmp_score -= base_economy_bonus;
5984 } else if (site_needed_for_economy == BasicEconomyBuildingStatus::kEncouraged) {
5985 tmp_score += base_economy_bonus;
5986 }
5987
5988 const int16_t bottom_limit = management_data.get_military_number_at(73) / 2 +
5989 management_data.get_military_number_at(47) / 10;
5990 const int16_t upper_limit =
5991 bottom_limit + std::abs(management_data.get_military_number_at(44) / 3);
5992
5993 if (tmp_score > upper_limit) {
5994 // Productionsite is needed
5995 bo.primary_priority += (tmp_score - bottom_limit) / 2;
5996 return BuildingNecessity::kNeeded;
5997 } else if (tmp_score > bottom_limit) {
5998 // Site is needed, but not right now
5999 return BuildingNecessity::kNeededPending;
6000 } else {
6001 // Not allowed
6002 return BuildingNecessity::kForbidden;
6003 }
6004
6005 } else if (bo.is(BuildingAttribute::kShipyard)) {
6006 if (bo.total_count() > 0 ||
6007 (!basic_economy_established &&
6008 site_needed_for_economy == BasicEconomyBuildingStatus::kDiscouraged) ||
6009 !map_allows_seafaring_) {
6010 return BuildingNecessity::kForbidden;
6011 }
6012 bo.primary_priority = 0;
6013 if (num_ports > 0) {
6014 bo.primary_priority += std::abs(management_data.get_military_number_at(150) * 3);
6015 }
6016 if (spots_ < kSpotsTooLittle) {
6017 bo.primary_priority += std::abs(management_data.get_military_number_at(151) * 3);
6018 }
6019 if (bo.primary_priority > 0) {
6020 return BuildingNecessity::kNeeded;
6021 }
6022 return BuildingNecessity::kAllowed;
6023 } else if (bo.max_needed_preciousness == 0) {
6024 return BuildingNecessity::kNotNeeded;
6025 } else {
6026 return BuildingNecessity::kForbidden;
6027 }
6028 } else if (purpose == PerfEvaluation::kForDismantle) { // now for dismantling
6029 // never dismantle last building (a care should be taken elsewhere)
6030 assert(bo.total_count() > 0);
6031 if (bo.total_count() == 1) {
6032 return BuildingNecessity::kNeeded;
6033 } else if (bo.max_preciousness >= 10 && bo.total_count() == 2) {
6034 return BuildingNecessity::kNeeded;
6035 } else if (!bo.ware_outputs.empty() &&
6036 bo.current_stats > (10 + 60 / bo.ware_outputs.size()) / 2) {
6037 return BuildingNecessity::kNeeded;
6038 } else if (bo.inputs.size() == 1 &&
6039 calculate_stocklevel(static_cast<size_t>(bo.inputs.at(0))) >
6040 static_cast<unsigned int>(
6041 std::abs(management_data.get_military_number_at(171)))) {
6042 return BuildingNecessity::kNeeded;
6043 } else {
6044 return BuildingNecessity::kNotNeeded;
6045 }
6046 }
6047 NEVER_HERE();
6048 }
6049
6050 // counts produced output on stock
calculate_stocklevel(Widelands::DescriptionIndex wt,const WareWorker what) const6051 uint32_t DefaultAI::calculate_stocklevel(Widelands::DescriptionIndex wt,
6052 const WareWorker what) const {
6053 uint32_t count = 0;
6054
6055 for (const Widelands::WarehouseSiteObserver& obs : warehousesites) {
6056 if (what == WareWorker::kWare) {
6057 count += obs.site->get_wares().stock(wt);
6058 } else {
6059 count += obs.site->get_workers().stock(wt);
6060 }
6061 }
6062 return count;
6063 }
6064
6065 // This is a wrapper function to prevent too frequent recalculation of stocklevel
6066 // and distinguish if we count stocks for production hint, for outputs or for workers of a
6067 // productionsite
6068 // if multiple outputs, it returns lowest value
get_stocklevel(BuildingObserver & bo,const uint32_t gametime,const WareWorker what) const6069 uint32_t DefaultAI::get_stocklevel(BuildingObserver& bo,
6070 const uint32_t gametime,
6071 const WareWorker what) const {
6072 if (bo.stocklevel_time < gametime - 5 * 1000) {
6073 if (what == WareWorker::kWare && (!bo.production_hints.empty() || !bo.ware_outputs.empty())) {
6074 // looking for smallest value
6075 bo.stocklevel_count = std::numeric_limits<uint32_t>::max();
6076 for (auto ph : bo.production_hints) {
6077 const uint32_t res = calculate_stocklevel(static_cast<size_t>(ph), what);
6078 if (res < bo.stocklevel_count) {
6079 bo.stocklevel_count = res;
6080 }
6081 }
6082 for (auto ph : bo.ware_outputs) {
6083 const uint32_t res = calculate_stocklevel(static_cast<size_t>(ph), what);
6084 if (res < bo.stocklevel_count) {
6085 bo.stocklevel_count = res;
6086 }
6087 }
6088 } else if (what == WareWorker::kWorker) {
6089 bo.stocklevel_count = std::numeric_limits<uint32_t>::max();
6090 for (auto ph : bo.positions) {
6091 const uint32_t res = calculate_stocklevel(static_cast<size_t>(ph), what);
6092 if (res < bo.stocklevel_count) {
6093 bo.stocklevel_count = res;
6094 }
6095 }
6096 } else {
6097 bo.stocklevel_count = 0;
6098 }
6099 assert(bo.stocklevel_count < std::numeric_limits<uint32_t>::max());
6100 bo.stocklevel_time = gametime;
6101 }
6102 return bo.stocklevel_count;
6103 }
6104
6105 /**
6106 * This function takes care about the unowned and opposing territory and
6107 * recalculates the priority for non-military buildings
6108 * The goal is to minimize losses when territory is lost
6109 *
6110 * \arg bf = BuildableField to be checked
6111 * \arg prio = priority until now.
6112 *
6113 * \returns the recalculated priority
6114 */
recalc_with_border_range(const BuildableField & bf,int32_t prio)6115 int32_t DefaultAI::recalc_with_border_range(const BuildableField& bf, int32_t prio) {
6116
6117 // no change when priority is not positive number
6118 if (prio <= 0) {
6119 return prio;
6120 }
6121
6122 if (bf.enemy_nearby || bf.near_border) {
6123 prio /= 2;
6124 }
6125
6126 // if unowned territory nearby
6127 prio -= bf.unowned_land_nearby / 4;
6128 prio -= bf.enemy_owned_land_nearby / 3;
6129
6130 // further decrease the score if enemy nearby
6131 if (bf.enemy_nearby) {
6132 prio -= 10;
6133 }
6134
6135 // and if close (up to 2 fields away) from border
6136 if (bf.near_border) {
6137 prio -= 10;
6138 if (spots_ > 0 && spots_ < kSpotsEnough) {
6139 prio -= std::abs(management_data.neuron_pool[60].get_result_safe(
6140 kSpotsEnough / spots_, kAbsValue)) /
6141 4;
6142 }
6143 }
6144
6145 return prio;
6146 }
6147 // for buildable field, it considers effect of building of type bo on position coords
consider_productionsite_influence(BuildableField & field,Coords coords,const BuildingObserver & bo)6148 void DefaultAI::consider_productionsite_influence(BuildableField& field,
6149 Coords coords,
6150 const BuildingObserver& bo) {
6151 if (bo.is(BuildingAttribute::kSpaceConsumer) && !bo.is(BuildingAttribute::kRanger) &&
6152 game().map().calc_distance(coords, field.coords) < 8) {
6153 ++field.space_consumers_nearby;
6154 }
6155
6156 for (size_t i = 0; i < bo.inputs.size(); ++i) {
6157 ++field.consumers_nearby.at(bo.inputs.at(i));
6158 }
6159
6160 for (size_t i = 0; i < bo.ware_outputs.size(); ++i) {
6161 ++field.producers_nearby.at(bo.ware_outputs.at(i));
6162 }
6163 if (bo.has_collected_map_resource()) {
6164 ++field.collecting_producers_nearby.at(bo.get_collected_map_resource());
6165 }
6166
6167 if (!bo.production_hints.empty()) {
6168 for (auto ph : bo.production_hints) {
6169 ++field.supporters_nearby.at(ph);
6170 }
6171 }
6172
6173 if (bo.is(BuildingAttribute::kRanger)) {
6174 ++field.rangers_nearby;
6175 }
6176 }
6177
6178 /// \returns the economy observer containing \arg economy
get_economy_observer(Economy & economy)6179 EconomyObserver* DefaultAI::get_economy_observer(Economy& economy) {
6180 for (std::deque<EconomyObserver*>::iterator i = economies.begin(); i != economies.end(); ++i) {
6181 if (&(*i)->economy == &economy) {
6182 return *i;
6183 }
6184 }
6185
6186 economies.push_front(new EconomyObserver(economy));
6187 return economies.front();
6188 }
6189
6190 // counts buildings with the BuildingAttribute
6191 // Type of buildings, not individual buildings are meant
count_buildings_with_attribute(BuildingAttribute attribute)6192 uint8_t DefaultAI::count_buildings_with_attribute(BuildingAttribute attribute) {
6193 uint8_t count = 0;
6194 if (tribe_ == nullptr) {
6195 late_initialization();
6196 }
6197
6198 for (BuildingObserver& bo : buildings_) {
6199 if (bo.is(attribute)) {
6200 ++count;
6201 }
6202 }
6203
6204 return count;
6205 }
6206
6207 // Calculates ratio of the buildings that the player has in comparison to all buildings that
6208 // are buildable by the player
6209 // In range 0 - 1000, to avoid floats
count_productionsites_without_buildings()6210 uint32_t DefaultAI::count_productionsites_without_buildings() {
6211 uint32_t total = 0;
6212 uint32_t existing = 0;
6213 if (tribe_ == nullptr) {
6214 late_initialization();
6215 }
6216
6217 for (BuildingObserver& bo : buildings_) {
6218 if (bo.type == BuildingObserver::Type::kProductionsite &&
6219 bo.is(BuildingAttribute::kBuildable)) {
6220 ++total;
6221 if (bo.cnt_built > 0) {
6222 existing += 1000;
6223 } else if (bo.cnt_under_construction > 0) {
6224 existing += 500;
6225 }
6226 }
6227 }
6228
6229 return (total > 0) ? (existing / total) : 0;
6230 }
6231
6232 // \returns the building observer
get_building_observer(char const * const name)6233 BuildingObserver& DefaultAI::get_building_observer(char const* const name) {
6234 if (tribe_ == nullptr) {
6235 late_initialization();
6236 }
6237
6238 for (BuildingObserver& bo : buildings_) {
6239 if (!strcmp(bo.name, name)) {
6240 return bo;
6241 }
6242 }
6243
6244 throw wexception("Help: I (player %d / tribe %s) do not know what to do with a %s",
6245 player_number(), tribe_->name().c_str(), name);
6246 }
6247
6248 // checks if the building has a building observer (for debug purposes)
has_building_observer(char const * const name)6249 bool DefaultAI::has_building_observer(char const* const name) {
6250 if (tribe_ == nullptr) {
6251 late_initialization();
6252 }
6253
6254 for (BuildingObserver& bo : buildings_) {
6255 if (!strcmp(bo.name, name)) {
6256 return true;
6257 }
6258 }
6259
6260 return false;
6261 }
6262
6263 // return observer for a first (only) building that has required attribute
get_building_observer(BuildingAttribute attribute)6264 Widelands::BuildingObserver& DefaultAI::get_building_observer(BuildingAttribute attribute) {
6265 if (tribe_ == nullptr) {
6266 late_initialization();
6267 }
6268
6269 for (BuildingObserver& bo : buildings_) {
6270 if (bo.is(attribute)) {
6271 return bo;
6272 }
6273 }
6274
6275 throw wexception(
6276 "Sorry, cannot find building with attribute %d", static_cast<int32_t>(attribute));
6277 }
6278
6279 // return observer for a building with the id
get_building_observer(const DescriptionIndex di)6280 Widelands::BuildingObserver& DefaultAI::get_building_observer(const DescriptionIndex di) {
6281 if (tribe_ == nullptr) {
6282 late_initialization();
6283 }
6284
6285 for (BuildingObserver& bo : buildings_) {
6286 if (bo.id == di) {
6287 return bo;
6288 }
6289 }
6290
6291 log("Sorry, cannot find building with id %d", static_cast<int32_t>(di));
6292 // I noticed that exception test is being lost so will will print it into log as well
6293 throw wexception("Sorry, cannot find building with id %d", static_cast<int32_t>(di));
6294 }
6295
6296 // this is called whenever we gain ownership of a PlayerImmovable
gain_immovable(PlayerImmovable & pi,const bool found_on_load)6297 void DefaultAI::gain_immovable(PlayerImmovable& pi, const bool found_on_load) {
6298 if (upcast(Building, building, &pi)) {
6299 gain_building(*building, found_on_load);
6300 } else if (upcast(Flag const, flag, &pi)) {
6301 new_flags.push_back(flag);
6302 } else if (upcast(Road const, road, &pi)) {
6303 roads.push_front(road);
6304 }
6305 }
6306
6307 // this is called whenever we lose ownership of a PlayerImmovable
lose_immovable(const PlayerImmovable & pi)6308 void DefaultAI::lose_immovable(const PlayerImmovable& pi) {
6309 if (upcast(Building const, building, &pi)) {
6310 lose_building(*building);
6311 } else if (upcast(Flag const, flag, &pi)) {
6312 // Flag to be removed can be:
6313 // 1. In one of our economies
6314 for (EconomyObserver* eco_obs : economies) {
6315 if (remove_from_dqueue<Widelands::Flag>(eco_obs->flags, flag)) {
6316 return;
6317 }
6318 }
6319
6320 // 2. in new flags to be processed yet
6321 if (remove_from_dqueue<Widelands::Flag>(new_flags, flag)) {
6322 return;
6323 }
6324
6325 // 3. Or in neither of them
6326 } else if (upcast(Road const, road, &pi)) {
6327 remove_from_dqueue<Widelands::Road>(roads, road);
6328 }
6329 }
6330
6331 // this is called when a mine reports "out of resources"
out_of_resources_site(const ProductionSite & site)6332 void DefaultAI::out_of_resources_site(const ProductionSite& site) {
6333
6334 const uint32_t gametime = game().get_gametime();
6335
6336 // we must identify which mine matches the productionsite a note reffers to
6337 for (std::deque<ProductionSiteObserver>::iterator i = mines_.begin(); i != mines_.end(); ++i) {
6338 if (i->site == &site) {
6339 if (i->no_resources_since > gametime) {
6340 i->no_resources_since = gametime;
6341 }
6342 break;
6343 }
6344 }
6345 }
6346
6347 // walk and search for territory controlled by some player type
6348 // usually scanning radius is enough but sometimes we must walk to
6349 // verify that an enemy territory is really accessible by land
other_player_accessible(const uint32_t max_distance,uint32_t * tested_fields,uint16_t * mineable_fields_count,const Coords & starting_spot,const WalkSearch & type)6350 bool DefaultAI::other_player_accessible(const uint32_t max_distance,
6351 uint32_t* tested_fields,
6352 uint16_t* mineable_fields_count,
6353 const Coords& starting_spot,
6354 const WalkSearch& type) {
6355 const Map& map = game().map();
6356 std::list<uint32_t> queue;
6357 std::unordered_set<uint32_t> done;
6358 queue.push_front(starting_spot.hash());
6359 PlayerNumber const pn = player_->player_number();
6360
6361 while (!queue.empty()) {
6362 // if already processed
6363 if (done.count(queue.front()) > 0) {
6364 queue.pop_front();
6365 continue;
6366 }
6367
6368 done.insert(queue.front());
6369
6370 Coords tmp_coords = Coords::unhash(queue.front());
6371
6372 // if beyond range
6373 if (map.calc_distance(starting_spot, tmp_coords) > max_distance) {
6374 continue;
6375 }
6376
6377 Field* f = map.get_fcoords(tmp_coords).field;
6378
6379 // not interested if not walkable (starting spot is an exemption.
6380 if (tmp_coords != starting_spot && !(f->nodecaps() & MOVECAPS_WALK)) {
6381 continue;
6382 }
6383
6384 // sometimes we search for any owned territory (f.e. when considering
6385 // a port location), but when testing (starting from) own military building
6386 // we must ignore own territory, of course
6387 const PlayerNumber field_owner = f->get_owned_by();
6388 if (field_owner > 0) {
6389
6390 // if field is owned by anybody
6391 if (type == WalkSearch::kAnyPlayer) {
6392 *tested_fields = done.size();
6393 return true;
6394 }
6395
6396 // if somebody but not me
6397 if (type == WalkSearch::kOtherPlayers && field_owner != pn) {
6398 *tested_fields = done.size();
6399 return true;
6400 }
6401
6402 // if owned by enemy
6403 if (type == WalkSearch::kEnemy && field_owner != pn) {
6404 // if not in the same team => it is an enemy
6405 if (!player_statistics.players_in_same_team(pn, field_owner)) {
6406 *tested_fields = done.size();
6407 return true;
6408 }
6409 }
6410 }
6411
6412 // increase mines counter
6413 // (used when testing possible port location)
6414 if (f->nodecaps() & BUILDCAPS_MINE) {
6415 ++mineable_fields_count;
6416 }
6417
6418 // add neighbours to a queue (duplicates are no problem)
6419 // to relieve AI/CPU we skip every second field in each direction
6420 // obstacles are usually wider then one field
6421 for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) {
6422 Coords neigh_coords1;
6423 map.get_neighbour(tmp_coords, dir, &neigh_coords1);
6424 Coords neigh_coords2;
6425 map.get_neighbour(neigh_coords1, dir, &neigh_coords2);
6426 queue.push_front(neigh_coords2.hash());
6427 }
6428 }
6429 *tested_fields = done.size();
6430 return false; // no players found
6431 }
6432
6433 // this is called whenever we gain a new building
gain_building(Building & b,const bool found_on_load)6434 void DefaultAI::gain_building(Building& b, const bool found_on_load) {
6435
6436 BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
6437
6438 if (bo.type == BuildingObserver::Type::kConstructionsite) {
6439 BuildingObserver& target_bo =
6440 get_building_observer(dynamic_cast<const ConstructionSite&>(b).building().name().c_str());
6441 ++target_bo.cnt_under_construction;
6442 if (target_bo.type == BuildingObserver::Type::kProductionsite) {
6443 ++numof_psites_in_constr;
6444 }
6445 if (target_bo.type == BuildingObserver::Type::kMilitarysite) {
6446 ++msites_per_size[target_bo.desc->get_size()].in_construction;
6447 }
6448 if (target_bo.type == BuildingObserver::Type::kMine) {
6449 ++mines_per_type[target_bo.mines].in_construction;
6450 }
6451 if (target_bo.type == BuildingObserver::Type::kWarehouse) {
6452 ++numof_warehouses_in_const_;
6453 }
6454 if (target_bo.type == BuildingObserver::Type::kTrainingsite) {
6455 ++ts_in_const_count_;
6456 }
6457
6458 set_taskpool_task_time(game().get_gametime(), SchedulerTaskId::kRoadCheck);
6459
6460 } else {
6461 ++bo.cnt_built;
6462 const uint32_t gametime = game().get_gametime();
6463 bo.last_building_built = gametime;
6464 // erase building from remaining_basic_buildings, unless we are loading a saved game
6465 if (!found_on_load && persistent_data->remaining_basic_buildings.count(bo.id) > 0) {
6466 if (persistent_data->remaining_basic_buildings[bo.id] > 1) {
6467 --persistent_data->remaining_basic_buildings[bo.id];
6468 } else {
6469 persistent_data->remaining_basic_buildings.erase(bo.id);
6470 }
6471 }
6472 // Remaining basic buildings map contain either no entry for the building, or the number is
6473 // nonzero
6474 assert(persistent_data->remaining_basic_buildings.count(bo.id) == 0 ||
6475 persistent_data->remaining_basic_buildings[bo.id] > 0);
6476
6477 if (bo.type == BuildingObserver::Type::kProductionsite) {
6478 productionsites.push_back(ProductionSiteObserver());
6479 productionsites.back().site = &dynamic_cast<ProductionSite&>(b);
6480 productionsites.back().bo = &bo;
6481 productionsites.back().bo->new_building_overdue = 0;
6482 if (found_on_load && gametime > 5 * 60 * 1000) {
6483 productionsites.back().built_time = gametime - 5 * 60 * 1000;
6484 } else {
6485 productionsites.back().built_time = gametime;
6486 }
6487 productionsites.back().unoccupied_till = gametime;
6488 ++productionsites.back().bo->unoccupied_count;
6489 if (bo.is(BuildingAttribute::kShipyard)) {
6490 marine_task_queue.push_back(kStopShipyard);
6491 marine_task_queue.push_back(kReprioritize);
6492 }
6493 if (bo.is(BuildingAttribute::kFisher)) {
6494 ++fishers_count_;
6495 }
6496
6497 } else if (bo.type == BuildingObserver::Type::kMine) {
6498 mines_.push_back(ProductionSiteObserver());
6499 mines_.back().site = &dynamic_cast<ProductionSite&>(b);
6500 mines_.back().bo = &bo;
6501 mines_.back().built_time = gametime;
6502 assert(mines_.back().no_resources_since == kNever);
6503 assert(!mines_.back().upgrade_pending);
6504 assert(mines_.back().dismantle_pending_since == kNever);
6505 ++mines_.back().bo->unoccupied_count;
6506
6507 ++mines_per_type[bo.mines].finished;
6508
6509 if (bo.is(BuildingAttribute::kBuildingMatProducer)) {
6510 ++buil_material_mines_count;
6511 }
6512
6513 set_inputs_to_zero(mines_.back());
6514
6515 // Is this first mine?
6516 if (bo.mines == iron_resource_id && gametime < first_iron_mine_built) {
6517 first_iron_mine_built = gametime;
6518 }
6519
6520 } else if (bo.type == BuildingObserver::Type::kMilitarysite) {
6521 militarysites.push_back(MilitarySiteObserver());
6522 militarysites.back().site = &dynamic_cast<MilitarySite&>(b);
6523 militarysites.back().bo = &bo;
6524 militarysites.back().understaffed = 0;
6525 if (found_on_load && gametime > 5 * 60 * 1000) {
6526 militarysites.back().built_time = gametime - 5 * 60 * 1000;
6527 } else {
6528 militarysites.back().built_time = gametime;
6529 }
6530 militarysites.back().last_change = 0;
6531 ++msites_per_size[bo.desc->get_size()].finished;
6532
6533 } else if (bo.type == BuildingObserver::Type::kTrainingsite) {
6534 ++ts_without_trainers_;
6535 ++ts_finished_count_;
6536 trainingsites.push_back(TrainingSiteObserver());
6537 trainingsites.back().site = &dynamic_cast<TrainingSite&>(b);
6538 trainingsites.back().bo = &bo;
6539
6540 } else if (bo.type == BuildingObserver::Type::kWarehouse) {
6541 ++numof_warehouses_;
6542 warehousesites.push_back(WarehouseSiteObserver());
6543 warehousesites.back().site = &dynamic_cast<Warehouse&>(b);
6544 warehousesites.back().bo = &bo;
6545 if (bo.is(BuildingAttribute::kPort)) {
6546 ++num_ports;
6547 }
6548 }
6549 }
6550 }
6551
6552 // this is called whenever we lose a building
lose_building(const Building & b)6553 void DefaultAI::lose_building(const Building& b) {
6554
6555 BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
6556
6557 if (bo.type == BuildingObserver::Type::kConstructionsite) {
6558 BuildingObserver& target_bo =
6559 get_building_observer(dynamic_cast<const ConstructionSite&>(b).building().name().c_str());
6560 --target_bo.cnt_under_construction;
6561 if (target_bo.type == BuildingObserver::Type::kProductionsite) {
6562 --numof_psites_in_constr;
6563 }
6564 if (target_bo.type == BuildingObserver::Type::kMilitarysite) {
6565 --msites_per_size[target_bo.desc->get_size()].in_construction;
6566 }
6567 if (target_bo.type == BuildingObserver::Type::kMine) {
6568 --mines_per_type[target_bo.mines].in_construction;
6569 }
6570 if (target_bo.type == BuildingObserver::Type::kWarehouse) {
6571 --numof_warehouses_in_const_;
6572 }
6573 if (target_bo.type == BuildingObserver::Type::kTrainingsite) {
6574 assert(ts_in_const_count_ > 0);
6575 --ts_in_const_count_;
6576 }
6577
6578 } else {
6579 --bo.cnt_built;
6580
6581 // we are not able to reliably identify if lost building is counted in
6582 // unconnected or unoccupied count, but we must adjust the value to
6583 // avoid inconsistency
6584 const uint32_t cnt_built = bo.cnt_built;
6585 if (bo.unconnected_count > cnt_built) {
6586 bo.unconnected_count = cnt_built;
6587 }
6588 if (bo.unoccupied_count > cnt_built) {
6589 bo.unoccupied_count = cnt_built;
6590 }
6591
6592 if (bo.type == BuildingObserver::Type::kProductionsite) {
6593 for (std::deque<ProductionSiteObserver>::iterator i = productionsites.begin();
6594 i != productionsites.end(); ++i) {
6595 if (i->site == &b) {
6596 if (i->upgrade_pending) {
6597 --bo.cnt_upgrade_pending;
6598 }
6599 assert(bo.cnt_upgrade_pending == 0 || bo.cnt_upgrade_pending == 1);
6600 productionsites.erase(i);
6601 break;
6602 }
6603 }
6604
6605 if (bo.is(BuildingAttribute::kFisher)) {
6606 assert(fishers_count_ > 0);
6607 --fishers_count_;
6608 }
6609
6610 } else if (bo.type == BuildingObserver::Type::kMine) {
6611 for (std::deque<ProductionSiteObserver>::iterator i = mines_.begin(); i != mines_.end();
6612 ++i) {
6613 if (i->site == &b) {
6614 mines_.erase(i);
6615 break;
6616 }
6617 }
6618
6619 --mines_per_type[bo.mines].finished;
6620
6621 if (bo.is(BuildingAttribute::kBuildingMatProducer)) {
6622 assert(buil_material_mines_count > 0);
6623 ++buil_material_mines_count;
6624 }
6625
6626 } else if (bo.type == BuildingObserver::Type::kMilitarysite) {
6627 --msites_per_size[bo.desc->get_size()].finished;
6628
6629 for (std::deque<MilitarySiteObserver>::iterator i = militarysites.begin();
6630 i != militarysites.end(); ++i) {
6631 if (i->site == &b) {
6632 militarysites.erase(i);
6633 break;
6634 }
6635 }
6636 } else if (bo.type == BuildingObserver::Type::kTrainingsite) {
6637 assert(ts_finished_count_ >= 1);
6638 --ts_finished_count_;
6639
6640 for (std::deque<TrainingSiteObserver>::iterator i = trainingsites.begin();
6641 i != trainingsites.end(); ++i) {
6642 if (i->site == &b) {
6643 trainingsites.erase(i);
6644 break;
6645 }
6646 }
6647 } else if (bo.type == BuildingObserver::Type::kWarehouse) {
6648 assert(numof_warehouses_ > 0);
6649 --numof_warehouses_;
6650 if (bo.is(BuildingAttribute::kPort)) {
6651 --num_ports;
6652 }
6653
6654 for (std::deque<WarehouseSiteObserver>::iterator i = warehousesites.begin();
6655 i != warehousesites.end(); ++i) {
6656 if (i->site == &b) {
6657 warehousesites.erase(i);
6658 break;
6659 }
6660 }
6661 }
6662 }
6663 }
6664
6665 // Checks that supply line exists for given building.
6666 // Recursively verify that all inputs have a producer.
6667 // TODO(unknown): this function leads to periodic freezes of ~1 second on big games on my system.
6668 // TODO(unknown): It needs profiling and optimization.
check_supply(const BuildingObserver & bo)6669 bool DefaultAI::check_supply(const BuildingObserver& bo) {
6670 size_t supplied = 0;
6671 for (const Widelands::DescriptionIndex& temp_inputs : bo.inputs) {
6672 for (const BuildingObserver& temp_building : buildings_) {
6673 if (temp_building.cnt_built &&
6674 std::find(temp_building.ware_outputs.begin(), temp_building.ware_outputs.end(),
6675 temp_inputs) != temp_building.ware_outputs.end() &&
6676 check_supply(temp_building)) {
6677 ++supplied;
6678 break;
6679 }
6680 }
6681 }
6682
6683 return supplied == bo.inputs.size();
6684 }
6685
6686 // TODO(tiborb): - should be called from scheduler, once in 60s is enough
update_player_stat(const uint32_t gametime)6687 void DefaultAI::update_player_stat(const uint32_t gametime) {
6688 if (player_statistics.get_update_time() > 0 &&
6689 player_statistics.get_update_time() + 15 * 1000 > gametime) {
6690 return;
6691 }
6692 player_statistics.set_update_time(gametime);
6693 Widelands::PlayerNumber const pn = player_number();
6694 PlayerNumber const nr_players = game().map().get_nrplayers();
6695
6696 // receiving games statistics and parsing it (reading latest entry)
6697 const Game::GeneralStatsVector& genstats = game().get_general_statistics();
6698
6699 // Collecting statistics and saving them in player_statistics object
6700 const Player* me = game().get_player(pn);
6701 for (Widelands::PlayerNumber j = 1; j <= nr_players; ++j) {
6702 const Player* this_player = game().get_player(j);
6703 if (this_player) {
6704 try {
6705 const uint32_t vsize = genstats.at(j - 1).miltary_strength.size();
6706
6707 uint32_t cur_strength = 0;
6708 uint32_t cur_land = 0;
6709 uint32_t old_strength = 0;
6710 uint32_t old60_strength = 0;
6711 uint32_t old_land = 0;
6712 uint32_t old60_land = 0;
6713 uint32_t cass = 0;
6714 if (vsize > 0) {
6715 cur_strength = genstats.at(j - 1).miltary_strength.back();
6716 cur_land = genstats.at(j - 1).land_size.back();
6717 cass = genstats.at(j - 1).nr_casualties.back();
6718
6719 if (vsize > 21) {
6720 old_strength = genstats.at(j - 1).miltary_strength[vsize - 20];
6721 old_land = genstats.at(j - 1).land_size[vsize - 20];
6722 } else {
6723 old_strength = genstats.at(j - 1).miltary_strength[0];
6724 old_land = genstats.at(j - 1).land_size[0];
6725 }
6726 if (vsize > 91) {
6727 old60_strength = genstats.at(j - 1).miltary_strength[vsize - 90];
6728 old60_land = genstats.at(j - 1).land_size[vsize - 90];
6729 } else {
6730 old60_strength = genstats.at(j - 1).miltary_strength[0];
6731 old60_land = genstats.at(j - 1).land_size[0];
6732 }
6733 }
6734
6735 player_statistics.add(pn, j, me->team_number(), this_player->team_number(),
6736 cur_strength, old_strength, old60_strength, cass, cur_land,
6737 old_land, old60_land);
6738 } catch (const std::out_of_range&) {
6739 log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
6740 static_cast<unsigned int>(player_number()),
6741 static_cast<unsigned int>(genstats.size()));
6742 }
6743 } else {
6744 // Well, under some circumstances it is possible we have stat for this player and he does
6745 // not exist anymore
6746 player_statistics.remove_stat(j);
6747 }
6748 }
6749
6750 player_statistics.recalculate_team_power();
6751 }
6752
6753 // This runs once in 15 minutes, and adjust wares targets based on number of
6754 // productionsites and ports
review_wares_targets(uint32_t const gametime)6755 void DefaultAI::review_wares_targets(uint32_t const gametime) {
6756
6757 player_ = game().get_player(player_number());
6758 tribe_ = &player_->tribe();
6759
6760 // to avoid floats real multiplier is multiplier/10
6761 const uint16_t multiplier = std::max<uint16_t>((productionsites.size() + num_ports * 5) / 5, 10);
6762
6763 for (EconomyObserver* observer : economies) {
6764 DescriptionIndex nritems = player_->egbase().tribes().nrwares();
6765 for (Widelands::DescriptionIndex id = 0; id < nritems; ++id) {
6766
6767 // Just skip wares that are not used by a tribe
6768 if (!tribe_->has_ware(id)) {
6769 continue;
6770 }
6771
6772 uint16_t default_target =
6773 tribe_->get_ware_descr(id)->default_target_quantity(tribe_->name());
6774
6775 // It seems that when default target for ware is not set, it returns
6776 // kInvalidWare (=254), this is confusing for AI so we change it to 10
6777 if (default_target == Widelands::kInvalidWare) {
6778 default_target = kTargetQuantCap;
6779 }
6780
6781 const uint16_t new_target = std::max<uint16_t>(default_target * multiplier / 10, 3);
6782 assert(new_target > 1);
6783
6784 game().send_player_command(new Widelands::CmdSetWareTargetQuantity(
6785 gametime, player_number(), observer->economy.serial(), id, new_target));
6786 }
6787 }
6788 }
6789
6790 // Sets due_time based on job ID
set_taskpool_task_time(const uint32_t gametime,const Widelands::SchedulerTaskId task)6791 void DefaultAI::set_taskpool_task_time(const uint32_t gametime,
6792 const Widelands::SchedulerTaskId task) {
6793
6794 for (auto& item : taskPool) {
6795 if (item.id == task) {
6796 item.due_time = gametime;
6797 return;
6798 }
6799 }
6800 NEVER_HERE();
6801 }
6802
6803 // Retrieves due time of the task based on its ID
get_taskpool_task_time(const Widelands::SchedulerTaskId task)6804 uint32_t DefaultAI::get_taskpool_task_time(const Widelands::SchedulerTaskId task) {
6805 for (const auto& item : taskPool) {
6806 if (item.id == task) {
6807 return item.due_time;
6808 }
6809 }
6810
6811 throw wexception("AI internal error: nonexistent task.");
6812 }
6813
6814 // This performs one "iteration" of sorting based on due_time
6815 // We by design do not need full sorting...
sort_task_pool()6816 void DefaultAI::sort_task_pool() {
6817 assert(!taskPool.empty());
6818 for (int8_t i = taskPool.size() - 1; i > 0; --i) {
6819 if (taskPool[i - 1].due_time > taskPool[i].due_time) {
6820 std::iter_swap(taskPool.begin() + i - 1, taskPool.begin() + i);
6821 }
6822 }
6823 }
6824
6825 // following two functions count mines of the same type (same output,
6826 // all levels)
mines_in_constr() const6827 uint32_t DefaultAI::mines_in_constr() const {
6828 uint32_t count = 0;
6829 for (const auto& m : mines_per_type) {
6830 count += m.second.in_construction;
6831 }
6832 return count;
6833 }
6834
mines_built() const6835 uint32_t DefaultAI::mines_built() const {
6836 uint32_t count = 0;
6837 for (const auto& m : mines_per_type) {
6838 count += m.second.finished;
6839 }
6840 return count;
6841 }
6842
6843 // following two functions count militarysites of the same size
msites_in_constr() const6844 uint32_t DefaultAI::msites_in_constr() const {
6845 uint32_t count = 0;
6846 for (const auto& m : msites_per_size) {
6847 count += m.second.in_construction;
6848 }
6849 return count;
6850 }
msites_built() const6851 uint32_t DefaultAI::msites_built() const {
6852 uint32_t count = 0;
6853 for (const auto& m : msites_per_size) {
6854 count += m.second.finished;
6855 }
6856 return count;
6857 }
6858
6859 // This prints some basic statistics during a game to the command line -
6860 // missing materials and counts of different types of buildings.
6861 // The main purpose of this is when a game creator needs to finetune a map
6862 // and needs to know what resourcess are missing for which player and so on.
6863 // By default it is off (see kPrintStats)
6864 // TODO(tiborb): - it would be nice to have this activated by a command line switch
print_stats(uint32_t const gametime)6865 void DefaultAI::print_stats(uint32_t const gametime) {
6866
6867 if (!kPrintStats) {
6868 set_taskpool_task_time(std::numeric_limits<int32_t>::max(), SchedulerTaskId::kPrintStats);
6869 return;
6870 }
6871
6872 PlayerNumber const pn = player_number();
6873
6874 const DescriptionIndex& nr_buildings = game().tribes().nrbuildings();
6875 std::set<DescriptionIndex> materials;
6876
6877 // Collect information about the different buildings that our tribe can have
6878 for (DescriptionIndex building_index = 0; building_index < nr_buildings; ++building_index) {
6879 const BuildingDescr& bld = *tribe_->get_building_descr(building_index);
6880 if (!tribe_->has_building(building_index)) {
6881 continue;
6882 }
6883 if (bld.type() == MapObjectType::PRODUCTIONSITE) {
6884 const ProductionSiteDescr& prod = dynamic_cast<const ProductionSiteDescr&>(bld);
6885 for (const auto& temp_input : prod.input_wares()) {
6886 if (materials.count(temp_input.first) == 0) {
6887 materials.insert(temp_input.first);
6888 }
6889 }
6890 for (const auto& temp_cost : prod.buildcost()) {
6891 if (materials.count(temp_cost.first) == 0) {
6892 materials.insert(temp_cost.first);
6893 }
6894 }
6895 }
6896
6897 if (bld.type() == MapObjectType::TRAININGSITE) {
6898 const ProductionSiteDescr& train = dynamic_cast<const TrainingSiteDescr&>(bld);
6899 for (const auto& temp_cost : train.buildcost()) {
6900 if (materials.count(temp_cost.first) == 0) {
6901 materials.insert(temp_cost.first);
6902 }
6903 }
6904 }
6905 }
6906
6907 if (false) {
6908 log(" %1d: %s Buildings count: Pr:%3u, Ml:%3u, Mi:%2u, Wh:%2u, Po:%u.\n", pn,
6909 gamestring_with_leading_zeros(gametime), static_cast<uint32_t>(productionsites.size()),
6910 static_cast<uint32_t>(militarysites.size()), static_cast<uint32_t>(mines_.size()),
6911 static_cast<uint32_t>(warehousesites.size() - num_ports), num_ports);
6912 }
6913 if (false) {
6914 log(" %1s %-30s %5s(perf) %6s %6s %6s %8s %5s %5s %5s %5s\n", "T", "Buildings", "work.",
6915 "const.", "unocc.", "uncon.", "needed", "prec.", "pprio", "stock", "targ.");
6916 }
6917 for (uint32_t j = 0; j < buildings_.size(); ++j) {
6918 BuildingObserver& bo = buildings_.at(j);
6919 if ((bo.total_count() > 0 || bo.new_building == BuildingNecessity::kNeeded ||
6920 bo.new_building == BuildingNecessity::kForced ||
6921 bo.new_building == BuildingNecessity::kNeededPending ||
6922 bo.new_building == BuildingNecessity::kAllowed) &&
6923 bo.type != BuildingObserver::Type::kMilitarysite) {
6924 std::string needeness;
6925 if (bo.new_building == BuildingNecessity::kNeededPending) {
6926 needeness = "pend";
6927 } else if (bo.new_building == BuildingNecessity::kForced) {
6928 needeness = "forc";
6929 } else if (bo.new_building == BuildingNecessity::kAllowed) {
6930 needeness = "allw";
6931 } else if (bo.new_building == BuildingNecessity::kNotNeeded ||
6932 bo.new_building == BuildingNecessity::kForbidden) {
6933 needeness = "no";
6934 } else {
6935 needeness = "yes";
6936 }
6937 std::string btype;
6938 switch (bo.type) {
6939 case BuildingObserver::Type::kWarehouse:
6940 btype = "W";
6941 break;
6942 case BuildingObserver::Type::kMine:
6943 btype = "M";
6944 break;
6945 case BuildingObserver::Type::kTrainingsite:
6946 btype = "T";
6947 break;
6948 case BuildingObserver::Type::kProductionsite:
6949 btype = "P";
6950 break;
6951 default:
6952 btype = "?";
6953 }
6954
6955 if (true) {
6956 log(" %1s %-30s %5d(%3d%%) %6d %6d %6d %8s %5d %5d %5d %5d\n", btype.c_str(), bo.name,
6957 bo.total_count() - bo.cnt_under_construction - bo.unoccupied_count -
6958 bo.unconnected_count,
6959 bo.current_stats, bo.cnt_under_construction, bo.unoccupied_count,
6960 bo.unconnected_count, needeness.c_str(), bo.max_needed_preciousness,
6961 bo.primary_priority, get_stocklevel(bo, gametime), bo.cnt_target);
6962 }
6963 }
6964 }
6965
6966 std::string why = "; Why: ";
6967
6968 if ((numof_psites_in_constr + mines_in_constr()) >
6969 (productionsites.size() + mines_built()) / persistent_data->ai_productionsites_ratio + 2) {
6970 why += " too many constr.";
6971 }
6972 // 3. too keep some proportions production sites vs military sites
6973 if ((numof_psites_in_constr + productionsites.size()) >
6974 (msites_in_constr() + militarysites.size()) * 5) {
6975 why += ", too many productionsites";
6976 }
6977 // 4. if we do not have 2 mines at least
6978 if (mines_.size() < 2) {
6979 why += ", less then 2 mines";
6980 }
6981
6982 if (false) {
6983 log("Prodsites in constr: %2d, mines in constr: %2d %s %s\n", numof_psites_in_constr,
6984 mines_in_constr(),
6985 (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) ? "NEW BUILDING STOP" :
6986 "",
6987 why.c_str());
6988 }
6989
6990 if (false) {
6991 log("Least military score: %5d/%3d, msites in constr: %3d,"
6992 "soldier st: %2d, strength: %3d\n",
6993 persistent_data->least_military_score, persistent_data->ai_personality_mil_upper_limit,
6994 msites_in_constr(), static_cast<int8_t>(soldier_status_),
6995 player_statistics.get_modified_player_power(player_number()));
6996 }
6997 }
6998
6999 template <typename T>
check_range(T value,T bottom_range,T upper_range,const char * value_name)7000 void DefaultAI::check_range(T value, T bottom_range, T upper_range, const char* value_name) {
7001 if (value < bottom_range || value > upper_range) {
7002 log(" %d: unexpected value for %s: %d\n", player_number(), value_name, value);
7003 }
7004 }
7005
check_range(T value,T upper_range,const char * value_name)7006 template <typename T> void DefaultAI::check_range(T value, T upper_range, const char* value_name) {
7007 if (value > upper_range) {
7008 log(" %d: unexpected value for %s: %d\n", player_number(), value_name, value);
7009 }
7010 }
7011
7012 template <typename T>
remove_from_dqueue(std::deque<T const * > & dq,T const * member)7013 bool DefaultAI::remove_from_dqueue(std::deque<T const*>& dq, T const* member) {
7014 for (auto it = dq.begin(); it != dq.end(); ++it) {
7015 if (*it == member) {
7016 it = dq.erase(it);
7017 return true;
7018 }
7019 }
7020 return false;
7021 }
7022
7023 // Looking for situation where for a critical mine (iron, or marble) there is just one mine and it
7024 // is
7025 // unoccupied, probably we need to dismantle another one to release a miner
critical_mine_unoccupied(uint32_t gametime)7026 bool DefaultAI::critical_mine_unoccupied(uint32_t gametime) {
7027 // resetting unoccupied
7028 for (auto& mine : mines_per_type) {
7029 mine.second.unoccupied = 0;
7030 }
7031 for (auto& mine : mines_) {
7032 if (!mines_per_type[mine.bo->mines].is_critical) {
7033 continue;
7034 }
7035 if (mine.built_time + 3 * 60 * 1000 < gametime && !mine.site->can_start_working()) {
7036 ++mines_per_type[mine.bo->mines].unoccupied;
7037 }
7038 }
7039
7040 // Now check that that there is no working mine of the critical type
7041 for (auto& mine : mines_per_type) {
7042 if (mine.second.is_critical && mine.second.finished > 0 &&
7043 mine.second.unoccupied == mine.second.finished) {
7044 return true;
7045 }
7046 assert(mine.second.unoccupied <= mines_.size());
7047 assert(mine.second.unoccupied <= mine.second.total_count());
7048 }
7049 return false;
7050 }
7051
7052 // Sets all inputs to zero and return true if inputs are already empty
set_inputs_to_zero(const Widelands::ProductionSiteObserver & site)7053 bool DefaultAI::set_inputs_to_zero(const Widelands::ProductionSiteObserver& site) {
7054 uint16_t remaining_wares = 0;
7055
7056 for (auto& queue : site.site->inputqueues()) {
7057 remaining_wares += queue->get_filled();
7058 if (queue->get_max_fill() > 0) {
7059 game().send_player_set_input_max_fill(
7060 *site.site, queue->get_index(), queue->get_type(), 0);
7061 }
7062 }
7063 return remaining_wares == 0;
7064 }
7065
set_inputs_to_max(const Widelands::ProductionSiteObserver & site)7066 void DefaultAI::set_inputs_to_max(const Widelands::ProductionSiteObserver& site) {
7067 for (auto& queue : site.site->inputqueues()) {
7068 if (queue->get_max_fill() < queue->get_max_size()) {
7069 game().send_player_set_input_max_fill(
7070 *site.site, queue->get_index(), queue->get_type(), queue->get_max_size());
7071 }
7072 }
7073 }
stop_site(const Widelands::ProductionSiteObserver & site)7074 void DefaultAI::stop_site(const Widelands::ProductionSiteObserver& site) {
7075 if (!site.site->is_stopped()) {
7076 game().send_player_start_stop_building(*site.site);
7077 }
7078 }
7079
initiate_dismantling(Widelands::ProductionSiteObserver & site,uint32_t gametime)7080 void DefaultAI::initiate_dismantling(Widelands::ProductionSiteObserver& site, uint32_t gametime) {
7081 site.dismantle_pending_since = gametime;
7082 set_inputs_to_zero(site);
7083 site.bo->construction_decision_time = gametime;
7084 }
7085
get_land_owner(const Widelands::Map & map,const uint32_t coords) const7086 Widelands::PlayerNumber DefaultAI::get_land_owner(const Widelands::Map& map,
7087 const uint32_t coords) const {
7088 FCoords f = map.get_fcoords(Widelands::Coords::unhash(coords));
7089 return f.field->get_owned_by();
7090 }
7091