From db6b4b12359c7f2f1404cac7ba1192bb932696e5 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Fri, 14 Apr 2023 18:17:33 +0800 Subject: [PATCH] feat: TypeId module --- src/klotski_core/group/CMakeLists.txt | 2 +- src/klotski_core/group/block_num.cc | 38 ++++++++------ src/klotski_core/group/block_num_.cc | 74 +++++++++++++++++++++++++++ src/klotski_core/group/group.h | 40 +++++++++++++++ test/group/block_num.cc | 69 ++++++++++++++----------- 5 files changed, 177 insertions(+), 46 deletions(-) create mode 100644 src/klotski_core/group/block_num_.cc diff --git a/src/klotski_core/group/CMakeLists.txt b/src/klotski_core/group/CMakeLists.txt index e0cadd0..0df73cf 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 build_cases.cc group_info.cc) +add_library(group OBJECT seeds.cc group.cc block_num.cc block_num_.cc build_cases.cc group_info.cc) diff --git a/src/klotski_core/group/block_num.cc b/src/klotski_core/group/block_num.cc index 88d75f0..c908b0e 100644 --- a/src/klotski_core/group/block_num.cc +++ b/src/klotski_core/group/block_num.cc @@ -1,4 +1,6 @@ -#include +#include +#include +#include "group.h" #include "common.h" #include "type_id.h" @@ -6,15 +8,22 @@ namespace klotski { using Common::range_reverse; -uint32_t Group::type_id(const RawCode &raw_code) { - return type_id(block_num(raw_code)); +TypeId::TypeId(uint32_t type_id) { + if (type_id >= TYPE_ID_LIMIT) { // invalid type id + throw std::invalid_argument("type id overflow"); + } + type_id_ = type_id; +} + +TypeId::TypeId(const RawCode &raw_code) noexcept { + type_id_ = to_type_id(block_num(raw_code)); } -uint32_t Group::type_id(const CommonCode &common_code) { - return type_id(block_num(common_code)); +TypeId::TypeId(const CommonCode &common_code) noexcept { + type_id_ = to_type_id(block_num(common_code)); } -uint32_t Group::type_id(const block_num_t &block_num) { +uint32_t TypeId::to_type_id(block_num_t &&block_num) noexcept { /// flag -> ... 0000 0xxx 0xxx xxxx /// n_x2x n_2x1 n_1x1 auto n_x2x = block_num.n_1x2 + block_num.n_2x1; @@ -22,18 +31,17 @@ uint32_t Group::type_id(const block_num_t &block_num) { 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; +TypeId::block_num_t TypeId::block_num() const noexcept { + auto flag = TYPE_ID_INDEX[type_id_]; + auto n_2x1 = (flag >> 4) & 0b111; return block_num_t { .n_1x1 = static_cast(flag & 0b1111), - .n_1x2 = static_cast(n_x2x - n_2x1), - .n_2x1 = n_2x1, + .n_1x2 = static_cast((flag >> 8) - n_2x1), + .n_2x1 = static_cast(n_2x1), }; } -Group::block_num_t Group::block_num(const RawCode &raw_code) { +TypeId::block_num_t TypeId::block_num(const RawCode &raw_code) noexcept { block_num_t result; auto tmp = raw_code.unwrap(); for (int addr = 0; addr < 20; ++addr, tmp >>= 3) { @@ -52,9 +60,9 @@ Group::block_num_t Group::block_num(const RawCode &raw_code) { return result; } -Group::block_num_t Group::block_num(const CommonCode &common_code) { +TypeId::block_num_t TypeId::block_num(const CommonCode &common_code) noexcept { block_num_t result; - auto range = range_reverse((uint32_t)common_code.unwrap()); + auto range = range_reverse(static_cast(common_code.unwrap())); for (; range; range >>= 2) { switch (range & 0b11) { case 0b01: /// 1x2 block diff --git a/src/klotski_core/group/block_num_.cc b/src/klotski_core/group/block_num_.cc new file mode 100644 index 0000000..88d75f0 --- /dev/null +++ b/src/klotski_core/group/block_num_.cc @@ -0,0 +1,74 @@ +#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/group.h b/src/klotski_core/group/group.h index c8e6028..eb3e4e9 100644 --- a/src/klotski_core/group/group.h +++ b/src/klotski_core/group/group.h @@ -8,6 +8,46 @@ namespace klotski { const uint32_t TYPE_ID_LIMIT = 203; +class TypeId { +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 + +private: + uint32_t type_id_; + static inline uint32_t to_type_id(block_num_t &&block_num) noexcept; + +public: + explicit TypeId(uint32_t type_id); + explicit TypeId(const RawCode &raw_code) noexcept; + explicit TypeId(const CommonCode &common_code) noexcept; + + /// Release raw type id value. + constexpr uint32_t unwrap() const noexcept { return type_id_; } + + /// Get the number of klotski blocks. + block_num_t block_num() const noexcept; + static block_num_t block_num(const RawCode &raw_code) noexcept; + static block_num_t block_num(const CommonCode &common_code) noexcept; +}; + +inline bool operator==(const TypeId &t1, const TypeId &t2) { return t1.unwrap() == t2.unwrap(); } +inline bool operator!=(const TypeId &t1, const TypeId &t2) { return t1.unwrap() != t2.unwrap(); } + +inline bool operator==(const TypeId::block_num_t &b1, const TypeId::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 TypeId::block_num_t &b1, const TypeId::block_num_t &b2) { + return (b1.n_1x1 != b2.n_1x1) || (b1.n_1x2 != b2.n_1x2) || (b1.n_2x1 != b2.n_2x1); +} + class Group { /// -------------------------------- block statistics --------------------------------- public: diff --git a/test/group/block_num.cc b/test/group/block_num.cc index 359e9d6..c151f01 100644 --- a/test/group/block_num.cc +++ b/test/group/block_num.cc @@ -4,59 +4,68 @@ #include "all_cases.h" #include "gtest/gtest.h" -using klotski::Group; +using klotski::TypeId; +using klotski::RawCode; using klotski::AllCases; using klotski::CommonCode; + using klotski::TYPE_ID_LIMIT; +using klotski::ALL_CASES_SIZE; const char BLOCK_NUM_MD5[] = "46a7b3af6d039cbe2f7eaebdd196c6a2"; TEST(Group, type_id) { - std::thread threads[16]; + for (uint32_t type_id = 0; type_id < TYPE_ID_LIMIT; ++type_id) { + EXPECT_EQ(TypeId(type_id).unwrap(), type_id); + } + try { + EXPECT_EQ(TypeId(TYPE_ID_LIMIT).unwrap(), TYPE_ID_LIMIT + 1); + } catch (...) {} // should panic + auto test = [](uint64_t head) { - for (const auto &range : AllCases::fetch()[head]) { + for (auto &&range: AllCases::fetch()[head]) { auto common_code = CommonCode::unsafe_create(head << 32 | range); - auto type_id = Group::type_id(common_code); // found type id - EXPECT_LT(type_id, TYPE_ID_LIMIT); - EXPECT_EQ(type_id, Group::type_id(common_code.to_raw_code())); - EXPECT_EQ(type_id, Group::type_id(Group::block_num(common_code))); - EXPECT_EQ(Group::block_num(type_id), Group::block_num(common_code)); + auto raw_code = RawCode::from_common_code(common_code); + + auto type_id = TypeId(common_code); + EXPECT_EQ(type_id, TypeId(raw_code)); + EXPECT_LT(type_id.unwrap(), TYPE_ID_LIMIT); + EXPECT_EQ(type_id.block_num(), TypeId::block_num(raw_code)); + EXPECT_EQ(type_id.block_num(), TypeId::block_num(common_code)); } }; - for (uint64_t head = 0; head < 16; ++head) { // split into 16 threads - threads[head] = std::thread(test, head); - } - for (auto &t : threads) { - t.join(); + + std::thread threads[16]; + for (uint64_t head = 0; head < 16; ++head) { + threads[head] = std::thread(test, head); // multi-threads verify } + for (auto &t : threads) { t.join(); } } TEST(Group, block_num) { - std::thread threads[16]; - std::string block_num_data; - std::string block_num_str[16]; - - auto test = [&block_num_str](uint64_t head) { + std::string result[16]; + auto test = [&result](uint64_t head) { char buffer[13]; + result[head].reserve(ALL_CASES_SIZE[head]); // vector pre-allocated for (auto &&range: AllCases::fetch()[head]) { auto common_code = CommonCode::unsafe_create(head << 32 | range); - auto tmp = Group::block_num(common_code); + auto tmp = TypeId(common_code).block_num(); EXPECT_LE(tmp.n_1x2 * 2 + tmp.n_2x1 * 2 + tmp.n_1x1, 14); - EXPECT_EQ(tmp, Group::block_num(common_code.to_raw_code())); sprintf(buffer, "%d,%d,%d\n", tmp.n_1x2 + tmp.n_2x1, tmp.n_1x1, tmp.n_2x1); - block_num_str[head] += buffer; + result[head] += buffer; } }; - for (uint64_t head = 0; head < 16; ++head) { // split into 16 threads - threads[head] = std::thread(test, head); - } - for (auto &t : threads) { - t.join(); + std::thread threads[16]; + for (uint64_t head = 0; head < 16; ++head) { + threads[head] = std::thread(test, head); // multi-threads verify } - for (auto &&tmp : block_num_str) { // combine string - block_num_data += tmp; + for (auto &t : threads) { t.join(); } + + std::string block_num_str; + for (auto &&tmp : result) { + block_num_str += tmp; // combine result } - auto block_num_md5 = md5(block_num_data.c_str(), block_num_data.size()); - EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5); + 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 }