mirror of https://github.com/dnomd343/klotski.git
Dnomd343
6 months ago
4 changed files with 144 additions and 83 deletions
@ -0,0 +1,49 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <array> |
||||
|
#include <algorithm> |
||||
|
|
||||
|
#include "group/group.h" |
||||
|
#include "all_cases/all_cases.h" |
||||
|
|
||||
|
using klotski::cases::Ranges; |
||||
|
using klotski::cases::AllCases; |
||||
|
using klotski::codec::CommonCode; |
||||
|
using klotski::cases::BasicRanges; |
||||
|
using klotski::cases::RangesUnion; |
||||
|
|
||||
|
using klotski::array_sum; |
||||
|
using klotski::range_reverse; |
||||
|
|
||||
|
using klotski::cases::BLOCK_NUM; |
||||
|
using klotski::cases::ALL_CASES_NUM; |
||||
|
using klotski::cases::ALL_CASES_NUM; |
||||
|
using klotski::cases::BASIC_RANGES_NUM; |
||||
|
using klotski::cases::BASIC_RANGES_NUM_; |
||||
|
|
||||
|
/// All valid klotski heads.
|
||||
|
constexpr auto Heads = std::to_array({ |
||||
|
0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14 |
||||
|
}); |
||||
|
|
||||
|
/// Assert that Ranges are sorted and unique.
|
||||
|
#define EXPECT_SORTED_AND_UNIQUE(R) \ |
||||
|
EXPECT_TRUE(std::ranges::is_sorted(R.begin(), R.end())); \ |
||||
|
EXPECT_EQ(std::ranges::adjacent_find(R.begin(), R.end()), R.end()) // no duplicates
|
||||
|
|
||||
|
/// Assert that two ordered arrays are in a containment relationship.
|
||||
|
#define EXPECT_SUBSET(R1, R2) \ |
||||
|
EXPECT_TRUE(std::ranges::includes(R1.begin(), R1.end(), R2.begin(), R2.end())) |
||||
|
|
||||
|
/// Assert that all CommonCodes generated from head and Ranges are valid.
|
||||
|
#define EXPECT_COMMON_CODES(head, ranges) \ |
||||
|
for (const auto range : ranges) \ |
||||
|
EXPECT_TRUE(CommonCode::check(static_cast<uint64_t>(head) << 32 | range)) |
||||
|
|
||||
|
struct block_num_t { |
||||
|
int n_1x1; |
||||
|
int n_1x2; |
||||
|
int n_2x1; |
||||
|
}; |
||||
|
|
||||
|
block_num_t get_block_num(uint32_t range); |
@ -0,0 +1,23 @@ |
|||||
|
#include <cstdint> |
||||
|
|
||||
|
#include "cases.h" |
||||
|
#include "utils/utility.h" |
||||
|
|
||||
|
block_num_t get_block_num(uint32_t range) { |
||||
|
block_num_t result {}; |
||||
|
range = klotski::range_reverse(range); |
||||
|
for (; range; range >>= 2) { |
||||
|
switch (range & 0b11) { |
||||
|
case 0b01: // 1x2 block
|
||||
|
++result.n_1x2; |
||||
|
continue; |
||||
|
case 0b10: // 2x1 block
|
||||
|
++result.n_2x1; |
||||
|
continue; |
||||
|
case 0b11: // 1x1 block
|
||||
|
++result.n_1x1; |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
@ -1,120 +1,108 @@ |
|||||
#include <gtest/gtest.h> |
#include <gtest/gtest.h> |
||||
|
|
||||
|
#include "group/group.h" |
||||
|
#include "helper/cases.h" |
||||
#include "ranges/ranges.h" |
#include "ranges/ranges.h" |
||||
|
|
||||
#include <algorithm> |
|
||||
#include <group/group.h> |
|
||||
|
|
||||
using klotski::cases::Ranges; |
|
||||
|
|
||||
static_assert(std::is_base_of_v<std::vector<uint32_t>, Ranges>); |
static_assert(std::is_base_of_v<std::vector<uint32_t>, Ranges>); |
||||
static_assert(std::is_base_of_v<std::array<Ranges, 16>, klotski::cases::RangesUnion>); |
static_assert(std::is_base_of_v<std::array<Ranges, 16>, RangesUnion>); |
||||
|
|
||||
TEST(Ranges, demo) { |
|
||||
|
|
||||
for (int type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) { |
|
||||
auto [n, n_2x1, n_1x1] = klotski::cases::BLOCK_NUM[type_id]; |
|
||||
|
|
||||
Ranges ranges; |
|
||||
ranges.spawn(n, n_2x1, n_1x1); |
|
||||
|
|
||||
EXPECT_TRUE(std::ranges::is_sorted(ranges.begin(), ranges.end())); |
|
||||
|
|
||||
const auto match = std::ranges::adjacent_find(ranges.begin(), ranges.end()); |
|
||||
|
|
||||
EXPECT_EQ(match, ranges.end()); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
TEST(Ranges, combine) { |
|
||||
|
|
||||
Ranges ranges; |
|
||||
|
|
||||
for (int type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) { |
TEST(Ranges, check) { |
||||
auto [n, n_2x1, n_1x1] = klotski::cases::BLOCK_NUM[type_id]; |
RangesUnion all_cases; |
||||
|
const auto ranges = BasicRanges::instance().fetch(); |
||||
ranges.spawn(n, n_2x1, n_1x1); |
|
||||
|
|
||||
|
for (const auto head : Heads) { |
||||
|
for (auto range : ranges) { |
||||
|
if (Ranges::check(head, range_reverse(range)) == 0) { |
||||
|
all_cases[head].emplace_back(range); // valid cases
|
||||
|
} |
||||
|
} |
||||
} |
} |
||||
|
EXPECT_EQ(all_cases, AllCases::instance().fetch()); |
||||
std::ranges::stable_sort(ranges.begin(), ranges.end()); |
|
||||
|
|
||||
EXPECT_EQ(ranges, klotski::cases::BasicRanges::instance().fetch()); |
|
||||
|
|
||||
} |
} |
||||
|
|
||||
TEST(Ranges, reverse) { |
TEST(Ranges, spawn) { |
||||
|
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) { |
||||
for (int type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) { |
|
||||
auto [n, n_2x1, n_1x1] = klotski::cases::BLOCK_NUM[type_id]; |
|
||||
|
|
||||
Ranges ranges; |
Ranges ranges; |
||||
ranges.spawn(n, n_2x1, n_1x1); |
ranges.spawn(n, n_2x1, n_1x1); |
||||
|
EXPECT_SORTED_AND_UNIQUE(ranges); |
||||
|
|
||||
Ranges reverse {ranges}; |
for (const auto range : ranges) { |
||||
for (auto &x : reverse) { |
const auto [val_1x1, val_1x2, val_2x1] = get_block_num(range); |
||||
x = klotski::range_reverse(x); |
EXPECT_EQ(val_1x1, n_1x1); |
||||
|
EXPECT_EQ(val_2x1, n_2x1); |
||||
|
EXPECT_EQ(val_1x2 + val_2x1, n); |
||||
} |
} |
||||
ranges.reverse(); |
|
||||
|
|
||||
EXPECT_EQ(ranges, reverse); |
|
||||
|
|
||||
} |
} |
||||
|
|
||||
|
|
||||
} |
} |
||||
|
|
||||
TEST(Ranges, derive) { |
TEST(Ranges, derive) { |
||||
|
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) { |
||||
for (int type_id = 0; type_id < klotski::cases::TYPE_ID_LIMIT; ++type_id) { |
|
||||
auto [n, n_2x1, n_1x1] = klotski::cases::BLOCK_NUM[type_id]; |
|
||||
|
|
||||
Ranges ranges; |
Ranges ranges; |
||||
ranges.spawn(n, n_2x1, n_1x1); |
ranges.spawn(n, n_2x1, n_1x1); |
||||
|
|
||||
ranges.reverse(); |
ranges.reverse(); |
||||
|
|
||||
auto kk = klotski::cases::GroupUnion::unsafe_create(type_id); |
RangesUnion cases; |
||||
|
for (const auto head : Heads) { |
||||
klotski::cases::RangesUnion result; |
ranges.derive(head, cases[head]); |
||||
|
EXPECT_SORTED_AND_UNIQUE(cases[head]); |
||||
for (int head = 0; head < 16; ++head) { |
EXPECT_COMMON_CODES(head, cases[head]); |
||||
|
} |
||||
if (head % 4 == 3) { |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
ranges.derive(head, result[head]); |
ranges.reverse(); |
||||
|
for (const auto head : Heads) { |
||||
|
EXPECT_SUBSET(ranges, cases[head]); // subset verify
|
||||
} |
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
EXPECT_EQ(result, kk.cases()); |
TEST(Ranges, reverse) { |
||||
|
auto ranges = BasicRanges::instance().fetch(); |
||||
|
Ranges reverse {ranges}; |
||||
|
|
||||
|
for (auto &x : reverse) { |
||||
|
x = range_reverse(x); // manual reverse
|
||||
} |
} |
||||
|
ranges.reverse(); |
||||
|
EXPECT_EQ(ranges, reverse); |
||||
|
|
||||
|
ranges.reverse(); |
||||
|
EXPECT_EQ(ranges, BasicRanges::instance().fetch()); |
||||
} |
} |
||||
|
|
||||
TEST(Ranges, check) { |
TEST(Ranges, combine) { |
||||
auto ranges = klotski::cases::BasicRanges::instance().fetch(); |
Ranges all_ranges; |
||||
|
RangesUnion all_cases; |
||||
klotski::cases::RangesUnion all_cases; |
|
||||
|
|
||||
for (int head = 0; head < 16; ++head) { |
all_ranges.reserve(BASIC_RANGES_NUM_); // pre reserve
|
||||
if (head % 4 == 3) { |
for (const auto head : Heads) { |
||||
continue; |
all_cases[head].reserve(ALL_CASES_NUM[head]); // pre reserve
|
||||
} |
} |
||||
|
|
||||
for (auto range : ranges) { |
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) { |
||||
if (Ranges::check(head, klotski::range_reverse(range)) == 0) { |
Ranges ranges; |
||||
all_cases[head].emplace_back(range); |
ranges.spawn(n, n_2x1, n_1x1); |
||||
} |
// TODO: add += interface
|
||||
|
all_ranges.insert(all_ranges.end(), ranges.begin(), ranges.end()); |
||||
|
ranges.reverse(); |
||||
|
for (const auto head : Heads) { |
||||
|
ranges.derive(head, all_cases[head]); // derive from sub ranges
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
EXPECT_EQ(klotski::cases::AllCases::instance().fetch(), all_cases); |
std::ranges::stable_sort(all_ranges.begin(), all_ranges.end()); |
||||
|
for (const auto head : Heads) { |
||||
|
std::ranges::stable_sort(all_cases[head].begin(), all_cases[head].end()); |
||||
|
} |
||||
|
EXPECT_EQ(all_ranges, BasicRanges::instance().fetch()); // verify content
|
||||
|
EXPECT_EQ(all_cases, AllCases::instance().fetch()); // verify content
|
||||
|
|
||||
|
all_ranges.reverse(); |
||||
|
for (const auto head : Heads) { |
||||
|
all_cases[head].clear(); |
||||
|
all_ranges.derive(head, all_cases[head]); // derive from all ranges
|
||||
|
} |
||||
|
EXPECT_EQ(all_cases, AllCases::instance().fetch()); // verify content
|
||||
} |
} |
||||
|
|
||||
// TODO: export CommonCode
|
// TODO: export CommonCode
|
||||
|
Loading…
Reference in new issue