wfc compiled
This commit is contained in:
249
README.md
249
README.md
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user