1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

- Implemented serialization of Goals. Loaded games work, at least.

- Attempts to add Subterranean Gates to SectorMap
- Extensive use of ClearWayTo goal.
This commit is contained in:
DjWarmonger 2014-02-20 20:18:49 +00:00
parent 8a9f2781c2
commit b0b0249524
6 changed files with 164 additions and 85 deletions

View File

@ -480,6 +480,32 @@ float FuzzyHelper::evaluate (Goals::GatherArmy & g)
float army = g.hero->getArmyStrength();
return g.value / std::max(g.value - army, 1000.0f);
}
float FuzzyHelper::evaluate (Goals::ClearWayTo & g)
{
if (!g.hero.h)
throw cannotFulfillGoalException("ClearWayTo called without hero!");
SectorMap sm(g.hero);
int3 t = sm.firstTileToGet(g.hero, g.tile);
if (t.valid())
{
if (isSafeToVisit(g.hero, t))
{
g.setpriority(Goals::VisitTile(g.tile).sethero(g.hero).setisAbstract(g.isAbstract).accept(this));
}
else
{
g.setpriority (Goals::GatherArmy(evaluateDanger(t, g.hero.h)*SAFE_ATTACK_CONSTANT).
sethero(g.hero).setisAbstract(true).accept(this));
}
return g.priority;
}
else
return -1;
}
float FuzzyHelper::evaluate (Goals::BuildThis & g)
{
return 1;

View File

@ -67,6 +67,7 @@ public:
float evaluate (Goals::CollectRes & g);
float evaluate (Goals::Build & g);
float evaluate (Goals::GatherArmy & g);
float evaluate (Goals::ClearWayTo & g);
float evaluate (Goals::Invalid & g);
float evaluate (Goals::AbstractGoal & g);
void setPriority (Goals::TSubgoal & g);

View File

@ -452,16 +452,28 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
TGoalVec ClearWayTo::getAllPossibleSubgoals()
{
TGoalVec ret;
for (auto h : cb->getHeroesInfo())
std::vector<const CGHeroInstance *> heroes;
if (hero)
heroes.push_back(hero.h);
else
{
if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
h->visitablePos() == tile) //we are already on that tile! what does it mean?
continue;
heroes = cb->getHeroesInfo();
}
for (auto h : heroes)
{
//TODO: handle clearing way to allied heroes that are blocked
//if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
// h->visitablePos() == tile) //we are already on that tile! what does it mean?
// continue;
//if our hero is trapped, make sure we request clearing the way from OUR perspective
SectorMap sm(h);
int3 tileToHit = sm.firstTileToGet(hero ? hero : h, tile);
//if our hero is trapped, make sure we request clearing the way from OUR perspective
int3 tileToHit = sm.firstTileToGet(h, tile);
if (!tileToHit.valid())
continue;
@ -471,10 +483,13 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
}
auto topObj = cb->getTopObj(tileToHit);
if(topObj)
if (topObj)
{
if (vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[h], topObj))
continue; //do not capure object reserved by other hero
if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
if (topObj != hero.get(true)) //the hero we wnat to free
if (topObj != hero.get(true)) //the hero we want to free
logAi->errorStream() << boost::format("%s stands in the way of %s") % topObj->getHoverText() % h->getHoverText();
if (topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
{
@ -482,6 +497,7 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
{
//do NOT use VISIT_TILE, as tile with quets guard can't be visited
ret.push_back (sptr (Goals::GetObj(topObj->id.getNum()).sethero(h)));
continue; //do not try to visit tile or gather army
}
else
{
@ -490,8 +506,16 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
}
}
}
else
if (isSafeToVisit(h, tileToHit)) //this makes sense only if tile is guarded, but there i no quest object
{
ret.push_back (sptr (Goals::VisitTile(tileToHit).sethero(h)));
}
else
{
ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(tileToHit, h)*SAFE_ATTACK_CONSTANT).
sethero(h).setisAbstract(true)));
}
}
if (ai->canRecruitAnyHero())
ret.push_back (sptr (Goals::RecruitHero()));
@ -563,10 +587,12 @@ TGoalVec Explore::getAllPossibleSubgoals()
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
ai->whatToDoToReachTile(h, t, ret);
if (ai->canReachTile(h, t))
ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
}
int3 t = whereToExplore(h);
@ -577,33 +603,14 @@ TGoalVec Explore::getAllPossibleSubgoals()
else if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost
{
t = ai->explorationDesperate(h);
ai->whatToDoToReachTile(h, t, ret);
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())
//{
// for (auto h : heroes) //this is costly function, use only when there is no other way
// {
// auto t = ai->explorationDesperate (h); //we assume that no more than one tile on the way is guarded
// if (t.valid())
// {
// if (isSafeToVisit(h, t))
// {
// ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
// }
// else
// {
// ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT).
// sethero(h).setisAbstract(true)));
// }
// }
// }
//}
if (ret.empty())
{
throw goalFulfilledException (sptr(Goals::Explore().sethero(hero)));
@ -886,7 +893,8 @@ TGoalVec Conquer::getAllPossibleSubgoals()
for (auto obj : ourObjs) //double loop, performance risk?
{
auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded
ai->whatToDoToReachTile (h, t, ret);
if (ai->canReachTile(h, t))
ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true)));
}
}
if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture

