1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-28 23:06:24 +02:00

Merge pull request #2229 from vcmi/zone_placement_improvements

Zone placement improvements
This commit is contained in:
DjWarmonger 2023-06-17 08:53:08 +02:00 committed by GitHub
commit 02ea798c97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 44 deletions

View File

@ -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);

View File

@ -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);