TiborB has proposed merging lp:~widelands-dev/widelands/ai_portspaces into lp:widelands.
Requested reviews: Widelands Developers (widelands-dev) For more details, see: https://code.launchpad.net/~widelands-dev/widelands/ai_portspaces/+merge/335785 AI now scans entire map for portspaces. And multiple changes to the logic, including logic of placing militarysites - they now know there is (unowned) vicinity of port spaces (or portspaces themselves) -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai_portspaces into lp:widelands.
=== modified file 'src/ai/ai_help_structs.cc' --- src/ai/ai_help_structs.cc 2017-11-24 09:19:52 +0000 +++ src/ai/ai_help_structs.cc 2018-01-05 22:15:52 +0000 @@ -236,6 +236,7 @@ unowned_land_nearby(0), enemy_owned_land_nearby(0U), unowned_buildable_spots_nearby(0U), + unowned_portspace_vicinity_nearby(0U), nearest_buildable_spot_nearby(0U), near_border(false), unowned_mines_spots_nearby(0), === modified file 'src/ai/ai_help_structs.h' --- src/ai/ai_help_structs.h 2017-11-17 07:16:12 +0000 +++ src/ai/ai_help_structs.h 2018-01-05 22:15:52 +0000 @@ -316,6 +316,7 @@ uint16_t unowned_land_nearby; uint16_t enemy_owned_land_nearby; uint16_t unowned_buildable_spots_nearby; + uint16_t unowned_portspace_vicinity_nearby; uint16_t nearest_buildable_spot_nearby; // to identify that field is too close to border and no production building should be built there bool near_border; @@ -356,7 +357,7 @@ Widelands::ExtendedBool is_portspace; bool port_nearby; // to increase priority if a port is nearby, // especially for new colonies - Widelands::ExtendedBool portspace_nearby; // prefer military buildings closer to the portspace + Widelands::ExtendedBool portspace_nearby; // special fields intended for ports int32_t max_buildcap_nearby; // It is not necessary to check resources (stones, fish...) too frequently as they do not change // fast. This stores the time of the last check. === modified file 'src/ai/defaultai.cc' --- src/ai/defaultai.cc 2017-12-10 21:19:02 +0000 +++ src/ai/defaultai.cc 2018-01-05 22:15:52 +0000 @@ -123,7 +123,7 @@ if (note.ownership == NoteFieldPossession::Ownership::GAINED) { unusable_fields.push_back(note.fc); } - }); + }); // Subscribe to NoteImmovables. immovable_subscriber_ = @@ -139,7 +139,7 @@ } else { lose_immovable(*note.pi); } - }); + }); // Subscribe to ProductionSiteOutOfResources. outofresource_subscriber_ = Notifications::subscribe<NoteProductionSiteOutOfResources>( @@ -150,7 +150,7 @@ out_of_resources_site(*note.ps); - }); + }); // Subscribe to TrainingSiteSoldierTrained. soldiertrained_subscriber_ = Notifications::subscribe<NoteTrainingSiteSoldierTrained>( @@ -161,46 +161,48 @@ soldier_trained(*note.ts); - }); + }); // Subscribe to ShipNotes. - shipnotes_subscriber_ = Notifications::subscribe<NoteShipMessage>([this]( - const NoteShipMessage& note) { - - // in a short time between start and late_initialization the player - // can get notes that can not be processed. - // It seems that this causes no problem, at least no substantial - if (player_ == nullptr) { - return; - } - if (note.ship->get_owner()->player_number() != player_->player_number()) { - return; - } - - switch (note.message) { - - case NoteShipMessage::Message::kGained: - gain_ship(*note.ship, NewShip::kBuilt); - break; - - case NoteShipMessage::Message::kLost: - for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { - if (i->ship == note.ship) { - allships.erase(i); - break; - } - } - break; - - case NoteShipMessage::Message::kWaitingForCommand: - for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { - if (i->ship == note.ship) { - i->waiting_for_command_ = true; - break; - } - } - } - }); + shipnotes_subscriber_ = + Notifications::subscribe<NoteShipMessage>([this](const NoteShipMessage& note) { + + // in a short time between start and late_initialization the player + // can get notes that can not be processed. + // It seems that this causes no problem, at least no substantial + if (player_ == nullptr) { + return; + } + if (note.ship->get_owner()->player_number() != player_->player_number()) { + return; + } + + switch (note.message) { + + case NoteShipMessage::Message::kGained: + gain_ship(*note.ship, NewShip::kBuilt); + break; + + case NoteShipMessage::Message::kLost: + for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); + ++i) { + if (i->ship == note.ship) { + allships.erase(i); + break; + } + } + break; + + case NoteShipMessage::Message::kWaitingForCommand: + for (std::deque<ShipObserver>::iterator i = allships.begin(); i != allships.end(); + ++i) { + if (i->ship == note.ship) { + i->waiting_for_command_ = true; + break; + } + } + } + }); } DefaultAI::~DefaultAI() { @@ -923,26 +925,6 @@ const Map& map = game().map(); - // here we generate list of all ports and their vicinity from entire map - for (const Coords& c : map.get_port_spaces()) { - MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(c), 3)); - do { - const int32_t hash = map.get_fcoords(*(mr.location().field)).hash(); - if (port_reserved_coords.count(hash) == 0) - port_reserved_coords.insert(hash); - } while (mr.advance(map)); - - // the same for NW neighbour of a field - Coords c_nw; - map.get_tln(c, &c_nw); - MapRegion<Area<FCoords>> mr_nw(map, Area<FCoords>(map.get_fcoords(c_nw), 3)); - do { - const int32_t hash = map.get_fcoords(*(mr_nw.location().field)).hash(); - if (port_reserved_coords.count(hash) == 0) - port_reserved_coords.insert(hash); - } while (mr_nw.advance(map)); - } - // here we scan entire map for own ships std::set<OPtr<Ship>> found_ships; for (int16_t y = 0; y < map.get_height(); ++y) { @@ -990,13 +972,26 @@ if (ps_obs.bo->is(BuildingAttribute::kSpaceConsumer) && !ps_obs.bo->is(BuildingAttribute::kRanger)) { MapRegion<Area<FCoords>> mr( - map, Area<FCoords>(map.get_fcoords(ps_obs.site->get_position()), 4)); + map, Area<FCoords>(map.get_fcoords(ps_obs.site->get_position()), 2)); do { blocked_fields.add(mr.location(), game().get_gametime() + 20 * 60 * 1000); } while (mr.advance(map)); } } + // getting list of all fields nearby port space + // TODO it seems port spaces can change over time so ports_vicinity needs to be refreshed from + // time to time + for (const Coords& c : map.get_port_spaces()) { + MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(c), 3)); + do { + const uint32_t hash = mr.location().hash(); + if (!ports_vicinity.count(hash)) { + ports_vicinity.insert(hash); + } + } while (mr.advance(map)); + } + // printing identified basic buildings if we are in the basic economy mode basic_economy_established = persistent_data->remaining_basic_buildings.empty(); if (!basic_economy_established) { @@ -1214,9 +1209,12 @@ Area<FCoords>(field.coords, actual_enemy_check_area), nullptr, find_enemy_owned_walkable); field.nearest_buildable_spot_nearby = std::numeric_limits<uint16_t>::max(); + field.unowned_buildable_spots_nearby = 0; + field.unowned_portspace_vicinity_nearby = 0; if (field.unowned_land_nearby > 0) { std::vector<Coords> found_buildable_fields; + // first looking for unowned buildable spots field.unowned_buildable_spots_nearby = map.find_fields(Area<FCoords>(field.coords, kBuildableSpotsCheckArea), &found_buildable_fields, find_unowned_buildable); @@ -1227,8 +1225,16 @@ field.nearest_buildable_spot_nearby = cur_distance; } } - } else { - field.unowned_buildable_spots_nearby = 0; + + // now looking for unowned_portspace_vicinity_nearby + MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, kBuildableSpotsCheckArea)); + do { + + if (mr.location().field->get_owned_by() == 0 && + ports_vicinity.count(mr.location().hash()) > 0) { + field.unowned_portspace_vicinity_nearby += 1; + } + } while (mr.advance(map)); } // Is this near the border? Get rid of fields owned by ally @@ -1276,25 +1282,17 @@ } // identifying portspace fields - if (field.is_portspace == - Widelands::ExtendedBool::kUnset) { // if we know it, no need to do it once more - if (player_->get_buildcaps(field.coords) & BUILDCAPS_PORT) { - field.is_portspace = ExtendedBool::kTrue; - } else { - field.is_portspace = ExtendedBool::kFalse; - } + if (player_->get_buildcaps(field.coords) & BUILDCAPS_PORT) { + field.is_portspace = ExtendedBool::kTrue; + } else { + field.is_portspace = ExtendedBool::kFalse; } // testing for near portspaces - if (field.portspace_nearby == Widelands::ExtendedBool::kUnset) { + if (ports_vicinity.count(field.coords.hash()) > 0) { + field.portspace_nearby = ExtendedBool::kTrue; + } else { field.portspace_nearby = ExtendedBool::kFalse; - MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 4)); - do { - if (port_reserved_coords.count(mr.location().hash()) > 0) { - field.portspace_nearby = ExtendedBool::kTrue; - break; - } - } while (mr.advance(map)); } // testing if a port is nearby, such field will get a priority boost @@ -1613,29 +1611,23 @@ const uint8_t score_parts_size = 60; int32_t score_parts[score_parts_size] = {0}; if (field.enemy_owned_land_nearby) { - score_parts[0] = 3 * - management_data.neuron_pool[73].get_result_safe( - field.enemy_owned_land_nearby / 5, kAbsValue); - score_parts[1] = - 3 * - management_data.neuron_pool[76].get_result_safe(field.enemy_owned_land_nearby, kAbsValue); - score_parts[2] = 3 * - management_data.neuron_pool[54].get_result_safe( - field.enemy_military_presence * 2, kAbsValue); - score_parts[3] = 3 * - management_data.neuron_pool[61].get_result_safe( - field.enemy_military_presence / 3, kAbsValue); + score_parts[0] = 3 * management_data.neuron_pool[73].get_result_safe( + field.enemy_owned_land_nearby / 5, kAbsValue); + score_parts[1] = 3 * management_data.neuron_pool[76].get_result_safe( + field.enemy_owned_land_nearby, kAbsValue); + score_parts[2] = 3 * management_data.neuron_pool[54].get_result_safe( + field.enemy_military_presence * 2, kAbsValue); + score_parts[3] = 3 * management_data.neuron_pool[61].get_result_safe( + field.enemy_military_presence / 3, kAbsValue); score_parts[4] = (!field.enemy_accessible_) ? (-100 + management_data.get_military_number_at(55)) : 0; - score_parts[5] = - 2 * - management_data.neuron_pool[50].get_result_safe(field.enemy_owned_land_nearby, kAbsValue); + score_parts[5] = 2 * management_data.neuron_pool[50].get_result_safe( + field.enemy_owned_land_nearby, kAbsValue); score_parts[6] = field.enemy_military_sites * std::abs(management_data.get_military_number_at(67) / 2); - score_parts[7] = - 2 * - management_data.neuron_pool[34].get_result_safe(field.enemy_military_sites * 2, kAbsValue); + score_parts[7] = 2 * management_data.neuron_pool[34].get_result_safe( + field.enemy_military_sites * 2, kAbsValue); score_parts[8] = management_data.neuron_pool[56].get_result_safe( field.enemy_military_presence * 2, kAbsValue); @@ -1644,13 +1636,11 @@ score_parts[10] = (field.enemy_accessible_) ? management_data.get_military_number_at(80) : 0; score_parts[11] = - -3 * - management_data.neuron_pool[8].get_result_safe( - (field.military_in_constr_nearby + field.military_unstationed) * 3, kAbsValue); + -3 * management_data.neuron_pool[8].get_result_safe( + (field.military_in_constr_nearby + field.military_unstationed) * 3, kAbsValue); score_parts[12] = - -3 * - management_data.neuron_pool[74].get_result_safe( - (field.military_in_constr_nearby + field.military_unstationed) * 5, kAbsValue); + -3 * management_data.neuron_pool[74].get_result_safe( + (field.military_in_constr_nearby + field.military_unstationed) * 5, kAbsValue); score_parts[13] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ? -std::abs(management_data.get_military_number_at(32)) : 0; @@ -1677,9 +1667,8 @@ std::abs(management_data.get_military_number_at(58)) : 0; if (expansion_type.get_expansion_type() == ExpansionMode::kResources) { - score_parts[23] = 2 * - management_data.neuron_pool[78].get_result_safe( - (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue); + score_parts[23] = 2 * management_data.neuron_pool[78].get_result_safe( + (field.unowned_mines_spots_nearby + 2) / 3, kAbsValue); } score_parts[24] = @@ -1708,22 +1697,19 @@ management_data.neuron_pool[10].get_result_safe(field.military_loneliness / 50, kAbsValue); score_parts[30] = - -10 * - management_data.neuron_pool[8].get_result_safe( - 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue); + -10 * management_data.neuron_pool[8].get_result_safe( + 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue); score_parts[31] = - -10 * - management_data.neuron_pool[31].get_result_safe( - 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue); + -10 * management_data.neuron_pool[31].get_result_safe( + 3 * (field.military_in_constr_nearby + field.military_unstationed), kAbsValue); score_parts[32] = -4 * field.military_in_constr_nearby * std::abs(management_data.get_military_number_at(82)); score_parts[33] = (field.military_in_constr_nearby > 0) ? -5 * management_data.get_military_number_at(85) : 0; - score_parts[34] = -1 * - management_data.neuron_pool[4].get_result_safe( - (field.area_military_capacity + 4) / 5, kAbsValue); + score_parts[34] = -1 * management_data.neuron_pool[4].get_result_safe( + (field.area_military_capacity + 4) / 5, kAbsValue); score_parts[35] = 3 * management_data.get_military_number_at(133); if (expansion_type.get_expansion_type() == ExpansionMode::kEconomy) { @@ -1750,14 +1736,12 @@ score_parts[41] = 3 * std::abs(management_data.get_military_number_at(93)); } - score_parts[42] = - (field.unowned_land_nearby) ? - management_data.neuron_pool[18].get_result_safe(field.own_non_military_nearby, kAbsValue) : - 0; + score_parts[42] = (field.unowned_land_nearby) ? management_data.neuron_pool[18].get_result_safe( + field.own_non_military_nearby, kAbsValue) : + 0; - score_parts[43] = 2 * - management_data.neuron_pool[11].get_result_safe( - field.unowned_buildable_spots_nearby, kAbsValue); + score_parts[43] = 2 * management_data.neuron_pool[11].get_result_safe( + field.unowned_buildable_spots_nearby, kAbsValue); score_parts[44] = management_data.neuron_pool[12].get_result_safe(field.unowned_mines_spots_nearby, kAbsValue); score_parts[45] = @@ -1767,12 +1751,10 @@ score_parts[46] = -1 * management_data.neuron_pool[55].get_result_safe(field.ally_military_presence, kAbsValue); - score_parts[47] = - -1 * - management_data.neuron_pool[53].get_result_safe(2 * field.ally_military_presence, kAbsValue); - score_parts[48] = -2 * - management_data.neuron_pool[4].get_result_safe( - (field.area_military_capacity + 4) / 5, kAbsValue); + score_parts[47] = -1 * management_data.neuron_pool[53].get_result_safe( + 2 * field.ally_military_presence, kAbsValue); + score_parts[48] = -2 * management_data.neuron_pool[4].get_result_safe( + (field.area_military_capacity + 4) / 5, kAbsValue); score_parts[49] = ((field.military_in_constr_nearby + field.military_unstationed) > 0) ? -std::abs(management_data.get_military_number_at(81)) : 0; @@ -1781,6 +1763,13 @@ 0; score_parts[56] = (any_unconnected_imm) ? 2 * std::abs(management_data.get_military_number_at(23)) : 0; + score_parts[57] = 1 * management_data.neuron_pool[18].get_result_safe( + 2 * field.unowned_portspace_vicinity_nearby, kAbsValue); + score_parts[58] = 3 * management_data.neuron_pool[19].get_result_safe( + 5 * field.unowned_portspace_vicinity_nearby, kAbsValue); + score_parts[59] = (field.unowned_portspace_vicinity_nearby) ? + 10 * std::abs(management_data.get_military_number_at(31)) : + 0; for (uint16_t i = 0; i < score_parts_size; i++) { field.military_score_ += score_parts[i]; @@ -2425,13 +2414,6 @@ continue; } - // testing for reserved ports - if (!bo.is(BuildingAttribute::kPort)) { - if (port_reserved_coords.count(bf->coords.hash()) > 0) { - continue; - } - } - if (std::rand() % 3 == 0 && bo.total_count() > 0) { continue; } // add randomnes and ease AI @@ -2454,10 +2436,21 @@ int32_t prio = 0; // score of a bulding on a field + // testing for reserved ports + if (!bo.is(BuildingAttribute::kPort)) { + if (bf->portspace_nearby == ExtendedBool::kTrue) { + if (num_ports == 0) { + continue; + } + // If we have at least on port, we can perhaps build here something + // but decreasing the score to discourage it + prio -= 5 * std::abs(management_data.get_military_number_at(52)); + } + } + if (bo.type == BuildingObserver::Type::kProductionsite) { - prio = management_data.neuron_pool[44].get_result_safe(bf->military_score_ / 20) / 5; - assert(prio >= -20 && prio <= 20); + prio += management_data.neuron_pool[44].get_result_safe(bf->military_score_ / 20) / 5; // this can be only a well (as by now) if (bo.is(BuildingAttribute::kWell)) { @@ -2470,7 +2463,7 @@ continue; } - prio = bo.primary_priority; + prio += bo.primary_priority; // keep wells more distant if (bf->producers_nearby.at(bo.outputs.at(0)) > 2) { @@ -2487,7 +2480,7 @@ } else if (bo.is(BuildingAttribute::kLumberjack)) { - prio = bo.primary_priority; + prio += bo.primary_priority; if (bo.new_building == BuildingNecessity::kForced) { prio += 5 * std::abs(management_data.get_military_number_at(17)); @@ -2512,7 +2505,7 @@ // Quarries are generally to be built everywhere where rocks are // no matter the need for granite, as rocks are considered an obstacle // to expansion - prio = 2 * bf->rocks_nearby; + prio += 2 * bf->rocks_nearby; if (bf->rocks_nearby > 0 && bf->near_border) { prio += management_data.get_military_number_at(27) / 2; @@ -2569,9 +2562,8 @@ prio -= bf->producers_nearby.at(bo.outputs.at(0)) * 20; prio += bf->supporters_nearby.at(bo.outputs.at(0)) * 20; - prio += - -5 + - bf->fish_nearby * (1 + std::abs(management_data.get_military_number_at(63) / 15)); + prio += -5 + bf->fish_nearby * + (1 + std::abs(management_data.get_military_number_at(63) / 15)); if (resource_necessity_water_needed_) { prio *= 3; } @@ -2587,8 +2579,6 @@ assert(bo.new_building == BuildingNecessity::kNeeded); - prio = 0; - if (bo.total_count() == 0) { prio += 200; } else { @@ -2635,9 +2625,8 @@ } if (current_stocklevel < 40) { - prio += 5 * - management_data.neuron_pool[23].get_result_safe( - (40 - current_stocklevel) / 2, kAbsValue); + prio += 5 * management_data.neuron_pool[23].get_result_safe( + (40 - current_stocklevel) / 2, kAbsValue); } for (auto ph : bo.production_hints) { @@ -2648,10 +2637,14 @@ if (bf->enemy_nearby) { prio -= 20; } + + if (bf->unowned_portspace_vicinity_nearby > 0) { + prio -= 500; + } } } else if (bo.is(BuildingAttribute::kRecruitment)) { - prio = bo.primary_priority; + prio += bo.primary_priority; prio -= bf->unowned_land_nearby * 2; prio -= (bf->enemy_nearby) * 100; prio -= (expansion_type.get_expansion_type() != ExpansionMode::kEconomy) * 100; @@ -2684,9 +2677,8 @@ // we attempt to cluster space consumers together prio += bf->space_consumers_nearby * 2; // and be far from rangers - prio += 1 - - bf->rangers_nearby * - std::abs(management_data.get_military_number_at(102)) / 5; + prio += 1 - bf->rangers_nearby * + std::abs(management_data.get_military_number_at(102)) / 5; } else { // leave some free space between them prio -= bf->producers_nearby.at(bo.outputs.at(0)) * @@ -2756,7 +2748,7 @@ // Consider border with exemption of some huts if (!(bo.is(BuildingAttribute::kLumberjack) || bo.is(BuildingAttribute::kNeedsCoast) || bo.is(BuildingAttribute::kFisher))) { - prio = recalc_with_border_range(*bf, prio); + prio += recalc_with_border_range(*bf, prio); } else if (bf->near_border && (bo.is(BuildingAttribute::kLumberjack) || bo.is(BuildingAttribute::kNeedsCoast))) { prio /= 2; @@ -2765,12 +2757,12 @@ } // production sites done else if (bo.type == BuildingObserver::Type::kMilitarysite) { - assert(prio == 0); - prio = bo.primary_priority; + prio += bo.primary_priority; // Two possibilities why to construct militarysite here if (!bf->defense_msite_allowed && - bf->nearest_buildable_spot_nearby < bo.desc->get_conquers() && + (bf->nearest_buildable_spot_nearby < bo.desc->get_conquers() || + bf->unowned_portspace_vicinity_nearby > 0) && (bf->military_in_constr_nearby + bf->military_unstationed) < concurent_ms_in_constr_no_enemy) { // it will conquer new buildable spots for buildings or mines @@ -2811,7 +2803,7 @@ if (bf->is_portspace != ExtendedBool::kTrue && bo.is(BuildingAttribute::kPort)) { continue; } - prio = bo.primary_priority; + prio += bo.primary_priority; // iterating over current warehouses and testing a distance // getting distance to nearest warehouse and adding it to a score @@ -2849,7 +2841,7 @@ // Even if a site is forced it has kNeeded necessity now assert(bo.primary_priority > 0 && bo.new_building == BuildingNecessity::kNeeded); - prio = bo.primary_priority; + prio += bo.primary_priority; // for spots close to a border if (bf->near_border) { @@ -2881,13 +2873,6 @@ continue; } - // testing also vicinity - if (!bo.is(BuildingAttribute::kPort)) { - if (port_reserved_coords.count(bf->coords.hash()) > 0) { - continue; - } - } - // Prefer road side fields prio += bf->preferred ? 5 : 0; @@ -4244,28 +4229,51 @@ const uint32_t gametime) { bo.primary_priority = 0; - if (numof_warehouses_in_const_ > 0 || - bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed || !basic_economy_established) { - bo.new_building_overdue = 0; - bo.primary_priority = 0; - return BuildingNecessity::kForbidden; - } - + // First there are situation when we cannot built the warehouse/port + // a) This is port and map is not seafaring one if (bo.is(BuildingAttribute::kPort) && !map_allows_seafaring_) { bo.new_building_overdue = 0; - bo.primary_priority = 0; - return BuildingNecessity::kForbidden; - } - - bo.primary_priority = 0; - - // Build one warehouse for ~every 35 productionsites and mines_. - // Militarysites are slightly important as well, to have a bigger - // chance for a warehouses (containing waiting soldiers or wares - // needed for soldier training) near the frontier. - int32_t needed_count = static_cast<int32_t>(productionsites.size() + mines_.size()) / - (40 + management_data.get_military_number_at(21) / 10) + - 1; + return BuildingNecessity::kForbidden; + } + + // b) the site is prohibited + if (bo.prohibited_till > gametime) { + bo.new_building_overdue = 0; + return BuildingNecessity::kForbidden; + } + + // If this is a port and is the first bo be built + const bool first_port_allowed = (bo.is(BuildingAttribute::kPort) && bo.total_count() == 0); + + // c) there are warehouses in construction (first port is exemption) + if (numof_warehouses_in_const_ > 0 && !first_port_allowed) { + bo.new_building_overdue = 0; + return BuildingNecessity::kForbidden; + } + + // d) there is ai limit for this bulding + if (bo.aimode_limit_status() != AiModeBuildings::kAnotherAllowed) { + bo.new_building_overdue = 0; + return BuildingNecessity::kForbidden; + } + + // e) basic economy not established, but first port is an exemption + if (!basic_economy_established && !first_port_allowed) { + bo.new_building_overdue = 0; + return BuildingNecessity::kForbidden; + } + + // Number of needed warehouses decides if new one is needed and also + // converts to the score + int32_t needed_count = 0; + if (first_port_allowed) { + needed_count += numof_warehouses_ + numof_warehouses_in_const_ + 1; + } else { + needed_count += static_cast<int32_t>(productionsites.size() + mines_.size()) / + (40 + management_data.get_military_number_at(21) / 10) + + 1; + } + assert(needed_count >= 0 && needed_count <= (static_cast<uint16_t>(productionsites.size() + mines_.size()) / 10) + 2); @@ -4275,22 +4283,27 @@ ++needed_count; } - if (bo.is(BuildingAttribute::kPort) && (productionsites.size() + mines_.size()) > 10) { + // Port should always have higher score than a warehouse + if (bo.is(BuildingAttribute::kPort)) { ++needed_count; } + // suppres a warehouse if not enough spots + if (spots_ < kSpotsEnough && !bo.is(BuildingAttribute::kPort)) { + --needed_count; + } + if (needed_count <= numof_warehouses_in_const_ + numof_warehouses_) { bo.new_building_overdue = 0; return BuildingNecessity::kForbidden; } // So now we know the warehouse here is needed. - bo.primary_priority = 1 + - (needed_count - numof_warehouses_in_const_ - numof_warehouses_) * - std::abs(management_data.get_military_number_at(22)); + bo.primary_priority = 1 + (needed_count - numof_warehouses_) * + std::abs(management_data.get_military_number_at(22) * 5); ++bo.new_building_overdue; bo.primary_priority += - bo.new_building_overdue * std::abs(management_data.get_military_number_at(16)) / 40; + bo.new_building_overdue * std::abs(management_data.get_military_number_at(16)) / 10; return BuildingNecessity::kAllowed; } @@ -4371,9 +4384,8 @@ // We build one trainig site per X military sites // with some variations, of course - int32_t target = 1 + - static_cast<int32_t>(militarysites.size() + productionsites.size()) / - (std::abs(management_data.get_military_number_at(113) / 2) + 1); + int32_t target = 1 + static_cast<int32_t>(militarysites.size() + productionsites.size()) / + (std::abs(management_data.get_military_number_at(113) / 2) + 1); assert(target > 0 && target < 500); uint16_t current_proportion = 0; @@ -5390,6 +5402,7 @@ } // counts buildings with the BuildingAttribute +// Type of buildings, not individual buildings are meant uint8_t DefaultAI::count_buildings_with_attribute(BuildingAttribute attribute) { uint8_t count = 0; if (tribe_ == nullptr) { @@ -5750,15 +5763,6 @@ warehousesites.back().bo = &bo; if (bo.is(BuildingAttribute::kPort)) { ++num_ports; - // unblock nearby fields, might be used for other buildings... - const Map& map = game().map(); - MapRegion<Area<FCoords>> mr( - map, Area<FCoords>(map.get_fcoords(warehousesites.back().site->get_position()), 3)); - do { - const int32_t hash = map.get_fcoords(*(mr.location().field)).hash(); - if (port_reserved_coords.count(hash) > 0) - port_reserved_coords.erase(hash); - } while (mr.advance(map)); } } } === modified file 'src/ai/defaultai.h' --- src/ai/defaultai.h 2017-12-10 21:22:43 +0000 +++ src/ai/defaultai.h 2018-01-05 22:15:52 +0000 @@ -39,7 +39,7 @@ namespace Widelands { struct Road; struct Flag; -} +} // namespace Widelands /** * Default Widelands Computer Player (defaultAI) @@ -305,10 +305,10 @@ std::deque<Widelands::FCoords> unusable_fields; std::deque<Widelands::BuildableField*> buildable_fields; Widelands::BlockedFields blocked_fields; + std::unordered_set<uint32_t> ports_vicinity; Widelands::PlayersStrengths player_statistics; Widelands::ManagementData management_data; Widelands::ExpansionType expansion_type; - std::unordered_set<uint32_t> port_reserved_coords; std::deque<Widelands::MineableField*> mineable_fields; std::deque<Widelands::Flag const*> new_flags; std::deque<Widelands::Road const*> roads;
_______________________________________________ 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