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)
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 "type_id.h"
@ -6,15 +8,22 @@ namespace klotski {
using Common::range_reverse;
uint32_t Group::type_id(const RawCode &raw_code) {
return type_id(block_num(raw_code));
TypeId::TypeId(uint32_t type_id) {
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) {
return type_id(block_num(common_code));
TypeId::TypeId(const CommonCode &common_code) noexcept {
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
/// n_x2x n_2x1 n_1x1
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;
}
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;
TypeId::block_num_t TypeId::block_num() const noexcept {
auto flag = TYPE_ID_INDEX[type_id_];
auto n_2x1 = (flag >> 4) & 0b111;
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,
.n_1x2 = static_cast<uint8_t>((flag >> 8) - 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;
auto tmp = raw_code.unwrap();
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;
}
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;
auto range = range_reverse((uint32_t)common_code.unwrap());
auto range = range_reverse(static_cast<uint32_t>(common_code.unwrap()));
for (; range; range >>= 2) {
switch (range & 0b11) {
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;
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 {
/// -------------------------------- block statistics ---------------------------------
public:

69
test/group/block_num.cc

@ -4,59 +4,68 @@
#include "all_cases.h"
#include "gtest/gtest.h"
using klotski::Group;
using klotski::TypeId;
using klotski::RawCode;
using klotski::AllCases;
using klotski::CommonCode;
using klotski::TYPE_ID_LIMIT;
using klotski::ALL_CASES_SIZE;
const char BLOCK_NUM_MD5[] = "46a7b3af6d039cbe2f7eaebdd196c6a2";
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) {
for (const auto &range : AllCases::fetch()[head]) {
for (auto &&range: AllCases::fetch()[head]) {
auto common_code = CommonCode::unsafe_create(head << 32 | range);
auto type_id = Group::type_id(common_code); // found type id
EXPECT_LT(type_id, TYPE_ID_LIMIT);
EXPECT_EQ(type_id, Group::type_id(common_code.to_raw_code()));
EXPECT_EQ(type_id, Group::type_id(Group::block_num(common_code)));
EXPECT_EQ(Group::block_num(type_id), Group::block_num(common_code));
auto raw_code = RawCode::from_common_code(common_code);
auto type_id = TypeId(common_code);
EXPECT_EQ(type_id, TypeId(raw_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);
}
for (auto &t : threads) {
t.join();
std::thread threads[16];
for (uint64_t head = 0; head < 16; ++head) {
threads[head] = std::thread(test, head); // multi-threads verify
}
for (auto &t : threads) { t.join(); }
}
TEST(Group, block_num) {
std::thread threads[16];
std::string block_num_data;
std::string block_num_str[16];
auto test = [&block_num_str](uint64_t head) {
std::string result[16];
auto test = [&result](uint64_t head) {
char buffer[13];
result[head].reserve(ALL_CASES_SIZE[head]); // vector pre-allocated
for (auto &&range: AllCases::fetch()[head]) {
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_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);
block_num_str[head] += buffer;
result[head] += buffer;
}
};
for (uint64_t head = 0; head < 16; ++head) { // split into 16 threads
threads[head] = std::thread(test, head);
}
for (auto &t : threads) {
t.join();
std::thread threads[16];
for (uint64_t head = 0; head < 16; ++head) {
threads[head] = std::thread(test, head); // multi-threads verify
}
for (auto &&tmp : block_num_str) { // combine string
block_num_data += tmp;
for (auto &t : threads) { t.join(); }
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());
EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5);
auto block_num_md5 = md5(block_num_str.c_str(), block_num_str.size());
EXPECT_STREQ(block_num_md5.c_str(), BLOCK_NUM_MD5); // verify md5
}

Loading…
Cancel
Save