|
|
@ -1,21 +1,22 @@ |
|
|
|
#include <iostream> |
|
|
|
|
|
|
|
#include "reverse.h" |
|
|
|
#include "utility.h" |
|
|
|
#include "all_cases.h" |
|
|
|
|
|
|
|
//typedef uint32_t Range;
|
|
|
|
|
|
|
|
using klotski::cases::BasicRanges; |
|
|
|
using klotski::cases::range_reverse; |
|
|
|
namespace klotski { |
|
|
|
namespace cases { |
|
|
|
|
|
|
|
inline int low_zero_num(uint32_t bin) { |
|
|
|
return __builtin_ctzl(bin); |
|
|
|
|
|
|
|
// WARN: be aware of serious performance issues
|
|
|
|
// return __builtin_popcount(~(bin ^ -bin)) - 1;
|
|
|
|
/// Calculate all possible klotski heads.
|
|
|
|
static consteval std::array<int, 12> case_heads() { |
|
|
|
std::array<int, 12> heads = {}; |
|
|
|
for (int i = 0, head = 0; head < 15; ++head) { |
|
|
|
if (head % 4 != 3) { |
|
|
|
heads[i++] = head; |
|
|
|
} |
|
|
|
} |
|
|
|
return heads; |
|
|
|
} |
|
|
|
|
|
|
|
int check_range(int head, uint32_t range) noexcept { |
|
|
|
/// Check whether the combination of head and range is valid.
|
|
|
|
static int check_range(int head, uint32_t range) noexcept { |
|
|
|
constexpr uint32_t MASK_1x1 = 0b00000001; |
|
|
|
constexpr uint32_t MASK_1x2 = 0b00000011; |
|
|
|
constexpr uint32_t MASK_2x1 = 0b00010001; |
|
|
@ -48,69 +49,59 @@ int check_range(int head, uint32_t range) noexcept { |
|
|
|
return 0; // pass check
|
|
|
|
} |
|
|
|
|
|
|
|
void demo() { |
|
|
|
|
|
|
|
// std::cout << __builtin_popcount(~(6 ^ -6)) - 1 << std::endl;
|
|
|
|
// return;
|
|
|
|
|
|
|
|
constexpr std::array<int, 12> heads = { |
|
|
|
0x0, 0x1, 0x2, |
|
|
|
0x4, 0x5, 0x6, |
|
|
|
0x8, 0x9, 0xA, |
|
|
|
0xC, 0xD, 0xE, |
|
|
|
}; |
|
|
|
|
|
|
|
auto num = 0; |
|
|
|
|
|
|
|
std::array<std::vector<uint32_t>, 16> data; |
|
|
|
|
|
|
|
// TODO: vector reserve
|
|
|
|
|
|
|
|
auto basic_ranges = BasicRanges::Instance().Fetch(); |
|
|
|
|
|
|
|
// std::vector<uint32_t> reversed {basic_ranges.begin(), basic_ranges.end()};
|
|
|
|
// for (auto &x : reversed) {
|
|
|
|
// x = range_reverse(x);
|
|
|
|
// }
|
|
|
|
|
|
|
|
for (auto head : heads) { |
|
|
|
for (uint32_t index = 0; index < basic_ranges.size(); ++index) { |
|
|
|
auto offset = check_range(head, basic_ranges[index]); |
|
|
|
if (offset) { // invalid case
|
|
|
|
auto tmp = (uint32_t)0b1 << (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(basic_ranges[index]) & ~(tmp - 1); |
|
|
|
while (range_reverse(basic_ranges[++index]) < tmp); // located next range
|
|
|
|
--index; |
|
|
|
|
|
|
|
// next += reversed[index] & ~(next - 1);
|
|
|
|
// auto kk = std::lower_bound(reversed.begin() + index, reversed.end(), next);
|
|
|
|
// index = kk - reversed.begin() - 1;
|
|
|
|
|
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
data[head].emplace_back(range_reverse(basic_ranges[index])); |
|
|
|
|
|
|
|
// ++index;
|
|
|
|
|
|
|
|
++num; |
|
|
|
// volatile auto r = range_reverse(basic_ranges[index]); // release valid cases
|
|
|
|
/// Build all valid ranges of the specified head.
|
|
|
|
void AllCases::BuildCases(int head, Ranges &basic_ranges, Ranges &release) noexcept { |
|
|
|
release.reserve(ALL_CASES_NUM[head]); |
|
|
|
for (uint32_t index = 0; index < basic_ranges.size(); ++index) { |
|
|
|
auto offset = check_range(head, basic_ranges[index]); |
|
|
|
if (offset) { // invalid case
|
|
|
|
auto tmp = (uint32_t)0b1 << (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(basic_ranges[index]) & ~(tmp - 1); |
|
|
|
while (range_reverse(basic_ranges[++index]) < tmp); // located next range
|
|
|
|
--index; |
|
|
|
continue; |
|
|
|
} |
|
|
|
release.emplace_back(range_reverse(basic_ranges[index])); // release valid case
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// for (auto &x : data) {
|
|
|
|
// std::cout << x.size() << std::endl;
|
|
|
|
// }
|
|
|
|
|
|
|
|
for (uint64_t head = 0; head < 15; ++head) { |
|
|
|
for (auto x : data[head]) { |
|
|
|
printf("%09llX\n", (head << 32) | x); |
|
|
|
void AllCases::Build() noexcept { |
|
|
|
if (available_) { // data already available
|
|
|
|
return; |
|
|
|
} |
|
|
|
if (building_.try_lock()) { // mutex lock success
|
|
|
|
if (available_) { |
|
|
|
building_.unlock(); |
|
|
|
return; |
|
|
|
} |
|
|
|
auto basic_ranges = BasicRanges::Instance().Fetch(); |
|
|
|
for (auto head : case_heads()) { |
|
|
|
BuildCases(head, basic_ranges, GetCases()[head]); |
|
|
|
} |
|
|
|
available_ = true; |
|
|
|
} else { |
|
|
|
building_.lock(); // blocking waiting
|
|
|
|
} |
|
|
|
building_.unlock(); // release mutex
|
|
|
|
} |
|
|
|
|
|
|
|
// std::cout << num << std::endl;
|
|
|
|
AllRanges& AllCases::GetCases() noexcept { |
|
|
|
static std::array<Ranges, 16> cases; |
|
|
|
return cases; |
|
|
|
} |
|
|
|
|
|
|
|
AllCases& AllCases::Instance() noexcept { |
|
|
|
static AllCases instance; |
|
|
|
return instance; |
|
|
|
} |
|
|
|
|
|
|
|
AllRanges& AllCases::Fetch() noexcept { |
|
|
|
this->Build(); |
|
|
|
return GetCases(); |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace cases
|
|
|
|
} // namespace klotski
|
|
|
|