mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #2229 from vcmi/zone_placement_improvements
Zone placement improvements
This commit is contained in:
		| @@ -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 | ||||
| 		bool improved = evaluateSolution(); | ||||
|  | ||||
| 		moveOneZone(zones, totalForces, distances, overlaps); | ||||
|  | ||||
| 		//4. NOW after everything was moved, re-evaluate zone positions | ||||
| 		attractConnectedZones(zones, forces, distances); | ||||
| 		separateOverlappingZones(zones, forces, overlaps); | ||||
|  | ||||
| 		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); | ||||
|  | ||||
| 			improved |= evaluateSolution();; | ||||
| 		} | ||||
|  | ||||
| 		//check fitness function | ||||
| 		bool improvement = false; | ||||
| 		if ((totalDistance + 1) * (totalOverlap + 1) < (bestTotalDistance + 1) * (bestTotalOverlap + 1)) | ||||
| 		if (!improved) | ||||
| 		{ | ||||
| 			//multiplication is better for auto-scaling, but stops working if one factor is 0 | ||||
| 			improvement = true; | ||||
| 			//Only cool down if we didn't see any improvement | ||||
| 			stifness *= stiffnessIncreaseFactor; | ||||
| 		} | ||||
|  | ||||
| 		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) | ||||
| 		{ | ||||
| 			bestTotalDistance = totalDistance; | ||||
| 			bestTotalOverlap = totalOverlap; | ||||
|  | ||||
| 			for(const auto & zone : zones) | ||||
| 				bestSolution[zone.second] = zone.second->getCenter(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	logGlobal->trace("Best fitness reached: total distance %2.4f, total overlap %2.4f", bestTotalDistance, bestTotalOverlap); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user