Browse Source

refactor: enhance `FastCal` module

master
Dnomd343 3 weeks ago
parent
commit
052961aa77
  1. 15
      src/core/benchmark/fast_cal.cc
  2. 41
      src/core/fast_cal/fast_cal.h
  3. 141
      src/core/fast_cal/internal/fast_cal.cc
  4. 22
      src/core/fast_cal/internal/fast_cal.inl
  5. 9
      src/core/fast_cal/internal/layer_queue.inl
  6. 30
      src/core/main.cc
  7. 2
      src/core/raw_code/internal/raw_code.inl
  8. 2
      src/core/raw_code/raw_code.h
  9. 13
      src/core/utils/common.h

15
src/core/benchmark/fast_cal.cc

@ -8,7 +8,7 @@
using klotski::codec::CommonCode; using klotski::codec::CommonCode;
using klotski::fast_cal::FastCalPro; using klotski::fast_cal::FastCal;
static void FastCalBenchmark(benchmark::State &state) { static void FastCalBenchmark(benchmark::State &state) {
@ -16,11 +16,16 @@ static void FastCalBenchmark(benchmark::State &state) {
// auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code(); // auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code();
for (auto _ : state) { for (auto _ : state) {
auto fc = FastCalPro(code); auto fc = FastCal(code);
benchmark::DoNotOptimize(fc.solve()); benchmark::DoNotOptimize(fc.solve());
// benchmark::DoNotOptimize(fc.demo()); // benchmark::DoNotOptimize(fc.solve_multi());
// benchmark::DoNotOptimize(FastCal_demo(code)); // benchmark::DoNotOptimize(fc.furthest());
// auto tmp = klotski::cases::Group_extend(code); // fc.search([](const klotski::codec::RawCode tmp) {
// return tmp == 0x7F87E0E5BFFF492;
// // return tmp == 0x1FB1E37F9E9F5CA;
// });
// fc.build_all();
} }
} }

41
src/core/fast_cal/fast_cal.h

@ -1,42 +1,39 @@
/// Klotski Engine by Dnomd343 @2024 /// Klotski Engine by Dnomd343 @2024
// TODO: only copy from old implementation, the interfaces will change in future.
#pragma once #pragma once
#include <vector>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <parallel_hashmap/phmap.h>
#include "mover/mover.h" #include "mover/mover.h"
#include "layer_queue.h" #include "layer_queue.h"
#include "raw_code/raw_code.h" #include "raw_code/raw_code.h"
#include <parallel_hashmap/phmap.h>
namespace klotski::fast_cal { namespace klotski::fast_cal {
class FastCalPro { class FastCal {
public: public:
FastCalPro() = delete; FastCal() = delete;
explicit FastCalPro(codec::RawCode raw_code); explicit FastCal(codec::RawCode code);
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
// TODO: add global build
void build();
/// Calculate a minimum-step case. /// Calculate a minimum-step case.
std::optional<codec::RawCode> solve(); std::optional<codec::RawCode> solve();
/// Calculate all the maximum-step cases.
std::vector<codec::RawCode> furthest();
/// Calculate all the minimum-step cases. /// Calculate all the minimum-step cases.
std::vector<codec::RawCode> solve_multi(); std::vector<codec::RawCode> solve_multi();
// ------------------------------------------------------------------------------------- //
/// Calculate all cases.
void build_all();
/// Calculate all the maximum-step cases.
std::vector<codec::RawCode> furthest();
/// Calculate the first case that meets the requirement. /// Calculate the first case that meets the requirement.
std::optional<codec::RawCode> search(std::function<bool(codec::RawCode)> &&match); std::optional<codec::RawCode> search(std::function<bool(codec::RawCode)> &&match);
@ -61,16 +58,18 @@ private:
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
// TODO: add invalid RawCode constexpr var /// Using non-existed RawCode value to represent NULL.
static constexpr auto nil = codec::RawCode::unsafe_create(0);
// ------------------------------------------------------------------------------------- //
struct data_t { struct info_t {
uint64_t mask; uint64_t mask;
codec::RawCode back; codec::RawCode parent;
}; };
// codec::RawCode root_; LayerQueue<codec::RawCode> seeker_;
LayerQueue<codec::RawCode> codes_; phmap::flat_hash_map<codec::RawCode, info_t> cases_;
phmap::flat_hash_map<codec::RawCode, data_t> cases_;
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
}; };

141
src/core/fast_cal/internal/fast_cal.cc

@ -1,110 +1,119 @@
#include "mover/mover.h" #include "utils/common.h"
#include "group/group.h"
#include "fast_cal/fast_cal.h" #include "fast_cal/fast_cal.h"
using klotski::codec::RawCode; using klotski::BLOCK_MSK;
using klotski::codec::CommonCode; using klotski::BLOCK_CE_2x2;
using klotski::cases::RangesUnion;
using klotski::codec::RawCode;
using klotski::mover::MaskMover; using klotski::mover::MaskMover;
using klotski::cases::GroupUnion; using klotski::fast_cal::FastCal;
// ----------------------------------------------------------------------------------------- //
void FastCal::build_all() {
auto mover = MaskMover([this](const RawCode code, const uint64_t mask) {
try_emplace(code, mask);
});
while (!seeker_.is_ending()) {
spawn_next(mover);
}
}
using klotski::fast_cal::FastCalPro; std::vector<RawCode> FastCal::furthest() {
if (!seeker_.is_ending()) {
build_all();
}
return seeker_.last_layer();
}
static KLSK_INLINE bool is_solved(RawCode raw_code) { // ----------------------------------------------------------------------------------------- //
return ((raw_code.unwrap() >> 39) & 0b111) == 0b100;
std::vector<std::vector<RawCode>> FastCal::exports() const {
return seeker_.all_layers();
} }
std::optional<RawCode> FastCalPro::solve() { std::vector<RawCode> FastCal::backtrack(RawCode code) const {
// if (is_solved(root_.unwrap())) { if (const auto match = cases_.find(code); match == cases_.end()) {
// return root_; return {}; // case not found
// } }
std::vector<RawCode> path;
while (code != 0) {
path.emplace_back(code);
code = cases_.find(code)->second.parent;
}
std::reverse(path.begin(), path.end());
return path;
}
// ----------------------------------------------------------------------------------------- //
static KLSK_INLINE bool is_solved(const RawCode raw_code) {
return ((raw_code.unwrap() >> 39) & BLOCK_MSK) == BLOCK_CE_2x2;
}
std::optional<RawCode> FastCal::solve() {
if (is_solved(seeker_.current())) {
return seeker_.current(); // root case solved
}
uint64_t solution = 0; RawCode solution {nil};
auto mover = MaskMover([this, &solution](RawCode code, uint64_t mask) { auto mover = MaskMover([this, &solution](const RawCode code, const uint64_t mask) {
if (try_emplace(code, mask) && is_solved(code)) { if (try_emplace(code, mask) && is_solved(code)) {
solution = code.unwrap(); solution = code;
} }
}); });
while (!codes_.is_ending()) {
while (!seeker_.is_ending()) {
spawn_next(mover); spawn_next(mover);
if (solution != 0) { if (solution != nil) {
return RawCode::unsafe_create(solution); return solution;
} }
} }
return std::nullopt; return std::nullopt;
} }
std::optional<RawCode> FastCalPro::search(std::function<bool(RawCode)> &&match) { std::optional<RawCode> FastCal::search(std::function<bool(RawCode)> &&match) {
// TODO: check root case if (match(seeker_.current())) {
return seeker_.current(); // root case matched
}
uint64_t target = 0; RawCode target {nil};
auto mover = MaskMover([this, &target, match = std::move(match)](RawCode code, uint64_t mask) { auto mover = MaskMover([this, &target, match = std::move(match)](const RawCode code, const uint64_t mask) {
if (try_emplace(code, mask) && match(code)) { if (try_emplace(code, mask) && match(code)) {
target = code.unwrap(); target = code;
} }
}); });
while (!codes_.is_ending()) {
while (!seeker_.is_ending()) {
spawn_next(mover); spawn_next(mover);
if (target != 0) { if (target != nil) {
return RawCode::unsafe_create(target); return target;
} }
} }
return std::nullopt; return std::nullopt;
} }
std::vector<RawCode> FastCalPro::solve_multi() { std::vector<RawCode> FastCal::solve_multi() {
// TODO: check root case if (is_solved(seeker_.current())) {
return {seeker_.current()}; // root case solved
}
bool stop_flag = false; bool stop_flag = false;
std::vector<RawCode> results {}; std::vector<RawCode> results {};
auto mover = MaskMover([this, &stop_flag, &results](const RawCode code, const uint64_t mask) {
auto mover = MaskMover([this, &stop_flag, &results](RawCode code, uint64_t mask) {
if (try_emplace(code, mask) && is_solved(code)) { if (try_emplace(code, mask) && is_solved(code)) {
stop_flag = true; stop_flag = true;
results.emplace_back(code); results.emplace_back(code);
} }
}); });
while (!codes_.is_ending()) { while (!seeker_.is_ending()) {
spawn_next(mover); spawn_next(mover);
if (stop_flag && (codes_.is_new_layer() || codes_.is_ending())) { if (stop_flag && (seeker_.is_new_layer() || seeker_.is_ending())) {
return results; return results;
} }
} }
return {}; return {};
} }
void FastCalPro::build() { // ----------------------------------------------------------------------------------------- //
auto mover = MaskMover([this](RawCode code, uint64_t mask) {
try_emplace(code, mask);
});
while (!codes_.is_ending()) {
spawn_next(mover);
}
}
std::vector<RawCode> FastCalPro::furthest() {
if (!codes_.is_ending()) {
build();
}
return codes_.last_layer();
}
std::vector<RawCode> FastCalPro::backtrack(RawCode code) const {
if (const auto match = cases_.find(code); match == cases_.end()) {
return {}; // case not found
}
std::vector<RawCode> path;
while (code != 0) {
path.emplace_back(code);
code = cases_.find(code)->second.back;
}
std::reverse(path.begin(), path.end());
return path;
}
std::vector<std::vector<RawCode>> FastCalPro::exports() const {
return codes_.all_layers();
}

22
src/core/fast_cal/internal/fast_cal.inl

@ -4,30 +4,30 @@
namespace klotski::fast_cal { namespace klotski::fast_cal {
inline FastCalPro::FastCalPro(codec::RawCode raw_code) inline FastCal::FastCal(const codec::RawCode code)
: codes_({raw_code}, cases::GroupUnion::from_raw_code(raw_code).max_group_size()) { : seeker_({code}, cases::GroupUnion::from_raw_code(code).max_group_size()) {
const auto reserve = cases::GroupUnion::from_raw_code(raw_code).max_group_size(); const auto reserve = cases::GroupUnion::from_raw_code(code).max_group_size();
cases_.reserve(static_cast<size_t>(reserve * 1.56)); cases_.reserve(static_cast<size_t>(reserve * 1.56));
cases_.emplace(raw_code, data_t {0, codec::RawCode::unsafe_create(0)}); // without mask cases_.emplace(code, info_t {0, nil}); // without mask
} }
inline KLSK_INLINE bool FastCalPro::try_emplace(codec::RawCode code, uint64_t mask) { inline KLSK_INLINE bool FastCal::try_emplace(const codec::RawCode code, const uint64_t mask) {
if (const auto match = cases_.find(code); match != cases_.end()) { if (const auto match = cases_.find(code); match != cases_.end()) {
match->second.mask |= mask; // update mask match->second.mask |= mask; // update mask
return false; return false;
} }
cases_.emplace(code, data_t { cases_.emplace(code, info_t {
.mask = mask, .mask = mask,
.back = codes_.current(), .parent = seeker_.current(),
}); });
codes_.emplace(code); seeker_.emplace(code);
return true; return true;
} }
inline KLSK_INLINE void FastCalPro::spawn_next(mover::MaskMover &mover) { inline KLSK_INLINE void FastCal::spawn_next(mover::MaskMover &mover) {
auto curr = codes_.current(); auto curr = seeker_.current();
mover.next_cases(curr, cases_.find(curr)->second.mask); mover.next_cases(curr, cases_.find(curr)->second.mask);
codes_.next(); seeker_.next();
} }
} // namespace klotski::fast_cal } // namespace klotski::fast_cal

9
src/core/fast_cal/internal/layer_queue.inl

@ -10,7 +10,7 @@ LayerQueue<T>::LayerQueue(std::initializer_list<T> first_layer, const size_t max
for (const auto node : first_layer) { for (const auto node : first_layer) {
emplace(node); emplace(node);
} }
layer_offset_.reserve(139); // TODO: confirm the max layer number layer_offset_.reserve(232); // TODO: confirm the max layer number
layer_offset_.emplace_back(layer_end_); layer_offset_.emplace_back(layer_end_);
} }
@ -66,9 +66,12 @@ template <typename T>
requires std::is_trivial_v<T> requires std::is_trivial_v<T>
std::vector<std::vector<T>> LayerQueue<T>::all_layers() const { std::vector<std::vector<T>> LayerQueue<T>::all_layers() const {
std::vector<std::vector<T>> result; std::vector<std::vector<T>> result;
result.reserve(layer_offset_.size() - 1);
for (size_t i = 0; i < layer_offset_.size() - 1; ++i) { for (size_t i = 0; i < layer_offset_.size() - 1; ++i) {
std::vector<T> layer {data_ + layer_offset_[i], data_ + layer_offset_[i + 1]}; result.emplace_back(std::vector<T> {
result.emplace_back(layer); data_ + layer_offset_[i],
data_ + layer_offset_[i + 1]
});
} }
return result; return result;
} }

30
src/core/main.cc

@ -19,6 +19,7 @@
#include "../../third_party/thread-pool/include/BS_thread_pool.hpp" #include "../../third_party/thread-pool/include/BS_thread_pool.hpp"
using klotski::mover::MaskMover; using klotski::mover::MaskMover;
using klotski::fast_cal::FastCal;
using klotski::cases::AllCases; using klotski::cases::AllCases;
using klotski::cases::BasicRanges; using klotski::cases::BasicRanges;
@ -48,24 +49,25 @@ int main() {
const auto start = std::chrono::system_clock::now(); const auto start = std::chrono::system_clock::now();
auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code(); // const auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code();
klotski::fast_cal::FastCalPro fc {code}; const auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code();
FastCal fc {code};
std::cout << fc.solve().value() << std::endl; // std::cout << fc.solve().value() << std::endl;
// for (auto x : fc.solve_multi()) { for (const auto x : fc.solve_multi()) {
// std::cout << x << std::endl; std::cout << x.to_common_code() << std::endl;
// } }
// for (auto x : fc.furthest()) { for (const auto x : fc.furthest()) {
// std::cout << x.to_common_code() << std::endl; std::cout << x.to_common_code() << std::endl;
// } }
// fc.furthest(); fc.build_all();
// for (const auto &layer : fc.exports()) { for (const auto &layer : fc.exports()) {
// std::cout << layer.size() << std::endl; std::cout << layer.size() << std::endl;
// } }
// std::cout << "layer num: " << fc.exports().size() << std::endl; std::cout << "layer num: " << fc.exports().size() << std::endl;
// for (int i = 0; i < 10000000; ++i) { // for (int i = 0; i < 10000000; ++i) {
// MaskMover mover([](uint64_t code, uint64_t mask) { // MaskMover mover([](uint64_t code, uint64_t mask) {

2
src/core/raw_code/internal/raw_code.inl

@ -10,7 +10,7 @@ inline RawCode::RawCode(const CommonCode common_code) {
code_ = extract(common_code.unwrap()); code_ = extract(common_code.unwrap());
} }
inline RawCode RawCode::unsafe_create(const uint64_t raw_code) { constexpr RawCode RawCode::unsafe_create(const uint64_t raw_code) {
return std::bit_cast<RawCode>(raw_code); // init directly return std::bit_cast<RawCode>(raw_code); // init directly
} }

2
src/core/raw_code/raw_code.h

@ -80,7 +80,7 @@ public:
explicit RawCode(CommonCode common_code); explicit RawCode(CommonCode common_code);
/// Create RawCode without any check. /// Create RawCode without any check.
static RawCode unsafe_create(uint64_t raw_code); static constexpr RawCode unsafe_create(uint64_t raw_code);
/// Create RawCode with validity check. /// Create RawCode with validity check.
static std::optional<RawCode> create(uint64_t raw_code); static std::optional<RawCode> create(uint64_t raw_code);

13
src/core/utils/common.h

@ -21,6 +21,19 @@
// TODO: using constexpr // TODO: using constexpr
namespace klotski {
// TODO: remove `CE` tag
constexpr uint_fast8_t BLOCK_SPC {0b000}; // space
constexpr uint_fast8_t BLOCK_FIL {0b111}; // fill
constexpr uint_fast8_t BLOCK_CE_1x2 {0b001};
constexpr uint_fast8_t BLOCK_CE_2x1 {0b010};
constexpr uint_fast8_t BLOCK_CE_1x1 {0b011};
constexpr uint_fast8_t BLOCK_CE_2x2 {0b100};
constexpr uint_fast8_t BLOCK_MSK {0b111}; // mask
} // namespace klotski
/// NOTE: 0b101 and 0b110 are reserved /// NOTE: 0b101 and 0b110 are reserved
#define BLOCK_space 0b000 #define BLOCK_space 0b000
#define BLOCK_fill 0b111 #define BLOCK_fill 0b111

Loading…
Cancel
Save