1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Add another placement technique - swap two misplaced zones. Don't move same zones in consecutive iterations.

This commit is contained in:
Tomasz Zieliński 2023-04-20 12:24:57 +02:00
parent c34b1cd713
commit 00d7901e59
2 changed files with 113 additions and 59 deletions

View File

@ -277,7 +277,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand)
auto zone = grid[x][y]; auto zone = grid[x][y];
if (zone) if (zone)
{ {
//i.e. for grid size 5 we get range (0.5 - 4.5) //i.e. for grid size 5 we get range (0.25 - 4.75)
auto targetX = rand->nextDouble(x + 0.25f, x + 0.75f); auto targetX = rand->nextDouble(x + 0.25f, x + 0.75f);
vstd::abetween(targetX, 0.5, gridSize - 0.5); vstd::abetween(targetX, 0.5, gridSize - 0.5);
auto targetY = rand->nextDouble(y + 0.25f, y + 0.75f); auto targetY = rand->nextDouble(y + 0.25f, y + 0.75f);
@ -597,37 +597,86 @@ void CZonePlacer::separateOverlappingZones(TZoneMap &zones, TForceVector &forces
} }
} }
void CZonePlacer::moveOneZone(TZoneMap & zones, TForceVector & totalForces, TDistanceVector & distances, TDistanceVector & overlaps) const void CZonePlacer::moveOneZone(TZoneMap& zones, TForceVector& totalForces, TDistanceVector& distances, TDistanceVector& overlaps)
{ {
float maxRatio = 0; const int maxDistanceMovementRatio = zones.size() * zones.size(); //The more zones, the greater total distance expected
const int maxDistanceMovementRatio = 1e1 * static_cast<int>(zones.size() * zones.size()); //experimental - the more zones, the greater total distance expected
std::shared_ptr<Zone> misplacedZone; typedef std::pair<float, std::shared_ptr<Zone>> Misplacement;
std::vector<Misplacement> misplacedZones;
float totalDistance = 0; float totalDistance = 0;
float totalOverlap = 0; float totalOverlap = 0;
for (const auto& zone : distances) //find most misplaced zone for (const auto& zone : distances) //find most misplaced zone
{ {
if (vstd::contains(lastSwappedZones, zone.first->getId()))
{
continue;
}
totalDistance += zone.second; totalDistance += zone.second;
float overlap = overlaps[zone.first]; float overlap = overlaps[zone.first];
totalOverlap += overlap; totalOverlap += overlap;
//if distance to actual movement is long, the zone is misplaced //if distance to actual movement is long, the zone is misplaced
float ratio = (zone.second + overlap) / static_cast<float>(totalForces[zone.first].mag()); float ratio = (zone.second + overlap) / static_cast<float>(totalForces[zone.first].mag());
if (ratio > maxRatio) if (ratio > maxDistanceMovementRatio)
{ {
maxRatio = ratio; misplacedZones.emplace_back(std::make_pair(ratio, zone.first));
misplacedZone = zone.first;
} }
} }
logGlobal->trace("Worst misplacement/movement ratio: %3.2f", maxRatio);
if (maxRatio > maxDistanceMovementRatio && misplacedZone) if (misplacedZones.empty())
return;
boost::sort(misplacedZones, [](const Misplacement& lhs, Misplacement& rhs)
{ {
return lhs.first > rhs.first; //Biggest first
});
logGlobal->trace("Worst misplacement/movement ratio: %3.2f", misplacedZones.front().first);
if (misplacedZones.size() >= 2)
{
//Swap 2 misplaced zones
auto firstZone = misplacedZones.front().second;
std::shared_ptr<Zone> secondZone;
auto level = firstZone->getCenter().z;
for (size_t i = 1; i < misplacedZones.size(); i++)
{
//Only swap zones on the same level
//Don't swap zones that should be connected (Jebus)
if (misplacedZones[i].second->getCenter().z == level &&
!vstd::contains(firstZone->getConnections(), misplacedZones[i].second->getId()))
{
secondZone = misplacedZones[i].second;
break;
}
}
if (secondZone)
{
logGlobal->trace("Swapping two misplaced zones %d and %d", firstZone->getId(), secondZone->getId());
auto firstCenter = firstZone->getCenter();
auto secondCenter = secondZone->getCenter();
firstZone->setCenter(secondCenter);
secondZone->setCenter(firstCenter);
lastSwappedZones.insert(firstZone->getId());
lastSwappedZones.insert(secondZone->getId());
return;
}
}
lastSwappedZones.clear(); //If we didn't swap zones in this iteration, we can do it in the next
//find most distant zone that should be attracted and move inside it
std::shared_ptr<Zone> targetZone; std::shared_ptr<Zone> targetZone;
auto misplacedZone = misplacedZones.front().second;
float3 ourCenter = misplacedZone->getCenter(); float3 ourCenter = misplacedZone->getCenter();
if (totalDistance > totalOverlap) if (totalDistance > totalOverlap)
{ {
//find most distant zone that should be attracted and move inside it //Move one zone towards most distant zone to reduce distance
float maxDistance = 0; float maxDistance = 0;
for (auto con : misplacedZone->getConnections()) for (auto con : misplacedZone->getConnections())
{ {
@ -652,6 +701,9 @@ void CZonePlacer::moveOneZone(TZoneMap & zones, TForceVector & totalForces, TDis
} }
else else
{ {
//Move misplaced zone away from overlapping zone
//FIXME: Does that ever happend? Check the number ranges and rescale if needed
float maxOverlap = 0; float maxOverlap = 0;
for(const auto & otherZone : zones) for(const auto & otherZone : zones)
{ {
@ -678,7 +730,8 @@ void CZonePlacer::moveOneZone(TZoneMap & zones, TForceVector & totalForces, TDis
logGlobal->trace("New distance %f", targetZone->getCenter().dist2d(misplacedZone->getCenter())); logGlobal->trace("New distance %f", targetZone->getCenter().dist2d(misplacedZone->getCenter()));
} }
} }
} //Don't swap that zone in next iteration
lastSwappedZones.insert(misplacedZone->getId());
} }
float CZonePlacer::metric (const int3 &A, const int3 &B) const float CZonePlacer::metric (const int3 &A, const int3 &B) const

View File

@ -45,7 +45,7 @@ private:
void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand); void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand);
void attractConnectedZones(TZoneMap & zones, TForceVector & forces, TDistanceVector & distances) const; void attractConnectedZones(TZoneMap & zones, TForceVector & forces, TDistanceVector & distances) const;
void separateOverlappingZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &overlaps); void separateOverlappingZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &overlaps);
void moveOneZone(TZoneMap & zones, TForceVector & totalForces, TDistanceVector & distances, TDistanceVector & overlaps) const; void moveOneZone(TZoneMap & zones, TForceVector & totalForces, TDistanceVector & distances, TDistanceVector & overlaps);
private: private:
int width; int width;
@ -65,6 +65,7 @@ private:
//distance [a][b] = number of zone connections required to travel between the zones //distance [a][b] = number of zone connections required to travel between the zones
std::map<int, std::map<int, size_t>> distancesBetweenZones; std::map<int, std::map<int, size_t>> distancesBetweenZones;
std::set<TRmgTemplateZoneId> lastSwappedZones;
RmgMap & map; RmgMap & map;
}; };