Browse Source

refactor: project structure of all cases

master
Dnomd343 7 months ago
parent
commit
e03859c413
  1. 17
      src/core/CMakeLists.txt
  2. 83
      src/core/all_cases/all_cases.h
  3. 49
      src/core/all_cases/inline_impl.inl
  4. 14
      src/core/all_cases/internal/all_cases.cc
  5. 21
      src/core/all_cases/internal/all_cases.inl
  6. 11
      src/core/all_cases/internal/basic_ranges.cc
  7. 21
      src/core/all_cases/internal/basic_ranges.inl
  8. 23
      src/core/utils/utility.h

17
src/core/CMakeLists.txt

@ -4,23 +4,34 @@ project(core VERSION 0.2.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(KLOTSKI_CORE_SRC
all_cases/basic_ranges.cc
all_cases/all_cases.cc
all_cases/internal/basic_ranges.cc
all_cases/internal/all_cases.cc
common_code/common_code.cc
common_code/serialize.cc
common_code/sundry.cc
raw_code/raw_code.cc
raw_code/convert.cc
raw_code/sundry.cc
raw_code/mirror.cc
short_code/convert.cc
short_code/serialize.cc
short_code/short_code.cc
short_code/sundry.cc
)
add_library(klotski_core STATIC ${KLOTSKI_CORE_SRC})
target_compile_options(klotski_core PRIVATE -fno-rtti -fno-exceptions)
target_include_directories(klotski_core PUBLIC utils all_cases raw_code short_code common_code)
target_include_directories(klotski_core PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/utils # TODO: remove item
${CMAKE_CURRENT_SOURCE_DIR}/all_cases # TODO: remove item
${CMAKE_CURRENT_SOURCE_DIR}/raw_code # TODO: remove item
${CMAKE_CURRENT_SOURCE_DIR}/short_code # TODO: remove item
${CMAKE_CURRENT_SOURCE_DIR}/common_code # TODO: remove item
)
# TODO: just for dev testing
add_executable(klotski_core_bin main.cc)

83
src/core/all_cases/all_cases.h

@ -1,4 +1,4 @@
#pragma once
/// Klotski Engine by Dnomd343 @2024
/// Based on the requirements of valid klotski, the 2x2 block that must exist
/// and only one, witch will occupy 4 empty slots, and the remaining 16 slots
@ -35,6 +35,8 @@
/// not consume too much time, but it can almost double the speed of the case
/// checking subsequent.
#pragma once
#include <array>
#include <mutex>
#include <vector>
@ -42,11 +44,11 @@
#include <numeric>
#include <functional>
#include "utility.h"
#include "utils/utility.h"
namespace klotski::cases {
// ----------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------- //
typedef uint32_t Range;
typedef std::vector<Range> Ranges;
@ -55,7 +57,7 @@ typedef std::array<Ranges, 16> RangesUnion;
typedef std::function<void()> Notifier;
typedef std::function<void(std::function<void()>&&)> Executor;
// ----------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------- //
constexpr auto BASIC_RANGES_NUM = 7311921;
@ -66,56 +68,75 @@ constexpr std::array ALL_CASES_NUM {
2942906, 2260392, 2942906, 0,
};
// TODO: move to short_code namespace (also `numeric` header)
constexpr auto ALL_CASES_NUM_ = std::accumulate(
ALL_CASES_NUM.begin(), ALL_CASES_NUM.end(), 0
);
ALL_CASES_NUM.begin(), ALL_CASES_NUM.end(), 0);
// ----------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------- //
class BasicRanges {
public:
void build() noexcept;
const Ranges& fetch() noexcept;
[[nodiscard]] bool is_available() const noexcept;
/// Execute the build process and ensure thread safety.
void build();
MARK_INSTANCE(BasicRanges);
static BasicRanges& instance() noexcept;
/// Get the basic-ranges and make sure the result is available.
const Ranges& fetch();
/// Determine whether the basic-ranges data is available.
[[nodiscard]] bool is_available() const;
private:
std::mutex building_;
bool available_ = false;
std::mutex building_ {};
/// Get static singleton variable.
static Ranges& get_ranges();
/// Search and sort all possible basic-ranges permutations.
static void build_ranges(Ranges &ranges);
BasicRanges() = default;
static Ranges& get_ranges() noexcept;
static void build_ranges(Ranges &ranges) noexcept;
static void spawn_ranges(Ranges &ranges, int, int, int, int) noexcept;
/// Spawn all range permutations of specified conditions.
static void spawn_ranges(Ranges &ranges, int, int, int, int);
KLSK_INSTANCE(BasicRanges)
};
// ----------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------- //
class AllCases {
public:
void build() noexcept;
void build_parallel(Executor &&executor) noexcept;
void build_parallel_async(Executor &&executor, Notifier &&callback) noexcept;
/// Execute the build process and ensure thread safety.
void build();
/// TODO: remove this interface
/// Execute the build process with parallel support and ensure thread safety.
void build_parallel(Executor &&executor);
/// Execute the build process in parallel without blocking.
void build_parallel_async(Executor &&executor, Notifier &&callback);
const RangesUnion& fetch() noexcept;
[[nodiscard]] bool is_available() const noexcept;
/// Get all-cases and make sure the result is available.
const RangesUnion& fetch();
MARK_INSTANCE(AllCases);
static AllCases& instance() noexcept;
/// Determine whether the all-cases data is available.
[[nodiscard]] bool is_available() const;
private:
std::mutex building_;
bool available_ = false;
std::mutex building_ {};
/// Get static singleton variable.
static RangesUnion& get_cases();
/// Build all valid ranges of the specified head.
static void build_cases(int head, Ranges &release);
AllCases() = default;
static RangesUnion& get_cases() noexcept;
static void build_cases(int head, Ranges &release) noexcept;
KLSK_INSTANCE(AllCases)
};
// ----------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------- //
} // namespace klotski::cases
#include "inline_impl.inl"
#include "internal/basic_ranges.inl"
#include "internal/all_cases.inl"

