Browse Source

First implementation of random connections

Tomasz Zieliński 7 months ago
parent
commit
f0971dfdd6

+ 43 - 43
Mods/vcmi/Content/config/rmg/symmetric/6lm10a.json

@@ -256,59 +256,59 @@
 		},
 		},
 		"connections" :
 		"connections" :
 		[
 		[
-			{ "a" : "1", "b" : "9", "guard" : 6000 },
-			{ "a" : "1", "b" : "21", "guard" : 3000 },
-			{ "a" : "1", "b" : "21", "guard" : 3000 },
+			{ "a" : "1", "b" : "9", "guard" : 6000, "road" : "true" },
+			{ "a" : "1", "b" : "21", "guard" : 3000, "road" : "random" },
+			{ "a" : "1", "b" : "21", "guard" : 3000, "road" : "true" },
 
 
-			{ "a" : "2", "b" : "10", "guard" : 6000 },
-			{ "a" : "2", "b" : "22", "guard" : 3000 },
-			{ "a" : "2", "b" : "22", "guard" : 3000 },
+			{ "a" : "2", "b" : "10", "guard" : 6000, "road" : "random" },
+			{ "a" : "2", "b" : "22", "guard" : 3000, "road" : "random" },
+			{ "a" : "2", "b" : "22", "guard" : 3000, "road" : "random" },
 
 
-			{ "a" : "3", "b" : "21", "guard" : 3000 },
-			{ "a" : "3", "b" : "23", "guard" : 3000 },
-			{ "a" : "3", "b" : "24", "guard" : 3000 },
+			{ "a" : "3", "b" : "21", "guard" : 3000, "road" : "true" },
+			{ "a" : "3", "b" : "23", "guard" : 3000, "road" : "false" },
+			{ "a" : "3", "b" : "24", "guard" : 3000, "road" : "random" },
 
 
-			{ "a" : "4", "b" : "22", "guard" : 3000 },
-			{ "a" : "4", "b" : "23", "guard" : 3000 },
-			{ "a" : "4", "b" : "25", "guard" : 3000 },
+			{ "a" : "4", "b" : "22", "guard" : 3000, "road" : "random" },
+			{ "a" : "4", "b" : "23", "guard" : 3000, "road" : "false" },
+			{ "a" : "4", "b" : "25", "guard" : 3000, "road" : "true" },
 
 
-			{ "a" : "5", "b" : "13", "guard" : 6000 },
-			{ "a" : "5", "b" : "24", "guard" : 3000 },
-			{ "a" : "5", "b" : "24", "guard" : 3000 },
+			{ "a" : "5", "b" : "13", "guard" : 6000, "road" : "true" },
+			{ "a" : "5", "b" : "24", "guard" : 3000, "road" : "random" },
+			{ "a" : "5", "b" : "24", "guard" : 3000, "road" : "random" },
 
 
 			{ "a" : "6", "b" : "14", "guard" : 6000 },
 			{ "a" : "6", "b" : "14", "guard" : 6000 },
-			{ "a" : "6", "b" : "25", "guard" : 3000 },
-			{ "a" : "6", "b" : "25", "guard" : 3000 },
+			{ "a" : "6", "b" : "25", "guard" : 3000, "road" : "random" },
+			{ "a" : "6", "b" : "25", "guard" : 3000, "road" : "true" },
 
 
-			{ "a" : "7", "b" : "21", "guard" : 6000 },
-			{ "a" : "7", "b" : "21", "guard" : 6000 },
-			{ "a" : "8", "b" : "22", "guard" : 6000 },
-			{ "a" : "8", "b" : "22", "guard" : 6000 },
-			{ "a" : "15", "b" : "24", "guard" : 6000 },
-			{ "a" : "15", "b" : "24", "guard" : 6000 },
-			{ "a" : "16", "b" : "25", "guard" : 6000 },
-			{ "a" : "16", "b" : "25", "guard" : 6000 },
+			{ "a" : "7", "b" : "21", "guard" : 6000, "road" : "random" },
+			{ "a" : "7", "b" : "21", "guard" : 6000, "road" : "random" },
+			{ "a" : "8", "b" : "22", "guard" : 6000, "road" : "random" },
+			{ "a" : "8", "b" : "22", "guard" : 6000, "road" : "random" },
+			{ "a" : "15", "b" : "24", "guard" : 6000, "road" : "random"  },
+			{ "a" : "15", "b" : "24", "guard" : 6000, "road" : "random"  },
+			{ "a" : "16", "b" : "25", "guard" : 6000, "road" : "random"  },
+			{ "a" : "16", "b" : "25", "guard" : 6000, "road" : "random"  },
 
 
-			{ "a" : "9", "b" : "10", "guard" : 6000 },
-			{ "a" : "9", "b" : "21", "guard" : 6000 },
-			{ "a" : "9", "b" : "23", "guard" : 6000 },
-			{ "a" : "10", "b" : "22", "guard" : 6000 },
-			{ "a" : "10", "b" : "23", "guard" : 6000 },
-			{ "a" : "13", "b" : "23", "guard" : 6000 },
-			{ "a" : "13", "b" : "24", "guard" : 6000 },
-			{ "a" : "13", "b" : "14", "guard" : 6000 },
-			{ "a" : "14", "b" : "23", "guard" : 6000 },
-			{ "a" : "14", "b" : "25", "guard" : 6000 },
+			{ "a" : "9", "b" : "10", "guard" : 6000, "road" : "true" },
+			{ "a" : "9", "b" : "21", "guard" : 6000, "road" : "random" },
+			{ "a" : "9", "b" : "23", "guard" : 6000, "road" : "false" },
+			{ "a" : "10", "b" : "22", "guard" : 6000, "road" : "random" },
+			{ "a" : "10", "b" : "23", "guard" : 6000, "road" : "random" },
+			{ "a" : "13", "b" : "23", "guard" : 6000, "road" : "random" },
+			{ "a" : "13", "b" : "24", "guard" : 6000, "road" : "random" },
+			{ "a" : "13", "b" : "14", "guard" : 6000, "road" : "true" },
+			{ "a" : "14", "b" : "23", "guard" : 6000, "road" : "false" },
+			{ "a" : "14", "b" : "25", "guard" : 6000, "road" : "random" },
 
 
-			{ "a" : "11", "b" : "21", "guard" : 6000 },
-			{ "a" : "11", "b" : "24", "guard" : 6000 },
-			{ "a" : "12", "b" : "22", "guard" : 6000 },
-			{ "a" : "12", "b" : "25", "guard" : 6000 },
+			{ "a" : "11", "b" : "21", "guard" : 6000, "road" : "true" },
+			{ "a" : "11", "b" : "24", "guard" : 6000, "road" : "random" },
+			{ "a" : "12", "b" : "22", "guard" : 6000, "road" : "true" },
+			{ "a" : "12", "b" : "25", "guard" : 6000, "road" : "random" },
 
 
-			{ "a" : "17", "b" : "21", "guard" : 9000 },
-			{ "a" : "18", "b" : "22", "guard" : 9000 },
-			{ "a" : "19", "b" : "24", "guard" : 9000 },
-			{ "a" : "20", "b" : "25", "guard" : 9000 },
+			{ "a" : "17", "b" : "21", "guard" : 9000, "road" : "random"  },
+			{ "a" : "18", "b" : "22", "guard" : 9000, "road" : "random"  },
+			{ "a" : "19", "b" : "24", "guard" : 9000, "road" : "random"  },
+			{ "a" : "20", "b" : "25", "guard" : 9000, "road" : "random"  },
 
 
 			{ "a" : "21", "b" : "22", "type": "wide" },
 			{ "a" : "21", "b" : "22", "type": "wide" },
 			{ "a" : "24", "b" : "25", "type": "wide" }
 			{ "a" : "24", "b" : "25", "type": "wide" }

+ 1 - 0
lib/rmg/CMapGenerator.cpp

@@ -328,6 +328,7 @@ void CMapGenerator::genZones()
 {
 {
 	placer->placeZones(rand.get());
 	placer->placeZones(rand.get());
 	placer->assignZones(rand.get());
 	placer->assignZones(rand.get());
+	placer->dropRandomRoads(rand.get());
 
 
 	logGlobal->info("Zones generated successfully");
 	logGlobal->info("Zones generated successfully");
 }
 }

+ 157 - 0
lib/rmg/CZonePlacer.cpp

@@ -18,6 +18,7 @@
 #include "../mapping/CMapEditManager.h"
 #include "../mapping/CMapEditManager.h"
 #include "../GameLibrary.h"
 #include "../GameLibrary.h"
 #include "CMapGenOptions.h"
 #include "CMapGenOptions.h"
+#include "CRmgTemplate.h"
 #include "RmgMap.h"
 #include "RmgMap.h"
 #include "Zone.h"
 #include "Zone.h"
 #include "Functions.h"
 #include "Functions.h"
@@ -1004,6 +1005,162 @@ void CZonePlacer::assignZones(vstd::RNG * rand)
 	logGlobal->info("Finished zone colouring");
 	logGlobal->info("Finished zone colouring");
 }
 }
 
 