View File

@ -193,7 +193,7 @@ class NotLose : public CGoal<NotLose>
public:
NotLose() : CGoal (Goals::DO_NOT_LOSE) {priority = 100;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
//TSubgoal whatToDoToAchieve() override;
};
class Conquer : public CGoal<Conquer>
{
@ -221,9 +221,9 @@ class Explore : public CGoal<Explore>
};
class GatherArmy : public CGoal<GatherArmy>
{
private:
GatherArmy() : CGoal (Goals::GATHER_ARMY){};
public:
GatherArmy() : CGoal (Goals::GATHER_ARMY){};
GatherArmy(int val) : CGoal (Goals::GATHER_ARMY){value = val; priority = 2.5;};
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
@ -234,7 +234,7 @@ class BoostHero : public CGoal<BoostHero>
public:
BoostHero() : CGoal (Goals::INVALID){priority = -1e10;}; //TODO
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
};
class RecruitHero : public CGoal<RecruitHero>
{
@ -245,9 +245,9 @@ class RecruitHero : public CGoal<RecruitHero>
};
class BuildThis : public CGoal<BuildThis>
{
private:
BuildThis() : CGoal (Goals::BUILD_STRUCTURE){};
public:
BuildThis() : CGoal (Goals::BUILD_STRUCTURE){}; //FIXME: should be not allowed (private)
BuildThis(BuildingID Bid, const CGTownInstance *tid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid; town = tid; priority = 5;};
BuildThis(BuildingID Bid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid; priority = 5;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
@ -255,27 +255,27 @@ public:
};
class CollectRes : public CGoal<CollectRes>
{
private:
public:
CollectRes() : CGoal (Goals::COLLECT_RES){};
public:
CollectRes(int rid, int val) : CGoal (Goals::COLLECT_RES) {resID = rid; value = val; priority = 2;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
};
class GatherTroops : public CGoal<GatherTroops>
{
private:
GatherTroops() : CGoal (Goals::GATHER_TROOPS){priority = 2;};
public:
GatherTroops() : CGoal (Goals::GATHER_TROOPS){priority = 2;};
GatherTroops(int type, int val) : CGoal (Goals::GATHER_TROOPS){objid = type; value = val; priority = 2;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
};
class GetObj : public CGoal<GetObj>
{
private:
GetObj() {}; // empty constructor not allowed
public:
GetObj() {}; // empty constructor not allowed
GetObj(int Objid) : CGoal(Goals::GET_OBJ) {objid = Objid; priority = 3;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
@ -285,9 +285,9 @@ public:
};
class FindObj : public CGoal<FindObj>
{
private:
FindObj() {}; // empty constructor not allowed
public:
FindObj() {}; // empty constructor not allowed
FindObj(int ID) : CGoal(Goals::FIND_OBJ) {objid = ID; priority = 1;};
FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {objid = ID; resID = subID; priority = 1;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
@ -295,9 +295,9 @@ public:
};
class VisitHero : public CGoal<VisitHero>
{
private:
VisitHero() : CGoal (Goals::VISIT_HERO){};
public:
VisitHero() : CGoal (Goals::VISIT_HERO){};
VisitHero(int hid) : CGoal (Goals::VISIT_HERO){objid = hid; priority = 4;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
@ -307,9 +307,9 @@ public:
};
class GetArtOfType : public CGoal<GetArtOfType>
{
private:
GetArtOfType() : CGoal (Goals::GET_ART_TYPE){};
public:
GetArtOfType() : CGoal (Goals::GET_ART_TYPE){};
GetArtOfType(int type) : CGoal (Goals::GET_ART_TYPE){aid = type; priority = 2;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
@ -317,9 +317,9 @@ public:
class VisitTile : public CGoal<VisitTile>
//tile, in conjunction with hero elementar; assumes tile is reachable
{
private:
VisitTile() {}; // empty constructor not allowed
public:
VisitTile() {}; // empty constructor not allowed
VisitTile(int3 Tile) : CGoal (Goals::VISIT_TILE) {tile = Tile; priority = 5;};
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
@ -329,7 +329,10 @@ public:
class ClearWayTo : public CGoal<ClearWayTo>
{
public:
ClearWayTo() : CGoal (Goals::CLEAR_WAY_TO){};
ClearWayTo(int3 Tile) : CGoal (Goals::CLEAR_WAY_TO) {tile = Tile; priority = 5;};
ClearWayTo(int3 Tile, HeroPtr h) : CGoal (Goals::CLEAR_WAY_TO) {tile = Tile; hero = h; priority = 5;};
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
bool operator== (ClearWayTo &g) {return g.tile == tile;}
@ -337,9 +340,9 @@ public:
class DigAtTile : public CGoal<DigAtTile>
//elementar with hero on tile
{
private:
DigAtTile() : CGoal (Goals::DIG_AT_TILE){};
public:
DigAtTile() : CGoal (Goals::DIG_AT_TILE){};
DigAtTile(int3 Tile) : CGoal (Goals::DIG_AT_TILE) {tile = Tile; priority = 20;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
@ -351,9 +354,11 @@ class CIssueCommand : public CGoal<CIssueCommand>
std::function<bool()> command;
public:
CIssueCommand(): CGoal(ISSUE_COMMAND){};
CIssueCommand(std::function<bool()> _command): CGoal(ISSUE_COMMAND), command(_command) {priority = 1e10;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
TSubgoal whatToDoToAchieve() override;
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
};
}

View File

@ -598,6 +598,8 @@ void VCAI::saveGame(COSer<CSaveFile> &h, const int version)
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
NET_EVENT_HANDLER;
validateVisitableObjs();
registerGoals(h);
CAdventureAI::saveGame(h, version);
serializeInternal(h, version);
}
@ -606,6 +608,8 @@ void VCAI::loadGame(CISer<CLoadFile> &h, const int version)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
NET_EVENT_HANDLER;
registerGoals(h);
CAdventureAI::loadGame(h, version);
serializeInternal(h, version);
}
@ -1215,24 +1219,18 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
return possibleDestinations;
}
void VCAI::whatToDoToReachTile (const CGHeroInstance * h, int3 t, Goals::TGoalVec& vec)
///TODO: possibly merge with Goals::ClearWayTo
bool VCAI::canReachTile (const CGHeroInstance * h, int3 t)
{
if (t.valid())
{
auto obj = cb->getTopObj(t);
if (obj && vstd::contains(ai->reservedObjs, obj) && !vstd::contains(reservedHeroesMap[h], obj))
return; //do not capture object reserved by another hero
if (isSafeToVisit(h, t))
{
vec.push_back (sptr (Goals::VisitTile(t).sethero(h)));
}
return false; //do not capture object reserved by another hero
else
{
vec.push_back (sptr (Goals::GatherArmy(evaluateDanger(t, h)*SAFE_ATTACK_CONSTANT).
sethero(h).setisAbstract(true)));
}
return true;
}
else
return false;
}
bool VCAI::canRecruitAnyHero (const CGTownInstance * t) const
@ -2709,8 +2707,16 @@ void SectorMap::exploreNewSector(crint3 pos, int num)
s.embarkmentPoints.push_back(neighPos);
}
});
if(t->visitable && vstd::contains(ai->knownSubterraneanGates, t->visitableObjects.front()))
toVisit.push(ai->knownSubterraneanGates[t->visitableObjects.front()]->visitablePos());
if(t->visitable)
{
auto obj = t->visitableObjects.front();
if (vstd::contains(ai->knownSubterraneanGates, obj))
{ //not really sure what does it do, but subtrranean gates do not make one sector
toVisit.push(ai->knownSubterraneanGates[obj]->visitablePos());
s.subterraneanGates.push_back (obj);
}
}
}
}
}
@ -2893,7 +2899,20 @@ For ship construction etc, another function (goal?) is needed
}
}
//TODO consider other types of connections between sectors?
for (auto gate : s->subterraneanGates)
{
auto gatePair = ai->knownSubterraneanGates.find(gate);
if (gatePair != ai->knownSubterraneanGates.end())
{
//check the other side of gate
Sector *neigh = &infoOnSectors[retreiveTile(gatePair->second->visitablePos())];
if(!preds[neigh]) //if we didn't come into this sector yet
{
preds[neigh] = s; //it becomes our new target sector
sq.push(neigh);
}
}
}
}
if(!preds[dst])
@ -3001,10 +3020,19 @@ For ship construction etc, another function (goal?) is needed
//disembark
return ret;
}
else
else //use subterranean gates
{
auto firstGate = boost::find_if(src->subterraneanGates, [=](const CGObjectInstance * gate) -> bool
{
return retreiveTile(gate->visitablePos()) == sectorToReach->id;
});
if(firstGate != src->subterraneanGates.end())
{
return (*firstGate)->visitablePos();
}
//TODO
//transition between two land/water sectors. Monolith? Whirlpool? ...
//Monolith? Whirlpool? ...
return ret;
//throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
}
@ -3078,19 +3106,6 @@ void SectorMap::makeParentBFS(crint3 source)
}
}
});
//this code is unused, as tiles on both sides of game are different sectors
//const TerrainTile *t = cb->getTile(curPos);
//if(t->topVisitableId() == Obj::SUBTERRANEAN_GATE)
//{
// //try finding the exit gate
// auto it = ai->knownSubterraneanGates.find(t->topVisitableObj());
// if (it != ai->knownSubterraneanGates.end())
// {
// const int3 outPos = it->second->visitablePos();
// parent[outPos] = curPos; //TODO: is it only one tile?
// }
//}
}
}

