diff --git a/src/core/group/group.h b/src/core/group/group.h index ec86d59..d0a823f 100644 --- a/src/core/group/group.h +++ b/src/core/group/group.h @@ -114,7 +114,7 @@ public: [[nodiscard]] cases::RangesUnion cases() const; /// Get the group instance with the specified pattern id. - [[nodiscard]] constexpr std::optional groups(uint32_t pattern_id) const; + [[nodiscard]] constexpr std::optional groups(uint_fast16_t pattern_id) const; // ------------------------------------------------------------------------------------- // @@ -153,19 +153,21 @@ private: // ------------------------------------------------------------------------------------- // }; +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); + class Group { public: // ------------------------------------------------------------------------------------- // - // TODO: enum with uint_fast8_t - enum class Toward { + enum class Toward : uint_fast8_t { A = 0, // baseline B = 1, // horizontal mirror C = 2, // vertical mirror D = 3, // diagonal mirror }; - enum class MirrorType { + enum class MirrorType : uint_fast8_t { Full = 0, // fully self-symmetry Horizontal = 1, // horizontal self-symmetry Centro = 2, // centrosymmetric @@ -182,25 +184,22 @@ public: [[nodiscard]] constexpr char toward_char() const; /// Get the original type id. - [[nodiscard]] constexpr uint32_t type_id() const; + [[nodiscard]] constexpr uint_fast8_t type_id() const; /// Get the original pattern id. - [[nodiscard]] constexpr uint32_t pattern_id() const; - - /// Get the string form of current group. - [[nodiscard]] constexpr std::string to_string() const; + [[nodiscard]] constexpr uint_fast16_t pattern_id() const; // ------------------------------------------------------------------------------------- // Group() = delete; /// Create Group without any check. - static constexpr Group unsafe_create(uint32_t type_id, - uint32_t pattern_id, Toward toward); + static constexpr Group unsafe_create(uint_fast8_t type_id, + uint_fast16_t pattern_id, Toward toward); /// Create Group with validity check. - static constexpr std::optional create(uint32_t type_id, - uint32_t pattern_id, Toward toward); + static constexpr std::optional create(uint_fast8_t type_id, + uint_fast16_t pattern_id, Toward toward); // ------------------------------------------------------------------------------------- // @@ -240,6 +239,9 @@ public: // ------------------------------------------------------------------------------------- // + /// Get the group in string form. + [[nodiscard]] std::string to_string() const; + #ifndef KLSK_NDEBUG /// Output group info only for debug. friend std::ostream& operator<<(std::ostream &out, Group self); @@ -251,19 +253,22 @@ public: // ------------------------------------------------------------------------------------- // private: - uint32_t type_id_; // TODO: using uint_fast8_t + uint_fast8_t type_id_; Toward toward_; - uint32_t pattern_id_; + uint_fast16_t pattern_id_; /// Tiled merge of type_id and pattern_id. [[nodiscard]] constexpr uint32_t flat_id() const; /// Hidden constructor called from unsafe_create. - constexpr Group(uint32_t type_id, uint32_t pattern_id, Toward toward); + constexpr Group(uint_fast8_t type_id, Toward toward, uint_fast16_t pattern_id); // ------------------------------------------------------------------------------------- // }; +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); + class CaseInfo { public: // ------------------------------------------------------------------------------------- // @@ -274,9 +279,6 @@ public: /// Get the original case id. [[nodiscard]] constexpr uint32_t case_id() const; - /// Get the string form of current case. - [[nodiscard]] constexpr std::string to_string() const; - // ------------------------------------------------------------------------------------- // CaseInfo() = delete; @@ -289,6 +291,9 @@ public: // ------------------------------------------------------------------------------------- // + /// Get case info in string form. + [[nodiscard]] std::string to_string() const; + #ifndef KLSK_NDEBUG /// Output case info only for debug. friend std::ostream& operator<<(std::ostream &out, CaseInfo self); @@ -309,6 +314,9 @@ private: // ------------------------------------------------------------------------------------- // }; +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); + class GroupCases { public: // ------------------------------------------------------------------------------------- // @@ -382,15 +390,6 @@ private: /// Spawn all the unsorted codes of the current group. std::vector Group_extend(codec::RawCode raw_code, uint32_t reserve = 0); -static_assert(std::is_standard_layout_v); -static_assert(std::is_trivially_copyable_v); - -static_assert(std::is_standard_layout_v); -static_assert(std::is_trivially_copyable_v); - -static_assert(std::is_standard_layout_v); -static_assert(std::is_trivially_copyable_v); - } // namespace klotski::group #include "internal/type_id.inl" @@ -398,40 +397,5 @@ static_assert(std::is_trivially_copyable_v); #include "internal/group_cases.inl" #include "internal/group.inl" #include "internal/case_info.inl" - -// ----------------------------------------------------------------------------------------- // - -// TODO: move to `hash.inl` - -namespace std { - -template <> -struct std::hash { - constexpr std::size_t operator()(const klotski::group::Group &g) const noexcept { - // TODO: perf hash alg - return std::hash{}(g.type_id() ^ g.pattern_id() ^ (int)g.toward()); - } -}; - -template <> -struct std::hash { - constexpr std::size_t operator()(const klotski::group::GroupUnion &gu) const noexcept { - return std::hash{}(gu.unwrap()); - } -}; - -// TODO: add `std::hash` for CaseInfo - -template <> -struct std::hash { - constexpr std::size_t operator()(const klotski::group::CaseInfo &info) const noexcept { - // TODO: perf hash alg - const auto h1 = std::hash{}(info.group()); - const auto h2 = std::hash{}(info.case_id()); - return h1 ^ h2; - } -}; - -} // namespace std - -// ----------------------------------------------------------------------------------------- // +#include "internal/group_mirror.inl" +#include "internal/hash.inl" diff --git a/src/core/group/internal/case_info.inl b/src/core/group/internal/case_info.inl index 49eacf8..aa2102e 100644 --- a/src/core/group/internal/case_info.inl +++ b/src/core/group/internal/case_info.inl @@ -9,7 +9,7 @@ inline std::ostream& operator<<(std::ostream &out, CaseInfo self) { return out; } -constexpr std::string CaseInfo::to_string() const { +inline std::string CaseInfo::to_string() const { return std::format("{}-{}", group_.to_string(), case_id_); } diff --git a/src/core/group/internal/group.inl b/src/core/group/internal/group.inl index b1bff17..5352f4e 100644 --- a/src/core/group/internal/group.inl +++ b/src/core/group/internal/group.inl @@ -4,11 +4,13 @@ namespace klotski::group { -constexpr uint32_t Group::type_id() const { +// ----------------------------------------------------------------------------------------- // + +constexpr uint_fast8_t Group::type_id() const { return type_id_; } -constexpr uint32_t Group::pattern_id() const { +constexpr uint_fast16_t Group::pattern_id() const { return pattern_id_; } @@ -16,13 +18,6 @@ constexpr auto Group::toward() const -> Toward { return toward_; } -#ifndef KLSK_NDEBUG -inline std::ostream& operator<<(std::ostream &out, Group self) { - out << self.to_string(); - return out; -} -#endif - constexpr char Group::toward_char() const { // TODO: select chars from pre-build std::array switch (mirror_type()) { @@ -46,9 +41,19 @@ constexpr char Group::toward_char() const { } return '\0'; // TODO: never reach } + return '\0'; // TODO: never reach } -constexpr std::string Group::to_string() const { // TODO: `std::string` not support constexpr +// ----------------------------------------------------------------------------------------- // + +#ifndef KLSK_NDEBUG +inline std::ostream& operator<<(std::ostream &out, Group self) { + out << self.to_string(); + return out; +} +#endif + +inline std::string Group::to_string() const { auto c = toward_char(); if (c == '\0') { return std::format("{}-{}", type_id_, pattern_id_); @@ -62,11 +67,11 @@ constexpr auto operator==(const Group &lhs, const Group &rhs) { && lhs.toward_ == rhs.toward_; } -constexpr Group Group::unsafe_create(uint32_t type_id, uint32_t pattern_id, Toward toward) { - return {type_id, pattern_id, toward}; +constexpr Group Group::unsafe_create(uint_fast8_t type_id, uint_fast16_t pattern_id, Toward toward) { + return {type_id, toward, pattern_id}; } -constexpr std::optional Group::create(uint32_t type_id, uint32_t pattern_id, Toward toward) { +constexpr std::optional Group::create(uint_fast8_t type_id, uint_fast16_t pattern_id, Toward toward) { if (type_id >= TYPE_ID_LIMIT) { return std::nullopt; } @@ -93,92 +98,7 @@ inline Group Group::from_common_code(codec::CommonCode common_code) { return from_raw_code(common_code.to_raw_code()); } -constexpr auto Group::mirror_type() const -> MirrorType { - return static_cast(PATTERN_DATA[flat_id()] & 0b111); -} - -constexpr bool Group::is_vertical_mirror() const { - switch (mirror_type()) { - case MirrorType::Full: - return true; - case MirrorType::Horizontal: - return false; - case MirrorType::Centro: - return false; - case MirrorType::Vertical: - return true; - case MirrorType::Ordinary: - return false; - } -} - -constexpr bool Group::is_horizontal_mirror() const { - switch (mirror_type()) { - case MirrorType::Full: - return true; - case MirrorType::Horizontal: - return true; - case MirrorType::Centro: - return false; - case MirrorType::Vertical: - return false; - case MirrorType::Ordinary: - return false; - } -} - -constexpr Group Group::to_vertical_mirror() const { - switch (mirror_type()) { - case MirrorType::Full: - return *this; - case MirrorType::Horizontal: - if (toward_ == Toward::A) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::C); - } - return Group::unsafe_create(type_id_, pattern_id_, Toward::A); - case MirrorType::Centro: - if (toward_ == Toward::A) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::B); - } - return Group::unsafe_create(type_id_, pattern_id_, Toward::A); - case MirrorType::Vertical: - return *this; - case MirrorType::Ordinary: - if (toward_ == Toward::A) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::C); - } else if (toward_ == Toward::B) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::D); - } else if (toward_ == Toward::C) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::A); - } - return Group::unsafe_create(type_id_, pattern_id_, Toward::B); - } -} - -constexpr Group Group::to_horizontal_mirror() const { - switch (mirror_type()) { - case MirrorType::Full: - case MirrorType::Horizontal: - return *this; - case MirrorType::Centro: - case MirrorType::Vertical: - if (toward_ == Toward::A) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::B); - } - return Group::unsafe_create(type_id_, pattern_id_, Toward::A); - case MirrorType::Ordinary: - if (toward_ == Toward::A) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::B); - } else if (toward_ == Toward::B) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::A); - } else if (toward_ == Toward::C) { - return Group::unsafe_create(type_id_, pattern_id_, Toward::D); - } - return Group::unsafe_create(type_id_, pattern_id_, Toward::C); - } -} - -constexpr Group::Group(uint32_t type_id, uint32_t pattern_id, Toward toward) { +constexpr Group::Group(uint_fast8_t type_id, Toward toward, uint_fast16_t pattern_id) { type_id_ = type_id; pattern_id_ = pattern_id; toward_ = toward; diff --git a/src/core/group/internal/group_mirror.inl b/src/core/group/internal/group_mirror.inl new file mode 100644 index 0000000..47fb3d8 --- /dev/null +++ b/src/core/group/internal/group_mirror.inl @@ -0,0 +1,90 @@ +#pragma once + +namespace klotski::group { + +constexpr auto Group::mirror_type() const -> MirrorType { + return static_cast(PATTERN_DATA[flat_id()] & 0b111); +} + +constexpr bool Group::is_vertical_mirror() const { + switch (mirror_type()) { + case MirrorType::Full: + return true; + case MirrorType::Horizontal: + return false; + case MirrorType::Centro: + return false; + case MirrorType::Vertical: + return true; + case MirrorType::Ordinary: + return false; + } +} + +constexpr bool Group::is_horizontal_mirror() const { + switch (mirror_type()) { + case MirrorType::Full: + return true; + case MirrorType::Horizontal: + return true; + case MirrorType::Centro: + return false; + case MirrorType::Vertical: + return false; + case MirrorType::Ordinary: + return false; + } +} + +constexpr Group Group::to_vertical_mirror() const { + switch (mirror_type()) { + case MirrorType::Full: + return *this; + case MirrorType::Horizontal: + if (toward_ == Toward::A) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::C); + } + return Group::unsafe_create(type_id_, pattern_id_, Toward::A); + case MirrorType::Centro: + if (toward_ == Toward::A) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::B); + } + return Group::unsafe_create(type_id_, pattern_id_, Toward::A); + case MirrorType::Vertical: + return *this; + case MirrorType::Ordinary: + if (toward_ == Toward::A) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::C); + } else if (toward_ == Toward::B) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::D); + } else if (toward_ == Toward::C) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::A); + } + return Group::unsafe_create(type_id_, pattern_id_, Toward::B); + } +} + +constexpr Group Group::to_horizontal_mirror() const { + switch (mirror_type()) { + case MirrorType::Full: + case MirrorType::Horizontal: + return *this; + case MirrorType::Centro: + case MirrorType::Vertical: + if (toward_ == Toward::A) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::B); + } + return Group::unsafe_create(type_id_, pattern_id_, Toward::A); + case MirrorType::Ordinary: + if (toward_ == Toward::A) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::B); + } else if (toward_ == Toward::B) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::A); + } else if (toward_ == Toward::C) { + return Group::unsafe_create(type_id_, pattern_id_, Toward::D); + } + return Group::unsafe_create(type_id_, pattern_id_, Toward::C); + } +} + +} // namespace klotski::group diff --git a/src/core/group/internal/group_union.cc b/src/core/group/internal/group_union.cc index 7e8611b..17a555e 100644 --- a/src/core/group/internal/group_union.cc +++ b/src/core/group/internal/group_union.cc @@ -1,4 +1,3 @@ -#include "mover/mover.h" #include "group/group.h" #include "constant/group_union.h" diff --git a/src/core/group/internal/group_union.inl b/src/core/group/internal/group_union.inl index 7d9bf4e..c3a4440 100644 --- a/src/core/group/internal/group_union.inl +++ b/src/core/group/internal/group_union.inl @@ -8,6 +8,24 @@ namespace klotski::group { // ----------------------------------------------------------------------------------------- // +constexpr uint32_t GroupUnion::size() const { + return GROUP_UNION_SIZE[type_id_]; +} + +constexpr uint32_t GroupUnion::group_num() const { + return GROUP_NUM[type_id_]; +} + +constexpr uint32_t GroupUnion::pattern_num() const { + return PATTERN_NUM[type_id_]; +} + +constexpr uint32_t GroupUnion::max_group_size() const { + return MAX_GROUP_SIZE[type_id_]; +} + +// ----------------------------------------------------------------------------------------- // + constexpr uint_fast8_t GroupUnion::unwrap() const { return type_id_; } @@ -25,31 +43,29 @@ constexpr std::optional GroupUnion::create(const uint_fast8_t type_i // ----------------------------------------------------------------------------------------- // -constexpr uint32_t GroupUnion::size() const { - return GROUP_UNION_SIZE[type_id_]; +#ifndef KLSK_NDEBUG +inline std::ostream& operator<<(std::ostream &out, GroupUnion self) { + out << self.type_id_; + return out; } +#endif -constexpr uint32_t GroupUnion::group_num() const { - return GROUP_NUM[type_id_]; +constexpr auto operator==(const GroupUnion &lhs, const GroupUnion &rhs) { + return lhs.type_id_ == rhs.type_id_; } -constexpr uint32_t GroupUnion::pattern_num() const { - return PATTERN_NUM[type_id_]; -} +// ----------------------------------------------------------------------------------------- // -constexpr uint32_t GroupUnion::max_group_size() const { - return MAX_GROUP_SIZE[type_id_]; +constexpr GroupUnion GroupUnion::from_raw_code(const codec::RawCode raw_code) { + return unsafe_create(type_id(raw_code)); } -#ifndef KLSK_NDEBUG -inline std::ostream& operator<<(std::ostream &out, GroupUnion self) { - out << self.type_id_; // TODO: benchmark using `std::format` - return out; +constexpr GroupUnion GroupUnion::from_short_code(const codec::ShortCode short_code) { + return from_common_code(short_code.to_common_code()); } -#endif -constexpr auto operator==(const GroupUnion &lhs, const GroupUnion &rhs) { - return lhs.type_id_ == rhs.type_id_; +constexpr GroupUnion GroupUnion::from_common_code(const codec::CommonCode common_code) { + return unsafe_create(type_id(common_code)); } // ----------------------------------------------------------------------------------------- // @@ -89,7 +105,7 @@ constexpr std::vector GroupUnion::groups() const { return groups; } -constexpr std::optional> GroupUnion::groups(uint32_t pattern_id) const { +constexpr std::optional> GroupUnion::groups(const uint_fast16_t pattern_id) const { if (pattern_id >= pattern_num()) { return std::nullopt; } @@ -117,18 +133,4 @@ constexpr std::optional> GroupUnion::groups(uint32_t pattern_ // ----------------------------------------------------------------------------------------- // -constexpr GroupUnion GroupUnion::from_raw_code(const codec::RawCode raw_code) { - return unsafe_create(type_id(raw_code)); -} - -constexpr GroupUnion GroupUnion::from_short_code(const codec::ShortCode short_code) { - return from_common_code(short_code.to_common_code()); -} - -constexpr GroupUnion GroupUnion::from_common_code(const codec::CommonCode common_code) { - return unsafe_create(type_id(common_code)); -} - -// ----------------------------------------------------------------------------------------- // - } // namespace klotski::group diff --git a/src/core/group/internal/hash.inl b/src/core/group/internal/hash.inl new file mode 100644 index 0000000..aac133c --- /dev/null +++ b/src/core/group/internal/hash.inl @@ -0,0 +1,30 @@ +#pragma once + +namespace std { + +template <> +struct std::hash { + constexpr std::size_t operator()(const klotski::group::Group &g) const noexcept { + // TODO: perf hash alg + return std::hash{}(g.type_id() ^ g.pattern_id() ^ (int)g.toward()); + } +}; + +template <> +struct std::hash { + constexpr std::size_t operator()(const klotski::group::GroupUnion &gu) const noexcept { + return std::hash{}(gu.unwrap()); + } +}; + +template <> +struct std::hash { + constexpr std::size_t operator()(const klotski::group::CaseInfo &info) const noexcept { + // TODO: perf hash alg + const auto h1 = std::hash{}(info.group()); + const auto h2 = std::hash{}(info.case_id()); + return h1 ^ h2; + } +}; + +} // namespace std