8 std::array<H3Index, 7> ring{};
10 if (gridDisk(cell, 1, ring.data()) != E_SUCCESS) {
17 for (
const auto &neighbor : ring) {
18 if (neighbor != H3_NULL && neighbor != cell && !isPentagon(neighbor)) {
29 std::unordered_set<H3Index, H3IndexHash> cells;
32 H3Error err = maxGridDiskSize(radius, &maxSize);
33 if (err != E_SUCCESS) {
34 spdlog::warn(describeH3Error(err));
38 std::vector<H3Index> disk(maxSize);
40 err = gridDisk(center, radius, disk.data());
41 if (err != E_SUCCESS) {
42 spdlog::warn(describeH3Error(err));
47 cells.reserve(maxSize);
48 for (
const auto &cell : disk) {
49 if (cell != H3_NULL && !isPentagon(cell) && isValidIndex(cell)) {
58std::unordered_set<H3Index, H3MazeGenerator::H3IndexHash>
61 std::unordered_set<H3Index, H3IndexHash> rooms;
62 std::unordered_set<H3Index, H3IndexHash> occupied;
64 for (
const auto &cell : allCells) {
65 if (occupied.contains(cell)) {
73 for (
auto neighbors =
getNeighbors(cell);
const auto &neighbor : neighbors) {
74 occupied.insert(neighbor);
76 occupied.insert(cell);
84 const std::unordered_set<H3Index, H3IndexHash> &rooms) {
86 std::vector<H3Index> roomNeighbors;
91 std::array<H3Index, 19> ring = {};
93 constexpr int kRingSize = 2;
94 if (E_SUCCESS != gridDisk(room, kRingSize, ring.data())) {
97 for (
const auto &candidate : ring) {
98 if (candidate == H3_NULL || candidate == room) {
103 if (rooms.contains(candidate)) {
104 int64_t distance = 0;
105 if (gridDistance(room, candidate, &distance) == E_SUCCESS && distance == kRingSize) {
106 roomNeighbors.emplace_back(candidate);
111 return roomNeighbors;
120 for (
const auto &n1 : neighbors1) {
121 if (std::ranges::any_of(neighbors2, [&](
const auto &n2) {
return n1 == n2; })) {
130std::unordered_set<H3Index, H3MazeGenerator::H3IndexHash>
138 std::unordered_set<H3Index, H3IndexHash> passages;
139 std::unordered_set<H3Index, H3IndexHash> visitedRooms;
142 std::vector<std::pair<H3Index, H3Index>> wallList;
145 const auto roomsVec = std::vector(rooms.begin(), rooms.end());
146 std::uniform_int_distribution<size_t> startDist(0, roomsVec.size() - 1);
147 const H3Index startRoom = roomsVec[startDist(
rng_)];
150 visitedRooms.insert(startRoom);
151 passages.insert(startRoom);
154 for (
const auto neighborRooms =
getRoomNeighbors(startRoom, rooms);
const auto &neighborRoom : neighborRooms) {
155 if (!visitedRooms.contains(neighborRoom)) {
156 if (
auto wall =
findWallBetween(startRoom, neighborRoom); wall.has_value()) {
157 wallList.emplace_back(wall.value(), neighborRoom);
163 while (!wallList.empty()) {
165 std::uniform_int_distribution<size_t> wallDist(0, wallList.size() - 1);
166 const size_t wallIdx = wallDist(
rng_);
167 auto [wall, nextRoom] = wallList[wallIdx];
170 wallList.erase(wallList.begin() +
static_cast<long>(wallIdx));
173 if (visitedRooms.contains(nextRoom)) {
178 passages.insert(wall);
179 passages.insert(nextRoom);
180 visitedRooms.insert(nextRoom);
184 const auto &newNeighborRoom : newNeighborRooms) {
185 if (!visitedRooms.contains(newNeighborRoom)) {
186 if (
auto newWall =
findWallBetween(nextRoom, newNeighborRoom); newWall.has_value()) {
187 wallList.emplace_back(newWall.value(), newNeighborRoom);
193 spdlog::info(
"Prim's algorithm: visited {} rooms out of {}", visitedRooms.size(), rooms.size());
200 const std::unordered_set<H3Index, H3IndexHash> &passages) {
202 std::unordered_map<H3Index, int> distances;
203 std::queue<H3Index> queue;
206 distances[start] = 0;
208 H3Index farthest = start;
211 while (!queue.empty()) {
212 H3Index current = queue.front();
215 for (
auto neighbors =
getNeighbors(current);
const auto &neighbor : neighbors) {
216 if (passages.contains(neighbor) && !distances.contains(neighbor)) {
217 distances[neighbor] = distances[current] + 1;
218 queue.push(neighbor);
220 if (distances[neighbor] > maxDist) {
221 maxDist = distances[neighbor];
228 spdlog::info(
"Farthest room is at distance {}", maxDist);
234 int64_t distance = 0;
235 if (gridDistance(center, cell, &distance) != E_SUCCESS) {
240 return distance >= radius - 1;
245 spdlog::info(
"Generating H3 maze with entrances, center cell, radius={}", radius);
249 if (allCells.empty()) {
250 spdlog::error(
"No cells in radius");
251 return {std::unordered_set<H3Index>(), H3_NULL, H3_NULL};
254 spdlog::info(
"Total cells in area: {}", allCells.size());
258 spdlog::info(
"Created {} rooms for maze", rooms.size());
261 return {std::unordered_set<H3Index>(), H3_NULL, H3_NULL};
266 spdlog::info(
"Generated {} passages", passages.size());
269 std::vector<H3Index> borderRooms;
270 for (
const auto &room : rooms) {
271 if (passages.contains(room) &&
isOnBorder(room, centerCell, radius)) {
272 borderRooms.emplace_back(room);
276 H3Index entrance = H3_NULL;
277 if (!borderRooms.empty()) {
278 std::uniform_int_distribution<size_t> borderDist(0, borderRooms.size() - 1);
279 entrance = borderRooms[borderDist(
rng_)];
280 }
else if (!passages.empty()) {
281 entrance = *passages.begin();
288 std::unordered_set<H3Index> walls;
289 for (
const auto &cell : allCells) {
290 if (!passages.contains(cell)) {
295 double wallPercent = walls.size() * 100.0 / allCells.size();
296 spdlog::info(
"Maze generated: {} walls ({:.1f}%), {} passages ({:.1f}%)", walls.size(), wallPercent,
297 passages.size(), 100.0 - wallPercent);
298 spdlog::info(
"Entrance: {}, Exit: {}", entrance, exit);
301 return {walls, entrance, exit};
std::array< H3Index, 6 > getNeighbors(H3Index cell)
Gets the 6 neighbors of a hexagonal cell.
static std::vector< H3Index > getRoomNeighbors(H3Index room, const std::unordered_set< H3Index, H3IndexHash > &rooms)
Gets rooms that are neighbors at distance 2.
MazeResult generateMazeWithEntrances(H3Index centerCell, int radius)
Generates a maze with entrance and exit points.
std::unordered_set< H3Index, H3IndexHash > generateMazePrim(const std::unordered_set< H3Index, H3IndexHash > &rooms)
Generates maze passages using randomized Prim's algorithm.
std::unordered_set< H3Index, H3IndexHash > createRoomGrid(const std::unordered_set< H3Index, H3IndexHash > &allCells)
Creates a sparse room grid with minimum 2-cell spacing.
std::unordered_set< H3Index > generateMaze(H3Index centerCell, int radius)
Generates a maze centered at the given cell.
std::array< H3Index, 6 > neighbors_
Temporary storage for neighbor cells.
void mazeGenerated(const std::unordered_set< H3Index > &walls)
Emitted when maze generation is complete.
std::optional< H3Index > findWallBetween(H3Index room1, H3Index room2)
Finds the wall cell between two rooms.
H3MazeGenerator(QObject *parent=nullptr)
Constructs an H3MazeGenerator.
static bool isOnBorder(H3Index cell, H3Index center, int radius)
Checks if a cell is on the border of the maze area.
static std::unordered_set< H3Index, H3IndexHash > getCellsInRadius(H3Index center, int radius)
Gets all cells within a radius of the center.
std::mt19937 rng_
Random number generator for maze randomization.
H3Index findFarthestRoom(H3Index start, const std::unordered_set< H3Index, H3IndexHash > &passages)
Finds the room farthest from the start using BFS.
Result structure containing generated maze data.
Procedural maze generation on H3 hexagonal grids using Prim's algorithm.