diff --git a/src/klotski_core/group/CMakeLists.txt b/src/klotski_core/group/CMakeLists.txt index 0df73cf..512a441 100644 --- a/src/klotski_core/group/CMakeLists.txt +++ b/src/klotski_core/group/CMakeLists.txt @@ -1,3 +1,3 @@ cmake_minimum_required(VERSION 3.0) -add_library(group OBJECT seeds.cc group.cc block_num.cc block_num_.cc build_cases.cc group_info.cc) +add_library(group OBJECT seeds.cc basic_id.cc build_cases.cc group_info.cc) diff --git a/src/klotski_core/group/block_num.cc b/src/klotski_core/group/basic_id.cc similarity index 75% rename from src/klotski_core/group/block_num.cc rename to src/klotski_core/group/basic_id.cc index c908b0e..7fc3f71 100644 --- a/src/klotski_core/group/block_num.cc +++ b/src/klotski_core/group/basic_id.cc @@ -8,6 +8,8 @@ namespace klotski { using Common::range_reverse; +/// ----------------------------------------- Type ID ----------------------------------------- + TypeId::TypeId(uint32_t type_id) { if (type_id >= TYPE_ID_LIMIT) { // invalid type id throw std::invalid_argument("type id overflow"); @@ -31,6 +33,8 @@ uint32_t TypeId::to_type_id(block_num_t &&block_num) noexcept { return std::lower_bound(TYPE_ID_INDEX, TYPE_ID_INDEX + TYPE_ID_LIMIT, flag) - TYPE_ID_INDEX; } +/// -------------------------------------- Block Number --------------------------------------- + TypeId::block_num_t TypeId::block_num() const noexcept { auto flag = TYPE_ID_INDEX[type_id_]; auto n_2x1 = (flag >> 4) & 0b111; @@ -79,4 +83,20 @@ TypeId::block_num_t TypeId::block_num(const CommonCode &common_code) noexcept { return result; } +/// ---------------------------------------- Group ID ----------------------------------------- + +GroupId::GroupId(uint32_t type_id, uint32_t group_id) : type_id_(type_id) { + if (group_id >= TYPE_ID_GROUP_NUM[type_id]) { + throw std::invalid_argument("group id overflow"); + } + group_id_ = group_id; +} + +GroupId::GroupId(const TypeId &type_id, uint32_t group_id) : type_id_(type_id) { + if (group_id >= TYPE_ID_GROUP_NUM[type_id.unwrap()]) { + throw std::invalid_argument("group id overflow"); + } + group_id_ = group_id; +} + } // namespace klotski diff --git a/src/klotski_core/group/block_num_.cc b/src/klotski_core/group/block_num_.cc deleted file mode 100644 index 88d75f0..0000000 --- a/src/klotski_core/group/block_num_.cc +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include "common.h" -#include "type_id.h" - -namespace klotski { - -using Common::range_reverse; - -uint32_t Group::type_id(const RawCode &raw_code) { - return type_id(block_num(raw_code)); -} - -uint32_t Group::type_id(const CommonCode &common_code) { - return type_id(block_num(common_code)); -} - -uint32_t Group::type_id(const block_num_t &block_num) { - /// flag -> ... 0000 0xxx 0xxx xxxx - /// n_x2x n_2x1 n_1x1 - auto n_x2x = block_num.n_1x2 + block_num.n_2x1; - auto flag = (n_x2x << 8) | (block_num.n_2x1 << 4) | block_num.n_1x1; - return std::lower_bound(TYPE_ID_INDEX, TYPE_ID_INDEX + TYPE_ID_LIMIT, flag) - TYPE_ID_INDEX; -} - -Group::block_num_t Group::block_num(uint32_t type_id) { - auto flag = TYPE_ID_INDEX[type_id]; - uint8_t n_x2x = flag >> 8; - uint8_t n_2x1 = (flag >> 4) & 0b1111; - return block_num_t { - .n_1x1 = static_cast(flag & 0b1111), - .n_1x2 = static_cast(n_x2x - n_2x1), - .n_2x1 = n_2x1, - }; -} - -Group::block_num_t Group::block_num(const RawCode &raw_code) { - block_num_t result; - auto tmp = raw_code.unwrap(); - for (int addr = 0; addr < 20; ++addr, tmp >>= 3) { - switch (tmp & 0b111) { - case B_1x1: - ++result.n_1x1; - continue; - case B_1x2: - ++result.n_1x2; - continue; - case B_2x1: - ++result.n_2x1; - continue; - } - } - return result; -} - -Group::block_num_t Group::block_num(const CommonCode &common_code) { - block_num_t result; - auto range = range_reverse((uint32_t)common_code.unwrap()); - for (; range; range >>= 2) { - switch (range & 0b11) { - case 0b01: /// 1x2 block - ++result.n_1x2; - continue; - case 0b10: /// 2x1 block - ++result.n_2x1; - continue; - case 0b11: /// 1x1 block - ++result.n_1x1; - continue; - } - } - return result; -} - -} // namespace klotski diff --git a/src/klotski_core/group/build_cases.cc b/src/klotski_core/group/build_cases.cc index a23eb51..e844441 100644 --- a/src/klotski_core/group/build_cases.cc +++ b/src/klotski_core/group/build_cases.cc @@ -15,7 +15,7 @@ using Common::check_range; using Common::range_reverse; std::vector Group::all_cases(uint32_t type_id) { - auto tmp = block_num(type_id); + auto tmp = TypeId(type_id).block_num(); std::vector ranges; // basic ranges of type_id BasicRanges::generate(ranges, { // generate target ranges .n1 = 16 - tmp.n_1x1 - (tmp.n_1x2 + tmp.n_2x1) * 2, /// space -> 00 diff --git a/src/klotski_core/group/group.cc b/src/klotski_core/group/group.cc deleted file mode 100644 index 575b1a7..0000000 --- a/src/klotski_core/group/group.cc +++ /dev/null @@ -1,5 +0,0 @@ -#include "group.h" - -namespace klotski { - -} // namespace klotski diff --git a/src/klotski_core/group/group.h b/src/klotski_core/group/group.h index eb3e4e9..e7666e0 100644 --- a/src/klotski_core/group/group.h +++ b/src/klotski_core/group/group.h @@ -8,6 +8,8 @@ namespace klotski { const uint32_t TYPE_ID_LIMIT = 203; +/// ----------------------------------------- Type ID ----------------------------------------- + class TypeId { public: /// 1. n_1x1 + (n_1x2 + n_2x1) * 2 <= 14 @@ -48,34 +50,41 @@ inline bool operator!=(const TypeId::block_num_t &b1, const TypeId::block_num_t return (b1.n_1x1 != b2.n_1x1) || (b1.n_1x2 != b2.n_1x2) || (b1.n_2x1 != b2.n_2x1); } -class Group { -/// -------------------------------- block statistics --------------------------------- +/// ---------------------------------------- Group ID ----------------------------------------- + +class GroupId { + TypeId type_id_; + uint32_t group_id_; + 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] - }; + GroupId(uint32_t type_id, uint32_t group_id); + GroupId(const TypeId &type_id, uint32_t group_id); - /// Get type_id value (0 ~ 202). - static uint32_t type_id(const RawCode &raw_code); - static uint32_t type_id(const block_num_t &block_num); - static uint32_t type_id(const CommonCode &common_code); + /// Release raw type id / group id value. + constexpr uint32_t unwrap() const noexcept { return group_id_; } + constexpr uint32_t type_id() const noexcept { return type_id_.unwrap(); } +}; - /// Get the number of klotski blocks. - static block_num_t block_num(uint32_t type_id); - static block_num_t block_num(const RawCode &raw_code); - static block_num_t block_num(const CommonCode &common_code); +inline bool operator==(const GroupId &g1, const GroupId &g2) { + return g1.type_id() == g2.type_id() && g1.unwrap() == g2.unwrap(); +} -/// --------------------------------- cases expansion --------------------------------- +inline bool operator!=(const GroupId &g1, const GroupId &g2) { + return g1.type_id() != g2.type_id() || g1.unwrap() != g2.unwrap(); +} + +/// ------------------------------------------ Group ------------------------------------------ + +class Group { +public: +/// ----------------------------------- group seeds ----------------------------------- static CommonCode group_seed(const RawCode &raw_code); static CommonCode group_seed(const CommonCode &common_code); static CommonCode group_seed(uint32_t type_id, uint32_t group_id); - static std::vector group_seeds(uint32_t type_id); + /// Get all seeds in the specified type id. + static std::vector group_seeds(const TypeId &type_id); /// --------------------------------- cases expansion --------------------------------- @@ -127,12 +136,4 @@ public: }; -inline bool operator==(const Group::block_num_t &b1, const Group::block_num_t &b2) { - return (b1.n_1x1 == b2.n_1x1) && (b1.n_1x2 == b2.n_1x2) && (b1.n_2x1 == b2.n_2x1); -} - -inline bool operator!=(const Group::block_num_t &b1, const Group::block_num_t &b2) { - return (b1.n_1x1 != b2.n_1x1) || (b1.n_1x2 != b2.n_1x2) || (b1.n_2x1 != b2.n_2x1); -} - } diff --git a/src/klotski_core/group/group_info.cc b/src/klotski_core/group/group_info.cc index 7de93e1..57a6860 100644 --- a/src/klotski_core/group/group_info.cc +++ b/src/klotski_core/group/group_info.cc @@ -16,7 +16,7 @@ Group::group_info_t Group::group_info(const RawCode &raw_code) { Group::group_info_t Group::group_info(const CommonCode &common_code) { - auto type_id = Group::type_id(common_code); + auto type_id = TypeId(common_code).unwrap(); std::cout << type_id << std::endl; std::cout << "group num: " << TYPE_ID_GROUP_NUM[type_id] << std::endl; diff --git a/src/klotski_core/group/seeds.cc b/src/klotski_core/group/seeds.cc index 3cb6eea..3355806 100644 --- a/src/klotski_core/group/seeds.cc +++ b/src/klotski_core/group/seeds.cc @@ -6,6 +6,8 @@ namespace klotski { + + CommonCode Group::group_seed(uint32_t type_id, uint32_t group_id) { // TODO: check value @@ -31,4 +33,23 @@ CommonCode Group::group_seed(uint32_t type_id, uint32_t group_id) { return seed; } +std::vector Group::group_seeds(const TypeId &type_id) { + auto offset = GROUP_SEEDS + TYPE_ID_OFFSET[type_id.unwrap()]; // group id offset + return {offset, offset + TYPE_ID_GROUP_NUM[type_id.unwrap()]}; +} + +CommonCode Group::group_seed(const RawCode &raw_code) { + + + + return CommonCode(0); +} + +CommonCode Group::group_seed(const CommonCode &common_code) { + + + + return CommonCode(0); +} + } // namespace klotski diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8f31179..e88e8b3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -61,7 +61,7 @@ add_test(NAME core COMMAND test_core) ####################################################################################### set(TEST_GROUP_SRC - group/block_num.cc + group/basic_id.cc group/build_cases.cc ) add_executable(test_group ${TEST_GROUP_SRC}) diff --git a/test/group/block_num.cc b/test/group/basic_id.cc similarity index 75% rename from test/group/block_num.cc rename to test/group/basic_id.cc index c151f01..2f8e943 100644 --- a/test/group/block_num.cc +++ b/test/group/basic_id.cc @@ -1,16 +1,19 @@ #include #include "md5.h" #include "group.h" +#include "type_id.h" #include "all_cases.h" #include "gtest/gtest.h" using klotski::TypeId; +using klotski::GroupId; using klotski::RawCode; using klotski::AllCases; using klotski::CommonCode; using klotski::TYPE_ID_LIMIT; using klotski::ALL_CASES_SIZE; +using klotski::TYPE_ID_GROUP_NUM; const char BLOCK_NUM_MD5[] = "46a7b3af6d039cbe2f7eaebdd196c6a2"; @@ -19,7 +22,7 @@ TEST(Group, type_id) { EXPECT_EQ(TypeId(type_id).unwrap(), type_id); } try { - EXPECT_EQ(TypeId(TYPE_ID_LIMIT).unwrap(), TYPE_ID_LIMIT + 1); + EXPECT_EQ(TypeId(TYPE_ID_LIMIT).unwrap(), -1); } catch (...) {} // should panic auto test = [](uint64_t head) { @@ -69,3 +72,23 @@ TEST(Group, block_num) { auto block_num_md5 = md5(block_num_str.c_str(), block_num_str.size()); EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5); // verify md5 } + +TEST(Group, group_id) { + for (uint32_t type_id = 0; type_id < TYPE_ID_LIMIT; ++type_id) { + for (uint32_t group_id = 0; group_id < TYPE_ID_GROUP_NUM[type_id]; ++group_id) { + auto gid = GroupId(type_id, group_id); + EXPECT_EQ(gid, GroupId(TypeId(type_id), group_id)); + EXPECT_EQ(gid.type_id(), type_id); + EXPECT_EQ(gid.unwrap(), group_id); + } + try { + EXPECT_EQ(GroupId(type_id, TYPE_ID_GROUP_NUM[type_id]).unwrap(), -1); + } catch (...) {} // should panic + } +} + +TEST(Group, operators) { + // TODO: TypeId + // TODO: block_num_t + // TODO: GroupId +} diff --git a/test/group/build_cases.cc b/test/group/build_cases.cc index f65adbc..cfd58ae 100644 --- a/test/group/build_cases.cc +++ b/test/group/build_cases.cc @@ -10,6 +10,7 @@ #include "group/group_seeds.h" using klotski::Group; +using klotski::TypeId; using klotski::AllCases; using klotski::RawCode; @@ -35,7 +36,7 @@ TEST(Group, all_cases) { for (uint32_t id = 0; id < TYPE_ID_LIMIT; ++id) { EXPECT_EQ(all_cases[id].size(), TYPE_ID_SIZE[id]); // verify cases number for (auto &&common_code : all_cases[id]) { - EXPECT_EQ(Group::type_id(common_code), id); // verify type id + EXPECT_EQ(TypeId(common_code).unwrap(), id); // verify type id combine.emplace_back(common_code.unwrap()); } std::is_sorted(all_cases[id].begin(), all_cases[id].end()); // verify data order @@ -55,9 +56,9 @@ TEST(Group, group_cases) { std::vector group(group_raw.begin(), group_raw.end()); // convert as CommonCodes EXPECT_EQ(seed, std::min_element(group.begin(), group.end())->unwrap()); // confirm min seed - uint32_t type_id = Group::type_id(CommonCode(seed)); // current type id + uint32_t type_id = TypeId(CommonCode(seed)).unwrap(); // current type id for (auto &&elem : group) { - EXPECT_EQ(Group::type_id(elem), type_id); // verify type id of group cases + EXPECT_EQ(TypeId(elem).unwrap(), type_id); // verify type id of group cases } return group; }; @@ -110,7 +111,7 @@ TEST(Group, build_groups) { .group_id = static_cast(id), .group_index = index, }; - EXPECT_EQ(Group::type_id(groups[id][index]), type_id); // verify type id + EXPECT_EQ(TypeId(groups[id][index]).unwrap(), type_id); // verify type id } group_seeds.emplace_back(groups[id][0]); // record seed }