From 83c4a53944ad5cc7826cd55e77ede1b35b65ee01 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sun, 22 Dec 2024 16:31:27 +0800 Subject: [PATCH] feat: add analyse module --- src/core/CMakeLists.txt | 3 + src/core/analyse/analyse.cc | 116 ++++++++++++++++++++++++++++++++++ src/core/analyse/analyse.h | 77 ++++++++++++++++++++++ src/core/analyse/backtrack.cc | 77 ++++++++++++++++++++++ src/core/main.cc | 15 +++++ 5 files changed, 288 insertions(+) create mode 100644 src/core/analyse/analyse.cc create mode 100644 src/core/analyse/analyse.h create mode 100644 src/core/analyse/backtrack.cc diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6cd7662..b91a904 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -28,6 +28,9 @@ set(KLSK_CORE_SRC group/internal/group_cases.cc fast_cal/internal/fast_cal.cc + + analyse/analyse.cc + analyse/backtrack.cc ) add_library(klotski_core STATIC ${KLSK_CORE_SRC}) diff --git a/src/core/analyse/analyse.cc b/src/core/analyse/analyse.cc new file mode 100644 index 0000000..6405895 --- /dev/null +++ b/src/core/analyse/analyse.cc @@ -0,0 +1,116 @@ +#include "analyse.h" +#include "utils/common.h" + +using klotski::Analyse; +using klotski::codec::RawCode; +using klotski::mover::MaskMover; + +Analyse::Analyse(const RawCode &code) { + this->root = (uint64_t)code; +} + +void Analyse::set_root(const RawCode &code) { + this->root = (uint64_t)code; +} + +std::vector Analyse::layer_export(uint32_t layer_num) { + std::vector layer_cases; + for (const auto &tmp : cases) { // traverse every cases + if (tmp.second.step == layer_num) { + layer_cases.emplace_back(RawCode::unsafe_create(tmp.first)); + } + } + return layer_cases; +} + +std::vector> Analyse::layer_export() { + std::vector> layer_cases; + for (const auto &tmp : cases) { // traverse every cases + if (tmp.second.step >= layer_cases.size()) { + layer_cases.resize(tmp.second.step + 1); + } + layer_cases[tmp.second.step].emplace_back( + RawCode::unsafe_create(tmp.first) // insert at target layer + ); + } + return layer_cases; +} + +std::vector Analyse::build_resolve() { + return build_until([](uint64_t code) { + return ((code >> (3 * 0xD)) & 0b111) == BLOCK_2x2; + }); +} + +/// memory initialize and return klotski core +MaskMover Analyse::init(uint64_t code) { + /// reset working data + cases.clear(); + cases.reserve(ANY_MAP_RESERVE); // hashmap pre-reserve + std::queue{}.swap(cache); + + /// insert root node + cache.emplace(&cases.emplace(code, analyse_t { + .code = code, + .mask = 0, + .step = 0, + .src = std::list{}, // without parent node + }).first->second); + + /// import klotski core + return MaskMover( + [this](RawCode code, uint64_t mask) { // lambda as function pointer + new_case(code.unwrap(), mask); + } + ); +} + +/// callback function for new case +void Analyse::new_case(uint64_t code, uint64_t mask) { + auto current = cases.find(code); + if (current != cases.end()) { // new case already exist + if (current->second.step == cache.front()->step + 1) { // new case at next layer + current->second.mask |= mask; // update mask info + current->second.src.emplace_back(cache.front()); // link more parent case + } + } else { // new case not exist + cache.emplace(&cases.emplace(code, analyse_t { // record new case + .code = code, + .mask = mask, + .step = cache.front()->step + 1, + .src = std::list{cache.front()}, // link parent case + }).first->second); + } +} + +/// analyse and build klotski tree +void Analyse::build() { + auto core = init(root); + while (!cache.empty()) { + core.next_cases(RawCode::unsafe_create(cache.front()->code), cache.front()->mask); + cache.pop(); + } +} + +std::vector Analyse::build_until(const match_t &match) { + auto core = init(root); + auto layer_end = cache.back(); + std::vector matched; // matched list + /// start BFS search + while (!cache.empty()) { + if (match(cache.front()->code)) { // match target + matched.emplace_back( + RawCode::unsafe_create(cache.front()->code) // record matched cases + ); + } + core.next_cases(RawCode::unsafe_create(cache.front()->code), cache.front()->mask); + if (cache.front() == layer_end) { // reach layer ending + if (!matched.empty()) { + return matched; // stop at first matched layer + } + layer_end = cache.back(); // reset layer ending + } + cache.pop(); + } + return std::vector{}; // no target found +} diff --git a/src/core/analyse/analyse.h b/src/core/analyse/analyse.h new file mode 100644 index 0000000..3940910 --- /dev/null +++ b/src/core/analyse/analyse.h @@ -0,0 +1,77 @@ +/// Klotski Engine by Dnomd343 @2024 + +// TODO: only copy from old implementation, the interfaces will change in future. + +#pragma once + +#include +#include +#include +#include +#include + +#include "mover/mover.h" +#include "raw_code/raw_code.h" + +namespace klotski { + +// TODO: try double or 4-times size +const uint32_t ANY_MAP_RESERVE = 65536; + +// TODO: Analyse enter klotski namespace later +using namespace klotski; + +class Analyse { +public: + typedef std::function match_t; + + /// setting root code + void set_root(const codec::RawCode &code); + explicit Analyse(const codec::RawCode &code); + + /// BFS search functions + void build(); + std::vector build_resolve(); + // TODO: build_furthest + std::vector build_until(const match_t &match); + + /// analysed layer export + // TODO: ending point search + std::vector> layer_export(); + std::vector layer_export(uint32_t layer_num); + +private: + struct analyse_t { + uint64_t code; + uint64_t mask; + uint32_t step; + std::list src; + }; + + uint64_t root; + std::queue cache; + std::unordered_map cases; + + inline mover::MaskMover init(uint64_t code); + void new_case(uint64_t code, uint64_t mask); + + +/// backtrack definitions +public: + struct track_t { + // TODO: try using RawCode + uint64_t code; + uint32_t layer_num; + std::list last; + std::list next; + }; + // TODO: try using RawCode + typedef std::vector> track_data_t; + + // TODO: using RawCode instead of uint64_t + track_data_t backtrack(const std::vector &codes); + + // TODO: RawCode enable `hash` and `equal_to` trait in namespace std +}; + +} // namespace klotski diff --git a/src/core/analyse/backtrack.cc b/src/core/analyse/backtrack.cc new file mode 100644 index 0000000..e2d7555 --- /dev/null +++ b/src/core/analyse/backtrack.cc @@ -0,0 +1,77 @@ +#include +#include "analyse.h" + +using klotski::Analyse; +using klotski::codec::RawCode; + +Analyse::track_data_t Analyse::backtrack(const std::vector &codes) { + /// codes pre-check and sort by steps + std::vector> todos; + for (const auto &code : codes) { + auto c = cases.find((uint64_t)code); + if (c == cases.end()) { // invalid input + return track_data_t{}; // return empty data + } + if (c->second.step >= todos.size()) { + todos.resize(c->second.step + 1); // enlarge schedule list + } + todos[c->second.step].emplace_back(&c->second); + } + std::reverse(todos.begin(), todos.end()); // backtrack start from further layer + + struct cache_t { + analyse_t *a; + track_t *b; + }; + std::queue track_cache; + track_data_t track_data(todos.size()); + /// start backtrack process + for (const auto &todo : todos) { + if (todo.empty()) { + continue; // without scheduled cases + } + std::queue{}.swap(track_cache); // clear track cache + for (const auto c : todo) { + /// case already exist -> skip its backtrack + if (track_data[c->step].find(c->code) == track_data[c->step].end()) { + track_cache.emplace(cache_t{ + .a = c, + .b = &track_data[c->step].emplace(c->code, track_t{ + .code = c->code, + .layer_num = c->step, + .last = std::list{}, // without parent node + .next = std::list{}, // without sub node + }).first->second, + }); + } + } + /// backtrack until root case + while (!track_cache.empty()) { + auto curr = track_cache.front(); // handle first element + for (auto src : curr.a->src) { // traverse src cases of current node + auto t_src = track_data[src->step].find(src->code); + if (t_src != track_data[src->step].end()) { // match src case + /// link (curr.b) and (t_src->second) + t_src->second.next.emplace_back(curr.b); + curr.b->last.emplace_back(&t_src->second); + } else { // src case not found + /// link (curr.b) and (t_src_new->second) + auto t_src_new = track_data[src->step].emplace(src->code, track_t { + .code = src->code, + .layer_num = src->step, + .last = std::list{}, + .next = std::list{curr.b}, // link to curr.b + }).first; + curr.b->last.emplace_back(&t_src_new->second); + /// insert into working queue + track_cache.emplace(cache_t { + .a = src, + .b = &t_src_new->second, + }); + } + } + track_cache.pop(); + } + } + return track_data; +} diff --git a/src/core/main.cc b/src/core/main.cc index a4f45b0..318a1f2 100644 --- a/src/core/main.cc +++ b/src/core/main.cc @@ -7,6 +7,7 @@ #include "group/group.h" #include "mover/mover.h" +#include "analyse/analyse.h" #include "raw_code/raw_code.h" #include "fast_cal/fast_cal.h" #include "all_cases/all_cases.h" @@ -15,6 +16,8 @@ #include +using klotski::Analyse; + using klotski::mover::MaskMover; using klotski::fast_cal::FastCal; @@ -45,6 +48,18 @@ int main() { const auto start = std::chrono::system_clock::now(); + // TODO: maybe we can support `std::format` + + const auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code(); + const auto solve_1 = CommonCode::unsafe_create(0xDAAF4CC00).to_raw_code(); + const auto solve_2 = CommonCode::unsafe_create(0xDAA7F3000).to_raw_code(); + + Analyse analyse {code}; + analyse.build(); + const auto backtrack = analyse.backtrack({solve_1, solve_2}); + std::cout << backtrack.size() << std::endl; + std::cout << backtrack[0].size() << ", " << backtrack[81].size() << std::endl; + // const auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code(); // const auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code(); // FastCal fc {code};