Browse Source

feat: TypeId module

master
Dnomd343 1 year ago
parent
commit
db6b4b1235
  1. 2
      src/klotski_core/group/CMakeLists.txt
  2. 38
      src/klotski_core/group/block_num.cc
  3. 74
      src/klotski_core/group/block_num_.cc
  4. 40
      src/klotski_core/group/group.h
  5. 69
      test/group/block_num.cc

2
src/klotski_core/group/CMakeLists.txt

@ -1,3 +1,3 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
add_library(group OBJECT seeds.cc group.cc block_num.cc build_cases.cc group_info.cc) add_library(group OBJECT seeds.cc group.cc block_num.cc block_num_.cc build_cases.cc group_info.cc)

38
src/klotski_core/group/block_num.cc

@ -1,4 +1,6 @@
#include <group.h> #include <stdexcept>
#include <algorithm>
#include "group.h"
#include "common.h" #include "common.h"
#include "type_id.h" #include "type_id.h"
@ -6,15 +8,22 @@ namespace klotski {
using Common::range_reverse; using Common::range_reverse;
uint32_t Group::type_id(const RawCode &raw_code) { TypeId::TypeId(uint32_t type_id) {
return type_id(block_num(raw_code)); if (type_id >= TYPE_ID_LIMIT) { // invalid type id
throw std::invalid_argument("type id overflow");
}
type_id_ = type_id;
}
TypeId::TypeId(const RawCode &raw_code) noexcept {
type_id_ = to_type_id(block_num(raw_code));
} }
uint32_t Group::type_id(const CommonCode &common_code) { TypeId::TypeId(const CommonCode &common_code) noexcept {
return type_id(block_num(common_code)); type_id_ = to_type_id(block_num(common_code));
} }
uint32_t Group::type_id(const block_num_t &block_num) { uint32_t TypeId::to_type_id(block_num_t &&block_num) noexcept {
/// flag -> ... 0000 0xxx 0xxx xxxx /// flag -> ... 0000 0xxx 0xxx xxxx
/// n_x2x n_2x1 n_1x1 /// n_x2x n_2x1 n_1x1
auto n_x2x = block_num.n_1x2 + block_num.n_2x1; auto n_x2x = block_num.n_1x2 + block_num.n_2x1;
@ -22,18 +31,17 @@ uint32_t Group::type_id(const block_num_t &block_num) {
return std::lower_bound(TYPE_ID_INDEX, TYPE_ID_INDEX + TYPE_ID_LIMIT, flag) - TYPE_ID_INDEX; return std::lower_bound(TYPE_ID_INDEX, TYPE_ID_INDEX + TYPE_ID_LIMIT, flag) - TYPE_ID_INDEX;
} }
Group::block_num_t Group::block_num(uint32_t type_id) { TypeId::block_num_t TypeId::block_num() const noexcept {
auto flag = TYPE_ID_INDEX[type_id]; auto flag = TYPE_ID_INDEX[type_id_];
uint8_t n_x2x = flag >> 8; auto n_2x1 = (flag >> 4) & 0b111;
uint8_t n_2x1 = (flag >> 4) & 0b1111;
return block_num_t { return block_num_t {
.n_1x1 = static_cast<uint8_t>(flag & 0b1111), .n_1x1 = static_cast<uint8_t>(flag & 0b1111),
.n_1x2 = static_cast<uint8_t>(n_x2x - n_2x1), .n_1x2 = static_cast<uint8_t>((flag >> 8) - n_2x1),
.n_2x1 = n_2x1, .n_2x1 = static_cast<uint8_t>(n_2x1),
}; };
} }
Group::block_num_t Group::block_num(const RawCode &raw_code) { TypeId::block_num_t TypeId::block_num(const RawCode &raw_code) noexcept {
block_num_t result; block_num_t result;
auto tmp = raw_code.unwrap(); auto tmp = raw_code.unwrap();
for (int addr = 0; addr < 20; ++addr, tmp >>= 3) { for (int addr = 0; addr < 20; ++addr, tmp >>= 3) {
@ -52,9 +60,9 @@ Group::block_num_t Group::block_num(const RawCode &raw_code) {
return result; return result;
} }
Group::block_num_t Group::block_num(const CommonCode &common_code) { TypeId::block_num_t TypeId::block_num(const CommonCode &common_code) noexcept {
block_num_t result; block_num_t result;
auto range = range_reverse((uint32_t)common_code.unwrap()); auto range = range_reverse(static_cast<uint32_t>(common_code.unwrap()));
for (; range; range >>= 2) { for (; range; range >>= 2) {
switch (range & 0b11) { switch (range & 0b11) {
case 0b01: /// 1x2 block case 0b01: /// 1x2 block

74
src/klotski_core/group/block_num_.cc

@ -0,0 +1,74 @@
#include <group.h>
#include "common.h"
#include "type_id.h"
namespace klotski {
using Common::range_reverse;
uint32_t Group::type_id(const RawCode &raw_code) {
return type_id(block_num(raw_code));
}
uint32_t Group::type_id(const CommonCode &common_code) {
return type_id(block_num(common_code));
}
uint32_t Group::type_id(const block_num_t &block_num) {
/// flag -> ... 0000 0xxx 0xxx xxxx
/// n_x2x n_2x1 n_1x1
auto n_x2x = block_num.n_1x2 + block_num.n_2x1;
auto flag = (n_x2x << 8) | (block_num.n_2x1 << 4) | block_num.n_1x1;
return std::lower_bound(TYPE_ID_INDEX, TYPE_ID_INDEX + TYPE_ID_LIMIT, flag) - TYPE_ID_INDEX;
}
Group::block_num_t Group::block_num(uint32_t type_id) {
auto flag = TYPE_ID_INDEX[type_id];
uint8_t n_x2x = flag >> 8;
uint8_t n_2x1 = (flag >> 4) & 0b1111;
return block_num_t {
.n_1x1 = static_cast<uint8_t>(flag & 0b1111),
.n_1x2 = static_cast<uint8_t>(n_x2x - n_2x1),
.n_2x1 = n_2x1,
};
}
Group::block_num_t Group::block_num(const RawCode &raw_code) {
block_num_t result;
auto tmp = raw_code.unwrap();
for (int addr = 0; addr < 20; ++addr, tmp >>= 3) {
switch (tmp & 0b111) {
case B_1x1:
++result.n_1x1;
continue;
case B_1x2:
++result.n_1x2;
continue;
case B_2x1:
++result.n_2x1;
continue;
}
}
return result;
}
Group::block_num_t Group::block_num(const CommonCode &common_code) {
block_num_t result;
auto range = range_reverse((uint32_t)common_code.unwrap());
for (; range; range >>= 2) {
switch (range & 0b11) {
case 0b01: /// 1x2 block
++result.n_1x2;
continue;
case 0b10: /// 2x1 block
++result.n_2x1;
continue;
case 0b11: /// 1x1 block
++result.n_1x1;
continue;
}
}
return result;
}
} // namespace klotski

40
src/klotski_core/group/group.h

@ -8,6 +8,46 @@ namespace klotski {
const uint32_t TYPE_ID_LIMIT = 203; const uint32_t TYPE_ID_LIMIT = 203;
class TypeId {
public:
/// 1. n_1x1 + (n_1x2 + n_2x1) * 2 <= 14
/// 2. (n_1x1 != 0) && (n_2x1 != 7)
struct block_num_t {
uint8_t n_1x1 = 0; /// [0, 14]
uint8_t n_1x2 = 0; /// [0, 7]
uint8_t n_2x1 = 0; /// [0, 7]
};
/// n_space = 16 - n_1x1 - (n_1x2 + n_2x1) * 2
private:
uint32_t type_id_;
static inline uint32_t to_type_id(block_num_t &&block_num) noexcept;
public:
explicit TypeId(uint32_t type_id);
explicit TypeId(const RawCode &raw_code) noexcept;
explicit TypeId(const CommonCode &common_code) noexcept;
/// Release raw type id value.
constexpr uint32_t unwrap() const noexcept { return type_id_; }
/// Get the number of klotski blocks.
block_num_t block_num() const noexcept;
static block_num_t block_num(const RawCode &raw_code) noexcept;
static block_num_t block_num(const CommonCode &common_code) noexcept;
};
inline bool operator==(const TypeId &t1, const TypeId &t2) { return t1.unwrap() == t2.unwrap(); }
inline bool operator!=(const TypeId &t1, const TypeId &t2) { return t1.unwrap() != t2.unwrap(); }
inline bool operator==(const TypeId::block_num_t &b1, const TypeId::block_num_t &b2) {
return (b1.n_1x1 == b2.n_1x1) && (b1.n_1x2 == b2.n_1x2) && (b1.n_2x1 == b2.n_2x1);
}
inline bool operator!=(const TypeId::block_num_t &b1, const TypeId::block_num_t &b2) {
return (b1.n_1x1 != b2.n_1x1) || (b1.n_1x2 != b2.n_1x2) || (b1.n_2x1 != b2.n_2x1);
}
class Group { class Group {
/// -------------------------------- block statistics --------------------------------- /// -------------------------------- block statistics ---------------------------------
public: public:

69
test/group/block_num.cc

@ -4,59 +4,68 @@
#include "all_cases.h" #include "all_cases.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
using klotski::Group; using klotski::TypeId;
using klotski::RawCode;
using klotski::AllCases; using klotski::AllCases;
using klotski::CommonCode; using klotski::CommonCode;
using klotski::TYPE_ID_LIMIT; using klotski::TYPE_ID_LIMIT;
using klotski::ALL_CASES_SIZE;
const char BLOCK_NUM_MD5[] = "46a7b3af6d039cbe2f7eaebdd196c6a2"; const char BLOCK_NUM_MD5[] = "46a7b3af6d039cbe2f7eaebdd196c6a2";
TEST(Group, type_id) { TEST(Group, type_id) {
std::thread threads[16]; for (uint32_t type_id = 0; type_id < TYPE_ID_LIMIT; ++type_id) {
EXPECT_EQ(TypeId(type_id).unwrap(), type_id);
}
try {
EXPECT_EQ(TypeId(TYPE_ID_LIMIT).unwrap(), TYPE_ID_LIMIT + 1);
} catch (...) {} // should panic
auto test = [](uint64_t head) { auto test = [](uint64_t head) {
for (const auto &range : AllCases::fetch()[head]) { for (auto &&range: AllCases::fetch()[head]) {
auto common_code = CommonCode::unsafe_create(head << 32 | range); auto common_code = CommonCode::unsafe_create(head << 32 | range);
auto type_id = Group::type_id(common_code); // found type id auto raw_code = RawCode::from_common_code(common_code);
EXPECT_LT(type_id, TYPE_ID_LIMIT);
EXPECT_EQ(type_id, Group::type_id(common_code.to_raw_code())); auto type_id = TypeId(common_code);
EXPECT_EQ(type_id, Group::type_id(Group::block_num(common_code))); EXPECT_EQ(type_id, TypeId(raw_code));
EXPECT_EQ(Group::block_num(type_id), Group::block_num(common_code)); EXPECT_LT(type_id.unwrap(), TYPE_ID_LIMIT);
EXPECT_EQ(type_id.block_num(), TypeId::block_num(raw_code));
EXPECT_EQ(type_id.block_num(), TypeId::block_num(common_code));
} }
}; };
for (uint64_t head = 0; head < 16; ++head) { // split into 16 threads
threads[head] = std::thread(test, head); std::thread threads[16];
} for (uint64_t head = 0; head < 16; ++head) {
for (auto &t : threads) { threads[head] = std::thread(test, head); // multi-threads verify
t.join();
} }
for (auto &t : threads) { t.join(); }
} }
TEST(Group, block_num) { TEST(Group, block_num) {
std::thread threads[16]; std::string result[16];
std::string block_num_data; auto test = [&result](uint64_t head) {
std::string block_num_str[16];
auto test = [&block_num_str](uint64_t head) {
char buffer[13]; char buffer[13];
result[head].reserve(ALL_CASES_SIZE[head]); // vector pre-allocated
for (auto &&range: AllCases::fetch()[head]) { for (auto &&range: AllCases::fetch()[head]) {
auto common_code = CommonCode::unsafe_create(head << 32 | range); auto common_code = CommonCode::unsafe_create(head << 32 | range);
auto tmp = Group::block_num(common_code); auto tmp = TypeId(common_code).block_num();
EXPECT_LE(tmp.n_1x2 * 2 + tmp.n_2x1 * 2 + tmp.n_1x1, 14); EXPECT_LE(tmp.n_1x2 * 2 + tmp.n_2x1 * 2 + tmp.n_1x1, 14);
EXPECT_EQ(tmp, Group::block_num(common_code.to_raw_code()));
sprintf(buffer, "%d,%d,%d\n", tmp.n_1x2 + tmp.n_2x1, tmp.n_1x1, tmp.n_2x1); sprintf(buffer, "%d,%d,%d\n", tmp.n_1x2 + tmp.n_2x1, tmp.n_1x1, tmp.n_2x1);
block_num_str[head] += buffer; result[head] += buffer;
} }
}; };
for (uint64_t head = 0; head < 16; ++head) { // split into 16 threads std::thread threads[16];
threads[head] = std::thread(test, head); for (uint64_t head = 0; head < 16; ++head) {
} threads[head] = std::thread(test, head); // multi-threads verify
for (auto &t : threads) {
t.join();
} }
for (auto &&tmp : block_num_str) { // combine string for (auto &t : threads) { t.join(); }
block_num_data += tmp;
std::string block_num_str;
for (auto &&tmp : result) {
block_num_str += tmp; // combine result
} }
auto block_num_md5 = md5(block_num_data.c_str(), block_num_data.size()); auto block_num_md5 = md5(block_num_str.c_str(), block_num_str.size());
EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5); EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5); // verify md5
} }

Loading…
Cancel
Save