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
|
||||
*/
|
||||
template<typename T, size_t Width, size_t Height>
|
||||
template<typename T, size_t Width, size_t Height, bool Looping = false>
|
||||
class Array2D {
|
||||
public:
|
||||
using ValueType = T;
|
||||
using CoordType = std::tuple<int, int>;
|
||||
|
||||
Array2D() = default;
|
||||
constexpr Array2D() = default;
|
||||
|
||||
/**
|
||||
* @brief Get the total size of the world
|
||||
*/
|
||||
size_t size() const {
|
||||
constexpr size_t size() const {
|
||||
return Width * Height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert coordinates to cell ID
|
||||
*/
|
||||
int getId(CoordType coord) const {
|
||||
constexpr int getId(CoordType coord) const {
|
||||
auto [x, y] = coord;
|
||||
return y * Width + x;
|
||||
}
|
||||
@@ -35,66 +35,86 @@ public:
|
||||
/**
|
||||
* @brief Convert cell ID to coordinates
|
||||
*/
|
||||
CoordType getCoord(int id) const {
|
||||
constexpr CoordType getCoord(int id) const {
|
||||
int x = id % Width;
|
||||
int y = id / Width;
|
||||
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
|
||||
*/
|
||||
size_t width() const { return Width; }
|
||||
constexpr size_t width() const { return Width; }
|
||||
|
||||
/**
|
||||
* @brief Get height of the array
|
||||
*/
|
||||
size_t height() const { return Height; }
|
||||
constexpr size_t height() const { return Height; }
|
||||
|
||||
/**
|
||||
* @brief Access element at coordinates
|
||||
*/
|
||||
T& at(int x, int y) {
|
||||
constexpr T& at(int x, int y) {
|
||||
return data_[y * Width + x];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element by ID
|
||||
*/
|
||||
T& operator[](int id) {
|
||||
constexpr T& operator[](int id) {
|
||||
return data_[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element by ID (const)
|
||||
*/
|
||||
const T& operator[](int id) const {
|
||||
constexpr const T& operator[](int id) const {
|
||||
return data_[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get value at specific index
|
||||
*/
|
||||
T getValue(size_t index) const {
|
||||
constexpr T getValue(size_t index) const {
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<T, Width * Height> data_;
|
||||
std::array<T, Width * Height> data_{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -106,19 +126,19 @@ public:
|
||||
using ValueType = T;
|
||||
using CoordType = std::tuple<int, int, int>;
|
||||
|
||||
Array3D() = default;
|
||||
constexpr Array3D() = default;
|
||||
|
||||
/**
|
||||
* @brief Get the total size of the world
|
||||
*/
|
||||
size_t size() const {
|
||||
constexpr size_t size() const {
|
||||
return Width * Height * Depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert coordinates to cell ID
|
||||
*/
|
||||
int getId(CoordType coord) const {
|
||||
constexpr int getId(CoordType coord) const {
|
||||
auto [x, y, z] = coord;
|
||||
return z * (Width * Height) + y * Width + x;
|
||||
}
|
||||
@@ -126,7 +146,7 @@ public:
|
||||
/**
|
||||
* @brief Convert cell ID to coordinates
|
||||
*/
|
||||
CoordType getCoord(int id) const {
|
||||
constexpr CoordType getCoord(int id) const {
|
||||
int x = id % Width;
|
||||
int y = (id / Width) % Height;
|
||||
int z = id / (Width * Height);
|
||||
@@ -136,48 +156,47 @@ public:
|
||||
/**
|
||||
* @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];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element by ID
|
||||
*/
|
||||
T& operator[](int id) {
|
||||
constexpr T& operator[](int id) {
|
||||
return data_[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access element by ID (const)
|
||||
*/
|
||||
const T& operator[](int id) const {
|
||||
constexpr const T& operator[](int id) const {
|
||||
return data_[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get value at specific index
|
||||
*/
|
||||
T getValue(size_t index) const {
|
||||
constexpr T getValue(size_t index) const {
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<T, Width * Height * Depth> data_;
|
||||
std::array<T, Width * Height * Depth> data_{};
|
||||
};
|
||||
|
||||
} // namespace WFC
|
||||
|
||||
|
||||
Reference in New Issue
Block a user