From 5d63e04b17b5db8a6974afd5dea37cf26689ca05 Mon Sep 17 00:00:00 2001 From: shylie Date: Mon, 4 May 2026 11:29:38 -0400 Subject: [PATCH] Completely rework the algorithm --- .clangd | 7 + CMakeLists.txt | 42 ++- include/sand.h | 121 -------- include/sand/rule.h | 52 ++++ include/sand/rule_builder.h | 36 +++ include/sand/sand.h | 43 +++ include/sand/type.h | 39 +++ include/sand/type_builder.h | 31 ++ include/sand/type_range.h | 49 +++ src/main.cpp | 153 ++++------ src/rule.cpp | 63 ++++ src/rule_builder.cpp | 95 ++++++ src/sand.cpp | 575 ++++-------------------------------- src/sand.hip | 23 ++ src/type.cpp | 13 + src/type_builder.cpp | 40 +++ src/type_range.cpp | 42 +++ 17 files changed, 683 insertions(+), 741 deletions(-) create mode 100644 .clangd delete mode 100644 include/sand.h create mode 100644 include/sand/rule.h create mode 100644 include/sand/rule_builder.h create mode 100644 include/sand/sand.h create mode 100644 include/sand/type.h create mode 100644 include/sand/type_builder.h create mode 100644 include/sand/type_range.h create mode 100644 src/rule.cpp create mode 100644 src/rule_builder.cpp create mode 100644 src/sand.hip create mode 100644 src/type.cpp create mode 100644 src/type_builder.cpp create mode 100644 src/type_range.cpp diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..9a8849b --- /dev/null +++ b/.clangd @@ -0,0 +1,7 @@ +Documentation: + CommentFormat: Doxygen + +If: + PathMatch: .*\.hip +CompileFlags: + Add: [-x, hip] diff --git a/CMakeLists.txt b/CMakeLists.txt index 0604b16..bdbea5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,33 +1,47 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) -project(sand) +project(sand LANGUAGES CXX HIP) -find_package(OpenMP REQUIRED) +find_package(OpenMP) find_package(PkgConfig REQUIRED) pkg_check_modules(LUAJIT REQUIRED luajit) -add_library(sand SHARED +add_library(sand STATIC + include/sand/sand.h + include/sand/type.h + include/sand/type_builder.h + include/sand/type_range.h + include/sand/rule.h + include/sand/rule_builder.h + + src/type.cpp + src/type_builder.cpp + src/type_range.cpp + src/rule.cpp + src/rule_builder.cpp src/sand.cpp - src/lualib.cpp + + src/sand.hip ) -target_include_directories(sand - PUBLIC include - PRIVATE ${LUAJIT_INCLUDE_DIRS} -) -target_link_libraries(sand PRIVATE ${LUAJIT_LIBRARIES} OpenMP::OpenMP_CXX) +target_include_directories(sand PUBLIC include) set_target_properties(sand PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE ON ) -if (EXISTS third/tracy/CMakeLists.txt) - option(TRACY_ENABLE "Enable Tracy" ON) - set(TRACY_STATIC OFF) - set(TRACY_ON_DEMAND ON) +if(OpenMP_FOUND) + target_link_libraries(sand PRIVATE OpenMP::OpenMP_CXX) +endif() + +if(EXISTS third/tracy/CMakeLists.txt) add_subdirectory(third/tracy) target_link_libraries(sand PUBLIC Tracy::TracyClient) + if(NOT TRACY_ENABLE) + target_compile_definitions(sand PUBLIC FrameMark ZoneScoped) + endif() else() target_compile_definitions(sand PUBLIC FrameMark ZoneScoped) endif() diff --git a/include/sand.h b/include/sand.h deleted file mode 100644 index eaf7d9f..0000000 --- a/include/sand.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef SAND_H -#define SAND_H - -#include -#include -#include -#include -#include - -namespace sand -{ - -struct type -{ - uint16_t id; - uint16_t default_result; -}; -using type_map = std::unordered_map; - -class RulesBuilder; -class TypeBuilder -{ - friend RulesBuilder; - -public: - TypeBuilder(); - - TypeBuilder& add_type(const std::string& name); - TypeBuilder& add_type(const std::string& name, - const std::string& default_result); - - RulesBuilder finish(); - -private: - uint16_t next_type_id = 0; - type_map types; -}; // class TypeBuilder - -class Rule; -class Sand; -class RulesBuilder -{ - friend TypeBuilder; - friend Rule; - friend Sand; - -public: - Rule add_rule(const std::string& source_name, const std::string& to); - - Sand build(uint16_t width, uint16_t height, const std::string& initial); - -private: - RulesBuilder(const TypeBuilder& type_builder); - - void finish_rule(const Rule& rule); - - type_map types; - std::unordered_map>> - rules; -}; // class RulesBuilder - -class Rule -{ - friend RulesBuilder; - -public: - using match_fn = std::function; - - ~Rule(); - - Rule& top_left(match_fn match); - Rule& top_middle(match_fn match); - Rule& top_right(match_fn match); - Rule& middle_left(match_fn match); - Rule& middle_right(match_fn match); - Rule& bottom_left(match_fn match); - Rule& bottom_middle(match_fn match); - Rule& bottom_right(match_fn match); - -private: - Rule(RulesBuilder& builder, uint16_t source, uint16_t to); - - RulesBuilder& builder; - uint16_t source; - uint16_t to; - match_fn matches[8]; -}; // class RuleBuilder - -class Sand -{ - friend RulesBuilder; - -public: - void set(uint16_t x, uint16_t y, const std::string& type); - uint16_t get(uint16_t x, uint16_t y); - - void update(); - -private: - Sand(const RulesBuilder& builder, uint16_t width, uint16_t height, - const std::string& initial); - - uint16_t apply_rule(uint16_t tile, unsigned __int128 key) const; - - static unsigned __int128 apply_mask(unsigned __int128 key, uint8_t mask); - - uint16_t width; - uint16_t height; - type_map types; - std::unordered_map default_conversions; - std::unordered_map>> - rules; - std::vector elements; - bool iter; -}; // class Sand - -} // namespace sand - -#endif // SAND_H diff --git a/include/sand/rule.h b/include/sand/rule.h new file mode 100644 index 0000000..e3a62f4 --- /dev/null +++ b/include/sand/rule.h @@ -0,0 +1,52 @@ +#ifndef SAND_RULE_H +#define SAND_RULE_H + +#include "sand/type.h" + +#include + +namespace sand +{ + +class rule +{ +public: + class builder; + using match_function = std::function; + struct metadata + { + uint32_t begin; + uint32_t end; + }; + using mask = uint32_t; + + rule(const rule&) = delete; + rule(rule&&) = default; + + rule& operator=(const rule&) = delete; + + ~rule(); + + rule& top_left(match_function match); + rule& top(match_function match); + rule& top_right(match_function match); + rule& left(match_function match); + rule& right(match_function match); + rule& bottom_left(match_function match); + rule& bottom(match_function match); + rule& bottom_right(match_function match); + +private: + rule(builder& rb, type from, type to); + + builder& rb; + type from; + type to; + match_function matches[8]; + + friend builder; +}; + +} + +#endif // SAND_RULE_H diff --git a/include/sand/rule_builder.h b/include/sand/rule_builder.h new file mode 100644 index 0000000..7c720b0 --- /dev/null +++ b/include/sand/rule_builder.h @@ -0,0 +1,36 @@ +#ifndef SAND_RULE_BUILDER_H +#define SAND_RULE_BUILDER_H + +#include "sand/rule.h" +#include "sand/type_range.h" + +namespace sand +{ + +class sand; +class rule::builder +{ +public: + rule add_rule(type from, type to); + + sand build(uint16_t width, uint16_t height, type initial); + +private: + builder(type::range types, const std::vector& default_conversions); + + void finish_rule(const rule& rule); + unsigned int get_mask_index(type from, type to); + unsigned int insert_conversion(type from, type to); + + type::range types; + std::vector conversions; + std::vector metas; + std::vector masks; + + friend rule; + friend type::builder; +}; + +} + +#endif // SAND_RULE_BUILDER_H diff --git a/include/sand/sand.h b/include/sand/sand.h new file mode 100644 index 0000000..898ba98 --- /dev/null +++ b/include/sand/sand.h @@ -0,0 +1,43 @@ +#ifndef SAND_H +#define SAND_H + +#include "sand/rule.h" +#include "sand/rule_builder.h" +#include "sand/type.h" +#include "sand/type_builder.h" +#include "sand/type_range.h" + +#include + +namespace sand +{ + +class sand +{ +public: + type get(int x, int y) const; + void set(int x, int y, type type); + + void tick(); + +private: + sand(type::range types, const std::vector& conversions, + const std::vector& metas, + const std::vector& masks, int width, int height, + type initial); + + type::range types; + std::vector conversions; + std::vector metas; + std::vector masks; + int width; + int height; + std::vector data; + bool current; + + friend class rule::builder; +}; + +} + +#endif // SAND_H diff --git a/include/sand/type.h b/include/sand/type.h new file mode 100644 index 0000000..7a1f3b9 --- /dev/null +++ b/include/sand/type.h @@ -0,0 +1,39 @@ +#ifndef SAND_TYPE_H +#define SAND_TYPE_H + +#include + +namespace sand +{ + +class rule; +class sand; +class type +{ +public: + class builder; + class range; + + bool operator==(const type&) const; + + static const type OFF_GRID; + + static constexpr unsigned int MAX_TYPES = UINT16_MAX; + +private: + using id_ty = uint16_t; + + type(id_ty id); + operator id_ty() const; + + id_ty id; + + friend builder; + friend range; + friend rule; + friend sand; +}; + +} + +#endif // SAND_TYPE_H diff --git a/include/sand/type_builder.h b/include/sand/type_builder.h new file mode 100644 index 0000000..e61a8bf --- /dev/null +++ b/include/sand/type_builder.h @@ -0,0 +1,31 @@ +#ifndef SAND_TYPE_BUILDER_H +#define SAND_TYPE_BUILDER_H + +#include "sand/rule.h" +#include "sand/type.h" + +#include + +namespace sand +{ + +class type::builder +{ +public: + builder(); + + type assign(); + range assign_range(unsigned int count); + + void set_default_conversion(const type& to, const type& from); + + rule::builder build(); + +private: + id_ty next_id; + std::vector default_conversions; +}; + +} + +#endif // SAND_TYPE_BUILDER_H diff --git a/include/sand/type_range.h b/include/sand/type_range.h new file mode 100644 index 0000000..faa8a90 --- /dev/null +++ b/include/sand/type_range.h @@ -0,0 +1,49 @@ +#ifndef SAND_TYPE_RANGE_H +#define SAND_TYPE_RANGE_H + +#include "sand/type.h" + +namespace sand +{ + +class type::range +{ +public: + class iter; + + iter begin() const; + iter end() const; + + bool has(const type&) const; + + uint16_t size() const; + +private: + explicit range(id_ty begin, id_ty end); + + id_ty _begin; + id_ty _end; + + friend builder; +}; + +class type::range::iter +{ +public: + type operator*() const; + iter& operator++(); + iter& operator--(); + + bool operator!=(const iter&) const; + +private: + iter(id_ty value); + + id_ty value; + + friend range; +}; + +} + +#endif // SAND_TYPE_RANGE_H diff --git a/src/main.cpp b/src/main.cpp index 3f3a703..09fc1e4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,112 +5,70 @@ #include #include #include -#include -#include +#include -constexpr uint16_t WIDTH = 128; -constexpr uint16_t HEIGHT = 128; +#ifdef TRACY_ENABLE +#include +#endif + +constexpr uint16_t WIDTH = 256; +constexpr uint16_t HEIGHT = 256; constexpr int WINDOW_WIDTH = 1024; constexpr int WINDOW_HEIGHT = 1024; struct color { - uint8_t c[3]; + uint8_t r, g, b; }; -color lookup_color(uint16_t id) -{ - if (id == 0) - { - return { 0, 0, 0 }; - } - else if (id == 1) - { - return { 150, 150, 255 }; - } - else - { - color c; - c.c[0] = 257 - id; - c.c[1] = 257 - id; - c.c[2] = 257 - id; - return c; - } -} - int main(int argc, char** argv) { - auto tb = sand::TypeBuilder().add_type("air"); + auto tb = sand::type::builder(); + auto air = tb.assign(); + auto sand1 = tb.assign(); + auto sand2 = tb.assign(); - for (int i = 0; i < 255; i++) + tb.set_default_conversion(sand1, sand2); + tb.set_default_conversion(sand2, sand1); + + auto rb = tb.build(); + + rb.add_rule(air, sand2).top([=](auto t) { return t == sand1; }); + rb.add_rule(air, sand1).top([=](auto t) { return t == sand2; }); + rb.add_rule(sand1, air).bottom([=](auto t) { return t == air; }); + rb.add_rule(sand2, air).bottom([=](auto t) { return t == air; }); + + rb.add_rule(air, sand1) + .top_right([=](auto t) { return t == sand2; }) + .right([=](auto t) { return t != air && t != sand::type::OFF_GRID; }); + rb.add_rule(air, sand2) + .top_left([=](auto t) { return t == sand1; }) + .left([=](auto t) { return t != air && t != sand::type::OFF_GRID; }); + rb.add_rule(sand1, air) + .bottom_right([=](auto t) { return t == air; }) + .bottom([=](auto t) { return t != air && t != sand::type::OFF_GRID; }); + rb.add_rule(sand2, air) + .bottom_left([=](auto t) { return t == air; }) + .bottom([=](auto t) { return t != air && t != sand::type::OFF_GRID; }); + + auto s = rb.build(WIDTH, HEIGHT, air); + + const auto& cf = [=](sand::type t) -> color { - tb.add_type("stone" + std::to_string(i), "stone" + std::to_string(i + 1)); - } - - auto rb = tb.finish(); - - rb.add_rule("air", "stone0") - .bottom_middle([](const std::string& t) { return t == "offgrid"; }) - .top_middle([](const std::string& t) { return t == "air"; }); - - for (int i = 0; i < 128; i++) - { - auto ar = rb.add_rule("air", "stone" + std::to_string(i + 1)); - auto afn - = [i](const std::string& t) { return t == "stone" + std::to_string(i); }; - - auto sr = rb.add_rule("stone" + std::to_string(i), "air"); - auto sfn = [](const std::string& t) { return t == "air"; }; - - switch (i % 3) + if (t == air) { - case 0: - ar.bottom_left(afn); - sr.top_right(sfn); - break; - - case 1: - ar.bottom_middle(afn); - sr.top_middle(sfn); - break; - - case 2: - ar.bottom_right(afn); - sr.top_left(sfn); - break; + return { 0x40, 0x60, 0x80 }; } - } - - for (int i = 128; i < 255; i++) - { - auto ar = rb.add_rule("air", "stone" + std::to_string(i + 1)); - auto afn - = [i](const std::string& t) { return t == "stone" + std::to_string(i); }; - - auto sr = rb.add_rule("stone" + std::to_string(i), "air"); - auto sfn = [](const std::string& t) { return t == "air"; }; - - switch (i % 3) + else if (t == sand1 || t == sand2) { - case 0: - ar.top_left(afn); - sr.bottom_right(sfn); - break; - - case 1: - ar.top_middle(afn); - sr.bottom_middle(sfn); - break; - - case 2: - ar.top_right(afn); - sr.bottom_left(sfn); - break; + return { 0xB0, 0x90, 0x50 }; } - } - - auto s = rb.build(WIDTH, HEIGHT, "air"); + else + { + return { 0, 0, 0 }; + } + }; SDL_Init(SDL_INIT_VIDEO); SDL_Window* window @@ -136,11 +94,16 @@ int main(int argc, char** argv) float dt = (ms - prev_ms) / 1000.0f; prev_ms = ms; time += dt; + SDL_Log("%f\n", dt); float x, y; int state; - time = 0; - s.update(); + if (time > 0) + { + time = 0; + + s.tick(); + } if ((state = SDL_GetMouseState(&x, &y))) { @@ -149,7 +112,7 @@ int main(int argc, char** argv) if (ix >= 0 && ix < WIDTH && iy >= 0 && iy < HEIGHT) { - s.set(ix, iy, "stone0"); + s.set(ix, iy, sand1); } } @@ -158,8 +121,10 @@ int main(int argc, char** argv) { for (int j = 0; j < HEIGHT; j++) { - memcpy(&static_cast(surface->pixels)[3 * (i + j * WIDTH)], - lookup_color(s.get(i, j)).c, 3); + color c = cf(s.get(i, j)); + static_cast(surface->pixels)[3 * (i + j * WIDTH) + 0] = c.r; + static_cast(surface->pixels)[3 * (i + j * WIDTH) + 1] = c.g; + static_cast(surface->pixels)[3 * (i + j * WIDTH) + 2] = c.b; } } SDL_UnlockSurface(surface); diff --git a/src/rule.cpp b/src/rule.cpp new file mode 100644 index 0000000..382b63f --- /dev/null +++ b/src/rule.cpp @@ -0,0 +1,63 @@ +#include "sand/rule.h" + +#include "sand/rule_builder.h" +using namespace sand; + +rule::~rule() { rb.finish_rule(*this); } + +rule& rule::top_left(rule::match_function match) +{ + matches[0] = match; + return *this; +} + +rule& rule::top(rule::match_function match) +{ + matches[1] = match; + return *this; +} + +rule& rule::top_right(rule::match_function match) +{ + matches[2] = match; + return *this; +} + +rule& rule::left(rule::match_function match) +{ + matches[3] = match; + return *this; +} + +rule& rule::right(rule::match_function match) +{ + matches[4] = match; + return *this; +} + +rule& rule::bottom_left(rule::match_function match) +{ + matches[5] = match; + return *this; +} + +rule& rule::bottom(rule::match_function match) +{ + matches[6] = match; + return *this; +} + +rule& rule::bottom_right(rule::match_function match) +{ + matches[7] = match; + return *this; +} + +rule::rule(rule::builder& rb, type from, type to) : + rb(rb), + from(from), + to(to), + matches{ nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr } +{ +} diff --git a/src/rule_builder.cpp b/src/rule_builder.cpp new file mode 100644 index 0000000..ad0534c --- /dev/null +++ b/src/rule_builder.cpp @@ -0,0 +1,95 @@ +#include "sand/rule_builder.h" + +#include "sand/sand.h" +using namespace sand; + +// ------------------- +// rules datastructure +// ------------------- +// `conversions` - possible result types of a rule +// `metas` - refers to slices of `conversions` for a +// specific tile type +// `masks` - bitmask that represents which conversions are +// *invalid* for given tile types (to, from) and a neighbor position + +rule rule::builder::add_rule(type from, type to) +{ + return rule(*this, from, to); +} + +::sand::sand rule::builder::build(uint16_t width, uint16_t height, + type initial) +{ + return ::sand::sand(types, conversions, metas, masks, width, height, + initial); +} + +rule::builder::builder(type::range types, + const std::vector& default_conversions) : + types(types), + conversions(default_conversions), + metas(types.size()), + masks(types.size() * types.size() * 8, -1U) +{ + for (const auto& type : types) + { + metas[type] = { .begin = type, .end = type + 1u }; + } +} + +void rule::builder::finish_rule(const rule& rule) +{ + unsigned int position = insert_conversion(rule.from, rule.to); + for (int i = 0; i < 8; i++) + { + // not wildcard + // ------------ + // include `position` only for matching types + if (rule.matches[i]) + { + for (const auto& type : types) + { + if (rule.matches[i](type)) + { + // mask out default conversion + masks[i + type * 8 + rule.from * types.size() * 8] &= ~(1 << 0); + } + else + { + // mask out rule.to from + masks[i + type * 8 + rule.from * types.size() * 8] + &= ~(1 << position); + } + } + } + } +} + +unsigned int rule::builder::get_mask_index(type from, type to) +{ + for (int i = metas[from].begin; i < metas[from].end; i++) + { + if (conversions[i] == to) + { + return i; + } + } + return insert_conversion(from, to); +} + +unsigned int rule::builder::insert_conversion(type from, type to) +{ + uint32_t position = metas[from].end; + metas[from].end += 1; + conversions.insert(conversions.begin() + position, to); + + for (auto& meta : metas) + { + if (meta.begin >= position) + { + meta.begin += 1; + meta.end += 1; + } + } + return position - metas[from].begin; +} diff --git a/src/sand.cpp b/src/sand.cpp index 83450e1..57b640d 100644 --- a/src/sand.cpp +++ b/src/sand.cpp @@ -1,529 +1,80 @@ -#include "sand.h" +#include "sand/sand.h" -#include -#include -#include -#include +#include "sand/type.h" -using namespace sand; - -namespace +sand::type sand::sand::get(int x, int y) const { - -constexpr uint16_t OFFGRID_VALUE = 0; - -template -int binary_search(T element, const T* elements, size_t begin, size_t end, - auto (*fn)(T, T)->std::strong_ordering) -{ - if (end - begin == 1) + if (x < 0 || x >= width || y < 0 || y >= height) { - if (fn(element, elements[begin]) == std::strong_ordering::equal) + return type::OFF_GRID; + } + return data[x + y * width + current * width * height]; +} + +void sand::sand::set(int x, int y, type type) +{ + data[x + y * width + current * width * height] = type; +} + +void sand::sand::tick() +{ +#pragma omp parallel for + for (int i = 0; i < width * height; i++) + { + const int x = i % width; + const int y = i / width; + + auto [begin, end] = metas[get(x, y)]; + uint32_t mask = -1U; + + int iter = 0; + for (int dy = -1; dy <= 1; dy++) { - return begin; - } - else - { - - return -1; - } - } - - size_t index = begin + (end - begin) / 2; - - auto comparison_result = fn(element, elements[index]); - - if (comparison_result == std::strong_ordering::less) - { - return binary_search(element, elements, begin, index, fn); - } - - if (comparison_result == std::strong_ordering::greater) - { - return binary_search(element, elements, index, end, fn); - } - - return index; -} - -} // namespace - -TypeBuilder::TypeBuilder() : - next_type_id(OFFGRID_VALUE) -{ - add_type("offgrid"); -} - -TypeBuilder& TypeBuilder::add_type(const std::string& name) -{ - if (!types.contains(name)) - { - types[name] = { .id = next_type_id, .default_result = next_type_id }; - next_type_id += 1; - } - - return *this; -} - -TypeBuilder& TypeBuilder::add_type(const std::string& name, - const std::string& default_result) -{ - add_type(name); - add_type(default_result); - - types[name].default_result = types[default_result].id; - - return *this; -} - -RulesBuilder TypeBuilder::finish() { return RulesBuilder(*this); } - -Rule RulesBuilder::add_rule(const std::string& name, const std::string& to) -{ - return Rule(*this, types[name].id, types[to].id); -} - -Sand RulesBuilder::build(uint16_t width, uint16_t height, - const std::string& initial) -{ - for (auto& [key, rule] : rules) - { - std::sort(rule.begin(), rule.end(), - [](auto a, auto b) { return a.first < b.first; }); - } - return Sand(*this, width, height, initial); -} - -RulesBuilder::RulesBuilder(const TypeBuilder& type_builder) : - types(type_builder.types) -{ -} - -void RulesBuilder::finish_rule(const Rule& rule) -{ - // 8 nested loops is kinda silly, maybe there's a better way to do this? - // top left - for (const auto& [tl_name, tl_type] : types) - { - if (!rule.matches[0] || rule.matches[0](tl_name)) - { - // top middle - for (const auto& [tm_name, tm_type] : types) + for (int dx = -1; dx <= 1; dx++) { - if (!rule.matches[1] || rule.matches[1](tm_name)) + if (dx == 0 && dy == 0) { - // top right - for (const auto& [tr_name, tr_type] : types) - { - if (!rule.matches[2] || rule.matches[2](tr_name)) - { - // middle left - for (const auto& [ml_name, ml_type] : types) - { - if (!rule.matches[3] || rule.matches[3](ml_name)) - { - // middle right - for (const auto& [mr_name, mr_type] : types) - { - if (!rule.matches[4] || rule.matches[4](mr_name)) - { - // bottom left - for (const auto& [bl_name, bl_type] : types) - { - if (!rule.matches[5] || rule.matches[5](bl_name)) - { - // bottom middle - for (const auto& [bm_name, bm_type] : types) - { - if (!rule.matches[6] || rule.matches[6](bm_name)) - { - // bottom right - for (const auto& [br_name, br_type] : types) - { - if (!rule.matches[7] - || rule.matches[7](br_name)) - { - unsigned __int128 neighbors = 0; - - if (rule.matches[0]) - { - neighbors += tl_type.id; - } - else - { - neighbors += 0xFFFF; - } - neighbors <<= 16; - - if (rule.matches[1]) - { - neighbors += tm_type.id; - } - else - { - neighbors += 0xFFFF; - } - neighbors <<= 16; - - if (rule.matches[2]) - { - neighbors += tr_type.id; - } - else - { - neighbors += 0xFFFF; - } - neighbors <<= 16; - - if (rule.matches[3]) - { - neighbors += ml_type.id; - } - else - { - neighbors += 0xFFFF; - } - neighbors <<= 16; - - if (rule.matches[4]) - { - neighbors += mr_type.id; - } - else - { - neighbors += 0xFFFF; - } - neighbors <<= 16; - - if (rule.matches[5]) - { - neighbors += bl_type.id; - } - else - { - neighbors += 0xFFFF; - } - neighbors <<= 16; - - if (rule.matches[6]) - { - neighbors += bm_type.id; - } - else - { - neighbors += 0xFFFF; - } - neighbors <<= 16; - - if (rule.matches[7]) - { - neighbors += br_type.id; - } - else - { - neighbors += 0xFFFF; - } - - if (!rules.contains(rule.source)) - { - rules[rule.source] = {}; - } - - rules[rule.source].push_back( - { neighbors, rule.to }); - } - - if (!rule.matches[7]) - { - break; - } - } // bottom right - } - - if (!rule.matches[6]) - { - break; - } - } // bottom middle - } - - if (!rule.matches[5]) - { - break; - } - } // bottom left - } - - if (!rule.matches[4]) - { - break; - } - } // middle right - } - - if (!rule.matches[3]) - { - break; - } - } // middle left - } - - if (!rule.matches[2]) - { - break; - } - } // top right + continue; } - if (!rule.matches[1]) - { - break; - } - } // top middle - } + mask &= masks[iter + get(x + dx, y + dy) * 8 + + get(x, y) * types.size() * 8]; - if (!rule.matches[0]) - { - break; - } - } // top left -} - -Rule::~Rule() { builder.finish_rule(*this); } - -Rule& Rule::top_left(match_fn match) -{ - matches[0] = match; - - return *this; -} - -Rule& Rule::top_middle(match_fn match) -{ - matches[1] = match; - - return *this; -} - -Rule& Rule::top_right(match_fn match) -{ - matches[2] = match; - - return *this; -} - -Rule& Rule::middle_left(match_fn match) -{ - matches[3] = match; - - return *this; -} - -Rule& Rule::middle_right(match_fn match) -{ - matches[4] = match; - - return *this; -} - -Rule& Rule::bottom_left(match_fn match) -{ - matches[5] = match; - - return *this; -} - -Rule& Rule::bottom_middle(match_fn match) -{ - matches[6] = match; - - return *this; -} - -Rule& Rule::bottom_right(match_fn match) -{ - matches[7] = match; - - return *this; -} - -Rule::Rule(RulesBuilder& builder, uint16_t source, uint16_t to) : - builder(builder), - source(source), - to(to), - matches{} -{ -} - -Sand::Sand(const RulesBuilder& builder, uint16_t width, uint16_t height, - const std::string& initial) : - width(width), - height(height), - types(builder.types), - rules(builder.rules), - elements(width * height * 2, types[initial].id), - iter(false) -{ - for (const auto& [name, type] : types) - { - default_conversions[type.id] = type.default_result; - } - - int cnt = 0; - for (const auto& [key, elem] : rules) - { - cnt += elem.size(); - } -} - -void Sand::set(uint16_t x, uint16_t y, const std::string& type) -{ - elements[x + y * width + iter * width * height] = types[type].id; -} - -uint16_t Sand::get(uint16_t x, uint16_t y) -{ - return elements[x + y * width + iter * width * height]; -} - -void Sand::update() -{ - ZoneScoped; - - int n; -#pragma omp parallel for private(n) - for (n = 0; n < width * height; n++) - { - int i = n % width; - int j = n / height; - - unsigned __int128 neighbors = 0; - if (i > 0 && j > 0) - { - neighbors += get(i - 1, j - 1); - } - else - { - neighbors += OFFGRID_VALUE; - } - neighbors <<= 16; - - if (j > 0) - { - neighbors += get(i, j - 1); - } - else - { - neighbors += OFFGRID_VALUE; - } - neighbors <<= 16; - - if (i < width - 1 && j > 0) - { - neighbors += get(i + 1, j - 1); - } - else - { - neighbors += OFFGRID_VALUE; - } - neighbors <<= 16; - - if (i > 0) - { - neighbors += get(i - 1, j); - } - else - { - neighbors += OFFGRID_VALUE; - } - neighbors <<= 16; - - if (i < width - 1) - { - neighbors += get(i + 1, j); - } - else - { - neighbors += OFFGRID_VALUE; - } - neighbors <<= 16; - - if (i > 0 && j < height - 1) - { - neighbors += get(i - 1, j + 1); - } - else - { - neighbors += OFFGRID_VALUE; - } - neighbors <<= 16; - - if (j < height - 1) - { - neighbors += get(i, j + 1); - } - else - { - neighbors += OFFGRID_VALUE; - } - neighbors <<= 16; - - if (i < width - 1 && j < height - 1) - { - neighbors += get(i + 1, j + 1); - } - else - { - neighbors += OFFGRID_VALUE; - } - - elements[i + j * width + !iter * width * height] - = apply_rule(get(i, j), neighbors); - } - - iter = !iter; -} - -uint16_t Sand::apply_rule(uint16_t tile, unsigned __int128 key) const -{ - if (rules.contains(tile)) - { - auto& rules_for_tile = rules.at(tile); - - if (!rules_for_tile.empty()) - { - for (int mask = 0; mask < 256; mask++) - { - std::pair element - = { apply_mask(key, mask), 0 }; - std::strong_ordering (*fn)(decltype(element), decltype(element)) - = [](auto a, auto b) { return a.first <=> b.first; }; - - auto found = binary_search(element, rules_for_tile.data(), 0, - rules_for_tile.size(), fn); - if (found >= 0) - { - return rules_for_tile[found].second; - } + iter += 1; } } - } - return default_conversions.at(tile); -} - -unsigned __int128 Sand::apply_mask(unsigned __int128 key, uint8_t mask) -{ - unsigned __int128 any = 0xFFFF; - for (int i = 0; i < 8; i++) - { - if (mask & 1) + bool found = false; + for (int bit = begin; bit < end; bit++) { - key |= any; + if (mask & 1) + { + data[x + y * width + !current * width * height] = conversions[bit]; + found = true; + break; + } + mask >>= 1; + } + if (!found) + { + data[x + y * width + !current * width * height] = conversions[begin]; } - - mask >>= 1; - any <<= 16; } - return key; + + current = !current; +} + +sand::sand::sand(type::range types, const std::vector& conversions, + const std::vector& metas, + const std::vector& masks, int width, int height, + type initial) : + types(types), + conversions(conversions), + metas(metas), + masks(masks), + width(width), + height(height), + data(width * height * 2, initial), + current(false) +{ } diff --git a/src/sand.hip b/src/sand.hip new file mode 100644 index 0000000..5c5af53 --- /dev/null +++ b/src/sand.hip @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include + +#define HIP_CHECK(condition) \ + do \ + { \ + const hipError_t error = condition; \ + if (error != hipSuccess) \ + { \ + std::cerr << "An error occured: \"" << hipGetErrorString(error) \ + << "\t at " << __FILE__ << ":" << __LINE__ << std::endl; \ + std::exit(-1); \ + } \ + } while (false) + +template constexpr T ceildiv(const T& a, const T& b) +{ + return (a + b - 1) / b; +} diff --git a/src/type.cpp b/src/type.cpp new file mode 100644 index 0000000..5d8835d --- /dev/null +++ b/src/type.cpp @@ -0,0 +1,13 @@ +#include "sand/type.h" +using namespace sand; + +const type type::OFF_GRID = 0; + +bool type::operator==(const type& other) const { return id == other.id; } + +type::type(id_ty id) : + id(id) +{ +} + +type::operator id_ty() const { return id; } diff --git a/src/type_builder.cpp b/src/type_builder.cpp new file mode 100644 index 0000000..46300fe --- /dev/null +++ b/src/type_builder.cpp @@ -0,0 +1,40 @@ +#include "sand/type_builder.h" + +#include "sand/rule_builder.h" +#include "sand/type_range.h" + +#include +using namespace sand; + +type::builder::builder() : + // 0 is a special ID + next_id(1), + default_conversions{ OFF_GRID } +{ +} + +type type::builder::assign() +{ + default_conversions.push_back(next_id); + return next_id++; +} + +type::range type::builder::assign_range(unsigned int count) +{ + assert(count > 0 && "count must be greater than 0"); + + type::range r(next_id, next_id + count); + next_id += count; + + return r; +} + +void type::builder::set_default_conversion(const type& to, const type& from) +{ + default_conversions[to] = from; +} + +rule::builder type::builder::build() +{ + return rule::builder(type::range(0, next_id), default_conversions); +} diff --git a/src/type_range.cpp b/src/type_range.cpp new file mode 100644 index 0000000..fad2954 --- /dev/null +++ b/src/type_range.cpp @@ -0,0 +1,42 @@ +#include "sand/type_range.h" +using namespace sand; + +type::range::iter type::range::begin() const { return _begin; } +type::range::iter type::range::end() const { return _end; } + +bool type::range::has(const type& type) const +{ + return type.id >= _begin && type.id < _end; +} + +uint16_t type::range::size() const { return _end - _begin; } + +type::range::range(type::id_ty begin, type::id_ty end) : + _begin(begin), + _end(end) +{ +} + +type type::range::iter::operator*() const { return value; } + +type::range::iter& type::range::iter::operator++() +{ + value++; + return *this; +} + +type::range::iter& type::range::iter::operator--() +{ + value--; + return *this; +} + +bool type::range::iter::operator!=(const type::range::iter& other) const +{ + return value != other.value; +} + +type::range::iter::iter(type::id_ty value) : + value(value) +{ +}