diff --git a/src/core/group/group.h b/src/core/group/group.h index c11d3f5..bc55c6d 100644 --- a/src/core/group/group.h +++ b/src/core/group/group.h @@ -23,21 +23,21 @@ /// can also be reversed to get the number of blocks, which are one by one /// corresponding. /// -/// flag => | 0xxx | 0xxx | xxxx | -/// (12-bit) | (n_1x2 + n_2x1) | (n_2x1) | (n_1x1) | +/// flag => | xxx | xxx | xxxx | +/// (10-bit) | (n_1x2 + n_2x1) | (n_2x1) | (n_1x1) | /// | (0 ~ 7) | (0 ~ 7) | (0 ~ 14) | /// -/// flag => ((n_1x2 + n_2x1) << 8) | (n_2x1 << 4) | (n_1x1) +/// flag => ((n_1x2 + n_2x1) << 7) | (n_2x1 << 3) | (n_1x1) /// /// Using the table lookup method, the `type_id` of any case can be obtained -/// within O(1), which is encapsulated in `GroupType`. +/// within O(1), which is encapsulated in `GroupUnion`. /// Since the `type_id` cannot change when moving, all cases belonging to the /// same `type_id` must be divided into different groups (of course there may /// be only one). For a group, list the CommonCodes of all its cases, the /// smallest of which is called the group's `seed`. List all the groups under /// the same `type_id`, and arrange them from large to small, and arrange the -/// groups of the same size from small to large according to the `seed`, and +/// groups of the same size from small to large according to the `seed`, then /// start numbering from 0 to get the `group_id`. /// All cases of the same group will have the same `type_id` and `group_id`, @@ -51,14 +51,14 @@ /// /// Eg1: 1A9BF0C00 -> `169-1-7472` /// Eg2: 4FEA13400 -> `164-0-30833` -/// + /// The range of `type_id` is [0, 203), the maximum `group_id` is 2652 (there -/// are 2653 groups when `type_id` is 164), the maximum `case_id` is 964655 -/// (there are 964656 cases when `type_id` is 58 and `group_id` is 0). -/// Therefore, these three numbers meet the following range requirements. +/// are 2653 groups when `type_id = 164`), the maximum `case_id` is 964655 +/// (there are 964656 cases when `type_id = 58` & `group_id = 0`). Therefore, +/// these three numbers meet the following range requirements. /// -/// type_id < 203 | group_id < 2653 | case_id < 964656 -/// (8-bit ~ 256) | (12-bit ~ 4096) | (20-bit ~ 1048576) +/// | type_id: [0, 203) | group_id: [0, 2653) | case_id: [0, 964656) | +/// | (8-bit ~ 256) | (12-bit ~ 4096) | (20-bit ~ 1048576) | /// /// Typically, these three variables are generally recorded in decimal and /// displayed in the form of strings. They can facilitate the relationship @@ -67,103 +67,117 @@ #pragma once #include "raw_code/raw_code.h" +#include "short_code/short_code.h" #include "common_code/common_code.h" -static_assert(sizeof(int) == 4); - namespace klotski::cases { class Group; -// TODO: should we expose block_num_t ? +class GroupUnion { +public: + GroupUnion() = delete; -// TODO: flat_iter for Groups ? + // ------------------------------------------------------------------------------------- // -// TODO: should we make sure the thread safe of GroupUnion / Group ? + /// Get the original type id. + [[nodiscard]] int type_id() const; -/// RSC -> block_num_t <-> type_id -> group_num -/// |-----> max_size + /// Get the number of cases contained. + [[nodiscard]] uint32_t size() const; -/// RSC -> seed <-> type_id + group_id -> cases + /// Get the number of groups contained. + [[nodiscard]] uint32_t group_num() const; -/// info_t <-> RSC + /// Get the upper limit of the group size. + [[nodiscard]] uint32_t max_group_size() const; -class GroupUnion { -public: - /// 1. n_1x1 + (n_1x2 + n_2x1) * 2 <= 14 - /// 2. (n_1x1 != 0) && (n_2x1 != 7) - struct block_num_t { - uint8_t n_1x1 = 0; /// [0, 14] - uint8_t n_1x2 = 0; /// [0, 7] - uint8_t n_2x1 = 0; /// [0, 7] - }; - /// n_space = 16 - n_1x1 - (n_1x2 + n_2x1) * 2 + /// Get all group instances under the current type id. + [[nodiscard]] std::vector groups() const; - GroupUnion() = delete; - // TODO: disallow copy or move + /// Get the group instance with the specified group id. + [[nodiscard]] std::optional group(int group_id) const; -private: - // TODO: only store type_id_ - int type_id_ {}; + // ------------------------------------------------------------------------------------- // - // TODO: only allow private build (std::bit_cast directly) - explicit GroupUnion(const int type_id) : type_id_(type_id) {} - - // TODO: fast convert from RawCode / CommonCode -> block_num_t - static block_num_t block_num(codec::RawCode raw_code); - static block_num_t block_num(codec::CommonCode common_code); - - // TODO: should we impl type_id -> block_num ? + /// Create GroupUnion from type id. + static std::optional from_id(int type_id); - // TODO: convert from block_num -> type_id - static int type_id(block_num_t block_num); - -public: - // TODO: allow convert from RawCode / ShortCode / CommonCode - // TODO: GroupUnion will fetch from static singleton (203 instances) + /// Create GroupUnion from RawCode. static GroupUnion from_raw_code(codec::RawCode raw_code); + + /// Create GroupUnion from ShortCode. static GroupUnion from_short_code(codec::RawCode short_code); + + /// Create GroupUnion from CommonCode. static GroupUnion from_common_code(codec::CommonCode common_code); - // TODO: fetch singleton from type_id - static std::optional from_type_id(int type_id); + // ------------------------------------------------------------------------------------- // - // TODO: get Group from local (init by seed) - [[nodiscard]] Group group(int group_id); +private: + int type_id_ {}; - // TODO: GroupUnion do not storage cases (new version) + // TODO: only allow private build (std::bit_cast directly) + // explicit GroupUnion(const int type_id) : type_id_(type_id) {} }; class Group { -private: - // TODO: Group init by `seed` - // TODO: Group will also storage parent's type_id (or maybe not) +public: + Group() = delete; + + // TODO: fetch group size directly + [[nodiscard]] uint32_t size() const; + + void build(); + + // TODO: maybe define CommonCodes here + // TODO: get all cases from current Group + const std::vector& cases(); + static Group from_raw_code(codec::RawCode raw_code); + static Group from_common_code(codec::CommonCode common_code); + +private: int type_id_; - int group_id_; // -> seed / size + int group_id_; - explicit Group(int type_id, int group_id) : type_id_(type_id), group_id_(group_id) {} + // TODO: only allow cast from struct directly. + // explicit Group(int type_id, int group_id) : type_id_(type_id), group_id_(group_id) {} - // TODO: add mutex on the build process + // TODO: mutex only in inner impl class. // bool available_; // std::mutex building_; +}; +class GroupCase { public: - // TODO: iter all Groups like a flat array ? + struct info_t { + uint16_t type_id; // TODO: int or uint ? + uint16_t group_id; + uint32_t group_index; + }; - Group() = delete; + // TODO: mark as instance. - // TODO: maybe define CommonCodes here - // TODO: get all cases from current Group - const std::vector& cases(); + /// Build group cases accelerated index. + static void speed_up(); - // TODO: can we fetch Group directly from here ? - // args: type_id & group_id + /// Get the CommonCode using the group info. + static codec::CommonCode parse(const info_t &info); - // TODO: fetch group size directly - [[nodiscard]] uint32_t size() const; -}; + /// Get group info according to specified case. + static info_t encode(const codec::RawCode &raw_code); + static info_t encode(const codec::CommonCode &common_code); + +private: + static bool available_; + static std::mutex building_; + + static codec::CommonCode fast_decode(const info_t &info); + static codec::CommonCode tiny_decode(const info_t &info); -// TODO: fast convert from info_t <--> CommonCode / RawCode + static info_t fast_encode(const codec::CommonCode &common_code); + static info_t tiny_encode(const codec::CommonCode &common_code); +}; } // namespace klotski::cases diff --git a/src/core/group/internal/group.cc b/src/core/group/internal/group.cc index b3853ea..dca4ddc 100644 --- a/src/core/group/internal/group.cc +++ b/src/core/group/internal/group.cc @@ -1 +1,19 @@ #include "group/group.h" + +/// 1. n_1x1 + (n_1x2 + n_2x1) * 2 <= 14 +/// 2. (n_1x1 != 0) && (n_2x1 != 7) +struct block_num_t { + uint8_t n_1x1 = 0; /// [0, 14] + uint8_t n_1x2 = 0; /// [0, 7] + uint8_t n_2x1 = 0; /// [0, 7] +}; +/// n_space = 16 - n_1x1 - (n_1x2 + n_2x1) * 2 + +// TODO: fast convert from RawCode / CommonCode -> block_num_t +static block_num_t block_num(klotski::codec::RawCode raw_code); +static block_num_t block_num(klotski::codec::CommonCode common_code); + +// TODO: should we impl type_id -> block_num ? + +// TODO: convert from block_num -> type_id +static int type_id(block_num_t block_num); diff --git a/src/core/group/internal/group.inl b/src/core/group/internal/group.inl new file mode 100644 index 0000000..d8c6920 --- /dev/null +++ b/src/core/group/internal/group.inl @@ -0,0 +1,23 @@ +#pragma once + +// namespace internal { +// +// class GroupImpl { +// public: +// explicit GroupImpl(int flat_id) : flat_id_(flat_id) {} +// +// const std::vector& cases(); +// +// int flat_id_; +// +// static constexpr std::array ins() { +// return std::array {}; +// } +// }; +// +// } // namespace internal +// +// inline const std::vector& Group::cases() { +// static auto kk = internal::GroupImpl::ins(); +// return kk[0].cases(); +// } diff --git a/src/core/main.cc b/src/core/main.cc index 0ad642d..b9bf9d4 100644 --- a/src/core/main.cc +++ b/src/core/main.cc @@ -2,6 +2,7 @@ #include #include "core/core.h" +#include "group/group.h" #include "raw_code/raw_code.h" #include "fast_cal/fast_cal.h" #include "all_cases/all_cases.h" @@ -16,6 +17,7 @@ using klotski::cases::BasicRanges; using klotski::codec::RawCode; using klotski::codec::ShortCode; using klotski::codec::CommonCode; +using klotski::cases::GroupUnion; using klotski::codec::SHORT_CODE_LIMIT; @@ -29,7 +31,7 @@ int main() { // core.next_cases(RawCode::from_common_code(0x1A9BF0C00).value().unwrap(), 0x0); // auto cal = FastCal(RawCode::from_common_code(0x1A9BF0C00).value()); - auto cal = FastCal(RawCode::from_common_code("25EEF04").value()); + // auto cal = FastCal(RawCode::from_common_code("25EEF04").value()); // auto ret = cal.solve(); // @@ -37,13 +39,13 @@ int main() { // std::cout << kk.to_common_code() << "," << kk.to_common_code().to_short_code() << std::endl; // } - for (const auto solve : cal.solve_multi()) { - for (const auto raw_code : cal.backtrack(solve)) { - const auto common_code = raw_code.to_common_code(); - std::cout << common_code << "/" << common_code.to_short_code() << std::endl; - } - std::cout << "----" << std::endl; - } + // for (const auto solve : cal.solve_multi()) { + // for (const auto raw_code : cal.backtrack(solve)) { + // const auto common_code = raw_code.to_common_code(); + // std::cout << common_code << "/" << common_code.to_short_code() << std::endl; + // } + // std::cout << "----" << std::endl; + // } std::cerr << ((clock() - start) * 1000 / CLOCKS_PER_SEC) << "ms" << std::endl;