From 2a8871490926a565d9f8353c1ef2039aa9184219 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sat, 8 Apr 2023 13:04:35 +0800 Subject: [PATCH] update: enhance common utils --- src/klotski_core/utils/common.cc | 37 ++++++++++----------- src/klotski_core/utils/common.h | 56 +++++++++++++++++++++----------- test/basic/utils.cc | 46 +++++++++++++------------- 3 files changed, 77 insertions(+), 62 deletions(-) diff --git a/src/klotski_core/utils/common.cc b/src/klotski_core/utils/common.cc index cf10995..4a4591c 100644 --- a/src/klotski_core/utils/common.cc +++ b/src/klotski_core/utils/common.cc @@ -1,47 +1,44 @@ #include "common.h" -using klotski::Common; - -uint32_t Common::range_reverse(uint32_t bin) noexcept { // reverse binary every 2-bits +uint32_t klotski::Common::range_reverse(uint32_t bin) noexcept { 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); } -/// WARN: don't check unknown data -> may cause infinite loop -uint8_t Common::check_range(uint32_t head, uint32_t range) noexcept { // check generated range - /// 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 - /// ... ... ... ... +uint32_t klotski::Common::check_range(uint32_t head, uint32_t range) noexcept { + /// 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; - uint8_t block_offset = 1; - uint32_t cache = M_2x2 << head; // fill 2x2 block - for (int addr = 0; range; range >>= 2, ++block_offset) { // traverse every 2-bits - while ((cache >> addr) & 0b1) { + uint32_t offset = 1; + uint32_t tmp = M_2x2 << head; // fill 2x2 block + for (int addr = 0; range; range >>= 2, ++offset) { // traverse every 2-bits + while ((tmp >> addr) & 0b1) { ++addr; // search next unfilled block } switch (range & 0b11) { case 0b00: /// space case 0b11: /// 1x1 block - cache |= M_1x1 << addr; // fill space or 1x1 block + tmp |= M_1x1 << addr; // fill space or 1x1 block break; case 0b10: /// 2x1 block - if (addr > 15 || cache >> (addr + 4) & 0b1) { // invalid address - return block_offset; // broken block number + if (addr > 15 || tmp >> (addr + 4) & 0b1) { // invalid address + return offset; // broken block number } - cache |= M_2x1 << addr; // fill 2x1 block + tmp |= M_2x1 << addr; // fill 2x1 block break; case 0b01: /// 1x2 block - if ((addr & 0b11) == 0b11 || cache >> (addr + 1) & 0b1) { // invalid address - return block_offset; // broken block number + if ((addr & 0b11) == 0b11 || tmp >> (addr + 1) & 0b1) { // invalid address + return offset; // broken block number } - cache |= M_1x2 << addr; // fill 1x2 block + tmp |= M_1x2 << addr; // fill 1x2 block break; } } diff --git a/src/klotski_core/utils/common.h b/src/klotski_core/utils/common.h index 35ade8d..4ea95b0 100644 --- a/src/klotski_core/utils/common.h +++ b/src/klotski_core/utils/common.h @@ -1,22 +1,22 @@ #pragma once /// Klotski is an intellectual game, which fill a `5x4` chessboard with movable pieces -/// (or called blocks), and a valid layout obeys the following three requirements: +/// (called blocks), and a valid layout obeys the following three requirements: /// /// 1. There are four types of blocks, namely `2x2` `2x1` `1x2` `1x1`. /// -/// 2. There should be at least two free slots (or called space). +/// 2. There should be at least two free slots (called space). /// /// 3. `2x2` block must have and only one, `2x1` `1x2` `1x1` are not required in number. /// /// NOTE: 2x2 -> # # | 2x1 -> # | 1x2 -> # # | 1x1 -> # /// # # | # | | /// -/// After statistics, there are a total of 29334498 cases that meet the above requirements. +/// After statistics, there are a total of `29334498` cases that meet the above requirements. #include -/// 0b101 and 0b110 reserved +/// NOTE: 0b101 and 0b110 are reserved #define B_space 0b000 #define B_fill 0b111 #define B_1x2 0b001 @@ -24,44 +24,62 @@ #define B_1x1 0b011 #define B_2x2 0b100 -/// C_1x1 | C_1x2 | C_2x1 | C_2x2 +/// The four types of blocks are recorded using the following format. + +/// 1x1 | 1x2 | 2x1 | 2x2 +/// [B_1x1] | [B_1x2] [B_fill] | [B_2x1] | [B_2x2] [B_fill] +/// | | [B_fill] | [B_fill] [B_fill] + +/// Mapped to the chessboard, it can be expressed with the following 60-bit data. + +/// C_1x1 | C_1x2 | C_2x1 | C_2x2 /// 011 000 000 000 | 001 111 000 000 | 010 000 000 000 | 100 111 000 000 /// 000 000 000 000 | 000 000 000 000 | 111 000 000 000 | 111 111 000 000 -/// ... | ... | ... | ... +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 #define C_1x1 (uint64_t)0x3 // 011 #define C_1x2 (uint64_t)0x39 // 111 001 #define C_2x1 (uint64_t)0x7002 // 111 000 000 000 010 #define C_2x2 (uint64_t)0x3F03C // 111 111 000 000 111 100 -/// F_1x1 | F_1x2 | F_2x1 | F_2x2 +/// Express block coverage with the following data. + +/// F_1x1 | F_1x2 | F_2x1 | F_2x2 /// 111 000 000 000 | 111 111 000 000 | 111 000 000 000 | 111 111 000 000 /// 000 000 000 000 | 000 000 000 000 | 111 000 000 000 | 111 111 000 000 -/// ... | ... | ... | ... +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 #define F_1x1 (uint64_t)0x7 // 111 #define F_1x2 (uint64_t)0x3F // 111 111 #define F_2x1 (uint64_t)0x7007 // 111 000 000 000 111 #define F_2x2 (uint64_t)0x3F03F // 111 111 000 000 111 111 -/// F_1x1_R | F_1x1_D | F_2x1_R | F_1x2_D +/// F_1x1_R | F_1x1_D | F_2x1_R | F_1x2_D /// 000 111 000 000 | 000 000 000 000 | 000 111 000 000 | 000 000 000 000 /// 000 000 000 000 | 111 000 000 000 | 000 111 000 000 | 111 111 000 000 -/// ... | ... | ... | ... +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 +/// 000 000 000 000 | 000 000 000 000 | 000 000 000 000 | 000 000 000 000 #define F_1x1_R (uint64_t)0x38 // 111 000 #define F_1x1_D (uint64_t)0x7000 // 111 000 000 000 000 #define F_2x1_R (uint64_t)0x38038 // 111 000 000 000 111 000 #define F_1x2_D (uint64_t)0x3F000 // 111 111 000 000 000 000 -/// `range_reverse` flip a 32bit data every two bits +namespace klotski { +namespace Common { + +/// `range_reverse` flip a 32-bit data every two bits /// 00 01 10 11 ... => ... 11 10 01 00 -/// (high 8-bits) (low 8-bits) +/// (high 8-bit) (low 8-bit) +uint32_t range_reverse(uint32_t bin) noexcept; -namespace klotski { - class Common { - public: - static uint32_t range_reverse(uint32_t bin) noexcept; - static uint8_t check_range(uint32_t head, uint32_t range) noexcept; - }; -} +/// WARN: don't check unknown data -> may cause infinite loop +/// Detect whether the head and range are valid, and return the error position. +uint32_t check_range(uint32_t head, uint32_t range) noexcept; + +}} // namespace klotski::Common diff --git a/test/basic/utils.cc b/test/basic/utils.cc index 7b19d43..81d038b 100644 --- a/test/basic/utils.cc +++ b/test/basic/utils.cc @@ -1,36 +1,36 @@ -#include #include "common.h" #include "gtest/gtest.h" -using klotski::Common; +using klotski::Common::check_range; +using klotski::Common::range_reverse; TEST(Utils, range_reverse) { - EXPECT_EQ(Common::range_reverse((uint32_t)0x00000003), (uint32_t)0xC0000000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x0000000C), (uint32_t)0x30000000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x00000030), (uint32_t)0x0C000000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x000000C0), (uint32_t)0x03000000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x00000300), (uint32_t)0x00C00000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x00000C00), (uint32_t)0x00300000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x00003000), (uint32_t)0x000C0000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x0000C000), (uint32_t)0x00030000); + EXPECT_EQ(range_reverse((uint32_t)0x00000003), (uint32_t)0xC0000000); + EXPECT_EQ(range_reverse((uint32_t)0x0000000C), (uint32_t)0x30000000); + EXPECT_EQ(range_reverse((uint32_t)0x00000030), (uint32_t)0x0C000000); + EXPECT_EQ(range_reverse((uint32_t)0x000000C0), (uint32_t)0x03000000); + EXPECT_EQ(range_reverse((uint32_t)0x00000300), (uint32_t)0x00C00000); + EXPECT_EQ(range_reverse((uint32_t)0x00000C00), (uint32_t)0x00300000); + EXPECT_EQ(range_reverse((uint32_t)0x00003000), (uint32_t)0x000C0000); + EXPECT_EQ(range_reverse((uint32_t)0x0000C000), (uint32_t)0x00030000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x0000000F), (uint32_t)0xF0000000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x000000FF), (uint32_t)0xFF000000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x0000FFFF), (uint32_t)0xFFFF0000); + EXPECT_EQ(range_reverse((uint32_t)0x0000000F), (uint32_t)0xF0000000); + EXPECT_EQ(range_reverse((uint32_t)0x000000FF), (uint32_t)0xFF000000); + EXPECT_EQ(range_reverse((uint32_t)0x0000FFFF), (uint32_t)0xFFFF0000); - EXPECT_EQ(Common::range_reverse((uint32_t)0x55555555), (uint32_t)0x55555555); - EXPECT_EQ(Common::range_reverse((uint32_t)0xAAAAAAAA), (uint32_t)0xAAAAAAAA); + EXPECT_EQ(range_reverse((uint32_t)0x55555555), (uint32_t)0x55555555); + EXPECT_EQ(range_reverse((uint32_t)0xAAAAAAAA), (uint32_t)0xAAAAAAAA); } TEST(Utils, check_range) { - EXPECT_NE(Common::check_range(1, 0xA9BF0C00), 0); - EXPECT_EQ(Common::check_range(1, 0x0030FE6A), 0); + EXPECT_NE(check_range(1, 0xA9BF0C00), 0); // valid cases + EXPECT_EQ(check_range(1, 0x0030FE6A), 0); - EXPECT_NE(Common::check_range(4, 0xFEA13400), 0); - EXPECT_EQ(Common::check_range(4, 0x001C4ABF), 0); + EXPECT_NE(check_range(4, 0xFEA13400), 0); // valid cases + EXPECT_EQ(check_range(4, 0x001C4ABF), 0); - EXPECT_EQ(Common::check_range(5, 0x004845D3), 5); - EXPECT_EQ(Common::check_range(10, 0x003B7521), 8); - EXPECT_EQ(Common::check_range(13, 0x000EC9D3), 9); - EXPECT_EQ(Common::check_range(15, 0x00A3B724), 6); + EXPECT_EQ(check_range(5, 0x004845D3), 5); // invalid cases + EXPECT_EQ(check_range(10, 0x003B7521), 8); + EXPECT_EQ(check_range(13, 0x000EC9D3), 9); + EXPECT_EQ(check_range(15, 0x00A3B724), 6); }