2d dungeon
This commit is contained in:
46
demos/2DDungeon/CMakeLists.txt
Normal file
46
demos/2DDungeon/CMakeLists.txt
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(2d_dungeon_demo CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Add the main project as a subdirectory to get the nd-wfc library
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../src ${CMAKE_CURRENT_BINARY_DIR}/nd-wfc)
|
||||||
|
|
||||||
|
# Add compiler warnings
|
||||||
|
if(MSVC)
|
||||||
|
add_compile_options(/W4)
|
||||||
|
else()
|
||||||
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Create demo executable
|
||||||
|
add_executable(dungeon_demo main.cpp)
|
||||||
|
|
||||||
|
# Link to nd-wfc library
|
||||||
|
target_link_libraries(dungeon_demo PRIVATE nd-wfc)
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
target_include_directories(dungeon_demo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
# Set output directory and properties
|
||||||
|
set_target_properties(dungeon_demo PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
||||||
|
CXX_STANDARD 20
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
)
|
||||||
|
|
||||||
|
# Optional: Enable optimizations for release builds
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(dungeon_demo PRIVATE /O2)
|
||||||
|
else()
|
||||||
|
target_compile_options(dungeon_demo PRIVATE -O3 -march=native)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Ensure consistent runtime library settings for MSVC
|
||||||
|
if(MSVC)
|
||||||
|
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||||
|
target_compile_options(dungeon_demo PRIVATE $<$<CONFIG:Debug>:/MDd> $<$<CONFIG:Release>:/MD>)
|
||||||
|
endif()
|
||||||
18
demos/2DDungeon/Makefile
Normal file
18
demos/2DDungeon/Makefile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
BUILD_DIR := build
|
||||||
|
BUILD_TYPE ?= Debug
|
||||||
|
|
||||||
|
.PHONY: all clean run configure build
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
configure:
|
||||||
|
cmake -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE)
|
||||||
|
|
||||||
|
build: configure
|
||||||
|
cmake --build $(BUILD_DIR)
|
||||||
|
|
||||||
|
run: build
|
||||||
|
./$(BUILD_DIR)/bin/dungeon_demo
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
66
demos/2DDungeon/main.cpp
Normal file
66
demos/2DDungeon/main.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include <nd-wfc/wfc.hpp>
|
||||||
|
#include <nd-wfc/wfc_builder.hpp>
|
||||||
|
#include <nd-wfc/worlds.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// Tile types for the dungeon
|
||||||
|
enum class Tile : int {
|
||||||
|
Empty = 0,
|
||||||
|
Wall = 1,
|
||||||
|
Floor = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t DungeonWidth = 16;
|
||||||
|
constexpr size_t DungeonHeight = 16;
|
||||||
|
|
||||||
|
using World = WFC::Array2D<Tile, DungeonWidth, DungeonHeight>;
|
||||||
|
|
||||||
|
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<World>
|
||||||
|
::DefineIDs<Tile::Floor>
|
||||||
|
::Constrain<decltype([](const World& world, size_t index, WFC::WorldValue<Tile> val, auto& constrainer) constexpr {
|
||||||
|
|
||||||
|
auto [x, y] = world.getCoord(index);
|
||||||
|
|
||||||
|
// floor cannot be adjacent to empty space
|
||||||
|
constrainer.Exclude(Tile::Empty, world.getCoordOffset(x, y, -1, 0)); // Left
|
||||||
|
constrainer.Exclude(Tile::Empty, world.getCoordOffset(x, y, 1, 0)); // Right
|
||||||
|
constrainer.Exclude(Tile::Empty, world.getCoordOffset(x, y, 0, -1)); // Up
|
||||||
|
constrainer.Exclude(Tile::Empty, world.getCoordOffset(x, y, 0, 1)); // Down
|
||||||
|
})>
|
||||||
|
::DefineIDs<Tile::Wall, Tile::Empty>
|
||||||
|
::SetInitialState<decltype([](World& world, auto& constrainer, auto& rng) constexpr {
|
||||||
|
// make it impossible for the edge to be floor
|
||||||
|
for (size_t x = 0; x < world.width(); ++x) {
|
||||||
|
constrainer.Exclude(world.getId({x, 0}), Tile::Floor);
|
||||||
|
constrainer.Exclude(world.getId({x, world.height() - 1}), Tile::Floor);
|
||||||
|
}
|
||||||
|
for (size_t y = 0; y < world.height(); ++y) {
|
||||||
|
constrainer.Exclude(world.getId({0, y}), Tile::Wall);
|
||||||
|
constrainer.Exclude(world.getId({world.width() - 1, y}), Tile::Wall);
|
||||||
|
}
|
||||||
|
})>
|
||||||
|
::Build;
|
||||||
|
|
||||||
|
World world{};
|
||||||
|
DungeonBuilder::Solve(world, 0xDEADBEEF);
|
||||||
|
printDungeon(world);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
32
demos/sudoku/Makefile
Normal file
32
demos/sudoku/Makefile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
BUILD_DIR := build
|
||||||
|
BUILD_TYPE ?= Release
|
||||||
|
|
||||||
|
.PHONY: all clean run test benchmark configure build
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
configure:
|
||||||
|
cmake -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE)
|
||||||
|
|
||||||
|
build: configure
|
||||||
|
cmake --build $(BUILD_DIR) -j$(shell nproc)
|
||||||
|
|
||||||
|
run: build
|
||||||
|
./$(BUILD_DIR)/bin/sudoku_wfc_demo
|
||||||
|
|
||||||
|
test: build
|
||||||
|
@if [ -f ./$(BUILD_DIR)/bin/sudoku_tests ]; then \
|
||||||
|
./$(BUILD_DIR)/bin/sudoku_tests; \
|
||||||
|
else \
|
||||||
|
echo "Tests not available (Google Test not found)"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
benchmark: build
|
||||||
|
@if [ -f ./$(BUILD_DIR)/bin/sudoku_benchmarks ]; then \
|
||||||
|
./$(BUILD_DIR)/bin/sudoku_benchmarks; \
|
||||||
|
else \
|
||||||
|
echo "Benchmarks not available (Google Benchmark not found)"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Simple build script for the Sudoku demo
|
|
||||||
|
|
||||||
set -e # Exit on any error
|
|
||||||
|
|
||||||
echo "Building Fast Sudoku Loader and Solution Validator..."
|
|
||||||
echo "======================================================"
|
|
||||||
|
|
||||||
# Create build directory if it doesn't exist
|
|
||||||
if [ ! -d "build" ]; then
|
|
||||||
echo "Creating build directory..."
|
|
||||||
mkdir build
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd build
|
|
||||||
|
|
||||||
# Configure with CMake
|
|
||||||
echo "Configuring with CMake..."
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
|
||||||
|
|
||||||
# Build the project
|
|
||||||
echo "Building project..."
|
|
||||||
make -j$(nproc)
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Build completed successfully!"
|
|
||||||
echo "Run the demo with: ./bin/sudoku_demo"
|
|
||||||
|
|
||||||
# Check what executables were built
|
|
||||||
available_executables=""
|
|
||||||
if [ -f "./bin/sudoku_tests" ]; then
|
|
||||||
echo "Run tests with: ./bin/sudoku_tests"
|
|
||||||
available_executables="$available_executables test"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "./bin/sudoku_benchmarks" ]; then
|
|
||||||
echo "Run benchmarks with: ./bin/sudoku_benchmarks"
|
|
||||||
available_executables="$available_executables benchmark"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Handle different run modes
|
|
||||||
case "$1" in
|
|
||||||
"run")
|
|
||||||
echo "Running demo..."
|
|
||||||
./bin/sudoku_demo
|
|
||||||
;;
|
|
||||||
"test")
|
|
||||||
if [ -f "./bin/sudoku_tests" ]; then
|
|
||||||
echo "Running tests..."
|
|
||||||
./bin/sudoku_tests
|
|
||||||
else
|
|
||||||
echo "Tests not available (Google Test not found)"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
"benchmark")
|
|
||||||
if [ -f "./bin/sudoku_benchmarks" ]; then
|
|
||||||
echo "Running benchmarks..."
|
|
||||||
./bin/sudoku_benchmarks
|
|
||||||
else
|
|
||||||
echo "Benchmarks not available (Google Benchmark not found)"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
"all")
|
|
||||||
echo "Running demo..."
|
|
||||||
./bin/sudoku_demo
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if [ -f "./bin/sudoku_tests" ]; then
|
|
||||||
echo "Running tests..."
|
|
||||||
./bin/sudoku_tests
|
|
||||||
echo ""
|
|
||||||
else
|
|
||||||
echo "Tests not available (Google Test not found)"
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "./bin/sudoku_benchmarks" ]; then
|
|
||||||
echo "Running benchmarks..."
|
|
||||||
./bin/sudoku_benchmarks
|
|
||||||
else
|
|
||||||
echo "Benchmarks not available (Google Benchmark not found)"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if [ -n "$1" ]; then
|
|
||||||
echo "Unknown option: $1"
|
|
||||||
echo "Usage: $0 [run|test|benchmark|all]"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -9,25 +9,25 @@ namespace WFC {
|
|||||||
/**
|
/**
|
||||||
* @brief 2D Array World implementation
|
* @brief 2D Array World implementation
|
||||||
*/
|
*/
|
||||||
template<typename T, size_t Width, size_t Height>
|
template<typename T, size_t Width, size_t Height, bool Looping = false>
|
||||||
class Array2D {
|
class Array2D {
|
||||||
public:
|
public:
|
||||||
using ValueType = T;
|
using ValueType = T;
|
||||||
using CoordType = std::tuple<int, int>;
|
using CoordType = std::tuple<int, int>;
|
||||||
|
|
||||||
Array2D() = default;
|
constexpr Array2D() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the total size of the world
|
* @brief Get the total size of the world
|
||||||
*/
|
*/
|
||||||
size_t size() const {
|
constexpr size_t size() const {
|
||||||
return Width * Height;
|
return Width * Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert coordinates to cell ID
|
* @brief Convert coordinates to cell ID
|
||||||
*/
|
*/
|
||||||
int getId(CoordType coord) const {
|
constexpr int getId(CoordType coord) const {
|
||||||
auto [x, y] = coord;
|
auto [x, y] = coord;
|
||||||
return y * Width + x;
|
return y * Width + x;
|
||||||
}
|
}
|
||||||
@@ -35,66 +35,86 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Convert cell ID to coordinates
|
* @brief Convert cell ID to coordinates
|
||||||
*/
|
*/
|
||||||
CoordType getCoord(int id) const {
|
constexpr CoordType getCoord(int id) const {
|
||||||
int x = id % Width;
|
int x = id % Width;
|
||||||
int y = id / Width;
|
int y = id / Width;
|
||||||
return {x, y};
|
return {x, y};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetCoordOffsetX(int x, int dx) const {
|
||||||
|
if constexpr (Looping) {
|
||||||
|
return (x + dx + Width) % Width;
|
||||||
|
} else {
|
||||||
|
return static_cast<size_t>(std::clamp(x + dx, 0, static_cast<int>(Width) - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetCoordOffsetY(int y, int dy) const {
|
||||||
|
if constexpr (Looping) {
|
||||||
|
return (y + dy + Width) % Width;
|
||||||
|
} else {
|
||||||
|
return static_cast<size_t>(std::clamp(y + dy, 0, static_cast<int>(Height) - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t getCoordOffset(int x, int y, int dx, int dy) const {
|
||||||
|
return getId({GetCoordOffsetX(x, dx), GetCoordOffsetY(y, dy)});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get width of the array
|
* @brief Get width of the array
|
||||||
*/
|
*/
|
||||||
size_t width() const { return Width; }
|
constexpr size_t width() const { return Width; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get height of the array
|
* @brief Get height of the array
|
||||||
*/
|
*/
|
||||||
size_t height() const { return Height; }
|
constexpr size_t height() const { return Height; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access element at coordinates
|
* @brief Access element at coordinates
|
||||||
*/
|
*/
|
||||||
T& at(int x, int y) {
|
constexpr T& at(int x, int y) {
|
||||||
return data_[y * Width + x];
|
return data_[y * Width + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access element at coordinates (const)
|
* @brief Access element at coordinates (const)
|
||||||
*/
|
*/
|
||||||
const T& at(int x, int y) const {
|
constexpr const T& at(int x, int y) const {
|
||||||
return data_[y * Width + x];
|
return data_[y * Width + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access element by ID
|
* @brief Access element by ID
|
||||||
*/
|
*/
|
||||||
T& operator[](int id) {
|
constexpr T& operator[](int id) {
|
||||||
return data_[id];
|
return data_[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access element by ID (const)
|
* @brief Access element by ID (const)
|
||||||
*/
|
*/
|
||||||
const T& operator[](int id) const {
|
constexpr const T& operator[](int id) const {
|
||||||
return data_[id];
|
return data_[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set value at specific index (required by WFC)
|
* @brief Set value at specific index (required by WFC)
|
||||||
*/
|
*/
|
||||||
void setValue(size_t index, T value) {
|
constexpr void setValue(size_t index, T value) {
|
||||||
data_[index] = value;
|
data_[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get value at specific index
|
* @brief Get value at specific index
|
||||||
*/
|
*/
|
||||||
T getValue(size_t index) const {
|
constexpr T getValue(size_t index) const {
|
||||||
return data_[index];
|
return data_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<T, Width * Height> data_;
|
std::array<T, Width * Height> data_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,19 +126,19 @@ public:
|
|||||||
using ValueType = T;
|
using ValueType = T;
|
||||||
using CoordType = std::tuple<int, int, int>;
|
using CoordType = std::tuple<int, int, int>;
|
||||||
|
|
||||||
Array3D() = default;
|
constexpr Array3D() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the total size of the world
|
* @brief Get the total size of the world
|
||||||
*/
|
*/
|
||||||
size_t size() const {
|
constexpr size_t size() const {
|
||||||
return Width * Height * Depth;
|
return Width * Height * Depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert coordinates to cell ID
|
* @brief Convert coordinates to cell ID
|
||||||
*/
|
*/
|
||||||
int getId(CoordType coord) const {
|
constexpr int getId(CoordType coord) const {
|
||||||
auto [x, y, z] = coord;
|
auto [x, y, z] = coord;
|
||||||
return z * (Width * Height) + y * Width + x;
|
return z * (Width * Height) + y * Width + x;
|
||||||
}
|
}
|
||||||
@@ -126,7 +146,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Convert cell ID to coordinates
|
* @brief Convert cell ID to coordinates
|
||||||
*/
|
*/
|
||||||
CoordType getCoord(int id) const {
|
constexpr CoordType getCoord(int id) const {
|
||||||
int x = id % Width;
|
int x = id % Width;
|
||||||
int y = (id / Width) % Height;
|
int y = (id / Width) % Height;
|
||||||
int z = id / (Width * Height);
|
int z = id / (Width * Height);
|
||||||
@@ -136,48 +156,47 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Access element at coordinates
|
* @brief Access element at coordinates
|
||||||
*/
|
*/
|
||||||
T& at(int x, int y, int z) {
|
constexpr T& at(int x, int y, int z) {
|
||||||
return data_[z * (Width * Height) + y * Width + x];
|
return data_[z * (Width * Height) + y * Width + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access element at coordinates (const)
|
* @brief Access element at coordinates (const)
|
||||||
*/
|
*/
|
||||||
const T& at(int x, int y, int z) const {
|
constexpr const T& at(int x, int y, int z) const {
|
||||||
return data_[z * (Width * Height) + y * Width + x];
|
return data_[z * (Width * Height) + y * Width + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access element by ID
|
* @brief Access element by ID
|
||||||
*/
|
*/
|
||||||
T& operator[](int id) {
|
constexpr T& operator[](int id) {
|
||||||
return data_[id];
|
return data_[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access element by ID (const)
|
* @brief Access element by ID (const)
|
||||||
*/
|
*/
|
||||||
const T& operator[](int id) const {
|
constexpr const T& operator[](int id) const {
|
||||||
return data_[id];
|
return data_[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set value at specific index (required by WFC)
|
* @brief Set value at specific index (required by WFC)
|
||||||
*/
|
*/
|
||||||
void setValue(size_t index, T value) {
|
constexpr void setValue(size_t index, T value) {
|
||||||
data_[index] = value;
|
data_[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get value at specific index
|
* @brief Get value at specific index
|
||||||
*/
|
*/
|
||||||
T getValue(size_t index) const {
|
constexpr T getValue(size_t index) const {
|
||||||
return data_[index];
|
return data_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<T, Width * Height * Depth> data_;
|
std::array<T, Width * Height * Depth> data_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace WFC
|
} // namespace WFC
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user