Browse Source

update: enhance common utils

master
Dnomd343 1 year ago
parent
commit
2a88714909
  1. 37
      src/klotski_core/utils/common.cc
  2. 56
      src/klotski_core/utils/common.h
  3. 46
      test/basic/utils.cc

37
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;
}
}

56
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 <cstdint>
/// 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

46
test/basic/utils.cc

@ -1,36 +1,36 @@
#include <cstdint>
#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);
}

Loading…
Cancel
Save