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,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 = 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)
{ {
std::shared_ptr<Zone> targetZone; return lhs.first > rhs.first; //Biggest first
float3 ourCenter = misplacedZone->getCenter(); });
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 //Only swap zones on the same level
float maxDistance = 0; //Don't swap zones that should be connected (Jebus)
for (auto con : misplacedZone->getConnections()) if (misplacedZones[i].second->getCenter().z == level &&
!vstd::contains(firstZone->getConnections(), misplacedZones[i].second->getId()))
{ {
auto otherZone = zones[con]; secondZone = misplacedZones[i].second;
float distance = static_cast<float>(otherZone->getCenter().dist2dSQ(ourCenter)); break;
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 if (secondZone)
{ {
float maxOverlap = 0; logGlobal->trace("Swapping two misplaced zones %d and %d", firstZone->getId(), secondZone->getId());
for(const auto & otherZone : zones)
{
float3 otherZoneCenter = otherZone.second->getCenter();
if (otherZone.second == misplacedZone || otherZoneCenter.z != ourCenter.z) auto firstCenter = firstZone->getCenter();
continue; auto secondCenter = secondZone->getCenter();
firstZone->setCenter(secondCenter);
secondZone->setCenter(firstCenter);
auto distance = static_cast<float>(otherZoneCenter.dist2dSQ(ourCenter)); lastSwappedZones.insert(firstZone->getId());
if (distance > maxOverlap) lastSwappedZones.insert(secondZone->getId());
{ return;
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.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 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;
}; };