1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +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];
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);
vstd::abetween(targetX, 0.5, gridSize - 0.5);
auto targetY = rand->nextDouble(y + 0.25f, y + 0.75f);
@ -597,88 +597,141 @@ 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 = 1e1 * static_cast<int>(zones.size() * zones.size()); //experimental - the more zones, the greater total distance expected
std::shared_ptr<Zone> misplacedZone;
const int maxDistanceMovementRatio = zones.size() * zones.size(); //The more zones, the greater total distance expected
typedef std::pair<float, std::shared_ptr<Zone>> Misplacement;
std::vector<Misplacement> misplacedZones;
float totalDistance = 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;
float overlap = overlaps[zone.first];
totalOverlap += overlap;
//if distance to actual movement is long, the zone is misplaced
float ratio = (zone.second + overlap) / static_cast<float>(totalForces[zone.first].mag());
if (ratio > maxRatio)
if (ratio > maxDistanceMovementRatio)
{
maxRatio = ratio;
misplacedZone = zone.first;
misplacedZones.emplace_back(std::make_pair(ratio, 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)
{
std::shared_ptr<Zone> targetZone;
float3 ourCenter = misplacedZone->getCenter();
return lhs.first > rhs.first; //Biggest first
});
if (totalDistance > totalOverlap)
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++)
{
//find most distant zone that should be attracted and move inside it
float maxDistance = 0;
for (auto con : misplacedZone->getConnections())
//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()))
{
auto otherZone = zones[con];
float distance = static_cast<float>(otherZone->getCenter().dist2dSQ(ourCenter));
if (distance > maxDistance)
{
maxDistance = distance;
targetZone = otherZone;
}
}
if (targetZone) //TODO: consider refactoring duplicated code
{
float3 vec = targetZone->getCenter() - ourCenter;
float newDistanceBetweenZones = (std::max(misplacedZone->getSize(), targetZone->getSize())) / mapSize;
logGlobal->trace("Trying to move zone %d %s towards %d %s. Old distance %f", misplacedZone->getId(), ourCenter.toString(), targetZone->getId(), targetZone->getCenter().toString(), maxDistance);
logGlobal->trace("direction is %s", vec.toString());
misplacedZone->setCenter(targetZone->getCenter() - vec.unitVector() * newDistanceBetweenZones); //zones should now overlap by half size
logGlobal->trace("New distance %f", targetZone->getCenter().dist2d(misplacedZone->getCenter()));
secondZone = misplacedZones[i].second;
break;
}
}
else
if (secondZone)
{
float maxOverlap = 0;
for(const auto & otherZone : zones)
{
float3 otherZoneCenter = otherZone.second->getCenter();
logGlobal->trace("Swapping two misplaced zones %d and %d", firstZone->getId(), secondZone->getId());
if (otherZone.second == misplacedZone || otherZoneCenter.z != ourCenter.z)
continue;
auto firstCenter = firstZone->getCenter();
auto secondCenter = secondZone->getCenter();
firstZone->setCenter(secondCenter);
secondZone->setCenter(firstCenter);
auto distance = static_cast<float>(otherZoneCenter.dist2dSQ(ourCenter));
if (distance > maxOverlap)
{
maxOverlap = distance;
targetZone = otherZone.second;
}
}
if (targetZone)
{
float3 vec = ourCenter - targetZone->getCenter();
float newDistanceBetweenZones = (misplacedZone->getSize() + targetZone->getSize()) / mapSize;
logGlobal->trace("Trying to move zone %d %s away from %d %s. Old distance %f", misplacedZone->getId(), ourCenter.toString(), targetZone->getId(), targetZone->getCenter().toString(), maxOverlap);
logGlobal->trace("direction is %s", vec.toString());
misplacedZone->setCenter(targetZone->getCenter() + vec.unitVector() * newDistanceBetweenZones); //zones should now be just separated
logGlobal->trace("New distance %f", targetZone->getCenter().dist2d(misplacedZone->getCenter()));
}
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;
auto misplacedZone = misplacedZones.front().second;
float3 ourCenter = misplacedZone->getCenter();
if (totalDistance > totalOverlap)
{
//Move one zone towards most distant zone to reduce distance
float maxDistance = 0;
for (auto con : misplacedZone->getConnections())
{
auto otherZone = zones[con];
float distance = static_cast<float>(otherZone->getCenter().dist2dSQ(ourCenter));
if (distance > maxDistance)
{
maxDistance = distance;
targetZone = otherZone;
}
}
if (targetZone) //TODO: consider refactoring duplicated code
{
float3 vec = targetZone->getCenter() - ourCenter;
float newDistanceBetweenZones = (std::max(misplacedZone->getSize(), targetZone->getSize())) / mapSize;
logGlobal->trace("Trying to move zone %d %s towards %d %s. Old distance %f", misplacedZone->getId(), ourCenter.toString(), targetZone->getId(), targetZone->getCenter().toString(), maxDistance);
logGlobal->trace("direction is %s", vec.toString());
misplacedZone->setCenter(targetZone->getCenter() - vec.unitVector() * newDistanceBetweenZones); //zones should now overlap by half size
logGlobal->trace("New distance %f", targetZone->getCenter().dist2d(misplacedZone->getCenter()));
}
}
else
{
//Move misplaced zone away from overlapping zone
//FIXME: Does that ever happend? Check the number ranges and rescale if needed
float maxOverlap = 0;
for(const auto & otherZone : zones)
{
float3 otherZoneCenter = otherZone.second->getCenter();
if (otherZone.second == misplacedZone || otherZoneCenter.z != ourCenter.z)
continue;
auto distance = static_cast<float>(otherZoneCenter.dist2dSQ(ourCenter));
if (distance > maxOverlap)
{
maxOverlap = distance;
targetZone = otherZone.second;
}
}
if (targetZone)
{
float3 vec = ourCenter - targetZone->getCenter();
float newDistanceBetweenZones = (misplacedZone->getSize() + targetZone->getSize()) / mapSize;
logGlobal->trace("Trying to move zone %d %s away from %d %s. Old distance %f", misplacedZone->getId(), ourCenter.toString(), targetZone->getId(), targetZone->getCenter().toString(), maxOverlap);
logGlobal->trace("direction is %s", vec.toString());
misplacedZone->setCenter(targetZone->getCenter() + vec.unitVector() * newDistanceBetweenZones); //zones should now be just separated
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

View File

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