浏览代码

Merge pull request #2229 from vcmi/zone_placement_improvements

Zone placement improvements
DjWarmonger 2 年之前
父节点
当前提交
02ea798c97
共有 2 个文件被更改,包括 71 次插入44 次删除
  1. 46 30
      lib/rmg/CZonePlacer.cpp
  2. 25 14
      lib/rmg/modificators/ConnectionsPlacer.cpp

+ 46 - 30
lib/rmg/CZonePlacer.cpp

@@ -326,8 +326,43 @@ void CZonePlacer::placeZones(CRandomGenerator * rand)
 	TDistanceVector distances;
 	TDistanceVector overlaps;
 
+	auto evaluateSolution = [this, zones, &distances, &overlaps, &bestSolution]() -> bool
+	{
+		bool improvement = false;
+
+		float totalDistance = 0;
+		float totalOverlap = 0;
+		for (const auto& zone : distances) //find most misplaced zone
+		{
+			totalDistance += zone.second;
+			float overlap = overlaps[zone.first];
+			totalOverlap += overlap;
+		}
+
+		//check fitness function
+		if ((totalDistance + 1) * (totalOverlap + 1) < (bestTotalDistance + 1) * (bestTotalOverlap + 1))
+		{
+			//multiplication is better for auto-scaling, but stops working if one factor is 0
+			improvement = true;
+		}
+
+		//Save best solution
+		if (improvement)
+		{
+			bestTotalDistance = totalDistance;
+			bestTotalOverlap = totalOverlap;
+
+			for (const auto& zone : zones)
+				bestSolution[zone.second] = zone.second->getCenter();
+		}
+
+		logGlobal->trace("Total distance between zones after this iteration: %2.4f, Total overlap: %2.4f, Improved: %s", totalDistance, totalOverlap , improvement);
+
+		return improvement;
+	};
+
 	 //Start with low stiffness. Bigger graphs need more time and more flexibility
-	for (stifness = stiffnessConstant / zones.size(); stifness <= stiffnessConstant; stifness *= stiffnessIncreaseFactor)
+	for (stifness = stiffnessConstant / zones.size(); stifness <= stiffnessConstant;)
 	{
 		//1. attract connected zones
 		attractConnectedZones(zones, forces, distances);
@@ -345,42 +380,23 @@ void CZonePlacer::placeZones(CRandomGenerator * rand)
 			totalForces[zone.first] += zone.second; //accumulate
 		}
 
-		//3. now perform drastic movement of zone that is completely not linked
-
-		moveOneZone(zones, totalForces, distances, overlaps);
-
-		//4. NOW after everything was moved, re-evaluate zone positions
-		attractConnectedZones(zones, forces, distances);
-		separateOverlappingZones(zones, forces, overlaps);
+		bool improved = evaluateSolution();
 
-		float totalDistance = 0;
-		float totalOverlap = 0;
-		for(const auto & zone : distances) //find most misplaced zone
+		if (!improved)
 		{
-			totalDistance += zone.second;
-			float overlap = overlaps[zone.first];
-			totalOverlap += overlap;
-		}
+			//3. now perform drastic movement of zone that is completely not linked
+			//TODO: Don't do this is fitness was improved
+			moveOneZone(zones, totalForces, distances, overlaps);
 
-		//check fitness function
-		bool improvement = false;
-		if ((totalDistance + 1) * (totalOverlap + 1) < (bestTotalDistance + 1) * (bestTotalOverlap + 1))
-		{
-			//multiplication is better for auto-scaling, but stops working if one factor is 0
-			improvement = true;
+			improved |= evaluateSolution();;
 		}
 
-		logGlobal->trace("Total distance between zones after this iteration: %2.4f, Total overlap: %2.4f, Improved: %s", totalDistance, totalOverlap , improvement);
-
-		//save best solution
-		if (improvement)
+		if (!improved)
 		{
-			bestTotalDistance = totalDistance;
-			bestTotalOverlap = totalOverlap;
-
-			for(const auto & zone : zones)
-				bestSolution[zone.second] = zone.second->getCenter();
+			//Only cool down if we didn't see any improvement
+			stifness *= stiffnessIncreaseFactor;
 		}
+
 	}
 
 	logGlobal->trace("Best fitness reached: total distance %2.4f, total overlap %2.4f", bestTotalDistance, bestTotalOverlap);

+ 25 - 14
lib/rmg/modificators/ConnectionsPlacer.cpp

@@ -106,34 +106,45 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
 	bool directProhibited = vstd::contains(ourTerrain->prohibitTransitions, otherZone->getTerrainType())
 						 || vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType());
 	auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
+
+	float maxDist = -10e6;
 	if(!directProhibited && directConnectionIterator != dNeighbourZones.end())
 	{
 		int3 guardPos(-1, -1, -1);
-		int3 borderPos;
-		while(!directConnectionIterator->second.empty())
+		int3 roadNode;
+		for (auto borderPos : directConnectionIterator->second)
 		{
-			borderPos = *RandomGeneratorUtil::nextItem(directConnectionIterator->second, zone.getRand());
-			guardPos = zone.areaPossible().nearest(borderPos);
-			assert(borderPos != guardPos);
+			int3 potentialPos = zone.areaPossible().nearest(borderPos);
+			assert(borderPos != potentialPos);
 
-			float dist = map.getTileInfo(guardPos).getNearestObjectDistance();
-			if (dist >= 3) //Don't place guards at adjacent tiles
+			//Take into account distance to objects from both sides
+			float dist = std::min(map.getTileInfo(potentialPos).getNearestObjectDistance(),
+				map.getTileInfo(borderPos).getNearestObjectDistance());
+			if (dist > 3) //Don't place guards at adjacent tiles
 			{
 
-				auto safetyGap = rmg::Area({ guardPos });
+				auto safetyGap = rmg::Area({ potentialPos });
 				safetyGap.unite(safetyGap.getBorderOutside());
 				safetyGap.intersect(zone.areaPossible());
 				if (!safetyGap.empty())
 				{
 					safetyGap.intersect(otherZone->areaPossible());
 					if (safetyGap.empty())
-						break; //successfull position
+					{
+						float distanceToCenter = zone.getPos().dist2d(potentialPos) * otherZone->getPos().dist2d(potentialPos);
+
+						auto localDist = (dist - distanceToCenter) * //Prefer close to zone center
+							(std::max(distanceToCenter, dist) / std::min(distanceToCenter, dist));
+						//Distance to center dominates and is negative, so imbalanced proportions will result in huge penalty
+						if (localDist > maxDist)
+						{
+							maxDist = localDist;
+							guardPos = potentialPos;
+							roadNode = borderPos;
+						}
+					}
 				}
 			}
-			
-			//failed position
-			directConnectionIterator->second.erase(borderPos);
-			guardPos = int3(-1, -1, -1);
 		}
 		
 		if(guardPos.valid())
@@ -184,7 +195,7 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
 				zone.getModificator<RoadPlacer>()->addRoadNode(guardPos);
 				
 				assert(otherZone->getModificator<RoadPlacer>());
-				otherZone->getModificator<RoadPlacer>()->addRoadNode(borderPos);
+				otherZone->getModificator<RoadPlacer>()->addRoadNode(roadNode);
 				
 				assert(otherZone->getModificator<ConnectionsPlacer>());
 				otherZone->getModificator<ConnectionsPlacer>()->otherSideConnection(connection);