Browse Source

refactor: enhance group module

master
Dnomd343 1 week ago
parent
commit
a9730dc313
  1. 2
      CMakeLists.txt
  2. 1
      src/core/CMakeLists.txt
  3. 79
      src/core/group/group.h
  4. 15
      src/core/group/group_fwd.h
  5. 40
      src/core/group/internal/case_info.inl
  6. 15
      src/core/group/internal/constant/group.h
  7. 11
      src/core/group/internal/constant/group_union.h
  8. 74
      src/core/group/internal/group.inl
  9. 22
      src/core/group/internal/group_cases.inl
  10. 15
      src/core/group/internal/group_union.cc
  11. 17
      src/core/group/internal/group_union.inl
  12. 30
      src/core/group/internal/hash.inl
  13. 0
      src/core/group/internal/mirror.inl
  14. 2
      src/core/group/internal/type_id.inl
  15. 16
      src/core_test/group/group_union.cc

2
CMakeLists.txt

@ -18,7 +18,7 @@ if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release) set(CMAKE_BUILD_TYPE Release)
endif() endif()
add_compile_options(-Wall -Wextra) add_compile_options(-Wall -Wextra -g)
# TODO: enabled by LTO option # TODO: enabled by LTO option
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")

1
src/core/CMakeLists.txt

@ -38,6 +38,7 @@ add_library(klotski::core ALIAS klotski_core)
if (KLSK_DEV_MODE) if (KLSK_DEV_MODE)
add_executable(klotski_core_bin main.cc) add_executable(klotski_core_bin main.cc)
target_compile_options(klotski_core_bin PRIVATE -fno-rtti -fno-exceptions)
target_link_libraries(klotski_core_bin PRIVATE klotski::core) target_link_libraries(klotski_core_bin PRIVATE klotski::core)
endif() endif()

79
src/core/group/group.h

