widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #03855
Re: [Merge] lp:~widelands-dev/widelands/ai-military-changes into lp:widelands
Review: Needs Fixing
Please change the AI hint from ts_type=2 to something that is more understandable outside of the AI context, maybe trainingssite_type="no_bread_just_meat" or something along these lines. Numbers are fully opaque to everybody that looks at the conf files.
Diff comments:
> === modified file 'src/ai/ai_help_structs.h'
> --- src/ai/ai_help_structs.h 2015-03-05 20:57:07 +0000
> +++ src/ai/ai_help_structs.h 2015-03-23 21:37:01 +0000
> @@ -40,7 +40,7 @@
> class ProductionSite;
> class MilitarySite;
>
> -enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse };
> +enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse};
>
> struct CheckStepRoadAI {
> CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
> @@ -110,6 +110,22 @@
> }
> };
>
> +// Sometimes we need to know how many nodes our allies owns
> +struct FindNodeAllyOwned {
> + bool accept(const Map&, const FCoords& fc) const {
> + return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() != 0) &&
> + (fc.field->get_owned_by() != pn) &&
> + !player_->is_hostile(*game.get_player(fc.field->get_owned_by()));
> + }
> +
> + Player* player_;
> + Game& game;
> + PlayerNumber pn;
> +
> + FindNodeAllyOwned(Player* p, Game& g, PlayerNumber n) : player_(p), game(g), pn(n) {
> + }
> +};
> +
> // When looking for unowned terrain to acquire, we must
> // pay speciall attention to fields where mines can be built.
> // Fields should be completely unowned
> @@ -148,8 +164,9 @@
>
> bool accept(const Map& /* map */, const FCoords& coord) const {
> return (world_.terrain_descr(coord.field->terrain_d()).get_is() &
> - TerrainDescription::Type::kWater) ||
> - (world_.terrain_descr(coord.field->terrain_r()).get_is() & TerrainDescription::Type::kWater);
> + TerrainDescription::Type::kWater) ||
> + (world_.terrain_descr(coord.field->terrain_r()).get_is() &
> + TerrainDescription::Type::kWater);
> }
>
> private:
> @@ -213,7 +230,7 @@
> struct BuildableField {
> Widelands::FCoords coords;
>
> - uint32_t next_update_due_;
> + uint32_t field_info_expiration_;
>
> bool preferred_;
> bool enemy_nearby_;
> @@ -232,7 +249,7 @@
> uint8_t space_consumers_nearby_;
> // to manage the military better following variables exists:
> // capacity of nearby buildings:
> - int16_t military_capacity_;
> + int16_t area_military_capacity_;
> // distance to near buldings:
> int16_t military_loneliness_;
> // count of military buildings in construction
> @@ -241,7 +258,7 @@
> // are construction sites that will change this once they are built
> int16_t military_in_constr_nearby_;
> // actual count of soldiers in nearby buldings
> - int16_t military_presence_;
> + int16_t area_military_presence_;
> // stationed (manned) military buildings nearby
> int16_t military_stationed_;
> // stationed (manned) military buildings nearby
> @@ -256,7 +273,7 @@
>
> BuildableField(const Widelands::FCoords& fc)
> : coords(fc),
> - next_update_due_(0),
> + field_info_expiration_(20000),
> preferred_(false),
> enemy_nearby_(0),
> unowned_land_nearby_(0),
> @@ -277,10 +294,10 @@
> critters_nearby_(-1),
> ground_water_(1),
> space_consumers_nearby_(0),
> - military_capacity_(0),
> + area_military_capacity_(0),
> military_loneliness_(1000),
> military_in_constr_nearby_(0),
> - military_presence_(0),
> + area_military_presence_(0),
> military_stationed_(0),
> military_unstationed_(0),
> is_portspace_(false),
> @@ -292,7 +309,7 @@
> struct MineableField {
> Widelands::FCoords coords;
>
> - uint32_t next_update_due_;
> + uint32_t field_info_expiration_;
>
> bool preferred_;
>
> @@ -302,7 +319,7 @@
>
> MineableField(const Widelands::FCoords& fc)
> : coords(fc),
> - next_update_due_(0),
> + field_info_expiration_(20000),
> preferred_(false),
> mines_nearby_(0),
> same_mine_fields_nearby_(0) {
> @@ -346,14 +363,16 @@
> bool is_fisher_; // need to identify fishers
> bool is_port_;
> bool is_shipyard_;
> - bool space_consumer_; // farm, vineyard... = true
> - bool expansion_type_; // military building used that can be used to control area
> - bool fighting_type_; // military building built near enemies
> - bool mountain_conqueror_; // military building built near mountains
> + bool space_consumer_; // farm, vineyard... = true
> + bool expansion_type_; // military building used that can be used to control area
> + bool fighting_type_; // military building built near enemies
> + bool mountain_conqueror_; // military building built near mountains
> uint32_t prohibited_till_; // do not build before (ms)
> uint32_t forced_after_; // do not wait until ware is needed
> + uint8_t ts_type_;
>
> - bool unoccupied_; //
> + bool unoccupied_;
> + uint16_t unconnected_; // to any warehouse (count of such buildings)
>
> int32_t mines_; // type of resource it mines_
> uint16_t mines_percent_; // % of res it can mine
> @@ -429,4 +448,44 @@
> uint8_t preciousness_;
> };
>
> +//Computer player does not get notification messages about enemy militarysites
> +//and warehouses, so following is collected based on observation
> +//It is conventient to have some information preserved, like nearby minefields,
> +//when it was attacked, whether it is warehouse and so on
> +//Also AI test more such targets when considering attack and calculated score is
> +//is stored in the observer
> +struct EnemySiteObserver {
> + bool warehouse_;
> + uint8_t attack_soldiers;
> + uint8_t defenders;
> + uint8_t stationed_soldiers;
> + uint32_t last_time_attackable;
> + uint32_t last_tested;
> + int16_t score;
> + bool warehouse;
> + Widelands::ExtendedBool mines_nearby;
> + int16_t no_attack_counter;
> +
> + EnemySiteObserver()
> + : warehouse_(false),
> + attack_soldiers(0),
> + stationed_soldiers(0),
> + last_time_attackable(std::numeric_limits<uint32_t>::max()),
> + last_tested(0),
> + score(0),
> + mines_nearby(Widelands::ExtendedBool::kUnset),
> + no_attack_counter(0) {
> + }
> +};
> +
> +// as all mines have 3 levels, AI does not know total count of mines per mined material
> +// so this observer will be used for this
> +struct MineTypesObserver {
> + uint16_t in_construction;
> + uint16_t finished;
> +
> + MineTypesObserver() : in_construction(0), finished(0) {
> + }
> +};
> +
> #endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H
>
> === modified file 'src/ai/ai_hints.cc'
> --- src/ai/ai_hints.cc 2015-03-05 20:57:07 +0000
> +++ src/ai/ai_hints.cc 2015-03-23 21:37:01 +0000
> @@ -33,7 +33,8 @@
> mountain_conqueror_(section ? section->get_bool("mountain_conqueror") : false),
> prohibited_till_(section ? section->get_natural("prohibited_till", 0) : 0),
> forced_after_(section ? section->get_natural("forced_after", 864000) : 0), // 10 days default
> - mines_percent_(section ? section->get_int("mines_percent", 100) : 0)
> + mines_percent_(section ? section->get_int("mines_percent", 100) : 0),
> + ts_type_(section ? section->get_int("ts_type", 0) : 0)
> {
> if (section) {
> if (section->has_val("renews_map_resource"))
>
> === modified file 'src/ai/ai_hints.h'
> --- src/ai/ai_hints.h 2015-03-05 20:57:07 +0000
> +++ src/ai/ai_hints.h 2015-03-23 21:37:01 +0000
> @@ -94,6 +94,10 @@
> return mines_percent_;
> }
>
> + uint8_t get_ts_type() const {
> + return ts_type_;
> + }
> +
> private:
> std::string renews_map_resource_;
> std::string mines_;
> @@ -109,6 +113,7 @@
> int32_t prohibited_till_;
> int32_t forced_after_;
> uint8_t mines_percent_;
> + uint8_t ts_type_;
>
> DISALLOW_COPY_AND_ASSIGN(BuildingHints);
> };
>
> === modified file 'src/ai/defaultai.cc'
> --- src/ai/defaultai.cc 2015-03-06 07:36:40 +0000
> +++ src/ai/defaultai.cc 2015-03-23 21:37:01 +0000
> @@ -51,12 +51,15 @@
> #include "profile/profile.h"
>
> // following is in miliseconds (widelands counts time in ms)
> -constexpr int kFieldUpdateInterval = 2000;
> -constexpr int kIdleMineUpdateInterval = 22000;
> +// constexpr int kFieldUpdateInterval = 2000;
> +constexpr int kFieldInfoExpiration = 12 * 1000;
> +constexpr int kMineFieldInfoExpiration = 20 * 1000;
> +constexpr int kNewMineConstInterval = 19000;
> constexpr int kBusyMineUpdateInterval = 2000;
> // building of the same building can be started after 25s at earliest
> constexpr int kBuildingMinInterval = 25 * 1000;
> constexpr int kMinBFCheckInterval = 5 * 1000;
> +constexpr int kMinMFCheckInterval = 19 * 1000;
> constexpr int kShipCheckInterval = 5 * 1000;
> constexpr int kMarineDecisionInterval = 20 * 1000;
> constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
> @@ -85,6 +88,8 @@
> num_milit_constructionsites(0),
> num_prod_constructionsites(0),
> num_ports(0),
> + last_attacked_player_(std::numeric_limits<uint16_t>::max()),
> + enemysites_check_delay_(60),
> next_ai_think_(0),
> next_mine_construction_due_(0),
> inhibit_road_building_(0),
> @@ -92,9 +97,8 @@
> enemy_last_seen_(0),
> numof_warehouses_(0),
> new_buildings_stop_(false),
> - resource_necessity_territory_(255),
> - resource_necessity_mines_(255),
> - resource_necessity_stones_(255),
> + resource_necessity_territory_(100),
> + resource_necessity_mines_(100),
> resource_necessity_water_(0),
> resource_necessity_water_needed_(false),
> unstationed_milit_buildings_(0),
> @@ -105,7 +109,13 @@
> next_attack_waittime_(10),
> seafaring_economy(false),
> colony_scan_area_(35),
> - spots_(0) {
> + spots_(0),
> + vacant_mil_positions_(0),
> + ts_type1_count_(0),
> + ts_type1_const_count_(0),
> + ts_type2_count_(0),
> + ts_type2_const_count_(0),
> + ts_without_trainers_(0) {
>
> // Subscribe to NoteFieldPossession.
> field_possession_subscriber_ =
> @@ -166,23 +176,23 @@
> break;
>
> case NoteShipMessage::Message::kLost:
> - for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
> + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
> if (i->ship == note.ship) {
> allships.erase(i);
> break;
> }
> }
> - break;
> + break;
>
> case NoteShipMessage::Message::kWaitingForCommand:
> - for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
> + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
> if (i->ship == note.ship) {
> i->waiting_for_command_ = true;
> break;
> }
> }
> - break;
> - default:
> + break;
> + default:
> ;
> }
> });
> @@ -236,7 +246,11 @@
> if (DueTask == ScheduleTasks::kBbuildableFieldsCheck) {
replacing these if /elif clases through one switch() will make them more efficient and easier to read.
> update_all_buildable_fields(gametime);
> taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
> - } else if (DueTask == ScheduleTasks::kRoadCheck) {
> + } else if (DueTask == ScheduleTasks::kMineableFieldsCheck) {
> + update_all_mineable_fields(gametime);
> + taskDue[ScheduleTasks::kMineableFieldsCheck] = gametime + kMinMFCheckInterval;
> + }
> + if (DueTask == ScheduleTasks::kRoadCheck) {
> if (check_economies()) { // is a must
> return;
> };
> @@ -245,8 +259,6 @@
> } else if (DueTask == ScheduleTasks::kUnbuildableFCheck) {
> taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
> update_all_not_buildable_fields();
> - } else if (DueTask == ScheduleTasks::kConsiderAttack) {
> - consider_attack(gametime);
> } else if (DueTask == ScheduleTasks::kCheckEconomies) {
> check_economies();
> taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
> @@ -280,6 +292,9 @@
> check_militarysites(gametime);
> } else if (DueTask == ScheduleTasks::kCheckTrainingsites) {
> check_trainingsites(gametime);
> + } else if (DueTask == ScheduleTasks::kCountMilitaryVacant) {
> + count_military_vacant_positions();
> + taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000;
> } else if (DueTask == ScheduleTasks::kWareReview) {
> if (check_economies()) { // economies must be consistent
> return;
> @@ -291,6 +306,9 @@
> return;
> }
> print_stats(gametime);
> + } else if (DueTask == ScheduleTasks::kCheckEnemySites) {
> + check_enemy_sites(gametime);
> + taskDue[ScheduleTasks::kCheckEnemySites] = gametime + 19 * 1000;
> }
> }
>
> @@ -340,6 +358,7 @@
> bo.production_hint_ = -1;
> bo.current_stats_ = 0;
> bo.unoccupied_ = false;
> + bo.unconnected_ = 0;
> bo.is_buildable_ = bld.is_buildable();
> bo.need_trees_ = bh.is_logproducer();
> bo.need_stones_ = bh.is_stoneproducer();
> @@ -353,6 +372,8 @@
> bo.prohibited_till_ = bh.get_prohibited_till() * 1000; // value in conf is in seconds
> bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds
> bo.is_port_ = bld.get_isport();
> + bo.ts_type_ = bh.get_ts_type();
> +
> if (bh.renews_map_resource()) {
> bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource());
> }
> @@ -382,6 +403,11 @@
> }
>
> bo.mines_percent_ = bh.get_mines_percent();
> +
> + // populating mines_per_type map
> + if (mines_per_type.count(bo.mines_) == 0) {
> + mines_per_type[bo.mines_] = MineTypesObserver();
> + }
> }
>
> // here we identify hunters
> @@ -439,6 +465,7 @@
> for (const WareAmount& temp_input : train.inputs()) {
> bo.inputs_.push_back(temp_input.first);
> }
> + bo.ts_type_ = bh.get_ts_type();
> continue;
> }
>
> @@ -544,12 +571,14 @@
> taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
> taskDue[ScheduleTasks::kCheckEconomies] = 1000;
> taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
> - taskDue[ScheduleTasks::kConsiderAttack] = 300000;
> taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
> taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
> + taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
> taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
> taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000;
> taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
> + taskDue[ScheduleTasks::kCountMilitaryVacant] = 10 * 60 * 1000;
> + taskDue[ScheduleTasks::kCheckEnemySites] = 10 * 60 * 1000;
> }
>
> /**
> @@ -562,7 +591,10 @@
>
> uint16_t i = 0;
>
> - while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&
> + // we test 40 fields that were update more than 1 seconds ago
> + while (!buildable_fields.empty() &&
> + (buildable_fields.front()->field_info_expiration_ - kFieldInfoExpiration + 1000) <=
> + gametime &&
> i < 40) {
> BuildableField& bf = *buildable_fields.front();
>
> @@ -582,7 +614,7 @@
> }
>
> update_buildable_field(bf);
> - bf.next_update_due_ = gametime + kFieldUpdateInterval;
> + bf.field_info_expiration_ = gametime + kFieldInfoExpiration;
> buildable_fields.push_back(&bf);
> buildable_fields.pop_front();
>
> @@ -600,8 +632,12 @@
>
> uint16_t i = 0; // counter, used to track # of checked fields
>
> - while (!mineable_fields.empty() && mineable_fields.front()->next_update_due_ <= gametime &&
> - i < 40) {
> + // we test 30 fields that were updated more than 1 seconds ago
> + // to avoid re-test of the same field twice
> + while (!mineable_fields.empty() &&
> + (mineable_fields.front()->field_info_expiration_ - kMineFieldInfoExpiration + 1000) <=
> + gametime &&
> + i < 30) {
> MineableField* mf = mineable_fields.front();
>
> // check whether we lost ownership of the node
> @@ -620,7 +656,7 @@
> }
>
> update_mineable_field(*mf);
> - mf->next_update_due_ = gametime + kFieldUpdateInterval; // in fact this has very small effect
> + mf->field_info_expiration_ = gametime + kMineFieldInfoExpiration;
> mineable_fields.push_back(mf);
> mineable_fields.pop_front();
>
> @@ -672,32 +708,38 @@
> void DefaultAI::update_buildable_field(BuildableField& field, uint16_t range, bool military) {
> // look if there is any unowned land nearby
> Map& map = game().map();
> + const int32_t gametime = game().get_gametime();
> FindNodeUnowned find_unowned(player_, game());
> FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
> PlayerNumber const pn = player_->player_number();
> const World& world = game().world();
> field.unowned_land_nearby_ =
> map.find_fields(Area<FCoords>(field.coords, range), nullptr, find_unowned);
> + FindNodeAllyOwned find_ally(player_, game(), player_number());
> + const int32_t AllyOwnedFields =
> + map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally);
>
> field.near_border_ = false;
> - if (field.unowned_land_nearby_ > 0) {
> + if (AllyOwnedFields > 0) {
> + field.near_border_ = true;
> + } else if (field.unowned_land_nearby_ > 0) {
> if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned) > 0) {
> field.near_border_ = true;
> }
> }
>
> // to save some CPU
> - if ((mines_.size() > 8 && game().get_gametime() % 3 > 0) || field.unowned_land_nearby_ == 0) {
> + if ((mines_.size() > 8 && gametime % 3 > 0) || field.unowned_land_nearby_ == 0) {
> field.unowned_mines_pots_nearby_ = 0;
> } else {
> uint32_t close_mines =
> map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_mines_pots);
> uint32_t distant_mines =
> - map.find_fields(Area<FCoords>(field.coords, (range + 6 < 12) ? 12 : range + 6),
> + map.find_fields(Area<FCoords>(field.coords, (range + 6 < 14) ? 14 : range + 6),
> nullptr,
> find_unowned_mines_pots);
> distant_mines = distant_mines - close_mines;
> - field.unowned_mines_pots_nearby_ = 3 * close_mines + distant_mines / 2;
> + field.unowned_mines_pots_nearby_ = 4 * close_mines + distant_mines / 2;
> if (distant_mines > 0) {
> field.unowned_mines_pots_nearby_ += 15;
> }
> @@ -710,7 +752,7 @@
> }
> }
>
> - // testing for near porspaces
> + // testing for near portspaces
> if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {
> field.portspace_nearby_ = ExtendedBool::kFalse;
> MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));
> @@ -745,9 +787,9 @@
> int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
> field.preferred_ = false;
> field.enemy_nearby_ = false;
> - field.military_capacity_ = 0;
> + field.area_military_capacity_ = 0;
> field.military_loneliness_ = 1000; // instead of floats(v-
> - field.military_presence_ = 0;
> + field.area_military_presence_ = 0;
> field.military_stationed_ = 0;
> field.trees_nearby_ = 0;
> field.space_consumers_nearby_ = 0;
> @@ -772,8 +814,7 @@
> }
>
> // counting fields with fish
> - if (field.water_nearby_ > 0 &&
> - (field.fish_nearby_ == -1 || game().get_gametime() % 10 == 0)) {
> + if (field.water_nearby_ > 0 && (field.fish_nearby_ == -1 || gametime % 10 == 0)) {
> map.find_fields(Area<FCoords>(field.coords, 6),
> &resource_list,
> FindNodeResource(world.get_resource("fish")));
> @@ -782,7 +823,7 @@
>
> // counting fields with critters (game)
> // not doing this always, this does not change fast
> - if (game().get_gametime() % 10 == 0) {
> + if (gametime % 10 == 0) {
> map.find_bobs(Area<FCoords>(field.coords, 6), &critters_list, FindBobCritter());
> field.critters_nearby_ = critters_list.size();
> }
> @@ -808,7 +849,7 @@
> if (player_->is_hostile(player_immovable->owner())) {
> field.enemy_nearby_ = true;
> }
> - enemy_last_seen_ = game().get_gametime();
> + enemy_last_seen_ = gametime;
>
> continue;
> }
> @@ -840,7 +881,7 @@
> }
>
> // stones are not renewable, we will count them only if previous state si nonzero
> - if (field.stones_nearby_ > 0) {
> + if (field.stones_nearby_ > 0 && gametime % 3 == 0) {
>
> int32_t const stone_attr = MapObjectDescr::get_attribute_id("granite");
> field.stones_nearby_ = 0;
> @@ -863,13 +904,13 @@
>
> // we get immovables with higher radius
> immovables.clear();
> - map.find_immovables(Area<FCoords>(field.coords, (range < 10) ? 10 : range), &immovables);
> + map.find_immovables(Area<FCoords>(field.coords, (range < 11) ? 11 : range), &immovables);
> field.military_stationed_ = 0;
> field.military_unstationed_ = 0;
> field.military_in_constr_nearby_ = 0;
> - field.military_capacity_ = 0;
> + field.area_military_capacity_ = 0;
> field.military_loneliness_ = 1000;
> - field.military_presence_ = 0;
> + field.area_military_presence_ = 0;
>
> for (uint32_t i = 0; i < immovables.size(); ++i) {
>
> @@ -890,6 +931,7 @@
> }
> }
>
> + // if we are here, immovable is ours
> if (upcast(Building const, building, &base_immovable)) {
> if (upcast(ConstructionSite const, constructionsite, building)) {
> const BuildingDescr& target_descr = constructionsite->building();
> @@ -899,7 +941,8 @@
> const int32_t radius = target_ms_d->get_conquers() + 4;
>
> if (radius > dist) {
> - field.military_capacity_ += target_ms_d->get_max_number_of_soldiers() / 2 + 1;
> + field.area_military_capacity_ +=
> + target_ms_d->get_max_number_of_soldiers() / 2 + 1;
> field.military_loneliness_ *= static_cast<double_t>(dist) / radius;
> field.military_in_constr_nearby_ += 1;
> }
> @@ -912,8 +955,8 @@
>
> if (radius > dist) {
>
> - field.military_capacity_ += militarysite->max_soldier_capacity();
> - field.military_presence_ += militarysite->stationed_soldiers().size();
> + field.area_military_capacity_ += militarysite->max_soldier_capacity();
> + field.area_military_presence_ += militarysite->stationed_soldiers().size();
>
> if (militarysite->stationed_soldiers().empty()) {
> field.military_unstationed_ += 1;
> @@ -935,7 +978,7 @@
> Map& map = game().map();
> map.find_immovables(Area<FCoords>(field.coords, 5), &immovables);
> field.preferred_ = false;
> - field.mines_nearby_ = 1;
> + field.mines_nearby_ = 0;
> FCoords fse;
> map.get_brn(field.coords, &fse);
>
> @@ -948,11 +991,20 @@
>
> for (const ImmovableFound& temp_immovable : immovables) {
> if (upcast(Building const, bld, temp_immovable.object)) {
> + if (player_number() != bld->owner().player_number()) {
> + continue;
> + }
> if (bld->descr().get_ismine()) {
> - ++field.mines_nearby_;
> + if (get_building_observer(bld->descr().name().c_str()).mines_ ==
> + field.coords.field->get_resources()) {
> + ++field.mines_nearby_;
> + }
> } else if (upcast(ConstructionSite const, cs, bld)) {
> if (cs->building().get_ismine()) {
> - ++field.mines_nearby_;
> + if (get_building_observer(cs->building().name().c_str()).mines_ ==
> + field.coords.field->get_resources()) {
> + ++field.mines_nearby_;
> + }
> }
> }
> }
> @@ -976,23 +1028,34 @@
> for (uint32_t i = 0; i < buildings_.size(); ++i) {
> buildings_.at(i).current_stats_ = 0;
> buildings_.at(i).unoccupied_ = false;
> + buildings_.at(i).unconnected_ = 0;
> }
>
> // Check all available productionsites
> for (uint32_t i = 0; i < productionsites.size(); ++i) {
> assert(productionsites.front().bo->cnt_built_ > 0);
> - // Add statistics value
> - productionsites.front().bo->current_stats_ +=
> - productionsites.front().site->get_crude_statistics();
> -
> - // counting fishers
> - if (productionsites.front().bo->is_fisher_) {
> - fishers_count += 1;
> + // is connected
> + const bool connected_to_wh =
> + !productionsites.front().site->get_economy()->warehouses().empty();
> +
> + // unconnected buildings are excluded from statistics review
> + if (connected_to_wh) {
> + // Add statistics value
> + productionsites.front().bo->current_stats_ +=
> + productionsites.front().site->get_crude_statistics();
> +
> + // counting fishers
> + if (productionsites.front().bo->is_fisher_) {
> + fishers_count += 1;
> + }
> +
> + // Check whether this building is completely occupied
> + productionsites.front().bo->unoccupied_ |=
> + !productionsites.front().site->can_start_working();
> + } else {
> + productionsites.front().bo->unconnected_ += 1;
> }
>
> - // Check whether this building is completely occupied
> - productionsites.front().bo->unoccupied_ |= !productionsites.front().site->can_start_working();
> -
> // Now reorder the buildings
> productionsites.push_back(productionsites.front());
> productionsites.pop_front();
> @@ -1000,11 +1063,11 @@
>
> if (resource_necessity_water_needed_) {
> if (fishers_count == 0) {
> - resource_necessity_water_ = 255;
> + resource_necessity_water_ = 100;
> } else if (fishers_count == 1) {
> - resource_necessity_water_ = 150;
> + resource_necessity_water_ = 50;
> } else {
> - resource_necessity_water_ = 18;
> + resource_necessity_water_ = 10;
> }
> }
>
> @@ -1012,10 +1075,20 @@
> // Check all available mines
> for (uint32_t i = 0; i < mines_.size(); ++i) {
> assert(mines_.front().bo->cnt_built_ > 0);
> - // Add statistics value
> - mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent();
> - // Check whether this building is completely occupied
> - mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working();
> +
> + const bool connected_to_wh =
> + !productionsites.front().site->get_economy()->warehouses().empty();
> +
> + // unconnected mines are excluded from statistics review
> + if (connected_to_wh) {
> + // Add statistics value
> + mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent();
> + // Check whether this building is completely occupied
> + mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working();
> + } else {
> + buildings_.at(i).unconnected_ += 1;
> + }
> +
> // Now reorder the buildings
> mines_.push_back(mines_.front());
> mines_.pop_front();
> @@ -1023,8 +1096,9 @@
>
> // Scale statistics down
> for (uint32_t i = 0; i < buildings_.size(); ++i) {
> - if (buildings_.at(i).cnt_built_ > 0) {
> - buildings_.at(i).current_stats_ /= buildings_.at(i).cnt_built_;
> + if ((buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_) > 0) {
> + buildings_.at(i).current_stats_ /=
> + (buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_);
> }
> }
> }
> @@ -1100,54 +1174,48 @@
>
> // sometimes there is too many military buildings in construction, so we must
> // prevent initialization of further buildings start
> - const uint32_t treshold = militarysites.size() / 40 + 2;
> -
> - if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) {
> + const int32_t vacant_plus_in_construction_minus_prod =
> + vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 15;
> + if (vacant_plus_in_construction_minus_prod > 20) {
> expansion_mode = MilitaryStrategy::kNoNewMilitary;
> - } else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) {
> + } else if (vacant_plus_in_construction_minus_prod > 13) {
> expansion_mode = MilitaryStrategy::kDefenseOnly;
> - } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= treshold - 1) {
> + } else if (vacant_plus_in_construction_minus_prod > 6) {
> expansion_mode = MilitaryStrategy::kResourcesOrDefense;
> - } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
> - expansion_mode = MilitaryStrategy::kExpansion;
> } else {
> - expansion_mode = MilitaryStrategy::kPushExpansion;
> + if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
> + expansion_mode = MilitaryStrategy::kExpansion;
> + } else {
> + expansion_mode = MilitaryStrategy::kPushExpansion;
> + }
> }
>
> // we must consider need for mines
> // set necessity for mines
> // we use 'virtual mines', because also mine spots can be changed
> // to mines when AI decides so
> - const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15;
> - if (virtual_mines <= 7) {
> - resource_necessity_mines_ = std::numeric_limits<uint8_t>::max();
> - } else if (virtual_mines > 19) {
> - resource_necessity_mines_ = 50;
> + const int32_t virtual_mines =
> + mines_.size() + mineable_fields.size() / 15 - productionsites.size() / 25;
> + resource_necessity_mines_ = 100 * (15 - virtual_mines) / 15;
> + resource_necessity_mines_ = (resource_necessity_mines_ > 100) ? 100 : resource_necessity_mines_;
> + resource_necessity_mines_ = (resource_necessity_mines_ < 20) ? 10 : resource_necessity_mines_;
> +
> + // here we calculate how badly we need to expand, result is number (0-100)
> + // like a percent
> + if (spots_ == 0) {
> + resource_necessity_territory_ = 100;
> } else {
> - const uint32_t tmp = (24 - virtual_mines) * 10;
> - resource_necessity_mines_ = tmp;
> - }
> -
> - // here we calculate a need for expansion and reduce necessity for new land
> - // the game has two stages:
> - // First: virtual mines<=5 - stage of building the economics
> - // Second: virtual mines>5 - teritorial expansion
> - if (virtual_mines <= 5) {
> - if (spots_avail.at(BUILDCAPS_BIG) <= 4) {
> - resource_necessity_territory_ = 255;
> - } else {
> - resource_necessity_territory_ = 0;
> + resource_necessity_territory_ = 100 * 5 * (productionsites.size() + 5) / spots_;
> + resource_necessity_territory_ =
> + (resource_necessity_territory_ > 100) ? 100 : resource_necessity_territory_;
> + resource_necessity_territory_ =
> + (resource_necessity_territory_ < 10) ? 10 : resource_necessity_territory_;
> + // alse we need at lest 4 big spots
> + if (spots_avail.at(BUILDCAPS_BIG) < 2) {
> + resource_necessity_territory_ = 100;
> }
> - } else { // or we have enough mines and regulate speed of expansion
> - if (spots_ == 0) {
> - resource_necessity_territory_ = 255;
> - } else {
> - const uint32_t tmp = 255 * 4 * productionsites.size() / spots_;
> - if (tmp > 255) {
> - resource_necessity_territory_ = 255;
> - } else {
> - resource_necessity_territory_ = tmp;
> - }
> + if (spots_avail.at(BUILDCAPS_MEDIUM) < 4) {
> + resource_necessity_territory_ = 100;
> }
> }
>
> @@ -1205,7 +1273,7 @@
> ++i) {
> BuildableField* const bf = *i;
>
> - if (bf->next_update_due_ < gametime - 10000) {
> + if (bf->field_info_expiration_ < gametime) {
> continue;
> }
>
> @@ -1369,7 +1437,7 @@
> continue;
> }
>
> - if (bo.total_count() == 0) {
> + if (bo.total_count() - bo.unconnected_ == 0) {
> prio += 150;
> }
>
> @@ -1394,12 +1462,15 @@
> continue;
> }
>
> - if (new_buildings_stop_) {
> + if (gametime > 5 * 60 * 1000 &&
> + (bo.total_count() - bo.unconnected_ == 0)) {
> + prio += 10;
> + } else if (new_buildings_stop_) {
> continue;
> }
>
> prio +=
> - (bf->critters_nearby_ * 2) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
> + (bf->critters_nearby_ * 3) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
>
> } else if (bo.is_fisher_) { // fisher
>
> @@ -1490,7 +1561,7 @@
> prio -= bf->space_consumers_nearby_ * 5;
>
> } else { // FISH BREEDERS and GAME KEEPERS
> - if (new_buildings_stop_ && bo.total_count() > 0) {
> + if (new_buildings_stop_ && (bo.total_count() - bo.unconnected_) > 0) {
> continue;
> }
>
> @@ -1502,7 +1573,7 @@
> prio += bf->water_nearby_ / 5;
> }
>
> - if (bo.total_count() > bo.cnt_target_) {
> + if ((bo.total_count() - bo.unconnected_) > bo.cnt_target_) {
> continue;
> }
>
> @@ -1543,9 +1614,10 @@
> continue;
> }
>
> - if (bo.forced_after_ < gametime && bo.total_count() == 0) {
> + if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
> prio += 150;
> - } else if (bo.cnt_built_ == 1 && game().get_gametime() > 40 * 60 * 1000 &&
> + } else if ((bo.cnt_built_ - bo.unconnected_) == 1 &&
> + game().get_gametime() > 40 * 60 * 1000 &&
> bo.desc->enhancement() != INVALID_INDEX && !mines_.empty()) {
> prio += 10;
> } else if (bo.is_shipyard_) {
> @@ -1554,9 +1626,10 @@
> }
> } else if (!output_is_needed) {
> continue;
> - } else if (bo.cnt_built_ == 0 && game().get_gametime() > 40 * 60 * 1000) {
> + } else if ((bo.cnt_built_ - bo.unconnected_) == 0 &&
> + game().get_gametime() > 40 * 60 * 1000) {
> prio += kDefaultPrioBoost;
> - } else if (bo.cnt_built_ > 1 && bo.current_stats_ > 97) {
> + } else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
> prio -= kDefaultPrioBoost * (new_buildings_stop_);
> } else if (new_buildings_stop_)
> continue;
> @@ -1593,15 +1666,16 @@
>
> else if (bo.is_shipyard_) {
> // for now AI builds only one shipyard
> - if (bf->water_nearby_ > 3 && bo.total_count() == 0 && seafaring_economy) {
> + if (bf->water_nearby_ > 3 && (bo.total_count() - bo.unconnected_) == 0 &&
> + seafaring_economy) {
> prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_;
> }
>
> } else if (!bo.inputs_.empty()) {
> - if (bo.total_count() == 0) {
> + if ((bo.total_count() == 0 - bo.unconnected_)) {
> prio += max_needed_preciousness + kDefaultPrioBoost;
> }
> - if (bo.cnt_built_ > 0 && bo.current_stats_ > 70) {
> + if ((bo.cnt_built_ - bo.unconnected_) > 0 && bo.current_stats_ > 70) {
> prio += max_needed_preciousness + kDefaultPrioBoost - 3 +
> (bo.current_stats_ - 70) / 5;
> }
> @@ -1623,7 +1697,6 @@
> }
> } // production sites done
> else if (bo.type == BuildingObserver::MILITARYSITE) {
> -
> // we allow 1 exemption from big buildings prohibition
> if (bo.build_material_shortage_ &&
> (bo.cnt_under_construction_ > 0 || !(bf->enemy_nearby_))) {
> @@ -1631,6 +1704,7 @@
> }
>
> if (!bf->unowned_land_nearby_) {
> +
> continue;
> }
>
> @@ -1648,8 +1722,7 @@
>
> if (expansion_mode == MilitaryStrategy::kResourcesOrDefense &&
> !(bf->unowned_mines_pots_nearby_ || bf->stones_nearby_ || bf->water_nearby_ ||
> - (bf->distant_water_ && resource_necessity_water_needed_) ||
> - bf->enemy_nearby_)) {
> + (bf->distant_water_ && resource_necessity_water_needed_) || bf->enemy_nearby_)) {
> continue;
> }
>
> @@ -1667,31 +1740,40 @@
> if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {
> continue;
> };
> - }
> - else {
> + } else {
> continue;
> } // the building is not suitable for situation
> -
> // not to build so many military buildings nearby
> if (!bf->enemy_nearby_ &&
> (bf->military_in_constr_nearby_ + bf->military_unstationed_) > 0) {
> continue;
> }
> -
> // a boost to prevent an expansion halt
> int32_t local_boost = 0;
> if (expansion_mode == MilitaryStrategy::kPushExpansion) {
> local_boost = 200;
> }
>
> - prio = ((bf->unowned_land_nearby_ * 2 * resource_necessity_territory_) / 255 +
> - (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 255 +
> - bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost +
> - (bf->water_nearby_ * resource_necessity_water_) / 255);
> + // priority based on basic resources
> + prio = ((bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 100 +
> + bf->stones_nearby_ + bf->military_loneliness_ / 10 - 40 + local_boost +
> + (bf->water_nearby_ * resource_necessity_water_) / 100);
> +
> + // Depending on wheter resource only are considered or no
> + if (expansion_mode == MilitaryStrategy::kResourcesOrDefense) {
> + prio *= 2;
> + prio += bf->unowned_land_nearby_ * resource_necessity_territory_ / 100 / 2;
> + } else { // addding score for territory
> + prio += (bf->unowned_land_nearby_ * resource_necessity_territory_) / 100 * 3 / 2;
> + }
> +
> + // adding score for distance to other military sites
> + prio += bf->military_loneliness_ / 10 - 40;
>
> // special bonus due to remote water for atlanteans
> - if (resource_necessity_water_needed_)
> - prio += (bf->distant_water_ * resource_necessity_water_) / 255;
> + if (resource_necessity_water_needed_) {
> + prio += (bf->distant_water_ * resource_necessity_water_) / 100 / 2;
> + }
>
> // special bonus if a portspace is close
> if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
> @@ -1702,6 +1784,11 @@
> }
> }
>
> + //special bonus for bigger buildings in enemy is nearby
> + if (bf->enemy_nearby_) {
> + prio += (bo.desc->get_size() - 1) * 15;
> + }
> +
> if (bo.desc->get_size() < maxsize) {
> prio = prio - 5;
> } // penalty
> @@ -1713,12 +1800,12 @@
> // for expansion)
> const int16_t bottom_treshold =
> 15 - ((virtual_mines <= 4) ? (5 - virtual_mines) * 2 : 0);
> - if (bf->enemy_nearby_ && bf->military_capacity_ < bottom_treshold) {
> - prio += 50 + (bottom_treshold - bf->military_capacity_) * 20;
> + if (bf->enemy_nearby_ && bf->area_military_capacity_ < bottom_treshold) {
> + prio += 50 + (bottom_treshold - bf->area_military_capacity_) * 20;
> }
>
> - if (bf->enemy_nearby_ && bf->military_capacity_ > bottom_treshold + 4) {
> - prio -= (bf->military_capacity_ - (bottom_treshold + 4)) * 5;
> + if (bf->enemy_nearby_ && bf->area_military_capacity_ > bottom_treshold + 4) {
> + prio -= (bf->area_military_capacity_ - (bottom_treshold + 4)) * 5;
> }
>
> } else if (bo.type == BuildingObserver::WAREHOUSE) {
> @@ -1780,7 +1867,7 @@
>
> // take care about and enemies
> if (bf->enemy_nearby_) {
> - prio /= 2;
> + prio /= 4;
> }
>
> if (bf->unowned_land_nearby_ && !bo.is_port_) {
> @@ -1789,24 +1876,43 @@
>
> } else if (bo.type == BuildingObserver::TRAININGSITE) {
>
> - if (virtual_mines < 5) {
> - continue;
> - }
> -
> // exclude spots on border
> if (bf->near_border_) {
> continue;
> }
>
> - if (virtual_mines < 3) {
> - continue;
> - }
> -
> - // build after 20 production sites and then after each 50 production site
> - if (static_cast<int32_t>((productionsites.size() + 40) / 60) > bo.total_count() &&
> - bo.cnt_under_construction_ == 0) {
> - prio = 4 + kDefaultPrioBoost;
> - }
> + // it is a bit difficult to get a new trainer.....
> + if (ts_without_trainers_) {
> + continue;
> + }
> +
> + // target is only one for both types
> + if ((ts_type1_const_count_ + ts_type2_const_count_) > 0) {
> + continue;
> + }
> +
> + // we build one training site for 100 militarysites
> + if (bo.ts_type_ == 1 &&
> + militarysites.size() / 100 < static_cast<int32_t>(ts_type1_count_)) {
> + continue;
> + }
> + // we build one training site for 100 militarysites
> + if (bo.ts_type_ == 2 &&
> + militarysites.size() / 100 < static_cast<int32_t>(ts_type2_count_)) {
> + continue;
> + }
> +
> + // for type1 we need 15 productionsties
> + if (bo.ts_type_ == 1 && productionsites.size() < 15) {
> + continue;
> + }
> +
> + // for type2 we need 4 mines
> + if (bo.ts_type_ == 2 && virtual_mines < 4) {
> + continue;
> + }
> +
> + prio = 4 + kDefaultPrioBoost;
>
> // take care about borders and enemies
> if (bf->enemy_nearby_) {
> @@ -1854,8 +1960,9 @@
> // then try all mines_ - as soon as basic economy is build up.
> if (gametime > next_mine_construction_due_) {
>
> - update_all_mineable_fields(gametime);
> - next_mine_construction_due_ = gametime + kIdleMineUpdateInterval;
> + // not done here
> + // update_all_mineable_fields(gametime);
> + next_mine_construction_due_ = gametime + kNewMineConstInterval;
>
> if (!mineable_fields.empty()) {
>
> @@ -1885,22 +1992,33 @@
> check_ware_necessity(
> bo, &output_is_needed, &max_preciousness, &max_needed_preciousness);
>
> - if (!output_is_needed && bo.total_count() > 0) {
> + if (!output_is_needed && (bo.total_count() - bo.unconnected_) > 0) {
> continue;
> }
>
> // if current one(s) are performing badly
> - if (bo.total_count() >= 1 && bo.current_stats_ < 50) {
> + if ((bo.total_count() - bo.unconnected_) >= 1 && bo.current_stats_ < 50) {
> continue;
> }
>
> // this is penalty if there are existing mines too close
> // it is treated as multiplicator for count of near mines
> uint32_t nearness_penalty = 0;
> - if ((bo.cnt_built_ + bo.cnt_under_construction_) == 0) {
> + if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
> + 0) {
> nearness_penalty = 0;
> } else {
> - nearness_penalty = 10;
> + nearness_penalty = 30;
> + }
> +
> + // bonus score to prefer if too few mines
> + uint32_t bonus_score = 0;
> + if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
> + 0) {
> + bonus_score = 15;
> + } else if ((mines_per_type[bo.mines_].in_construction +
> + mines_per_type[bo.mines_].finished) == 1) {
> + bonus_score = 5;
> }
>
> // iterating over fields
> @@ -1908,26 +2026,46 @@
> j != mineable_fields.end();
> ++j) {
>
> - if ((*j)->coords.field->get_resources() != bo.mines_) {
> - continue;
> - }
> -
> - int32_t prio = (*j)->coords.field->get_resources_amount();
> + MineableField* const mf = *j;
> +
> + if (mf->field_info_expiration_ <= gametime) {
> + continue;
> + }
> +
> + if (mf->coords.field->get_resources() != bo.mines_) {
> + continue;
> + }
> +
> + int32_t prio = 0;
> + MapRegion<Area<FCoords>> mr(map, Area<FCoords>(mf->coords, 2));
> + do {
> + if (bo.mines_ == mr.location().field->get_resources()) {
> + prio += mr.location().field->get_resources_amount();
> + }
> + } while (mr.advance(map));
> +
> + prio /= 10;
> +
> + // Only build mines_ on locations where some material can be mined
> + if (prio < 1) {
> + continue;
> + }
>
> // applying nearnes penalty
> - prio = prio - (*j)->mines_nearby_ * nearness_penalty;
> -
> - // Only build mines_ on locations where some material can be mined
> - if (prio < 2) {
> - continue;
> - }
> + prio -= mf->mines_nearby_ * nearness_penalty;
> +
> + // applying bonus score
> + prio += bonus_score;
> +
> + // applying max needed
> + prio += max_needed_preciousness * 3;
>
> // prefer mines in the middle of mine fields of the
> // same type, so we add a small bonus here
> // depending on count of same mines nearby,
> // though this does not reflects how many resources
> // are (left) in nearby mines
> - prio += (*j)->same_mine_fields_nearby_ / 3;
> + prio += mf->same_mine_fields_nearby_;
>
> // Continue if field is blocked at the moment
> bool blocked = false;
> @@ -1946,13 +2084,12 @@
> }
>
> // Prefer road side fields
> - prio += (*j)->preferred_ ? 1 : 0;
> + prio += mf->preferred_ ? 1 : 0;
>
> if (prio > proposed_priority) {
> - // proposed_building = bo.id;
> best_building = &bo;
> proposed_priority = prio;
> - proposed_coords = (*j)->coords;
> + proposed_coords = mf->coords;
> mine = true;
> }
> } // end of evaluation of field
> @@ -2257,11 +2394,11 @@
>
> if (occupied_military_) {
> eco->dismantle_grace_time_ =
> - (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
> + (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
>
> } else { // for other normal buildings
> eco->dismantle_grace_time_ =
> - gametime + (5 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
> + gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
> }
> }
>
> @@ -2547,6 +2684,9 @@
> site.unoccupied_till_ = game().get_gametime();
> }
>
> + // is it connected to wh at all?
> + const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
> +
> // do not dismantle or upgrade the same type of building too soon - to give some time to update
> // statistics
> if (site.bo->last_dismantle_time_ > game().get_gametime() - 30 * 1000) {
> @@ -2571,7 +2711,8 @@
> // b) if there are two buildings
> // statistics percents are decisive
> const BuildingIndex enhancement = site.site->descr().enhancement();
> - if (enhancement != INVALID_INDEX && (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
> + if (connected_to_wh && enhancement != INVALID_INDEX &&
> + (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
>
> BuildingIndex enbld = INVALID_INDEX; // to get rid of this
>
> @@ -2664,7 +2805,11 @@
> // so finally we dismantle the lumberjac
> site.bo->last_dismantle_time_ = game().get_gametime();
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
>
> return true;
> }
> @@ -2675,7 +2820,11 @@
> site.site->get_statistics_percent() == 0) {
> site.bo->last_dismantle_time_ = gametime;
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
>
> return true;
> }
> @@ -2695,7 +2844,11 @@
> if (site.bo->stocklevel_ > 250 + productionsites.size() * 5) { // dismantle
> site.bo->last_dismantle_time_ = game().get_gametime();
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
>
> @@ -2714,7 +2867,11 @@
> // the destruction of the flag avoids that defaultAI will have too many
> // unused roads - if needed the road will be rebuild directly.
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
>
> @@ -2723,7 +2880,11 @@
> // it is possible that there are stones but quary is not able to mine them
> site.bo->last_dismantle_time_ = game().get_gametime();
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
>
> return true;
> }
> @@ -2743,7 +2904,7 @@
> site.bo->space_consumer_ && !site.bo->plants_trees_) {
>
> // if we have more buildings then target
> - if (site.bo->cnt_built_ > site.bo->cnt_target_) {
> + if ((site.bo->cnt_built_ - site.bo->unconnected_) > site.bo->cnt_target_) {
> if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
> site.bo->stocklevel_ = get_stocklevel(*site.bo);
> site.bo->stocklevel_time = game().get_gametime();
> @@ -2753,7 +2914,11 @@
> site.bo->stocklevel_ > 100) { // production stats == 0%
> site.bo->last_dismantle_time_ = game().get_gametime();
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
> }
> @@ -2762,7 +2927,11 @@
> if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built_ > 1) {
>
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
>
> @@ -2778,7 +2947,11 @@
>
> site.bo->last_dismantle_time_ = game().get_gametime();
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
>
> @@ -2792,7 +2965,11 @@
>
> site.bo->last_dismantle_time_ = game().get_gametime();
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
>
> @@ -2813,16 +2990,29 @@
>
> site.bo->last_dismantle_time_ = game().get_gametime();
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
>
> if (score > 120 && !site.site->is_stopped()) {
>
> game().send_player_start_stop_building(*site.site);
> + return true;
> }
> + const uint32_t trees_in_vicinity =
> + map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5),
> + nullptr,
> + FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
>
> - if (score < 80 && site.site->is_stopped()) {
> + if (trees_in_vicinity > 25) {
> + if (!site.site->is_stopped()) {
> + game().send_player_start_stop_building(*site.site);
> + }
> + } else if (score < 80 && site.site->is_stopped()) {
>
> game().send_player_start_stop_building(*site.site);
> }
> @@ -3043,11 +3233,17 @@
> // Get link to productionsite that should be checked
> ProductionSiteObserver& site = mines_.front();
>
> + const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
> +
> // first get rid of mines that are missing workers for some time (6 minutes),
> // released worker (if any) can be usefull elsewhere !
> if (site.built_time_ + 6 * 60 * 1000 < gametime && !site.site->can_start_working()) {
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> return true;
> }
>
> @@ -3059,7 +3255,11 @@
> // dismantling when the failed count is too high
> if (site.no_resources_count > 12) {
> flags_to_be_removed.push_back(site.site->base_flag().get_position());
> - game().send_player_dismantle(*site.site);
> + if (connected_to_wh) {
> + game().send_player_dismantle(*site.site);
> + } else {
> + game().send_player_bulldoze(*site.site);
> + }
> site.bo->construction_decision_time_ = gametime;
> return true;
> }
> @@ -3084,6 +3284,11 @@
> return false;
> }
>
> + if (!connected_to_wh) {
> + // no enhancement possible
> + return false;
> + }
> +
> bool changed = false;
> if (player_->is_building_type_allowed(enhancement)) {
> // first exclude possibility there are enhancements in construction or unoccupied_
> @@ -3212,6 +3417,18 @@
> return count;
> }
>
> +// this just counts free positions in military and training sites
> +void DefaultAI::count_military_vacant_positions() {
> + // counting vacant positions
> + vacant_mil_positions_ = 0;
> + for (TrainingSiteObserver tso : trainingsites) {
> + vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size();
> + }
> + for (MilitarySiteObserver mso : militarysites) {
> + vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
> + }
> +}
> +
> // this function only manipulates with trainingsites' inputs priority
> // decreases it when too many unoccupied military buildings
> bool DefaultAI::check_trainingsites(uint32_t gametime) {
> @@ -3221,22 +3438,38 @@
> if (!trainingsites.empty()) {
> taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval;
> } else {
> - taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 5 * kTrainingSitesCheckInterval;
> - }
> -
> - uint8_t new_priority = DEFAULT_PRIORITY;
> - if (unstationed_milit_buildings_ > 2) {
> - new_priority = LOW_PRIORITY;
> - } else {
> - new_priority = DEFAULT_PRIORITY;
> - }
> + taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 2 * kTrainingSitesCheckInterval;
> + return false;
> + }
> +
> + TrainingSite* ts = trainingsites.front().site;
> + TrainingSiteObserver& tso = trainingsites.front();
> +
> + const BuildingIndex enhancement = ts->descr().enhancement();
> +
> + if (enhancement != INVALID_INDEX && ts_without_trainers_ == 0 && mines_.size() > 3 &&
> + (ts_type1_const_count_ + ts_type2_const_count_) == 0 && ts_type2_count_ > 0) {
> +
> + if (player_->is_building_type_allowed(enhancement)) {
> + game().send_player_enhance_building(*tso.site, enhancement);
> + }
> + }
> +
> + trainingsites.push_back(trainingsites.front());
> + trainingsites.pop_front();
> +
> + // changing capacity
> + if (tso.site->soldier_capacity() != 2) {
> + game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity());
> + }
> +
> + ts_without_trainers_ = 0; // zeroing
> for (std::list<TrainingSiteObserver>::iterator site = trainingsites.begin();
> site != trainingsites.end();
> ++site) {
>
> - for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) {
> - game().send_player_set_ware_priority(
> - *site->site, wwWARE, site->bo->inputs_.at(k), new_priority);
> + if (!site->site->can_start_working()) {
> + ts_without_trainers_ += 1;
> }
> }
> return true;
> @@ -3310,14 +3543,18 @@
> BuildableField bf(f);
> update_buildable_field(bf, vision, true);
> const int32_t size_penalty = ms->get_size() - 1;
> + FindNodeAllyOwned find_ally(player_, game(), player_number());
> + const int32_t allyOwnedFields =
> + map.find_fields(Area<FCoords>(f, vision), nullptr, find_ally);
>
> int16_t score = 0;
> - score += (bf.military_capacity_ > 5);
> - score += (bf.military_presence_ > 3);
> + score += (bf.area_military_capacity_ > 6);
> + score += (bf.area_military_capacity_ > 18);
> + score += (bf.area_military_presence_ > 4);
> score += (bf.military_loneliness_ < 180);
> - score += (bf.military_stationed_ > (2 + size_penalty));
> - score -= (ms->soldier_capacity() * 2 > static_cast<uint32_t>(bf.military_capacity_));
> - score += (bf.unowned_land_nearby_ < 10);
> + score += (bf.military_stationed_ > 2);
> + score -= size_penalty;
> + score += ((bf.unowned_land_nearby_ + allyOwnedFields) < 10);
>
> if (score >= 4) {
> if (ms->get_playercaps() & Widelands::Building::PCap_Dismantle) {
> @@ -3340,7 +3577,6 @@
>
> // yes enemy is nearby, but still we must distinguish whether
> // he is accessible (over the land)
> -
> if (other_player_accessible(
> vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) {
>
> @@ -3502,7 +3738,10 @@
> }
> }
>
> - throw wexception("Help: I do not know what to do with a %s", name);
> + throw wexception("Help: I (player %d / tribe %s) do not know what to do with a %s",
> + player_number(),
> + tribe_->name().c_str(),
> + name);
> }
>
> // this is called whenever we gain ownership of a PlayerImmovable
> @@ -3804,6 +4043,7 @@
>
> // this is called whenever we gain a new building
> void DefaultAI::gain_building(Building& b) {
> +
> BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
>
> if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
> @@ -3817,6 +4057,17 @@
> if (target_bo.type == BuildingObserver::MILITARYSITE) {
> ++num_milit_constructionsites;
> }
> + if (target_bo.type == BuildingObserver::MINE) {
> + mines_per_type[target_bo.mines_].in_construction += 1;
> + }
> + if (target_bo.type == BuildingObserver::TRAININGSITE) {
> + if (target_bo.ts_type_ == 1) {
> + ts_type1_const_count_ += 1;
> + }
> + if (target_bo.ts_type_ == 2) {
> + ts_type2_const_count_ += 1;
> + }
> + }
>
> // Let defaultAI try to directly connect the constructionsite
> taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime();
> @@ -3852,6 +4103,9 @@
>
> for (uint32_t i = 0; i < bo.inputs_.size(); ++i)
> ++wares.at(bo.inputs_.at(i)).consumers_;
> +
> + mines_per_type[bo.mines_].finished += 1;
> +
> } else if (bo.type == BuildingObserver::MILITARYSITE) {
> militarysites.push_back(MilitarySiteObserver());
> militarysites.back().site = &dynamic_cast<MilitarySite&>(b);
> @@ -3860,9 +4114,16 @@
> militarysites.back().enemies_nearby_ = true;
>
> } else if (bo.type == BuildingObserver::TRAININGSITE) {
> + ts_without_trainers_ += 1;
> trainingsites.push_back(TrainingSiteObserver());
> trainingsites.back().site = &dynamic_cast<TrainingSite&>(b);
> trainingsites.back().bo = &bo;
> + if (bo.ts_type_ == 1) {
> + ts_type1_count_ += 1;
> + }
> + if (bo.ts_type_ == 2) {
> + ts_type2_count_ += 1;
> + }
>
> } else if (bo.type == BuildingObserver::WAREHOUSE) {
> ++numof_warehouses_;
> @@ -3888,6 +4149,7 @@
>
> // this is called whenever we lose a building
> void DefaultAI::lose_building(const Building& b) {
> +
> BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
>
> if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
> @@ -3901,6 +4163,17 @@
> if (target_bo.type == BuildingObserver::MILITARYSITE) {
> --num_milit_constructionsites;
> }
> + if (target_bo.type == BuildingObserver::MINE) {
> + mines_per_type[target_bo.mines_].in_construction -= 1;
> + }
> + if (target_bo.type == BuildingObserver::TRAININGSITE) {
> + if (target_bo.ts_type_ == 1) {
> + ts_type1_const_count_ -= 1;
> + }
> + if (target_bo.ts_type_ == 2) {
> + ts_type2_const_count_ -= 1;
> + }
> + }
>
> } else {
> --bo.cnt_built_;
> @@ -3938,6 +4211,9 @@
> for (uint32_t i = 0; i < bo.inputs_.size(); ++i) {
> --wares.at(bo.inputs_.at(i)).consumers_;
> }
> +
> + mines_per_type[bo.mines_].finished -= 1;
> +
> } else if (bo.type == BuildingObserver::MILITARYSITE) {
>
> for (std::list<MilitarySiteObserver>::iterator i = militarysites.begin();
> @@ -3955,6 +4231,12 @@
> ++i) {
> if (i->site == &b) {
> trainingsites.erase(i);
> + if (bo.ts_type_ == 1) {
> + ts_type1_count_ -= 1;
> + }
> + if (bo.ts_type_ == 2) {
> + ts_type2_count_ -= 1;
> + }
> break;
> }
> }
> @@ -3999,49 +4281,21 @@
> return supplied == bo.inputs_.size();
> }
>
> -/**
> - * The defaultAi "considers" via this function whether to attack an
> - * enemy, if opposing military buildings are in sight. In case of an attack it
> - * sends all available forces.
> - *
> - * \returns true, if attack was started.
> - */
> -
> -bool DefaultAI::consider_attack(int32_t const gametime) {
> -
> - // we assume that we are not attacking so we extend waitperiod
> - // in case of attack the variable will be decreased below
> - // this is intended to save some CPU and add randomness in attacking
> - // and also differentiate according to type
> - next_attack_waittime_ += gametime % 30;
> - if (next_attack_waittime_ > 600 && type_ == DEFENSIVE) {
> - next_attack_waittime_ = 20;
> - }
> - if (next_attack_waittime_ > 450 && type_ == NORMAL) {
> - next_attack_waittime_ = 20;
> - }
> - if (next_attack_waittime_ > 300 && type_ == AGGRESSIVE) {
> - next_attack_waittime_ = 20;
> - }
> -
> - // Only useable, if it owns at least one militarysite
> - if (militarysites.empty()) {
> - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
> - return false;
> - }
> -
> - // First we iterate over all players and define which ones (if any)
> - // are attackable (comparing overal strength)
> - // counting players in game
> - uint32_t plr_in_game = 0;
> +bool DefaultAI::check_enemy_sites(uint32_t const gametime) {
> +
> + Map& map = game().map();
> +
> + // define which players are attackable
> std::vector<bool> player_attackable;
> - PlayerNumber const nr_players = game().map().get_nrplayers();
> + PlayerNumber const nr_players = map.get_nrplayers();
> player_attackable.resize(nr_players);
> - bool any_attackable = false;
> + uint32_t plr_in_game = 0;
> uint16_t const pn = player_number();
> - std::unordered_set<uint32_t> irrelevant_immovables;
> -
> - std::vector<ImmovableFound> target_buildings;
> +
> + iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
> +
> + // receiving games statistics and parsing it (reading latest entry)
> + const Game::GeneralStatsVector& genstats = game().get_general_statistics();
>
> // defining treshold ratio of own_strenght/enemy's_strength
> uint32_t treshold_ratio = 100;
> @@ -4052,37 +4306,10 @@
> treshold_ratio = 120;
> }
>
> - iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
> -
> - // receiving games statistics and parsing it (reading latest entry)
> - const Game::GeneralStatsVector& genstats = game().get_general_statistics();
> -
> - // first we try to prevent exhaustion of military forces (soldiers)
> - // via excessive attacking
> - // before building an economy with mines.
> - // 'Margin' is an difference between count of actual soldiers and
> - // military sites to be manned.
> - // If we have no mines yet, we need to preserve some soldiers for further
> - // expansion (if enemy allows this)
> - // TODO(sirver): this next line is completely unreadable and maybe even wrong given the
> - // precedence of ?: and +. Replace through some if/else.
> - int32_t needed_margin = (mines_.size() < 6) ?
> - ((6 - mines_.size()) * 3) :
> - 0 + 2 + ((type_ == NORMAL) ? 4 : 0 + ((type_ == DEFENSIVE) ? 8 : 0));
> - const int32_t current_margin =
> - genstats[pn - 1].miltary_strength.back() - militarysites.size() - num_milit_constructionsites;
> -
> - if (current_margin < needed_margin) { // no attacking!
> - last_attack_target_.x = std::numeric_limits<uint16_t>::max();
> - last_attack_target_.y = std::numeric_limits<uint16_t>::max();
> - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
> - return false;
> - }
> -
> // now we test all players which one are 'attackable'
> for (uint8_t j = 1; j <= plr_in_game; ++j) {
> - if (pn == j) {
> - player_attackable.at(j - 1) = false;
> + if (pn == j) { // its me
> + player_attackable[j - 1] = false;
> continue;
> }
>
> @@ -4095,12 +4322,10 @@
> // Avoid division by zero
> } else if (genstats.at(j - 1).miltary_strength.back() == 0) {
> player_attackable.at(j - 1) = true;
> - any_attackable = true;
> // Check threshold
> } else if ((genstats.at(pn - 1).miltary_strength.back() * 100 /
> genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) {
> player_attackable.at(j - 1) = true;
> - any_attackable = true;
> } else {
> player_attackable.at(j - 1) = false;
> }
> @@ -4112,147 +4337,229 @@
> }
> }
>
> - // if we cannot attack anybody, terminating...
> - if (!any_attackable) {
> - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
> - last_attack_target_.x = std::numeric_limits<uint16_t>::max();
> - last_attack_target_.y = std::numeric_limits<uint16_t>::max();
> - return false;
> - }
> -
> - // the logic of attacking is to pick n own military buildings - random ones
> - // and test the vicinity for attackable buildings
> - // candidates are put into target_buildings vector for later processing
> - const uint16_t test_every = 4;
> - Map& map = game().map();
> - MilitarySite* best_ms_target = nullptr;
> - Warehouse* best_wh_target = nullptr;
> - int32_t best_ms_score = 0;
> - int32_t best_wh_score = 0;
> - const int8_t minimal_difference = 2;
> -
> - for (uint32_t position = gametime % test_every; position < militarysites.size();
> - position += test_every) {
> -
> - std::list<MilitarySiteObserver>::iterator mso = militarysites.begin();
> - std::advance(mso, position);
> -
> - MilitarySite* ms = mso->site;
> -
> - if (!mso->enemies_nearby_) {
> - continue;
> - }
> -
> + // first we scan vicitnity of couple of militarysites to get new enemy sites
> + // militarysites rotate
> + int32_t i = 0;
> + for (MilitarySiteObserver mso : militarysites) {
> + i += 1;
> + if (i % 4 == 0)
> + continue;
> + if (i > 20)
> + continue;
> +
> + MilitarySite* ms = mso.site;
> uint32_t const vision = ms->descr().vision_range();
> FCoords f = map.get_fcoords(ms->get_position());
>
> // get list of immovable around this our military site
> std::vector<ImmovableFound> immovables;
> - map.find_immovables(Area<FCoords>(f, vision + 3), &immovables, FindImmovableAttackable());
> + map.find_immovables(Area<FCoords>(f, (vision + 3 < 13) ? 13 : vision + 3),
> + &immovables,
> + FindImmovableAttackable());
>
> for (uint32_t j = 0; j < immovables.size(); ++j) {
> -
> - // skip if in irrelevant_immovables
> - const uint32_t hash = coords_hash(immovables.at(j).coords);
> - if (irrelevant_immovables.count(hash) == 0) {
> - irrelevant_immovables.insert(hash);
> - }
> -
> - // maybe these are not good candidates to attack
> - if (upcast(MilitarySite, bld, immovables.at(j).object)) {
> -
> - if (!player_attackable[bld->owner().player_number() - 1]) {
> - continue;
> - }
> -
> - // in case this is the same building as previously attacked
> - if (last_attack_target_ == bld->get_position()) {
> - continue;
> - }
> -
> + if (upcast(MilitarySite const, bld, immovables.at(j).object)) {
> + if (player_->is_hostile(bld->owner())) {
> + if (enemy_sites.count(coords_hash(bld->get_position())) == 0) {
> + enemy_sites[coords_hash(bld->get_position())] = EnemySiteObserver();
> + }
> + }
> + }
> + if (upcast(Warehouse const, wh, immovables.at(j).object)) {
> + if (player_->is_hostile(wh->owner())) {
> + if (enemy_sites.count(coords_hash(wh->get_position())) == 0) {
> + enemy_sites[coords_hash(wh->get_position())] = EnemySiteObserver();
> + }
> + }
> + }
> + }
> + }
> +
> + // now we update some of them
> + uint32_t best_target = std::numeric_limits<uint32_t>::max();
> + uint8_t best_score = 0;
> + uint32_t count = 0;
> +
> + for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
> + site != enemy_sites.end();
> + ++site) {
> +
> + // we test max 12 sites and prefer ones tested more then 1 min ago
> + if (((site->second.last_tested + (enemysites_check_delay_ * 1000)) > gametime && count > 4) ||
> + count > 12) {
> + continue;
> + }
> + count += 1;
> +
> + site->second.last_tested = gametime;
> + uint8_t defenders = 0;
> + bool is_warehouse = false;
> + bool is_attackable = false;
> + uint16_t onwer_number = 100;
> +
> + // testing if we can attack the building - result is a flag
> + // if we dont get a flag, we remove the building from observers list
> + FCoords f = map.get_fcoords(coords_unhash(site->first));
> + uint32_t site_to_be_removed = std::numeric_limits<uint32_t>::max();
> + Flag* flag = nullptr;
> + if (upcast(MilitarySite, bld, f.field->get_immovable())) {
> + if (player_->is_hostile(bld->owner())) {
> + defenders = bld->present_soldiers().size();
> + flag = &bld->base_flag();
> if (bld->can_attack()) {
> -
> - int32_t attack_soldiers = player_->find_attack_soldiers(bld->base_flag());
> - if (attack_soldiers < 1) {
> - continue;
> - }
> -
> - const int32_t soldiers_difference =
> - player_->find_attack_soldiers(bld->base_flag()) - bld->present_soldiers().size();
> -
> - if (soldiers_difference < minimal_difference)
> - continue;
> - if (soldiers_difference <= best_ms_score)
> - continue;
> -
> - best_ms_target = bld;
> - best_ms_score = soldiers_difference;
> - continue;
> - }
> - } else if (upcast(Warehouse, wh, immovables.at(j).object)) {
> - if (!player_->is_hostile(wh->owner())) {
> - continue;
> - }
> -
> - // in case this is the same building as previously attacked
> - if (last_attack_target_ == wh->get_position()) {
> - continue;
> - }
> -
> - if (wh->can_attack()) {
> - int32_t attack_soldiers = player_->find_attack_soldiers(wh->base_flag());
> - if (attack_soldiers < 1) {
> - continue;
> - }
> -
> - const int32_t soldiers_difference = player_->find_attack_soldiers(wh->base_flag()) -
> - wh->present_soldiers().size() +
> - 3; //+3 is to boost attack here
> -
> - if (soldiers_difference < minimal_difference)
> - continue;
> - if (soldiers_difference <= best_wh_score)
> - continue;
> -
> - best_wh_target = wh;
> - best_wh_score = soldiers_difference;
> - }
> - }
> - }
> - }
> -
> - // we always try to attack warehouse first
> - if (best_wh_target != nullptr && gametime % 2 == 0) {
> - // attacking with all attack-ready soldiers
> - int32_t attackers = player_->find_attack_soldiers(best_wh_target->base_flag());
> -
> - game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers);
> - last_attack_target_ = best_wh_target->get_position();
> - taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
> - next_attack_waittime_ = 10;
> - return true;
> -
> - } else if (best_ms_target != nullptr) {
> -
> - // attacking with defenders + 6 soldiers
> - int32_t attackers = player_->find_attack_soldiers(best_ms_target->base_flag());
> - const int32_t defenders = best_ms_target->present_soldiers().size();
> - if (attackers > defenders + 10) { // we need to leave meaningful count of soldiers
> - // for next attack
> - attackers = defenders + 6;
> - }
> -
> - game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers);
> - last_attack_target_ = best_ms_target->get_position();
> - taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
> - next_attack_waittime_ = 10;
> - return true;
> + is_attackable = true;
> + }
> + onwer_number = bld->owner().player_number();
> + }
> + }
> + if (upcast(Warehouse, Wh, f.field->get_immovable())) {
> + if (player_->is_hostile(Wh->owner())) {
> + defenders = Wh->present_soldiers().size();
> + flag = &Wh->base_flag();
> + is_warehouse = true;
> + if (Wh->can_attack()) {
> + is_attackable = true;
> + }
> + onwer_number = Wh->owner().player_number();
> + }
> + }
> +
> + // if flag is defined it is a good taget
> + if (flag) {
> + // updating some info
> + // updating info on mines nearby if needed
> + if (site->second.mines_nearby == ExtendedBool::kUnset) {
> + FindNodeMineable find_mines_spots_nearby(game(), f.field->get_resources());
> + const int32_t minescount =
> + map.find_fields(Area<FCoords>(f, 6), nullptr, find_mines_spots_nearby);
> + if (minescount > 0) {
> + site->second.mines_nearby = ExtendedBool::kTrue;
> + } else {
> + site->second.mines_nearby = ExtendedBool::kFalse;
> + }
> + }
> +
> + site->second.warehouse = is_warehouse;
> +
> + // getting rid of default
> + if (site->second.last_time_attackable == std::numeric_limits<uint32_t>::max()) {
> + site->second.last_time_attackable = gametime;
> + }
> +
> + // can we attack:
> + if (is_attackable) {
> + site->second.attack_soldiers = player_->find_attack_soldiers(*flag);
> + } else {
> + site->second.attack_soldiers = 0;
> + }
> +
> + site->second.defenders = defenders;
> +
> + if (site->second.attack_soldiers > 0) {
> + site->second.score = site->second.attack_soldiers - site->second.defenders / 2;
> +
> + if (!is_warehouse)
> + site->second.score -= 1;
> +
> + // here is some differentiation based on "character" of a player
> + if (type_ == NORMAL) {
> + site->second.score -= 1;
> + site->second.score -= vacant_mil_positions_ / 10;
> + } else if (type_ == DEFENSIVE) {
> + site->second.score -= 2;
> + site->second.score -= vacant_mil_positions_ / 5;
> + } else { //=AGRESSIVE
> + site->second.score -= vacant_mil_positions_ / 15;
> + }
> + if (site->second.mines_nearby == ExtendedBool::kFalse) {
> + site->second.score -= 1;
> + }
> + // we dont want to attack multiple players at the same time too eagerly
> + if (onwer_number != last_attacked_player_) {
> + site->second.score -= 3;
> + }
> + // if we dont have mines yet
> + if (mines_.size() <= 2) {
> + site->second.score -= 2;
> + }
> + // also we should have at least some training sites
> + if ((ts_type1_count_ + ts_type2_count_) == 0) {
> + site->second.score -= 2;
> + }
> + // treating no attack score
> + if (site->second.no_attack_counter < 0) {
> + site->second.score = 0;
> + site->second.no_attack_counter += 1;
> + }
> + } else {
> + site->second.score = 0;
> + } // or the score will remain 0
> +
> + if (site->second.score > 0 && player_attackable[onwer_number - 1]) {
> + if (site->second.score > best_score) {
> + best_score = site->second.score;
> + best_target = site->first;
> + }
> + }
> +
> + if (site->second.attack_soldiers > 0) {
> + site->second.last_time_attackable = gametime;
> + }
> + if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) {
> + site_to_be_removed = site->first;
> + }
> + } else { // we dont have a flag, let remove the site from out observer list
> + site_to_be_removed = site->first;
> + }
> +
> + if (site_to_be_removed < std::numeric_limits<uint32_t>::max()) {
> + enemy_sites.erase(site_to_be_removed);
> + continue;
> + }
> + }
> +
> + // modifying enemysites_check_delay_,this depends on the count
> + // of enemysites in observer
> + if (count >= 13 && enemysites_check_delay_ < 180) {
> + enemysites_check_delay_ += 3;
> + }
> + if (count < 10 && enemysites_check_delay_ > 45) {
> + enemysites_check_delay_ -= 2;
> + }
> +
> + // if coordinates hash is not set
> + if (best_target == std::numeric_limits<uint32_t>::max()) {
> + return false;
> + }
> +
> + // attacking
> + FCoords f = map.get_fcoords(coords_unhash(best_target));
> + // setting no attack counter here
> + // this gauranties that it will not be attacked in next 4
> + // turns
> + enemy_sites[best_target].no_attack_counter = -4;
> +
> + Flag* flag = nullptr; // flag of a building to be attacked
> + if (upcast(MilitarySite, bld, f.field->get_immovable())) {
> + flag = &bld->base_flag();
> + } else if (upcast(Warehouse, Wh, f.field->get_immovable())) {
> + flag = &Wh->base_flag();
> } else {
> - taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
> - last_attack_target_.x = std::numeric_limits<uint16_t>::max();
> - last_attack_target_.y = std::numeric_limits<uint16_t>::max();
> + return false; // this should not happen
> + }
> +
> + // how many attack soldiers we can send?
> + uint32_t attackers = player_->find_attack_soldiers(*flag);
> + // Just add some randomness
> + attackers -= gametime % 3;
> + if (attackers <= 0) {
> return false;
> }
> +
> + game().send_player_enemyflagaction(*flag, player_number(), attackers);
> + last_attacked_player_ = flag->owner().player_number();
> +
> + return true;
> }
>
> // This runs once in 15 minutes, and adjust wares targets based on number of
> @@ -4286,7 +4593,7 @@
> // run over dueTasks map and returns task with lower duetime
> DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) {
>
> - uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
> + uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
> ScheduleTasks DueTask = ScheduleTasks::kIdle; // default
> taskDue[ScheduleTasks::kIdle] = gametime;
>
>
> === modified file 'src/ai/defaultai.h'
> --- src/ai/defaultai.h 2015-03-05 20:57:07 +0000
> +++ src/ai/defaultai.h 2015-03-23 21:37:01 +0000
> @@ -9,6 +9,7 @@
> * This program is distributed in the hope that it will be useful,
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> * You should have received a copy of the GNU General Public License
> @@ -82,9 +83,9 @@
> enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
> enum class ScheduleTasks : uint8_t {
> kBbuildableFieldsCheck,
> + kMineableFieldsCheck,
> kRoadCheck,
> kUnbuildableFCheck,
> - kConsiderAttack,
> kCheckEconomies,
> kProductionsitesStats,
> kConstructBuilding,
> @@ -96,7 +97,9 @@
> kPrintStats,
> kIdle,
> kCheckMilitarysites,
> - kCheckTrainingsites
> + kCheckTrainingsites,
> + kCountMilitaryVacant,
> + kCheckEnemySites
> };
> enum class MilitaryStrategy : uint8_t {
> kNoNewMilitary,
> @@ -161,7 +164,6 @@
> int16_t* max_preciousness,
> int16_t* max_needed_preciousness);
>
> -
> ScheduleTasks get_oldest_task(uint32_t);
>
> bool construct_building(uint32_t);
> @@ -195,12 +197,14 @@
> bool check_militarysites(uint32_t);
> bool marine_main_decisions(uint32_t);
> bool check_ships(uint32_t);
> + bool check_enemy_sites(uint32_t);
> void print_stats(uint32_t);
> uint32_t get_stocklevel_by_hint(size_t);
> uint32_t get_stocklevel(BuildingObserver&);
> uint32_t get_warehoused_stock(Widelands::WareIndex wt);
> uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_
> void review_wares_targets(uint32_t);
> + void count_military_vacant_positions();
>
> // sometimes scanning an area in radius gives inappropriate results, so this is to verify that
> // other player is accessible
> @@ -232,7 +236,7 @@
>
> bool check_supply(const BuildingObserver&);
>
> - bool consider_attack(int32_t);
> + // bool consider_attack(int32_t);
>
> void print_land_stats();
>
> @@ -252,6 +256,10 @@
> uint32_t num_prod_constructionsites;
> uint32_t num_ports;
>
> + uint16_t last_attacked_player_;
> + // check ms in this interval - will auto-adjust
> + uint32_t enemysites_check_delay_;
> +
> std::list<Widelands::FCoords> unusable_fields;
> std::list<BuildableField*> buildable_fields;
> std::list<BlockedField> blocked_fields;
> @@ -268,6 +276,9 @@
> std::list<TrainingSiteObserver> trainingsites;
> std::list<ShipObserver> allships;
> std::map<ScheduleTasks, uint32_t> taskDue;
> + std::map<uint32_t, EnemySiteObserver> enemy_sites;
> + // it will map mined material to observer
> + std::map<int32_t, MineTypesObserver> mines_per_type;
>
> std::vector<WareObserver> wares;
>
> @@ -284,10 +295,10 @@
> // when territory is expanded for every candidate field benefits are calculated
> // but need for water, space, mines can vary
> // so if 255 = resource is needed, 0 = not needed
> - uint8_t resource_necessity_territory_;
> - uint8_t resource_necessity_mines_;
> - uint8_t resource_necessity_stones_;
> - uint8_t resource_necessity_water_;
> + int32_t resource_necessity_territory_;
> + int32_t resource_necessity_mines_;
> + int32_t resource_necessity_stones_; // NOCOM
> + int32_t resource_necessity_water_;
> bool resource_necessity_water_needed_; // unless atlanteans
>
> uint16_t unstationed_milit_buildings_; // counts empty military buildings (ones where no soldier
> @@ -295,14 +306,21 @@
> uint16_t military_under_constr_;
> uint16_t military_last_dismantle_;
> uint32_t military_last_build_; // sometimes expansions just stops, this is time of last military
> - // building build
> + // building build
> Widelands::Coords
> - last_attack_target_; // flag to abuilding (position) that was attacked last time
> + last_attack_target_; // flag to abuilding (position) that was attacked last time
> uint32_t next_attack_waittime_; // second till the next attack consideration
> - bool seafaring_economy; // false by default, until first port space is found
> + bool seafaring_economy; // false by default, until first port space is found
> uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory
> // it decreases with failed scans
> int32_t spots_; // sum of buildable fields
> + int32_t vacant_mil_positions_; // sum of vacant positions in militarysites and training sites
> + //statistics for training sites per type
> + uint8_t ts_type1_count_;
> + uint8_t ts_type1_const_count_;
> + uint8_t ts_type2_count_;
> + uint8_t ts_type2_const_count_;
> + uint8_t ts_without_trainers_;
>
> enum {kReprioritize, kStopShipyard, kStapShipyard};
>
>
> === modified file 'tribes/atlanteans/dungeon/conf'
> --- tribes/atlanteans/dungeon/conf 2014-03-17 17:23:26 +0000
> +++ tribes/atlanteans/dungeon/conf 2015-03-23 21:37:01 +0000
> @@ -77,3 +77,6 @@
> [idle]
> pics=dungeon_i_??.png # ???
> hotspot=47 48
> +
> +[aihints]
> +ts_type=2
please use trainingssite_type and a string instead of an integer
>
> === modified file 'tribes/atlanteans/labyrinth/conf'
> --- tribes/atlanteans/labyrinth/conf 2014-10-07 20:06:46 +0000
> +++ tribes/atlanteans/labyrinth/conf 2015-03-23 21:37:01 +0000
> @@ -99,4 +99,4 @@
> hotspot=80 88
>
> [aihints]
> -prohibited_till=2700
> +ts_type=1
>
> === modified file 'tribes/barbarians/battlearena/conf'
> --- tribes/barbarians/battlearena/conf 2014-07-29 09:27:08 +0000
> +++ tribes/barbarians/battlearena/conf 2015-03-23 21:37:01 +0000
> @@ -74,3 +74,6 @@
> pics=battlearena_w_??.png # ???
> hotspot=110 72
> fps=10
> +
> +[aihints]
> +ts_type=1
>
> === modified file 'tribes/barbarians/trainingcamp/conf'
> --- tribes/barbarians/trainingcamp/conf 2014-09-24 21:06:00 +0000
> +++ tribes/barbarians/trainingcamp/conf 2015-03-23 21:37:01 +0000
> @@ -36,7 +36,7 @@
> max_level=4
>
> [aihints]
> -prohibited_till=2700
> +ts_type=2
>
> [soldier hp]
> min_level=0
>
> === modified file 'tribes/empire/arena/conf'
> --- tribes/empire/arena/conf 2014-09-30 08:05:35 +0000
> +++ tribes/empire/arena/conf 2015-03-23 21:37:01 +0000
> @@ -54,3 +54,6 @@
> [build]
> pics=arena_b_??.png # ???
> hotspot=82 83
> +
> +[aihints]
> +ts_type=1
>
> === modified file 'tribes/empire/colosseum/conf'
> --- tribes/empire/colosseum/conf 2014-09-30 08:05:35 +0000
> +++ tribes/empire/colosseum/conf 2015-03-23 21:37:01 +0000
> @@ -60,3 +60,6 @@
> [idle]
> pics=colosseum_i_??.png # ???
> hotspot=81 106
> +
> +[aihints]
> +ts_type=1
>
> === modified file 'tribes/empire/piggery/conf'
> --- tribes/empire/piggery/conf 2014-08-02 19:55:23 +0000
> +++ tribes/empire/piggery/conf 2015-03-23 21:37:01 +0000
> @@ -2,6 +2,7 @@
> output=meat
>
> [aihints]
> +forced_after=9000
>
> [buildcost]
> log=2
>
> === modified file 'tribes/empire/trainingcamp/conf'
> --- tribes/empire/trainingcamp/conf 2014-08-02 19:55:23 +0000
> +++ tribes/empire/trainingcamp/conf 2015-03-23 21:37:01 +0000
> @@ -127,4 +127,4 @@
> hotspot=82 105
>
> [aihints]
> -prohibited_till=2700
> +ts_type=2
>
--
https://code.launchpad.net/~widelands-dev/widelands/ai-military-changes/+merge/253881
Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/ai-military-changes.
References