wfc compiled

This commit is contained in:
cdemeyer-teachx
2025-08-24 17:14:46 +09:00
parent de5317967d
commit 76ad49b05a
31 changed files with 973 additions and 1785 deletions

249
README.md
View File

@@ -1,45 +1,33 @@
# N-Dimensional Wave Function Collapse (nd-wfc)
# N-Dimensional Wave Function Collapse (WFC) Library
An optimized C++ implementation of the Wave Function Collapse algorithm that supports multiple dimensions (2D, 3D, 4D, etc.).
A templated C++20 Wave Function Collapse engine that can work with 2D grids, 3D grids, Sudoku, Picross, and any other constraint satisfaction problem. The engine is coordinate-system agnostic and can work with any graph-like structure.
## Features
- **N-dimensional support**: Works with 2D, 3D, 4D, and higher dimensions
- **Optimized performance**: Efficient constraint propagation and entropy calculation
- **Flexible tile system**: Support for weighted tiles and custom constraints
- **Third-party integration**: Built-in support for JSON (RapidJSON), textures (STB), and 3D models (Assimp)
- **SDL2 graphics**: Windowing and rendering capabilities for visualization
- **Comprehensive testing**: Google Test integration for robust testing
- **Templated Design**: Works with any World type that satisfies the `WorldConcept`
- **Builder Pattern**: Easy to use fluent interface for setting up WFC systems
- **Constraint System**: Flexible constraint definition using lambda functions
- **Multiple World Types**: Built-in support for 2D/3D arrays, graphs, and Sudoku grids
- **Coordinate Agnostic**: Works without coordinate systems using only cell IDs
- **C++20**: Uses modern C++ features like concepts, ranges, and smart pointers
## Project Structure
```
nd-wfc/
├── CMakeLists.txt # Main CMake configuration
├── cmake/
│ └── nd-wfc-config.cmake.in # CMake package config
├── include/nd-wfc/ # Public headers
│ ├── types.hpp # Core types and enums
│ ├── wfc.hpp # Main WFC class
── grid.hpp # N-dimensional grid
│ ├── wave.hpp # Wave function representation
│ ├── propagator.hpp # Constraint propagator
│ ├── entropy.hpp # Entropy calculator
│ ├── constraint.hpp # Constraint management
│ └── tile.hpp # Tile system
│ ├── wfc.h # Main include file
│ ├── wfc.hpp # Core WFC classes and builder pattern
── worlds.hpp # Built-in World type implementations
├── src/ # Implementation files
├── tests/ # Google Test files
│ ├── CMakeLists.txt
── test_main.cpp
│ └── test_wfc.cpp
── main.cpp # Basic test program
├── examples/ # Example programs
│ ├── CMakeLists.txt
│ ├── basic_wfc_2d.cpp
│ ├── basic_wfc_3d.cpp
── texture_generation.cpp
│ └── model_generation.cpp
├── thirdparty/ # External dependencies
│ └── CMakeLists.txt
│ ├── tilemap_example.cpp # 2D tilemap generation
│ ├── sudoku_example.cpp # Full 9x9 Sudoku solver
── simple_sudoku_example.cpp # Simple 2x2 Sudoku for testing
└── build/ # Build directory (generated)
```
@@ -48,7 +36,7 @@ nd-wfc/
### Prerequisites
- CMake 3.16 or higher
- C++17 compatible compiler (GCC 8+, Clang 8+, MSVC 2019+)
- C++20 compatible compiler (GCC 10+, Clang 10+, MSVC 2019+)
- Git
### Build Instructions
@@ -66,78 +54,186 @@ cd build
cmake ..
# Build the project
cmake --build . --config Release
make
# Run tests
ctest --output-on-failure
# Run basic test
./src/nd-wfc-main
# Run examples
./examples/basic_wfc_2d
./examples/tilemap_example
./examples/simple_sudoku_example
```
### CMake Options
- `ND_WFC_BUILD_TESTS=ON/OFF` - Build tests (default: ON)
- `ND_WFC_BUILD_EXAMPLES=ON/OFF` - Build examples (default: ON)
- `ND_WFC_USE_SYSTEM_LIBS=ON/OFF` - Use system libraries instead of bundled (default: OFF)
## Usage
### Basic 2D Example
### Basic Example (2x2 Grid)
```cpp
#include "nd-wfc/wfc.hpp"
#include "nd-wfc/types.hpp"
#include <nd-wfc/wfc.h>
#include <iostream>
int main() {
// Create a 10x10 grid
nd_wfc::Size<2> size = {10, 10};
enum SimpleTiles { A = 1, B = 2 };
// Define tile types
std::vector<nd_wfc::TileId> tiles = {0, 1, 2, 3};
auto wfc = WFC::Builder<WFC::Array2D<uint8_t, 2, 2>>()
.variable(A, [](WFC::Array2D<uint8_t, 2, 2>& world, int worldID,
WFC::Constrainer& constrainer, uint8_t var) {
auto [x, y] = world.getCoord(worldID);
if (y > 0) constrainer.only((y-1) * 2 + x, B);
if (y < 1) constrainer.only((y+1) * 2 + x, B);
if (x > 0) constrainer.only(y * 2 + (x-1), B);
if (x < 1) constrainer.only(y * 2 + (x+1), B);
})
.variable(B, [](WFC::Array2D<uint8_t, 2, 2>& world, int worldID,
WFC::Constrainer& constrainer, uint8_t var) {
auto [x, y] = world.getCoord(worldID);
if (y > 0) constrainer.only((y-1) * 2 + x, A);
if (y < 1) constrainer.only((y+1) * 2 + x, A);
if (x > 0) constrainer.only(y * 2 + (x-1), A);
if (x < 1) constrainer.only(y * 2 + (x+1), A);
})
.build(WFC::Array2D<uint8_t, 2, 2>());
// Define constraints (adjacency rules)
std::vector<nd_wfc::Constraint> constraints = {
{0, 0, nd_wfc::Direction::North}, // Tile 0 can connect to tile 0 from North
{0, 1, nd_wfc::Direction::South}, // Tile 0 can connect to tile 1 from South
// ... more constraints
};
// Configure WFC
nd_wfc::WfcConfig config;
config.seed = 12345;
// Create and run WFC
nd_wfc::Wfc2D wfc(size, tiles, constraints, config);
nd_wfc::WfcResult result = wfc.run();
if (result == nd_wfc::WfcResult::Success) {
// Access the result
const auto& grid = wfc.getGrid();
// Use grid data...
if (wfc->run()) {
std::cout << "Solution found!" << std::endl;
// Print solution...
}
return 0;
}
```
### 3D Example
### 2D Tilemap Example
```cpp
// Create a 3D WFC instance
nd_wfc::Size<3> size = {8, 8, 8};
nd_wfc::Wfc3D wfc(size, tiles, constraints, config);
enum TileType { SEA = 1, BEACH = 2, LAND = 3 };
auto wfc = WFC::Builder<WFC::Array2D<uint8_t, 100, 100>>()
.variable(SEA, [](WFC::Array2D<uint8_t, 100, 100>& world, int worldID,
WFC::Constrainer& constrainer, uint8_t var) {
auto [x, y] = world.getCoord(worldID);
// Adjacent cells should be SEA or BEACH (no direct LAND next to SEA)
if (y > 0) constrainer.only((y-1) * 100 + x, SEA, BEACH);
if (y < 99) constrainer.only((y+1) * 100 + x, SEA, BEACH);
if (x > 0) constrainer.only(y * 100 + (x-1), SEA, BEACH);
if (x < 99) constrainer.only(y * 100 + (x+1), SEA, BEACH);
})
.variable(BEACH, [](WFC::Array2D<uint8_t, 100, 100>& world, int worldID,
WFC::Constrainer& constrainer, uint8_t var) {
auto [x, y] = world.getCoord(worldID);
// Adjacent cells should be SEA or LAND (BEACH is transition between them)
if (y > 0) constrainer.only((y-1) * 100 + x, SEA, LAND);
if (y < 99) constrainer.only((y+1) * 100 + x, SEA, LAND);
if (x > 0) constrainer.only(y * 100 + (x-1), SEA, LAND);
if (x < 99) constrainer.only(y * 100 + (x+1), SEA, LAND);
})
.variable(LAND, [](WFC::Array2D<uint8_t, 100, 100>& world, int worldID,
WFC::Constrainer& constrainer, uint8_t var) {
auto [x, y] = world.getCoord(worldID);
// Adjacent cells should be LAND or BEACH (no direct SEA next to LAND)
if (y > 0) constrainer.only((y-1) * 100 + x, LAND, BEACH);
if (y < 99) constrainer.only((y+1) * 100 + x, LAND, BEACH);
if (x > 0) constrainer.only(y * 100 + (x-1), LAND, BEACH);
if (x < 99) constrainer.only(y * 100 + (x+1), LAND, BEACH);
})
.build(WFC::Array2D<uint8_t, 100, 100>());
```
## Third-party Libraries
## World Types
The project includes several third-party libraries:
The library provides several built-in World types:
- **RapidJSON**: JSON parsing and serialization
- **STB**: Image loading and manipulation (STB Image, STB Image Write)
- **Assimp**: 3D model loading and processing
- **SDL2**: Graphics and windowing
- **Google Test**: Unit testing framework
### Array2D<T, Width, Height>
2D array with compile-time dimensions.
```cpp
WFC::Array2D<uint8_t, 100, 100> world;
```
### Array3D<T, Width, Height, Depth>
3D array with compile-time dimensions.
```cpp
WFC::Array3D<uint8_t, 10, 10, 10> world;
```
### GraphWorld<T>
Graph-based world for non-grid structures.
```cpp
WFC::GraphWorld<uint8_t> world(100); // 100 nodes
world.addEdge(0, 1); // Connect nodes
```
### SudokuWorld
Specialized 9x9 grid with Sudoku helper functions.
```cpp
WFC::SudokuWorld world;
auto rowCells = world.getRowCells(cellId);
auto colCells = world.getColumnCells(cellId);
auto boxCells = world.getBoxCells(cellId);
```
## Custom World Types
To create a custom World type, implement these requirements:
```cpp
struct MyWorld {
using ValueType = MyValueType;
using CoordType = MyCoordType;
size_t size() const;
int getId(CoordType coord) const;
CoordType getCoord(int id) const;
};
```
## API Reference
### Builder<WorldT>
- `Builder& variable(VarT value, ConstraintFunc func)` - Add a variable with constraint function
- `std::unique_ptr<WFC<WorldT>> build(WorldT world)` - Build the WFC instance
### WFC<WorldT>
- `bool run()` - Run the WFC algorithm
- `std::optional<VarT> getValue(int cellId)` - Get value at cell (if collapsed)
- `const std::unordered_set<int>& getPossibleValues(int cellId)` - Get possible values for cell
### Constrainer
- `void constrain(int cellId, const std::unordered_set<int>& allowedValues)` - Constrain to specific values
- `void only(int cellId, int value)` - Constrain to single value
- `void only(int cellId, int value1, int value2)` - Constrain to two values
## Examples
The `examples/` directory contains:
- `tilemap_example.cpp` - 2D tilemap generation with Sea/Beach/Land
- `sudoku_example.cpp` - Full 9x9 Sudoku solver
- `simple_sudoku_example.cpp` - Simple 2x2 Sudoku for testing
## Running Examples
```bash
cd build/examples
./tilemap_example # Generate a 100x100 tilemap
./simple_sudoku_example # Solve a simple 2x2 Sudoku
```
## Architecture
The WFC engine consists of:
1. **World Types**: Define the problem space and coordinate systems
2. **Builder Pattern**: Fluent interface for setting up variables and constraints
3. **Constraint System**: Lambda-based constraint definitions
4. **Wave Function Collapse Algorithm**: Core WFC implementation with observation and propagation
5. **Propagation Queue**: Efficient constraint propagation system
## Contributing
@@ -155,5 +251,4 @@ This project is licensed under the MIT License - see the LICENSE file for detail
## References
- Original WFC Algorithm: https://github.com/mxgmn/WaveFunctionCollapse
- N-dimensional WFC concepts and optimizations
- Various academic papers on constraint satisfaction and procedural generation
- Constraint satisfaction and procedural generation concepts