Browse Source

- Removed slow and buggy part of exploration code
- Various tweaks and performance improvements. remove_if on long vector is a bad idea.

DjWarmonger 12 years ago
parent
commit
473250e223
5 changed files with 53 additions and 76 deletions
  1. 3 24
      AI/VCAI/AIUtility.cpp
  2. 3 1
      AI/VCAI/Fuzzy.cpp
  3. 22 24
      AI/VCAI/Goals.cpp
  4. 24 26
      AI/VCAI/VCAI.cpp
  5. 1 1
      lib/mapping/CMap.h

+ 3 - 24
AI/VCAI/AIUtility.cpp

@@ -184,12 +184,12 @@ bool compareDanger(const CGObjectInstance *lhs, const CGObjectInstance *rhs)
 bool isSafeToVisit(HeroPtr h, crint3 tile)
 {
 	const ui64 heroStrength = h->getTotalStrength(),
-		dangerStrength = evaluateDanger(tile, *h);
+				dangerStrength = evaluateDanger(tile, *h);
 	if(dangerStrength)
 	{
 		if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
 		{
-            logAi->debugStream() << boost::format("It's, safe for %s to visit tile %s") % h->name % tile;
+            logAi->traceStream() << boost::format("It's safe for %s to visit tile %s") % h->name % tile;
 			return true;
 		}
 		else
@@ -240,28 +240,7 @@ int3 whereToExplore(HeroPtr h)
 	catch(cannotFulfillGoalException &e)
 	{
 		std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
-		try
-		{
-			return ai->explorationNewPoint(radius, h, tiles);
-		}
-		catch(cannotFulfillGoalException &e)
-		{
-			std::map<int, std::vector<int3> > profits;
-			{
-				TimeCheck tc("Evaluating exploration possibilities");
-				tiles[0].clear(); //we can't reach FoW anyway
-				for(auto &vt : tiles)
-					for(auto &tile : vt)
-						profits[howManyTilesWillBeDiscovered(tile, radius)].push_back(tile);
-			}
-
-			if(profits.empty())
-				return int3 (-1,-1,-1);
-
-			auto bestDest = profits.end();
-			bestDest--;
-			return bestDest->second.front(); //TODO which is the real best tile?
-		}
+		return ai->explorationNewPoint(radius, h, tiles);
 	}
 }
 

+ 3 - 1
AI/VCAI/Fuzzy.cpp

@@ -351,7 +351,7 @@ void FuzzyHelper::initVisitTile()
 
 	helper += vt.strengthRatio, vt.heroStrength, vt.tileDistance, vt.missionImportance;
 
-	vt.strengthRatio->addTerm (new fl::ShoulderTerm("LOW", 0.3, SAFE_ATTACK_CONSTANT, true));
+	vt.strengthRatio->addTerm (new fl::ShoulderTerm("LOW", 0.9, SAFE_ATTACK_CONSTANT, true));
 	vt.strengthRatio->addTerm (new fl::ShoulderTerm("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3, false));
 
 	vt.heroStrength->addTerm (new fl::ShoulderTerm("LOW", 1, 2500, true)); //assumed strength of new hero from tavern
@@ -378,6 +378,8 @@ void FuzzyHelper::initVisitTile()
 	//vt.rules.addRule (new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is very LOW", engine));
 	//use unarmed scouts if possible
 	vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH", engine));
+	//don't assign targets to heroes who are too weak
+	vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is very LOW then Value is very LOW", engine));
 	//if medium heroes can't scratch enemy, don't try to arm them
 	vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is LOW", engine));
 	//do not cancel important goals

+ 22 - 24
AI/VCAI/Goals.cpp

@@ -273,9 +273,13 @@ TSubgoal VisitHero::whatToDoToAchieve()
 
 	if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
 	{
-		assert (hero->pos != pos); //don't try to visit yourself
-		settile(pos).setisElementar(true);
-		return sptr (*this);
+		if (hero->pos == pos)
+			logAi->errorStream() << "Hero " << hero.name << " tries to visit himself.";
+		else
+		{
+			settile(pos).setisElementar(true);
+			return sptr (*this);
+		}
 	}
 	return sptr (Goals::Invalid());
 }
@@ -395,28 +399,22 @@ TGoalVec Explore::getAllPossibleSubgoals()
 		});
 	}
 
