Browse Source

feat: export all layer cases

master
Dnomd343 3 weeks ago
parent
commit
b683ae7687
  1. 2
      src/core/CMakeLists.txt
  2. 83
      src/core/fast_cal/fast_cal.h
  3. 105
      src/core/fast_cal/internal/cal_core.cc
  4. 55
      src/core/fast_cal/internal/demo.cc
  5. 95
      src/core/fast_cal/internal/fast_cal.cc
  6. 8
      src/core/fast_cal/internal/fast_cal.inl
  7. 69
      src/core/fast_cal/internal/layer_queue.inl
  8. 44
      src/core/fast_cal/layer_queue.h
  9. 17
      src/core/main.cc

2
src/core/CMakeLists.txt

@ -20,8 +20,6 @@ set(KLOTSKI_CORE_SRC
mover/internal/mover.cc
fast_cal/internal/cal_core.cc
fast_cal/internal/fast_cal.cc
fast_cal/internal/demo.cc
group/internal/group_union.cc

83
src/core/fast_cal/fast_cal.h

@ -4,78 +4,17 @@
#pragma once
// #include <queue>
#include <vector>
#include <cstdint>
#include <functional>
// #include <unordered_map>
#include "group/group.h"
#include "mover/mover.h"
#include "layer_queue.h"
#include "raw_code/raw_code.h"
#include "group/group.h"
// #include <absl/container/flat_hash_map.h>
#include <ranges>
#include <parallel_hashmap/phmap.h>
// using klotski::codec::RawCode;
// using klotski::mover::MaskMover;
// 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);
// RawCode demo();
/// 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 MaskMover init(uint64_t code);
// void new_case(uint64_t code, uint64_t mask);
// };
#include "layer_queue.h"
namespace klotski::fast_cal {
class FastCalPro {
@ -88,13 +27,13 @@ public:
// TODO: add global build
/// Calculate the case with minimum steps.
/// Calculate a minimum-step case.
std::optional<codec::RawCode> solve();
/// Calculate the cases with maximum steps.
/// Calculate all the maximum-step cases.
std::vector<codec::RawCode> furthest();
/// Calculate all of the minimum-step cases.
/// Calculate all the minimum-step cases.
std::vector<codec::RawCode> solve_multi();
/// Calculate the first case that meets the requirement.
@ -102,10 +41,11 @@ public:
// ------------------------------------------------------------------------------------- //
/// Backtrack the shortest path from specified case.
std::vector<codec::RawCode> backtrack(codec::RawCode code) const;
/// Export all group cases with different minimum-step.
[[nodiscard]] std::vector<std::vector<codec::RawCode>> exports() const;
// std::vector<std::vector<codec::RawCode>> export_layers() const;
/// Backtrack one of the minimum-step path from specified case.
[[nodiscard]] std::vector<codec::RawCode> backtrack(codec::RawCode code) const;
// ------------------------------------------------------------------------------------- //
@ -125,7 +65,8 @@ private:
uint64_t back;
};
LayerQueue<uint64_t> codes_;
// codec::RawCode root_;
LayerQueue<codec::RawCode> codes_;
phmap::flat_hash_map<uint64_t, data_t> cases_;
// ------------------------------------------------------------------------------------- //
@ -133,6 +74,4 @@ private:
} // namespace klotski::fast_cal
// klotski::codec::RawCode FastCal_demo(klotski::codec::RawCode code);
#include "internal/fast_cal.inl"

105
src/core/fast_cal/internal/cal_core.cc

@ -1,105 +0,0 @@
#include "fast_cal/fast_cal.h"
// MaskMover 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 mover
// return MaskMover(
// [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
// }

55
src/core/fast_cal/internal/demo.cc

@ -2,8 +2,6 @@
#include <iostream>
//#include <absl/container/flat_hash_map.h>
#include <parallel_hashmap/phmap.h>
#include "mover/mover.h"
@ -23,7 +21,9 @@ static KLSK_INLINE bool is_solved(uint64_t raw_code) {
}
std::optional<RawCode> FastCalPro::solve() {
// TODO: check root case
// if (is_solved(root_.unwrap())) {
// return root_;
// }
uint64_t solution = 0;
auto mover = MaskMover([this, &solution](uint64_t code, uint64_t mask) {
@ -89,9 +89,7 @@ std::vector<RawCode> FastCalPro::furthest() {
while (true) {
spawn_next(mover);
if (codes_.is_ending()) {
return codes_.layer_cases() | std::views::transform([](uint64_t code) {
return RawCode::unsafe_create(code);
}) | std::ranges::to<std::vector>();
return codes_.last_layer();
}
}
}
@ -109,45 +107,6 @@ std::vector<RawCode> FastCalPro::backtrack(RawCode code) const {
return path;
}
// std::vector<std::vector<RawCode>> FastCalPro::export_layers() const {
// auto layer = furthest();
//
// std::vector<std::vector<RawCode>> result;
// result.emplace_back(layer);
//
// for (auto code : layer) {
// std::cout << code << std::endl;
// }
// return {};
// }
// RawCode FastCal_demo(RawCode raw_code) {
// FastCalPro fc {raw_code};
// return fc.solve().value();
// auto tmp = fc.solve();
// std::cout << tmp.value().to_common_code() << std::endl;
// auto path = fc.backtrack(tmp.value());
// std::cout << path.size() << std::endl;
// fc.export_layers();
// auto tmp = fc.solve_multi();
// for (const auto x : tmp) {
// std::cout << x.to_common_code() << std::endl;
// }
// auto tmp = fc.furthest();
// for (const auto x : tmp) {
// std::cout << x.to_common_code() << std::endl;
// }
// auto tmp = fc.achieve([](RawCode r) {
// return r == 0x7F87E0E5BFFF492;
// });
// std::cout << tmp.value().to_common_code() << std::endl;
// return RawCode::unsafe_create(0);
// }
std::vector<std::vector<RawCode>> FastCalPro::exports() const {
return codes_.all_layers();
}

95
src/core/fast_cal/internal/fast_cal.cc

@ -1,95 +0,0 @@
#include <algorithm>
#include <iostream>
#include "utils/common.h"
#include "fast_cal/fast_cal.h"
#include "raw_code/raw_code.h"
#include "group/group.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;
// }

8
src/core/fast_cal/internal/fast_cal.inl

@ -2,7 +2,7 @@
namespace klotski::fast_cal {
inline FastCalPro::FastCalPro(codec::RawCode raw_code) : codes_(cases::GroupUnion::from_raw_code(raw_code).max_group_size(), {raw_code.unwrap()}) {
inline FastCalPro::FastCalPro(codec::RawCode raw_code) : codes_({raw_code}, cases::GroupUnion::from_raw_code(raw_code).max_group_size()) {
auto reserve = cases::GroupUnion::from_raw_code(raw_code).max_group_size();
// cases_.reserve(static_cast<size_t>(25955 * 1.56));
cases_.reserve(static_cast<size_t>(reserve * 1.56));
@ -16,15 +16,15 @@ inline KLSK_INLINE bool FastCalPro::try_emplace(uint64_t code, uint64_t mask) {
}
cases_.emplace(code, data_t {
.mask = mask,
.back = codes_.current(),
.back = codes_.current().unwrap(),
});
codes_.emplace(code);
codes_.emplace(codec::RawCode::unsafe_create(code));
return true;
}
inline KLSK_INLINE void FastCalPro::spawn_next(mover::MaskMover &mover) {
auto curr = codes_.current();
mover.next_cases(curr, cases_.find(curr)->second.mask);
mover.next_cases(curr.unwrap(), cases_.find(curr.unwrap())->second.mask);
codes_.next();
}

69
src/core/fast_cal/internal/layer_queue.inl

@ -3,19 +3,38 @@
namespace klotski {
template <typename T>
inline LayerQueue<T>::LayerQueue(size_t reserve, std::initializer_list<T> first_layer) {
data_.resize(reserve);
LayerQueue<T>::LayerQueue(std::initializer_list<T> first_layer, const size_t reserve) {
// data_.resize(reserve);
static_assert(std::is_trivial_v<T>);
// data_ = new T[reserve];
data_ = (T*)std::malloc(sizeof(T) * reserve);
queue_begin_ = 0;
queue_end_ = 0;
for (auto x : first_layer) {
emplace(x);
}
layer_begin_ = 0;
layer_end_ = first_layer.size();
layers_.reserve(139);
layers_.emplace_back(0, layer_end_);
// layers_.emplace_back(0, layer_end_);
layers_.emplace_back(0);
layers_.emplace_back(layer_end_);
}
template <typename T>
LayerQueue<T>::~LayerQueue() {
// delete []data_;
std::free(data_);
}
template <typename T>
void LayerQueue<T>::emplace(T item) {
data_[queue_end_] = item;
void LayerQueue<T>::emplace(T node) {
data_[queue_end_] = node;
++queue_end_;
}
@ -30,7 +49,8 @@ void LayerQueue<T>::next() {
if (queue_begin_ == layer_end_ && !is_ending()) {
layer_begin_ = layer_end_;
layer_end_ = queue_end_;
layers_.emplace_back(layer_begin_, layer_end_);
// layers_.emplace_back(layer_begin_, layer_end_);
layers_.emplace_back(layer_end_);
}
}
@ -44,13 +64,40 @@ template <typename T>
return queue_begin_ == layer_begin_;
}
//template <typename T>
//std::vector<T> LayerQueue<T>::layer_cases() const {
// std::vector<T> layer_cases;
// for (size_t offset = layer_begin_; offset < layer_end_; ++offset) {
// layer_cases.emplace_back(data_[offset]);
// }
// return layer_cases;
//}
template <typename T>
std::vector<T> LayerQueue<T>::layer_cases() const {
std::vector<T> layer_cases;
for (size_t offset = layer_begin_; offset < layer_end_; ++offset) {
layer_cases.emplace_back(data_[offset]);
std::vector<T> LayerQueue<T>::last_layer() const {
std::vector<T> layer;
layer.reserve(layer_end_ - layer_begin_);
for (auto offset = layer_begin_; offset < layer_end_; ++offset) {
layer.emplace_back(data_[offset]); // TODO: copy directly
}
return layer;
}
template <typename T>
std::vector<std::vector<T>> LayerQueue<T>::all_layers() const {
std::vector<std::vector<T>> result;
for (size_t i = 0; i < layers_.size() - 1; ++i) {
size_t begin = layers_[i];
size_t end = layers_[i + 1];
std::vector<T> layer {};
layer.reserve(end - begin);
for (auto offset = begin; offset < end; ++offset) {
layer.emplace_back(data_[offset]); // TODO: copy directly
}
result.emplace_back(layer);
}
return layer_cases;
return result;
}
} // namespace klotski

44
src/core/fast_cal/layer_queue.h

@ -3,39 +3,55 @@
#pragma once
#include <vector>
#include <cstdint>
namespace klotski {
template <typename T>
class LayerQueue {
class LayerQueue final {
public:
LayerQueue(size_t reserve, std::initializer_list<T> first_layer);
~LayerQueue();
void emplace(T item);
/// Construct from first layer cases and reserve size.
LayerQueue(std::initializer_list<T> first_layer, size_t reserve);
T current() const;
// ------------------------------------------------------------------------------------- //
/// Pop the head of the queue.
void next();
/// Obtain the current working node.
T current() const;
/// Emplace new node at the end of the queue.
void emplace(T node);
// ------------------------------------------------------------------------------------- //
/// Whether the queue is empty.
[[nodiscard]] bool is_ending() const;
/// Whether the queue front is on new layer.
[[nodiscard]] bool is_new_layer() const;
std::vector<T> layer_cases() const;
// ------------------------------------------------------------------------------------- //
// TODO: allow export all layers
/// Get the nodes of the last layer.
std::vector<T> last_layer() const;
private:
size_t queue_begin_ {0};
size_t queue_end_ {0};
/// Get all the nodes of each layer.
std::vector<std::vector<T>> all_layers() const;
size_t layer_begin_ {0};
size_t layer_end_ {0};
// ------------------------------------------------------------------------------------- //
private:
size_t layer_begin_, layer_end_;
size_t queue_begin_, queue_end_;
std::vector<T> data_ {};
// std::vector<T> data_ {};
T *data_ {nullptr};
std::vector<std::pair<size_t, size_t>> layers_ {};
std::vector<size_t> layers_ {};
// std::vector<std::pair<size_t, size_t>> layers_ {};
};
} // namespace klotski

17
src/core/main.cc

@ -48,7 +48,22 @@ int main() {
auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code();
klotski::fast_cal::FastCalPro fc {code};
std::cout << fc.solve().value() << std::endl;
// std::cout << fc.solve().value() << std::endl;
// for (auto x : fc.solve_multi()) {
// std::cout << x << std::endl;
// }
// for (auto x : fc.furthest()) {
// std::cout << x << std::endl;
// }
fc.furthest();
for (const auto &layer : fc.exports()) {
std::cout << layer.size() << std::endl;
}
std::cout << "layer num: " << fc.exports().size() << std::endl;
// for (int i = 0; i < 10000000; ++i) {
// MaskMover mover([](uint64_t code, uint64_t mask) {

Loading…
Cancel
Save