random generator bugfix

This commit is contained in:
Connor
2026-02-09 23:54:49 +09:00
parent 8567d86cee
commit f88f4c16a0
4 changed files with 43 additions and 11 deletions

View File

@@ -47,27 +47,39 @@ int main() {
auto [x, y] = world.getCoord(index); auto [x, y] = world.getCoord(index);
// enable walls in 3x3 area around floor (without center)
// must come before Exclude to avoid collapsing then un-collapsing cells
for (int dy = -1; dy <= 1; ++dy) {
for (int dx = -1; dx <= 1; ++dx) {
if (dx == 0 && dy == 0) continue;
constrainer.template Include<Tile::Wall>(world.getCoordOffset(x, y, dx, dy));
}
}
// floor cannot be adjacent to empty space // floor cannot be adjacent to empty space
constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, -1, 0)); // Left constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, -1, 0)); // Left
constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, 1, 0)); // Right constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, 1, 0)); // Right
constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, 0, -1)); // Up constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, 0, -1)); // Up
constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, 0, 1)); // Down constrainer.template Exclude<Tile::Empty>(world.getCoordOffset(x, y, 0, 1)); // Down
})> })>
::SetInitialState<decltype([](World& world, auto& constrainer, auto& rng) constexpr { ::SetInitialState<decltype([](World& world, auto& constrainer, auto&) constexpr {
// disable walls everywhere by default
for (size_t i = 0; i < world.size(); ++i) {
constrainer.template Exclude<Tile::Wall>(i);
}
// make it impossible for the edge to be floor // make it impossible for the edge to be floor
for (size_t x = 0; x < world.width(); ++x) { for (size_t x = 0; x < world.width(); ++x) {
constrainer.template Exclude<Tile::Floor>(world.getId({static_cast<int>(x), 0})); constrainer.template Exclude<Tile::Floor>(world.getId({static_cast<int>(x), 0}));
constrainer.template Exclude<Tile::Floor>(world.getId({static_cast<int>(x), static_cast<int>(world.height() - 1)})); constrainer.template Exclude<Tile::Floor>(world.getId({static_cast<int>(x), static_cast<int>(world.height() - 1)}));
} }
for (size_t y = 0; y < world.height(); ++y) { // seed floor tiles to kick-start dungeon generation
constrainer.template Exclude<Tile::Wall>(world.getId({0, static_cast<int>(y)})); constrainer.template Only<Tile::Floor>(world.getId({2, 2}));
constrainer.template Exclude<Tile::Wall>(world.getId({static_cast<int>(world.width() - 1), static_cast<int>(y)}));
}
})> })>
::SetRandomSelector<WFC::AdvancedRandomSelector<Tile>>
::Build; ::Build;
World world{}; World world{};
bool success = WFC::Run<DungeonBuilder>(world, 0xDEADBEEF); bool success = WFC::Run<DungeonBuilder>(world, std::random_device{}());
if (!success) { if (!success) {
std::cout << "WFC solver failed!\n"; std::cout << "WFC solver failed!\n";
} }

View File

@@ -117,6 +117,23 @@ public:
ApplyMask(cellId, 1 << value.InternalIndex); ApplyMask(cellId, 1 << value.InternalIndex);
} }
/**
* @brief Re-enable specific values for a cell (OR bits back in)
* @param cellId The ID of the cell to modify
*/
template <typename IDMapT::Type ... IncludedValues>
void Include(size_t cellId) {
static_assert(sizeof...(IncludedValues) > 0, "At least one included value must be provided");
if (m_wave.IsCollapsed(cellId)) return; // don't un-collapse decided cells
auto indices = IDMapT::template ValuesToIndices<IncludedValues...>();
m_wave.Enable(cellId, BitContainerT::GetMask(indices));
}
void Include(WorldValue<typename IDMapT::Type> value, size_t cellId) {
if (m_wave.IsCollapsed(cellId)) return;
m_wave.Enable(cellId, 1 << value.InternalIndex);
}
private: private:
void ApplyMask(size_t cellId, MaskType mask) { void ApplyMask(size_t cellId, MaskType mask) {
bool wasCollapsed = m_wave.IsCollapsed(cellId); bool wasCollapsed = m_wave.IsCollapsed(cellId);

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <random>
namespace WFC { namespace WFC {
/** /**
@@ -27,14 +29,14 @@ public:
template <typename VarT> template <typename VarT>
class AdvancedRandomSelector { class AdvancedRandomSelector {
private: private:
std::mt19937& m_rng; mutable std::mt19937 m_rng;
public: public:
explicit AdvancedRandomSelector(std::mt19937& rng) : m_rng(rng) {} explicit AdvancedRandomSelector(uint32_t seed = 0x12345678) : m_rng(seed) {}
uint32_t rng(uint32_t max) const { uint32_t rng(uint32_t max) const {
std::uniform_int_distribution<uint32_t> dist(0, max); std::uniform_int_distribution<uint32_t> dist(0, max - 1);
return dist(m_rng); return dist(m_rng);
} }
}; };

View File

@@ -27,6 +27,7 @@ public:
public: public:
void Collapse(size_t index, ElementT mask) { m_data[index] &= mask; } void Collapse(size_t index, ElementT mask) { m_data[index] &= mask; }
void Enable(size_t index, ElementT mask) { m_data[index] |= mask; }
size_t size() const { return m_data.size(); } size_t size() const { return m_data.size(); }
size_t Entropy(size_t index) const { return std::popcount(m_data[index]); } size_t Entropy(size_t index) const { return std::popcount(m_data[index]); }
bool IsCollapsed(size_t index) const { return Entropy(index) == 1; } bool IsCollapsed(size_t index) const { return Entropy(index) == 1; }