From 47787c88fd9a445abf8db05679e4dd84826dca42 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sat, 11 Jan 2025 17:21:48 +0800 Subject: [PATCH] feat: add `S2HintMover` support --- src/core/CMakeLists.txt | 1 + src/core/mover/internal/s2_hint_mover.cc | 314 +++++++++++++++++++++++ src/core/mover/mover.h | 1 + src/core/mover/s2_hint_mover.h | 32 +++ 4 files changed, 348 insertions(+) create mode 100644 src/core/mover/internal/s2_hint_mover.cc create mode 100644 src/core/mover/s2_hint_mover.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a5d4ae2..5b6d672 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -87,6 +87,7 @@ set(KLSK_CORE_SRC mover/internal/mover.cc mover/internal/s2_mover.cc + mover/internal/s2_hint_mover.cc group/internal/group_union.cc group/internal/group.cc diff --git a/src/core/mover/internal/s2_hint_mover.cc b/src/core/mover/internal/s2_hint_mover.cc new file mode 100644 index 0000000..2a577ab --- /dev/null +++ b/src/core/mover/internal/s2_hint_mover.cc @@ -0,0 +1,314 @@ +#include "mover/s2_hint_mover.h" +#include "raw_code/raw_code.h" + +using klotski::codec::RawCode; +using klotski::mover::S2HintMover; + +#define ADDR(N) ((N) * 3) + +#define CAPTURE(code, N) (((code) >> ADDR(N)) & 0b111ULL) + +#define MOVE_1x1(code, SRC, DST) \ + ((code) & ~(K_MASK_1x1_ << ADDR(SRC)) | (K_MASK_1x1 << ADDR(DST))) + +#define MOVE_1x2(code, SRC, DST) \ + ((code) & ~(K_MASK_1x2_ << ADDR(SRC)) | (K_MASK_1x2 << ADDR(DST))) + +#define MOVE_2x1(code, SRC, DST) \ + ((code) & ~(K_MASK_2x1_ << ADDR(SRC)) | (K_MASK_2x1 << ADDR(DST))) + +#define MOVE_2x2(code, SRC, DST) \ + ((code) & ~(K_MASK_2x2_ << ADDR(SRC)) | (K_MASK_2x2 << ADDR(DST))) + +#define RELEASE_1x1(code, SRC, DST) \ + release_(MOVE_1x1(code, SRC, DST), (0b1 << (DST))) + +#define RELEASE_1x2(code, SRC, DST) \ + release_(MOVE_1x2(code, SRC, DST), (0b11 << (DST))) + +#define RELEASE_2x1(code, SRC, DST) \ + release_(MOVE_2x1(code, SRC, DST), (0b10001 << (DST))) + +#define RELEASE_2x2(code, SRC, DST) \ + release_(MOVE_2x2(code, SRC, DST), (0b110011 << (DST))) + +#define MAYBE_UP(hint, N) (((hint) & (1 << ((N) - 4))) == 0) + +#define MAYBE_DOWN(hint, N) (((hint) & (1 << ((N) + 4))) == 0) + +#define MAYBE_LEFT(hint, N) (((hint) & (1 << ((N) - 1))) == 0) + +#define MAYBE_RIGHT(hint, N) (((hint) & (1 << ((N) + 1))) == 0) + +template +void S2HintMover::move_single(const uint64_t code, const uint64_t hint) const { + if (N >= 4 && MAYBE_UP(hint, N)) { // case up + if (CAPTURE(code, N - 4) == BLOCK_1x1) { + RELEASE_1x1(code, N - 4, N); + } else if (N >= 8 && CAPTURE(code, N - 8) == BLOCK_2x1) { + RELEASE_2x1(code, N - 8, N - 4); + } + } + + if (N < 16 && MAYBE_DOWN(hint, N)) { // case down + if (const uint8_t block = CAPTURE(code, N + 4); block == BLOCK_1x1) { + RELEASE_1x1(code, N + 4, N); + } else if (N < 12 && block == BLOCK_2x1) { + RELEASE_2x1(code, N + 4, N); + } + } + + if (N % 4 >= 1 && MAYBE_LEFT(hint, N)) { // case left + if (CAPTURE(code, N - 1) == BLOCK_1x1) { + RELEASE_1x1(code, N - 1, N); + } else if (N % 4 >= 2 && CAPTURE(code, N - 2) == BLOCK_1x2) { + RELEASE_1x2(code, N - 2, N - 1); + } + } + + if (N % 4 < 3 && MAYBE_RIGHT(hint, N)) { // case right + if (const uint8_t block = CAPTURE(code, N + 1); block == BLOCK_1x1) { + RELEASE_1x1(code, N + 1, N); + } else if (N % 4 < 2 && block == BLOCK_1x2) { + RELEASE_1x2(code, N + 1, N); + } + } +} + +template +void S2HintMover::move_double_h(const uint64_t code, uint64_t hint) const { + do { // case up + if (N >= 4 && MAYBE_UP(hint, N)) { + if (const uint8_t block = CAPTURE(code, N - 4); block == BLOCK_1x1) { // left part + RELEASE_1x1(code, N - 4, N); + RELEASE_1x1(code, N - 4, N + 1); + } else if (block == BLOCK_1x2) { + RELEASE_1x2(code, N - 4, N); + break; + } else if (N >= 8 && block == BLOCK_fill) { + if (const uint8_t block_ = CAPTURE(code, N - 8); block_ == BLOCK_2x2) { + RELEASE_2x2(code, N - 8, N - 4); + break; + } else if (block_ == BLOCK_2x1) { + RELEASE_2x1(code, N - 8, N - 4); + } + } + if (!MAYBE_UP(hint, N + 1)) { + break; + } + if (const uint8_t block = CAPTURE(code, N - 3); block == BLOCK_1x1) { // right part + RELEASE_1x1(code, N - 3, N + 1); + RELEASE_1x1(code, N - 3, N); + } else if (N >= 8 && block == BLOCK_fill && CAPTURE(code, N - 7) == BLOCK_2x1) { + RELEASE_2x1(code, N - 7, N - 3); + } + } + } while (false); + + do { // case down + if (N < 16 && MAYBE_DOWN(hint, N)) { + if (const uint8_t block = CAPTURE(code, N + 4); block == BLOCK_1x1) { // left part + RELEASE_1x1(code, N + 4, N); + RELEASE_1x1(code, N + 4, N + 1); + } else if (N < 12 && block == BLOCK_2x1) { + RELEASE_2x1(code, N + 4, N); + } else if (block == BLOCK_1x2) { + RELEASE_1x2(code, N + 4, N); + break; + } else if (N < 12 && block == BLOCK_2x2) { + RELEASE_2x2(code, N + 4, N); + break; + } + if (!MAYBE_DOWN(hint, N + 1)) { + break; + } + if (const uint8_t block = CAPTURE(code, N + 5); block == BLOCK_1x1) { // right part + RELEASE_1x1(code, N + 5, N + 1); + RELEASE_1x1(code, N + 5, N); + } else if (N < 12 && block == BLOCK_2x1) { + RELEASE_2x1(code, N + 5, N + 1); + } + } + } while (false); + + if (N % 4 >= 1 && MAYBE_LEFT(hint, N)) { // case left + if (CAPTURE(code, N - 1) == BLOCK_1x1) { + RELEASE_1x1(code, N - 1, N); + RELEASE_1x1(code, N - 1, N + 1); + } else if (N % 4 == 2 && CAPTURE(code, N - 2) == BLOCK_1x2) { + RELEASE_1x2(code, N - 2, N - 1); + RELEASE_1x2(code, N - 2, N); + } + } + + if (N % 4 < 2 && MAYBE_RIGHT(hint, N + 1)) { // case right + if (const uint8_t block = CAPTURE(code, N + 2); block == BLOCK_1x1) { + RELEASE_1x1(code, N + 2, N + 1); + RELEASE_1x1(code, N + 2, N); + } else if (N % 4 == 0 && block == BLOCK_1x2) { + RELEASE_1x2(code, N + 2, N + 1); + RELEASE_1x2(code, N + 2, N); + } + } +} + +template +void S2HintMover::move_double_v(const uint64_t code, uint64_t hint) const { + if (N >= 4 && MAYBE_UP(hint, N)) { // case up + if (CAPTURE(code, N - 4) == BLOCK_1x1) { + RELEASE_1x1(code, N - 4, N); + RELEASE_1x1(code, N - 4, N + 4); + } else if (N >= 8 && CAPTURE(code, N - 8) == BLOCK_2x1) { + RELEASE_2x1(code, N - 8, N - 4); + RELEASE_2x1(code, N - 8, N); + } + } + + if (N < 12 && MAYBE_DOWN(hint, N + 4)) { // case down + if (CAPTURE(code, N + 8) == BLOCK_1x1) { + RELEASE_1x1(code, N + 8, N + 4); + RELEASE_1x1(code, N + 8, N); + } else if (N < 8 && CAPTURE(code, N + 8) == BLOCK_2x1) { + RELEASE_2x1(code, N + 8, N + 4); + RELEASE_2x1(code, N + 8, N); + } + } + + do { // case left + if (N % 4 != 0 && MAYBE_LEFT(hint, N)) { + if (const uint8_t block = CAPTURE(code, N - 1); block == BLOCK_1x1) { // up part + RELEASE_1x1(code, N - 1, N); + RELEASE_1x1(code, N - 1, N + 4); + } else if (block == BLOCK_2x1) { + RELEASE_2x1(code, N - 1, N); + break; + } else if (N % 4 >= 2 && block == BLOCK_fill) { + if (const uint8_t block_ = CAPTURE(code, N - 2); block_ == BLOCK_2x2) { + RELEASE_2x2(code, N - 2, N - 1); + break; + } else if (block_ == BLOCK_1x2) { + RELEASE_1x2(code, N - 2, N - 1); + } + } + if (!MAYBE_LEFT(hint, N + 4)) { + break; + } + if (const uint8_t block = CAPTURE(code, N + 3); block == BLOCK_1x1) { // down part + RELEASE_1x1(code, N + 3, N + 4); + RELEASE_1x1(code, N + 3, N); + } else if (N % 4 >= 2 && block == BLOCK_fill && CAPTURE(code, N + 2) == BLOCK_1x2) { + RELEASE_1x2(code, N + 2, N + 3); + } + } + } while (false); + + do { // case right + if (N % 4 < 3 && MAYBE_RIGHT(hint, N)) { + if (const uint8_t block = CAPTURE(code, N + 1); block == BLOCK_1x1) { // up part + RELEASE_1x1(code, N + 1, N); + RELEASE_1x1(code, N + 1, N + 4); + } else if (N % 4 < 2 && block == BLOCK_1x2) { + RELEASE_1x2(code, N + 1, N); + } else if (block == BLOCK_2x1) { + RELEASE_2x1(code, N + 1, N); + break; + } else if (N % 4 < 2 && block == BLOCK_2x2) { + RELEASE_2x2(code, N + 1, N); + break; + } + if (!MAYBE_RIGHT(hint, N + 4)) { + break; + } + if (const uint8_t block = CAPTURE(code, N + 5); block == BLOCK_1x1) { // down part + RELEASE_1x1(code, N + 5, N + 4); + RELEASE_1x1(code, N + 5, N); + } else if (N % 4 < 2 && block == BLOCK_1x2) { + RELEASE_1x2(code, N + 5, N + 4); + } + } + } while (false); +} + +void S2HintMover::move_double_h(const uint64_t code, uint64_t hint, const int offset) const { + switch (offset) { + case 0: move_double_h<0>(code, hint); break; + case 1: move_double_h<1>(code, hint); break; + case 2: move_double_h<2>(code, hint); break; + case 4: move_double_h<4>(code, hint); break; + case 5: move_double_h<5>(code, hint); break; + case 6: move_double_h<6>(code, hint); break; + case 8: move_double_h<8>(code, hint); break; + case 9: move_double_h<9>(code, hint); break; + case 10: move_double_h<10>(code, hint); break; + case 12: move_double_h<12>(code, hint); break; + case 13: move_double_h<13>(code, hint); break; + case 14: move_double_h<14>(code, hint); break; + case 16: move_double_h<16>(code, hint); break; + case 17: move_double_h<17>(code, hint); break; + case 18: move_double_h<18>(code, hint); break; + default: std::unreachable(); + } +} + +void S2HintMover::move_double_v(const uint64_t code, uint64_t hint, const int offset) const { + switch (offset) { + case 0: move_double_v<0>(code, hint); break; + case 1: move_double_v<1>(code, hint); break; + case 2: move_double_v<2>(code, hint); break; + case 3: move_double_v<3>(code, hint); break; + case 4: move_double_v<4>(code, hint); break; + case 5: move_double_v<5>(code, hint); break; + case 6: move_double_v<6>(code, hint); break; + case 7: move_double_v<7>(code, hint); break; + case 8: move_double_v<8>(code, hint); break; + case 9: move_double_v<9>(code, hint); break; + case 10: move_double_v<10>(code, hint); break; + case 11: move_double_v<11>(code, hint); break; + case 12: move_double_v<12>(code, hint); break; + case 13: move_double_v<13>(code, hint); break; + case 14: move_double_v<14>(code, hint); break; + case 15: move_double_v<15>(code, hint); break; + default: std::unreachable(); + } +} + +void S2HintMover::move_single(const uint64_t code, uint64_t hint, const int offset) const { + switch (offset) { + case 0: move_single<0>(code, hint); break; + case 1: move_single<1>(code, hint); break; + case 2: move_single<2>(code, hint); break; + case 3: move_single<3>(code, hint); break; + case 4: move_single<4>(code, hint); break; + case 5: move_single<5>(code, hint); break; + case 6: move_single<6>(code, hint); break; + case 7: move_single<7>(code, hint); break; + case 8: move_single<8>(code, hint); break; + case 9: move_single<9>(code, hint); break; + case 10: move_single<10>(code, hint); break; + case 11: move_single<11>(code, hint); break; + case 12: move_single<12>(code, hint); break; + case 13: move_single<13>(code, hint); break; + case 14: move_single<14>(code, hint); break; + case 15: move_single<15>(code, hint); break; + case 16: move_single<16>(code, hint); break; + case 17: move_single<17>(code, hint); break; + case 18: move_single<18>(code, hint); break; + case 19: move_single<19>(code, hint); break; + default: std::unreachable(); + } +} + +void S2HintMover::next_cases(const uint64_t code, const uint64_t hint) const { + const uint64_t mask = code | (code >> 1) | (code >> 2) | 0xFDB6DB6DB6DB6DB6; + const int space_a = std::countr_one(mask) / 3; + const int space_b = (63 - std::countl_one(mask)) / 3; + + if (space_a + 1 == space_b && space_a % 4 != 3) { + move_double_h(code, hint, space_a); + } else if (space_a + 4 == space_b) { + move_double_v(code, hint, space_a); + } else { + move_single(code, hint, space_a); + move_single(code, hint, space_b); + } +} diff --git a/src/core/mover/mover.h b/src/core/mover/mover.h index 9d8d078..8036aca 100644 --- a/src/core/mover/mover.h +++ b/src/core/mover/mover.h @@ -100,3 +100,4 @@ private: } // namespace klotski::mover #include "s2_mover.h" +#include "s2_hint_mover.h" diff --git a/src/core/mover/s2_hint_mover.h b/src/core/mover/s2_hint_mover.h new file mode 100644 index 0000000..4ae11c0 --- /dev/null +++ b/src/core/mover/s2_hint_mover.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace klotski::mover { + +class S2HintMover { +public: + using release_t = std::function; + + explicit S2HintMover(release_t release_func) : release_(std::move(release_func)) {} + + void next_cases(uint64_t code, uint64_t hint) const; + +private: + release_t release_; + + template + void move_single(uint64_t code, uint64_t hint) const; + + template + void move_double_h(uint64_t code, uint64_t hint) const; + + template + void move_double_v(uint64_t code, uint64_t hint) const; + + void move_single(uint64_t code, uint64_t hint, int offset) const; + void move_double_h(uint64_t code, uint64_t hint, int offset) const; + void move_double_v(uint64_t code, uint64_t hint, int offset) const; +}; + +} // namespace klotski::mover