Completely rework the algorithm
This commit is contained in:
parent
f6c0e07489
commit
5d63e04b17
7
.clangd
Normal file
7
.clangd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Documentation:
|
||||||
|
CommentFormat: Doxygen
|
||||||
|
|
||||||
|
If:
|
||||||
|
PathMatch: .*\.hip
|
||||||
|
CompileFlags:
|
||||||
|
Add: [-x, hip]
|
||||||
@ -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)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(LUAJIT REQUIRED luajit)
|
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/sand.cpp
|
||||||
src/lualib.cpp
|
|
||||||
|
src/sand.hip
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(sand
|
target_include_directories(sand PUBLIC include)
|
||||||
PUBLIC include
|
|
||||||
PRIVATE ${LUAJIT_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
target_link_libraries(sand PRIVATE ${LUAJIT_LIBRARIES} OpenMP::OpenMP_CXX)
|
|
||||||
set_target_properties(sand PROPERTIES
|
set_target_properties(sand PROPERTIES
|
||||||
CXX_STANDARD 20
|
CXX_STANDARD 20
|
||||||
CXX_STANDARD_REQUIRED ON
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
POSITION_INDEPENDENT_CODE ON
|
||||||
)
|
)
|
||||||
|
|
||||||
if (EXISTS third/tracy/CMakeLists.txt)
|
if(OpenMP_FOUND)
|
||||||
option(TRACY_ENABLE "Enable Tracy" ON)
|
target_link_libraries(sand PRIVATE OpenMP::OpenMP_CXX)
|
||||||
set(TRACY_STATIC OFF)
|
endif()
|
||||||
set(TRACY_ON_DEMAND ON)
|
|
||||||
|
if(EXISTS third/tracy/CMakeLists.txt)
|
||||||
add_subdirectory(third/tracy)
|
add_subdirectory(third/tracy)
|
||||||
target_link_libraries(sand PUBLIC Tracy::TracyClient)
|
target_link_libraries(sand PUBLIC Tracy::TracyClient)
|
||||||
|
if(NOT TRACY_ENABLE)
|
||||||
|
target_compile_definitions(sand PUBLIC FrameMark ZoneScoped)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
target_compile_definitions(sand PUBLIC FrameMark ZoneScoped)
|
target_compile_definitions(sand PUBLIC FrameMark ZoneScoped)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
121
include/sand.h
121
include/sand.h
@ -1,121 +0,0 @@
|
|||||||
#ifndef SAND_H
|
|
||||||
#define SAND_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace sand
|
|
||||||
{
|
|
||||||
|
|
||||||
struct type
|
|
||||||
{
|
|
||||||
uint16_t id;
|
|
||||||
uint16_t default_result;
|
|
||||||
};
|
|
||||||
using type_map = std::unordered_map<std::string, type>;
|
|
||||||
|
|
||||||
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<uint16_t,
|
|
||||||
std::vector<std::pair<unsigned __int128, uint16_t>>>
|
|
||||||
rules;
|
|
||||||
}; // class RulesBuilder
|
|
||||||
|
|
||||||
class Rule
|
|
||||||
{
|
|
||||||
friend RulesBuilder;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using match_fn = std::function<bool(const std::string&)>;
|
|
||||||
|
|
||||||
~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<uint16_t, uint16_t> default_conversions;
|
|
||||||
std::unordered_map<uint16_t,
|
|
||||||
std::vector<std::pair<unsigned __int128, uint16_t>>>
|
|
||||||
rules;
|
|
||||||
std::vector<uint16_t> elements;
|
|
||||||
bool iter;
|
|
||||||
}; // class Sand
|
|
||||||
|
|
||||||
} // namespace sand
|
|
||||||
|
|
||||||
#endif // SAND_H
|
|
||||||
52
include/sand/rule.h
Normal file
52
include/sand/rule.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef SAND_RULE_H
|
||||||
|
#define SAND_RULE_H
|
||||||
|
|
||||||
|
#include "sand/type.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace sand
|
||||||
|
{
|
||||||
|
|
||||||
|
class rule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class builder;
|
||||||
|
using match_function = std::function<bool(type)>;
|
||||||
|
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
|
||||||
36
include/sand/rule_builder.h
Normal file
36
include/sand/rule_builder.h
Normal file
@ -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<type>& 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<type> conversions;
|
||||||
|
std::vector<metadata> metas;
|
||||||
|
std::vector<mask> masks;
|
||||||
|
|
||||||
|
friend rule;
|
||||||
|
friend type::builder;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAND_RULE_BUILDER_H
|
||||||
43
include/sand/sand.h
Normal file
43
include/sand/sand.h
Normal file
@ -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 <vector>
|
||||||
|
|
||||||
|
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<type>& conversions,
|
||||||
|
const std::vector<rule::metadata>& metas,
|
||||||
|
const std::vector<rule::mask>& masks, int width, int height,
|
||||||
|
type initial);
|
||||||
|
|
||||||
|
type::range types;
|
||||||
|
std::vector<type> conversions;
|
||||||
|
std::vector<rule::metadata> metas;
|
||||||
|
std::vector<rule::mask> masks;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
std::vector<type> data;
|
||||||
|
bool current;
|
||||||
|
|
||||||
|
friend class rule::builder;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAND_H
|
||||||
39
include/sand/type.h
Normal file
39
include/sand/type.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef SAND_TYPE_H
|
||||||
|
#define SAND_TYPE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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
|
||||||
31
include/sand/type_builder.h
Normal file
31
include/sand/type_builder.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef SAND_TYPE_BUILDER_H
|
||||||
|
#define SAND_TYPE_BUILDER_H
|
||||||
|
|
||||||
|
#include "sand/rule.h"
|
||||||
|
#include "sand/type.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<type> default_conversions;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAND_TYPE_BUILDER_H
|
||||||
49
include/sand/type_range.h
Normal file
49
include/sand/type_range.h
Normal file
@ -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
|
||||||
153
src/main.cpp
153
src/main.cpp
@ -5,112 +5,70 @@
|
|||||||
#include <SDL3/SDL_surface.h>
|
#include <SDL3/SDL_surface.h>
|
||||||
#include <SDL3/SDL_timer.h>
|
#include <SDL3/SDL_timer.h>
|
||||||
#include <SDL3/SDL_video.h>
|
#include <SDL3/SDL_video.h>
|
||||||
#include <sand.h>
|
#include <sand/sand.h>
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
constexpr uint16_t WIDTH = 128;
|
#ifdef TRACY_ENABLE
|
||||||
constexpr uint16_t HEIGHT = 128;
|
#include <tracy/Tracy.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr uint16_t WIDTH = 256;
|
||||||
|
constexpr uint16_t HEIGHT = 256;
|
||||||
|
|
||||||
constexpr int WINDOW_WIDTH = 1024;
|
constexpr int WINDOW_WIDTH = 1024;
|
||||||
constexpr int WINDOW_HEIGHT = 1024;
|
constexpr int WINDOW_HEIGHT = 1024;
|
||||||
|
|
||||||
struct color
|
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)
|
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));
|
if (t == air)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
case 0:
|
return { 0x40, 0x60, 0x80 };
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
else if (t == sand1 || t == sand2)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
case 0:
|
return { 0xB0, 0x90, 0x50 };
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
auto s = rb.build(WIDTH, HEIGHT, "air");
|
return { 0, 0, 0 };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
SDL_Window* window
|
SDL_Window* window
|
||||||
@ -136,11 +94,16 @@ int main(int argc, char** argv)
|
|||||||
float dt = (ms - prev_ms) / 1000.0f;
|
float dt = (ms - prev_ms) / 1000.0f;
|
||||||
prev_ms = ms;
|
prev_ms = ms;
|
||||||
time += dt;
|
time += dt;
|
||||||
|
SDL_Log("%f\n", dt);
|
||||||
|
|
||||||
float x, y;
|
float x, y;
|
||||||
int state;
|
int state;
|
||||||
time = 0;
|
if (time > 0)
|
||||||
s.update();
|
{
|
||||||
|
time = 0;
|
||||||
|
|
||||||
|
s.tick();
|
||||||
|
}
|
||||||
|
|
||||||
if ((state = SDL_GetMouseState(&x, &y)))
|
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)
|
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++)
|
for (int j = 0; j < HEIGHT; j++)
|
||||||
{
|
{
|
||||||
memcpy(&static_cast<uint8_t*>(surface->pixels)[3 * (i + j * WIDTH)],
|
color c = cf(s.get(i, j));
|
||||||
lookup_color(s.get(i, j)).c, 3);
|
static_cast<uint8_t*>(surface->pixels)[3 * (i + j * WIDTH) + 0] = c.r;
|
||||||
|
static_cast<uint8_t*>(surface->pixels)[3 * (i + j * WIDTH) + 1] = c.g;
|
||||||
|
static_cast<uint8_t*>(surface->pixels)[3 * (i + j * WIDTH) + 2] = c.b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_UnlockSurface(surface);
|
SDL_UnlockSurface(surface);
|
||||||
|
|||||||
63
src/rule.cpp
Normal file
63
src/rule.cpp
Normal file
@ -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 }
|
||||||
|
{
|
||||||
|
}
|
||||||
95
src/rule_builder.cpp
Normal file
95
src/rule_builder.cpp
Normal file
@ -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<type>& 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;
|
||||||
|
}
|
||||||
575
src/sand.cpp
575
src/sand.cpp
@ -1,529 +1,80 @@
|
|||||||
#include "sand.h"
|
#include "sand/sand.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include "sand/type.h"
|
||||||
#include <compare>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <tracy/Tracy.hpp>
|
|
||||||
|
|
||||||
using namespace sand;
|
sand::type sand::sand::get(int x, int y) const
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
{
|
||||||
|
if (x < 0 || x >= width || y < 0 || y >= height)
|
||||||
constexpr uint16_t OFFGRID_VALUE = 0;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
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 (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;
|
for (int dx = -1; dx <= 1; dx++)
|
||||||
}
|
|
||||||
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<T>(element, elements, begin, index, fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comparison_result == std::strong_ordering::greater)
|
|
||||||
{
|
|
||||||
return binary_search<T>(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)
|
|
||||||
{
|
{
|
||||||
if (!rule.matches[1] || rule.matches[1](tm_name))
|
if (dx == 0 && dy == 0)
|
||||||
{
|
{
|
||||||
// top right
|
continue;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rule.matches[1])
|
mask &= masks[iter + get(x + dx, y + dy) * 8
|
||||||
{
|
+ get(x, y) * types.size() * 8];
|
||||||
break;
|
|
||||||
}
|
|
||||||
} // top middle
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rule.matches[0])
|
iter += 1;
|
||||||
{
|
|
||||||
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<unsigned __int128, uint16_t> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return default_conversions.at(tile);
|
bool found = false;
|
||||||
}
|
for (int bit = begin; bit < end; bit++)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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<type>& conversions,
|
||||||
|
const std::vector<rule::metadata>& metas,
|
||||||
|
const std::vector<rule::mask>& 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)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/sand.hip
Normal file
23
src/sand.hip
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include <hip/amd_detail/amd_hip_runtime.h>
|
||||||
|
#include <hip/driver_types.h>
|
||||||
|
#include <hip/hip_runtime.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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 <typename T> constexpr T ceildiv(const T& a, const T& b)
|
||||||
|
{
|
||||||
|
return (a + b - 1) / b;
|
||||||
|
}
|
||||||
13
src/type.cpp
Normal file
13
src/type.cpp
Normal file
@ -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; }
|
||||||
40
src/type_builder.cpp
Normal file
40
src/type_builder.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include "sand/type_builder.h"
|
||||||
|
|
||||||
|
#include "sand/rule_builder.h"
|
||||||
|
#include "sand/type_range.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
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);
|
||||||
|
}
|
||||||
42
src/type_range.cpp
Normal file
42
src/type_range.cpp
Normal file
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user