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. _______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp