Browse Source

refactor: enhance `FastCal` module

master
Dnomd343 3 weeks ago
parent
commit
052961aa77
  1. 17
      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

17
src/core/benchmark/fast_cal.cc

@ -8,19 +8,24 @@
using klotski::codec::CommonCode;
using klotski::fast_cal::FastCalPro;
using klotski::fast_cal::FastCal;
static void FastCalBenchmark(benchmark::State &state) {
auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code();
// auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code();
// auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code();
for (auto _ : state) {
auto fc = FastCalPro(code);
auto fc = FastCal(code);
benchmark::DoNotOptimize(fc.solve());
// benchmark::DoNotOptimize(fc.demo());
// benchmark::DoNotOptimize(FastCal_demo(code));
// auto tmp = klotski::cases::Group_extend(code);
// benchmark::DoNotOptimize(fc.solve_multi());
// benchmark::DoNotOptimize(fc.furthest());
// 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
// TODO: only copy from old implementation, the interfaces will change in future.
#pragma once
#include <vector>
#include <cstdint>
#include <functional>
#include <parallel_hashmap/phmap.h>
#include "mover/mover.h"
#include "layer_queue.h"
#include "raw_code/raw_code.h"
#include <parallel_hashmap/phmap.h>
namespace klotski::fast_cal {
class FastCalPro {
class FastCal {
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.
std::optional<codec::RawCode> solve();
/// Calculate all the maximum-step cases.
std::vector<codec::RawCode> furthest();
/// Calculate all the minimum-step cases.
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.
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;
codec::RawCode back;
codec::RawCode parent;
};
// codec::RawCode root_;
LayerQueue<codec::RawCode> codes_;
phmap::flat_hash_map<codec::RawCode, data_t> cases_;
LayerQueue<codec::RawCode> seeker_;
phmap::flat_hash_map<codec::RawCode, info_t> cases_;
// ------------------------------------------------------------------------------------- //
};

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

@ -1,110 +1,119 @@
#include "mover/mover.h"
#include "group/group.h"
#include "utils/common.h"
#include "fast_cal/fast_cal.h"
using klotski::codec::RawCode;
using klotski::codec::CommonCode;
using klotski::cases::RangesUnion;
using klotski::BLOCK_MSK;
using klotski::BLOCK_CE_2x2;
using klotski::codec::RawCode;
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() {
// if (is_solved(root_.unwrap())) {
// return root_;
// }
std::vector<RawCode> FastCal::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.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;
auto mover = MaskMover([this, &solution](RawCode code, uint64_t mask) {
RawCode solution {nil};
auto mover = MaskMover([this, &solution](const RawCode code, const uint64_t mask) {
if (try_emplace(code, mask) && is_solved(code)) {
solution = code.unwrap();
solution = code;
}
});
while (!codes_.is_ending()) {
while (!seeker_.is_ending()) {
spawn_next(mover);
if (solution != 0) {
return RawCode::unsafe_create(solution);
if (solution != nil) {
return solution;
}
}
return std::nullopt;
}
std::optional<RawCode> FastCalPro::search(std::function<bool(RawCode)> &&match) {
// TODO: check root case
std::optional<RawCode> FastCal::search(std::function<bool(RawCode)> &&match) {
if (match(seeker_.current())) {
return seeker_.current(); // root case matched
}
uint64_t target = 0;
auto mover = MaskMover([this, &target, match = std::move(match)](RawCode code, uint64_t mask) {
RawCode target {nil};
auto mover = MaskMover([this, &target, match = std::move(match)](const RawCode code, const uint64_t mask) {
if (try_emplace(code, mask) && match(code)) {
target = code.unwrap();
target = code;
}
});
while (!codes_.is_ending()) {
while (!seeker_.is_ending()) {
spawn_next(mover);
if (target != 0) {
return RawCode::unsafe_create(target);
if (target != nil) {
return target;
}
}
return std::nullopt;
}
std::vector<RawCode> FastCalPro::solve_multi() {
// TODO: check root case
std::vector<RawCode> FastCal::solve_multi() {
if (is_solved(seeker_.current())) {
return {seeker_.current()}; // root case solved
}
bool stop_flag = false;
std::vector<RawCode> results {};
auto mover = MaskMover([this, &stop_flag, &results](RawCode code, uint64_t mask) {
auto mover = MaskMover([this, &stop_flag, &results](const RawCode code, const uint64_t mask) {
if (try_emplace(code, mask) && is_solved(code)) {
stop_flag = true;
results.emplace_back(code);
}
});
while (!codes_.is_ending()) {
while (!seeker_.is_ending()) {
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 {};
}
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 {
inline FastCalPro::FastCalPro(codec::RawCode raw_code)
: codes_({raw_code}, cases::GroupUnion::from_raw_code(raw_code).max_group_size()) {
const auto reserve = cases::GroupUnion::from_raw_code(raw_code).max_group_size();
inline FastCal::FastCal(const codec::RawCode code)
: seeker_({code}, cases::GroupUnion::from_raw_code(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_.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()) {
match->second.mask |= mask; // update mask
return false;
}
cases_.emplace(code, data_t {
cases_.emplace(code, info_t {
.mask = mask,
.back = codes_.current(),
.parent = seeker_.current(),
});
codes_.emplace(code);
seeker_.emplace(code);
return true;
}
inline KLSK_INLINE void FastCalPro::spawn_next(mover::MaskMover &mover) {
auto curr = codes_.current();
inline KLSK_INLINE void FastCal::spawn_next(mover::MaskMover &mover) {
auto curr = seeker_.current();
mover.next_cases(curr, cases_.find(curr)->second.mask);
codes_.next();
seeker_.next();
}
} // 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) {
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_);
}
@ -66,9 +66,12 @@ template <typename T>
requires std::is_trivial_v<T>
std::vector<std::vector<T>> LayerQueue<T>::all_layers() const {
std::vector<std::vector<T>> result;
result.reserve(layer_offset_.size() - 1);
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(layer);
result.emplace_back(std::vector<T> {
data_ + layer_offset_[i],
data_ + layer_offset_[i + 1]
});
}
return result;
}

30
src/core/main.cc

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

2
src/core/raw_code/raw_code.h

@ -80,7 +80,7 @@ public:
explicit RawCode(CommonCode common_code);
/// 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.
static std::optional<RawCode> create(uint64_t raw_code);

13
src/core/utils/common.h

@ -21,6 +21,19 @@
// 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
#define BLOCK_space 0b000
#define BLOCK_fill 0b111

Loading…
Cancel
Save