diff --git a/src/klotski_core/all_cases/all_cases.h b/src/klotski_core/all_cases/all_cases.h index 7e7e31d..85a2384 100644 --- a/src/klotski_core/all_cases/all_cases.h +++ b/src/klotski_core/all_cases/all_cases.h @@ -37,7 +37,7 @@ const uint32_t ALL_CASES_SIZE[16] = { const uint32_t ALL_CASES_SIZE_SUM = 29334498; -class AllCases : public BasicRanges { +class AllCases : private BasicRanges { public: /// Trigger the build process. static void build(); @@ -46,7 +46,7 @@ public: static Status status() noexcept; /// Blocking access to constructed data. - static const std::vector (&fetch())[16]; + static const basic_ranges_t (&fetch())[16]; /// Export all possible common codes. static std::vector release(); @@ -54,9 +54,14 @@ public: private: static bool available_; static std::mutex building_; - static std::vector data_[16]; + static basic_ranges_t data_[16]; static void build_data(); + +public: + using BasicRanges::NOT_INIT; + using BasicRanges::BUILDING; + using BasicRanges::AVAILABLE; }; } // namespace klotski diff --git a/src/klotski_core/all_cases/basic_ranges.cc b/src/klotski_core/all_cases/basic_ranges.cc index 9d89dc4..cb6b7d3 100644 --- a/src/klotski_core/all_cases/basic_ranges.cc +++ b/src/klotski_core/all_cases/basic_ranges.cc @@ -10,9 +10,9 @@ using Common::range_reverse; /// static variable initialize std::mutex BasicRanges::building_; bool BasicRanges::available_ = false; -std::vector BasicRanges::data_; +BasicRanges::basic_ranges_t BasicRanges::data_; -const std::vector& BasicRanges::fetch() { +const BasicRanges::basic_ranges_t& BasicRanges::fetch() { if (status() != AVAILABLE) { build(); } @@ -50,7 +50,7 @@ void BasicRanges::build_data() { for (int n = 0; n <= 7; ++n) // number of 1x2 and 2x1 block -> 0 ~ 7 for (int n_2x1 = 0; n_2x1 <= n; ++n_2x1) // number of 2x1 block -> 0 ~ n for (int n_1x1 = 0; n_1x1 <= (14 - n * 2); ++n_1x1) // number of 1x1 block -> 0 ~ (14 - 2n) - generate(generate_t { // generate target ranges + generate(data_, generate_t { // generate target ranges .n1 = 16 - n * 2 - n_1x1, /// space -> 00 .n2 = n - n_2x1, /// 1x2 -> 01 .n3 = n_2x1, /// 2x1 -> 10 @@ -65,7 +65,7 @@ void BasicRanges::build_data() { } /// Generate basic-ranges of specific type. -void BasicRanges::generate(generate_t info) { +void BasicRanges::generate(basic_ranges_t &release, generate_t info) { constexpr auto MASK_01 = (uint32_t)0b01 << 30; constexpr auto MASK_10 = (uint32_t)0b10 << 30; constexpr auto MASK_11 = (uint32_t)0b11 << 30; @@ -88,7 +88,7 @@ void BasicRanges::generate(generate_t info) { while (!cache.empty()) { // queue without element -> work complete auto current = cache.front(); if (!current.nx) { // both n1, n2, n3, n4 -> 0 - data_.emplace_back(current.prefix); // release prefix as basic range + release.emplace_back(current.prefix); // release prefix as basic range cache.pop(); continue; // skip current search } diff --git a/src/klotski_core/all_cases/basic_ranges.h b/src/klotski_core/all_cases/basic_ranges.h index 82eb0d0..7ce4118 100644 --- a/src/klotski_core/all_cases/basic_ranges.h +++ b/src/klotski_core/all_cases/basic_ranges.h @@ -44,6 +44,7 @@ public: BUILDING, AVAILABLE, }; + typedef std::vector basic_ranges_t; /// Trigger the build process, from `NOT_INIT` to `BUILDING`. static void build(); @@ -52,22 +53,26 @@ public: static Status status() noexcept; /// Blocking access to constructed data. - static const std::vector& fetch(); + static const basic_ranges_t& fetch(); private: - struct generate_t { - int n1; // number of `00` - int n2; // number of `01` - int n3; // number of `10` - int n4; // number of `11` - }; - static bool available_; static std::mutex building_; - static std::vector data_; + static basic_ranges_t data_; static void build_data(); - static void generate(generate_t info); + +public: + /// The number of types of blocks. + struct generate_t { + int n1; // number of `00` -> space + int n2; // number of `01` -> 1x2 block + int n3; // number of `10` -> 2x1 block + int n4; // number of `11` -> 1x1 block + }; + + /// Generate all basic-ranges of the specified type. + static void generate(basic_ranges_t &release, generate_t info); }; } // namespace klotski diff --git a/src/klotski_core/ffi/tmain.cc b/src/klotski_core/ffi/tmain.cc index 687a091..cd2dd92 100644 --- a/src/klotski_core/ffi/tmain.cc +++ b/src/klotski_core/ffi/tmain.cc @@ -21,8 +21,55 @@ using klotski::CommonCode; using klotski::Benchmark; +using klotski::BasicRanges; + void tmain() { +// std::vector ok; +// for (auto &&c : AllCases::release()) { +// if (Group::type_id(c) == 123) { +// ok.emplace_back(c); +// } +// } +// std::cout << ok.size() << std::endl; + +// std::cout << Group::all_cases(123).size() << std::endl; +// std::vector err; +// auto ret = Group::all_cases(123); +// std::cout << ret.size() << std::endl; +// err.reserve(ret.size()); +// for (auto &&r : ret) { +// if (Group::type_id(r) != 123) { +// std::cout << r << std::endl; +// } +// err.emplace_back(r); +// } + +// uint32_t sum = 0; +// for (uint32_t type_id = 0; type_id < 204; ++type_id) { +// sum += Group::all_cases(type_id).size(); +// auto tmp = Group::block_num(type_id); +// +// std::vector ranges; +// +// BasicRanges::generate(ranges, BasicRanges::generate_t { // generate target ranges +// .n1 = 16 - tmp.n_1x1 - (tmp.n_1x2 + tmp.n_2x1) * 2, /// space -> 00 +// .n2 = tmp.n_1x2, /// 1x2 -> 01 +// .n3 = tmp.n_2x1, /// 2x1 -> 10 +// .n4 = tmp.n_1x1, /// 1x1 -> 11 +// }); +// sum += ranges.size(); +// } +// std::cout << "sum = " << sum << std::endl; + + + uint32_t sum = 0; + for (uint32_t type_id = 0; type_id < 204; ++type_id) { + sum += Group::all_cases(type_id).size(); + } + std::cout << "sum = " << sum << std::endl; + + // auto r = Group::block_num(123); // std::cout << (int)r.n_1x1 << " " << (int)r.n_1x2 << " " << (int)r.n_2x1 << std::endl; diff --git a/src/klotski_core/group/CMakeLists.txt b/src/klotski_core/group/CMakeLists.txt index 2ba2948..4c97a96 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 group.cc) +add_library(group OBJECT group.cc block_num.cc) diff --git a/src/klotski_core/group/block_num.cc b/src/klotski_core/group/block_num.cc new file mode 100644 index 0000000..140c384 --- /dev/null +++ b/src/klotski_core/group/block_num.cc @@ -0,0 +1,73 @@ +#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) { + 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 + 204, 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.cc b/src/klotski_core/group/group.cc index 1d1e336..3439742 100644 --- a/src/klotski_core/group/group.cc +++ b/src/klotski_core/group/group.cc @@ -2,76 +2,59 @@ #include #include "core.h" #include "group.h" -#include "common.h" #include "absl/container/flat_hash_map.h" - -#include "type_id.h" +#include "common.h" namespace klotski { -using Common::range_reverse; +using klotski::AllCases; +using klotski::BasicRanges; -uint32_t Group::type_id(const RawCode &raw_code) { - return type_id(block_num(raw_code)); -} +using klotski::Common::check_range; +using klotski::Common::range_reverse; -uint32_t Group::type_id(const CommonCode &common_code) { - return type_id(block_num(common_code)); -} +std::vector Group::all_cases(uint32_t type_id) { -uint32_t Group::type_id(const block_num_t &block_num) { - 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 + 204, flag) - TYPE_ID_INDEX; -} + auto tmp = block_num(type_id); -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, - }; -} + std::vector ranges; -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; - } + BasicRanges::generate(ranges, BasicRanges::generate_t { // generate target ranges + .n1 = 16 - tmp.n_1x1 - (tmp.n_1x2 + tmp.n_2x1) * 2, /// space -> 00 + .n2 = tmp.n_1x2, /// 1x2 -> 01 + .n3 = tmp.n_2x1, /// 2x1 -> 10 + .n4 = tmp.n_1x1, /// 1x1 -> 11 + }); + + for (auto &range : ranges) { + range = range_reverse(range); // basic ranges reversed } - 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; +// std::cout << ranges.size() << std::endl; + + std::vector all_cases; + + for (uint64_t head = 0; head < 15; ++head) { // address of 2x2 block + /// head -> 0/1/2 / 4/5/6 / 8/9/10 / 12/13/14 + if ((head & 0b11) == 0b11) { + ++head; // skip invalid address + } + + /// head(4-bit) + basic-range(32-bit) --check--> valid cases + for (auto &&range : ranges) { + + if (!check_range(head, range)) { // case valid + all_cases.emplace_back(CommonCode::create( + + head << 32 | range_reverse(range) + + )); +// range_reverse(range); // release valid cases + } } } - return result; + + return all_cases; } std::vector Group::group_cases(const RawCode &seed) { diff --git a/src/klotski_core/group/group.h b/src/klotski_core/group/group.h index 95cc3eb..6273cb8 100644 --- a/src/klotski_core/group/group.h +++ b/src/klotski_core/group/group.h @@ -9,8 +9,8 @@ namespace klotski { const uint32_t TYPE_ID_LIMIT = 204; class Group { +/// ---------------------------- block statistics ----------------------------- public: - struct block_num_t { uint8_t n_1x1 = 0; uint8_t n_1x2 = 0; @@ -25,12 +25,15 @@ public: static block_num_t block_num(const RawCode &raw_code); static block_num_t block_num(const CommonCode &common_code); +/// ---------------------------- xxxxxxxxxxxxxxxxx ---------------------------- + // static uint32_t max_group_size(uint32_t type_id); static uint32_t max_group_size(const RawCode &raw_code) { return 65535 * 8; }; + static std::vector all_cases(uint32_t type_id); static std::vector group_cases(const RawCode &seed); }; diff --git a/test/group/group.cc b/test/group/group.cc index 909d65f..16adb10 100644 --- a/test/group/group.cc +++ b/test/group/group.cc @@ -12,18 +12,16 @@ using klotski::ALL_CASES_SIZE_SUM; const char BLOCK_NUM_MD5[] = "46a7b3af6d039cbe2f7eaebdd196c6a2"; -TEST(Group, block_num) { +TEST(Group, type_id) { std::thread threads[16]; - std::string block_num_datas[16]; - - auto test = [&block_num_datas](uint64_t head) { - char buffer[13]; - for (auto &&range: AllCases::fetch()[head]) { + auto test = [](uint64_t head) { + for (const auto &range : AllCases::fetch()[head]) { auto common_code = CommonCode::unsafe_create(head << 32 | range); - auto tmp = Group::block_num(common_code); - 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_datas[head] += buffer; + 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)); } }; for (uint64_t head = 0; head < 16; ++head) { // split into 16 threads @@ -32,42 +30,33 @@ TEST(Group, block_num) { for (auto &t : threads) { t.join(); } - - std::string block_num_data; - for (auto &&tmp : block_num_datas) { - block_num_data += tmp; - } - 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_data = new char[ALL_CASES_SIZE_SUM * 7 + 1]; // 'x,?x,x' + '\n' -// auto *current = block_num_data; -// for (auto &&common_code : AllCases::release()) { -// auto tmp = Group::block_num(common_code); -// EXPECT_EQ(tmp, Group::block_num(common_code.to_raw_code())); -// sprintf(current, "%d,%d,%d\n", tmp.n_1x2 + tmp.n_2x1, tmp.n_1x1, tmp.n_2x1); -// current += strlen(current); -// } -// auto block_num_md5 = md5(block_num_data, current - block_num_data); -// EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5); } -TEST(Group, type_id) { +TEST(Group, block_num) { std::thread threads[16]; - auto test = [](uint64_t head) { - for (const auto &range : AllCases::fetch()[head]) { + std::string block_num_data; + std::string block_num_str[16]; + + auto test = [&block_num_str](uint64_t head) { + char buffer[13]; + 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 tmp = Group::block_num(common_code); + 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; } }; + for (uint64_t head = 0; head < 16; ++head) { // split into 16 threads threads[head] = std::thread(test, head); } for (auto &t : threads) { t.join(); } + for (auto &&tmp : block_num_str) { // combine string + block_num_data += tmp; + } + auto block_num_md5 = md5(block_num_data.c_str(), block_num_data.size()); + EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5); }