diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f1d669..b19eee3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,5 @@ set(CMAKE_CXX_STANDARD 14) project(klotski) #add_subdirectory(src) -add_subdirectory(all_cases) +#add_subdirectory(all_cases) +add_subdirectory(klotski) diff --git a/klotski/CMakeLists.txt b/klotski/CMakeLists.txt new file mode 100644 index 0000000..ba0fdf1 --- /dev/null +++ b/klotski/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.0) +set(CMAKE_CXX_STANDARD 14) + +add_executable(klotski main.cc common.cc all_cases.cc) diff --git a/klotski/all_cases.cc b/klotski/all_cases.cc new file mode 100644 index 0000000..3b2ee2f --- /dev/null +++ b/klotski/all_cases.cc @@ -0,0 +1,98 @@ +#include +#include "common.h" +#include "all_cases.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; +// return (bin * 0x01010101) >> 24; // AMD CPU + bin += bin >> 8; + bin += bin >> 16; + return bin & 0b111111; +} + +void AllCases::generate_ranges(int n1, int n2, int n3, int n4) { // generate target ranges + int len, limit; + constexpr uint32_t M_01 = 0b01 << 30; + constexpr uint32_t M_10 = 0b10 << 30; + constexpr uint32_t M_11 = 0b11 << 30; + std::vector cache_1, cache_2; + + len = n1 + n2; + limit = 0b1 << len; + for (uint32_t bin = 0; bin < limit; ++bin) { + if (binary_count(bin) != n2) { // skip binary without `n2` non-zero bits + continue; + } + uint32_t range = 0; + for (int i = 0; i < len; ++i) { // generate range base on binary value + range >>= 2; + if ((bin >> i) & 0b1) { // non-zero bit + range |= M_01; // 01000... + } + } + cache_1.emplace_back(range); // insert into first layer + } + + len += n3; + limit <<= n3; + for (uint32_t bin = 0; bin < limit; ++bin) { + if (binary_count(bin) != n3) { // skip binary without `n3` non-zero bits + continue; + } + for (uint32_t base : cache_1) { // traverse first layer + uint32_t range = 0; + for (int i = 0; i < len; ++i) { // generate range base on binary value + if ((bin >> i) & 0b1) { // non-zero bit + (range >>= 2) |= M_10; // 10000... + continue; + } + (range >>= 2) |= base & M_11; + base <<= 2; + } + cache_2.emplace_back(range); // insert into second layer + } + } + + len += n4; + limit <<= n4; + for (uint32_t bin = 0; bin < limit; ++bin) { + if (binary_count(bin) != n4) { // skip binary without `n4` non-zero bits + continue; + } + for (uint32_t base : cache_2) { // traverse second layer + uint32_t range = 0; + for (int i = 0; i < len; ++i) { // generate range base on binary value + if ((bin >> i) & 0b1) { // non-zero bit + (range >>= 2) |= M_11; // 11000... + continue; + } + (range >>= 2) |= base & M_11; + base <<= 2; + } + basic_ranges.emplace_back(range); // insert into release ranges + } + } +} + +void AllCases::build_basic_ranges() { // build basic ranges + for (int n = 0; n <= 7; ++n) { // number of 1x2 and 2x1 block -> 0 ~ 7 + for (int n_2x1 = 0; n_2x1 <= n; ++n_2x1) { // number of 2x1 block -> 0 ~ n + for (int n_1x1 = 0; n_1x1 <= (14 - n * 2); ++n_1x1) { // number of 1x1 block -> 0 ~ (14 - 2n) + int n_1x2 = n - n_2x1; + int n_space = 16 - n * 2 - n_1x1; + /// 0x0 -> 00 | 1x2 -> 01 | 2x1 -> 10 | 1x1 -> 11 + generate_ranges(n_space, n_1x2, n_2x1, n_1x1); // generate target ranges + } + } + } + std::sort(basic_ranges.begin(), basic_ranges.end()); // sort basic ranges + for (uint32_t &range : basic_ranges) { + range = Common::range_reverse(range); // range reverse + } +} + +const std::vector* AllCases::get_basic_ranges() { + return &basic_ranges; +} diff --git a/klotski/all_cases.h b/klotski/all_cases.h new file mode 100644 index 0000000..e2ada86 --- /dev/null +++ b/klotski/all_cases.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class AllCases { +public: +// AllCases() {} +// void find_all_cases(); + + void build_basic_ranges(); + + const std::vector* get_basic_ranges(); + +private: +// std::vector all_cases[16]; + std::vector basic_ranges; + +// void load_basic_ranges(); + void generate_ranges(int n1, int n2, int n3, int n4); +}; diff --git a/klotski/common.cc b/klotski/common.cc new file mode 100644 index 0000000..21a2118 --- /dev/null +++ b/klotski/common.cc @@ -0,0 +1,40 @@ +#include "common.h" + +uint32_t Common::range_reverse(uint32_t bin) { // reverse binary every 2-bits + bin = ((bin << 16) & 0xFFFF0000) | ((bin >> 16) & 0x0000FFFF); + bin = ((bin << 8) & 0xFF00FF00) | ((bin >> 8) & 0x00FF00FF); + bin = ((bin << 4) & 0xF0F0F0F0) | ((bin >> 4) & 0x0F0F0F0F); + return ((bin << 2) & 0xCCCCCCCC) | ((bin >> 2) & 0x33333333); +} + +bool Common::check_case(uint32_t head, uint32_t range) { // check whether the case is valid + uint32_t mask = 0b110011 << head; // fill 2x2 block + for (int addr = 0; range; range >>= 2) { // traverse every 2-bits + while (mask >> addr & 0b1) { + ++addr; // search next not filled block + } + switch (range & 0b11) { + case 0b00: // space block + case 0b11: // 1x1 block + if (addr > 19) { // invalid address + return false; + } + mask |= 0b1 << addr; // fill 1x1 block + break; + case 0b10: // 2x1 block + if (addr > 15 || mask >> (addr + 4) & 0b1) { // invalid address + return false; + } + mask |= 0b10001 << addr; // fill 2x1 block + break; + case 0b01: // 1x2 block + if (addr > 18 || (addr & 0b11) == 0b11 || mask >> (addr + 1) & 0b1) { // invalid address + return false; + } + mask |= 0b11 << addr; // fill 1x2 block + break; + } + // TODO: should we ensure that low-bits as 0? + } + return true; // valid case +} diff --git a/klotski/common.h b/klotski/common.h new file mode 100644 index 0000000..8c80a6c --- /dev/null +++ b/klotski/common.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +class Common { +public: + static uint32_t range_reverse(uint32_t bin); + static bool check_case(uint32_t head, uint32_t range); +}; diff --git a/klotski/main.cc b/klotski/main.cc new file mode 100644 index 0000000..52e02e4 --- /dev/null +++ b/klotski/main.cc @@ -0,0 +1,13 @@ +#include +#include "all_cases.h" + +int main() { + + auto a = AllCases(); + a.build_basic_ranges(); + + auto br = a.get_basic_ranges(); + std::cout << br->size() << std::endl; + + return 0; +}