View File

@ -83,6 +83,7 @@ struct SectorMap
int id;
std::vector<int3> tiles;
std::vector<int3> embarkmentPoints; //tiles of other sectors onto which we can (dis)embark
std::vector<const CGObjectInstance *> subterraneanGates;
bool water; //all tiles of sector are land or water
Sector()
{
@ -180,7 +181,7 @@ public:
int3 explorationBestNeighbour(int3 hpos, int radius, HeroPtr h);
int3 explorationNewPoint(HeroPtr h);
int3 explorationDesperate(HeroPtr h);
void whatToDoToReachTile (const CGHeroInstance * h, int3 t, Goals::TGoalVec& vec);
bool canReachTile (const CGHeroInstance * h, int3 t);
void recruitHero();
virtual std::string getBattleAIName() const override;
@ -312,6 +313,29 @@ public:
//special function that can be called ONLY from game events handling thread and will send request ASAP
void requestActionASAP(std::function<void()> whatToDo);
template <typename Handler> void registerGoals(Handler &h)
{
//h.registerType<Goals::AbstractGoal, Goals::BoostHero>();
h.registerType<Goals::AbstractGoal, Goals::Build>();
h.registerType<Goals::AbstractGoal, Goals::BuildThis>();
//h.registerType<Goals::AbstractGoal, Goals::CIssueCommand>();
h.registerType<Goals::AbstractGoal, Goals::ClearWayTo>();
h.registerType<Goals::AbstractGoal, Goals::CollectRes>();
h.registerType<Goals::AbstractGoal, Goals::Conquer>();
h.registerType<Goals::AbstractGoal, Goals::DigAtTile>();
h.registerType<Goals::AbstractGoal, Goals::Explore>();
h.registerType<Goals::AbstractGoal, Goals::FindObj>();
h.registerType<Goals::AbstractGoal, Goals::GatherArmy>();
h.registerType<Goals::AbstractGoal, Goals::GatherTroops>();
h.registerType<Goals::AbstractGoal, Goals::GetArtOfType>();
h.registerType<Goals::AbstractGoal, Goals::GetObj>();
h.registerType<Goals::AbstractGoal, Goals::Invalid>();
//h.registerType<Goals::AbstractGoal, Goals::NotLose>();
h.registerType<Goals::AbstractGoal, Goals::RecruitHero>();
h.registerType<Goals::AbstractGoal, Goals::VisitHero>();
h.registerType<Goals::AbstractGoal, Goals::VisitTile>();
h.registerType<Goals::AbstractGoal, Goals::Win>();
}
template <typename Handler> void serializeInternal(Handler &h, const int version)
{