+void CZonePlacer::dropRandomRoads(vstd::RNG * rand)
+{
+	auto zones = map.getZones();
+
+	//First, build a graph of road connections
+	std::map<TRmgTemplateZoneId, std::set<TRmgTemplateZoneId>> roadGraph;
+	std::vector<rmg::ZoneConnection> randomConnections;
+	std::vector<rmg::ZoneConnection> fixedConnections;
+
+	//Collect all road connections and build initial graph
+	for(const auto & zone : zones)
+	{
+		for(const auto & connection : zone.second->getConnections())
+		{
+			if(connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE)
+			{
+				roadGraph[connection.getZoneA()].insert(connection.getZoneB());
+				roadGraph[connection.getZoneB()].insert(connection.getZoneA());
+				fixedConnections.push_back(connection);
+			}
+			else if(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM)
+			{
+				roadGraph[connection.getZoneA()].insert(connection.getZoneB());
+				roadGraph[connection.getZoneB()].insert(connection.getZoneA());
+				randomConnections.push_back(connection);
+			}
+		}
+	}
+
+	//Find all connected components in the initial graph
+	std::map<TRmgTemplateZoneId, int> zoneToComponent;
+	int numComponents = 0;
+
+	auto dfsComponent = [&](TRmgTemplateZoneId start, int component)
+	{
+		std::stack<TRmgTemplateZoneId> stack;
+		stack.push(start);
+
+		while(!stack.empty())
+		{
+			auto current = stack.top();
+			stack.pop();
+
+			if(zoneToComponent.find(current) != zoneToComponent.end())
+				continue;
+
+			zoneToComponent[current] = component;
+
+			for(auto neighbor : roadGraph[current])
+			{
+				if(zoneToComponent.find(neighbor) == zoneToComponent.end())
+				{
+					stack.push(neighbor);
+				}
+			}
+		}
+	};
+
+	//Find all components
+	for(const auto & zone : zones)
+	{
+		if(zoneToComponent.find(zone.first) == zoneToComponent.end() && !roadGraph[zone.first].empty())
+		{
+			dfsComponent(zone.first, numComponents++);
+		}
+	}
+
+	//Process each component separately
+	for(int component = 0; component < numComponents; component++)
+	{
+		//Get random connections for this component
+		std::vector<rmg::ZoneConnection> componentRandomConnections;
+		for(const auto & conn : randomConnections)
+		{
+			if(zoneToComponent[conn.getZoneA()] == component)
+			{
+				componentRandomConnections.push_back(conn);
+			}
+		}
+
+		//Shuffle random connections
+		RandomGeneratorUtil::randomShuffle(componentRandomConnections, *rand);
+
+		//Try removing each random connection
+		for(const auto & conn : componentRandomConnections)
+		{
+			//Temporarily remove this connection
+			roadGraph[conn.getZoneA()].erase(conn.getZoneB());
+			roadGraph[conn.getZoneB()].erase(conn.getZoneA());
+
+			//Check if graph remains connected
+			bool canRemove = true;
+			std::set<TRmgTemplateZoneId> visited;
+
+			//Start DFS from any zone in this component
+			auto startZone = conn.getZoneA();
+			std::stack<TRmgTemplateZoneId> stack;
+			stack.push(startZone);
+
+			while(!stack.empty())
+			{
+				auto current = stack.top();
+				stack.pop();
+
+				if(visited.find(current) != visited.end())
+					continue;
+
+				visited.insert(current);
+
+				for(auto neighbor : roadGraph[current])
+				{
+					if(visited.find(neighbor) == visited.end())
+					{
+						stack.push(neighbor);
+					}
+				}
+			}
+
+			//Check if all zones in this component are still reachable
+			for(const auto & zone : zones)
+			{
+				if(zoneToComponent[zone.first] == component && !roadGraph[zone.first].empty())
+				{
+					if(visited.find(zone.first) == visited.end())
+					{
+						canRemove = false;
+						break;
+					}
+				}
+			}
+
+			if(!canRemove)
+			{
+				//Restore connection if removing it would break connectivity
+				roadGraph[conn.getZoneA()].insert(conn.getZoneB());
+				roadGraph[conn.getZoneB()].insert(conn.getZoneA());
+			}
+			else
+			{
+				//Mark this connection as having no road
+				for(auto & zonePtr : zones)
+				{
+					for(auto & connection : zonePtr.second->getConnections())
+					{
+						if(connection.getId() == conn.getId())
+						{
+							const_cast<rmg::ZoneConnection&>(connection) = conn; //FIXME: avoid const_cast
+							break;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
 const TDistanceMap& CZonePlacer::getDistanceMap()
 const TDistanceMap& CZonePlacer::getDistanceMap()
 {
 {
 	return distancesBetweenZones;
 	return distancesBetweenZones;

+ 1 - 0
lib/rmg/CZonePlacer.h

@@ -46,6 +46,7 @@ public:
 	void placeOnGrid(vstd::RNG* rand);
 	void placeOnGrid(vstd::RNG* rand);
 	float scaleForceBetweenZones(const std::shared_ptr<Zone> zoneA, const std::shared_ptr<Zone> zoneB) const;
 	float scaleForceBetweenZones(const std::shared_ptr<Zone> zoneA, const std::shared_ptr<Zone> zoneB) const;
 	void assignZones(vstd::RNG * rand);
 	void assignZones(vstd::RNG * rand);
+	void dropRandomRoads(vstd::RNG * rand);
 
 
 	const TDistanceMap & getDistanceMap();
 	const TDistanceMap & getDistanceMap();
 	
 	

+ 1 - 2
lib/rmg/modificators/ConnectionsPlacer.cpp

@@ -477,8 +477,7 @@ void ConnectionsPlacer::collectNeighbourZones()
 
 
 bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection) const
 bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection) const
 {
 {
-	return connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE ||
-		(connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM && zone.getRand().nextDouble(0, 1) >= 0.5f);
+	return connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE;
 }
 }
 
 
 void ConnectionsPlacer::createBorder()
 void ConnectionsPlacer::createBorder()