diff --git a/src/core/benchmark/fast_cal.cc b/src/core/benchmark/fast_cal.cc index a2725c0..c7c3c5b 100644 --- a/src/core/benchmark/fast_cal.cc +++ b/src/core/benchmark/fast_cal.cc @@ -8,19 +8,24 @@ using klotski::codec::CommonCode; -using klotski::fast_cal::FastCalPro; +using klotski::fast_cal::FastCal; static void FastCalBenchmark(benchmark::State &state) { auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code(); -// auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code(); + // auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code(); for (auto _ : state) { - auto fc = FastCalPro(code); + auto fc = FastCal(code); + benchmark::DoNotOptimize(fc.solve()); -// benchmark::DoNotOptimize(fc.demo()); - // benchmark::DoNotOptimize(FastCal_demo(code)); -// auto tmp = klotski::cases::Group_extend(code); + // benchmark::DoNotOptimize(fc.solve_multi()); + // benchmark::DoNotOptimize(fc.furthest()); + // fc.search([](const klotski::codec::RawCode tmp) { + // return tmp == 0x7F87E0E5BFFF492; + // // return tmp == 0x1FB1E37F9E9F5CA; + // }); + // fc.build_all(); } } diff --git a/src/core/fast_cal/fast_cal.h b/src/core/fast_cal/fast_cal.h index a11b859..b624c38 100644 --- a/src/core/fast_cal/fast_cal.h +++ b/src/core/fast_cal/fast_cal.h @@ -1,42 +1,39 @@ /// Klotski Engine by Dnomd343 @2024 -// TODO: only copy from old implementation, the interfaces will change in future. - #pragma once -#include #include #include +#include #include "mover/mover.h" #include "layer_queue.h" #include "raw_code/raw_code.h" -#include - namespace klotski::fast_cal { -class FastCalPro { +class FastCal { public: - FastCalPro() = delete; + FastCal() = delete; - explicit FastCalPro(codec::RawCode raw_code); + explicit FastCal(codec::RawCode code); // ------------------------------------------------------------------------------------- // - // TODO: add global build - - void build(); - /// Calculate a minimum-step case. std::optional solve(); - /// Calculate all the maximum-step cases. - std::vector furthest(); - /// Calculate all the minimum-step cases. std::vector solve_multi(); + // ------------------------------------------------------------------------------------- // + + /// Calculate all cases. + void build_all(); + + /// Calculate all the maximum-step cases. + std::vector furthest(); + /// Calculate the first case that meets the requirement. std::optional search(std::function &&match); @@ -61,16 +58,18 @@ private: // ------------------------------------------------------------------------------------- // - // TODO: add invalid RawCode constexpr var + /// Using non-existed RawCode value to represent NULL. + static constexpr auto nil = codec::RawCode::unsafe_create(0); + + // ------------------------------------------------------------------------------------- // - struct data_t { + struct info_t { uint64_t mask; - codec::RawCode back; + codec::RawCode parent; }; - // codec::RawCode root_; - LayerQueue codes_; - phmap::flat_hash_map cases_; + LayerQueue seeker_; + phmap::flat_hash_map cases_; // ------------------------------------------------------------------------------------- // }; diff --git a/src/core/fast_cal/internal/fast_cal.cc b/src/core/fast_cal/internal/fast_cal.cc index a118755..eb77df9 100644 --- a/src/core/fast_cal/internal/fast_cal.cc +++ b/src/core/fast_cal/internal/fast_cal.cc @@ -1,110 +1,119 @@ -#include "mover/mover.h" -#include "group/group.h" +#include "utils/common.h" #include "fast_cal/fast_cal.h" -using klotski::codec::RawCode; -using klotski::codec::CommonCode; -using klotski::cases::RangesUnion; +using klotski::BLOCK_MSK; +using klotski::BLOCK_CE_2x2; +using klotski::codec::RawCode; using klotski::mover::MaskMover; -using klotski::cases::GroupUnion; +using klotski::fast_cal::FastCal; + +// ----------------------------------------------------------------------------------------- // + +void FastCal::build_all() { + auto mover = MaskMover([this](const RawCode code, const uint64_t mask) { + try_emplace(code, mask); + }); + while (!seeker_.is_ending()) { + spawn_next(mover); + } +} -using klotski::fast_cal::FastCalPro; +std::vector FastCal::furthest() { + if (!seeker_.is_ending()) { + build_all(); + } + return seeker_.last_layer(); +} -static KLSK_INLINE bool is_solved(RawCode raw_code) { - return ((raw_code.unwrap() >> 39) & 0b111) == 0b100; +// ----------------------------------------------------------------------------------------- // + +std::vector> FastCal::exports() const { + return seeker_.all_layers(); } -std::optional FastCalPro::solve() { - // if (is_solved(root_.unwrap())) { - // return root_; - // } +std::vector FastCal::backtrack(RawCode code) const { + if (const auto match = cases_.find(code); match == cases_.end()) { + return {}; // case not found + } + std::vector path; + while (code != 0) { + path.emplace_back(code); + code = cases_.find(code)->second.parent; + } + std::reverse(path.begin(), path.end()); + return path; +} + +// ----------------------------------------------------------------------------------------- // + +static KLSK_INLINE bool is_solved(const RawCode raw_code) { + return ((raw_code.unwrap() >> 39) & BLOCK_MSK) == BLOCK_CE_2x2; +} + +std::optional FastCal::solve() { + if (is_solved(seeker_.current())) { + return seeker_.current(); // root case solved + } - uint64_t solution = 0; - auto mover = MaskMover([this, &solution](RawCode code, uint64_t mask) { + RawCode solution {nil}; + auto mover = MaskMover([this, &solution](const RawCode code, const uint64_t mask) { if (try_emplace(code, mask) && is_solved(code)) { - solution = code.unwrap(); + solution = code; } }); - while (!codes_.is_ending()) { + + while (!seeker_.is_ending()) { spawn_next(mover); - if (solution != 0) { - return RawCode::unsafe_create(solution); + if (solution != nil) { + return solution; } } return std::nullopt; } -std::optional FastCalPro::search(std::function &&match) { - // TODO: check root case +std::optional FastCal::search(std::function &&match) { + if (match(seeker_.current())) { + return seeker_.current(); // root case matched + } - uint64_t target = 0; - auto mover = MaskMover([this, &target, match = std::move(match)](RawCode code, uint64_t mask) { + RawCode target {nil}; + auto mover = MaskMover([this, &target, match = std::move(match)](const RawCode code, const uint64_t mask) { if (try_emplace(code, mask) && match(code)) { - target = code.unwrap(); + target = code; } }); - while (!codes_.is_ending()) { + + while (!seeker_.is_ending()) { spawn_next(mover); - if (target != 0) { - return RawCode::unsafe_create(target); + if (target != nil) { + return target; } } return std::nullopt; } -std::vector FastCalPro::solve_multi() { - // TODO: check root case +std::vector FastCal::solve_multi() { + if (is_solved(seeker_.current())) { + return {seeker_.current()}; // root case solved + } bool stop_flag = false; std::vector results {}; - - auto mover = MaskMover([this, &stop_flag, &results](RawCode code, uint64_t mask) { + auto mover = MaskMover([this, &stop_flag, &results](const RawCode code, const uint64_t mask) { if (try_emplace(code, mask) && is_solved(code)) { stop_flag = true; results.emplace_back(code); } }); - while (!codes_.is_ending()) { + while (!seeker_.is_ending()) { spawn_next(mover); - if (stop_flag && (codes_.is_new_layer() || codes_.is_ending())) { + if (stop_flag && (seeker_.is_new_layer() || seeker_.is_ending())) { return results; } - } return {}; } -void FastCalPro::build() { - auto mover = MaskMover([this](RawCode code, uint64_t mask) { - try_emplace(code, mask); - }); - while (!codes_.is_ending()) { - spawn_next(mover); - } -} - -std::vector FastCalPro::furthest() { - if (!codes_.is_ending()) { - build(); - } - return codes_.last_layer(); -} - -std::vector FastCalPro::backtrack(RawCode code) const { - if (const auto match = cases_.find(code); match == cases_.end()) { - return {}; // case not found - } - std::vector path; - while (code != 0) { - path.emplace_back(code); - code = cases_.find(code)->second.back; - } - std::reverse(path.begin(), path.end()); - return path; -} - -std::vector> FastCalPro::exports() const { - return codes_.all_layers(); -} +// ----------------------------------------------------------------------------------------- // diff --git a/src/core/fast_cal/internal/fast_cal.inl b/src/core/fast_cal/internal/fast_cal.inl index 4aee075..0173f0b 100644 --- a/src/core/fast_cal/internal/fast_cal.inl +++ b/src/core/fast_cal/internal/fast_cal.inl @@ -4,30 +4,30 @@ namespace klotski::fast_cal { -inline FastCalPro::FastCalPro(codec::RawCode raw_code) - : codes_({raw_code}, cases::GroupUnion::from_raw_code(raw_code).max_group_size()) { - const auto reserve = cases::GroupUnion::from_raw_code(raw_code).max_group_size(); +inline FastCal::FastCal(const codec::RawCode code) + : seeker_({code}, cases::GroupUnion::from_raw_code(code).max_group_size()) { + const auto reserve = cases::GroupUnion::from_raw_code(code).max_group_size(); cases_.reserve(static_cast(reserve * 1.56)); - cases_.emplace(raw_code, data_t {0, codec::RawCode::unsafe_create(0)}); // without mask + cases_.emplace(code, info_t {0, nil}); // without mask } -inline KLSK_INLINE bool FastCalPro::try_emplace(codec::RawCode code, uint64_t mask) { +inline KLSK_INLINE bool FastCal::try_emplace(const codec::RawCode code, const uint64_t mask) { if (const auto match = cases_.find(code); match != cases_.end()) { match->second.mask |= mask; // update mask return false; } - cases_.emplace(code, data_t { + cases_.emplace(code, info_t { .mask = mask, - .back = codes_.current(), + .parent = seeker_.current(), }); - codes_.emplace(code); + seeker_.emplace(code); return true; } -inline KLSK_INLINE void FastCalPro::spawn_next(mover::MaskMover &mover) { - auto curr = codes_.current(); +inline KLSK_INLINE void FastCal::spawn_next(mover::MaskMover &mover) { + auto curr = seeker_.current(); mover.next_cases(curr, cases_.find(curr)->second.mask); - codes_.next(); + seeker_.next(); } } // namespace klotski::fast_cal diff --git a/src/core/fast_cal/internal/layer_queue.inl b/src/core/fast_cal/internal/layer_queue.inl index d71ec1e..ae17276 100644 --- a/src/core/fast_cal/internal/layer_queue.inl +++ b/src/core/fast_cal/internal/layer_queue.inl @@ -10,7 +10,7 @@ LayerQueue::LayerQueue(std::initializer_list first_layer, const size_t max for (const auto node : first_layer) { emplace(node); } - layer_offset_.reserve(139); // TODO: confirm the max layer number + layer_offset_.reserve(232); // TODO: confirm the max layer number layer_offset_.emplace_back(layer_end_); } @@ -66,9 +66,12 @@ template requires std::is_trivial_v std::vector> LayerQueue::all_layers() const { std::vector> result; + result.reserve(layer_offset_.size() - 1); for (size_t i = 0; i < layer_offset_.size() - 1; ++i) { - std::vector layer {data_ + layer_offset_[i], data_ + layer_offset_[i + 1]}; - result.emplace_back(layer); + result.emplace_back(std::vector { + data_ + layer_offset_[i], + data_ + layer_offset_[i + 1] + }); } return result; } diff --git a/src/core/main.cc b/src/core/main.cc index bdca255..0e87a05 100644 --- a/src/core/main.cc +++ b/src/core/main.cc @@ -19,6 +19,7 @@ #include "../../third_party/thread-pool/include/BS_thread_pool.hpp" using klotski::mover::MaskMover; +using klotski::fast_cal::FastCal; using klotski::cases::AllCases; using klotski::cases::BasicRanges; @@ -48,24 +49,25 @@ int main() { const auto start = std::chrono::system_clock::now(); - auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code(); - klotski::fast_cal::FastCalPro fc {code}; + // const auto code = CommonCode::unsafe_create(0x1A9BF0C00).to_raw_code(); + const auto code = CommonCode::unsafe_create(0x4FEA13400).to_raw_code(); + FastCal 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 (const auto x : fc.solve_multi()) { + std::cout << x.to_common_code() << std::endl; + } - // for (auto x : fc.furthest()) { - // std::cout << x.to_common_code() << std::endl; - // } + for (const auto x : fc.furthest()) { + std::cout << x.to_common_code() << 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; + fc.build_all(); + 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) { diff --git a/src/core/raw_code/internal/raw_code.inl b/src/core/raw_code/internal/raw_code.inl index 17d6756..f7e62c8 100644 --- a/src/core/raw_code/internal/raw_code.inl +++ b/src/core/raw_code/internal/raw_code.inl @@ -10,7 +10,7 @@ inline RawCode::RawCode(const CommonCode common_code) { code_ = extract(common_code.unwrap()); } -inline RawCode RawCode::unsafe_create(const uint64_t raw_code) { +constexpr RawCode RawCode::unsafe_create(const uint64_t raw_code) { return std::bit_cast(raw_code); // init directly } diff --git a/src/core/raw_code/raw_code.h b/src/core/raw_code/raw_code.h index ce5b71c..ff5f8e9 100644 --- a/src/core/raw_code/raw_code.h +++ b/src/core/raw_code/raw_code.h @@ -80,7 +80,7 @@ public: explicit RawCode(CommonCode common_code); /// Create RawCode without any check. - static RawCode unsafe_create(uint64_t raw_code); + static constexpr RawCode unsafe_create(uint64_t raw_code); /// Create RawCode with validity check. static std::optional create(uint64_t raw_code); diff --git a/src/core/utils/common.h b/src/core/utils/common.h index 4d46012..a47e18e 100644 --- a/src/core/utils/common.h +++ b/src/core/utils/common.h @@ -21,6 +21,19 @@ // TODO: using constexpr +namespace klotski { + +// TODO: remove `CE` tag +constexpr uint_fast8_t BLOCK_SPC {0b000}; // space +constexpr uint_fast8_t BLOCK_FIL {0b111}; // fill +constexpr uint_fast8_t BLOCK_CE_1x2 {0b001}; +constexpr uint_fast8_t BLOCK_CE_2x1 {0b010}; +constexpr uint_fast8_t BLOCK_CE_1x1 {0b011}; +constexpr uint_fast8_t BLOCK_CE_2x2 {0b100}; +constexpr uint_fast8_t BLOCK_MSK {0b111}; // mask + +} // namespace klotski + /// NOTE: 0b101 and 0b110 are reserved #define BLOCK_space 0b000 #define BLOCK_fill 0b111