@ -150,9 +150,12 @@
#pragma once #pragma once
#include "raw_code/raw_code.h" #include <mutex>
#include "short_code/short_code.h"
#include "common_code/common_code.h" #include "ranges/ranges_fwd.h"
#include "raw_code/raw_code_fwd.h"
#include "short_code/short_code_fwd.h"
#include "common_code/common_code_fwd.h"
#include "internal/constant/group.h" #include "internal/constant/group.h"
#include "internal/constant/group_union.h" #include "internal/constant/group_union.h"
@ -195,13 +198,13 @@ public:
using Groups = std::vector<Group>; using Groups = std::vector<Group>;
/// Get all groups under the current type id. /// Get all groups under the current type id.
[[nodiscard]] constexpr Groups groups() const; // TODO: constexpr not support `std::vector` [[nodiscard]] Groups groups() const;
/// Get all klotski cases under the current type id. /// Get all klotski cases under the current type id.
[[nodiscard]] cases::RangesUnion cases() const; [[nodiscard]] cases::RangesUnion cases() const;
/// Get the group instance with the specified pattern id. /// Get the group instance with the specified pattern id.
[[nodiscard]] constexpr std::optional<Groups> groups(uint_least16_t pattern_id) const; [[nodiscard]] std::optional<Groups> groups(uint_least16_t pattern_id) const;
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
@ -240,23 +243,21 @@ private:
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
}; };
// TODO: using macro for all likely-class static_assert(sizeof(GroupUnion) == 1);
static_assert(std::is_standard_layout_v<GroupUnion>); static_assert(std::is_standard_layout_v<GroupUnion>);
static_assert(std::is_trivially_copyable_v<GroupUnion>); static_assert(std::is_trivially_copyable_v<GroupUnion>);
static_assert(!std::is_default_constructible_v<GroupUnion>); static_assert(std::has_unique_object_representations_v<GroupUnion>);
static_assert(std::is_trivially_destructible_v<GroupUnion>);
static_assert(std::is_nothrow_destructible_v<GroupUnion>);
static_assert(std::is_nothrow_copy_assignable_v<GroupUnion>); } // namespace klotski::group
static_assert(std::is_nothrow_move_assignable_v<GroupUnion>);
static_assert(std::is_nothrow_copy_constructible_v<GroupUnion>);
static_assert(std::is_nothrow_move_constructible_v<GroupUnion>);
static_assert(std::is_trivially_copy_assignable_v<GroupUnion>); template <>
static_assert(std::is_trivially_move_assignable_v<GroupUnion>); struct std::hash<klotski::group::GroupUnion> {
static_assert(std::is_trivially_copy_constructible_v<GroupUnion>); constexpr std::size_t operator()(const klotski::group::GroupUnion &gu) const noexcept {
static_assert(std::is_trivially_move_constructible_v<GroupUnion>); return std::hash<uint_fast8_t>{}(gu.unwrap());
}
};
namespace klotski::group {
class Group { class Group {
public: public:
@ -274,7 +275,7 @@ public:
Horizontal = 1, // horizontal self-symmetry Horizontal = 1, // horizontal self-symmetry
Centro = 2, // centrosymmetric Centro = 2, // centrosymmetric
Vertical = 3, // vertical self-symmetry Vertical = 3, // vertical self-symmetry
Ordinary = 4, // non self-symmetric Ordinary = 4, // non-self-symmetric
}; };
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
@ -342,7 +343,7 @@ public:
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
/// Get the group in string form. /// Get the group in string form.
[[nodiscard]] std::string to_string() const; [[nodiscard]] KLSK_INLINE std::string to_string() const;
#ifndef KLSK_NDEBUG #ifndef KLSK_NDEBUG
/// Output group info only for debug. /// Output group info only for debug.
@ -368,8 +369,21 @@ private:
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
}; };
static_assert(sizeof(Group) == 4);
static_assert(std::is_standard_layout_v<Group>); static_assert(std::is_standard_layout_v<Group>);
static_assert(std::is_trivially_copyable_v<Group>); static_assert(std::is_trivially_copyable_v<Group>);
static_assert(std::has_unique_object_representations_v<Group>);
} // namespace klotski::group
template <>
struct std::hash<klotski::group::Group> {
constexpr std::size_t operator()(const klotski::group::Group &g) const noexcept {
return std::hash<uint32_t>{}(std::bit_cast<uint32_t>(g));
}
};
namespace klotski::group {
class CaseInfo { class CaseInfo {
public: public:
@ -394,7 +408,7 @@ public:
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
/// Get case info in string form. /// Get case info in string form.
[[nodiscard]] std::string to_string() const; [[nodiscard]] KLSK_INLINE std::string to_string() const;
#ifndef KLSK_NDEBUG #ifndef KLSK_NDEBUG
/// Output case info only for debug. /// Output case info only for debug.
@ -416,8 +430,21 @@ private:
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
}; };
static_assert(sizeof(CaseInfo) == 8);
static_assert(std::is_standard_layout_v<CaseInfo>); static_assert(std::is_standard_layout_v<CaseInfo>);
static_assert(std::is_trivially_copyable_v<CaseInfo>); static_assert(std::is_trivially_copyable_v<CaseInfo>);
static_assert(std::has_unique_object_representations_v<CaseInfo>);
} // namespace klotski::group
template <>
struct std::hash<klotski::group::CaseInfo> {
constexpr std::size_t operator()(const klotski::group::CaseInfo &info) const noexcept {
return std::hash<uint64_t>{}(std::bit_cast<uint64_t>(info));
}
};
namespace klotski::group {
class GroupCases { class GroupCases {
public: public:
@ -491,12 +518,10 @@ private:
} // namespace klotski::group } // namespace klotski::group
#include "internal/type_id.inl"
#include "internal/group_union.inl"
#include "internal/group.inl" #include "internal/group.inl"
#include "internal/group_mirror.inl"
#include "internal/group_cases.inl" #include "internal/group_cases.inl"
#include "internal/group_union.inl"
#include "internal/mirror.inl"
#include "internal/type_id.inl"
#include "internal/case_info.inl" #include "internal/case_info.inl"
#include "internal/hash.inl"

15
src/core/group/group_fwd.h

@ -0,0 +1,15 @@
/// Klotski Engine by Dnomd343 @2024
#pragma once
#include "internal/constant/group.h"
#include "internal/constant/group_union.h"
namespace klotski::group {
class Group;
class CaseInfo;
class GroupCases;
class GroupUnion;
} // namespace klotski::group

40
src/core/group/internal/case_info.inl

@ -4,25 +4,41 @@
namespace klotski::group { namespace klotski::group {
inline std::ostream& operator<<(std::ostream &out, CaseInfo self) { // ----------------------------------------------------------------------------------------- //
constexpr Group CaseInfo::group() const {
return group_;
}
constexpr uint32_t CaseInfo::case_id() const {
return case_id_;
}
// ----------------------------------------------------------------------------------------- //
KLSK_INLINE_H std::string CaseInfo::to_string() const {
// TODO: benchmark of format `{}-{}{}-{}`
return std::format("{}-{}", group_.to_string(), case_id_);
}
#ifndef KLSK_NDEBUG
inline std::ostream& operator<<(std::ostream &out, const CaseInfo self) {
out << self.to_string(); out << self.to_string();
return out; return out;
} }
#endif
constexpr auto operator==(const CaseInfo &lhs, const CaseInfo &rhs) { constexpr auto operator==(const CaseInfo &lhs, const CaseInfo &rhs) {
return lhs.group_ == rhs.group_ && lhs.case_id_ == rhs.case_id_; return lhs.group_ == rhs.group_ && lhs.case_id_ == rhs.case_id_;
} }
inline std::string CaseInfo::to_string() const { // ----------------------------------------------------------------------------------------- //
return std::format("{}-{}", group_.to_string(), case_id_);
}
constexpr Group CaseInfo::group() const { constexpr CaseInfo::CaseInfo(const Group group, const uint32_t case_id)
return group_; : group_(group), case_id_(case_id) {}
}
constexpr uint32_t CaseInfo::case_id() const { constexpr CaseInfo CaseInfo::unsafe_create(const Group group, const uint32_t case_id) {
return case_id_; return {group, case_id};
} }
constexpr std::optional<CaseInfo> CaseInfo::create(const Group group, const uint32_t case_id) { constexpr std::optional<CaseInfo> CaseInfo::create(const Group group, const uint32_t case_id) {
@ -32,10 +48,6 @@ constexpr std::optional<CaseInfo> CaseInfo::create(const Group group, const uint
return unsafe_create(group, case_id); return unsafe_create(group, case_id);
} }
constexpr CaseInfo CaseInfo::unsafe_create(const Group group, const uint32_t case_id) { // ----------------------------------------------------------------------------------------- //
return {group, case_id};
}
constexpr CaseInfo::CaseInfo(const Group group, const uint32_t case_id) : group_(group), case_id_(case_id) {}
} // namespace klotski::group } // namespace klotski::group

15
src/core/group/internal/constant/group.h

@ -4,14 +4,21 @@
namespace klotski::group { namespace klotski::group {
// seed(36) + size(20) + type(3) /// Properties of all patterns as follows:
/// (H) 5-bit ~ Padding for u64.
/// 36-bit ~ The seed of the group with the Toward::A.
/// 20-bit ~ The number of cases in the included groups.
/// (L) 3-bit ~ MirrorType enumeration of the included groups.
constexpr auto PATTERN_DATA = std::to_array<uint64_t>({ constexpr auto PATTERN_DATA = std::to_array<uint64_t>({
#include "pattern.inc" #include "pattern.inc"
}); });
// seed(36) + type_id(8) + pattern_id(10) + toward(2) /// Properties of all groups as follows:
/// (H) 8-bit ~ Padding for u64.
/// 36-bit ~ The seed of current group.
/// 8-bit ~ The type_id of current group.
/// 10-bit ~ The pattern_id of current group.
/// (L) 2-bit ~ Toward enumeration of current group.
constexpr auto GROUP_DATA = std::to_array<uint64_t>({ constexpr auto GROUP_DATA = std::to_array<uint64_t>({
#include "group.inc" #include "group.inc"
}); });

11
src/core/group/internal/constant/group_union.h

@ -7,8 +7,6 @@
namespace klotski::group { namespace klotski::group {
constexpr uint32_t TYPE_ID_LIMIT = 203; constexpr uint32_t TYPE_ID_LIMIT = 203;
constexpr uint32_t ALL_GROUP_NUM = 25422; // TODO: from GROUP_NUM
constexpr uint32_t ALL_PATTERN_NUM = 6577; // TODO: from PATTERN_NUM
/// The number of groups contained in GroupUnion. /// The number of groups contained in GroupUnion.
constexpr auto GROUP_NUM = std::to_array<uint16_t>({ constexpr auto GROUP_NUM = std::to_array<uint16_t>({
@ -27,8 +25,11 @@ constexpr auto GROUP_NUM = std::to_array<uint16_t>({
214, 6 , 18 , 54 , 2 , 44 , 40 , 124 , 84 , 70 , 18 , 214, 6 , 18 , 54 , 2 , 44 , 40 , 124 , 84 , 70 , 18 ,
}); });
constexpr uint32_t ALL_GROUP_NUM = array_sum(GROUP_NUM);
constexpr auto GROUP_OFFSET = to_offset<uint16_t, 203>(GROUP_NUM); constexpr auto GROUP_OFFSET = to_offset<uint16_t, 203>(GROUP_NUM);
/// The number of patterns contained in GroupUnion.
constexpr auto PATTERN_NUM = std::to_array<uint16_t>({ constexpr auto PATTERN_NUM = std::to_array<uint16_t>({
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 4 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 4 , 1 , 1 , 1 , 1 ,
@ -45,7 +46,9 @@ constexpr auto PATTERN_NUM = std::to_array<uint16_t>({
58, 2 , 6 , 15 , 1 , 11 , 12, 31 , 24 , 19 , 6 , 58, 2 , 6 , 15 , 1 , 11 , 12, 31 , 24 , 19 , 6 ,
}); });
const auto PATTERN_OFFSET = to_offset<uint16_t, 203>(PATTERN_NUM); constexpr uint32_t ALL_PATTERN_NUM = array_sum(PATTERN_NUM);
constexpr auto PATTERN_OFFSET = to_offset<uint16_t, 203>(PATTERN_NUM);
/// The maximum Group size in each GroupUnion. /// The maximum Group size in each GroupUnion.
constexpr auto MAX_GROUP_SIZE = std::to_array<uint32_t>({ constexpr auto MAX_GROUP_SIZE = std::to_array<uint32_t>({
@ -119,7 +122,7 @@ constexpr auto BLOCK_NUM = std::to_array<std::tuple<uint8_t, uint8_t, uint8_t>>(
{7, 4, 0 }, {7, 5, 0 }, {7, 6, 0 }, {7, 4, 0 }, {7, 5, 0 }, {7, 6, 0 },
}); });
/// The number of cases with different heads in GroupUnion. /// The number of cases with different heads in GroupUnion. (0/2/12/14, 1/13, 4/6/8/10, 5/9)
constexpr auto GROUP_UNION_CASES_NUM = std::to_array<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>>({ constexpr auto GROUP_UNION_CASES_NUM = std::to_array<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>>({
{1 , 1 , 1 , 1 }, {16 , 16 , 16 , 16 }, {120 , 120 , 120 , 120 }, {1 , 1 , 1 , 1 }, {16 , 16 , 16 , 16 }, {120 , 120 , 120 , 120 },
{560 , 560 , 560 , 560 }, {1820 , 1820 , 1820 , 1820 }, {4368 , 4368 , 4368 , 4368 }, {560 , 560 , 560 , 560 }, {1820 , 1820 , 1820 , 1820 }, {4368 , 4368 , 4368 , 4368 },

74
src/core/group/internal/group.inl

@ -2,12 +2,23 @@
#include <format> #include <format>
#include "constant/group.h" #include "short_code/short_code.h"
#include "common_code/common_code.h"
namespace klotski::group { namespace klotski::group {
// ----------------------------------------------------------------------------------------- // // ----------------------------------------------------------------------------------------- //
constexpr uint32_t Group::flat_id() const {
return PATTERN_OFFSET[type_id_] + pattern_id_;
}
constexpr uint32_t Group::size() const {
return (PATTERN_DATA[flat_id()] >> 3) & 0xFFFFF;
}
// ----------------------------------------------------------------------------------------- //
constexpr auto Group::toward() const -> Toward { constexpr auto Group::toward() const -> Toward {
return toward_; return toward_;
} }
@ -24,11 +35,11 @@ constexpr char Group::toward_char() const {
// TODO: select chars from pre-build std::array // TODO: select chars from pre-build std::array
switch (mirror_type()) { switch (mirror_type()) {
case MirrorType::Full: case MirrorType::Full:
return '\0'; return 'x';
case MirrorType::Horizontal: case MirrorType::Horizontal:
return (toward_ == Toward::A) ? 'n' : 'u'; return (toward_ == Toward::A) ? 'n' : 'u';
case MirrorType::Centro: case MirrorType::Centro:
return (toward_ == Toward::A) ? 'h' : 's'; return (toward_ == Toward::A) ? 's' : 'o';
case MirrorType::Vertical: case MirrorType::Vertical:
return (toward_ == Toward::A) ? 'p' : 'q'; return (toward_ == Toward::A) ? 'p' : 'q';
case MirrorType::Ordinary: case MirrorType::Ordinary:
@ -50,6 +61,16 @@ constexpr char Group::toward_char() const {
// ----------------------------------------------------------------------------------------- // // ----------------------------------------------------------------------------------------- //
inline Group Group::from_short_code(const codec::ShortCode short_code) {
return from_common_code(short_code.to_common_code());
}
inline Group Group::from_common_code(const codec::CommonCode common_code) {
return from_raw_code(common_code.to_raw_code());
}
// ----------------------------------------------------------------------------------------- //
#ifndef KLSK_NDEBUG #ifndef KLSK_NDEBUG
inline std::ostream& operator<<(std::ostream &out, const Group self) { inline std::ostream& operator<<(std::ostream &out, const Group self) {
out << self.to_string(); out << self.to_string();
@ -57,12 +78,8 @@ inline std::ostream& operator<<(std::ostream &out, const Group self) {
} }
#endif #endif
inline std::string Group::to_string() const { KLSK_INLINE_H std::string Group::to_string() const {
auto c = toward_char(); return std::format("{}-{}{}", type_id_, pattern_id_, toward_char());
if (c == '\0') {
return std::format("{}-{}", type_id_, pattern_id_);
}
return std::format("{}-{}{}", type_id_, pattern_id_, c);
} }
constexpr auto operator==(const Group &lhs, const Group &rhs) { constexpr auto operator==(const Group &lhs, const Group &rhs) {
@ -71,18 +88,27 @@ constexpr auto operator==(const Group &lhs, const Group &rhs) {
&& lhs.pattern_id_ == rhs.pattern_id_; && lhs.pattern_id_ == rhs.pattern_id_;
} }
constexpr Group Group::unsafe_create(const uint_fast8_t type_id, const uint_least16_t pattern_id, const Toward toward) { // ----------------------------------------------------------------------------------------- //
constexpr Group::Group(const Toward toward,
const uint_fast8_t type_id,
const uint_least16_t pattern_id)
: toward_(toward), type_id_(type_id), pattern_id_(pattern_id) {}
constexpr Group Group::unsafe_create(const uint_fast8_t type_id,
const uint_least16_t pattern_id,
const Toward toward) {
return {toward, type_id, pattern_id}; return {toward, type_id, pattern_id};
} }
constexpr std::optional<Group> Group::create(const uint_fast8_t type_id, const uint_least16_t pattern_id, const Toward toward) { constexpr std::optional<Group> Group::create(const uint_fast8_t type_id,
if (type_id >= TYPE_ID_LIMIT) { const uint_least16_t pattern_id,
return std::nullopt; const Toward toward) {
} if (type_id >= TYPE_ID_LIMIT || pattern_id >= PATTERN_NUM[type_id]) {
if (pattern_id >= GroupUnion::unsafe_create(type_id).pattern_num()) {
return std::nullopt; return std::nullopt;
} }
// TODO: perf check process
if (toward == Toward::A) { if (toward == Toward::A) {
return unsafe_create(type_id, pattern_id, toward); return unsafe_create(type_id, pattern_id, toward);
} }
@ -122,22 +148,6 @@ constexpr std::optional<Group> Group::create(const uint_fast8_t type_id, const u
return std::nullopt; // TODO: never reach return std::nullopt; // TODO: never reach
} }
constexpr uint32_t Group::flat_id() const { // ----------------------------------------------------------------------------------------- //
return PATTERN_OFFSET[type_id_] + pattern_id_;
}
constexpr uint32_t Group::size() const {
return (PATTERN_DATA[flat_id()] >> 3) & 0xFFFFF;
}
inline Group Group::from_short_code(const codec::ShortCode short_code) {
return from_common_code(short_code.to_common_code());
}
inline Group Group::from_common_code(const codec::CommonCode common_code) {
return from_raw_code(common_code.to_raw_code());
}
constexpr Group::Group(const Toward toward, const uint_fast8_t type_id, const uint_least16_t pattern_id) : toward_(toward), type_id_(type_id), pattern_id_(pattern_id) {}
} // namespace klotski::group } // namespace klotski::group

22
src/core/group/internal/group_cases.inl

@ -2,18 +2,20 @@
namespace klotski::group { namespace klotski::group {
inline codec::CommonCode GroupCases::obtain_code(CaseInfo info) { // TODO: maybe move `fast_obtain_*` into `.inl` file
inline codec::CommonCode GroupCases::obtain_code(const CaseInfo info) {
if (fast_) { if (fast_) {
return fast_obtain_code(info); return fast_obtain_code(info);
} }
return tiny_obtain_code(info); return tiny_obtain_code(info);
} }
inline CaseInfo GroupCases::obtain_info(const codec::CommonCode common_code) { inline Group GroupCases::obtain_group(const codec::ShortCode short_code) {
if (fast_) { if (fast_) {
return fast_obtain_info(common_code); return fast_obtain_group(short_code);
} }
return tiny_obtain_info(common_code); return Group::from_short_code(short_code);
} }
inline Group GroupCases::obtain_group(const codec::CommonCode common_code) { inline Group GroupCases::obtain_group(const codec::CommonCode common_code) {
@ -23,18 +25,18 @@ inline Group GroupCases::obtain_group(const codec::CommonCode common_code) {
return Group::from_common_code(common_code); return Group::from_common_code(common_code);
} }
inline Group GroupCases::obtain_group(const codec::ShortCode short_code) { inline CaseInfo GroupCases::obtain_info(const codec::ShortCode short_code) {
if (fast_) { if (fast_) {
return fast_obtain_group(short_code); return fast_obtain_info(short_code);
} }
return Group::from_short_code(short_code); return tiny_obtain_info(short_code.to_common_code());
} }
inline CaseInfo GroupCases::obtain_info(const codec::ShortCode short_code) { inline CaseInfo GroupCases::obtain_info(const codec::CommonCode common_code) {
if (fast_) { if (fast_) {
return fast_obtain_info(short_code); return fast_obtain_info(common_code);
} }
return tiny_obtain_info(short_code.to_common_code()); return tiny_obtain_info(common_code);
} }
} // namespace klotski::group } // namespace klotski::group

15
src/core/group/internal/group_union.cc

@ -1,5 +1,4 @@
#include "group/group.h" #include "group/group.h"
#include "constant/group_union.h"
using klotski::cases::Ranges; using klotski::cases::Ranges;
using klotski::group::GroupUnion; using klotski::group::GroupUnion;
@ -8,21 +7,21 @@ using klotski::cases::RangesUnion;
using klotski::cases::BASIC_RANGES_NUM; using klotski::cases::BASIC_RANGES_NUM;
#define RANGE_DERIVE(HEAD) ranges.derive(HEAD, cases.ranges(HEAD)) #define RANGE_DERIVE(HEAD) ranges.derive(HEAD, cases.ranges(HEAD))
#define RANGE_RESERVE(HEAD, SIZE) cases.ranges(HEAD).reserve(SIZE)
RangesUnion GroupUnion::cases() const { RangesUnion GroupUnion::cases() const {
auto [n, n_2x1, n_1x1] = BLOCK_NUM[type_id_];
auto [s_a, s_b, s_c, s_d] = GROUP_UNION_CASES_NUM[type_id_];
Ranges ranges {}; Ranges ranges {};
ranges.reserve(BASIC_RANGES_NUM[type_id_]); ranges.reserve(BASIC_RANGES_NUM[type_id_]);
const auto [n, n_2x1, n_1x1] = BLOCK_NUM[type_id_];
ranges.spawn(n, n_2x1, n_1x1); ranges.spawn(n, n_2x1, n_1x1);
ranges.reverse(); ranges.reverse();
RangesUnion cases; RangesUnion cases;
cases.ranges(0x0).reserve(s_a); cases.ranges(0x1).reserve(s_b); cases.ranges(0x2).reserve(s_a); const auto [s_a, s_b, s_c, s_d] = GROUP_UNION_CASES_NUM[type_id_];
cases.ranges(0x4).reserve(s_c); cases.ranges(0x5).reserve(s_d); cases.ranges(0x6).reserve(s_c); RANGE_RESERVE(0x0, s_a); RANGE_RESERVE(0x1, s_b); RANGE_RESERVE(0x2, s_a);
cases.ranges(0x8).reserve(s_c); cases.ranges(0x9).reserve(s_d); cases.ranges(0xA).reserve(s_c); RANGE_RESERVE(0x4, s_c); RANGE_RESERVE(0x5, s_d); RANGE_RESERVE(0x6, s_c);
cases.ranges(0xC).reserve(s_a); cases.ranges(0xD).reserve(s_b); cases.ranges(0xE).reserve(s_a); RANGE_RESERVE(0x8, s_c); RANGE_RESERVE(0x9, s_d); RANGE_RESERVE(0xA, s_c);
RANGE_RESERVE(0xC, s_a); RANGE_RESERVE(0xD, s_b); RANGE_RESERVE(0xE, s_a);
RANGE_DERIVE(0x0); RANGE_DERIVE(0x1); RANGE_DERIVE(0x2); RANGE_DERIVE(0x0); RANGE_DERIVE(0x1); RANGE_DERIVE(0x2);
RANGE_DERIVE(0x4); RANGE_DERIVE(0x5); RANGE_DERIVE(0x6); RANGE_DERIVE(0x4); RANGE_DERIVE(0x5); RANGE_DERIVE(0x6);

17
src/core/group/internal/group_union.inl

@ -1,7 +1,10 @@
#pragma once #pragma once
#include <ranges> #include <ranges>
#include <ostream> // TODO: only for debug output
#include "raw_code/raw_code.h"
#include "short_code/short_code.h"
#include "common_code/common_code.h"
namespace klotski::group { namespace klotski::group {
@ -44,7 +47,7 @@ constexpr std::optional<GroupUnion> GroupUnion::create(const uint_fast8_t type_i
#ifndef KLSK_NDEBUG #ifndef KLSK_NDEBUG
inline std::ostream& operator<<(std::ostream &out, GroupUnion self) { inline std::ostream& operator<<(std::ostream &out, GroupUnion self) {
out << static_cast<int>(self.type_id_); out << std::format("{}", self.type_id_);
return out; return out;
} }
#endif #endif
@ -69,8 +72,9 @@ constexpr GroupUnion GroupUnion::from_common_code(const codec::CommonCode common
// ----------------------------------------------------------------------------------------- // // ----------------------------------------------------------------------------------------- //
constexpr auto GroupUnion::groups() const -> Groups { // TODO: benchmark of force-inline
auto build = [this](size_t offset) { inline auto GroupUnion::groups() const -> Groups {
auto build = [this](const size_t offset) {
const auto raw = GROUP_DATA[offset]; const auto raw = GROUP_DATA[offset];
const uint_fast16_t pattern_id = (raw >> 2) & 0x3FF; const uint_fast16_t pattern_id = (raw >> 2) & 0x3FF;
const auto toward = static_cast<Group::Toward>(raw & 0b11); const auto toward = static_cast<Group::Toward>(raw & 0b11);
@ -82,12 +86,13 @@ constexpr auto GroupUnion::groups() const -> Groups {
| std::ranges::to<std::vector>(); | std::ranges::to<std::vector>();
} }
constexpr auto GroupUnion::groups(const uint_least16_t pattern_id) const -> std::optional<Groups> { // TODO: benchmark of force-inline
inline auto GroupUnion::groups(const uint_least16_t pattern_id) const -> std::optional<Groups> {
if (pattern_id >= pattern_num()) { if (pattern_id >= pattern_num()) {
return std::nullopt; return std::nullopt;
} }
auto flat_id = PATTERN_OFFSET[type_id_] + pattern_id; const auto flat_id = PATTERN_OFFSET[type_id_] + pattern_id;
switch (static_cast<Group::MirrorType>(PATTERN_DATA[flat_id] & 0b111)) { switch (static_cast<Group::MirrorType>(PATTERN_DATA[flat_id] & 0b111)) {
case Group::MirrorType::Full: case Group::MirrorType::Full:
return Groups { return Groups {

30
src/core/group/internal/hash.inl

@ -1,30 +0,0 @@
#pragma once
namespace std {
template <>
struct hash<klotski::group::Group> {
constexpr std::size_t operator()(const klotski::group::Group &g) const noexcept {
// TODO: perf hash alg
return std::hash<uint64_t>{}(g.type_id() ^ g.pattern_id() ^ (int)g.toward());
}
};
template <>
struct hash<klotski::group::GroupUnion> {
constexpr std::size_t operator()(const klotski::group::GroupUnion &gu) const noexcept {
return std::hash<uint32_t>{}(gu.unwrap());
}
};
template <>
struct hash<klotski::group::CaseInfo> {
constexpr std::size_t operator()(const klotski::group::CaseInfo &info) const noexcept {
// TODO: perf hash alg
const auto h1 = std::hash<klotski::group::Group>{}(info.group());
const auto h2 = std::hash<uint32_t>{}(info.case_id());
return h1 ^ h2;
}
};
} // namespace std

0
src/core/group/internal/group_mirror.inl → src/core/group/internal/mirror.inl

2
src/core/group/internal/type_id.inl

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "common_code/common_code.h"
namespace klotski::group { namespace klotski::group {
KLSK_INLINE_CE uint_fast8_t to_type_id(const int n, const int n_2x1, const int n_1x1) { KLSK_INLINE_CE uint_fast8_t to_type_id(const int n, const int n_2x1, const int n_1x1) {

16
src/core_test/group/group_union.cc

@ -22,6 +22,22 @@ using klotski::group::GroupUnion;
using klotski::group::TYPE_ID_LIMIT; using klotski::group::TYPE_ID_LIMIT;
using klotski::group::ALL_GROUP_NUM; using klotski::group::ALL_GROUP_NUM;
static_assert(std::is_standard_layout_v<GroupUnion>);
static_assert(std::is_trivially_copyable_v<GroupUnion>);
static_assert(!std::is_default_constructible_v<GroupUnion>);
static_assert(std::is_trivially_destructible_v<GroupUnion>);
static_assert(std::is_nothrow_destructible_v<GroupUnion>);
static_assert(std::is_nothrow_copy_assignable_v<GroupUnion>);
static_assert(std::is_nothrow_move_assignable_v<GroupUnion>);
static_assert(std::is_nothrow_copy_constructible_v<GroupUnion>);
static_assert(std::is_nothrow_move_constructible_v<GroupUnion>);
static_assert(std::is_trivially_copy_assignable_v<GroupUnion>);
static_assert(std::is_trivially_move_assignable_v<GroupUnion>);
static_assert(std::is_trivially_copy_constructible_v<GroupUnion>);
static_assert(std::is_trivially_move_constructible_v<GroupUnion>);
#define EXPECT_REPEAT(R, val) \ #define EXPECT_REPEAT(R, val) \
EXPECT_FALSE(R.empty()); \ EXPECT_FALSE(R.empty()); \
EXPECT_EQ(R.front(), val); \ EXPECT_EQ(R.front(), val); \

Loading…
Cancel
Save