2d dungeon

This commit is contained in:
Connor
2026-02-09 22:22:35 +09:00
parent b84970eddd
commit 480e1d5364
6 changed files with 207 additions and 119 deletions

View 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
View 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
View 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
View 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)

View File

@@ -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

View File

@@ -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