华容道高性能计算引擎
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

149 lines
4.8 KiB

#include <future>
#include "all_cases.h"
namespace klotski {
namespace cases {
/// 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;
}
/// 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;
constexpr uint32_t MASK_2x2 = 0b00110011;
uint32_t flags = MASK_2x2 << head; // fill 2x2 block
for (int addr = 0, offset = 1; range; range >>= 2, ++offset) { // traverse every 2-bit
auto num = low_zero_num(~flags);
addr += num; // next unfilled block
flags >>= num;
switch (range & 0b11) {
case 0b00: // space
case 0b11: // 1x1 block
flags |= MASK_1x1;
continue;
case 0b10: // 2x1 block
if ((flags >> 4) & 0b1 || addr > 15) { // invalid case
return offset; // broken offset
}
flags |= MASK_2x1;
continue;
case 0b01: // 1x2 block
if ((flags >> 1) & 0b1 || (addr & 0b11) == 0b11) { // invalid case
return offset; // broken offset
}
flags |= MASK_1x2;
continue;
}
}
return 0; // pass check
}
/// Build all valid ranges of the specified head.
void AllCases::BuildCases(int head, Ranges &release) noexcept {
release.reserve(ALL_CASES_NUM[head]);
auto &basic_ranges = BasicRanges::Instance().Fetch();
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
}
}
/// Execute the build process and ensure thread safety.
void AllCases::Build() noexcept {
BuildParallel([](auto &&func) {
func();
});
}
/// Execute the build process in parallel without blocking.
void AllCases::BuildParallelAsync(Executor &&executor, Notifier &&callback) noexcept {
if (available_) {
return; // reduce consumption of mutex
}
building_.lock();
if (available_) {
building_.unlock();
return; // data is already available
}
auto counter = std::make_shared<std::atomic<int>>(0);
auto all_done = std::make_shared<Notifier>(std::move(callback));
for (auto head : case_heads()) {
executor([this, head, counter, all_done]() {
BuildCases(head, GetCases()[head]);
if (counter->fetch_add(1) == case_heads().size() - 1) {
available_ = true;
building_.unlock(); // release building mutex
all_done->operator()(); // trigger callback
}
});
}
}
/// Execute the build process with parallel support and ensure thread safety.
void AllCases::BuildParallel(Executor &&executor) noexcept {
if (available_) {
return; // reduce consumption of mutex
}
std::lock_guard<std::mutex> guard(building_);
if (available_) {
return; // data is already available
}
std::vector<std::future<void>> futures;
for (auto head : case_heads()) {
// TODO: using std::move_only_function in C++23
// -> avoid using std::shared_ptr<std::promise<void>>
auto promise = std::make_shared<std::promise<void>>();
futures.emplace_back(promise->get_future());
executor([head, promise = std::move(promise)]() {
BuildCases(head, GetCases()[head]);
promise->set_value(); // subtask completed notification
});
}
for (auto &x : futures) {
x.get(); // wait until all subtasks completed
}
available_ = true;
}
RangesUnion& AllCases::GetCases() noexcept {
static RangesUnion cases;
return cases;
}
AllCases& AllCases::Instance() noexcept {
static AllCases instance;
return instance;
}
const RangesUnion& AllCases::Fetch() noexcept {
this->Build();
return GetCases();
}
bool AllCases::IsAvailable() const noexcept {
return available_;
}
} // namespace cases
} // namespace klotski