mirror of https://github.com/dnomd343/klotski.git
				
				
			
				 5 changed files with 282 additions and 34 deletions
			
			
		| @ -0,0 +1,65 @@ | |||||
|  | /// Klotski Engine by Dnomd343 @2024
 | ||||
|  | 
 | ||||
|  | // TODO: only copy from old implementation, the interfaces will change in future.
 | ||||
|  | 
 | ||||
|  | #pragma once | ||||
|  | 
 | ||||
|  | #include <queue> | ||||
|  | #include <vector> | ||||
|  | #include <cstdint> | ||||
|  | #include <functional> | ||||
|  | #include <unordered_map> | ||||
|  | 
 | ||||
|  | #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<bool(uint64_t)> 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<RawCode> backtrack(const RawCode &code); | ||||
|  | 
 | ||||
|  |     /// BFS search functions
 | ||||
|  |     void build(); | ||||
|  |     RawCode solve(); | ||||
|  |     std::vector<RawCode> furthest(); | ||||
|  |     std::vector<RawCode> solve_multi(); | ||||
|  |     RawCode target(const match_t &match); | ||||
|  |     std::vector<RawCode> target_multi(const match_t &match); | ||||
|  | 
 | ||||
|  |     /// static BFS search functions
 | ||||
|  |     static std::vector<RawCode> resolve(const RawCode &start); | ||||
|  |     static std::vector<std::vector<RawCode>> to_furthest(const RawCode &start); | ||||
|  |     static std::vector<std::vector<RawCode>> resolve_multi(const RawCode &start); | ||||
|  |     static std::vector<RawCode> search(const RawCode &start, const match_t &match); | ||||
|  |     static std::vector<std::vector<RawCode>> 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<fast_cal_t*> cache; | ||||
|  |     std::unordered_map<uint64_t, fast_cal_t> cases; | ||||
|  | 
 | ||||
|  |     inline Core init(uint64_t code); | ||||
|  |     void new_case(uint64_t code, uint64_t mask); | ||||
|  | }; | ||||
| @ -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<fast_cal_t*>{}.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<decltype(code)>(code), std::forward<decltype(mask)>(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<RawCode> FastCal::furthest() { | ||||
|  |     auto core = init(root); | ||||
|  |     auto layer_end = cache.back(); | ||||
|  |     std::vector<RawCode> 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<RawCode> FastCal::target_multi(const match_t &match) { | ||||
|  |     auto core = init(root); | ||||
|  |     auto layer_end = cache.back(); | ||||
|  |     std::vector<RawCode> 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<RawCode>{}; // no target found
 | ||||
|  | } | ||||
| @ -0,0 +1,91 @@ | |||||
|  | #include <algorithm> | ||||
|  | 
 | ||||
|  | #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<RawCode> FastCal::solve_multi() { | ||||
|  |     return FastCal::target_multi(resolved); | ||||
|  | } | ||||
|  | 
 | ||||
|  | std::vector<RawCode> FastCal::resolve(const RawCode &start) { | ||||
|  |     return FastCal::search(start, resolved); | ||||
|  | } | ||||
|  | 
 | ||||
|  | std::vector<std::vector<RawCode>> 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<RawCode> FastCal::backtrack(const RawCode &code) { | ||||
|  |     auto tmp = cases.find((uint64_t)code); | ||||
|  |     if (tmp == cases.end()) { | ||||
|  |         return std::vector<RawCode>{}; // code not exist
 | ||||
|  |     } | ||||
|  |     auto node = &tmp->second; // backtrack entry
 | ||||
|  |     std::vector<RawCode> 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<std::vector<RawCode>> FastCal::to_furthest(const RawCode &start) { | ||||
|  |     auto fc = FastCal(start); | ||||
|  |     std::vector<std::vector<RawCode>> result; | ||||
|  |     for (const auto &furthest : fc.furthest()) { | ||||
|  |         result.emplace_back(fc.backtrack(furthest)); // backtrack every furthest cases
 | ||||
|  |     } | ||||
|  |     return result; | ||||
|  | } | ||||
|  | 
 | ||||
|  | std::vector<RawCode> 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<RawCode>{}; // target not matched
 | ||||
|  |     } | ||||
|  |     return fc.backtrack(result); // backtrack target path
 | ||||
|  | } | ||||
|  | 
 | ||||
|  | std::vector<std::vector<RawCode>> FastCal::search_multi(const RawCode &start, const match_t &match) { | ||||
|  |     auto fc = FastCal(start); | ||||
|  |     std::vector<std::vector<RawCode>> result; | ||||
|  |     for (const auto &target : fc.target_multi(match)) { | ||||
|  |         result.emplace_back(fc.backtrack(target)); // backtrack every target
 | ||||
|  |     } | ||||
|  |     return result; | ||||
|  | } | ||||
					Loading…
					
					
				
		Reference in new issue