Browse Source

test: calculation logic of klotski Group

master
Dnomd343 3 weeks ago
parent
commit
3591d50608
  1. 3
      src/core_test/CMakeLists.txt
  2. 24
      src/core_test/cases/group_union.cc
  3. 76
      src/core_test/cases/helper/block_num.cc
  4. 90
      src/core_test/cases/helper/block_nums.cc
  5. 21
      src/core_test/cases/helper/cases.h
  6. 67
      src/core_test/cases/helper/group_impl.cc

3
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})

24
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;
}

76
src/core_test/cases/helper/block_num.cc

@ -0,0 +1,76 @@
#include <ranges>
#include <vector>
#include <unordered_map>
#include "cases.h"
/// Build the sequence list of all block numbers.
static std::vector<block_num_t> build_block_nums() {
std::vector<std::pair<uint32_t, block_num_t>> 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<std::vector>();
}
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_num_t>& 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<block_num_t, uint32_t> 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();
}

90
src/core_test/cases/helper/block_nums.cc

@ -1,90 +0,0 @@
#include <iostream>
#include "cases.h"
#include <ranges>
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<block_num_t> 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<std::pair<uint32_t, block_num_t>> 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<std::vector>();
}
uint32_t to_type_id(block_num_t block_num) {
auto f = []() {
auto ret = build_block_nums();
std::unordered_map<block_num_t, uint32_t> 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_num_t> block_nums() {
static auto data = build_block_nums();
return data;
}

21
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_num_t> block_nums();
/// Get all block number combinations without dependencies.
const std::vector<block_num_t>& block_nums();
// ------------------------------------------------------------------------------------- //
// TODO: get cases with type id (filter from AllCases)
const std::vector<CommonCode>& group_union_cases(uint32_t type_id);
// TODO: get cases with (type id + group id) (cal data from Group rules)
std::vector<CommonCode> group_cases(uint32_t type_id, uint32_t group_id);
// TODO: always return ref of `std::vector` here

67
src/core_test/cases/helper/group_impl.cc

@ -0,0 +1,67 @@
#include <iostream>
#include "cases.h"
/// Filter cases with different type_id from AllCases.
static std::vector<std::vector<CommonCode>> build_all_cases() {
std::vector<std::vector<CommonCode>> 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<CommonCode> extend_cases(CommonCode seed) {
auto raw_codes = klotski::cases::Group::extend(seed.to_raw_code()); // TODO: using inner build process
std::vector<CommonCode> common_codes {raw_codes.begin(), raw_codes.end()};
std::ranges::sort(common_codes.begin(), common_codes.end());
return common_codes;
}
std::vector<CommonCode> remove_sub(const std::vector<CommonCode> &all, const std::vector<CommonCode> &sub) {
std::vector<CommonCode> 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<std::vector<CommonCode>> split_groups(std::vector<CommonCode> codes) {
std::vector<std::vector<CommonCode>> 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<CommonCode> &g1, const std::vector<CommonCode> &g2) {
return g1.size() > g2.size();
});
return groups;
}
const std::vector<CommonCode>& 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<CommonCode> group_cases(uint32_t type_id, uint32_t group_id) {
auto groups = split_groups(group_union_cases(type_id));
return groups[group_id];
}
Loading…
Cancel
Save