From 3591d5060836161b74a2a2ca7aae3e8b20ecca23 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sat, 15 Jun 2024 16:58:48 +0800 Subject: [PATCH] test: calculation logic of klotski Group --- src/core_test/CMakeLists.txt | 3 +- src/core_test/cases/group_union.cc | 24 +++++++ src/core_test/cases/helper/block_num.cc | 76 ++++++++++++++++++++ src/core_test/cases/helper/block_nums.cc | 90 ------------------------ src/core_test/cases/helper/cases.h | 21 ++++-- src/core_test/cases/helper/group_impl.cc | 67 ++++++++++++++++++ 6 files changed, 186 insertions(+), 95 deletions(-) create mode 100644 src/core_test/cases/helper/block_num.cc delete mode 100644 src/core_test/cases/helper/block_nums.cc create mode 100644 src/core_test/cases/helper/group_impl.cc diff --git a/src/core_test/CMakeLists.txt b/src/core_test/CMakeLists.txt index 8348ab4..d56b1fd 100644 --- a/src/core_test/CMakeLists.txt +++ b/src/core_test/CMakeLists.txt @@ -25,7 +25,8 @@ set(KLSK_TEST_CASES_SRC cases/basic_ranges.cc cases/all_cases.cc cases/group_union.cc - cases/helper/block_nums.cc + cases/helper/block_num.cc + cases/helper/group_impl.cc ) add_executable(test_klotski_cases ${KLSK_TEST_CASES_SRC}) diff --git a/src/core_test/cases/group_union.cc b/src/core_test/cases/group_union.cc index dee3b2e..bf9526e 100644 --- a/src/core_test/cases/group_union.cc +++ b/src/core_test/cases/group_union.cc @@ -54,3 +54,27 @@ TEST(GroupUnion, demo) { } + +TEST(GroupUnion, cases) { + // auto ret = group_union_cases(0); + // + // EXPECT_EQ(ret, klotski::cases::GroupUnion::unsafe_create(0).cases().codes()); + + // group_union_cases(0); + + for (uint32_t type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) { + auto cases = group_union_cases(type_id); + EXPECT_EQ(cases, klotski::cases::GroupUnion::unsafe_create(type_id).cases().codes()); + } +} + +TEST(GroupUnion, group) { + + for (uint32_t type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) { + auto ret = group_cases(type_id, 0); + } + + // auto ret = group_cases(169, 0); + // std::cout << ret.size() << std::endl; + +} diff --git a/src/core_test/cases/helper/block_num.cc b/src/core_test/cases/helper/block_num.cc new file mode 100644 index 0000000..6bcfecb --- /dev/null +++ b/src/core_test/cases/helper/block_num.cc @@ -0,0 +1,76 @@ +#include +#include +#include + +#include "cases.h" + +/// Build the sequence list of all block numbers. +static std::vector build_block_nums() { + std::vector> nums; + for (int n = 0; n <= 7; ++n) { + for (int n_1x2 = 0; n_1x2 <= n; ++n_1x2) { + if (n == 7 && n_1x2 == 0) { + continue; + } + for (int n_1x1 = 0; n_1x1 <= (14 - n * 2); ++n_1x1) { + auto flag = (n << 7) | ((n - n_1x2) << 4) | n_1x1; + nums.emplace_back(flag, block_num_t { + .n_1x1 = n_1x1, + .n_1x2 = n_1x2, + .n_2x1 = n - n_1x2, + }); + } + } + } + + std::ranges::sort(nums.begin(), nums.end(), [](const auto lhs, const auto rhs) { + return lhs.first < rhs.first; + }); + return std::views::all(nums) | std::views::values | std::ranges::to(); +} + +block_num_t cal_block_num(uint32_t range) { + block_num_t result {}; + for (range = range_reverse(range); 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; + default:; // space + } + } + return result; +} + +const std::vector& block_nums() { + static auto data = build_block_nums(); + return data; +} + +block_num_t to_block_num(const uint32_t type_id) { + if (type_id < block_nums().size()) { + return block_nums()[type_id]; + } + std::abort(); +} + +uint32_t to_type_id(const block_num_t block_num) { + static auto data = [] { + std::unordered_map map; + for (auto i = 0; i < block_nums().size(); ++i) { + map.emplace(block_nums()[i], i); // TODO: using `std::views::enumerate` + } + return map; + }(); + + if (const auto match = data.find(block_num); match != data.end()) { + return match->second; + } + std::abort(); +} diff --git a/src/core_test/cases/helper/block_nums.cc b/src/core_test/cases/helper/block_nums.cc deleted file mode 100644 index 8b0ad5e..0000000 --- a/src/core_test/cases/helper/block_nums.cc +++ /dev/null @@ -1,90 +0,0 @@ -#include - -#include "cases.h" - -#include - -block_num_t cal_block_num(uint32_t range) { - block_num_t result {}; - range = range_reverse(range); - 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; -} - -static std::vector build_block_nums() { - auto cal_type_flag = [](const block_num_t &block_num) { - const auto n_x2x = block_num.n_1x2 + block_num.n_2x1; - return (n_x2x << 7) | (block_num.n_2x1 << 4) | block_num.n_1x1; - }; - - std::vector> nums; - for (int n = 0; n <= 7; ++n) { - for (int n_1x2 = 0; n_1x2 <= n; ++n_1x2) { - if (n == 7 && n_1x2 == 0) { - continue; - } - for (int n_1x1 = 0; n_1x1 <= (14 - n * 2); ++n_1x1) { - auto num = block_num_t { - .n_1x1 = n_1x1, - .n_1x2 = n_1x2, - .n_2x1 = n - n_1x2, - }; - nums.emplace_back(cal_type_flag(num), num); - } - } - } - - std::ranges::sort(nums.begin(), nums.end(), [](const auto val1, const auto val2) { - return val1.first < val2.first; - }); - - return std::views::all(nums) | std::views::transform([](const auto val) { - return val.second; - }) | std::ranges::to(); -} - -uint32_t to_type_id(block_num_t block_num) { - auto f = []() { - auto ret = build_block_nums(); - - std::unordered_map kk; - - for (auto i = 0; i < ret.size(); ++i) { - kk.emplace(ret[i], i); - } - - return kk; - - }; - - static auto data = f(); - - return data[block_num]; - - // TODO: handle invalid params -} - -block_num_t to_block_num(uint32_t type_id) { - static auto data = build_block_nums(); - - // TODO: handle invalid params - - return data[type_id]; -} - -std::vector block_nums() { - static auto data = build_block_nums(); - return data; -} diff --git a/src/core_test/cases/helper/cases.h b/src/core_test/cases/helper/cases.h index 74040a2..d1ae6fd 100644 --- a/src/core_test/cases/helper/cases.h +++ b/src/core_test/cases/helper/cases.h @@ -46,9 +46,9 @@ constexpr auto Heads = std::to_array({ /// The number of blocks in one klotski layout. struct block_num_t { - int n_1x1; - int n_1x2; - int n_2x1; + int n_1x1; // 4-bit + int n_1x2; // 3-bit + int n_2x1; // 3-bit }; template <> @@ -73,4 +73,17 @@ uint32_t to_type_id(block_num_t block_num); /// Calculate the block number value from type id. block_num_t to_block_num(uint32_t type_id); -std::vector block_nums(); +/// Get all block number combinations without dependencies. +const std::vector& block_nums(); + +// ------------------------------------------------------------------------------------- // + +// TODO: get cases with type id (filter from AllCases) + +const std::vector& group_union_cases(uint32_t type_id); + +// TODO: get cases with (type id + group id) (cal data from Group rules) + +std::vector group_cases(uint32_t type_id, uint32_t group_id); + +// TODO: always return ref of `std::vector` here diff --git a/src/core_test/cases/helper/group_impl.cc b/src/core_test/cases/helper/group_impl.cc new file mode 100644 index 0000000..2220cfc --- /dev/null +++ b/src/core_test/cases/helper/group_impl.cc @@ -0,0 +1,67 @@ +#include + +#include "cases.h" + +/// Filter cases with different type_id from AllCases. +static std::vector> build_all_cases() { + std::vector> codes; + for (auto code : AllCases::instance().fetch().codes()) { + const auto type_id = to_type_id(cal_block_num(code.unwrap())); + if (type_id >= codes.size()) { + codes.resize(type_id + 1); + } + codes[type_id].emplace_back(code); + } + return codes; +} + +std::vector extend_cases(CommonCode seed) { + auto raw_codes = klotski::cases::Group::extend(seed.to_raw_code()); // TODO: using inner build process + std::vector common_codes {raw_codes.begin(), raw_codes.end()}; + std::ranges::sort(common_codes.begin(), common_codes.end()); + return common_codes; +} + +std::vector remove_sub(const std::vector &all, const std::vector &sub) { + std::vector tmp; + tmp.reserve(all.size() - sub.size()); + std::ranges::set_difference(all.begin(), all.end(), sub.begin(), sub.end(), std::back_inserter(tmp)); + return tmp; +} + +static std::vector> split_groups(std::vector codes) { + + std::vector> groups; + + while (!codes.empty()) { + auto sub = extend_cases(codes[0]); + + groups.emplace_back(sub); + codes = remove_sub(codes, sub); + } + + std::ranges::stable_sort(groups.begin(), groups.end(), [](const std::vector &g1, const std::vector &g2) { + return g1.size() > g2.size(); + }); + + return groups; + +} + +const std::vector& group_union_cases(const uint32_t type_id) { + static auto data = build_all_cases(); + if (type_id < data.size()) { + return data[type_id]; + } + std::abort(); +} + +// TODO: maybe using multi-threads + +std::vector group_cases(uint32_t type_id, uint32_t group_id) { + + auto groups = split_groups(group_union_cases(type_id)); + + return groups[group_id]; + +}