mirror of https://github.com/dnomd343/klotski.git
Dnomd343
1 year ago
8 changed files with 377 additions and 3 deletions
@ -0,0 +1 @@ |
|||
#include "common_code.h" |
@ -0,0 +1,141 @@ |
|||
#pragma once |
|||
|
|||
/// CommonCode is a generic klotski encoding that records an valid case using
|
|||
/// 36-bit lengths, and stored in a `uint64_t`.
|
|||
|
|||
/// Since there is only one `2x2` block, it is encoded separately. Its upper
|
|||
/// left corner is called `head`, it has 12 possible positions and is encoded
|
|||
/// using 4-bit length (0 ~ 15).
|
|||
///
|
|||
/// 00 01 02 03
|
|||
/// 04 05 06 07 00 01 02
|
|||
/// 08 09 10 11 04 05 06 <- head of 2x2 block
|
|||
/// 12 13 14 15 08 09 10 (without 03/07/11/15)
|
|||
/// 16 17 18 19 12 13 14
|
|||
|
|||
/// Treat spaces as special blocks, there can be four kinds of blocks in total,
|
|||
/// namely `space`, `1x2`, `2x1`, `1x1`. Each of them is represented by 2-bit,
|
|||
/// which are `00` `01` `10` `11`. Arrange them according to their position and
|
|||
/// size, and we can get a binary sequence.
|
|||
///
|
|||
/// ( 2x2 -> # # ) | ( 2x1 -> # ) | ( 1x2 -> # # ) | ( 1x1 -> # )
|
|||
/// ( # # ) | ( # ) | |
|
|||
|
|||
/// This sequence can have up to 16 blocks, aka 32-bit in length. Therefore, in
|
|||
/// order to be compatible with all cases, the length of this part of the code
|
|||
/// is set to 32-bit. In addition, for the convenience of reading, it is
|
|||
/// stipulated that the sequence starts from the high bit, and the remaining
|
|||
/// bits should be filled with `0`.
|
|||
|
|||
/// Putting the content of the `head` in the upper 4-bit, and the lower 32-bit
|
|||
/// to store the sequence content, a 36-bit length code can be obtained, which
|
|||
/// corresponds to any valid layout one-to-one. When CommonCode is converted
|
|||
/// into a string, just directly export the hexadecimal data, and get a 9-bit
|
|||
/// string encoding. Characters are not case-sensitive, but it is recommended
|
|||
/// to use uppercase letters. In addition, the last `0` of the string is
|
|||
/// allowed to be omitted, and it can be completed to 9 digits when decoding,
|
|||
/// but note that if the encoding is all `0`, it should be reduced to the
|
|||
/// remaining one `0`.
|
|||
|
|||
/// Eg1:
|
|||
/// % # # % 2x2 -> head = 1
|
|||
/// % # # % 2x1 2x1 2x1 1x2 2x1 1x1 1x1 1x1 space space 1x1 ... ... ... ... ...
|
|||
/// @ $ $ @ 10 10 10 01 10 11 11 11 00 00 11 00 00 00 00 00
|
|||
/// @ & * @ 1010 1001 1011 1111 0000 1100 0000 0000
|
|||
/// * & A 9 B F 0 C 0 0
|
|||
/// CommonCode = 0x1A9BF0C00 -> "1A9BF0C"
|
|||
|
|||
/// Eg2:
|
|||
/// * @ & % 2x2 -> head = 4
|
|||
/// # # $ % 1x1 1x1 1x1 2x1 2x1 2x1 space 1x2 space 1x1 1x2 ... ... ... ... ...
|
|||
/// # # $ ^ 11 11 11 10 10 10 00 01 00 11 01 00 00 00 00 00
|
|||
/// ~ ~ ^ 1111 1110 1010 0001 0011 0100 0000 0000
|
|||
/// @ % % F E A 1 3 4 0 0
|
|||
/// CommonCode = 0x4FEA13400 -> "4FEA134"
|
|||
|
|||
#include <vector> |
|||
#include <cstdint> |
|||
#include <optional> |
|||
|
|||
namespace klotski { |
|||
namespace codec { |
|||
|
|||
class RawCode; |
|||
class ShortCode; |
|||
class CommonCode; |
|||
|
|||
//typedef std::vector<RawCode> RawCodes;
|
|||
//typedef std::vector<ShortCode> ShortCodes;
|
|||
//typedef std::vector<CommonCode> CommonCodes;
|
|||
|
|||
class CommonCode { |
|||
public: |
|||
/// Validity check
|
|||
static bool check(uint64_t common_code) noexcept; |
|||
|
|||
/// Operators of CommonCode
|
|||
explicit operator uint64_t() const noexcept { return code_; } |
|||
friend std::ostream& operator<<(std::ostream &out, const CommonCode &self); |
|||
|
|||
/// Export functions
|
|||
RawCode to_raw_code() const noexcept; |
|||
ShortCode to_short_code() const noexcept; |
|||
std::string to_string(bool shorten = false) const noexcept; |
|||
constexpr uint64_t unwrap() const noexcept { return code_; } |
|||
|
|||
/// CommonCode constructors
|
|||
CommonCode() = delete; |
|||
explicit CommonCode(RawCode raw_code) noexcept; |
|||
explicit CommonCode(ShortCode short_code) noexcept; |
|||
|
|||
/// CommonCode initializations
|
|||
static CommonCode unsafe_create(uint64_t common_code) noexcept; |
|||
static std::optional<CommonCode> create(uint64_t common_code) noexcept; |
|||
|
|||
static std::optional<CommonCode> from_string(std::string &&common_code) noexcept; |
|||
static std::optional<CommonCode> from_string(const std::string &common_code) noexcept; |
|||
|
|||
static CommonCode from_raw_code(RawCode raw_code) noexcept; |
|||
static std::optional<CommonCode> from_raw_code(uint64_t raw_code) noexcept; |
|||
|
|||
static CommonCode from_short_code(ShortCode short_code) noexcept; |
|||
static std::optional<CommonCode> from_short_code(uint32_t short_code) noexcept; |
|||
static std::optional<CommonCode> from_short_code(std::string &&short_code) noexcept; |
|||
static std::optional<CommonCode> from_short_code(const std::string &short_code) noexcept; |
|||
|
|||
/// Batch conversions
|
|||
// static CommonCodes convert(const RawCodes &raw_codes) noexcept;
|
|||
// static CommonCodes convert(const ShortCodes &short_codes) noexcept;
|
|||
|
|||
static std::string string_encode(uint64_t common_code, bool shorten) noexcept; |
|||
static std::optional<uint64_t> string_decode(const std::string &common_code) noexcept; |
|||
|
|||
private: |
|||
uint64_t code_; |
|||
// static std::string string_encode(uint64_t common_code, bool shorten) noexcept;
|
|||
// static std::optional<uint64_t> string_decode(const std::string &common_code) noexcept;
|
|||
}; |
|||
|
|||
/// CommonCode create
|
|||
//inline CommonCode CommonCode::create(uint64_t common_code) {
|
|||
// return CommonCode(common_code); // with check
|
|||
//}
|
|||
|
|||
/// CommonCode create without check
|
|||
inline CommonCode CommonCode::unsafe_create(uint64_t common_code) noexcept { |
|||
return *(CommonCode*)&common_code; // init directly
|
|||
} |
|||
|
|||
/// Compare implements
|
|||
//inline bool operator==(uint64_t c1, const CommonCode &c2) noexcept { return c1 == c2.unwrap(); }
|
|||
//inline bool operator==(const CommonCode &c1, uint64_t c2) noexcept { return c1.unwrap() == c2; }
|
|||
//inline bool operator!=(uint64_t c1, const CommonCode &c2) noexcept { return !(c1 == c2); }
|
|||
//inline bool operator!=(const CommonCode &c1, uint64_t c2) noexcept { return !(c1 == c2); }
|
|||
|
|||
//inline bool operator<(const CommonCode &c1, const CommonCode &c2) noexcept { return c1.unwrap() < c2.unwrap(); }
|
|||
//inline bool operator>(const CommonCode &c1, const CommonCode &c2) noexcept { return c1.unwrap() > c2.unwrap(); }
|
|||
//inline bool operator==(const CommonCode &c1, const CommonCode &c2) noexcept { return c1.unwrap() == c2.unwrap(); }
|
|||
//inline bool operator!=(const CommonCode &c1, const CommonCode &c2) noexcept { return !(c1 == c2); }
|
|||
|
|||
} // namespace codec
|
|||
} // namespace klotski
|
@ -0,0 +1,89 @@ |
|||
#include "common_code.h" |
|||
|
|||
namespace klotski { |
|||
namespace codec { |
|||
|
|||
//using klotski::RawCode;
|
|||
//using klotski::ShortCode;
|
|||
//using klotski::CommonCode;
|
|||
//
|
|||
//using klotski::RawCodes;
|
|||
//using klotski::ShortCode;
|
|||
//using klotski::CommonCodes;
|
|||
|
|||
/// -------------------------- CommonCode to RawCode --------------------------
|
|||
|
|||
//RawCode CommonCode::to_raw_code() const noexcept {
|
|||
// return RawCode(*this); // convert to raw code
|
|||
//}
|
|||
|
|||
/// ------------------------- CommonCode to ShortCode -------------------------
|
|||
|
|||
//ShortCode CommonCode::to_short_code() const noexcept {
|
|||
// return ShortCode(*this); // convert to short code
|
|||
//}
|
|||
|
|||
/// -------------------------- RawCode to CommonCode --------------------------
|
|||
|
|||
//CommonCode CommonCode::from_raw_code(uint64_t raw_code) {
|
|||
// return RawCode(raw_code).to_common_code();
|
|||
//}
|
|||
//
|
|||
//CommonCode CommonCode::from_raw_code(RawCode &&raw_code) noexcept {
|
|||
// return raw_code.to_common_code();
|
|||
//}
|
|||
//
|
|||
//CommonCode CommonCode::from_raw_code(const RawCode &raw_code) noexcept {
|
|||
// return raw_code.to_common_code();
|
|||
//}
|
|||
//
|
|||
//CommonCode::CommonCode(RawCode &&raw_code) noexcept {
|
|||
// code_ = raw_code.to_common_code().code_; // convert from raw code
|
|||
//}
|
|||
//
|
|||
//CommonCode::CommonCode(const RawCode &raw_code) noexcept {
|
|||
// code_ = raw_code.to_common_code().code_; // convert from raw code
|
|||
//}
|
|||
|
|||
/// ------------------------- ShortCode to CommonCode -------------------------
|
|||
|
|||
//CommonCode::CommonCode(ShortCode &&short_code) noexcept {
|
|||
// code_ = short_code.to_common_code().code_; // convert from short code
|
|||
//}
|
|||
//
|
|||
//CommonCode::CommonCode(const ShortCode &short_code) noexcept {
|
|||
// code_ = short_code.to_common_code().code_; // convert from short code
|
|||
//}
|
|||
//
|
|||
//CommonCode CommonCode::from_short_code(uint32_t short_code) {
|
|||
// return ShortCode(short_code).to_common_code();
|
|||
//}
|
|||
//
|
|||
//CommonCode CommonCode::from_short_code(ShortCode &&short_code) noexcept {
|
|||
// return short_code.to_common_code();
|
|||
//}
|
|||
//
|
|||
//CommonCode CommonCode::from_short_code(std::string &&short_code) {
|
|||
// return ShortCode(std::forward<std::string>(short_code)).to_common_code();
|
|||
//}
|
|||
//
|
|||
//CommonCode CommonCode::from_short_code(const ShortCode &short_code) noexcept {
|
|||
// return short_code.to_common_code();
|
|||
//}
|
|||
//
|
|||
//CommonCode CommonCode::from_short_code(const std::string &short_code) {
|
|||
// return ShortCode(short_code).to_common_code();
|
|||
//}
|
|||
|
|||
/// ---------------------------- Batch conversions ----------------------------
|
|||
|
|||
//CommonCodes CommonCode::convert(const RawCodes &raw_codes) noexcept {
|
|||
// return {raw_codes.begin(), raw_codes.end()};
|
|||
//}
|
|||
//
|
|||
//CommonCodes CommonCode::convert(const ShortCodes &short_codes) noexcept {
|
|||
// return {short_codes.begin(), short_codes.end()};
|
|||
//}
|
|||
|
|||
} // namespace codec
|
|||
} // namespace klotski
|
@ -0,0 +1,79 @@ |
|||
#include <string> |
|||
#include "common_code.h" |
|||
|
|||
namespace klotski { |
|||
namespace codec { |
|||
|
|||
/// Convert a single hexadecimal digit to a character.
|
|||
inline static char to_hex_char(uint64_t hex_bit) { |
|||
if (hex_bit < 0xA) { |
|||
return char(hex_bit + '0'); |
|||
} |
|||
return char(hex_bit + 'A' - 10); |
|||
} |
|||
|
|||
/// Serialize CommonCode into a 9-bit length string.
|
|||
static std::string normal_encode(uint64_t common_code) { |
|||
char code_str[9]; |
|||
for (int i = 0; i < 9; ++i) { |
|||
code_str[8 - i] = to_hex_char(common_code & 0b1111); |
|||
common_code >>= 4; |
|||
} |
|||
return std::string{code_str, code_str + 9}; |
|||
} |
|||
|
|||
/// Serialize CommonCode into a variable-length string, removing the trailing zero.
|
|||
static std::string shorten_encode(uint64_t common_code) { |
|||
int zero_start; |
|||
char code_str[9]; |
|||
for (int i = 0; i < 9; ++i) { |
|||
auto hex_bit = common_code >> (32 - i * 4) & 0b1111; |
|||
code_str[i] = to_hex_char(hex_bit); |
|||
if (hex_bit != 0) { |
|||
zero_start = 9; |
|||
} else if (zero_start == 9) { |
|||
zero_start = i; |
|||
} |
|||
} |
|||
return std::string{code_str, code_str + zero_start}; |
|||
} |
|||
|
|||
/// Serialize CommonCode to string by shorten option.
|
|||
std::string CommonCode::string_encode(uint64_t common_code, bool shorten) noexcept { |
|||
if (shorten) { |
|||
if (common_code == 0) { |
|||
return "0"; // special case
|
|||
} |
|||
return shorten_encode(common_code); |
|||
} |
|||
return normal_encode(common_code); |
|||
} |
|||
|
|||
/// Deserialize CommonCode from string and return std::nullopt on error.
|
|||
std::optional<uint64_t> CommonCode::string_decode(const std::string &common_code) noexcept { |
|||
//uint64_t CommonCode::string_decode(const std::string &common_code) noexcept {
|
|||
if (common_code.length() > 9 || common_code.empty()) { |
|||
return std::nullopt; // invalid string length
|
|||
// return 0;
|
|||
} |
|||
uint64_t result = 0; |
|||
for (auto hex_bit : common_code) { |
|||
if (hex_bit >= '0' && hex_bit <= '9') { // 0 ~ 9
|
|||
(result <<= 4) |= (hex_bit - '0'); |
|||
} else if (hex_bit >= 'A' && hex_bit <= 'F') { // A ~ F
|
|||
(result <<= 4) |= (hex_bit - 'A' + 10); |
|||
} else if (hex_bit >= 'a' && hex_bit <= 'f') { // a ~ f
|
|||
(result <<= 4) |= (hex_bit - 'a' + 10); |
|||
} else { |
|||
return std::nullopt; // invalid character
|
|||
} |
|||
} |
|||
return result << (36 - common_code.length() * 4); // low-bits fill with zero
|
|||
} |
|||
|
|||
std::string CommonCode::to_string(bool shorten) const noexcept { |
|||
return string_encode(code_, shorten); |
|||
} |
|||
|
|||
} // namespace codec
|
|||
} // namespace klotski
|
Loading…
Reference in new issue