#include #include #include #include // Tile types for the dungeon // Values start at 1 so default-initialized cells (0) don't match any tile enum class Tile : int { Empty = 1, Wall = 2, Floor = 3 }; constexpr size_t DungeonWidth = 16; constexpr size_t DungeonHeight = 16; using World = WFC::Array2D; // Concrete types needed for constrainer lambda signatures using IDMap = WFC::VariableIDMap; constexpr size_t WorldSize = DungeonWidth * DungeonHeight; using WaveT = WFC::Wave; using QueueT = WFC::WFCQueue; using ConstrainerT = WFC::Constrainer; void printDungeon(const World& world) { for (size_t y = 0; y < DungeonHeight; ++y) { for (size_t x = 0; x < DungeonWidth; ++x) { switch (world.at(x, y)) { case Tile::Floor: std::cout << '.'; break; case Tile::Wall: std::cout << '#'; break; case Tile::Empty: std::cout << ' '; break; } } std::cout << '\n'; } } int main() { std::cout << "2D Dungeon WFC Demo\n"; std::cout << "Dungeon size: " << DungeonWidth << "x" << DungeonHeight << "\n\n"; using DungeonBuilder = WFC::Builder ::DefineIDs ::Variable ::Constrain val, ConstrainerT& constrainer) { auto [x, y] = world.getCoord(index); // floor cannot be adjacent to empty space constrainer.template Exclude(world.getCoordOffset(x, y, -1, 0)); // Left constrainer.template Exclude(world.getCoordOffset(x, y, 1, 0)); // Right constrainer.template Exclude(world.getCoordOffset(x, y, 0, -1)); // Up constrainer.template Exclude(world.getCoordOffset(x, y, 0, 1)); // Down })> ::SetInitialState(world.getId({static_cast(x), 0})); constrainer.template Exclude(world.getId({static_cast(x), static_cast(world.height() - 1)})); } for (size_t y = 0; y < world.height(); ++y) { constrainer.template Exclude(world.getId({0, static_cast(y)})); constrainer.template Exclude(world.getId({static_cast(world.width() - 1), static_cast(y)})); } })> ::Build; World world{}; bool success = WFC::Run(world, 0xDEADBEEF); if (!success) { std::cout << "WFC solver failed!\n"; } printDungeon(world); return 0; }