mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
First draft of new algorithm that deliberately links zones with towns
This commit is contained in:
@@ -1005,167 +1005,572 @@ void CZonePlacer::assignZones(vstd::RNG * rand)
|
||||
logGlobal->info("Finished zone colouring");
|
||||
}
|
||||
|
||||
TRmgTemplateZoneId findSet(std::map<TRmgTemplateZoneId, TRmgTemplateZoneId> & parent, TRmgTemplateZoneId x)
|
||||
{
|
||||
if(parent[x] != x)
|
||||
parent[x] = findSet(parent, parent[x]);
|
||||
return parent[x];
|
||||
}
|
||||
|
||||
void unionSets(std::map<TRmgTemplateZoneId, TRmgTemplateZoneId> & parent, TRmgTemplateZoneId x, TRmgTemplateZoneId y)
|
||||
{
|
||||
TRmgTemplateZoneId rx = findSet(parent, x);
|
||||
TRmgTemplateZoneId ry = findSet(parent, y);
|
||||
if(rx != ry)
|
||||
parent[rx] = ry;
|
||||
}
|
||||
|
||||
void CZonePlacer::dropRandomRoads(vstd::RNG * rand)
|
||||
{
|
||||
auto zones = map.getZones();
|
||||
bool anyDropped;
|
||||
|
||||
do
|
||||
{
|
||||
anyDropped = false;
|
||||
|
||||
// Instead of a simple set, use a multiset to track multiple connections between zones
|
||||
std::map<TRmgTemplateZoneId, std::map<TRmgTemplateZoneId, int>> roadGraph;
|
||||
std::set<rmg::ZoneConnection> randomConnections;
|
||||
|
||||
//Build graph and collect random connections
|
||||
for(const auto & zone : zones)
|
||||
{
|
||||
for(const auto & connection : zone.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
||||
{
|
||||
roadGraph[connection.getZoneA()][connection.getZoneB()]++;
|
||||
roadGraph[connection.getZoneB()][connection.getZoneA()]++;
|
||||
}
|
||||
else if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
||||
{
|
||||
roadGraph[connection.getZoneA()][connection.getZoneB()]++;
|
||||
roadGraph[connection.getZoneB()][connection.getZoneA()]++;
|
||||
randomConnections.insert(connection);
|
||||
}
|
||||
// ROAD_FALSE connections are ignored
|
||||
}
|
||||
}
|
||||
logGlobal->info("Remaining random connections: %d", randomConnections.size());
|
||||
|
||||
if(randomConnections.empty())
|
||||
break;
|
||||
|
||||
//Convert to vector for shuffling
|
||||
std::vector<rmg::ZoneConnection> shuffledConnections(randomConnections.begin(), randomConnections.end());
|
||||
RandomGeneratorUtil::randomShuffle(shuffledConnections, *rand);
|
||||
|
||||
//Try each random connection in shuffled order
|
||||
for(const auto & conn : shuffledConnections)
|
||||
{
|
||||
auto zoneA = conn.getZoneA();
|
||||
auto zoneB = conn.getZoneB();
|
||||
|
||||
//Check if either zone would become isolated by removing this connection
|
||||
if(roadGraph[zoneA].size() <= 1 || roadGraph[zoneB].size() <= 1)
|
||||
{
|
||||
//Can't remove this connection as it would isolate a zone
|
||||
continue;
|
||||
}
|
||||
|
||||
bool multipleConnections = roadGraph[zoneA][zoneB] > 1;
|
||||
|
||||
//Check if this is the last road between these zones
|
||||
if(!multipleConnections)
|
||||
{
|
||||
//Temporarily remove this connection
|
||||
roadGraph[zoneA].erase(zoneB);
|
||||
roadGraph[zoneB].erase(zoneA);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Decrement the connection count
|
||||
roadGraph[zoneA][zoneB]--;
|
||||
roadGraph[zoneB][zoneA]--;
|
||||
}
|
||||
|
||||
//Check if graph remains connected as a whole
|
||||
bool canRemove = true;
|
||||
|
||||
// Multiple connections imply this one can be removed without checking
|
||||
if (!multipleConnections)
|
||||
{
|
||||
std::set<TRmgTemplateZoneId> visited;
|
||||
|
||||
// Get all zones that have road connections
|
||||
std::set<TRmgTemplateZoneId> zonesWithRoads;
|
||||
for(const auto & entry : roadGraph)
|
||||
{
|
||||
if(!entry.second.empty())
|
||||
{
|
||||
zonesWithRoads.insert(entry.first);
|
||||
}
|
||||
}
|
||||
|
||||
if(!zonesWithRoads.empty())
|
||||
{
|
||||
//Start DFS from any zone that has roads
|
||||
TRmgTemplateZoneId startZone = *zonesWithRoads.begin();
|
||||
|
||||
std::stack<TRmgTemplateZoneId> stack;
|
||||
stack.push(startZone);
|
||||
|
||||
while(!stack.empty())
|
||||
{
|
||||
auto current = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if(vstd::contains(visited, current))
|
||||
continue;
|
||||
|
||||
visited.insert(current);
|
||||
|
||||
for(auto & neighbor : roadGraph[current])
|
||||
{
|
||||
if(!vstd::contains(visited, neighbor.first))
|
||||
{
|
||||
stack.push(neighbor.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check if all zones with roads are still reachable
|
||||
for(auto zoneId : zonesWithRoads)
|
||||
{
|
||||
if(!vstd::contains(visited, zoneId))
|
||||
{
|
||||
canRemove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!canRemove)
|
||||
{
|
||||
//Restore connection and try next one
|
||||
roadGraph[zoneA][zoneB]++;
|
||||
roadGraph[zoneB][zoneA]++;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Found a connection we can remove - update in both zones that contain it
|
||||
auto & zonePtr = zones[zoneA];
|
||||
zonePtr->setRoadOption(conn.getId(), rmg::ERoadOption::ROAD_FALSE);
|
||||
|
||||
auto & otherZonePtr = zones[zoneB];
|
||||
otherZonePtr->setRoadOption(conn.getId(), rmg::ERoadOption::ROAD_FALSE);
|
||||
|
||||
logGlobal->info("Dropped random road between %d and %d", zoneA, zoneB);
|
||||
anyDropped = true;
|
||||
break; //Exit loop and rebuild graph
|
||||
}
|
||||
} while(anyDropped);
|
||||
|
||||
|
||||
// Mark all remaining random connections as TRUE
|
||||
// First, identify zones with towns
|
||||
std::set<TRmgTemplateZoneId> zonesWithTowns;
|
||||
|
||||
for(const auto & zone : zones)
|
||||
{
|
||||
// A zone has towns if it has player towns or neutral towns
|
||||
if(zone.second->getPlayerTowns().getTownCount() ||
|
||||
zone.second->getPlayerTowns().getCastleCount() ||
|
||||
zone.second->getNeutralTowns().getTownCount() ||
|
||||
zone.second->getNeutralTowns().getCastleCount())
|
||||
{
|
||||
zonesWithTowns.insert(zone.first);
|
||||
}
|
||||
}
|
||||
|
||||
logGlobal->info("Zones with towns: %d", zonesWithTowns.size());
|
||||
|
||||
if(zonesWithTowns.empty())
|
||||
{
|
||||
// No towns, no roads needed
|
||||
// Mark all roads as FALSE
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
||||
{
|
||||
auto id = connection.getId();
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Map to track which connections we've processed (to avoid duplicates)
|
||||
std::set<int> processedConnectionIds;
|
||||
|
||||
// Map of all connections by their ID for easier access
|
||||
std::map<int, std::pair<TRmgTemplateZoneId, TRmgTemplateZoneId>> connectionMap;
|
||||
|
||||
// Map true roads between zones
|
||||
std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> trueRoads;
|
||||
|
||||
// Track existing defined connections in template (both TRUE and RANDOM)
|
||||
std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> existingConnections;
|
||||
|
||||
// First pass: collect all connections defined in the template
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
||||
{
|
||||
auto id = connection.getId();
|
||||
auto zoneA = connection.getZoneA();
|
||||
auto zoneB = connection.getZoneB();
|
||||
|
||||
connectionMap[id] = std::make_pair(zoneA, zoneB);
|
||||
|
||||
// Track all valid connections defined in template
|
||||
existingConnections[zoneA].insert(zoneB);
|
||||
existingConnections[zoneB].insert(zoneA);
|
||||
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
||||
{
|
||||
auto id = connection.getId();
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
||||
// Record this as a true road
|
||||
trueRoads[zoneA].insert(zoneB);
|
||||
trueRoads[zoneB].insert(zoneA);
|
||||
|
||||
logGlobal->info("Keeping pre-configured TRUE road for connection %d between zones %d and %d",
|
||||
id, zoneA, zoneB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each town zone, ensure it has at least one connection if possible
|
||||
for(auto townZone : zonesWithTowns)
|
||||
{
|
||||
// Skip if the town zone already has a TRUE road
|
||||
if(vstd::contains(trueRoads, townZone) && !trueRoads[townZone].empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the town has any valid connections
|
||||
if(!vstd::contains(existingConnections, townZone) || existingConnections[townZone].empty())
|
||||
{
|
||||
logGlobal->warn("Town zone %d has no valid connections in template", townZone);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the best random connection to mark as TRUE
|
||||
bool foundConnection = false;
|
||||
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
if(foundConnection) break;
|
||||
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() != rmg::ERoadOption::ROAD_RANDOM)
|
||||
continue;
|
||||
|
||||
auto zoneA = connection.getZoneA();
|
||||
auto zoneB = connection.getZoneB();
|
||||
|
||||
// Check if this connection involves our town zone
|
||||
if(zoneA == townZone || zoneB == townZone)
|
||||
{
|
||||
// Found a connection for this town zone, mark it as TRUE
|
||||
zonePtr.second->setRoadOption(connection.getId(), rmg::ERoadOption::ROAD_TRUE);
|
||||
|
||||
auto otherZone = (zoneA == townZone) ? zoneB : zoneA;
|
||||
|
||||
logGlobal->info("Setting RANDOM road to TRUE for connection %d to ensure town zone %d has at least one connection (to zone %d)",
|
||||
connection.getId(), townZone, otherZone);
|
||||
|
||||
// Update trueRoads map
|
||||
trueRoads[zoneA].insert(zoneB);
|
||||
trueRoads[zoneB].insert(zoneA);
|
||||
|
||||
foundConnection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Union-find data structure for MST algorithm
|
||||
std::map<TRmgTemplateZoneId, TRmgTemplateZoneId> parent;
|
||||
|
||||
// Initialize all town zones as separate sets
|
||||
for(auto townZone : zonesWithTowns)
|
||||
{
|
||||
parent[townZone] = townZone;
|
||||
}
|
||||
|
||||
// Process TRUE roads first to build initial MST
|
||||
// Add all TRUE roads that directly connect towns to the MST
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
||||
{
|
||||
auto id = connection.getId();
|
||||
auto zoneA = connection.getZoneA();
|
||||
auto zoneB = connection.getZoneB();
|
||||
|
||||
// Skip if we've already processed this connection
|
||||
if(processedConnectionIds.find(id) != processedConnectionIds.end())
|
||||
continue;
|
||||
|
||||
processedConnectionIds.insert(id);
|
||||
|
||||
// If both ends have towns, add to MST
|
||||
if(vstd::contains(zonesWithTowns, zoneA) && vstd::contains(zonesWithTowns, zoneB))
|
||||
{
|
||||
unionSets(parent, zoneA, zoneB);
|
||||
|
||||
logGlobal->info("Adding TRUE road between town zones %d and %d to MST", zoneA, zoneB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find all paths through TRUE roads
|
||||
std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> reachableFromTown;
|
||||
|
||||
for(auto townZone : zonesWithTowns)
|
||||
{
|
||||
std::queue<TRmgTemplateZoneId> q;
|
||||
std::set<TRmgTemplateZoneId> visited;
|
||||
|
||||
q.push(townZone);
|
||||
visited.insert(townZone);
|
||||
|
||||
while(!q.empty())
|
||||
{
|
||||
auto current = q.front();
|
||||
q.pop();
|
||||
|
||||
// Add this zone to reachable list for the town
|
||||
reachableFromTown[townZone].insert(current);
|
||||
|
||||
// Check all TRUE road neighbors
|
||||
if(vstd::contains(trueRoads, current))
|
||||
{
|
||||
for(auto neighbor : trueRoads[current])
|
||||
{
|
||||
if(visited.find(neighbor) == visited.end())
|
||||
{
|
||||
visited.insert(neighbor);
|
||||
q.push(neighbor);
|
||||
|
||||
// If this is another town, update MST
|
||||
if(vstd::contains(zonesWithTowns, neighbor))
|
||||
{
|
||||
unionSets(parent, townZone, neighbor);
|
||||
|
||||
logGlobal->info("Adding multi-zone TRUE path between town zones %d and %d to MST",
|
||||
townZone, neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now process RANDOM roads to complete the MST
|
||||
// Collect all RANDOM roads
|
||||
std::vector<std::pair<int, std::pair<TRmgTemplateZoneId, TRmgTemplateZoneId>>> randomRoads;
|
||||
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
||||
{
|
||||
auto id = connection.getId();
|
||||
auto zoneA = connection.getZoneA();
|
||||
auto zoneB = connection.getZoneB();
|
||||
|
||||
// Skip if we've already processed this connection
|
||||
if(processedConnectionIds.find(id) != processedConnectionIds.end())
|
||||
continue;
|
||||
|
||||
processedConnectionIds.insert(id);
|
||||
|
||||
// Add to random roads vector
|
||||
randomRoads.push_back(std::make_pair(id, std::make_pair(zoneA, zoneB)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process random roads - prioritize direct connections between towns
|
||||
for(auto& road : randomRoads)
|
||||
{
|
||||
auto id = road.first;
|
||||
auto zoneA = road.second.first;
|
||||
auto zoneB = road.second.second;
|
||||
|
||||
// If both zones have towns, check if they're already connected in the MST
|
||||
if(vstd::contains(zonesWithTowns, zoneA) && vstd::contains(zonesWithTowns, zoneB))
|
||||
{
|
||||
if(findSet(parent, zoneA) != findSet(parent, zoneB))
|
||||
{
|
||||
// Not connected, add this road to MST
|
||||
unionSets(parent, zoneA, zoneB);
|
||||
|
||||
// Set road to TRUE
|
||||
bool found = false;
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getId() == id)
|
||||
{
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
|
||||
// Update trueRoads map
|
||||
trueRoads[zoneA].insert(zoneB);
|
||||
trueRoads[zoneB].insert(zoneA);
|
||||
|
||||
logGlobal->info("Setting RANDOM road to TRUE for direct connection %d between town zones %d and %d",
|
||||
id, zoneA, zoneB);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Already connected, set to FALSE
|
||||
bool found = false;
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getId() == id)
|
||||
{
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
|
||||
logGlobal->info("Setting RANDOM road to FALSE for connection %d between town zones %d and %d (already connected)",
|
||||
id, zoneA, zoneB);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special case: either zone is a town that has no other connections yet
|
||||
if((vstd::contains(zonesWithTowns, zoneA) && (!vstd::contains(trueRoads, zoneA) || trueRoads[zoneA].empty())) ||
|
||||
(vstd::contains(zonesWithTowns, zoneB) && (!vstd::contains(trueRoads, zoneB) || trueRoads[zoneB].empty())))
|
||||
{
|
||||
// Always mark this as TRUE - it's the only connection for an isolated town
|
||||
bool found = false;
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getId() == id)
|
||||
{
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
|
||||
// Update trueRoads map
|
||||
trueRoads[zoneA].insert(zoneB);
|
||||
trueRoads[zoneB].insert(zoneA);
|
||||
|
||||
// If either is a town zone, note it specially in the log
|
||||
auto townZoneId = vstd::contains(zonesWithTowns, zoneA) ? zoneA : zoneB;
|
||||
auto otherZoneId = vstd::contains(zonesWithTowns, zoneA) ? zoneB : zoneA;
|
||||
|
||||
logGlobal->info("Setting RANDOM road to TRUE for connection %d - only connection for isolated town zone %d",
|
||||
id, townZoneId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If one zone has a town and one doesn't
|
||||
TRmgTemplateZoneId townZone = vstd::contains(zonesWithTowns, zoneA) ? zoneA :
|
||||
vstd::contains(zonesWithTowns, zoneB) ? zoneB : 0;
|
||||
TRmgTemplateZoneId nonTownZone = vstd::contains(zonesWithTowns, zoneA) ? zoneB :
|
||||
vstd::contains(zonesWithTowns, zoneB) ? zoneA : 0;
|
||||
|
||||
if(townZone && nonTownZone)
|
||||
{
|
||||
// See if this non-town zone can connect to any other town
|
||||
std::set<TRmgTemplateZoneId> possibleConnections;
|
||||
|
||||
for(auto & otherZonePtr : zones)
|
||||
{
|
||||
if(otherZonePtr.first == nonTownZone) continue;
|
||||
|
||||
// Check if there's a possible connection to another zone
|
||||
if(vstd::contains(existingConnections, nonTownZone) &&
|
||||
vstd::contains(existingConnections[nonTownZone], otherZonePtr.first))
|
||||
{
|
||||
// If this other zone is a town or can reach other towns, note it
|
||||
if(vstd::contains(zonesWithTowns, otherZonePtr.first) && otherZonePtr.first != townZone)
|
||||
{
|
||||
possibleConnections.insert(otherZonePtr.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!possibleConnections.empty())
|
||||
{
|
||||
// This non-town zone can potentially connect to other towns
|
||||
// Check if any of these towns aren't already connected to our town
|
||||
bool canCreateNewConnection = false;
|
||||
|
||||
for(auto otherTown : possibleConnections)
|
||||
{
|
||||
if(findSet(parent, townZone) != findSet(parent, otherTown))
|
||||
{
|
||||
canCreateNewConnection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set road to TRUE if it could create a new connection
|
||||
// This ensures isolated town zones get connected through non-town zones
|
||||
bool found = false;
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getId() == id)
|
||||
{
|
||||
if(canCreateNewConnection)
|
||||
{
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
||||
logGlobal->info("Setting RANDOM road to TRUE for connection %d - potential path between town zones", id);
|
||||
}
|
||||
else
|
||||
{
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
||||
logGlobal->info("Setting RANDOM road to FALSE for connection %d - won't create new town connections", id);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
|
||||
if(canCreateNewConnection)
|
||||
{
|
||||
// Update trueRoads map
|
||||
trueRoads[zoneA].insert(zoneB);
|
||||
trueRoads[zoneB].insert(zoneA);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a dead-end connection to a town zone - keep for connectivity
|
||||
bool found = false;
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getId() == id)
|
||||
{
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_TRUE);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
|
||||
// Update trueRoads map
|
||||
trueRoads[zoneA].insert(zoneB);
|
||||
trueRoads[zoneB].insert(zoneA);
|
||||
|
||||
logGlobal->info("Setting RANDOM road to TRUE for connection %d - road to town zone %d",
|
||||
id, townZone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a connection between non-town zones
|
||||
// Set it to FALSE by default
|
||||
bool found = false;
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getId() == id)
|
||||
{
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) break;
|
||||
}
|
||||
|
||||
logGlobal->info("Setting RANDOM road to FALSE for connection %d between non-town zones %d and %d",
|
||||
id, zoneA, zoneB);
|
||||
}
|
||||
}
|
||||
|
||||
// Update town connectivity after adding all roads
|
||||
// Re-run BFS from each town zone through TRUE roads to see if we need more connections
|
||||
std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> connectedTownGroups;
|
||||
|
||||
for(auto townZone : zonesWithTowns)
|
||||
{
|
||||
std::queue<TRmgTemplateZoneId> q;
|
||||
std::set<TRmgTemplateZoneId> visited;
|
||||
|
||||
q.push(townZone);
|
||||
visited.insert(townZone);
|
||||
|
||||
while(!q.empty())
|
||||
{
|
||||
auto current = q.front();
|
||||
q.pop();
|
||||
|
||||
// Check all TRUE road neighbors
|
||||
if(vstd::contains(trueRoads, current))
|
||||
{
|
||||
for(auto neighbor : trueRoads[current])
|
||||
{
|
||||
if(visited.find(neighbor) == visited.end())
|
||||
{
|
||||
visited.insert(neighbor);
|
||||
q.push(neighbor);
|
||||
|
||||
// If this is another town, add to connected group
|
||||
if(vstd::contains(zonesWithTowns, neighbor))
|
||||
{
|
||||
connectedTownGroups[townZone].insert(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set any remaining RANDOM roads to FALSE
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
|
||||
{
|
||||
auto id = connection.getId();
|
||||
zonePtr.second->setRoadOption(id, rmg::ERoadOption::ROAD_FALSE);
|
||||
logGlobal->info("Setting remaining RANDOM road to FALSE for connection %d", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log the final town connectivity information
|
||||
logGlobal->info("======== Town Connectivity Report ========");
|
||||
for(auto townZone : zonesWithTowns)
|
||||
{
|
||||
std::string connections;
|
||||
|
||||
// Find all adjacent zones connected by roads
|
||||
std::set<TRmgTemplateZoneId> adjacentConnections;
|
||||
|
||||
for(auto & zonePtr : zones)
|
||||
{
|
||||
if(zonePtr.first == townZone)
|
||||
{
|
||||
for(auto & connection : zonePtr.second->getConnections())
|
||||
{
|
||||
if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
|
||||
{
|
||||
auto otherZoneId = connection.getOtherZoneId(townZone);
|
||||
adjacentConnections.insert(otherZoneId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!adjacentConnections.empty())
|
||||
{
|
||||
for(auto connectedZone : adjacentConnections)
|
||||
{
|
||||
if(!connections.empty())
|
||||
connections += ", ";
|
||||
connections += std::to_string(connectedZone);
|
||||
}
|
||||
}
|
||||
|
||||
if(connections.empty())
|
||||
connections = "None (no road connections)";
|
||||
|
||||
logGlobal->info("Zone %d with town connected to adjacent zones: %s", townZone, connections);
|
||||
}
|
||||
|
||||
logGlobal->info("Finished road generation - created minimal spanning tree connecting all towns");
|
||||
}
|
||||
|
||||
const TDistanceMap& CZonePlacer::getDistanceMap()
|
||||
|
||||
Reference in New Issue
Block a user