diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7c60b63..443f683 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -18,6 +18,9 @@ set(KLOTSKI_CORE_SRC short_code/internal/serialize.cc core/internal/core.cc + + fast_cal/internal/cal_core.cc + fast_cal/internal/fast_cal.cc ) add_library(klotski_core STATIC ${KLOTSKI_CORE_SRC}) diff --git a/src/core/fast_cal/fast_cal.h b/src/core/fast_cal/fast_cal.h new file mode 100644 index 0000000..e8384da --- /dev/null +++ b/src/core/fast_cal/fast_cal.h @@ -0,0 +1,65 @@ +/// 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 "core/core.h" +#include "raw_code/raw_code.h" + +using klotski::core::Core; +using klotski::codec::RawCode; + +// TODO: using prime number +const uint32_t FC_MAP_RESERVE = 65536 * 8; + +/// FastCal not found -> return invalid raw code +const RawCode FC_NOT_FOUND = RawCode::unsafe_create(0); + +class FastCal { +public: + typedef std::function match_t; + + /// setting root code + void set_root(const RawCode &code); + explicit FastCal(const RawCode &code); + + /// backtrack functions + int step_num(const RawCode &code); + std::vector backtrack(const RawCode &code); + + /// BFS search functions + void build(); + RawCode solve(); + std::vector furthest(); + std::vector solve_multi(); + RawCode target(const match_t &match); + std::vector target_multi(const match_t &match); + + /// static BFS search functions + static std::vector resolve(const RawCode &start); + static std::vector> to_furthest(const RawCode &start); + static std::vector> resolve_multi(const RawCode &start); + static std::vector search(const RawCode &start, const match_t &match); + static std::vector> search_multi(const RawCode &start, const match_t &match); + +private: + struct fast_cal_t { + uint64_t code; + uint64_t mask; + fast_cal_t *last; + }; + + uint64_t root; + std::queue cache; + std::unordered_map cases; + + inline Core init(uint64_t code); + void new_case(uint64_t code, uint64_t mask); +}; diff --git a/src/core/fast_cal/internal/cal_core.cc b/src/core/fast_cal/internal/cal_core.cc new file mode 100644 index 0000000..6eed096 --- /dev/null +++ b/src/core/fast_cal/internal/cal_core.cc @@ -0,0 +1,105 @@ +#include "fast_cal/fast_cal.h" + +Core FastCal::init(uint64_t code) { // initialize process + /// reset working data + cases.clear(); + cases.reserve(FC_MAP_RESERVE); // hashmap pre-reserve + std::queue{}.swap(cache); + + /// insert root node + cache.emplace(&cases.emplace(code, fast_cal_t { + .code = code, + .mask = 0, + .last = nullptr, // without parent node + }).first->second); + + /// import klotski core + return Core( + [this](auto &&code, auto &&mask) { // lambda as function pointer + new_case(std::forward(code), std::forward(mask)); + } + ); +} + +/// callback function for new case +void FastCal::new_case(uint64_t code, uint64_t mask) { + auto current = cases.find(code); + if (current != cases.end()) { // find existed case + current->second.mask |= mask; // update mask info + return; + } + cache.emplace(&cases.emplace(code, fast_cal_t { // record new case + .code = code, + .mask = mask, + .last = cache.front(), // link parent case + }).first->second); +} + +/// build total search tree +void FastCal::build() { + auto core = init(root); + while (!cache.empty()) { + core.next_cases(cache.front()->code, cache.front()->mask); + cache.pop(); + } +} + +/// found first matched target +RawCode FastCal::target(const match_t &match) { + auto core = init(root); + while (!cache.empty()) { + if (match(cache.front()->code)) { + return RawCode::unsafe_create(cache.front()->code); // match target + } + core.next_cases(cache.front()->code, cache.front()->mask); + cache.pop(); + } + return FC_NOT_FOUND; // target not found +} + +/// found all of the furthest cases +std::vector FastCal::furthest() { + auto core = init(root); + auto layer_end = cache.back(); + std::vector layer_cases; + /// start BFS search + while (!cache.empty()) { + core.next_cases(cache.front()->code, cache.front()->mask); + layer_cases.emplace_back( + RawCode::unsafe_create(cache.front()->code) // record layer cases + ); + if (cache.front() == layer_end) { // reach layer ending + if (cache.size() == 1) { + break; // stop loop at last layer + } + layer_cases.clear(); + layer_end = cache.back(); // reset layer ending + } + cache.pop(); + } + return layer_cases; // release the latest layer cases +} + +/// found multi-targets matched in first same layer +std::vector FastCal::target_multi(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(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/fast_cal/internal/fast_cal.cc b/src/core/fast_cal/internal/fast_cal.cc new file mode 100644 index 0000000..85479e2 --- /dev/null +++ b/src/core/fast_cal/internal/fast_cal.cc @@ -0,0 +1,91 @@ +#include + +#include "utils/common.h" +#include "fast_cal/fast_cal.h" +#include "raw_code/raw_code.h" + +FastCal::FastCal(const RawCode &code) { + this->root = (uint64_t)code; +} + +void FastCal::set_root(const RawCode &code) { + this->root = (uint64_t)code; +} + +/// klotski resolved -> 2x2 block at address 13 (aka 0xD) +auto resolved = [](uint64_t code) { + return ((code >> (3 * 0xD)) & 0b111) == BLOCK_2x2; // check 2x2 block address +}; + +RawCode FastCal::solve() { + return FastCal::target(resolved); +} + +std::vector FastCal::solve_multi() { + return FastCal::target_multi(resolved); +} + +std::vector FastCal::resolve(const RawCode &start) { + return FastCal::search(start, resolved); +} + +std::vector> FastCal::resolve_multi(const RawCode &start) { + return FastCal::search_multi(start, resolved); +} + +/// backtrack of FastCal tree +int FastCal::step_num(const RawCode &code) { + auto tmp = cases.find((uint64_t)code); + if (tmp == cases.end()) { + return -1; // code not exist + } + int num = 0; // step number + auto node = &tmp->second; // backtrack entry + while ((node = node->last) != nullptr) { + ++num; + } + return num; +} + +std::vector FastCal::backtrack(const RawCode &code) { + auto tmp = cases.find((uint64_t)code); + if (tmp == cases.end()) { + return std::vector{}; // code not exist + } + auto node = &tmp->second; // backtrack entry + std::vector path; // backtrack path + while (node != nullptr) { + path.emplace_back(RawCode::unsafe_create(node->code)); // record path info + node = node->last; + } + std::reverse(path.begin(), path.end()); // reverse path cases + return path; +} + +/// static BFS search functions +std::vector> FastCal::to_furthest(const RawCode &start) { + auto fc = FastCal(start); + std::vector> result; + for (const auto &furthest : fc.furthest()) { + result.emplace_back(fc.backtrack(furthest)); // backtrack every furthest cases + } + return result; +} + +std::vector FastCal::search(const RawCode &start, const match_t &match) { + auto fc = FastCal(start); + auto result = fc.target(match); + if (result == FC_NOT_FOUND) { + return std::vector{}; // target not matched + } + return fc.backtrack(result); // backtrack target path +} + +std::vector> FastCal::search_multi(const RawCode &start, const match_t &match) { + auto fc = FastCal(start); + std::vector> result; + for (const auto &target : fc.target_multi(match)) { + result.emplace_back(fc.backtrack(target)); // backtrack every target + } + return result; +} diff --git a/src/core/main.cc b/src/core/main.cc index 82706de..0ad642d 100644 --- a/src/core/main.cc +++ b/src/core/main.cc @@ -3,6 +3,7 @@ #include "core/core.h" #include "raw_code/raw_code.h" +#include "fast_cal/fast_cal.h" #include "all_cases/all_cases.h" #include "short_code/short_code.h" #include "common_code/common_code.h" @@ -21,45 +22,28 @@ using klotski::codec::SHORT_CODE_LIMIT; int main() { const auto start = clock(); - auto core = Core([](const uint64_t code, uint64_t) { - std::cout << RawCode::unsafe_create(code); - std::cout << std::endl; - }); + // auto core = Core([](const uint64_t code, uint64_t) { + // std::cout << RawCode::unsafe_create(code); + // std::cout << std::endl; + // }); + // core.next_cases(RawCode::from_common_code(0x1A9BF0C00).value().unwrap(), 0x0); - core.next_cases(RawCode::from_common_code(0x1A9BF0C00).value().unwrap(), 0x0); + // auto cal = FastCal(RawCode::from_common_code(0x1A9BF0C00).value()); + auto cal = FastCal(RawCode::from_common_code("25EEF04").value()); -// std::vector common_codes; -// common_codes.reserve(klotski::cases::ALL_CASES_NUM_); - -// for (uint64_t head = 0; head < 15; ++head) { -// for (auto range : AllCases::instance().fetch()[head]) { -// common_codes.emplace_back(head << 32 | range); -// } -// } - -// std::vector common_codes_str; -// common_codes_str.reserve(klotski::cases::ALL_CASES_NUM_); -// for (auto x : common_codes) { -// common_codes_str.emplace_back(CommonCode::string_encode(x, false)); -// } - - // ShortCode::speed_up(true); + // auto ret = cal.solve(); // - // for (uint32_t short_code = 0; short_code < SHORT_CODE_LIMIT; ++short_code) { - // ShortCode::unsafe_create(short_code).to_common_code(); + // for (auto kk : cal.backtrack(ret)) { + // std::cout << kk.to_common_code() << "," << kk.to_common_code().to_short_code() << std::endl; // } -// for (auto common_code : common_codes) { -// printf("%llX\n", common_code); -// CommonCode::string_encode(common_code, true); -// CommonCode::string_encode(common_code, false); -// printf("%s\n", CommonCode::string_encode(common_code, false).c_str()); -// std::cout << CommonCode::string_encode(common_code, false) << std::endl; -// } - -// for (auto &common_code_str : common_codes_str) { -// CommonCode::string_decode(common_code_str); -// } + for (const auto solve : cal.solve_multi()) { + for (const auto raw_code : cal.backtrack(solve)) { + const auto common_code = raw_code.to_common_code(); + std::cout << common_code << "/" << common_code.to_short_code() << std::endl; + } + std::cout << "----" << std::endl; + } std::cerr << ((clock() - start) * 1000 / CLOCKS_PER_SEC) << "ms" << std::endl;