mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Merge pull request #2229 from vcmi/zone_placement_improvements
Zone placement improvements
This commit is contained in:
commit
02ea798c97
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user