Browse Source

feat: add AllCases module

master
Dnomd343 1 year 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)
include_directories(utils)
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 "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

43
src/core/all_cases/all_cases.h

@ -38,20 +38,31 @@
#include <mutex>
#include <vector>
#include <cstdint>
void demo();
#include <numeric>
namespace klotski {
namespace cases {
typedef uint32_t Range;
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 {
public:
void Build();
void Build() noexcept;
const Ranges& Fetch() noexcept;
bool IsAvailable() const noexcept;
@ -68,9 +79,29 @@ private:
BasicRanges() = default;
static Ranges& GetRanges() noexcept;
static void BuildRanges(Ranges &ranges);
static void SpawnRanges(Ranges&, int, int, int, int) noexcept;
static void BuildRanges(Ranges &ranges) 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 klotski

27
src/core/all_cases/basic_ranges.cc

@ -1,7 +1,7 @@
#include <list>
#include <algorithm>
#include "reverse.h"
#include "utility.h"
#include "all_cases.h"
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.
void BasicRanges::BuildRanges(Ranges &ranges) {
void BasicRanges::BuildRanges(Ranges &ranges) noexcept {
ranges.clear();
ranges.reserve(BASIC_RANGES_NUM);
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.
void BasicRanges::Build() {
if (!available_) {
if (building_.try_lock()) { // mutex lock success
BuildRanges(GetRanges());
available_ = true;
} else {
building_.lock(); // blocking waiting
void BasicRanges::Build() noexcept {
if (available_) {
return; // reduce consumption of mutex
}
if (building_.try_lock()) { // mutex lock success
if (available_) {
building_.unlock();
return;
}
building_.unlock(); // release mutex
BuildRanges(GetRanges());
available_ = true;
} else {
building_.lock(); // blocking waiting
}
building_.unlock(); // release mutex
}
Ranges& BasicRanges::GetRanges() noexcept {
@ -108,7 +113,7 @@ BasicRanges& BasicRanges::Instance() noexcept {
}
const Ranges& BasicRanges::Fetch() noexcept {
Build();
this->Build();
return GetRanges();
}

10
src/core/main.cc

@ -2,6 +2,7 @@
#include "all_cases/all_cases.h"
using klotski::cases::AllCases;
using klotski::cases::BasicRanges;
int main() {
@ -10,8 +11,6 @@ int main() {
auto start = clock();
demo();
// std::cout << BasicRanges::Instance().IsAvailable() << std::endl;
// BasicRanges::Instance().Build();
// std::cout << BasicRanges::Instance().IsAvailable() << std::endl;
@ -19,6 +18,13 @@ int main() {
// 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;
return 0;

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

@ -1,7 +1,15 @@
#pragma once
#include <cstdint>
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 {
#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);
}
} // namespace cases
} // namespace klotski
Loading…
Cancel
Save