mirror of https://github.com/dnomd343/klotski.git
Dnomd343
8 months ago
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