Browse Source

feat: add AllCases module

master
Dnomd343 10 months ago
parent
commit
f8a6ba673e
  1. 2
      src/core/CMakeLists.txt
  2. 133
      src/core/all_cases/all_cases.cc
  3. 43
      src/core/all_cases/all_cases.h
  4. 27
      src/core/all_cases/basic_ranges.cc
  5. 10
      src/core/main.cc
  6. 11
      src/core/utils/utility.h

2
src/core/CMakeLists.txt

@ -6,4 +6,6 @@ project(klotski-core VERSION 0.1.2 LANGUAGES CXX)
add_compile_options(-fno-exceptions) add_compile_options(-fno-exceptions)
include_directories(utils)
add_executable(${PROJECT_NAME} main.cc all_cases/basic_ranges.cc all_cases/all_cases.cc) add_executable(${PROJECT_NAME} main.cc all_cases/basic_ranges.cc all_cases/all_cases.cc)

133
src/core/all_cases/all_cases.cc

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

43
src/core/all_cases/all_cases.h

@ -38,20 +38,31 @@
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
#include <numeric>
void demo();
namespace klotski { namespace klotski {
namespace cases { namespace cases {
typedef uint32_t Range; typedef uint32_t Range;
typedef std::vector<Range> Ranges; typedef std::vector<Range> Ranges;
typedef std::array<Ranges, 16> AllRanges;
constexpr auto BASIC_RANGES_NUM = 7311921;
constexpr std::array<int, 16> ALL_CASES_NUM {
2942906, 2260392, 2942906, 0,
2322050, 1876945, 2322050, 0,
2322050, 1876945, 2322050, 0,
2942906, 2260392, 2942906, 0,
};
const auto BASIC_RANGES_NUM = 7311921; constexpr auto ALL_CASES_NUM_ = std::accumulate(
ALL_CASES_NUM.begin(), ALL_CASES_NUM.end(), 0
);
class BasicRanges { class BasicRanges {
public: public:
void Build(); void Build() noexcept;
const Ranges& Fetch() noexcept; const Ranges& Fetch() noexcept;
bool IsAvailable() const noexcept; bool IsAvailable() const noexcept;
@ -68,9 +79,29 @@ private:
BasicRanges() = default; BasicRanges() = default;
static Ranges& GetRanges() noexcept; static Ranges& GetRanges() noexcept;
static void BuildRanges(Ranges &ranges); static void BuildRanges(Ranges &ranges) noexcept;
static void SpawnRanges(Ranges&, int, int, int, int) noexcept; static void SpawnRanges(Ranges &ranges, int, int, int, int) noexcept;
};
class AllCases {
public:
// void Build();
void Build() noexcept;
AllRanges& Fetch() noexcept;
static AllCases& Instance() noexcept;
private:
std::mutex building_;
bool available_ = false;
static AllRanges& GetCases() noexcept;
static void BuildCases(int head, Ranges &basic_ranges, Ranges &release) noexcept;
}; };
void demo();
} // namespace cases } // namespace cases
} // namespace klotski } // namespace klotski

27
src/core/all_cases/basic_ranges.cc

@ -1,7 +1,7 @@
#include <list> #include <list>
#include <algorithm> #include <algorithm>
#include "reverse.h" #include "utility.h"
#include "all_cases.h" #include "all_cases.h"
namespace klotski { namespace klotski {
@ -61,7 +61,7 @@ void BasicRanges::SpawnRanges(Ranges &ranges, int n1, int n2, int n3, int n4) no
} }
/// Search and sort all possible basic-ranges permutations. /// Search and sort all possible basic-ranges permutations.
void BasicRanges::BuildRanges(Ranges &ranges) { void BasicRanges::BuildRanges(Ranges &ranges) noexcept {
ranges.clear(); ranges.clear();
ranges.reserve(BASIC_RANGES_NUM); ranges.reserve(BASIC_RANGES_NUM);
std::list<RangeIter> flags {ranges.begin()}; // mark ordered interval std::list<RangeIter> flags {ranges.begin()}; // mark ordered interval
@ -85,16 +85,21 @@ void BasicRanges::BuildRanges(Ranges &ranges) {
} }
/// Execute the build process and ensure thread safety. /// Execute the build process and ensure thread safety.
void BasicRanges::Build() { void BasicRanges::Build() noexcept {
if (!available_) { if (available_) {
if (building_.try_lock()) { // mutex lock success return; // reduce consumption of mutex
BuildRanges(GetRanges()); }
available_ = true; if (building_.try_lock()) { // mutex lock success
} else { if (available_) {
building_.lock(); // blocking waiting building_.unlock();
return;
} }
building_.unlock(); // release mutex BuildRanges(GetRanges());
available_ = true;
} else {
building_.lock(); // blocking waiting
} }
building_.unlock(); // release mutex
} }
Ranges& BasicRanges::GetRanges() noexcept { Ranges& BasicRanges::GetRanges() noexcept {
@ -108,7 +113,7 @@ BasicRanges& BasicRanges::Instance() noexcept {
} }
const Ranges& BasicRanges::Fetch() noexcept { const Ranges& BasicRanges::Fetch() noexcept {
Build(); this->Build();
return GetRanges(); return GetRanges();
} }

10
src/core/main.cc

@ -2,6 +2,7 @@
#include "all_cases/all_cases.h" #include "all_cases/all_cases.h"
using klotski::cases::AllCases;
using klotski::cases::BasicRanges; using klotski::cases::BasicRanges;
int main() { int main() {
@ -10,8 +11,6 @@ int main() {
auto start = clock(); auto start = clock();
demo();
// std::cout << BasicRanges::Instance().IsAvailable() << std::endl; // std::cout << BasicRanges::Instance().IsAvailable() << std::endl;
// BasicRanges::Instance().Build(); // BasicRanges::Instance().Build();
// std::cout << BasicRanges::Instance().IsAvailable() << std::endl; // std::cout << BasicRanges::Instance().IsAvailable() << std::endl;
@ -19,6 +18,13 @@ int main() {
// printf("%08X\n", x); // printf("%08X\n", x);
// } // }
AllCases::Instance().Build();
for (uint64_t head = 0; head < 15; ++head) {
for (auto range : AllCases::Instance().Fetch()[head]) {
printf("%09llX\n", head << 32 | range);
}
}
std::cerr << ((clock() - start) * 1000 / CLOCKS_PER_SEC) << "ms" << std::endl; std::cerr << ((clock() - start) * 1000 / CLOCKS_PER_SEC) << "ms" << std::endl;
return 0; return 0;

11
src/core/all_cases/reverse.h → src/core/utils/utility.h

@ -1,7 +1,15 @@
#pragma once #pragma once
#include <cstdint>
namespace klotski { 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;
}
inline uint32_t range_reverse(uint32_t bin) noexcept { inline uint32_t range_reverse(uint32_t bin) noexcept {
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
@ -16,5 +24,4 @@ inline uint32_t range_reverse(uint32_t bin) noexcept {
return ((bin << 2) & 0xCCCCCCCC) | ((bin >> 2) & 0x33333333); return ((bin << 2) & 0xCCCCCCCC) | ((bin >> 2) & 0x33333333);
} }
} // namespace cases
} // namespace klotski } // namespace klotski
Loading…
Cancel
Save