-	auto objs = ai->visitableObjs; //try to use buildings that uncover map
-	erase_if(objs, [&](const CGObjectInstance *obj) -> bool
+	//try to use buildings that uncover map
+	std::vector<const CGObjectInstance *> objs;
+	for (auto obj : ai->visitableObjs)
 	{
-		if (vstd::contains(ai->alreadyVisited, obj))
-			return true;
-		switch (obj->ID.num)
+		if (!vstd::contains(ai->alreadyVisited, obj))
 		{
-			case Obj::REDWOOD_OBSERVATORY:
-			case Obj::PILLAR_OF_FIRE:
-			case Obj::CARTOGRAPHER:
-			case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates
-			//case Obj::MONOLITH1:
-			//case obj::MONOLITH2:
-			//case obj::MONOLITH3:
-			//case Obj::WHIRLPOOL:
-				return false; //do not erase
-				break;
-			default:
-				return true;
+			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)
 	{
 		for (auto obj : objs) //double loop, performance risk?
@@ -428,8 +426,8 @@ TGoalVec Explore::getAllPossibleSubgoals()
 		}
 
 		int3 t = whereToExplore(h);
-		if (t.z != -1) //no safe tile to explore - we need to break!
-		ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
+		if (t.z != -1) //no valid tile was found
+			ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
 	}
 	if (!hero && ai->canRecruitAnyHero())//if hero is assigned to that goal, no need to buy another one yet
 		ret.push_back (sptr(Goals::RecruitHero()));

+ 24 - 26
AI/VCAI/VCAI.cpp

@@ -1096,36 +1096,31 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
 	std::vector<const CGObjectInstance *> possibleDestinations;
 	for(const CGObjectInstance *obj : visitableObjs)
 	{
-		if(isAccessibleForHero(obj->visitablePos(), h) && !obj->wasVisited(playerID) &&
-			(obj->tempOwner != playerID || isWeeklyRevisitable(obj))) //flag or get weekly resources / creatures
+		const int3 pos = obj->visitablePos();
+		if (isAccessibleForHero(obj->visitablePos(), h) &&
+			!obj->wasVisited(playerID) &&
+			(obj->tempOwner != playerID || isWeeklyRevisitable(obj)) && //flag or get weekly resources / creatures
+			isSafeToVisit(h, pos) &&
+			shouldVisit(h, obj) &&
+			!vstd::contains(alreadyVisited, obj) &&
+			!vstd::contains(reservedObjs, obj))
+		{
 			possibleDestinations.push_back(obj);
+		}
 	}
 
-	boost::sort(possibleDestinations, isCloser);
-
 	possibleDestinations.erase(boost::remove_if(possibleDestinations, [&](const CGObjectInstance *obj) -> bool
 		{
-			const int3 pos = obj->visitablePos();
-			if(vstd::contains(alreadyVisited, obj))
-				return true;
-
-			if(!isSafeToVisit(h, pos))
-				return true;
-
-			if (!shouldVisit(h, obj))
-				return true;
-
-			if (vstd::contains(reservedObjs, obj)) //does checking for our own reserved objects make sense? here?
-				return true;
-
-			const CGObjectInstance *topObj = cb->getVisitableObjs(pos).back(); //it may be hero visiting this obj
+			const CGObjectInstance *topObj = cb->getVisitableObjs(obj->visitablePos()).back(); //it may be hero visiting this obj
 			//we don't try visiting object on which allied or owned hero stands
 			// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
 			if(topObj->ID == Obj::HERO  &&  cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
 				return true;
 
 			return false;
-		}),possibleDestinations.end());
+		}), possibleDestinations.end());
+
+	boost::sort(possibleDestinations, isCloser);
 
 	return possibleDestinations;
 }
@@ -2004,12 +1999,11 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
 	std::map<int3, int> dstToRevealedTiles;
 	for(crint3 dir : dirs)
 		if(cb->isInTheMap(hpos+dir))
-			dstToRevealedTiles[hpos + dir] = howManyTilesWillBeDiscovered(radius, hpos, dir) * isSafeToVisit(h, hpos + dir);
+			if (isSafeToVisit(h, hpos + dir) && isAccessibleForHero (hpos + dir, h))
+				dstToRevealedTiles[hpos + dir] = howManyTilesWillBeDiscovered(radius, hpos, dir);
 
 	auto best = dstToRevealedTiles.begin();
-	best->second *= cb->getPathInfo(best->first)->reachable();
-	best->second *= cb->getPathInfo(best->first)->accessible == CGPathNode::ACCESSIBLE;
-	for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++)
+	for (auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++)
 	{
 		const CGPathNode *pn = cb->getPathInfo(i->first);
 		//const TerrainTile *t = cb->getTile(i->first);
@@ -2042,13 +2036,17 @@ int3 VCAI::explorationNewPoint(int radius, HeroPtr h, std::vector<std::vector<in
 
 		for(const int3 &tile : tiles[i])
 		{
-			if(cb->getPathInfo(tile)->reachable() && isSafeToVisit(h, tile) && howManyTilesWillBeDiscovered(tile, radius) && !isBlockedBorderGate(tile))
+			if (cb->getTile(tile)->blocked) //does it shorten the time?
+				continue;
+			if(cb->getPathInfo(tile)->reachable() && howManyTilesWillBeDiscovered(tile, radius) &&
+				isSafeToVisit(h, tile) && !isBlockedBorderGate(tile))
 			{
-				return tile;
+				return tile; //return first tile that will discover anything
 			}
 		}
 	}
-	throw cannotFulfillGoalException("No accessible tile will bring discoveries!");
+	return int3 (-1,-1,-1);
+	//throw cannotFulfillGoalException("No accessible tile will bring discoveries!");
 }
 
 TResources VCAI::estimateIncome() const

+ 1 - 1
lib/mapping/CMap.h

@@ -354,7 +354,6 @@ public:
 	void checkForObjectives();
 
 	ui32 checksum;
-	/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
 	std::vector<Rumor> rumors;
 	std::vector<DisposedHero> disposedHeroes;
 	std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes;
@@ -383,6 +382,7 @@ public:
 	unique_ptr<CMapEditManager> editManager;
 
 private:
+	/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
 	TerrainTile*** terrain;
 
 public: