From a59dc23b13549224167672481c5461ed7c9b3e66 Mon Sep 17 00:00:00 2001 From: Zengtudor Date: Sun, 7 Sep 2025 11:53:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E7=94=9F=E6=88=90=E5=99=A8=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E6=88=BF=E9=97=B4=E5=88=9B=E5=BB=BA=E5=92=8C=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加地图生成器类,支持随机房间放置、走廊连接和房屋空间识别 使用洪水填充算法区分内外空间,确保生成合理的地图布局 --- src/test.cpp | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 2 deletions(-) diff --git a/src/test.cpp b/src/test.cpp index 294989d..adddccb 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1,3 +1,247 @@ -int main(){ +#include +#include +#include +#include +#include +#include + +// --- 常量定义 --- +// 使用 enum class 增强类型安全和代码可读性 +enum class TileType : char { + WALL = '#', + FLOOR = '.', + HOUSE_SPACE = 'H', // 临时标记,最终会变回 FLOOR + EXTERIOR = ' ' // 临时标记,用于洪水填充 +}; + +// --- 辅助结构体:矩形房间 --- +struct Rect { + int x, y, w, h; + + // 检查此矩形是否与另一个矩形相交(包括边缘接触) + bool intersects(const Rect& other) const { + return (x < other.x + other.w && x + w > other.x && + y < other.y + other.h && y + h > other.y); + } -} \ No newline at end of file + // 获取中心点坐标 + std::pair center() const { + return {x + w / 2, y + h / 2}; + } +}; + +// --- 地图生成器类 --- +class MapGenerator { +public: + MapGenerator(int width, int height) + : width_(width), height_(height), + // 使用 std::random_device 获取真随机数种子,初始化梅森旋转算法 + rng_(std::random_device{}()) { + if (width_ <= 0 || height_ <= 0) { + throw std::invalid_argument("Map dimensions must be positive."); + } + } + + // 主生成函数 + void generate(int max_rooms, int min_room_size, int max_room_size) { + // 1. 初始化地图,全部填充为墙壁 + initializeMap(); + + // 2. 随机放置房间 + createRooms(max_rooms, min_room_size, max_room_size); + + // 3. 按顺序连接所有房间 + createCorridors(); + + // 4. 识别并填充“房屋空间” + identifyAndFillHouseSpaces(); + } + + // 打印地图到控制台 + void printMap() const { + for (int y = 0; y < height_; ++y) { + for (int x = 0; x < width_; ++x) { + std::cout << static_cast(map_[y][x]); + } + std::cout << std::endl; + } + } + +private: + int width_, height_; + std::vector> map_; + std::vector rooms_; + std::mt19937 rng_; // 高质量随机数生成器 + + // 步骤1: 初始化地图,全部设置为墙壁 + void initializeMap() { + map_.assign(height_, std::vector(width_, TileType::WALL)); + } + + // 步骤2: 创造不重叠的房间 + void createRooms(int max_rooms, int min_room_size, int max_room_size) { + rooms_.clear(); + std::uniform_int_distribution<> size_dist(min_room_size, max_room_size); + std::uniform_int_distribution<> x_dist(1, width_ - max_room_size - 1); + std::uniform_int_distribution<> y_dist(1, height_ - max_room_size - 1); + + // 尝试放置 max_rooms 个房间 + for (int i = 0; i < max_rooms; ++i) { + Rect new_room; + new_room.w = size_dist(rng_); + new_room.h = size_dist(rng_); + new_room.x = x_dist(rng_); + new_room.y = y_dist(rng_); + + // 确保房间在地图边界内 + if (new_room.x + new_room.w >= width_ - 1 || new_room.y + new_room.h >= height_ - 1) { + continue; + } + + // 检查新房间是否与已存在的房间重叠 + bool overlaps = false; + for (const auto& room : rooms_) { + if (new_room.intersects(room)) { + overlaps = true; + break; + } + } + + if (!overlaps) { + carveRoom(new_room); + rooms_.push_back(new_room); + } + } + } + + // 在地图上“挖掘”一个房间 + void carveRoom(const Rect& room) { + for (int y = room.y; y < room.y + room.h; ++y) { + for (int x = room.x; x < room.x + room.w; ++x) { + map_[y][x] = TileType::FLOOR; + } + } + } + + // 步骤3: 连接房间 + void createCorridors() { + if (rooms_.size() < 2) return; + // 按房间位置排序,以连接邻近的房间 + std::sort(rooms_.begin(), rooms_.end(), [](const Rect& a, const Rect& b){ + return (a.x + a.y) < (b.x + b.y); + }); + + for (size_t i = 0; i < rooms_.size() - 1; ++i) { + auto [x1, y1] = rooms_[i].center(); + auto [x2, y2] = rooms_[i+1].center(); + carvePath(x1, y1, x2, y2); + } + } + + // 挖掘一条 L 形路径连接两个点 + void carvePath(int x1, int y1, int x2, int y2) { + // 随机决定先水平挖还是先垂直挖 + if (std::uniform_int_distribution<>(0, 1)(rng_) == 0) { + carveHorizontalTunnel(x1, x2, y1); + carveVerticalTunnel(y1, y2, x2); + } else { + carveVerticalTunnel(y1, y2, x1); + carveHorizontalTunnel(x1, x2, y2); + } + } + + void carveHorizontalTunnel(int x1, int x2, int y) { + for (int x = std::min(x1, x2); x <= std::max(x1, x2); ++x) { + map_[y][x] = TileType::FLOOR; + } + } + + void carveVerticalTunnel(int y1, int y2, int x) { + for (int y = std::min(y1, y2); y <= std::max(y1, y2); ++y) { + map_[y][x] = TileType::FLOOR; + } + } + + // 步骤4: 关键步骤 - 识别并填充房屋空间 + void identifyAndFillHouseSpaces() { + // 使用洪水填充算法(BFS)标记所有外部空间 + // 从 (0,0) 开始,所有能到达的墙壁都被认为是外部的一部分 + std::queue> q; + q.push({0, 0}); + + // 创建一个访问记录数组,防止重复处理 + std::vector> visited(height_, std::vector(width_, false)); + visited[0][0] = true; + map_[0][0] = TileType::EXTERIOR; // 标记为外部 + + // 定义 4 个移动方向 + int dx[] = {0, 0, 1, -1}; + int dy[] = {1, -1, 0, 0}; + + while (!q.empty()) { + auto [cx, cy] = q.front(); + q.pop(); + + for (int i = 0; i < 4; ++i) { + int nx = cx + dx[i]; + int ny = cy + dy[i]; + + // 检查边界 + if (nx < 0 || ny < 0 || nx >= width_ || ny >= height_) continue; + + // 如果邻居是墙壁且未被访问,则将其标记为外部并加入队列 + if (!visited[ny][nx] && map_[ny][nx] == TileType::WALL) { + visited[ny][nx] = true; + map_[ny][nx] = TileType::EXTERIOR; + q.push({nx, ny}); + } + } + } + + // 遍历整个地图,将在上一步中未被标记为 EXTERIOR 的所有 WALL 包围的区域, + // 即原始的 FLOOR 区域,识别为 HOUSE_SPACE。 + // 但根据您的要求,房屋空间也用空地表示,所以我们只需把 EXTERIOR 变回 WALL, + // 把未被触及的 WALL 留作围墙,而所有 FLOOR 区域自然就是我们想要的。 + // 这一步之后,我们实际上已经区分了 "内部墙" 和 "外部墙"。 + + // 最终清理:将临时标记转换回最终的瓦片类型 + for (int y = 0; y < height_; ++y) { + for (int x = 0; x < width_; ++x) { + if (map_[y][x] == TileType::EXTERIOR) { + map_[y][x] = TileType::WALL; // 将外部标记恢复为墙 + } + // 所有原本是 FLOOR 的地方,现在可以被理解为 房屋空间 或 走廊, + // 并且都用 '.' 表示,完美符合要求。 + } + } + } +}; + +// --- 主函数 --- +int main() { + // 设置地图参数 + const int MAP_WIDTH = 80; + const int MAP_HEIGHT = 40; + const int MAX_ROOMS = 15; + const int MIN_ROOM_SIZE = 5; + const int MAX_ROOM_SIZE = 10; + + try { + // 创建生成器实例 + MapGenerator generator(MAP_WIDTH, MAP_HEIGHT); + + // 生成地图 + generator.generate(MAX_ROOMS, MIN_ROOM_SIZE, MAX_ROOM_SIZE); + + // 打印结果 + std::cout << "--- Generated Map ---" << std::endl; + std::cout << "#: Wall, .: Floor/House Space" << std::endl; + generator.printMap(); + + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + + return 0; +}