I have done a preliminary code review - everything just small nits. Can you clean up the NOCOMs too and set a commit message?
Diff comments: > > === modified file 'src/ai/ai_help_structs.cc' > --- src/ai/ai_help_structs.cc 2019-04-09 16:43:49 +0000 > +++ src/ai/ai_help_structs.cc 2019-06-14 18:11:44 +0000 > @@ -1417,4 +1303,190 @@ > return update_time; > } > > +FlagWarehouseDistances::FlagInfo::FlagInfo(const uint32_t gametime, > + const uint16_t dist, > + const uint32_t wh) { > + expiry_time = gametime + kFlagDistanceExpirationPeriod; > + soft_expiry_time = gametime + kFlagDistanceExpirationPeriod / 2; > + distance = dist; > + nearest_warehouse = wh; > + new_road_prohibited_till = 0; > +} > +FlagWarehouseDistances::FlagInfo::FlagInfo() { > + expiry_time = 0; > + distance = 1000; 1000 appears multiple times - please make it a constexpr > + new_road_prohibited_till = 0; > +} > + > +// We are updating the distance info, but not all the time. > +// Always if after soft expiration period, but > +// if below expiration period only when the new value is lower than current > on > +// In both cases new expiration times are calculated > +bool FlagWarehouseDistances::FlagInfo::update(const uint32_t gametime, > + const uint16_t new_distance, > + const uint32_t nearest_wh) { > + const uint32_t new_expiry_time = gametime + > kFlagDistanceExpirationPeriod; > + > + if (gametime > soft_expiry_time) { > + distance = new_distance; > + expiry_time = new_expiry_time; > + soft_expiry_time = gametime + kFlagDistanceExpirationPeriod / 2; > + nearest_warehouse = nearest_wh; > + return true; > + } else if (new_distance < distance || (new_distance == distance && > + expiry_time < gametime + > kFlagDistanceExpirationPeriod)) { > + distance = new_distance; > + expiry_time = gametime + kFlagDistanceExpirationPeriod; > + nearest_warehouse = nearest_wh; > + return true; > + } > + return false; > +} > + > +uint16_t FlagWarehouseDistances::FlagInfo::get(const uint32_t gametime, > uint32_t* nw) const { > + *nw = nearest_warehouse; > + if (gametime <= expiry_time) { > + return distance; > + } > + return 1000; > +} > + > +void FlagWarehouseDistances::FlagInfo::road_built(const uint32_t gametime) { > + new_road_prohibited_till = gametime + 60 * 1000; > +} > + > +bool FlagWarehouseDistances::FlagInfo::road_prohibited(const uint32_t > gametime) const { > + return new_road_prohibited_till > gametime; > +} > + > +bool FlagWarehouseDistances::set_distance(const uint32_t flag_coords, > + const uint16_t distance, > + uint32_t const gametime, > + uint32_t const nearest_warehouse) { > + if (flags_map.count(flag_coords) == 0) { > + flags_map[flag_coords] = > + FlagWarehouseDistances::FlagInfo(gametime, distance, > nearest_warehouse); > + return true; > + } > + return flags_map[flag_coords].update(gametime, distance, > nearest_warehouse); > +} > + > +uint16_t FlagWarehouseDistances::count() const { > + return flags_map.size(); > +} > + > +int16_t > +FlagWarehouseDistances::get_distance(const uint32_t flag_coords, uint32_t > gametime, uint32_t* nw) { > + if (flags_map.count(flag_coords) == 0) { > + *nw = 0; > + return 1000; // this is to discourage to build second road > from brand new flag... > + } else { > + return flags_map[flag_coords].get(gametime, nw); > + } > +} > + > +void FlagWarehouseDistances::set_road_built(const uint32_t coords_hash, > const uint32_t gametime) { > + if (flags_map.count(coords_hash) == 1) { > + flags_map[coords_hash].road_built(gametime); > + } > +} > + > +bool FlagWarehouseDistances::get_road_prohibited(const uint32_t coords_hash, > + const uint32_t gametime) { > + if (flags_map.count(coords_hash) == 1) { > + return flags_map[coords_hash].road_prohibited(gametime); > + } > + return false; > +} > + > +bool FlagWarehouseDistances::remove_old_flag(uint32_t gametime) { > + for (std::map<uint32_t, FlagWarehouseDistances::FlagInfo>::iterator it > = flags_map.begin(); > + it != flags_map.end(); it++) { > + if (it->second.expiry_time + kOldFlagRemoveTime < gametime) { > + it = flags_map.erase(it); > + return true; > + } > + } > + return false; > +} > + > +// Returns pointer to winning road, providing the treshold is exceeded providing > provided that > +FlagCandidates::Candidate* FlagCandidates::get_winner(const int16_t > treshold) { > + if (flags_.empty()) { > + return nullptr; > + } > + sort(); > + if (flags_[0].score() < treshold) { > + return nullptr; > + } > + if (!flags_[0].is_buildable()) { > + return nullptr; > + } > + return &flags_[0]; > +} > + > +FlagCandidates::Candidate::Candidate( > + const uint32_t ch, bool de, uint16_t start_f_dist, uint16_t > act_dist_to_wh, uint16_t air_dst) { > + coords_hash = ch; > + different_economy = de; > + start_flag_dist_to_wh = start_f_dist; > + flag_to_flag_road_distance = 0; > + possible_road_distance = 1000; > + cand_flag_distance_to_wh = act_dist_to_wh; > + air_distance = air_dst; > +} > + > +int16_t FlagCandidates::Candidate::score() const { // NOCOM > + return different_economy * 2000 + (start_flag_dist_to_wh - > cand_flag_distance_to_wh) + > + (flag_to_flag_road_distance - 2 * possible_road_distance); > +} > + > +bool FlagCandidates::set_road_possible(const uint32_t coords_hash, const > uint16_t steps) { > + for (auto& item : flags_) { > + if (item.coords_hash == coords_hash) { > + item.possible_road_distance = steps; > + return true; > + } > + } > + printf("ERROR [set_road_possible] - no such flag\n"); // NOCOM > + return false; > +} > + > +bool FlagCandidates::set_cur_road_distance(const uint32_t coords, uint16_t > dist) { > + for (auto& item : flags_) { > + if (item.coords_hash == coords) { > + item.flag_to_flag_road_distance = dist; > + return true; > + } > + } > + return false; > +} > +void FlagCandidates::sort() { > + std::sort(flags_.begin(), flags_.end()); > +} > + > +void FlagCandidates::sort_by_air_distance() { > + std::sort(flags_.begin(), flags_.end(), > + [](const FlagCandidates::Candidate& lf, const > FlagCandidates::Candidate& rf) { > + return lf.air_distance < rf.air_distance; > + }); > +} > + > +void FlagCandidates::add_flag(const uint32_t coords, > + const bool different_economy, > + const uint16_t act_dist_to_wh, > + const uint16_t air_distance) { > + flags_.push_back( > + Candidate(coords, different_economy, start_flag_dist_to_wh, > act_dist_to_wh, air_distance)); > +} > + > +bool FlagCandidates::has_candidate(const uint32_t coords_hash) { > + for (auto& item : flags_) { > + if (item.coords_hash == coords_hash) { > + return true; > + } > + } > + return false; > +} > + > } // namespace Widelands > > === modified file 'src/ai/ai_help_structs.h' > --- src/ai/ai_help_structs.h 2019-05-25 08:20:22 +0000 > +++ src/ai/ai_help_structs.h 2019-06-14 18:11:44 +0000 > @@ -122,6 +123,11 @@ > // TODO(tiborb): this should be replaced by command line switch > constexpr int kFNeuronBitSize = 32; > constexpr int kMutationRatePosition = 42; > +// This is expiration time for distance from a flag to neares warehouse neares -> nearest > +constexpr int kFlagDistanceExpirationPeriod = 120 * 1000; > +// If the distance of flag-warehouse was not updated for this time, we > presume that the flag > +// does not exist anymore and remove it > +constexpr int kOldFlagRemoveTime = 5 * 60 * 1000; > > constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max(); > > > === modified file 'src/ai/defaultai.cc' > --- src/ai/defaultai.cc 2019-05-26 11:39:41 +0000 > +++ src/ai/defaultai.cc 2019-06-14 18:11:44 +0000 > @@ -3422,6 +3429,76 @@ > return true; > } > > + > +// Re-calculating warehouse to flag distances > +void DefaultAI::check_flag_distances(const uint32_t gametime) { > + for (WarehouseSiteObserver& wh_obs : warehousesites) { > + // wh_obs.flag_distances_last_update = gametime; NOCOM > + uint16_t checked_flags = 0; > + const uint32_t this_wh_hash = > wh_obs.site->get_position().hash(); > + uint32_t highest_distance_set = 0; > + > + std::queue<Widelands::Flag*> > + remaining_flags; // only used to collect flags reachable > walk over roads > + remaining_flags.push(&wh_obs.site->base_flag()); > + // const bool distance_set = Dead comment > + flag_warehouse_distance.set_distance( > + wh_obs.site->base_flag().get_position().hash(), 0, gametime, > this_wh_hash); > + uint32_t tmp_wh; > + assert(flag_warehouse_distance.get_distance( > + wh_obs.site->base_flag().get_position().hash(), > gametime, &tmp_wh) == 0); > + > + // Algorithm to walk on roads > + // All nodes are marked as to_be_checked == true first and once > the node is checked it is > + // changed to false. Under some conditions, the same node can > be checked twice, the > + // to_be_checked can be set back to true. Because less hoops > (fewer flag-to-flag roads) does > + // not always mean shortest road. > + while (!remaining_flags.empty()) { > + checked_flags += 1; ++checked_flags; > + // looking for a node with shortest existing road > distance from starting flag and one that > + // has to be checked Now going over roads leading from > this flag > + const uint16_t current_flag_distance = > flag_warehouse_distance.get_distance( > + remaining_flags.front()->get_position().hash(), > gametime, &tmp_wh); > + for (uint8_t i = 1; i <= 6; ++i) { > + Road* const road = > remaining_flags.front()->get_road(i); > + > + if (!road) { > + continue; > + } > + > + Flag* endflag = > &road->get_flag(Road::FlagStart); > + > + if (endflag == remaining_flags.front()) { > + endflag = > &road->get_flag(Road::FlagEnd); > + } > + const uint16_t steps_count = > road->get_path().get_nsteps(); > + > + // Calculated distance can be used or ignored > if f.e. longer than via other route > + bool const updated = > flag_warehouse_distance.set_distance( > + endflag->get_position().hash(), > current_flag_distance + steps_count, gametime, > + this_wh_hash); > + > + if (highest_distance_set < > current_flag_distance + steps_count) { > + highest_distance_set = > current_flag_distance + steps_count; > + } > + > + if (updated) { > + remaining_flags.push(endflag); > + } > + } > + remaining_flags.pop(); > + } > + > + printf( > + "%d: Checked flags: %3d, highest distance: %5d, warehouses: > %lu, time: %d s\n", // NOCOM > + player_number(), checked_flags, highest_distance_set, > warehousesites.size(), > + gametime / 1000); > + } > + > + // Now let do some lazy prunning - remove the flags that were not > updated for long prunning -> pruning > + flag_warehouse_distance.remove_old_flag(gametime); > +} > + > // Here we pick about 25 roads and investigate them. If it is a dead end we > dismantle it > bool DefaultAI::dismantle_dead_ends() { > bool road_dismantled = false; > @@ -3538,50 +3615,36 @@ > } > > bool is_warehouse = false; > - bool has_building = false; > if (Building* b = flag.get_building()) { > - has_building = true; > BuildingObserver& bo = > get_building_observer(b->descr().name().c_str()); > if (bo.type == BuildingObserver::Type::kWarehouse) { > is_warehouse = true; > } > } > + const uint32_t flag_coords_hash = flag.get_position().hash(); > > - // is connected to a warehouse? > + if (flag_warehouse_distance.get_road_prohibited(flag_coords_hash, > gametime)) {return false;} > const bool needs_warehouse = flag.get_economy()->warehouses().empty(); > > - if (!has_building && flag.nr_of_roads() == 1) { > - return false; > - } else if (is_warehouse && flag.nr_of_roads() <= 3) { > - // Do nothing > - } else if (needs_warehouse) { > - // Do nothing > - } else if (flag.current_wares() > 5) { > - // Do nothing > - } else if (has_building && std::rand() % 3 == 0) { > - // Do nothing > - } else if (std::rand() % 10 == 0) { > - // Do nothing > - } else { > - return false; > - } > - > - int16_t expected_shortcut = 27; > - if (has_building) { > - expected_shortcut -= 3; > - } > - expected_shortcut -= flag.current_wares() * 3; > - if (is_warehouse) { > - expected_shortcut -= 10; > - } > - if (std::rand() % 5 == 0) { > - expected_shortcut -= 10; > - } > - expected_shortcut -= 2 * flag.nr_of_roads(); > - > - create_shortcut_road(flag, 14, expected_shortcut, gametime); > - > - return true; > + uint32_t tmp_wh; // NOCOM > + > + // when deciding if we attempt to build a road from here we use > probability NOCOM > + uint16_t probability_score = 0; > + if (flag.nr_of_roads() == 1) {probability_score += 20;} > + if (is_warehouse && flag.nr_of_roads() <= 3) {probability_score += 20;} > + probability_score += flag.current_wares() * 5; > + if (needs_warehouse) {probability_score += 500;} > + if (std::rand() % 10 == 0) Add {} > + probability_score += > flag_warehouse_distance.get_distance(flag_coords_hash, gametime, &tmp_wh); > + > + if (std::rand() % 200 < probability_score) { > + const bool action_taken = create_shortcut_road(flag, 14, > gametime); > + if (!action_taken) {printf ("failed to build a road\n");} printf -> log > + return true; > + } > + printf ("not trying to build a road\n"); printf -> log > + return false; > + > } > > // This function takes a road (road is smallest section of roads with two > flags on the ends) > @@ -3756,318 +3819,343 @@ > // connection is not known > bool DefaultAI::create_shortcut_road(const Flag& flag, > uint16_t checkradius, > - int16_t min_reduction, > uint32_t gametime) { > > - // Increasing the failed_connection_tries counter > - // At the same time it indicates a time an economy is without a > warehouse > - EconomyObserver* eco = get_economy_observer(flag.economy()); > - // if we passed grace time this will be last attempt and if it fails > - // building is destroyes > - bool last_attempt_ = false; > - > - // this should not happen, but if the economy has a warehouse and a > dismantle > - // grace time set, we must 'zero' the dismantle grace time > - if (!flag.get_economy()->warehouses().empty() && > - eco->dismantle_grace_time != std::numeric_limits<uint32_t>::max()) { > - eco->dismantle_grace_time = > std::numeric_limits<uint32_t>::max(); > - } > - > - // first we deal with situations when this is economy with no warehouses > - // and this is a flag belonging to a building/constructionsite > - // such economy must get dismantle grace time (if not set yet) > - // end sometimes extended checkradius > - if (flag.get_economy()->warehouses().empty() && flag.get_building()) { > - > - // occupied military buildings get special treatment > - // (extended grace time + longer radius) > - bool occupied_military_ = false; > - Building* b = flag.get_building(); > - if (upcast(MilitarySite, militb, b)) { > - if > (militb->soldier_control()->stationed_soldiers().size() > 0) { > - occupied_military_ = true; > - } > - } > - > - // check if we are within grace time, if not or gracetime unset > we need to do something > - // if we are within gracetime we do nothing (this loop is > skipped) > - > - // if grace time is not set, this is probably first time > without a warehouse and we must set > - // it > - if (eco->dismantle_grace_time == > std::numeric_limits<uint32_t>::max()) { > - > - // constructionsites > - if (upcast(ConstructionSite const, constructionsite, > flag.get_building())) { > - BuildingObserver& bo = > - > get_building_observer(constructionsite->building().name().c_str()); > - // first very special case - a port (in the > phase of constructionsite) > - // this might be a new colonization port > - if (bo.is(BuildingAttribute::kPort)) { > - eco->dismantle_grace_time = gametime + > 60 * 60 * 1000; // one hour should be enough > - } else { // other constructionsites, usually > new (standalone) constructionsites > - eco->dismantle_grace_time = > - gametime + 30 * 1000 + // > very shot time is enough > - (eco->flags.size() * 30 * 1000); // > + 30 seconds for every flag in economy > - } > - > - // buildings > - } else { > - > - if (occupied_military_) { > - eco->dismantle_grace_time = > - (gametime + 90 * 60 * 1000) + > (eco->flags.size() * 20 * 1000); > - > - } else { // for other normal buildings > - eco->dismantle_grace_time = > - gametime + (45 * 60 * 1000) + > (eco->flags.size() * 20 * 1000); > - } > - } > - > - // we have passed grace_time - it is time to dismantle > - } else if (eco->dismantle_grace_time <= gametime) { > - last_attempt_ = true; > - // we increase a check radius in last attempt > - checkradius += 2; > - } > - > - // and bonus for occupied military buildings: > - if (occupied_military_) { > - checkradius += 4; > - } > - > - // and generally increase radius for unconnected buildings > - checkradius += 2; > - } > - > - // Now own roadfinding stuff > - const Map& map = game().map(); > - > - // initializing new object of FlagsForRoads, we will push there all > candidate flags > - // First we dont even know if a road can be built there (from current > flag) > - Widelands::FlagsForRoads RoadCandidates(min_reduction); > - > - FindNodeWithFlagOrRoad functor; > - CheckStepRoadAI check(player_, MOVECAPS_WALK, true); > - > - // get all flags within radius > - std::vector<Coords> reachable; > - map.find_reachable_fields( > - Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius), > &reachable, check, functor); > - > - for (const Coords& reachable_coords : reachable) { > - > - // ignore starting flag, of course > - if (reachable_coords == flag.get_position()) { > - continue; > - } > - > - // first make sure there is an immovable (should be, but still) > - if (upcast(PlayerImmovable const, player_immovable, > map[reachable_coords].get_immovable())) { > - > - // if it is the road, make a flag there > - if (dynamic_cast<const > Road*>(map[reachable_coords].get_immovable())) { > - game().send_player_build_flag(player_number(), > reachable_coords); > - } > - > - // do not go on if it is not a flag > - if (!dynamic_cast<const > Flag*>(map[reachable_coords].get_immovable())) { > - continue; > - } > - > - // testing if a flag/road's economy has a warehouse, if > not we are not > - // interested to connect to it > - if (player_immovable->economy().warehouses().size() == > 0) { > - continue; > - } > - > - // This is a candidate, sending all necessary info to > RoadCandidates > - const bool different_economy = > (player_immovable->get_economy() != flag.get_economy()); > - const int32_t air_distance = > map.calc_distance(flag.get_position(), reachable_coords); > - if > (!RoadCandidates.has_candidate(reachable_coords.hash())) { > - RoadCandidates.add_flag(reachable_coords, > air_distance, different_economy); > - } > - } > - } > - > - // now we walk over roads and if field is reachable by roads, we change > the distance assigned > - // above > - std::map<uint32_t, NearFlag> nearflags; // only used to collect flags > reachable walk over roads > - nearflags[flag.get_position().hash()] = NearFlag(&flag, 0); > - > - // Algorithm to walk on roads > - // All nodes are marked as to_be_checked == true first and once the > node is checked it is changed > - // to false. Under some conditions, the same node can be checked twice, > the to_be_checked can > - // be set back to true. Because less hoops (fewer flag-to-flag roads) > does not always mean > - // shortest > - // road. > - for (;;) { > - // looking for a node with shortest existing road distance from > starting flag and one that has > - // to be checked > - uint32_t start_field = std::numeric_limits<uint32_t>::max(); > - uint32_t nearest_distance = 10000; > - for (auto item : nearflags) { > - if (item.second.current_road_distance < > nearest_distance && item.second.to_be_checked) { > - nearest_distance = > item.second.current_road_distance; > - start_field = item.first; > - } > - } > - // OK, we failed to find a NearFlag where to_be_checked == > true, so quitting the loop now > - if (start_field == std::numeric_limits<uint32_t>::max()) { > - break; > - } > - > - nearflags[start_field].to_be_checked = false; > - > - // Now going over roads leading from this flag > - for (uint8_t i = 1; i <= 6; ++i) { > - Road* const road = > nearflags[start_field].flag->get_road(i); > - > - if (!road) { > - continue; > - } > - > - Flag* endflag = &road->get_flag(Road::FlagStart); > - > - if (endflag == nearflags[start_field].flag) { > - endflag = &road->get_flag(Road::FlagEnd); > - } > - > - const uint32_t endflag_hash = > endflag->get_position().hash(); > - > - const int32_t dist = > map.calc_distance(flag.get_position(), endflag->get_position()); > - > - if (dist > checkradius + 2) { // Testing bigger > vicinity then checkradius.... > - continue; > - } > - > - // There is few scenarios for this neighbour flag > - if (nearflags.count(endflag_hash) == 0) { > - // This is brand new flag > - nearflags[endflag_hash] = > - NearFlag(endflag, > nearflags[start_field].current_road_distance + > - > road->get_path().get_nsteps()); > - } else { > - // We know about this flag already > - if > (nearflags[endflag_hash].current_road_distance > > - > nearflags[start_field].current_road_distance + road->get_path().get_nsteps()) > { > - // ..but this current connection is > shorter than one found before > - > nearflags[endflag_hash].current_road_distance = > - > nearflags[start_field].current_road_distance + road->get_path().get_nsteps(); > - // So let re-check neighbours once more > - nearflags[endflag_hash].to_be_checked = > true; > - } > - } > - } > - } > - > - // Sending calculated walking costs from nearflags to RoadCandidates to > update info on > - // Candidate flags/roads > - for (auto& nf_walk : nearflags) { > - // NearFlag contains also flags beyond check radius, these are > not relevent for us > - if > (RoadCandidates.has_candidate(nf_walk.second.flag->get_position().hash())) { > - RoadCandidates.set_cur_road_distance( > - nf_walk.second.flag->get_position(), > nf_walk.second.current_road_distance); > - } > - } > - > - // Here we must consider how much are buildable fields lacking > - // the number will be transformed to a weight passed to findpath > function > - int32_t fields_necessity = 0; > - if (spots_ < kSpotsTooLittle) { > - fields_necessity += 10; > - } > - if (map_allows_seafaring_ && num_ports == 0) { > - fields_necessity += 10; > - } > - if (num_ports < 4) { > - fields_necessity += 5; > - } > - if (spots_ < kSpotsEnough) { > - fields_necessity += 5; > - } > - fields_necessity *= > std::abs(management_data.get_military_number_at(64)) * 5; > - > - // We need to sort these flags somehow, because we are not going to > investigate all of them > - // so sorting first by current road length (that might contain fake > values for flags that were > - // not reached over roads) and secondary by air_distance (nearer flags > first) > - std::sort(std::begin(RoadCandidates.flags_queue), > std::end(RoadCandidates.flags_queue), > - [](const FlagsForRoads::Candidate& a, const > FlagsForRoads::Candidate& b) { > - // Here we are doing a kind of bucketing > - const int32_t a_length = a.current_road_length / 50; > - const int32_t b_length = b.current_road_length / 50; > - return std::tie(b_length, a.air_distance) < > std::tie(a_length, b.air_distance); > - }); > - > - // Now we calculate roads from here to few best looking > RoadCandidates.... > - uint32_t possible_roads_count = 0; > - uint32_t current = 0; // hash of flag that we are going to calculate > in the iteration > - while (possible_roads_count < 5 && > RoadCandidates.get_best_uncalculated(¤t)) { > - const Widelands::Coords coords = Coords::unhash(current); > - Path path; > - > - // value of pathcost is not important, it just indicates, that > the path can be built > - // We send this information to RoadCandidates, with length of > possible road if applicable > - const int32_t pathcost = > - map.findpath(flag.get_position(), coords, 0, path, check, 0, > fields_necessity); > - if (pathcost >= 0) { > - RoadCandidates.road_possible(coords, path.get_nsteps()); > - possible_roads_count += 1; > - } > - } > - > - // re-sorting again, now by reduction score (custom operator specified > in .h file) > - std::sort(std::begin(RoadCandidates.flags_queue), > std::end(RoadCandidates.flags_queue)); > - > - // Well and finally building the winning road (if any) > - uint32_t winner_hash = 0; > - if (RoadCandidates.get_winner(&winner_hash)) { > - const Widelands::Coords target_coords = > Coords::unhash(winner_hash); > - Path& path = *new Path(); > + // Increasing the failed_connection_tries counter > + // At the same time it indicates a time an economy is without a warehouse > + EconomyObserver* eco = get_economy_observer(flag.economy()); > + // if we passed grace time this will be last attempt and if it fails > + // building is destroyes > + bool last_attempt_ = false; > + > + // this should not happen, but if the economy has a warehouse and a > dismantle > + // grace time set, we must 'zero' the dismantle grace time > + if (!flag.get_economy()->warehouses().empty() && > + eco->dismantle_grace_time != std::numeric_limits<uint32_t>::max()) { > + eco->dismantle_grace_time = std::numeric_limits<uint32_t>::max(); > + } > + > + // first we deal with situations when this is economy with no warehouses > + // and this is a flag belonging to a building/constructionsite > + // such economy must get dismantle grace time (if not set yet) > + // end sometimes extended checkradius > + if (flag.get_economy()->warehouses().empty() && flag.get_building()) { > + > + // occupied military buildings get special treatment > + // (extended grace time + longer radius) > + bool occupied_military_ = false; > + Building* b = flag.get_building(); > + if (upcast(MilitarySite, militb, b)) { > + if (militb->soldier_control()->stationed_soldiers().size() > 0) { > + occupied_military_ = true; > + } > + } > + > + // check if we are within grace time, if not or gracetime unset we > need to do something > + // if we are within gracetime we do nothing (this loop is skipped) > + > + // if grace time is not set, this is probably first time without a > warehouse and we must set > + // it > + if (eco->dismantle_grace_time == > std::numeric_limits<uint32_t>::max()) { We have std::numeric_limits<uint32_t>::max() 3 times, use a constexpr? > + > + // constructionsites > + if (upcast(ConstructionSite const, constructionsite, > flag.get_building())) { > + BuildingObserver& bo = > + > get_building_observer(constructionsite->building().name().c_str()); > + // first very special case - a port (in the phase of > constructionsite) > + // this might be a new colonization port > + if (bo.is(BuildingAttribute::kPort)) { > + eco->dismantle_grace_time = gametime + 60 * 60 * 1000; > // one hour should be enough > + } else { // other constructionsites, usually new > (standalone) constructionsites > + eco->dismantle_grace_time = > + gametime + 30 * 1000 + // very shot time > is enough > + (eco->flags.size() * 30 * 1000); // + 30 seconds for > every flag in economy > + } > + > + // buildings > + } else { > + > + if (occupied_military_) { > + eco->dismantle_grace_time = > + (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 > * 1000); > + > + } else { // for other normal buildings > + eco->dismantle_grace_time = > + gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 > * 1000); > + } > + } > + > + // we have passed grace_time - it is time to dismantle > + } else if (eco->dismantle_grace_time <= gametime) { > + last_attempt_ = true; > + // we increase a check radius in last attempt > + checkradius += 2; > + } > + > + // and bonus for occupied military buildings: > + if (occupied_military_) { > + checkradius += 4; > + } > + > + // and generally increase radius for unconnected buildings > + checkradius += 2; > + } > + > + // Now own roadfinding stuff > + const Map& map = game().map(); > + > + // Initializing new object of FlagsForRoads, we will push there all > candidate flags > + // First we dont even know if a road can be built there (from current > flag) > + // Adding also distance of this flag to nearest wh > + uint32_t tmp_wh; // This information is not used, but we need it > + const uint32_t current_flag_dist_to_wh = flag_warehouse_distance. > + get_distance(flag.get_position().hash(), gametime, &tmp_wh); > + > + FlagCandidates flag_candidates(current_flag_dist_to_wh); > + > + FindNodeWithFlagOrRoad functor; > + CheckStepRoadAI check(player_, MOVECAPS_WALK, true); > + > + // get all flags within radius > + std::vector<Coords> reachable; > + map.find_reachable_fields( > + Area<FCoords>(map.get_fcoords(flag.get_position()), checkradius), > &reachable, check, functor); > + > + for (const Coords& reachable_coords : reachable) { > + > + // ignore starting flag, of course > + if (reachable_coords == flag.get_position()) { > + continue; > + } > + > + const uint32_t reachable_coords_hash = reachable_coords.hash(); > + > + // first make sure there is an immovable (should be, but still) > + if (upcast(PlayerImmovable const, player_immovable, > map[reachable_coords].get_immovable())) { > + > + // if it is the road, make a flag there > + if (dynamic_cast<const > Road*>(map[reachable_coords].get_immovable())) { > + game().send_player_build_flag(player_number(), > reachable_coords); > + } > + > + // do not go on if it is not a flag > + if (!dynamic_cast<const > Flag*>(map[reachable_coords].get_immovable())) { > + continue; > + } > + > + // testing if a flag/road's economy has a warehouse, if not we > are not > + // interested to connect to it > + if (player_immovable->economy().warehouses().size() == 0) { > + continue; > + } > + > + // This is a candidate, sending all necessary info to > RoadCandidates > + const bool different_economy = (player_immovable->get_economy() > != flag.get_economy()); > + const uint16_t air_distance = > map.calc_distance(flag.get_position(), reachable_coords); > + > + if (!flag_candidates.has_candidate(reachable_coords_hash) && > !flag_warehouse_distance.get_road_prohibited(reachable_coords_hash, > gametime)) { > + flag_candidates.add_flag(reachable_coords_hash, > different_economy, > + flag_warehouse_distance.get_distance(reachable_coords_hash, > gametime, &tmp_wh), air_distance); > + } > + } > + } > + > + // now we walk over roads and if field is reachable by roads, we change > the distance assigned > + // above > + std::map<uint32_t, NearFlag> nearflags; // only used to collect flags > reachable walk over roads > + nearflags[flag.get_position().hash()] = NearFlag(&flag, 0); > + > + collect_nearflags(nearflags, flag, checkradius); > + > + > + > + // Sending calculated walking costs from nearflags to RoadCandidates to > update info on > + // Candidate flags/roads > + for (auto& nf_walk : nearflags) { > + const uint32_t nf_hash = > nf_walk.second.flag->get_position().hash(); > + // NearFlag contains also flags beyond check radius, these are not > relevent for us > + if (flag_candidates.has_candidate(nf_hash)) { > + flag_candidates.set_cur_road_distance( > + nf_hash, nf_walk.second.current_road_distance); > + } > + } > + > + // Here we must consider how much are buildable fields lacking > + // the number will be transformed to a weight passed to findpath function > + int32_t fields_necessity = 0; > + if (spots_ < kSpotsTooLittle) { > + fields_necessity += 10; > + } > + if (map_allows_seafaring_ && num_ports == 0) { > + fields_necessity += 10; > + } > + if (num_ports < 4) { > + fields_necessity += 5; > + } > + if (spots_ < kSpotsEnough) { > + fields_necessity += 5; > + } > + > + fields_necessity *= std::abs(management_data.get_military_number_at(64)) > * 5; > + > + // Now we calculate roads from here to few best looking > RoadCandidates.... > + flag_candidates.sort_by_air_distance(); > + uint32_t possible_roads_count = 0; > + for (const auto &flag_candidate : flag_candidates.flags()){ > + if (possible_roads_count > 10) {break;} > + const Widelands::Coords coords = > Coords::unhash(flag_candidate.coords_hash); > + Path path; > + > + // value of pathcost is not important, it just indicates, that the > path can be built > + // We send this information to RoadCandidates, with length of > possible road if applicable > + const int32_t pathcost = > + map.findpath(flag.get_position(), coords, 0, path, check, 0, > fields_necessity); > + if (pathcost >= 0) { > + flag_candidates.set_road_possible(flag_candidate.coords_hash, > path.get_nsteps()); > + possible_roads_count += 1; > + } > + } > + > + // re-sorting again now by default by a score > + flag_candidates.sort(); > + > + > + // Well and finally building the winning road (if any) > + const int32_t winner_min_score = (spots_ < kSpotsTooLittle) ? 50 : 25; > + > + FlagCandidates::Candidate* winner = > flag_candidates.get_winner(winner_min_score); > + if (winner) { > + const Widelands::Coords target_coords = > Coords::unhash(winner->coords_hash); > + printf("Winner to connect to %3dx%3d: ",flag.get_position(). > x,flag.get_position().y); //without new line > + winner->print(); > + > + // This is to prohibit the flag for some time but with > exemption of warehouse > + if (flag_warehouse_distance.get_distance(winner->coords_hash, > gametime, &tmp_wh) > 0) { > + > flag_warehouse_distance.set_road_built(winner->coords_hash, gametime); > + } > + // and we stright away set distance of future flag > + > flag_warehouse_distance.set_distance(flag.get_position().hash(), > winner->start_flag_dist_to_wh + winner->possible_road_distance, > + gametime, 0); // faking the warehouse > + > + Path& path = *new Path(); > #ifndef NDEBUG > - const int32_t pathcost = > - map.findpath(flag.get_position(), target_coords, 0, path, > check, 0, fields_necessity); > - assert(pathcost >= 0); > + const int32_t pathcost = > + map.findpath(flag.get_position(), target_coords, 0, path, check, > 0, fields_necessity); > + assert(pathcost >= 0); > #else > - map.findpath(flag.get_position(), target_coords, 0, path, > check, 0, fields_necessity); > + map.findpath(flag.get_position(), target_coords, 0, path, check, 0, > fields_necessity); > #endif > - game().send_player_build_road(player_number(), path); > - return true; > - } > - // We can't build a road so let's block the vicinity as an indication > this area is not > - // connectible > - // Usually we block for 2 minutes, but if it is a last attempt we block > for 10 minutes > - // Note: we block the vicinity only if this economy (usually a sole > flag with a building) is not > - // connected to a warehouse > - if (flag.get_economy()->warehouses().empty()) { > - > - // blocking only if latest block was less then 60 seconds ago > or it is last attempt > - if (eco->fields_block_last_time + 60000 < gametime || > last_attempt_) { > - eco->fields_block_last_time = gametime; > - > - const uint32_t block_time = last_attempt_ ? 10 * 60 * > 1000 : 2 * 60 * 1000; > - > - FindNodeAcceptAll buildable_functor; > - CheckStepOwnTerritory check_own(player_, MOVECAPS_WALK, > true); > - > - // get all flags within radius > - std::vector<Coords> reachable_to_block; > - > map.find_reachable_fields(Area<FCoords>(map.get_fcoords(flag.get_position()), > checkradius), > - &reachable_to_block, > check_own, buildable_functor); > - > - for (auto coords : reachable_to_block) { > - blocked_fields.add(coords, > game().get_gametime() + block_time); > - } > - } > - > - // If it last attempt we also destroy the flag (with a building > if any attached) > - if (last_attempt_) { > - remove_from_dqueue<Widelands::Flag>(eco->flags, &flag); > - game().send_player_bulldoze(*const_cast<Flag*>(&flag)); > - dead_ends_check_ = true; > - return true; > - } > - } > - return false; > + game().send_player_build_road(player_number(), path); > + return true; > + } > + // We can't build a road so let's block the vicinity as an indication > this area is not > + // connectible > + // Usually we block for 2 minutes, but if it is a last attempt we block > for 10 minutes > + // Note: we block the vicinity only if this economy (usually a sole flag > with a building) is not > + // connected to a warehouse > + if (flag.get_economy()->warehouses().empty()) { > + > + // blocking only if latest block was less then 60 seconds ago or it > is last attempt > + if (eco->fields_block_last_time + 60000 < gametime || last_attempt_) > { We have 60 * 1000 a lot - put it in a constexpr? > + eco->fields_block_last_time = gametime; > + > + const uint32_t block_time = last_attempt_ ? 10 * 60 * 1000 : 2 * > 60 * 1000; > + > + FindNodeAcceptAll buildable_functor; > + CheckStepOwnTerritory check_own(player_, MOVECAPS_WALK, true); > + > + // get all flags within radius > + std::vector<Coords> reachable_to_block; > + > map.find_reachable_fields(Area<FCoords>(map.get_fcoords(flag.get_position()), > checkradius), > + &reachable_to_block, check_own, > buildable_functor); > + > + for (auto coords : reachable_to_block) { > + blocked_fields.add(coords, game().get_gametime() + > block_time); > + } > + } > + > + // If it last attempt we also destroy the flag (with a building if > any attached) > + if (last_attempt_) { > + remove_from_dqueue<Widelands::Flag>(eco->flags, &flag); > + game().send_player_bulldoze(*const_cast<Flag*>(&flag)); > + dead_ends_check_ = true; > + return true; > + } > + } > + return false; > +} > + > +void DefaultAI::collect_nearflags(std::map<uint32_t, NearFlag> &nearflags, > const Flag& flag, const uint16_t checkradius) { > + // Algorithm to walk on roads > + // All nodes are marked as to_be_checked == true first and once the node > is checked it is changed > + // to false. Under some conditions, the same node can be checked twice, > the to_be_checked can > + // be set back to true. Because less hoops (fewer flag-to-flag roads) > does not always mean > + // shortest > + // road. > + > + const Map& map = game().map(); > + > + for (;;) { > + // looking for a node with shortest existing road distance from > starting flag and one that has > + // to be checked > + uint32_t start_field = std::numeric_limits<uint32_t>::max(); > + uint32_t nearest_distance = 10000; > + for (auto item : nearflags) { > + if (item.second.current_road_distance < nearest_distance && > item.second.to_be_checked) { > + nearest_distance = item.second.current_road_distance; > + start_field = item.first; > + } > + } > + // OK, we failed to find a NearFlag where to_be_checked == true, so > quitting the loop now > + if (start_field == std::numeric_limits<uint32_t>::max()) { > + break; > + } > + > + nearflags[start_field].to_be_checked = false; > + > + // Now going over roads leading from this flag > + for (uint8_t i = 1; i <= 6; ++i) { > + Road* const road = nearflags[start_field].flag->get_road(i); > + > + if (!road) { > + continue; > + } > + > + Flag* endflag = &road->get_flag(Road::FlagStart); > + > + if (endflag == nearflags[start_field].flag) { > + endflag = &road->get_flag(Road::FlagEnd); > + } > + > + const uint32_t endflag_hash = endflag->get_position().hash(); > + > + const int32_t dist = map.calc_distance(flag.get_position(), > endflag->get_position()); > + > + if (dist > checkradius + 2) { // Testing bigger vicinity then > checkradius.... > + continue; > + } > + > + // There is few scenarios for this neighbour flag > + if (nearflags.count(endflag_hash) == 0) { > + // This is brand new flag > + // calculating diff how much closer we will get to the flag > + nearflags[endflag_hash] = > + NearFlag(endflag, > nearflags[start_field].current_road_distance + > + road->get_path().get_nsteps()); > + } else { > + // We know about this flag already > + if (nearflags[endflag_hash].current_road_distance > > + nearflags[start_field].current_road_distance + > road->get_path().get_nsteps()) { > + // ..but this current connection is shorter than one > found before > + nearflags[endflag_hash].current_road_distance = > + nearflags[start_field].current_road_distance + > road->get_path().get_nsteps(); > + // So let re-check neighbours once more > + nearflags[endflag_hash].to_be_checked = true; > + } > + } > + } > + } > + > } > > /** > > === added file 'src/ai/test/test_ai.cc' > --- src/ai/test/test_ai.cc 1970-01-01 00:00:00 +0000 > +++ src/ai/test/test_ai.cc 2019-06-14 18:11:44 +0000 > @@ -0,0 +1,240 @@ > +/* > + * Copyright (C) 2007-2019 by the Widelands Development Team > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * 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 > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA > 02110-1301, USA. > + * > + */ > + > +#include <exception> > + > +#include <boost/test/unit_test.hpp> > + > +#ifdef _WIN32 > +#include "base/log.h" > +#endif > +#include "ai/ai_help_structs.h" > + > +// Triggered by BOOST_AUTO_TEST_CASE > +CLANG_DIAG_OFF("-Wdisabled-macro-expansion") > + > +namespace Widelands { // Needed? > +class World; > +} // namespace Widelands > + > +using namespace Widelands; > + > +BOOST_AUTO_TEST_SUITE(ai) > + > +BOOST_AUTO_TEST_CASE(flag_distance_soft_expiry) { > + FlagWarehouseDistances fw; > + uint32_t tmp_wh; > + BOOST_CHECK_EQUAL(fw.get_distance(0, 0, &tmp_wh), 1000); > + // set_distance(const uint32_t flag_coords, const uint16_t distance, Clean up dead comments? > + // uint32_t const gametime, uint32_t const nearest_warehouse) > + BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); > + // get_distance(const uint32_t flag_coords, uint32_t gametime, > uint32_t* nw) > + BOOST_CHECK_EQUAL(fw.get_distance(1, 2, &tmp_wh), 2); // distance now 2 > + BOOST_CHECK_EQUAL(tmp_wh, 3); > + > + // setting longer distance below soft_expiry time > + BOOST_CHECK_EQUAL(fw.set_distance(1, 3, kFlagDistanceExpirationPeriod / > 3, 4), false); > + // distance to 3 not updated > + BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod / 3, > &tmp_wh), 2); > + BOOST_CHECK_EQUAL(tmp_wh, 3); > + > + // now setting after soft expiry > + BOOST_CHECK_EQUAL( > + fw.set_distance(1, 1, kFlagDistanceExpirationPeriod / 3, 6), true); > // distance set to 1 > + BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod / 3, > &tmp_wh), 1); > + BOOST_CHECK_EQUAL(tmp_wh, 6); > +} > +BOOST_AUTO_TEST_CASE(flag_distance_below_expiry) > +/* Compare with void free_test_function() */ > +{ > + FlagWarehouseDistances fw; > + uint32_t tmp_wh; > + // set_distance(const uint32_t flag_coords, const uint16_t distance, > + // uint32_t const gametime, uint32_t const nearest_warehouse) > + BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); > + // get_distance(const uint32_t flag_coords, uint32_t gametime, > uint32_t* nw) > + > + // setting longer distance after soft but below expiry time > + BOOST_CHECK_EQUAL(fw.set_distance(1, 3, kFlagDistanceExpirationPeriod * > 2 / 3, 5), true); > + BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod * 2 > / 3, &tmp_wh), 3); > + BOOST_CHECK_EQUAL(tmp_wh, 5); > +} > + > +BOOST_AUTO_TEST_CASE(flag_distance_after_expiry) > +/* Compare with void free_test_function() */ > +{ > + FlagWarehouseDistances fw; > + uint32_t tmp_wh; > + // set_distance(const uint32_t flag_coords, const uint16_t distance, > + // uint32_t const gametime, uint32_t const nearest_warehouse) > + BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); > + // get_distance(const uint32_t flag_coords, uint32_t gametime, > uint32_t* nw) > + > + // setting longer distance below expiry time > + BOOST_CHECK_EQUAL(fw.set_distance(1, 3, 2 * > kFlagDistanceExpirationPeriod, 5), true); > + BOOST_CHECK_EQUAL(fw.get_distance(1, 3, &tmp_wh), 3); > + BOOST_CHECK_EQUAL(tmp_wh, 5); > +} > + > +BOOST_AUTO_TEST_CASE(flag_distance_expiration_extension) > +/* setting the same distance restart the expiry_period */ > +{ > + FlagWarehouseDistances fw; > + uint32_t tmp_wh; > + BOOST_CHECK_EQUAL(fw.set_distance(1, 2, 0, 3), true); > + BOOST_CHECK_EQUAL( > + fw.set_distance(1, 2, 0, 3), false); // cannot reset the same > distance in the same time > + // get_distance(const uint32_t flag_coords, uint32_t gametime, > uint32_t* nw) > + > + // Now we are after expiration time > + BOOST_CHECK_EQUAL(fw.get_distance(1, kFlagDistanceExpirationPeriod + 3, > &tmp_wh), 1000); > + > + // setting distance 2 time shortly one after another > + // set_distance(const uint32_t flag_coords, const uint16_t distance, > + // uint32_t const gametime, uint32_t const nearest_warehouse) > + BOOST_CHECK_EQUAL(fw.set_distance(1, 2, kFlagDistanceExpirationPeriod + > 3, 5), true); > + BOOST_CHECK_EQUAL(fw.set_distance(1, 2, kFlagDistanceExpirationPeriod + > 10, 5), true); > + // current expiry_time should be 2*kFlagDistanceExpirationPeriod + 10 > + BOOST_CHECK_EQUAL(fw.get_distance(1, 2 * kFlagDistanceExpirationPeriod, > &tmp_wh), 2); > + BOOST_CHECK_EQUAL(tmp_wh, 5); > + BOOST_CHECK_EQUAL(fw.get_distance(1, 2 * kFlagDistanceExpirationPeriod > + 15, &tmp_wh), 1000); > +} > + > +BOOST_AUTO_TEST_CASE(flag_distance_road_builtexpiration_extension) > +/* setting the same distance restart the expiry_period */ > +{ > + FlagWarehouseDistances fw; > + // uint32_t tmp_wh; > + // No road built on fresh flag > + BOOST_CHECK_EQUAL(fw.get_road_prohibited(1, 1), false); > + // get_distance(const uint32_t flag_coords, uint32_t gametime, > uint32_t* nw) > + > + // setting road we dont know about > + fw.set_road_built(1, 0); > + BOOST_CHECK_EQUAL(fw.get_road_prohibited(1, 1), false); > + > + // let fw knows about it > + fw.set_distance(1, 2, 0, 3); > + fw.set_road_built(1, 0); > + BOOST_CHECK_EQUAL(fw.get_road_prohibited(1, 1), true); > + BOOST_CHECK_EQUAL(fw.get_road_prohibited(1, 59999), true); > + BOOST_CHECK_EQUAL(fw.get_road_prohibited(1, 60001), false); > + > + BOOST_CHECK_EQUAL(fw.get_road_prohibited(2, 60001), false); > +} > + > +BOOST_AUTO_TEST_CASE(flag_distance_old_removal) > +/* setting the same distance restart the expiry_period */ > +{ > + FlagWarehouseDistances fw; > + fw.set_distance(1, 2, 0, 3); > + BOOST_CHECK_EQUAL(fw.count(), 1); > + BOOST_CHECK_EQUAL(fw.remove_old_flag(kOldFlagRemoveTime + > kFlagDistanceExpirationPeriod), false); > + BOOST_CHECK_EQUAL(fw.count(), 1); > + BOOST_CHECK_EQUAL( > + fw.remove_old_flag(kOldFlagRemoveTime + > kFlagDistanceExpirationPeriod + 2), true); > + BOOST_CHECK_EQUAL(fw.count(), 0); > +} > + > +BOOST_AUTO_TEST_CASE(new_flag_road_not_prohibited) > +/* setting the same distance restart the expiry_period */ > +{ > + FlagWarehouseDistances fw; > + // let fw knows about it > + BOOST_CHECK_EQUAL(fw.count(), 0); > + fw.set_distance(1, 2, 0, 3); > + BOOST_CHECK_EQUAL(fw.count(), 1); > + BOOST_CHECK_EQUAL(fw.get_road_prohibited(1, 1), false); > +} > + > +BOOST_AUTO_TEST_CASE(flag_candidate_init) > +/* setting the same distance restart the expiry_period */ > +{ > + FlagCandidates fc = FlagCandidates(10); > + BOOST_CHECK_EQUAL(fc.count(), 0); > +} > + > +BOOST_AUTO_TEST_CASE(flag_candidate_winner_score) { > + const uint16_t kCurFlDistToWh = 3; > + const uint16_t kStartFlagToWh = 10; > + > + const uint16_t kPosRoadDist = 5; > + const uint16_t kCurRoadDistFlToFl = 17; > + > + const uint32_t kTestedCoords = 11; > + > + FlagCandidates fc = FlagCandidates(kStartFlagToWh); > + > + // coord, different economy, distance to wh > + fc.add_flag(kTestedCoords, false, kCurFlDistToWh, 1); > + // setting coords, dist > + BOOST_CHECK_EQUAL(fc.set_cur_road_distance(kTestedCoords, > kCurRoadDistFlToFl), true); > + BOOST_CHECK_EQUAL( > + fc.set_cur_road_distance(1, 5), false); // we cannot set distance > to unknown flag > + BOOST_CHECK_EQUAL(fc.get_winner(), nullptr); // road not possible > + // set length of possible road > + BOOST_CHECK_EQUAL(fc.set_road_possible(kTestedCoords, kPosRoadDist), > true); > + BOOST_VERIFY(fc.get_winner()); > + BOOST_CHECK_EQUAL(fc.get_winner()->start_flag_dist_to_wh, > kStartFlagToWh); > + BOOST_CHECK_EQUAL(fc.get_winner()->cand_flag_distance_to_wh, > kCurFlDistToWh); > + BOOST_CHECK_EQUAL(fc.get_winner()->flag_to_flag_road_distance, > kCurRoadDistFlToFl); > + BOOST_CHECK_EQUAL(fc.get_winner()->possible_road_distance, > kPosRoadDist); > + BOOST_CHECK_EQUAL(fc.get_winner()->coords_hash, kTestedCoords); > + BOOST_CHECK_EQUAL(fc.get_winner()->different_economy, false); > + > + BOOST_CHECK_EQUAL(fc.get_winner()->score(), > + +(kStartFlagToWh - kCurFlDistToWh) + > (kCurRoadDistFlToFl - 2 * kPosRoadDist)); > +} > +BOOST_AUTO_TEST_CASE(flag_candidates_sorting) { > + FlagCandidates fc = FlagCandidates(10); > + > + fc.add_flag(0, false, 10, 1); > + fc.add_flag(1, false, 10, 1); > + fc.add_flag(2, false, 10, 1); > + BOOST_CHECK_EQUAL(fc.set_cur_road_distance(0, 5), true); > + BOOST_CHECK_EQUAL(fc.set_cur_road_distance(1, 5), true); > + BOOST_CHECK_EQUAL(fc.set_cur_road_distance(2, 5), true); > + BOOST_CHECK_EQUAL(fc.set_road_possible(0, 4), true); > + BOOST_CHECK_EQUAL(fc.set_road_possible(1, 2), true); > + BOOST_CHECK_EQUAL(fc.set_road_possible(2, 3), true); > + BOOST_CHECK_EQUAL(fc.get_winner()->coords_hash, 1); // sorted done > automatically > + BOOST_CHECK_EQUAL(fc.count(), 3); > +} > + > +BOOST_AUTO_TEST_CASE(flag_sort_by_air_distance) { > + FlagCandidates fc = FlagCandidates(10); > + > + fc.add_flag(0, false, 10, 4); > + fc.add_flag(1, false, 10, 1); > + fc.add_flag(2, false, 10, 2); > + fc.sort_by_air_distance(); > + BOOST_CHECK_EQUAL(fc.flags()[0].air_distance, 1); > +} > + > +BOOST_AUTO_TEST_CASE(flag_has_candidate) { > + FlagCandidates fc = FlagCandidates(10); > + > + fc.add_flag(0, false, 10, 4); > + fc.add_flag(1, false, 10, 1); > + fc.add_flag(2, false, 10, 2); > + BOOST_CHECK_EQUAL(fc.has_candidate(1), true); > + BOOST_CHECK_EQUAL(fc.has_candidate(3), false); > +} > + > +BOOST_AUTO_TEST_SUITE_END() -- https://code.launchpad.net/~widelands-dev/widelands/ai_flag_warehouse_distance/+merge/368544 Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/ai_flag_warehouse_distance. _______________________________________________ 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