TiborB has proposed merging lp:~widelands-dev/widelands/ai-military-changes into lp:widelands.
Requested reviews: Widelands Developers (widelands-dev) For more details, see: https://code.launchpad.net/~widelands-dev/widelands/ai-military-changes/+merge/253881 Another bunch of AI changes, this time only files within src/ai/ were changed. Reworked were: - attacking - training sites - mines - further small things -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai-military-changes into lp:widelands.
=== 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) { 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 === 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
_______________________________________________ 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