Browse Source

feat: add analyse module

master
Dnomd343 4 days ago
parent
commit
8850a8898f
  1. 3
      src/core/CMakeLists.txt
  2. 116
      src/core/analyse/analyse.cc
  3. 77
      src/core/analyse/analyse.h
  4. 77
      src/core/analyse/backtrack.cc
  5. 15
      src/core/main.cc

3
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})

116
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<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
}

77
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 <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

77
src/core/analyse/backtrack.cc

@ -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;
}

15
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 <parallel_hashmap/phmap.h>
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};

Loading…
Cancel
Save