Compare commits

...

13 Commits

  1. 8
      src/core/CMakeLists.txt
  2. 69
      src/core/benchmark/group.cc
  3. 38
      src/core/benchmark/ranges.cc
  4. 59
      src/core/group/group.h
  5. 16
      src/core/group/internal/constant/group.h
  6. 62
      src/core/group/internal/constant/group_union.h
  7. 0
      src/core/group/internal/constant/seeds.inc
  8. 1
      src/core/group/internal/constant/sizes.inc
  9. 76
      src/core/group/internal/group.cc
  10. 34
      src/core/group/internal/group.inl
  11. 161
      src/core/group/internal/group_union.cc
  12. 29
      src/core/group/internal/group_union.inl
  13. 38
      src/core/main.cc
  14. 78
      src/core/ranges/internal/derive.cc
  15. 65
      src/core/ranges/internal/ranges.cc
  16. 40
      src/core/ranges/internal/spawn.cc
  17. 30
      src/core/ranges/ranges.h
  18. 524
      src/core/ranges/statistics.py
  19. 5
      src/core_test/CMakeLists.txt
  20. 65
      src/core_test/cases/all_cases.cc
  21. 28
      src/core_test/cases/basic_ranges.cc
  22. 8
      src/core_test/cases/cases_helper.h
  23. 149
      src/core_test/cases/group_union.cc
  24. 90
      src/core_test/cases/helper/block_nums.cc
  25. 76
      src/core_test/cases/helper/cases.h
  26. 111
      src/core_test/cases/ranges.cc
  27. 35
      src/core_test/cases/ranges_union.cc

8
src/core/CMakeLists.txt

@ -6,7 +6,6 @@ set(CMAKE_CXX_STANDARD 23)
set(KLOTSKI_CORE_SRC
all_cases/internal/basic_ranges.cc
all_cases/internal/all_cases.cc
# all_cases/internal/derive.cc
common_code/internal/common_code.cc
common_code/internal/serialize.cc
@ -26,6 +25,7 @@ set(KLOTSKI_CORE_SRC
group/internal/group_union.cc
group/internal/group.cc
ranges/internal/spawn.cc
ranges/internal/ranges.cc
ranges/internal/derive.cc
)
@ -45,9 +45,13 @@ target_compile_options(codec_benchmark PRIVATE -fno-rtti -fno-exceptions)
target_link_libraries(codec_benchmark PRIVATE klotski::core benchmark::benchmark_main)
add_executable(group_benchmark benchmark/group.cc)
target_compile_options(group_benchmark PRIVATE -fno-rtti -fno-exceptions)
target_compile_options(group_benchmark PRIVATE -fno-rtti -fno-exceptions -fno-access-control)
target_link_libraries(group_benchmark PRIVATE klotski::core benchmark::benchmark_main)
add_executable(bm_all_cases benchmark/all_cases.cc)
target_compile_options(bm_all_cases PRIVATE -fno-rtti -fno-exceptions -fno-access-control)
target_link_libraries(bm_all_cases PRIVATE klotski::core benchmark::benchmark_main bs::thread_pool)
add_executable(bm_ranges benchmark/ranges.cc)
target_compile_options(bm_ranges PRIVATE -fno-rtti -fno-exceptions)
target_link_libraries(bm_ranges PRIVATE klotski::core benchmark::benchmark_main)

69
src/core/benchmark/group.cc

@ -2,14 +2,14 @@
#include <benchmark/benchmark.h>
#define private public
// #define private public
#include "group/group.h"
#include <ranges/ranges.h>
#include "../../../third_party/thread-pool/include/BS_thread_pool.hpp"
#include "all_cases/all_cases.h"
#undef private
// #undef private
using klotski::cases::AllCases;
@ -25,7 +25,7 @@ static std::vector<uint64_t> all_common_codes() {
return codes;
}
std::vector<uint64_t> common_code_samples(uint64_t num) {
std::vector<klotski::codec::CommonCode> common_code_samples(uint64_t num) {
static auto codes = all_common_codes();
@ -34,7 +34,7 @@ std::vector<uint64_t> common_code_samples(uint64_t num) {
// uint64_t offset = 0;
uint64_t offset = part_size / 2;
std::vector<uint64_t> result;
std::vector<klotski::codec::CommonCode> result;
for (uint64_t i = 0; i < num; ++i) {
uint64_t index = i * part_size + offset;
@ -43,22 +43,24 @@ std::vector<uint64_t> common_code_samples(uint64_t num) {
// uint64_t kk[] {343, 666, 114514, 35324, 123454, 76453, 93411};
// uint64_t index = kk[i % 7];
result.emplace_back(codes[index]);
result.emplace_back(klotski::codec::CommonCode::unsafe_create(codes[index]));
}
return result;
}
std::vector<uint64_t> raw_code_samples(uint64_t num) {
std::vector<klotski::codec::RawCode> raw_code_samples(uint64_t num) {
auto codes = common_code_samples(num);
for (auto &code : codes) {
code = klotski::codec::CommonCode::unsafe_create(code).to_raw_code().unwrap();
std::vector<klotski::codec::RawCode> raw_codes;
raw_codes.reserve(codes.size());
for (auto code : codes) {
raw_codes.emplace_back(code.to_raw_code());
}
return codes;
return raw_codes;
}
static void CommonCodeToTypeId(benchmark::State &state) {
@ -69,7 +71,7 @@ static void CommonCodeToTypeId(benchmark::State &state) {
for (auto code : samples) {
// volatile auto ret = klotski::cases::common_code_to_type_id(code);
volatile auto ret = klotski::cases::GroupUnion::type_id(code);
}
}
@ -82,16 +84,16 @@ static void RawCodeToTypeId(benchmark::State &state) {
auto samples = raw_code_samples(state.range(0));
// for (auto code : samples) {
// if (klotski::codec::RawCode::check(code) == false) {
// std::cout << "error" << std::endl;
// }
// }
for (auto code : samples) {
if (klotski::codec::RawCode::check(code.code_) == false) {
std::cout << "error" << std::endl;
}
}
for (auto _ : state) {
for (auto code : samples) {
// volatile auto ret = klotski::cases::raw_code_to_type_id(code);
volatile auto ret = klotski::cases::GroupUnion::type_id(code);
}
}
@ -102,11 +104,11 @@ static void RawCodeToTypeId(benchmark::State &state) {
static void GroupExtend(benchmark::State &state) {
auto src = klotski::codec::RawCode::from_common_code(0x1A9BF0C00)->unwrap();
auto src = klotski::codec::RawCode::from_common_code(0x1A9BF0C00).value();
for (auto _ : state) {
// volatile auto ret = klotski::cases::group_extend_from_seed(src);
volatile auto ret = klotski::cases::Group::extend(src, 0);
// std::cout << ret.size() << std::endl;
}
@ -222,22 +224,43 @@ static void RangesDerive(benchmark::State &state) {
auto group_union = klotski::cases::GroupUnion::unsafe_create(169);
std::vector<klotski::cases::GroupUnion> unions;
unions.reserve(klotski::cases::TYPE_ID_LIMIT);
for (int type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) {
unions.emplace_back(klotski::cases::GroupUnion::create(type_id).value());
}
for (auto _ : state) {
// results.clear();
// results.reserve(klotski::cases::ALL_CASES_NUM[5]);
volatile auto tmp = group_union.cases();
// volatile auto tmp = group_union.cases();
for (auto g_union : unions) {
volatile auto tmp = g_union.cases();
}
}
// std::cout << results.size() << " vs " << klotski::cases::ALL_CASES_NUM[5] << std::endl;
}
static void SpawnGroups(benchmark::State &state) {
volatile auto val = 169;
auto group_union = klotski::cases::GroupUnion::create(val).value();
for (auto _ : state) {
volatile auto kk = group_union.groups();
}
}
// BENCHMARK(CommonCodeToTypeId)->Arg(8)->Arg(64)->Arg(256);
// BENCHMARK(RawCodeToTypeId)->Arg(8)->Arg(64)->Arg(256);
// BENCHMARK(GroupExtend)->Unit(benchmark::kMillisecond);
BENCHMARK(GroupExtend)->Unit(benchmark::kMillisecond);
// BENCHMARK(FilterFromAllCases)->Unit(benchmark::kMillisecond);
@ -247,6 +270,8 @@ static void RangesDerive(benchmark::State &state) {
// BENCHMARK(OriginAllCases)->Unit(benchmark::kMillisecond);
BENCHMARK(RangesDerive)->Unit(benchmark::kMillisecond);
// BENCHMARK(RangesDerive)->Unit(benchmark::kMillisecond);
// BENCHMARK(SpawnGroups);
BENCHMARK_MAIN();

38
src/core/benchmark/ranges.cc

@ -0,0 +1,38 @@
#include <benchmark/benchmark.h>
#include "group/group.h"
#include "ranges/ranges.h"
#include "all_cases/all_cases.h"
using klotski::cases::AllCases;
static void SpawnRanges(benchmark::State &state) {
// constexpr auto nums = target_nums();
for (auto _ : state) {
klotski::cases::Ranges kk {};
kk.reserve(7311921);
// for (uint32_t type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) {
for (auto [n, n_2x1, n_1x1] : klotski::cases::BLOCK_NUM) {
kk.spawn(n, n_2x1, n_1x1);
}
// }
}
}
static void RangesUnionExport(benchmark::State &state) {
auto &all_cases = AllCases::instance().fetch();
for (auto _ : state) {
auto codes = all_cases.codes();
benchmark::DoNotOptimize(codes.size());
}
}
BENCHMARK(SpawnRanges)->Unit(benchmark::kMillisecond);
// BENCHMARK(RangesUnionExport)->Unit(benchmark::kMillisecond);
BENCHMARK_MAIN();

59
src/core/group/group.h

@ -27,7 +27,7 @@
/// (10-bit) | (n_1x2 + n_2x1) | (n_2x1) | (n_1x1) |
/// | (0 ~ 7) | (0 ~ 7) | (0 ~ 14) |
///
/// flag => ((n_1x2 + n_2x1) << 7) | (n_2x1 << 3) | (n_1x1)
/// flag => ((n_1x2 + n_2x1) << 7) | (n_2x1 << 4) | (n_1x1)
///
/// Using the table lookup method, the `type_id` of any case can be obtained
/// within O(1), which is encapsulated in `GroupUnion`.
@ -75,10 +75,8 @@ namespace klotski::cases {
constexpr uint32_t TYPE_ID_LIMIT = 203;
constexpr uint32_t ALL_GROUP_NUM = 25422;
// uint32_t common_code_to_type_id(uint64_t common_code);
// uint32_t raw_code_to_type_id(uint64_t raw_code);
// std::vector<uint64_t> group_extend_from_seed(uint64_t raw_code);
typedef std::vector<codec::RawCode> RawCodes;
typedef std::vector<codec::CommonCode> CommonCodes;
class Group;
@ -90,28 +88,31 @@ public:
// ------------------------------------------------------------------------------------- //
/// Get the original type id.
[[nodiscard]] uint32_t unwrap() const;
[[nodiscard]] constexpr uint32_t unwrap() const;
/// Create GroupUnion without any check.
static GroupUnion unsafe_create(uint32_t type_id);
static constexpr GroupUnion unsafe_create(uint32_t type_id);
/// Create GroupUnion with validity check.
static std::optional<GroupUnion> create(uint32_t type_id);
static constexpr std::optional<GroupUnion> create(uint32_t type_id);
// ------------------------------------------------------------------------------------- //
/// Get the number of cases contained.
[[nodiscard]] uint32_t size() const;
[[nodiscard]] constexpr uint32_t size() const;
/// Get the number of groups contained.
[[nodiscard]] uint32_t group_num() const;
[[nodiscard]] constexpr uint32_t group_num() const;
/// Get the upper limit of the group size.
[[nodiscard]] uint32_t max_group_size() const;
[[nodiscard]] constexpr uint32_t max_group_size() const;
// ------------------------------------------------------------------------------------- //
/// Get all cases under the current type id.
[[nodiscard]] RangesUnion cases() const;
/// Get all group instances under the current type id.
/// Get all groups under the current type id.
[[nodiscard]] std::vector<Group> groups() const;
/// Get the group instance with the specified group id.
@ -131,46 +132,63 @@ public:
// ------------------------------------------------------------------------------------- //
private:
uint32_t type_id_ {};
uint32_t type_id_;
// ------------------------------------------------------------------------------------- //
/// Get the type id of RawCode.
static uint32_t type_id(codec::RawCode raw_code);
static KLSK_INLINE uint32_t type_id(codec::RawCode raw_code);
/// Get the type id of CommonCode.
static uint32_t type_id(codec::CommonCode common_code);
static KLSK_INLINE uint32_t type_id(codec::CommonCode common_code);
// ------------------------------------------------------------------------------------- //
};
typedef std::vector<codec::RawCode> RawCodes;
typedef std::vector<codec::CommonCode> CommonCodes;
class Group {
public:
Group() = delete;
// ------------------------------------------------------------------------------------- //
/// Get the original type id.
[[nodiscard]] uint32_t type_id() const;
/// Get the original group id.
[[nodiscard]] uint32_t group_id() const;
/// Create Group without any check.
static Group unsafe_create(uint32_t type_id, uint32_t group_id);
/// Create Group with validity check.
static std::optional<Group> create(uint32_t type_id, uint32_t group_id);
// ------------------------------------------------------------------------------------- //
/// Get the number of cases contained.
[[nodiscard]] uint32_t size() const;
/// Get all cases under the current group.
[[nodiscard]] RangesUnion cases() const;
// ------------------------------------------------------------------------------------- //
static Group from_raw_code(codec::RawCode raw_code);
static Group from_short_code(codec::ShortCode short_code);
static Group from_common_code(codec::CommonCode common_code);
// private:
// ------------------------------------------------------------------------------------- //
private:
uint32_t type_id_;
uint32_t group_id_;
[[nodiscard]] uint32_t flat_id() const;
static std::vector<uint64_t> extend(codec::RawCode raw_code);
public:
static std::vector<codec::RawCode> extend(codec::RawCode raw_code, uint32_t reserve = 0);
};
class GroupCase {
@ -204,3 +222,4 @@ private:
} // namespace klotski::cases
#include "internal/group_union.inl"
#include "internal/group.inl"

16
src/core/group/internal/constant/group.h

@ -0,0 +1,16 @@
#pragma once
#include <array>
namespace klotski::cases {
// TODO: should we try to compress it?
constexpr auto GROUP_SIZE = std::to_array<uint32_t>({
#include "sizes.inc"
});
constexpr auto GROUP_SEED = std::to_array<uint64_t>({
#include "seeds.inc"
});
} // namespace klotski::cases

62
src/core/group/internal/constant/group_union.h

@ -2,6 +2,8 @@
#include <array>
#include "utils/utility.h"
namespace klotski::cases {
/// The number of groups contained in GroupUnion.
@ -21,6 +23,8 @@ constexpr auto GROUP_NUM = std::to_array<uint16_t>({
214, 6 , 18 , 54 , 2 , 44 , 40 , 124 , 84 , 70 , 18 ,
});
constexpr auto GROUP_OFFSET = to_offset<uint16_t, 203>(GROUP_NUM, 0);
/// The maximum Group size in each GroupUnion.
constexpr auto MAX_GROUP_SIZE = std::to_array<uint32_t>({
12 , 192 , 1440 , 6720 , 21840 , 52416 , 96096 , 137280, 154440, 137280, 96096 , 52416 ,
@ -67,18 +71,18 @@ constexpr auto GROUP_UNION_SIZE = std::to_array<uint32_t>({
constexpr auto BLOCK_NUM = std::to_array<std::tuple<uint8_t, uint8_t, uint8_t>>({
{0, 0, 0 }, {0, 0, 1 }, {0, 0, 2 }, {0, 0, 3 }, {0, 0, 4 }, {0, 0, 5 }, {0, 0, 6 }, {0, 0, 7 },
{0, 0, 8 }, {0, 0, 9 }, {0, 0, 10}, {0, 0, 11}, {0, 0, 12}, {0, 0, 13}, {0, 0, 14}, {1, 0, 0 },
{1, 0, 1 }, {1, 0, 2 }, {1, 0, 3 }, {1, 0, 4 }, {1, 0, 5 }, {1, 0, 6 }, {1, 0, 7 }, {1, 1, 0 },
{1, 0, 8 }, {1, 1, 8 }, {1, 0, 9 }, {1, 1, 1 }, {1, 1, 9 }, {1, 0, 10}, {1, 1, 10}, {1, 1, 2 },
{1, 1, 11}, {1, 1, 3 }, {1, 0, 11}, {1, 1, 12}, {1, 0, 12}, {1, 1, 4 }, {1, 1, 5 }, {1, 1, 6 },
{1, 1, 7 }, {2, 0, 0 }, {2, 0, 1 }, {2, 0, 2 }, {2, 0, 3 }, {2, 0, 4 }, {2, 0, 5 }, {2, 0, 6 },
{2, 0, 7 }, {2, 0, 8 }, {2, 1, 8 }, {2, 1, 0 }, {2, 0, 9 }, {2, 1, 1 }, {2, 1, 9 }, {2, 1, 2 },
{2, 1, 10}, {2, 0, 10}, {2, 1, 3 }, {2, 1, 4 }, {2, 1, 5 }, {2, 1, 6 }, {2, 1, 7 }, {2, 2, 0 },
{1, 0, 1 }, {1, 0, 2 }, {1, 0, 3 }, {1, 0, 4 }, {1, 0, 5 }, {1, 0, 6 }, {1, 0, 7 }, {1, 0, 8 },
{1, 0, 9 }, {1, 0, 10}, {1, 0, 11}, {1, 0, 12}, {1, 1, 0 }, {1, 1, 1 }, {1, 1, 2 }, {1, 1, 3 },
{1, 1, 4 }, {1, 1, 5 }, {1, 1, 6 }, {1, 1, 7 }, {1, 1, 8 }, {1, 1, 9 }, {1, 1, 10}, {1, 1, 11},
{1, 1, 12}, {2, 0, 0 }, {2, 0, 1 }, {2, 0, 2 }, {2, 0, 3 }, {2, 0, 4 }, {2, 0, 5 }, {2, 0, 6 },
{2, 0, 7 }, {2, 0, 8 }, {2, 0, 9 }, {2, 0, 10}, {2, 1, 0 }, {2, 1, 1 }, {2, 1, 2 }, {2, 1, 3 },
{2, 1, 4 }, {2, 1, 5 }, {2, 1, 6 }, {2, 1, 7 }, {2, 1, 8 }, {2, 1, 9 }, {2, 1, 10}, {2, 2, 0 },
{2, 2, 1 }, {2, 2, 2 }, {2, 2, 3 }, {2, 2, 4 }, {2, 2, 5 }, {2, 2, 6 }, {2, 2, 7 }, {2, 2, 8 },
{2, 2, 9 }, {2, 2, 10}, {3, 0, 0 }, {3, 0, 1 }, {3, 0, 2 }, {3, 0, 3 }, {3, 0, 4 }, {3, 0, 5 },
{3, 0, 6 }, {3, 0, 7 }, {3, 1, 8 }, {3, 0, 8 }, {3, 1, 0 }, {3, 1, 1 }, {3, 1, 2 }, {3, 1, 3 },
{3, 1, 4 }, {3, 1, 5 }, {3, 1, 6 }, {3, 1, 7 }, {3, 2, 0 }, {3, 2, 1 }, {3, 2, 2 }, {3, 2, 3 },
{3, 2, 4 }, {3, 2, 5 }, {3, 2, 6 }, {3, 2, 7 }, {3, 3, 0 }, {3, 3, 8 }, {3, 2, 8 }, {3, 3, 1 },
{3, 3, 2 }, {3, 3, 3 }, {3, 3, 4 }, {3, 3, 5 }, {3, 3, 6 }, {3, 3, 7 }, {4, 0, 0 }, {4, 0, 1 },
{3, 0, 6 }, {3, 0, 7 }, {3, 0, 8 }, {3, 1, 0 }, {3, 1, 1 }, {3, 1, 2 }, {3, 1, 3 }, {3, 1, 4 },
{3, 1, 5 }, {3, 1, 6 }, {3, 1, 7 }, {3, 1, 8 }, {3, 2, 0 }, {3, 2, 1 }, {3, 2, 2 }, {3, 2, 3 },
{3, 2, 4 }, {3, 2, 5 }, {3, 2, 6 }, {3, 2, 7 }, {3, 2, 8 }, {3, 3, 0 }, {3, 3, 1 }, {3, 3, 2 },
{3, 3, 3 }, {3, 3, 4 }, {3, 3, 5 }, {3, 3, 6 }, {3, 3, 7 }, {3, 3, 8 }, {4, 0, 0 }, {4, 0, 1 },
{4, 0, 2 }, {4, 0, 3 }, {4, 0, 4 }, {4, 0, 5 }, {4, 0, 6 }, {4, 1, 0 }, {4, 1, 1 }, {4, 1, 2 },
{4, 1, 3 }, {4, 1, 4 }, {4, 1, 5 }, {4, 1, 6 }, {4, 2, 0 }, {4, 2, 1 }, {4, 2, 2 }, {4, 2, 3 },
{4, 2, 4 }, {4, 2, 5 }, {4, 2, 6 }, {4, 3, 0 }, {4, 3, 1 }, {4, 3, 2 }, {4, 3, 3 }, {4, 3, 4 },
@ -102,36 +106,36 @@ constexpr auto GROUP_UNION_CASES_NUM = std::to_array<std::tuple<uint32_t, uint32
{1820 , 1820 , 1820 , 1820 }, {560 , 560 , 560 , 560 }, {120 , 120 , 120 , 120 },
{11 , 9 , 11 , 9 }, {154 , 126 , 154 , 126 }, {1001 , 819 , 1001 , 819 },
{4004 , 3276 , 4004 , 3276 }, {11011, 9009 , 11011, 9009 }, {22022, 18018, 22022, 18018},
{33033, 27027, 33033, 27027}, {37752, 30888, 37752, 30888}, {12 , 12 , 10 , 10 },
{33033, 27027, 33033, 27027}, {36036, 36036, 30030, 30030}, {22022, 18018, 22022, 18018},
{168 , 168 , 140 , 140 }, {24024, 24024, 20020, 20020}, {11011, 9009 , 11011, 9009 },
{12012, 12012, 10010, 10010}, {1092 , 1092 , 910 , 910 }, {4368 , 4368 , 3640 , 3640 },
{4368 , 4368 , 3640 , 3640 }, {4004 , 3276 , 4004 , 3276 }, {1092 , 1092 , 910 , 910 },
{1001 , 819 , 1001 , 819 }, {12012, 12012, 10010, 10010}, {24024, 24024, 20020, 20020},
{36036, 36036, 30030, 30030}, {41184, 41184, 34320, 34320}, {49 , 30 , 49 , 30 },
{33033, 27027, 33033, 27027}, {37752, 30888, 37752, 30888}, {33033, 27027, 33033, 27027},
{22022, 18018, 22022, 18018}, {11011, 9009 , 11011, 9009 }, {4004 , 3276 , 4004 , 3276 },
{1001 , 819 , 1001 , 819 }, {12 , 12 , 10 , 10 }, {168 , 168 , 140 , 140 },
{1092 , 1092 , 910 , 910 }, {4368 , 4368 , 3640 , 3640 }, {12012, 12012, 10010, 10010},
{24024, 24024, 20020, 20020}, {36036, 36036, 30030, 30030}, {41184, 41184, 34320, 34320},
{36036, 36036, 30030, 30030}, {24024, 24024, 20020, 20020}, {12012, 12012, 10010, 10010},
{4368 , 4368 , 3640 , 3640 }, {1092 , 1092 , 910 , 910 }, {49 , 30 , 49 , 30 },
{588 , 360 , 588 , 360 }, {3234 , 1980 , 3234 , 1980 }, {10780, 6600 , 10780, 6600 },
{24255, 14850, 24255, 14850}, {38808, 23760, 38808, 23760}, {45276, 27720, 45276, 27720},
{38808, 23760, 38808, 23760}, {24255, 14850, 24255, 14850}, {49005, 40590, 41580, 36630},
{99 , 82 , 84 , 74 }, {10780, 6600 , 10780, 6600 }, {1188 , 984 , 1008 , 888 },
{21780, 18040, 18480, 16280}, {6534 , 5412 , 5544 , 4884 }, {6534 , 5412 , 5544 , 4884 },
{3234 , 1980 , 3234 , 1980 }, {21780, 18040, 18480, 16280}, {49005, 40590, 41580, 36630},
{38808, 23760, 38808, 23760}, {24255, 14850, 24255, 14850}, {10780, 6600 , 10780, 6600 },
{3234 , 1980 , 3234 , 1980 }, {99 , 82 , 84 , 74 }, {1188 , 984 , 1008 , 888 },
{6534 , 5412 , 5544 , 4884 }, {21780, 18040, 18480, 16280}, {49005, 40590, 41580, 36630},
{78408, 64944, 66528, 58608}, {91476, 75768, 77616, 68376}, {78408, 64944, 66528, 58608},
{49005, 40590, 41580, 36630}, {21780, 18040, 18480, 16280}, {6534 , 5412 , 5544 , 4884 },
{58 , 58 , 39 , 39 }, {696 , 696 , 468 , 468 }, {3828 , 3828 , 2574 , 2574 },
{12760, 12760, 8580 , 8580 }, {28710, 28710, 19305, 19305}, {45936, 45936, 30888, 30888},
{53592, 53592, 36036, 36036}, {45936, 45936, 30888, 30888}, {28710, 28710, 19305, 19305},
{12760, 12760, 8580 , 8580 }, {3828 , 3828 , 2574 , 2574 }, {114 , 45 , 114 , 45 },
{1140 , 450 , 1140 , 450 }, {5130 , 2025 , 5130 , 2025 }, {13680, 5400 , 13680, 5400 },
{23940, 9450 , 23940, 9450 }, {28728, 11340, 28728, 11340}, {23940, 9450 , 23940, 9450 },
{13680, 5400 , 13680, 5400 }, {14355, 9270 , 12240, 9090 }, {5130 , 2025 , 5130 , 2025 },
{319 , 206 , 272 , 202 }, {3190 , 2060 , 2720 , 2020 }, {14355, 9270 , 12240, 9090 },
{38280, 24720, 32640, 24240}, {66990, 43260, 57120, 42420}, {80388, 51912, 68544, 50904},
{66990, 43260, 57120, 42420}, {38280, 24720, 32640, 24240}, {346 , 288 , 243 , 225 },
{13680, 5400 , 13680, 5400 }, {5130 , 2025 , 5130 , 2025 }, {319 , 206 , 272 , 202 },
{3190 , 2060 , 2720 , 2020 }, {14355, 9270 , 12240, 9090 }, {38280, 24720, 32640, 24240},
{66990, 43260, 57120, 42420}, {80388, 51912, 68544, 50904}, {66990, 43260, 57120, 42420},
{38280, 24720, 32640, 24240}, {14355, 9270 , 12240, 9090 }, {346 , 288 , 243 , 225 },
{3460 , 2880 , 2430 , 2250 }, {15570, 12960, 10935, 10125}, {41520, 34560, 29160, 27000},
{72660, 60480, 51030, 47250}, {87192, 72576, 61236, 56700}, {72660, 60480, 51030, 47250},
{41520, 34560, 29160, 27000}, {144 , 144 , 76 , 76 }, {6480 , 6480 , 3420 , 3420 },
{15570, 12960, 10935, 10125}, {1440 , 1440 , 760 , 760 }, {6480 , 6480 , 3420 , 3420 },
{17280, 17280, 9120 , 9120 }, {30240, 30240, 15960, 15960}, {36288, 36288, 19152, 19152},
{30240, 30240, 15960, 15960}, {17280, 17280, 9120 , 9120 }, {150 , 30 , 150 , 30 },
{41520, 34560, 29160, 27000}, {15570, 12960, 10935, 10125}, {144 , 144 , 76 , 76 },
{1440 , 1440 , 760 , 760 }, {6480 , 6480 , 3420 , 3420 }, {17280, 17280, 9120 , 9120 },
{30240, 30240, 15960, 15960}, {36288, 36288, 19152, 19152}, {30240, 30240, 15960, 15960},
{17280, 17280, 9120 , 9120 }, {6480 , 6480 , 3420 , 3420 }, {150 , 30 , 150 , 30 },
{1200 , 240 , 1200 , 240 }, {4200 , 840 , 4200 , 840 }, {8400 , 1680 , 8400 , 1680 },
{10500, 2100 , 10500, 2100 }, {8400 , 1680 , 8400 , 1680 }, {4200 , 840 , 4200 , 840 },
{507 , 230 , 426 , 244 }, {4056 , 1840 , 3408 , 1952 }, {14196, 6440 , 11928, 6832 },

0
src/core/group/internal/seeds.inc → src/core/group/internal/constant/seeds.inc

1
src/core/group/internal/constant/sizes.inc

File diff suppressed because one or more lines are too long

76
src/core/group/internal/group.cc

@ -1,45 +1,59 @@
#include <absl/container/flat_hash_map.h>
#include "core/core.h"
#include "group/group.h"
#include <queue>
using klotski::core::Core;
using klotski::cases::Group;
using klotski::codec::RawCode;
using klotski::codec::CommonCode;
using klotski::cases::RangesUnion;
#include <absl/container/btree_set.h>
#include <absl/container/flat_hash_map.h>
#include <absl/container/node_hash_map.h>
std::vector<RawCode> Group::extend(RawCode raw_code, uint32_t reserve) {
std::vector<RawCode> codes;
absl::flat_hash_map<uint64_t, uint64_t> cases; // <code, mask>
reserve = reserve ? reserve : GroupUnion::from_raw_code(raw_code).max_group_size();
codes.reserve(reserve);
cases.reserve(reserve);
auto core = Core([&codes, &cases](uint64_t code, uint64_t mask) {
if (const auto match = cases.find(code); match != cases.end()) {
match->second |= mask; // update mask
return;
}
cases.emplace(code, mask);
codes.emplace_back(RawCode::unsafe_create(code)); // new case
});
uint64_t offset = 0;
codes.emplace_back(raw_code);
cases.emplace(raw_code, 0); // without mask
while (offset != codes.size()) {
auto curr = codes[offset++].unwrap();
core.next_cases(curr, cases.find(curr)->second);
}
return codes;
}
#include <core/core.h>
RangesUnion Group::cases() const {
auto seed = CommonCode::unsafe_create(GROUP_SEED[flat_id()]);
std::vector<uint64_t> klotski::cases::Group::extend(codec::RawCode raw_code) {
// std::cout << seed << std::endl;
auto max_size = GroupUnion::from_raw_code(raw_code).max_group_size();
// auto max_size = GroupUnion::create(raw_code_to_type_id(raw_code))->max_group_size();
auto codes = extend(seed.to_raw_code(), size());
uint64_t offset = 0;
std::vector<uint64_t> results;
results.reserve(max_size);
results.emplace_back(raw_code);
// std::cout << codes.size() << std::endl;
absl::flat_hash_map<uint64_t, uint64_t> cases; // <code, mask>
cases.reserve(max_size);
cases.emplace(raw_code, 0); // without mask
// TODO: how to reserve
auto core = klotski::core::Core(
[&results, &cases](auto code, auto mask) { // callback function
auto current = cases.find(code);
if (current != cases.end()) {
current->second |= mask; // update mask
return;
}
cases.emplace(code, mask);
results.emplace_back(code);
}
);
RangesUnion data;
while (offset != results.size()) {
auto tmp = results[offset];
core.next_cases(tmp, cases.find(tmp)->second);
++offset;
for (auto raw_code : codes) {
auto common_code = raw_code.to_common_code().unwrap();
data[common_code >> 32].emplace_back(static_cast<uint32_t>(common_code));
}
return results;
// TODO: do sort process
return data;
}

34
src/core/group/internal/group.inl

@ -0,0 +1,34 @@
#pragma once
#include "constant/group.h"
namespace klotski::cases {
inline uint32_t Group::size() const {
return GROUP_SIZE[flat_id()];
}
inline uint32_t Group::flat_id() const {
return GROUP_OFFSET[type_id_] + group_id_;
}
inline uint32_t Group::type_id() const {
return type_id_;
}
inline uint32_t Group::group_id() const {
return group_id_;
}
inline Group Group::unsafe_create(const uint32_t type_id, const uint32_t group_id) {
return std::bit_cast<Group>(static_cast<uint64_t>(group_id) << 32 | type_id);
}
inline std::optional<Group> Group::create(const uint32_t type_id, const uint32_t group_id) {
if (type_id < TYPE_ID_LIMIT && group_id < GROUP_NUM[type_id]) {
return unsafe_create(type_id, group_id);
}
return std::nullopt;
}
} // namespace klotski::cases

161
src/core/group/internal/group_union.cc

@ -1,154 +1,51 @@
#include "core/core.h"
#include "group/group.h"
#include "constant/group_union.h"
// #include <queue>
// #include <absl/container/btree_set.h>
// #include <absl/container/flat_hash_map.h>
// #include <absl/container/node_hash_map.h>
#include <iostream>
#include <core/core.h>
using klotski::codec::RawCode;
using klotski::codec::CommonCode;
using klotski::cases::GroupUnion;
#include "constant/group_union.h"
#define RANGE_DERIVE(HEAD) ranges.derive(HEAD, cases[HEAD])
static KLSK_INLINE uint32_t type_id(const int n, const int n_2x1, const int n_1x1) {
static KLSK_INLINE uint32_t to_type_id(const int n, const int n_2x1, const int n_1x1) {
constexpr int offset[8] = {0, 15, 41, 74, 110, 145, 175, 196};
return offset[n] + (15 - n * 2) * n_2x1 + n_1x1;
}
static uint32_t common_code_to_type_id(const uint64_t common_code) {
const auto range = static_cast<uint32_t>(common_code);
uint32_t GroupUnion::type_id(const CommonCode common_code) {
const auto range = static_cast<uint32_t>(common_code.unwrap());
const auto n_1x1 = std::popcount((range >> 1) & range & 0x55555555);
const auto n_2x1 = std::popcount((range >> 1) & ~range & 0x55555555);
return type_id(std::popcount(range) - n_1x1 * 2, n_2x1, n_1x1);
}
static uint32_t raw_code_to_type_id(const uint64_t raw_code) {
const auto n = std::popcount(((raw_code >> 1) ^ raw_code) & 0x0249249249249249);
const auto n_2x1 = std::popcount((raw_code >> 1) & ~raw_code & 0x0249249249249249);
const auto n_1x1 = std::popcount((raw_code >> 1) & raw_code & 0x0249249249249249) - n - 3;
return type_id(n, n_2x1, n_1x1);
}
uint32_t klotski::cases::GroupUnion::type_id(codec::CommonCode common_code) {
return common_code_to_type_id(common_code.unwrap());
return to_type_id(std::popcount(range) - n_1x1 * 2, n_2x1, n_1x1);
}
uint32_t klotski::cases::GroupUnion::type_id(codec::RawCode raw_code) {
return raw_code_to_type_id(raw_code.unwrap());
uint32_t GroupUnion::type_id(const RawCode raw_code) {
const auto code = raw_code.unwrap();
const auto n = std::popcount(((code >> 1) ^ code) & 0x0249249249249249);
const auto n_2x1 = std::popcount((code >> 1) & ~code & 0x0249249249249249);
const auto n_1x1 = std::popcount((code >> 1) & code & 0x0249249249249249) - n - 3;
return to_type_id(n, n_2x1, n_1x1);
}
klotski::cases::RangesUnion klotski::cases::GroupUnion::cases() const {
Ranges ranges {};
klotski::cases::RangesUnion GroupUnion::cases() const {
auto [n, n_2x1, n_1x1] = BLOCK_NUM[type_id_];
auto [s_a, s_b, s_c, s_d] = GROUP_UNION_CASES_NUM[type_id_];
// int n = TYPE_ID_N_NUM[type_id_];
// int n_2x1 = TYPE_ID_N_2x1_NUM[type_id_];
// int n_1x1 = TYPE_ID_N_1x1_NUM[type_id_]; // TODO: cal from type_id
Ranges ranges {};
ranges.reserve(BASIC_RANGES_NUM[type_id_]);
ranges.spawn(n, n_2x1, n_1x1);
// for (int i = 0; i < TYPE_ID_LIMIT; ++i) {
// ranges.spawn(TYPE_ID_N_NUM[i], TYPE_ID_N_2x1_NUM[i], TYPE_ID_N_1x1_NUM[i]);
// }
// std::stable_sort(ranges.begin(), ranges.end());
// for (auto &x : ranges) {
// x = klotski::range_reverse(x);
// }
ranges.reverse();
// auto do_assert = [](uint32_t lhs, uint32_t rhs) {
// if (lhs != rhs) {
// std::cout << "error" << std::endl;
// }
// };
RangesUnion cases;
// cases[0x0].reserve(7815);
// cases[0x1].reserve(6795);
// cases[0x2].reserve(7815);
//
// cases[0x4].reserve(3525);
// cases[0x5].reserve(3465);
// cases[0x6].reserve(3525);
//
// cases[0x8].reserve(3525);
// cases[0x9].reserve(3465);
// cases[0xA].reserve(3525);
//
// cases[0xC].reserve(7815);
// cases[0xD].reserve(6795);
// cases[0xE].reserve(7815);
auto [A, B, C, D] = GROUP_UNION_CASES_NUM[type_id_];
cases[0x0].reserve(A);
cases[0x1].reserve(B);
cases[0x2].reserve(A);
cases[0x4].reserve(C);
cases[0x5].reserve(D);
cases[0x6].reserve(C);
cases[0x8].reserve(C);
cases[0x9].reserve(D);
cases[0xA].reserve(C);
cases[0xC].reserve(A);
cases[0xD].reserve(B);
cases[0xE].reserve(A);
ranges.derive(0x0, cases[0x0]);
ranges.derive(0x1, cases[0x1]);
ranges.derive(0x2, cases[0x2]);
ranges.derive(0x4, cases[0x4]);
ranges.derive(0x5, cases[0x5]);
ranges.derive(0x6, cases[0x6]);
ranges.derive(0x8, cases[0x8]);
ranges.derive(0x9, cases[0x9]);
ranges.derive(0xA, cases[0xA]);
ranges.derive(0xC, cases[0xC]);
ranges.derive(0xD, cases[0xD]);
ranges.derive(0xE, cases[0xE]);
// uint32_t A = cases[0x0].size();
// uint32_t B = cases[0x1].size();
// uint32_t C = cases[0x4].size();
// uint32_t D = cases[0x5].size();
//
// do_assert(cases[0x2].size(), A);
// do_assert(cases[0x6].size(), C);
// do_assert(cases[0x8].size(), C);
// do_assert(cases[0x9].size(), D);
// do_assert(cases[0xA].size(), C);
// do_assert(cases[0xC].size(), A);
// do_assert(cases[0xD].size(), B);
// do_assert(cases[0xE].size(), A);
//
// std::cout << A << ", " << B << ", " << C << ", " << D << std::endl;
// auto [A, B, C, D] = kk[type_id_];
// do_assert(cases[0x0].size(), A);
// do_assert(cases[0x1].size(), B);
// do_assert(cases[0x2].size(), A);
//
// do_assert(cases[0x4].size(), C);
// do_assert(cases[0x5].size(), D);
// do_assert(cases[0x6].size(), C);
//
// do_assert(cases[0x8].size(), C);
// do_assert(cases[0x9].size(), D);
// do_assert(cases[0xA].size(), C);
//
// do_assert(cases[0xC].size(), A);
// do_assert(cases[0xD].size(), B);
// do_assert(cases[0xE].size(), A);
cases[0x0].reserve(s_a); cases[0x1].reserve(s_b); cases[0x2].reserve(s_a);
cases[0x4].reserve(s_c); cases[0x5].reserve(s_d); cases[0x6].reserve(s_c);
cases[0x8].reserve(s_c); cases[0x9].reserve(s_d); cases[0xA].reserve(s_c);
cases[0xC].reserve(s_a); cases[0xD].reserve(s_b); cases[0xE].reserve(s_a);
RANGE_DERIVE(0x0); RANGE_DERIVE(0x1); RANGE_DERIVE(0x2);
RANGE_DERIVE(0x4); RANGE_DERIVE(0x5); RANGE_DERIVE(0x6);
RANGE_DERIVE(0x8); RANGE_DERIVE(0x9); RANGE_DERIVE(0xA);
RANGE_DERIVE(0xC); RANGE_DERIVE(0xD); RANGE_DERIVE(0xE);
return cases;
}

29
src/core/group/internal/group_union.inl

@ -1,20 +1,22 @@
#pragma once
#include <ranges>
#include "constant/group_union.h"
namespace klotski::cases {
// ------------------------------------------------------------------------------------- //
inline uint32_t GroupUnion::unwrap() const {
constexpr uint32_t GroupUnion::unwrap() const {
return type_id_;
}
inline GroupUnion GroupUnion::unsafe_create(const uint32_t type_id) {
constexpr GroupUnion GroupUnion::unsafe_create(const uint32_t type_id) {
return std::bit_cast<GroupUnion>(type_id);
}
inline std::optional<GroupUnion> GroupUnion::create(const uint32_t type_id) {
constexpr std::optional<GroupUnion> GroupUnion::create(const uint32_t type_id) {
if (type_id < TYPE_ID_LIMIT) {
return unsafe_create(type_id);
}
@ -23,28 +25,25 @@ inline std::optional<GroupUnion> GroupUnion::create(const uint32_t type_id) {
// ------------------------------------------------------------------------------------- //
inline uint32_t GroupUnion::size() const {
constexpr uint32_t GroupUnion::size() const {
return GROUP_UNION_SIZE[type_id_];
}
inline uint32_t GroupUnion::group_num() const {
constexpr uint32_t GroupUnion::group_num() const {
return GROUP_NUM[type_id_];
}
inline uint32_t GroupUnion::max_group_size() const {
constexpr uint32_t GroupUnion::max_group_size() const {
return MAX_GROUP_SIZE[type_id_];
}
inline std::vector<Group> GroupUnion::groups() const {
std::vector<Group> groups;
groups.reserve(group_num());
for (uint32_t group_id = 0; group_id < group_num(); ++group_id) {
groups.emplace_back(Group::unsafe_create(type_id_, group_id));
}
return groups;
auto build = [this](const uint32_t group_id) {
return Group::unsafe_create(type_id_, group_id);
};
return std::views::iota(0U, group_num())
| std::views::transform(build)
| std::ranges::to<std::vector>();
}
inline std::optional<Group> GroupUnion::group(const uint32_t group_id) const {

38
src/core/main.cc

@ -41,6 +41,44 @@ int main() {
// std::cout << ret[4].size() << std::endl;
}
// auto group_union = GroupUnion::unsafe_create(169);
// for (auto group : group_union.groups()) {
// std::cout << group.type_id_ << ", " << group.group_id_ << std::endl;
// }
// constexpr auto gu = GroupUnion::unsafe_create(169);
// constexpr auto gu_ = GroupUnion::create(169).value();
// // constexpr auto gu_ = GroupUnion::create(1169).value();
// constexpr auto k1 = gu.unwrap();
// constexpr auto k2 = gu.size();
// constexpr auto k3 = gu.group_num();
// constexpr auto k4 = gu.max_group_size();
// auto kk = GroupUnion::unsafe_create(169);
// auto pp = kk.group(0).value();
// std::cout << pp.type_id() << ", " << pp.group_id() << std::endl;
// for (auto group : kk.groups()) {
// std::cout << group.type_id() << ", " << group.group_id() << std::endl;
// }
auto gu = GroupUnion::unsafe_create(169);
// auto cases = gu.group(0).value().cases();
// for (auto &kk : cases) {
// std::cout << kk.size() << std::endl;
// }
klotski::cases::RangesUnion cases;
for (auto group : gu.groups()) {
cases += group.cases();
}
for (auto &kk : cases) {
std::ranges::sort(kk.begin(), kk.end());
}
std::cout << (cases == gu.cases()) << std::endl;
std::cerr << std::chrono::system_clock::now() - start << std::endl;
// auto raw_code = RawCode::from_common_code(0x1A9BF0C00)->unwrap();

78
src/core/ranges/internal/derive.cc

@ -1,7 +1,3 @@
#include <iostream>
#include <format>
#include "utils/utility.h"
#include "ranges/ranges.h"
@ -35,86 +31,22 @@ int Ranges::check(const int head, uint32_t range) {
return 0; // pass check
}
// int check_range(const int head, uint32_t range) {
// KLSK_ASSUME(head >= 0 && head < 16 && head % 4 != 3);
// uint32_t flags = 0b110011 << head; // fill 2x2 block
// for (int addr = 0, offset = 1; range; range >>= 2, ++offset) { // traverse every 2-bit
// const auto num = std::countr_one(flags);
// addr += num; // next unfilled block
// flags >>= num;
// switch (range & 0b11) {
// case 0b00: // space
// case 0b11: // 1x1 block
// flags |= 0b1;
// continue;
// case 0b01: // 1x2 block
// if (flags & 0b10 || addr % 4 == 3) { // invalid case
// return offset; // broken offset
// }
// flags |= 0b11;
// continue;
// case 0b10: // 2x1 block
// if (flags & 0b10000 || addr > 15) { // invalid case
// return offset; // broken offset
// }
// flags |= 0b10001;
// }
// }
// return 0; // pass check
// }
void Ranges::derive(const int head, Ranges &output) const {
// Ranges reversed {*this};
//
// for (auto &x : reversed) {
// x = klotski::range_reverse(x);
// }
// std::cout << "last = " << range_reverse(this->back()) << std::endl;
// auto &ranges = *this;
// uint32_t min_next = 0;
uint32_t last_val = range_reverse(this->back());
const uint32_t max_val = range_reverse(this->back());
for (uint32_t index = 0; index < size(); ++index) {
if (const auto offset = check(head, (*this)[index])) { // invalid case
uint32_t tmp = 1U << (32 - offset * 2); // distance to next possible range
/// !! <- broken
/// ( xx xx xx ) xx xx xx ... [reversed range]
/// +1 00 00 00 ... (delta)
tmp += range_reverse((*this)[index]) & ~(tmp - 1);
// std::cout << "min next = " << tmp << std::endl;
// if (min_next > tmp) {
// std::cout << "error" << std::endl;
// }
auto min_next = tmp;
// const int tmp = (16 - offset) * 2;
// const uint32_t min_next = ((range_reverse((*this)[index]) >> tmp) + 1) << tmp;
if (min_next > last_val) {
// std::cout << "get it" << std::endl;
break;
// return;
const uint32_t delta = 1U << (32 - offset * 2); // distance to next possible range
const auto min_next = delta + range_reverse((*this)[index]) & ~(delta - 1);
if (min_next > max_val) {
break; // index has overflowed
}
// TODO: overflow here in some type_id
// TODO: -> tmp > range[-1]
// TODO: maybe using binary search here
while (range_reverse((*this)[++index]) < min_next) {} // located next range
--index;
continue;
}
output.emplace_back(range_reverse((*this)[index])); // release valid case
}
// std::cout << "min_next = " << min_next << " | last_val = " << last_val << std::endl;
// std::cout << (min_next <= last_val) << std::endl;
}

65
src/core/ranges/internal/ranges.cc

@ -1,40 +1,45 @@
#include <algorithm>
#include "utils/utility.h"
#include "ranges/ranges.h"
#include "common_code/common_code.h"
using klotski::cases::Ranges;
using klotski::codec::CommonCode;
using klotski::cases::RangesUnion;
template<int N>
static void build_ranges(std::vector<uint32_t> &ranges, int n_10, int n_11) {
int n_01 = N - n_10;
int n_00 = 16 - N * 2 - n_11;
static constexpr auto heads = std::to_array<uint64_t>({
0x0, 0x1, 0x2, 0x4, 0x5, 0x6,
0x8, 0x9, 0xA, 0xC, 0xD, 0xE,
});
std::array<int, 16 - N> series {};
std::fill_n(series.begin() + n_00, n_01, 0b01);
std::fill_n(series.begin() + n_00 + n_01, n_10, 0b10);
std::fill_n(series.begin() + n_00 + n_01 + n_10, n_11, 0b11);
void Ranges::reverse() {
for (auto &x : *this) {
x = range_reverse(x);
}
}
do {
uint32_t range = 0;
for (const auto x : series) { // store every 2-bit
(range <<= 2) |= x;
}
ranges.emplace_back(range << (N * 2));
} while (std::ranges::next_permutation(series).found);
Ranges& Ranges::operator+=(const Ranges &ranges) {
this->insert(this->end(), ranges.begin(), ranges.end());
return *this;
}
RangesUnion& RangesUnion::operator+=(const RangesUnion &ranges_union) {
for (const auto head : heads) {
(*this)[head] += ranges_union[head];
}
return *this;
}
void Ranges::spawn(const int n, const int n_2x1, const int n_1x1) {
KLSK_ASSUME(n >= 0 && n_2x1 >= 0 && n_1x1 >= 0);
KLSK_ASSUME(n <= 7 && n_2x1 <= n && n_1x1 + n * 2 <= 14);
switch (n) {
case 0: return build_ranges<0>(*this, n_2x1, n_1x1);
case 1: return build_ranges<1>(*this, n_2x1, n_1x1);
case 2: return build_ranges<2>(*this, n_2x1, n_1x1);
case 3: return build_ranges<3>(*this, n_2x1, n_1x1);
case 4: return build_ranges<4>(*this, n_2x1, n_1x1);
case 5: return build_ranges<5>(*this, n_2x1, n_1x1);
case 6: return build_ranges<6>(*this, n_2x1, n_1x1);
case 7: return build_ranges<7>(*this, n_2x1, n_1x1);
std::vector<CommonCode> RangesUnion::codes() const {
size_type size = 0;
for (const auto head : heads) {
size += (*this)[head].size();
}
std::vector<CommonCode> codes;
codes.reserve(size);
for (const auto head : heads) {
for (const auto range : (*this)[head]) {
codes.emplace_back(CommonCode::unsafe_create(head << 32 | range));
}
}
return codes;
}

40
src/core/ranges/internal/spawn.cc

@ -0,0 +1,40 @@
#include <algorithm>
#include "utils/utility.h"
#include "ranges/ranges.h"
using klotski::cases::Ranges;
template<int N>
static void spawn_ranges(std::vector<uint32_t> &ranges, int n_10, int n_11) {
int n_01 = N - n_10;
int n_00 = 16 - N * 2 - n_11;
std::array<int, 16 - N> series {};
std::fill_n(series.begin() + n_00, n_01, 0b01);
std::fill_n(series.begin() + n_00 + n_01, n_10, 0b10);
std::fill_n(series.begin() + n_00 + n_01 + n_10, n_11, 0b11);
do {
uint32_t range = 0;
for (const auto x : series) { // store every 2-bit
(range <<= 2) |= x;
}
ranges.emplace_back(range << (N * 2));
} while (std::ranges::next_permutation(series).found);
}
void Ranges::spawn(const int n, const int n_2x1, const int n_1x1) {
KLSK_ASSUME(n >= 0 && n_2x1 >= 0 && n_1x1 >= 0);
KLSK_ASSUME(n <= 7 && n_2x1 <= n && n_1x1 + n * 2 <= 14);
switch (n) {
case 0: return spawn_ranges<0>(*this, n_2x1, n_1x1);
case 1: return spawn_ranges<1>(*this, n_2x1, n_1x1);
case 2: return spawn_ranges<2>(*this, n_2x1, n_1x1);
case 3: return spawn_ranges<3>(*this, n_2x1, n_1x1);
case 4: return spawn_ranges<4>(*this, n_2x1, n_1x1);
case 5: return spawn_ranges<5>(*this, n_2x1, n_1x1);
case 6: return spawn_ranges<6>(*this, n_2x1, n_1x1);
case 7: return spawn_ranges<7>(*this, n_2x1, n_1x1);
}
}

30
src/core/ranges/ranges.h

@ -1,36 +1,42 @@
#pragma once
#include <array>
#include <vector>
#include <cstdint>
#include "utils/utility.h"
namespace klotski::codec {
class CommonCode;
} // namespace klotski::codec
namespace klotski::cases {
class Ranges : public std::vector<uint32_t> {
class Ranges final : public std::vector<uint32_t> {
public:
/// Append the ranges from another instance.
Ranges& operator+=(const Ranges &ranges);
/// Spawn klotski-ranges that match the specified block numbers.
void spawn(int n, int n_2x1, int n_1x1);
/// Flip the klotski-ranges every two bits in low-high symmetry.
void reverse();
/// Derive the legal klotski-ranges with specified head.
/// Derive the legal klotski-ranges from reversed ranges with specified head.
void derive(int head, Ranges &output) const;
/// Check whether the combination of head and range is valid.
/// Check whether the combination of head and reversed range is valid.
static KLSK_INLINE int check(int head, uint32_t range);
};
// typedef std::array<Ranges, 16> RangesUnion;
class RangesUnion final : public std::array<Ranges, 16> {
std::vector<uint64_t> codes();
};
public:
/// Append the ranges from another instance.
RangesUnion& operator+=(const RangesUnion &ranges_union);
inline void Ranges::reverse() {
for (auto &x : *this) {
x = range_reverse(x);
}
}
/// Export the RangesUnion as CommonCode list.
[[nodiscard]] std::vector<codec::CommonCode> codes() const;
};
} // namespace klotski::cases

524
src/core/ranges/statistics.py

@ -1,524 +0,0 @@
#!/usr/bin/env python3
def load():
lines = open('tmp.txt').read().splitlines()
tmp = [x.split(' ') for x in lines]
data = [(int(x[0]), int(x[1]), int(x[2])) for x in tmp]
return data
def statis(data: list[tuple[int, int]]):
# data -> (distance, offset)
distances = sorted({x[0] for x in data})
for distance in distances:
all_offsets = [x[1] for x in data if x[0] == distance]
offsets = sorted(set(all_offsets))
# print(distance, offsets)
# for offset in offsets:
# print(offset, all_offsets.count(offset))
offset_num = {x: all_offsets.count(x) for x in offsets}
print(f'{distance} -> {offset_num}')
if __name__ == '__main__':
raw_data = load()
for head in range(16):
if head % 4 == 3:
continue
print(f'head = {head}')
statis([(x[1], x[2]) for x in raw_data])
# statis([(x[1], x[2]) for x in raw_data if x[0] == head])
print()
"""
0 -> {7: 177, 8: 2619, 9: 21087, 10: 156415, 11: 756356, 12: 1755849, 13: 1981898, 14: 1057653, 15: 212797}
1 -> {9: 18544, 10: 192582, 11: 596880, 12: 785128, 13: 460136, 14: 98136}
2 -> {8: 3578, 9: 15480, 10: 18792, 11: 8920, 12: 1760, 13: 120}
3 -> {7: 498, 8: 1963, 9: 2215, 10: 1000, 11: 192, 12: 13}
4 -> {8: 67}
5 -> {8: 1722, 9: 42570, 10: 178524, 11: 275405, 12: 178288, 13: 40830}
8 -> {7: 8}
10 -> {7: 490, 8: 5889, 9: 11075, 10: 7000, 11: 1728, 12: 143}
13 -> {6: 1}
15 -> {8: 7852, 9: 57590, 10: 120000, 11: 96384, 12: 26468}
16 -> {6: 50, 7: 433, 8: 625, 9: 297, 10: 53, 11: 3}
32 -> {7: 866, 8: 2500, 9: 1782, 10: 424, 11: 30}
42 -> {7: 23}
43 -> {7: 410, 8: 6875, 9: 16929, 10: 13091, 11: 3039}
57 -> {6: 189, 7: 457, 8: 295, 9: 68, 10: 5}
95 -> {6: 7}
98 -> {6: 182, 7: 1371, 8: 1475, 9: 476, 10: 45}
119 -> {7: 1828, 8: 7670, 9: 8160, 10: 2510}
184 -> {5: 1}
190 -> {5: 52, 6: 276, 7: 221, 8: 47, 9: 2}
284 -> {6: 552, 7: 884, 8: 282, 9: 16}
326 -> {6: 10}
327 -> {6: 266, 7: 2431, 8: 2679, 9: 494}
591 -> {5: 162, 6: 269, 7: 112, 8: 13}
806 -> {5: 6}
810 -> {5: 156, 6: 807, 7: 560, 8: 91}
895 -> {6: 1076, 7: 2912, 8: 1560}
1784 -> {4: 18, 5: 73, 6: 32, 7: 3}
2276 -> {5: 146, 6: 128, 7: 18}
2446 -> {5: 9}
2447 -> {5: 64, 6: 352, 7: 171}
5245 -> {4: 51, 5: 33, 6: 5}
6341 -> {4: 1}
6346 -> {4: 50, 5: 99, 6: 25}
6687 -> {5: 132, 6: 130}
15147 -> {3: 1}
15162 -> {3: 20, 4: 33, 5: 2}
17588 -> {4: 66, 5: 8}
18270 -> {4: 2}
18271 -> {4: 31, 5: 22}
43243 -> {3: 31, 4: 13}
48548 -> {3: 3}
48554 -> {3: 28, 4: 39}
49919 -> {4: 52}
122103 -> {2: 1}
122124 -> {2: 5, 3: 3}
133652 -> {3: 6}
136382 -> {3: 2}
136383 -> {3: 1}
342265 -> {2: 5}
367139 -> {2: 2}
367146 -> {2: 3}
953634 -> {1: 1}
953662 -> {1: 1}
"""
"""
head = 0
0 -> {7: 24, 8: 357, 9: 2834, 10: 19468, 11: 85928, 12: 181284, 13: 185611, 14: 89839, 15: 16369}
1 -> {9: 2548, 10: 23998, 11: 67680, 12: 80822, 13: 42756, 14: 8178}
2 -> {8: 460, 9: 1932, 10: 2262, 11: 1024, 12: 190, 13: 12}
3 -> {7: 46, 8: 161, 9: 177, 10: 79, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 222, 9: 5313, 10: 21489, 11: 31616, 12: 19247, 13: 4083}
10 -> {7: 46, 8: 483, 9: 885, 10: 553, 11: 135, 12: 11}
15 -> {8: 644, 9: 4602, 10: 9480, 11: 7530, 12: 2036}
16 -> {6: 2, 7: 20, 8: 25, 9: 9, 10: 1}
32 -> {7: 40, 8: 100, 9: 54, 10: 8}
43 -> {7: 20, 8: 275, 9: 513, 10: 247}
57 -> {6: 13, 7: 25, 8: 10, 9: 1}
98 -> {6: 13, 7: 75, 8: 50, 9: 7}
119 -> {7: 100, 8: 260, 9: 120}
190 -> {5: 5, 6: 23, 7: 18, 8: 3}
284 -> {6: 46, 7: 72, 8: 18}
327 -> {6: 23, 7: 198, 8: 171}
591 -> {5: 11, 6: 18, 7: 8, 8: 1}
810 -> {5: 11, 6: 54, 7: 40, 8: 7}
895 -> {6: 72, 7: 208, 8: 120}
1784 -> {5: 2, 6: 1}
2276 -> {5: 4, 6: 4}
2447 -> {5: 2, 6: 11}
15162 -> {3: 1, 4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 3, 4: 1}
48554 -> {3: 3, 4: 3}
49919 -> {4: 4}
122124 -> {2: 1}
342265 -> {2: 1}
367146 -> {2: 1}
head = 1
0 -> {7: 9, 8: 165, 9: 1464, 10: 10920, 11: 54215, 12: 130359, 13: 151147, 14: 81673, 15: 16369}
1 -> {9: 1320, 10: 14456, 11: 47280, 12: 64758, 13: 38684, 14: 8178}
2 -> {8: 226, 9: 1104, 10: 1518, 11: 800, 12: 170, 13: 12}
3 -> {7: 21, 8: 88, 9: 113, 10: 59, 11: 13, 12: 1}
4 -> {8: 8}
5 -> {8: 105, 9: 3036, 10: 14421, 11: 24700, 12: 17221, 13: 4083}
10 -> {7: 21, 8: 264, 9: 565, 10: 413, 11: 117, 12: 11}
15 -> {8: 352, 9: 2938, 10: 7080, 11: 6526, 12: 2036}
16 -> {6: 1, 7: 12, 8: 18, 9: 8, 10: 1}
32 -> {7: 24, 8: 72, 9: 48, 10: 8}
43 -> {7: 12, 8: 198, 9: 456, 10: 247}
57 -> {6: 7, 7: 15, 8: 8, 9: 1}
98 -> {6: 7, 7: 45, 8: 40, 9: 7}
119 -> {7: 60, 8: 208, 9: 120}
190 -> {5: 2, 6: 12, 7: 12, 8: 3}
284 -> {6: 24, 7: 48, 8: 18}
327 -> {6: 12, 7: 132, 8: 171}
591 -> {5: 4, 6: 10, 7: 6, 8: 1}
810 -> {5: 4, 6: 30, 7: 30, 8: 7}
895 -> {6: 40, 7: 156, 8: 120}
15162 -> {4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
122124 -> {2: 1, 3: 1}
133652 -> {3: 2}
136383 -> {3: 1}
342265 -> {2: 1}
367146 -> {2: 1}
953662 -> {1: 1}
head = 2
0 -> {7: 24, 8: 359, 9: 2838, 10: 19461, 11: 85764, 12: 181037, 13: 185611, 14: 89839, 15: 16369}
1 -> {9: 2556, 10: 24076, 11: 67800, 12: 80822, 13: 42756, 14: 8178}
2 -> {8: 460, 9: 1932, 10: 2262, 11: 1024, 12: 190, 13: 12}
3 -> {7: 44, 8: 165, 9: 179, 10: 79, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 222, 9: 5313, 10: 21489, 11: 31616, 12: 19247, 13: 4083}
10 -> {7: 44, 8: 495, 9: 895, 10: 553, 11: 135, 12: 11}
15 -> {8: 660, 9: 4654, 10: 9480, 11: 7530, 12: 2036}
16 -> {6: 2, 7: 19, 8: 27, 9: 10, 10: 1}
32 -> {7: 38, 8: 108, 9: 60, 10: 8}
43 -> {7: 19, 8: 297, 9: 570, 10: 247}
57 -> {6: 14, 7: 23, 8: 9, 9: 1}
98 -> {6: 14, 7: 69, 8: 45, 9: 7}
119 -> {7: 92, 8: 234, 9: 120}
190 -> {5: 5, 6: 23, 7: 18, 8: 3}
284 -> {6: 46, 7: 72, 8: 18}
327 -> {6: 23, 7: 198, 8: 171}
591 -> {5: 7, 6: 16, 7: 8, 8: 1}
810 -> {5: 7, 6: 48, 7: 40, 8: 7}
895 -> {6: 64, 7: 208, 8: 120}
5245 -> {4: 2, 5: 1}
6346 -> {4: 2, 5: 3}
6687 -> {5: 4}
15162 -> {3: 1, 4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 3, 4: 1}
48554 -> {3: 3, 4: 3}
49919 -> {4: 4}
122124 -> {2: 1}
342265 -> {2: 1}
367146 -> {2: 1}
head = 4
0 -> {7: 13, 8: 186, 9: 1638, 10: 12049, 11: 58254, 12: 135573, 13: 153173, 14: 81673, 15: 16369}
1 -> {9: 1464, 10: 15808, 11: 50040, 12: 66264, 13: 38684, 14: 8178}
2 -> {8: 244, 9: 1172, 10: 1584, 11: 816, 12: 170, 13: 12}
3 -> {7: 21, 8: 92, 9: 115, 10: 59, 11: 13, 12: 1}
5 -> {8: 122, 9: 3223, 10: 15048, 11: 25194, 12: 17221, 13: 4083}
10 -> {7: 21, 8: 276, 9: 575, 10: 413, 11: 117, 12: 11}
15 -> {8: 368, 9: 2990, 10: 7080, 11: 6526, 12: 2036}
16 -> {7: 4, 8: 12, 9: 7, 10: 1}
32 -> {7: 8, 8: 48, 9: 42, 10: 8}
43 -> {7: 4, 8: 132, 9: 399, 10: 247}
190 -> {5: 2, 6: 8, 7: 6, 8: 1}
284 -> {6: 16, 7: 24, 8: 6}
327 -> {6: 8, 7: 66, 8: 57}
591 -> {5: 7, 6: 16, 7: 8, 8: 1}
810 -> {5: 7, 6: 48, 7: 40, 8: 7}
895 -> {6: 64, 7: 208, 8: 120}
1784 -> {4: 1, 5: 3, 6: 1}
2276 -> {5: 6, 6: 4}
2447 -> {5: 3, 6: 11}
5245 -> {4: 4, 5: 5, 6: 1}
6346 -> {4: 4, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15162 -> {3: 1, 4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
342265 -> {2: 1}
367139 -> {2: 1}
953634 -> {1: 1}
head = 5
0 -> {7: 5, 8: 124, 9: 1161, 10: 8742, 11: 43440, 12: 105459, 13: 126813, 14: 73507, 15: 16369}
1 -> {9: 1044, 10: 11492, 11: 37800, 12: 53714, 13: 34612, 14: 8178}
2 -> {8: 162, 9: 840, 10: 1176, 11: 648, 12: 150, 13: 12}
3 -> {7: 11, 8: 55, 9: 77, 10: 44, 11: 11, 12: 1}
5 -> {8: 81, 9: 2310, 10: 11172, 11: 20007, 12: 15195, 13: 4083}
10 -> {7: 11, 8: 165, 9: 385, 10: 308, 11: 99, 12: 11}
15 -> {8: 220, 9: 2002, 10: 5280, 11: 5522, 12: 2036}
190 -> {5: 1, 6: 6, 7: 5, 8: 1}
284 -> {6: 12, 7: 20, 8: 6}
327 -> {6: 6, 7: 55, 8: 57}
591 -> {5: 7, 6: 14, 7: 7, 8: 1}
810 -> {5: 7, 6: 42, 7: 35, 8: 7}
895 -> {6: 56, 7: 182, 8: 120}
1784 -> {4: 2, 5: 9, 6: 6, 7: 1}
2276 -> {5: 18, 6: 24, 7: 6}
2447 -> {5: 9, 6: 66, 7: 57}
5245 -> {4: 5, 5: 5, 6: 1}
6346 -> {4: 5, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15162 -> {3: 2, 4: 4, 5: 1}
17588 -> {4: 8, 5: 4}
18271 -> {4: 4, 5: 11}
43243 -> {3: 3, 4: 1}
48548 -> {3: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
122103 -> {2: 1}
122124 -> {2: 1, 3: 1}
133652 -> {3: 2}
136382 -> {3: 1}
342265 -> {2: 1}
367139 -> {2: 1}
head = 6
0 -> {7: 13, 8: 189, 9: 1635, 10: 11994, 11: 58832, 12: 137293, 13: 154186, 14: 81673, 15: 16369}
1 -> {9: 1464, 10: 15522, 11: 49200, 12: 65762, 13: 38684, 14: 8178}
2 -> {8: 244, 9: 1172, 10: 1584, 11: 816, 12: 170, 13: 12}
3 -> {7: 13, 8: 68, 9: 101, 10: 57, 11: 13, 12: 1}
5 -> {8: 122, 9: 3223, 10: 15048, 11: 25194, 12: 17221, 13: 4083}
10 -> {7: 13, 8: 204, 9: 505, 10: 399, 11: 117, 12: 11}
15 -> {8: 272, 9: 2626, 10: 6840, 11: 6526, 12: 2036}
57 -> {6: 4, 7: 12, 8: 7, 9: 1}
98 -> {6: 4, 7: 36, 8: 35, 9: 7}
119 -> {7: 48, 8: 182, 9: 120}
190 -> {5: 2, 6: 8, 7: 6, 8: 1}
284 -> {6: 16, 7: 24, 8: 6}
327 -> {6: 8, 7: 66, 8: 57}
591 -> {5: 7, 6: 16, 7: 8, 8: 1}
810 -> {5: 7, 6: 48, 7: 40, 8: 7}
895 -> {6: 64, 7: 208, 8: 120}
1784 -> {4: 1, 5: 3, 6: 1}
2276 -> {5: 6, 6: 4}
2447 -> {5: 3, 6: 11}
5245 -> {4: 6, 5: 5, 6: 1}
6341 -> {4: 1}
6346 -> {4: 5, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15147 -> {3: 1}
15162 -> {3: 2, 4: 4}
17588 -> {4: 8}
18270 -> {4: 2}
18271 -> {4: 2}
43243 -> {3: 6, 4: 2}
48548 -> {3: 2}
48554 -> {3: 4, 4: 6}
49919 -> {4: 8}
122124 -> {2: 1, 3: 1}
133652 -> {3: 2}
136382 -> {3: 1}
head = 8
0 -> {7: 18, 8: 204, 9: 1439, 10: 10794, 11: 54722, 12: 131998, 13: 152149, 14: 81673, 15: 16369}
1 -> {9: 1172, 10: 13130, 11: 43200, 12: 59738, 13: 36648, 14: 8178}
2 -> {8: 320, 9: 1368, 10: 1698, 11: 832, 12: 170, 13: 12}
3 -> {7: 30, 8: 133, 9: 163, 10: 77, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 152, 9: 3762, 10: 16131, 11: 25688, 12: 17221, 13: 4083}
10 -> {7: 30, 8: 399, 9: 815, 10: 539, 11: 135, 12: 11}
15 -> {8: 532, 9: 4238, 10: 9240, 11: 7530, 12: 2036}
16 -> {6: 5, 7: 26, 8: 29, 9: 10, 10: 1}
32 -> {7: 52, 8: 116, 9: 60, 10: 8}
43 -> {7: 26, 8: 319, 9: 570, 10: 247}
57 -> {6: 18, 7: 55, 8: 44, 9: 12, 10: 1}
98 -> {6: 18, 7: 165, 8: 220, 9: 84, 10: 9}
119 -> {7: 220, 8: 1144, 9: 1440, 10: 502}
190 -> {5: 2, 6: 8, 7: 6, 8: 1}
284 -> {6: 16, 7: 24, 8: 6}
327 -> {6: 8, 7: 66, 8: 57}
591 -> {5: 17, 6: 23, 7: 9, 8: 1}
806 -> {5: 2}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 3, 5: 12, 6: 3}
2276 -> {5: 24, 6: 12}
2446 -> {5: 5}
2447 -> {5: 7, 6: 33}
5245 -> {4: 11, 5: 8, 6: 1}
6346 -> {4: 11, 5: 24, 6: 5}
6687 -> {5: 32, 6: 26}
15162 -> {3: 3, 4: 6, 5: 1}
17588 -> {4: 12, 5: 4}
18271 -> {4: 6, 5: 11}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 9
0 -> {7: 5, 8: 103, 9: 863, 10: 6189, 11: 32410, 12: 85591, 13: 111607, 14: 69424, 15: 16369}
1 -> {9: 748, 10: 8866, 11: 31440, 12: 47690, 13: 32576, 14: 8178}
2 -> {8: 154, 9: 812, 10: 1164, 11: 648, 12: 150, 13: 12}
3 -> {7: 25, 8: 110, 9: 138, 10: 68, 11: 14, 12: 1}
4 -> {8: 8}
5 -> {8: 69, 9: 2233, 10: 11058, 11: 20007, 12: 15195, 13: 4083}
10 -> {7: 25, 8: 330, 9: 690, 10: 476, 11: 126, 12: 11}
15 -> {8: 440, 9: 3588, 10: 8160, 11: 7028, 12: 2036}
16 -> {6: 6, 7: 52, 8: 93, 9: 56, 10: 13, 11: 1}
32 -> {7: 104, 8: 372, 9: 336, 10: 104, 11: 10}
43 -> {7: 52, 8: 1023, 9: 3192, 10: 3211, 11: 1013}
57 -> {6: 19, 7: 58, 8: 45, 9: 12, 10: 1}
98 -> {6: 19, 7: 174, 8: 225, 9: 84, 10: 9}
119 -> {7: 232, 8: 1170, 9: 1440, 10: 502}
190 -> {5: 8, 6: 41, 7: 37, 8: 11, 9: 1}
284 -> {6: 82, 7: 148, 8: 66, 9: 8}
326 -> {6: 2}
327 -> {6: 39, 7: 407, 8: 627, 9: 247}
591 -> {5: 26, 6: 35, 7: 11, 8: 1}
806 -> {5: 2}
810 -> {5: 24, 6: 105, 7: 55, 8: 7}
895 -> {6: 140, 7: 286, 8: 120}
1784 -> {4: 5, 5: 20, 6: 10, 7: 1}
2276 -> {5: 40, 6: 40, 7: 6}
2446 -> {5: 3}
2447 -> {5: 17, 6: 110, 7: 57}
5245 -> {4: 7, 5: 5, 6: 1}
6346 -> {4: 7, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 10
0 -> {7: 18, 8: 222, 9: 1694, 10: 10423, 11: 48002, 12: 115822, 13: 137978, 14: 77590, 15: 16369}
1 -> {9: 1536, 10: 16406, 11: 51120, 12: 66766, 13: 38684, 14: 8178}
2 -> {8: 320, 9: 1368, 10: 1698, 11: 832, 12: 170, 13: 12}
3 -> {7: 30, 8: 133, 9: 163, 10: 77, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 152, 9: 3762, 10: 16131, 11: 25688, 12: 17221, 13: 4083}
10 -> {7: 30, 8: 399, 9: 815, 10: 539, 11: 135, 12: 11}
15 -> {8: 532, 9: 4238, 10: 9240, 11: 7530, 12: 2036}
16 -> {6: 5, 7: 26, 8: 29, 9: 10, 10: 1}
32 -> {7: 52, 8: 116, 9: 60, 10: 8}
43 -> {7: 26, 8: 319, 9: 570, 10: 247}
57 -> {6: 26, 7: 65, 8: 46, 9: 12, 10: 1}
98 -> {6: 26, 7: 195, 8: 230, 9: 84, 10: 9}
119 -> {7: 260, 8: 1196, 9: 1440, 10: 502}
190 -> {5: 6, 6: 40, 7: 27, 8: 4}
284 -> {6: 80, 7: 108, 8: 24}
326 -> {6: 4}
327 -> {6: 36, 7: 297, 8: 228}
591 -> {5: 31, 6: 52, 7: 20, 8: 2}
806 -> {5: 2}
810 -> {5: 29, 6: 156, 7: 100, 8: 14}
895 -> {6: 208, 7: 520, 8: 240}
1784 -> {4: 3, 5: 12, 6: 7, 7: 1}
2276 -> {5: 24, 6: 28, 7: 6}
2446 -> {5: 1}
2447 -> {5: 11, 6: 77, 7: 57}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 12
0 -> {7: 7, 8: 109, 9: 1467, 10: 14381, 11: 69851, 12: 155802, 13: 168367, 14: 85756, 15: 16369}
1 -> {9: 1368, 10: 15236, 11: 49080, 12: 65762, 13: 38684, 14: 8178}
2 -> {8: 104, 9: 360, 10: 330, 11: 104, 12: 10}
3 -> {7: 53, 8: 199, 9: 210, 10: 89, 11: 16, 12: 1}
5 -> {8: 52, 9: 990, 10: 3135, 11: 3211, 12: 1013}
10 -> {7: 53, 8: 597, 9: 1050, 10: 623, 11: 144, 12: 11}
15 -> {8: 796, 9: 5460, 10: 10680, 11: 8032, 12: 2036}
16 -> {6: 6, 7: 69, 8: 85, 9: 30, 10: 3}
32 -> {7: 138, 8: 340, 9: 180, 10: 24}
42 -> {7: 7}
43 -> {7: 62, 8: 935, 9: 1710, 10: 741}
57 -> {6: 46, 7: 110, 8: 69, 9: 15, 10: 1}
95 -> {6: 5}
98 -> {6: 41, 7: 330, 8: 345, 9: 105, 10: 9}
119 -> {7: 440, 8: 1794, 9: 1800, 10: 502}
184 -> {5: 1}
190 -> {5: 9, 6: 53, 7: 48, 8: 13, 9: 1}
284 -> {6: 106, 7: 192, 8: 78, 9: 8}
326 -> {6: 4}
327 -> {6: 49, 7: 528, 8: 741, 9: 247}
591 -> {5: 15, 6: 23, 7: 9, 8: 1}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 1, 5: 4, 6: 1}
2276 -> {5: 8, 6: 4}
2447 -> {5: 4, 6: 11}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 13
0 -> {7: 21, 8: 281, 9: 1934, 10: 16983, 11: 94877, 12: 239808, 13: 286889, 14: 159250, 15: 32738}
1 -> {9: 1516, 10: 16068, 11: 50280, 12: 66264, 13: 38684, 14: 8178}
2 -> {8: 424, 9: 1768, 10: 2046, 11: 936, 12: 180, 13: 12}
3 -> {7: 90, 8: 315, 9: 303, 10: 114, 11: 18, 12: 1}
4 -> {8: 3}
5 -> {8: 209, 9: 4862, 10: 19437, 11: 28899, 12: 18234, 13: 4083}
8 -> {7: 4}
10 -> {7: 86, 8: 945, 9: 1515, 10: 798, 11: 162, 12: 11}
13 -> {6: 1}
15 -> {8: 1260, 9: 7878, 10: 13680, 11: 9036, 12: 2036}
16 -> {6: 14, 7: 128, 8: 188, 9: 92, 10: 17, 11: 1}
32 -> {7: 256, 8: 752, 9: 552, 10: 136, 11: 10}
42 -> {7: 12}
43 -> {7: 116, 8: 2068, 9: 5244, 10: 4199, 11: 1013}
57 -> {6: 27, 7: 68, 8: 47, 9: 12, 10: 1}
95 -> {6: 2}
98 -> {6: 25, 7: 204, 8: 235, 9: 84, 10: 9}
119 -> {7: 272, 8: 1222, 9: 1440, 10: 502}
190 -> {5: 5, 6: 27, 7: 19, 8: 3}
284 -> {6: 54, 7: 76, 8: 18}
327 -> {6: 27, 7: 209, 8: 171}
591 -> {5: 15, 6: 23, 7: 9, 8: 1}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 1, 5: 4, 6: 1}
2276 -> {5: 8, 6: 4}
2447 -> {5: 4, 6: 11}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 14
0 -> {7: 20, 8: 320, 9: 2120, 10: 15011, 11: 70061, 12: 155823, 13: 168367, 14: 85756, 15: 16369}
1 -> {9: 1808, 10: 17524, 11: 51960, 12: 66766, 13: 38684, 14: 8178}
2 -> {8: 460, 9: 1652, 10: 1470, 11: 440, 12: 40}
3 -> {7: 114, 8: 444, 9: 476, 10: 198, 11: 34, 12: 2}
4 -> {8: 16}
5 -> {8: 214, 9: 4543, 10: 13965, 11: 13585, 12: 4052}
8 -> {7: 4}
10 -> {7: 110, 8: 1332, 9: 2380, 10: 1386, 11: 306, 12: 22}
15 -> {8: 1776, 9: 12376, 10: 23760, 11: 17068, 12: 4072}
16 -> {6: 9, 7: 77, 8: 119, 9: 65, 10: 14, 11: 1}
32 -> {7: 154, 8: 476, 9: 390, 10: 112, 11: 10}
42 -> {7: 4}
43 -> {7: 73, 8: 1309, 9: 3705, 10: 3458, 11: 1013}
57 -> {6: 15, 7: 26, 8: 10, 9: 1}
98 -> {6: 15, 7: 78, 8: 50, 9: 7}
119 -> {7: 104, 8: 260, 9: 120}
190 -> {5: 5, 6: 27, 7: 19, 8: 3}
284 -> {6: 54, 7: 76, 8: 18}
327 -> {6: 27, 7: 209, 8: 171}
591 -> {5: 15, 6: 23, 7: 9, 8: 1}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 1, 5: 4, 6: 1}
2276 -> {5: 8, 6: 4}
2447 -> {5: 4, 6: 11}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
"""

5
src/core_test/CMakeLists.txt

@ -20,9 +20,12 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../core/common_code)
# ------------------------------------------------------------------------------------ #
set(KLSK_TEST_CASES_SRC
cases/all_cases.cc
cases/ranges.cc
cases/ranges_union.cc
cases/basic_ranges.cc
cases/all_cases.cc
cases/group_union.cc
cases/helper/block_nums.cc
)
add_executable(test_klotski_cases ${KLSK_TEST_CASES_SRC})

65
src/core_test/cases/all_cases.cc

@ -1,5 +1,9 @@
#include "hash.h"
#include "helper.h"
#include "cases_helper.h"
#include "common_code/common_code.h"
using klotski::cases::Ranges;
using klotski::codec::CommonCode;
static constexpr auto ALL_CASES_XXH3 = std::to_array<uint64_t>({
0x71c8ff7a71c93da0, 0x2a5247ee8bfed666, 0xf4efc8fc692d58e2, 0x2d06800538d394c2,
@ -30,38 +34,55 @@ protected:
const auto &all_cases = AllCases::instance().fetch();
for (int head = 0; head < 16; ++head) {
EXPECT_EQ(all_cases[head].size(), ALL_CASES_NUM[head]); // verify all cases size
}
for (int head = 0; head < 16; ++head) {
EXPECT_EQ(hash::xxh3(all_cases[head]), ALL_CASES_XXH3[head]); // verify all cases checksum
}
}
};
TEST_FF(AllCases, constant) {
EXPECT_EQ(ALL_CASES_NUM_, 29334498);
TEST_FF(AllCases, content) {
auto verify = [](const uint64_t head) {
auto &cases = AllCases::instance().fetch()[head];
EXPECT_EQ(ALL_CASES_NUM[0], 2942906);
EXPECT_EQ(ALL_CASES_NUM[1], 2260392);
EXPECT_EQ(ALL_CASES_NUM[2], 2942906);
EXPECT_EQ(cases.size(), ALL_CASES_NUM[head]); // size verify
EXPECT_EQ(hash::xxh3(cases), ALL_CASES_XXH3[head]); // checksum verify
EXPECT_EQ(ALL_CASES_NUM[4], 2322050);
EXPECT_EQ(ALL_CASES_NUM[5], 1876945);
EXPECT_EQ(ALL_CASES_NUM[6], 2322050);
EXPECT_TRUE(std::ranges::is_sorted(cases.begin(), cases.end()));
const auto match = std::ranges::adjacent_find(cases.begin(), cases.end());
EXPECT_EQ(match, cases.end()); // no duplicates
EXPECT_EQ(ALL_CASES_NUM[8], 2322050);
EXPECT_EQ(ALL_CASES_NUM[9], 1876945);
EXPECT_EQ(ALL_CASES_NUM[10], 2322050);
auto &all = BasicRanges::instance().fetch(); // subset verify
EXPECT_TRUE(std::ranges::includes(all.begin(), all.end(), cases.begin(), cases.end()));
EXPECT_EQ(ALL_CASES_NUM[12], 2942906);
EXPECT_EQ(ALL_CASES_NUM[13], 2260392);
EXPECT_EQ(ALL_CASES_NUM[14], 2942906);
for (const auto range : cases) {
CommonCode::check(head << 32 | range); // release verify
}
};
EXPECT_EQ(ALL_CASES_NUM[3], 0);
EXPECT_EQ(ALL_CASES_NUM[7], 0);
EXPECT_EQ(ALL_CASES_NUM[11], 0);
EXPECT_EQ(ALL_CASES_NUM[15], 0);
for (int head = 0; head < 16; ++head) {
if (head % 4 != 3) {
verify(head);
continue;
}
EXPECT_EQ(AllCases::instance().fetch()[head].size(), 0);
}
}
EXPECT_EQ(klotski::array_sum(ALL_CASES_NUM), ALL_CASES_NUM_);
TEST_FF(AllCases, constant) {
EXPECT_EQ(ALL_CASES_NUM_, 29334498);
EXPECT_EQ(ALL_CASES_NUM.size(), 16);
EXPECT_EQ(array_sum(ALL_CASES_NUM), ALL_CASES_NUM_);
auto ranges = BasicRanges::instance().fetch();
ranges.reverse();
for (int head = 0; head < 16; ++head) {
if (head % 4 == 3) {
EXPECT_EQ(ALL_CASES_NUM[head], 0);
continue;
}
Ranges release;
ranges.derive(head, release);
EXPECT_EQ(release.size(), ALL_CASES_NUM[head]);
}
}
TEST_FF(AllCases, all_cases) {

28
src/core_test/cases/basic_ranges.cc

@ -1,5 +1,10 @@
#include "hash.h"
#include "helper.h"
#include "group/group.h"
#include "cases_helper.h"
using klotski::cases::Ranges;
using klotski::cases::BLOCK_NUM;
using klotski::cases::TYPE_ID_LIMIT;
static constexpr uint64_t BASIC_RANGES_XXH3 = 0x34fce9da6a052533;
@ -28,9 +33,28 @@ protected:
}
};
TEST_FF(BasicRanges, content) {
auto &ranges = BasicRanges::instance().fetch();
EXPECT_TRUE(std::ranges::is_sorted(ranges.begin(), ranges.end()));
const auto match = std::ranges::adjacent_find(ranges.begin(), ranges.end());
EXPECT_EQ(match, ranges.end()); // no duplicates
EXPECT_EQ(ranges.size(), BASIC_RANGES_NUM_); // size verify
EXPECT_EQ(hash::xxh3(ranges), BASIC_RANGES_XXH3); // checksum verify
}
TEST_FF(BasicRanges, constant) {
// TODO: check BASIC_RANGES_NUM array
EXPECT_EQ(BASIC_RANGES_NUM_, 7311885);
EXPECT_EQ(BASIC_RANGES_NUM.size(), TYPE_ID_LIMIT);
EXPECT_EQ(array_sum(BASIC_RANGES_NUM), BASIC_RANGES_NUM_);
for (uint32_t type_id = 0; type_id < TYPE_ID_LIMIT; ++type_id) {
auto [n, n_2x1, n_1x1] = BLOCK_NUM[type_id];
Ranges ranges;
ranges.spawn(n, n_2x1, n_1x1);
EXPECT_EQ(ranges.size(), BASIC_RANGES_NUM[type_id]);
}
}
TEST_FF(BasicRanges, basic_ranges) {

8
src/core_test/cases/helper.h → src/core_test/cases/cases_helper.h

@ -1,9 +1,13 @@
#pragma once
#include <algorithm>
#include <gtest/gtest.h>
#include "exposer.h"
#include "all_cases.h"
#include "concurrent.h"
#include "gtest/gtest.h"
#include "all_cases/all_cases.h"
using klotski::array_sum;
using klotski::cases::AllCases;
using klotski::cases::BasicRanges;

149
src/core_test/cases/group_union.cc

@ -1,151 +1,31 @@
#include <algorithm>
#include <common_code.h>
#include <cstdint>
#include <iostream>
#include <format>
#include <utility.h>
#include <group/group.h>
#include <gtest/gtest.h>
struct block_num_t {
int n_1x1;
int n_1x2;
int n_2x1;
};
template <>
struct std::hash<block_num_t> {
size_t operator()(const block_num_t val) const noexcept {
return std::hash<int>()(val.n_1x1) ^ std::hash<int>()(val.n_1x2) ^ std::hash<int>()(val.n_2x1);
}
};
bool operator==(const block_num_t &lhs, const block_num_t &rhs) {
return lhs.n_1x1 == rhs.n_1x1 && lhs.n_1x2 == rhs.n_1x2 && lhs.n_2x1 == rhs.n_2x1;
}
uint32_t cal_flag(const block_num_t &block_num) {
return ((block_num.n_1x2 + block_num.n_2x1) << 7) | (block_num.n_2x1 << 3) | block_num.n_1x1;
}
std::vector<block_num_t> block_nums() {
std::vector<std::pair<uint32_t, block_num_t>> tmp;
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 <= (20 - 4 - 2 - n * 2); ++n_1x1) {
auto num = block_num_t {
.n_1x1 = n_1x1,
.n_1x2 = n_1x2,
.n_2x1 = n - n_1x2,
};
auto flag = cal_flag(num);
tmp.emplace_back(flag, num);
// std::cout << flag << std::endl;
}
}
}
std::sort(tmp.begin(), tmp.end(), [](const auto val1, const auto val2) {
return val1.first < val2.first;
});
// for (auto [x, _] : tmp) {
// std::cout << x << std::endl;
// }
std::vector<block_num_t> results;
std::ranges::transform(tmp, std::back_inserter(results), [](const auto val) {
return val.second;
});
// for (auto x : results) {
// std::cout << std::format("n_1x1 = {} / n_1x2 = {} / n_2x1 = {}", x.n_1x1, x.n_1x2, x.n_2x1) << std::endl;
// }
return results;
}
block_num_t cal_block_num(const uint64_t common_code) noexcept {
block_num_t result {};
auto range = klotski::range_reverse(static_cast<uint32_t>(common_code));
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;
}
uint32_t to_type_id(block_num_t block_num) {
auto f = []() {
auto ret = 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();
#include <format>
#include <cstdint>
#include <algorithm>
return data[block_num];
// TODO: only for debug
#include <iostream>
}
#include "group/group.h"
#include "helper/cases.h"
#include "common_code/common_code.h"
TEST(GroupUnion, demo) {
// kk();
auto kk = cal_block_num(0x1A9BF0C00);
std::cout << to_type_id(kk) << std::endl;
// for (auto i = 0; i < block_nums().size(); ++i) {
// // std::cout << block_nums()[i].n_2x1 + block_nums()[i].n_1x2 << std::endl;
//
// // std::cout << block_nums()[i].n_2x1 << std::endl;
//
// std::cout << block_nums()[i].n_2x1 + block_nums()[i].n_1x2 << ", ";
// std::cout << block_nums()[i].n_2x1 << ", ";
// std::cout << block_nums()[i].n_1x1 << std::endl;
// }
//
// return;
std::vector<std::vector<uint64_t>> pp;
pp.resize(block_nums().size());
// std::cout << pp.size() << std::endl;
for (uint64_t head = 0; head < 16; ++head) {
for (auto range : klotski::cases::AllCases::instance().fetch()[head]) {
for (auto range : AllCases::instance().fetch()[head]) {
uint64_t common_code = head << 32 | range;
auto type_id = to_type_id(cal_block_num(common_code));
@ -156,15 +36,9 @@ TEST(GroupUnion, demo) {
}
// std::cout << pp[0].size() << std::endl;
// std::cout << pp[169].size() << std::endl;
for (uint32_t type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) {
// std::cout << "start cases cal" << std::endl;
auto cases = klotski::cases::GroupUnion::unsafe_create(type_id).cases();
// std::cout << "end cases cal" << std::endl;
std::vector<uint64_t> extend {};
for (uint64_t head = 0; head < 16; ++head) {
@ -174,7 +48,6 @@ TEST(GroupUnion, demo) {
}
std::cout << "type_id " << type_id << " -> " << extend.size() << std::endl;
// std::cout << std::endl;
EXPECT_EQ(extend, pp[type_id]);
}

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

@ -0,0 +1,90 @@
#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;
}

76
src/core_test/cases/helper/cases.h

@ -0,0 +1,76 @@
#pragma once
#include <array>
#include <algorithm>
#include "group/group.h"
#include "all_cases/all_cases.h"
using klotski::cases::Ranges;
using klotski::cases::AllCases;
using klotski::codec::CommonCode;
using klotski::cases::BasicRanges;
using klotski::cases::RangesUnion;
using klotski::array_sum;
using klotski::range_reverse;
using klotski::cases::BLOCK_NUM;
using klotski::cases::ALL_CASES_NUM;
using klotski::cases::ALL_CASES_NUM;
using klotski::cases::BASIC_RANGES_NUM;
using klotski::cases::BASIC_RANGES_NUM_;
/// All valid klotski heads.
constexpr auto Heads = std::to_array({
0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14
});
// ------------------------------------------------------------------------------------- //
/// Assert that Ranges are sorted and unique.
#define EXPECT_SORTED_AND_UNIQUE(R) \
EXPECT_TRUE(std::ranges::is_sorted(R.begin(), R.end())); \
EXPECT_EQ(std::ranges::adjacent_find(R.begin(), R.end()), R.end()) // no duplicates
/// Assert that two ordered arrays are in a containment relationship.
#define EXPECT_SUBSET(R1, R2) \
EXPECT_TRUE(std::ranges::includes(R1.begin(), R1.end(), R2.begin(), R2.end()))
/// Assert that all CommonCodes generated from head and Ranges are valid.
#define EXPECT_COMMON_CODES(head, ranges) \
for (const auto range : ranges) \
EXPECT_TRUE(CommonCode::check(static_cast<uint64_t>(head) << 32 | range))
// ------------------------------------------------------------------------------------- //
/// The number of blocks in one klotski layout.
struct block_num_t {
int n_1x1;
int n_1x2;
int n_2x1;
};
template <>
struct std::hash<block_num_t> {
size_t operator()(const block_num_t val) const noexcept {
return (val.n_1x1 << 6) ^ (val.n_1x2 << 3) ^ val.n_2x1;
}
};
constexpr bool operator==(const block_num_t &lhs, const block_num_t &rhs) {
return lhs.n_1x1 == rhs.n_1x1 && lhs.n_1x2 == rhs.n_1x2 && lhs.n_2x1 == rhs.n_2x1;
}
// ------------------------------------------------------------------------------------- //
/// Calculate the block number from Range.
block_num_t cal_block_num(uint32_t range);
/// Calculate type id value from the block number.
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();

111
src/core_test/cases/ranges.cc

@ -0,0 +1,111 @@
#include <gtest/gtest.h>
#include "group/group.h"
#include "helper/cases.h"
#include "ranges/ranges.h"
static_assert(std::is_base_of_v<std::vector<uint32_t>, Ranges>);
TEST(Ranges, check) {
RangesUnion all_cases;
for (const auto head : Heads) {
for (auto range : BasicRanges::instance().fetch()) {
if (Ranges::check(head, range_reverse(range)) == 0) {
all_cases[head].emplace_back(range); // found valid cases
}
}
}
EXPECT_EQ(all_cases, AllCases::instance().fetch());
}
TEST(Ranges, spawn) {
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) {
Ranges ranges;
ranges.spawn(n, n_2x1, n_1x1);
EXPECT_SORTED_AND_UNIQUE(ranges); // sorted and unique
for (const auto range : ranges) {
const auto [val_1x1, val_1x2, val_2x1] = cal_block_num(range);
EXPECT_EQ(val_1x1, n_1x1);
EXPECT_EQ(val_2x1, n_2x1);
EXPECT_EQ(val_1x2 + val_2x1, n); // verify block nums
}
}
}
TEST(Ranges, derive) {
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) {
Ranges ranges;
ranges.spawn(n, n_2x1, n_1x1);
ranges.reverse(); // reverse ranges for derive
RangesUnion cases;
for (const auto head : Heads) {
ranges.derive(head, cases[head]);
EXPECT_SORTED_AND_UNIQUE(cases[head]); // sorted and unique
EXPECT_COMMON_CODES(head, cases[head]); // verify common codes
}
ranges.reverse();
for (const auto head : Heads) {
EXPECT_SUBSET(ranges, cases[head]); // derive ranges is subset
}
}
}
TEST(Ranges, reverse) {
auto ranges = BasicRanges::instance().fetch();
Ranges reverse {ranges};
for (auto &x : reverse) {
x = range_reverse(x); // manual reverse
}
ranges.reverse();
EXPECT_EQ(ranges, reverse);
ranges.reverse();
EXPECT_EQ(ranges, BasicRanges::instance().fetch());
}
TEST(Ranges, combine) {
Ranges all_ranges;
RangesUnion all_cases;
all_ranges.reserve(BASIC_RANGES_NUM_); // pre reserve
for (const auto head : Heads) {
all_cases[head].reserve(ALL_CASES_NUM[head]); // pre reserve
}
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) {
Ranges ranges;
ranges.spawn(n, n_2x1, n_1x1);
all_ranges += ranges;
ranges.reverse(); // reverse ranges for derive
for (const auto head : Heads) {
ranges.derive(head, all_cases[head]); // derive from sub ranges
}
}
std::ranges::stable_sort(all_ranges.begin(), all_ranges.end());
for (const auto head : Heads) {
std::ranges::stable_sort(all_cases[head].begin(), all_cases[head].end());
}
EXPECT_EQ(all_ranges, BasicRanges::instance().fetch()); // verify content
EXPECT_EQ(all_cases, AllCases::instance().fetch()); // verify content
all_ranges.reverse(); // reverse ranges for derive
for (const auto head : Heads) {
all_cases[head].clear();
all_ranges.derive(head, all_cases[head]); // derive from all ranges
}
EXPECT_EQ(all_cases, AllCases::instance().fetch()); // verify content
}
TEST(Ranges, operator) {
Ranges r, r1, r2;
r.spawn(5, 3, 4);
r.spawn(5, 4, 4);
r1.spawn(5, 3, 4);
r2.spawn(5, 4, 4);
EXPECT_EQ(r, r1 += r2);
}

35
src/core_test/cases/ranges_union.cc

@ -0,0 +1,35 @@
#include <gtest/gtest.h>
#include <format>
#include "hash.h"
#include "helper/cases.h"
#include "ranges/ranges.h"
static_assert(std::is_base_of_v<std::array<Ranges, 16>, RangesUnion>);
constexpr std::string_view ALL_CASES_MD5 = "3888e9fab8d3cbb50908b12b147cfb23";
TEST(RangesUnion, export) {
std::string buffer;
for (auto code : AllCases::instance().fetch().codes()) {
buffer += std::format("{:09X}\n", code.unwrap());
}
EXPECT_EQ(hash::md5(buffer), ALL_CASES_MD5);
}
TEST(RangesUnion, operator) {
Ranges r, r1, r2;
r.spawn(5, 3, 4);
r.spawn(5, 4, 4);
r1.spawn(5, 3, 4);
r2.spawn(5, 4, 4);
RangesUnion r_, r1_, r2_;
for (const auto head : Heads) {
r.derive(head, r_[head]);
r1.derive(head, r1_[head]);
r2.derive(head, r2_[head]);
}
EXPECT_EQ(r_, r1_ += r2_);
}
Loading…
Cancel
Save