mirror of https://github.com/dnomd343/klotski.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
5.6 KiB
168 lines
5.6 KiB
#include <stdexcept>
|
|
#include "common.h"
|
|
#include "common_code.h"
|
|
|
|
inline uint32_t binary_count(uint32_t bin) { // get number of non-zero bits
|
|
bin -= (bin >> 1) & 0x55555555;
|
|
bin = (bin & 0x33333333) + ((bin >> 2) & 0x33333333);
|
|
bin = ((bin >> 4) + bin) & 0x0F0F0F0F;
|
|
bin += bin >> 8;
|
|
bin += bin >> 16;
|
|
return bin & 0b111111;
|
|
}
|
|
|
|
/// NOTE: bin should not be zero
|
|
inline uint32_t last_zero_num(uint32_t bin) { // get last zero number
|
|
bin ^= (bin - 1);
|
|
return __builtin_popcount(bin >> 1);
|
|
}
|
|
|
|
uint64_t CommonCode::unwrap() const { // get raw uint64_t code
|
|
return code;
|
|
}
|
|
|
|
RawCode CommonCode::to_raw_code() const { // convert to raw code
|
|
return RawCode(*this);
|
|
}
|
|
|
|
ShortCode CommonCode::to_short_code() const { // convert to short code
|
|
return ShortCode(*this);
|
|
}
|
|
|
|
CommonCode CommonCode::unsafe_create(uint64_t code) { // create CommonCode without check
|
|
auto common_code = CommonCode(); // init directly
|
|
common_code.code = code;
|
|
return common_code;
|
|
}
|
|
|
|
CommonCode::CommonCode(uint64_t common_code) {
|
|
if (!CommonCode::check(common_code)) { // check input common code
|
|
throw std::invalid_argument("invalid common code");
|
|
}
|
|
code = common_code;
|
|
}
|
|
|
|
CommonCode::CommonCode(const RawCode &raw_code) { // init from raw code
|
|
code = raw_code.to_common_code().code;
|
|
}
|
|
|
|
CommonCode::CommonCode(const ShortCode &short_code) { // init from short code
|
|
code = short_code.to_common_code().code;
|
|
}
|
|
|
|
CommonCode::CommonCode(const std::string &common_code_str) {
|
|
if (common_code_str.length() > 9 || common_code_str.length() == 0) { // check string length
|
|
throw std::invalid_argument("common code format error");
|
|
}
|
|
|
|
uint64_t common_code = 0;
|
|
for (auto const &bit : common_code_str) {
|
|
common_code <<= 4;
|
|
if (bit >= '0' && bit <= '9') { // 0 ~ 9
|
|
common_code |= (bit - 48);
|
|
} else if (bit >= 'A' && bit <= 'Z') { // A ~ Z
|
|
common_code |= (bit - 55);
|
|
} else if (bit >= 'a' && bit <= 'z') { // a ~ z
|
|
common_code |= (bit - 87);
|
|
} else {
|
|
throw std::invalid_argument("common code format error"); // unknown characters
|
|
}
|
|
}
|
|
common_code <<= (9 - common_code_str.length()) * 4; // low-bits fill with zero
|
|
|
|
if (!CommonCode::check(common_code)) { // check converted common code
|
|
throw std::invalid_argument("invalid common code");
|
|
}
|
|
code = common_code;
|
|
}
|
|
|
|
std::string CommonCode::to_string(bool shorten) const { // convert uint64_t code to string
|
|
char result[10]; // max length 9-bits
|
|
sprintf(result, "%09lX", code);
|
|
if (shorten) { // remove `0` after common code
|
|
if (code == 0x000000000) {
|
|
return "0"; // special case -> only one `0`
|
|
}
|
|
result[9 - last_zero_num(code) / 4] = '\0'; // truncate string
|
|
}
|
|
return result; // char* -> std::string
|
|
}
|
|
|
|
bool CommonCode::check(uint64_t common_code) { // check whether common code is valid
|
|
uint32_t head = common_code >> 32;
|
|
if (head >= 16 || (head & 0b11) == 0b11) { // check 2x2 block address
|
|
return false; // invalid common code
|
|
}
|
|
|
|
/// ensure that there are >= 2 space blocks
|
|
uint32_t fill_num = 0, space_num = 0;
|
|
auto range = Common::range_reverse((uint32_t)common_code); // get common code range
|
|
for (int i = 0; i < 32; i += 2) { // traverse range
|
|
switch ((range >> i) & 0b11) {
|
|
case 0b00: // space block
|
|
++space_num;
|
|
case 0b11: // 1x1 block
|
|
++fill_num;
|
|
break;
|
|
case 0b01: // 1x2 block
|
|
case 0b10: // 2x1 block
|
|
fill_num += 2;
|
|
}
|
|
if (fill_num >= 16) { // all block filled
|
|
break;
|
|
}
|
|
}
|
|
if (space_num < 2) {
|
|
return false; // at least 2 space
|
|
}
|
|
return Common::check_case(head, range); // check by head and range
|
|
}
|
|
|
|
bool CommonCode::check_demo(uint64_t common_code) {
|
|
/// M_1x1 M_1x2 M_2x1 M_2x2
|
|
/// 1 0 0 0 1 1 0 0 1 0 0 0 1 1 0 0
|
|
/// 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0
|
|
/// ... ... ... ...
|
|
constexpr uint32_t M_1x1 = 0b1;
|
|
constexpr uint32_t M_1x2 = 0b11;
|
|
constexpr uint32_t M_2x1 = 0b10001;
|
|
constexpr uint32_t M_2x2 = 0b110011;
|
|
|
|
/// 2x2 address check (high 32-bits)
|
|
uint32_t head = common_code >> 32;
|
|
if (head >= 16 || (head & 0b11) == 0b11) { // check 2x2 block address
|
|
return false; // invalid common code
|
|
}
|
|
|
|
/// check range status (low 32-bits)
|
|
int space_num = 0;
|
|
uint32_t mask = M_2x2 << head; // fill 2x2 block
|
|
auto range = Common::range_reverse((uint32_t)common_code);
|
|
for (int addr = 0;; range >>= 2) { // traverse every 2-bits
|
|
while (mask >> addr & 0b1) {
|
|
++addr; // search next unfilled block
|
|
}
|
|
if (addr >= 20) {
|
|
return !range && space_num > 1; // empty range and >= 2 space
|
|
}
|
|
switch (range & 0b11) {
|
|
case 0b00: // space
|
|
++space_num;
|
|
case 0b11: // 1x1 block
|
|
mask |= M_1x1 << addr; // fill space or 1x1 block
|
|
break;
|
|
case 0b10: // 2x1 block
|
|
if (addr > 15 || mask >> (addr + 4) & 0b1) { // invalid address
|
|
return false;
|
|
}
|
|
mask |= M_2x1 << addr; // fill 2x1 block
|
|
break;
|
|
case 0b01: // 1x2 block
|
|
if ((addr & 0b11) == 0b11 || mask >> (addr + 1) & 0b1) { // invalid address
|
|
return false;
|
|
}
|
|
mask |= M_1x2 << addr; // fill 1x2 block
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|