mirror of https://github.com/dnomd343/klotski.git
Dnomd343
4 days ago
5 changed files with 288 additions and 0 deletions
@ -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<RawCode> Analyse::layer_export(uint32_t layer_num) { |
||||
|
std::vector<RawCode> 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<std::vector<RawCode>> Analyse::layer_export() { |
||||
|
std::vector<std::vector<RawCode>> 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<RawCode> 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<analyse_t*>{}.swap(cache); |
||||
|
|
||||
|
/// insert root node
|
||||
|
cache.emplace(&cases.emplace(code, analyse_t { |
||||
|
.code = code, |
||||
|
.mask = 0, |
||||
|
.step = 0, |
||||
|
.src = std::list<analyse_t*>{}, // 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<analyse_t*>{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<RawCode> Analyse::build_until(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(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<RawCode>{}; // no target found
|
||||
|
} |
@ -0,0 +1,77 @@ |
|||||
|
/// Klotski Engine by Dnomd343 @2024
|
||||
|
|
||||
|
// TODO: only copy from old implementation, the interfaces will change in future.
|
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <list> |
||||
|
#include <queue> |
||||
|
#include <cstdint> |
||||
|
#include <functional> |
||||
|
#include <unordered_map> |
||||
|
|
||||
|
#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<bool(uint64_t)> 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<codec::RawCode> build_resolve(); |
||||
|
// TODO: build_furthest
|
||||
|
std::vector<codec::RawCode> build_until(const match_t &match); |
||||
|
|
||||
|
/// analysed layer export
|
||||
|
// TODO: ending point search
|
||||
|
std::vector<std::vector<codec::RawCode>> layer_export(); |
||||
|
std::vector<codec::RawCode> layer_export(uint32_t layer_num); |
||||
|
|
||||
|
private: |
||||
|
struct analyse_t { |
||||
|
uint64_t code; |
||||
|
uint64_t mask; |
||||
|
uint32_t step; |
||||
|
std::list<analyse_t*> src; |
||||
|
}; |
||||
|
|
||||
|
uint64_t root; |
||||
|
std::queue<analyse_t*> cache; |
||||
|
std::unordered_map<uint64_t, analyse_t> 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<track_t*> last; |
||||
|
std::list<track_t*> next; |
||||
|
}; |
||||
|
// TODO: try using RawCode
|
||||
|
typedef std::vector<std::unordered_map<uint64_t, track_t>> track_data_t; |
||||
|
|
||||
|
// TODO: using RawCode instead of uint64_t
|
||||
|
track_data_t backtrack(const std::vector<codec::RawCode> &codes); |
||||
|
|
||||
|
// TODO: RawCode enable `hash` and `equal_to` trait in namespace std
|
||||
|
}; |
||||
|
|
||||
|
} // namespace klotski
|
@ -0,0 +1,77 @@ |
|||||
|
#include <algorithm> |
||||
|
#include "analyse.h" |
||||
|
|
||||
|
using klotski::Analyse; |
||||
|
using klotski::codec::RawCode; |
||||
|
|
||||
|
Analyse::track_data_t Analyse::backtrack(const std::vector<RawCode> &codes) { |
||||
|
/// codes pre-check and sort by steps
|
||||
|
std::vector<std::vector<analyse_t*>> 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<cache_t> 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<cache_t>{}.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<track_t*>{}, // without parent node
|
||||
|
.next = std::list<track_t*>{}, // 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<track_t*>{}, |
||||
|
.next = std::list<track_t*>{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; |
||||
|
} |
Loading…
Reference in new issue