49
src/core/all_cases/inline_impl.inl

@ -1,49 +0,0 @@
#pragma once
namespace klotski::cases {
// ----------------------------------------------------------------------------------------- //
inline BasicRanges& BasicRanges::instance() noexcept {
static BasicRanges instance;
return instance;
}
inline Ranges& BasicRanges::get_ranges() noexcept {
static Ranges ranges;
return ranges;
}
inline const Ranges& BasicRanges::fetch() noexcept {
this->build();
return get_ranges();
}
inline bool BasicRanges::is_available() const noexcept {
return available_; // no mutex required in one-way state
}
// ----------------------------------------------------------------------------------------- //
inline AllCases& AllCases::instance() noexcept {
static AllCases instance;
return instance;
}
inline RangesUnion& AllCases::get_cases() noexcept {
static RangesUnion cases;
return cases;
}
inline const RangesUnion& AllCases::fetch() noexcept {
this->build();
return get_cases();
}
inline bool AllCases::is_available() const noexcept {
return available_; // no mutex required in one-way state
}
// ----------------------------------------------------------------------------------------- //
} // namespace klotski::codec

14
src/core/all_cases/all_cases.cc → src/core/all_cases/internal/all_cases.cc

@ -1,6 +1,6 @@
#include <future>
#include "all_cases.h"
#include "all_cases/all_cases.h"
namespace klotski::cases {
@ -49,8 +49,7 @@ static int check_range(const int head, uint32_t range) noexcept {
return 0; // pass check
}
/// Build all valid ranges of the specified head.
void AllCases::build_cases(const int head, Ranges &release) noexcept {
void AllCases::build_cases(const int head, Ranges &release) {
release.clear();
release.reserve(ALL_CASES_NUM[head]);
auto &basic_ranges = BasicRanges::instance().fetch();
@ -70,15 +69,13 @@ void AllCases::build_cases(const int head, Ranges &release) noexcept {
}
}
/// Execute the build process and ensure thread safety.
void AllCases::build() noexcept {
void AllCases::build() {
build_parallel([](auto &&func) {
func();
});
}
/// Execute the build process with parallel support and ensure thread safety.
void AllCases::build_parallel(Executor &&executor) noexcept {
void AllCases::build_parallel(Executor &&executor) {
if (available_) {
return; // reduce consumption of mutex
}
@ -101,8 +98,7 @@ void AllCases::build_parallel(Executor &&executor) noexcept {
available_ = true;
}
/// Execute the build process in parallel without blocking.
void AllCases::build_parallel_async(Executor &&executor, Notifier &&callback) noexcept {
void AllCases::build_parallel_async(Executor &&executor, Notifier &&callback) {
if (available_) {
callback();
return; // reduce consumption of mutex

21
src/core/all_cases/internal/all_cases.inl

@ -0,0 +1,21 @@
#pragma once
namespace klotski::cases {
inline RangesUnion& AllCases::get_cases() {
static RangesUnion cases;
return cases;
}
inline const RangesUnion& AllCases::fetch() {
if (!available_) {
build();
}
return get_cases();
}
inline bool AllCases::is_available() const {
return available_; // no mutex required in one-way state
}
} // namespace klotski::codec

11
src/core/all_cases/basic_ranges.cc → src/core/all_cases/internal/basic_ranges.cc

@ -1,7 +1,7 @@
#include <list>
#include <algorithm>
#include "all_cases.h"
#include "all_cases/all_cases.h"
namespace klotski::cases {
@ -38,8 +38,7 @@ static void combine_sort(RangeIter begin, RangeIter mid, RangeIter end) noexcept
}
}
/// Spawn all ranges of specified conditions.
void BasicRanges::spawn_ranges(Ranges &ranges, int n1, int n2, int n3, int n4) noexcept {
void BasicRanges::spawn_ranges(Ranges &ranges, const int n1, const int n2, const int n3, const int n4) {
auto num = n1 + n2 + n3 + n4;
auto offset = (16 - num) << 1; // offset of low bits
@ -58,8 +57,7 @@ void BasicRanges::spawn_ranges(Ranges &ranges, int n1, int n2, int n3, int n4) n
} while (next_permutation(series.begin(), series.end()));
}
/// Search and sort all possible basic-ranges permutations.
void BasicRanges::build_ranges(Ranges &ranges) noexcept {
void BasicRanges::build_ranges(Ranges &ranges) {
ranges.clear();
ranges.reserve(BASIC_RANGES_NUM);
std::list<RangeIter> flags {ranges.begin()}; // mark ordered interval
@ -82,8 +80,7 @@ void BasicRanges::build_ranges(Ranges &ranges) noexcept {
}
}
/// Execute the build process and ensure thread safety.
void BasicRanges::build() noexcept {
void BasicRanges::build() {
if (available_) {
return; // reduce consumption of mutex
}

21
src/core/all_cases/internal/basic_ranges.inl

@ -0,0 +1,21 @@
#pragma once
namespace klotski::cases {
inline Ranges& BasicRanges::get_ranges() {
static Ranges ranges;
return ranges;
}
inline const Ranges& BasicRanges::fetch() {
if (!available_) {
build();
}
return get_ranges();
}
inline bool BasicRanges::is_available() const {
return available_; // no mutex required in one-way state
}
} // namespace klotski::codec

23
src/core/utils/utility.h

@ -2,14 +2,23 @@
#include <cstdint>
#define MARK_INSTANCE(T) \
T(T &&) = delete; \
T(const T &) = delete; \
T& operator=(T &&) = delete; \
T& operator=(const T &) = delete;
/// Mark target class as a singleton.
#define KLSK_INSTANCE(T) \
private: \
T() = default; \
public: \
T(T &&) = delete; \
T(const T &) = delete; \
T& operator=(T &&) = delete; \
T& operator=(const T &) = delete; \
static T& instance() { \
static T ins; \
return ins; \
}
namespace klotski {
/// Get the number of consecutive `0` in the low bits.
inline int low_zero_num(const uint32_t bin) {
return __builtin_ctzl(bin);
@ -19,6 +28,7 @@ inline int low_zero_num(const uint32_t bin) {
// return __builtin_popcount(~(bin ^ -bin)) - 1;
}
/// Get the number of consecutive `0` in the low bits.
inline int low_zero_num(const uint64_t bin) {
return __builtin_ctzll(bin);
@ -26,12 +36,13 @@ inline int low_zero_num(const uint64_t bin) {
// return __builtin_popcount(~(bin ^ -bin)) - 1;
}
/// Flips the input u32 every two bits in low-high symmetry.
inline uint32_t range_reverse(uint32_t bin) {
#if defined(__GNUC__) || defined(__clang__)
bin = __builtin_bswap32(bin);
// TODO: using `std::byteswap` (c++23)
#else
// FIXME: `_byteswap_ulong` under MSVC
// TODO: using `std::byteswap` (c++23)
bin = ((bin << 16) & 0xFFFF0000) | ((bin >> 16) & 0x0000FFFF);
bin = ((bin << 8) & 0xFF00FF00) | ((bin >> 8) & 0x00FF00FF);
#endif

Loading…
Cancel
Save