TGoalVec GatherArmy::getAllPossibleSubgoals() { //get all possible towns, heroes and dwellings we may use TGoalVec ret; //TODO: include evaluation of monsters gather in calculation for (auto t : cb->getTownsInfo()) { auto pos = t->visitablePos(); if (ai->isAccessibleForHero(pos, hero)) { if(!t->visitingHero && howManyReinforcementsCanGet(hero,t)) { if (!vstd::contains (ai->townVisitsThisWeek[hero], t)) ret.push_back (sptr (Goals::VisitTile(pos).sethero(hero))); } auto bid = ai->canBuildAnyStructure(t, std::vector<BuildingID> (unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)); if (bid != BuildingID::NONE) ret.push_back (sptr(BuildThis(bid, t))); } } auto otherHeroes = cb->getHeroesInfo(); auto heroDummy = hero; erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h) { return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->visitablePos(), h, true) || !ai->canGetArmy(heroDummy.h, h) || ai->getGoal(h)->goalType == Goals::GATHER_ARMY); }); for (auto h : otherHeroes) { ret.push_back (sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero))); //go to the other hero if we are faster ret.push_back (sptr (Goals::VisitHero(hero->id.getNum()).setisAbstract(true).sethero(h))); //let the other hero come to us } std::vector <const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if(obj->ID == Obj::CREATURE_GENERATOR1) { auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID); //Use flagged dwellings only when there are available creatures that we can afford if(relationToOwner == PlayerRelations::SAME_PLAYER) { auto dwelling = dynamic_cast<const CGDwelling*>(obj); for(auto & creLevel : dwelling->creatures) { if(creLevel.first) { for(auto & creatureID : creLevel.second) { auto creature = VLC->creh->creatures[creatureID]; if (ai->freeResources().canAfford(creature->cost)) objs.push_back(obj); } } } } } } for(auto h : cb->getHeroesInfo()) { for (auto obj : objs) { //find safe dwelling auto pos = obj->visitablePos(); if (ai->isGoodForVisit(obj, h)) ret.push_back (sptr (Goals::VisitTile(pos).sethero(h))); } } if (ai->canRecruitAnyHero()) //this is not stupid in early phase of game ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) { if (hero == ai->primaryHero() || value >= 1.1f) ret.push_back (sptr(Goals::Explore())); else //workaround to break loop - seemingly there are no ways to explore left throw goalFulfilledException (sptr(Goals::GatherArmy(0).sethero(hero))); } return ret; }
TGoalVec Explore::getAllPossibleSubgoals() { TGoalVec ret; std::vector<const CGHeroInstance *> heroes; if (hero) heroes.push_back(hero.h); else { //heroes = ai->getUnblockedHeroes(); heroes = cb->getHeroesInfo(); vstd::erase_if(heroes, [](const HeroPtr h) { if (ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer return true; if (!ai->isAbleToExplore(h)) return true; return !h->movement; //saves time, immobile heroes are useless anyway }); } //try to use buildings that uncover map std::vector<const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if (!vstd::contains(ai->alreadyVisited, obj)) { switch (obj->ID.num) { case Obj::REDWOOD_OBSERVATORY: case Obj::PILLAR_OF_FIRE: case Obj::CARTOGRAPHER: objs.push_back (obj); break; case Obj::MONOLITH_ONE_WAY_ENTRANCE: case Obj::MONOLITH_TWO_WAY: case Obj::SUBTERRANEAN_GATE: auto tObj = dynamic_cast<const CGTeleport *>(obj); assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end()); if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability) objs.push_back (obj); break; } } else { switch (obj->ID.num) { case Obj::MONOLITH_TWO_WAY: case Obj::SUBTERRANEAN_GATE: auto tObj = dynamic_cast<const CGTeleport *>(obj); if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability) break; for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits) { if(!cb->getObj(exit)) { // Always attempt to visit two-way teleports if one of channel exits is not visible objs.push_back(obj); break; } } break; } } } for (auto h : heroes) { auto sm = ai->getCachedSectorMap(h); for (auto obj : objs) //double loop, performance risk? { auto t = sm->firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded if (ai->isTileNotReserved(h, t)) ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true))); } int3 t = whereToExplore(h); if (t.valid()) { ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); } else { ai->markHeroUnableToExplore (h); //there is no freely accessible tile, do not poll this hero anymore //possible issues when gathering army to break if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost { t = ai->explorationDesperate(h); if (t.valid()) //don't waste time if we are completely blocked ret.push_back (sptr(Goals::ClearWayTo(t, h).setisAbstract(true))); } } } //we either don't have hero yet or none of heroes can explore if ((!hero || ret.empty()) && ai->canRecruitAnyHero()) ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) { throw goalFulfilledException (sptr(Goals::Explore().sethero(hero))); } //throw cannotFulfillGoalException("Cannot explore - no possible ways found!"); return ret; }
TGoalVec Explore::getAllPossibleSubgoals() { TGoalVec ret; std::vector<const CGHeroInstance *> heroes; if (hero) heroes.push_back(hero.h); else { //heroes = ai->getUnblockedHeroes(); heroes = cb->getHeroesInfo(); erase_if (heroes, [](const HeroPtr h) { if (ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer return true; return !h->movement; //saves time, immobile heroes are useless anyway }); } //try to use buildings that uncover map std::vector<const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if (!vstd::contains(ai->alreadyVisited, obj)) { switch (obj->ID.num) { case Obj::REDWOOD_OBSERVATORY: case Obj::PILLAR_OF_FIRE: case Obj::CARTOGRAPHER: case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates objs.push_back (obj); } } } for (auto h : heroes) { SectorMap sm(h); for (auto obj : objs) //double loop, performance risk? { auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded if (ai->canReachTile(h, t)) ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true))); } int3 t = whereToExplore(h); if (t.valid()) { ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); } else if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost { t = ai->explorationDesperate(h); if (t.valid()) //don't waste time if we are completely blocked ret.push_back (sptr(Goals::ClearWayTo(t, h).setisAbstract(true))); } } //we either don't have hero yet or none of heroes can explore if ((!hero || ret.empty()) && ai->canRecruitAnyHero()) ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) { throw goalFulfilledException (sptr(Goals::Explore().sethero(hero))); } //throw cannotFulfillGoalException("Cannot explore - no possible ways found!"); return ret; }
TGoalVec GatherArmy::getAllPossibleSubgoals() { //get all possible towns, heroes and dwellings we may use TGoalVec ret; if(!hero.validAndSet()) { return ret; } //TODO: include evaluation of monsters gather in calculation for(auto t : cb->getTownsInfo()) { auto waysToVisit = ai->ah->howToVisitObj(hero, t); if(waysToVisit.size()) { //grab army from town if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t)) { if(!vstd::contains(ai->townVisitsThisWeek[hero], t)) vstd::concatenate(ret, waysToVisit); } //buy army in town if (!t->visitingHero || t->visitingHero == hero.get(true)) { std::vector<int> values = { value, (int)howManyReinforcementsCanBuy(t->getUpperArmy(), t), (int)howManyReinforcementsCanBuy(hero.get(), t) }; int val = *std::min_element(values.begin(), values.end()); if (val) { auto goal = sptr(BuyArmy(t, val).sethero(hero)); if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice ret.push_back(goal); else logAi->debug("Can not buy army, because of ai->ah->containsObjective"); } } //build dwelling //TODO: plan building over multiple turns? //auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)); //Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm /*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1); if (bid.is_initialized()) { auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority)); if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice ret.push_back(goal); else logAi->debug("Can not build a structure, because of ai->ah->containsObjective"); }*/ } } auto otherHeroes = cb->getHeroesInfo(); auto heroDummy = hero; vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h) { if(h == heroDummy.h) return true; else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true)) return true; else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue return true; else if(ai->getGoal(h)->goalType == GATHER_ARMY) return true; else return false; }); for(auto h : otherHeroes) { // Go to the other hero if we are faster if(!vstd::contains(ai->visitedHeroes[hero], h)) { vstd::concatenate(ret, ai->ah->howToVisitObj(hero, h)); } // Go to the other hero if we are faster if(!vstd::contains(ai->visitedHeroes[h], hero)) { vstd::concatenate(ret, ai->ah->howToVisitObj(h, hero.get())); } } std::vector<const CGObjectInstance *> objs; for(auto obj : ai->visitableObjs) { if(obj->ID == Obj::CREATURE_GENERATOR1) { auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID); //Use flagged dwellings only when there are available creatures that we can afford if(relationToOwner == PlayerRelations::SAME_PLAYER) { auto dwelling = dynamic_cast<const CGDwelling *>(obj); ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero.get(), dwelling)); if(val) { for(auto & creLevel : dwelling->creatures) { if(creLevel.first) { for(auto & creatureID : creLevel.second) { auto creature = VLC->creh->creatures[creatureID]; if(ai->ah->freeResources().canAfford(creature->cost)) objs.push_back(obj); //TODO: reserve resources? } } } } } } } for(auto h : cb->getHeroesInfo()) { for(auto obj : objs) { //find safe dwelling if(ai->isGoodForVisit(obj, h)) { vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj)); } } } if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game { if(auto t = ai->findTownWithTavern()) { for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes { if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy { ret.push_back(sptr(RecruitHero())); break; } } } } if(ret.empty()) { const bool allowGatherArmy = false; if(hero == ai->primaryHero()) ret.push_back(sptr(Explore(allowGatherArmy))); else throw cannotFulfillGoalException("No ways to gather army"); } return ret; }