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 "group/group.h" |
|||
#include "helper/cases.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::array<Ranges, 16>, klotski::cases::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; |
|||
static_assert(std::is_base_of_v<std::array<Ranges, 16>, RangesUnion>); |
|||
|
|||
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.spawn(n, n_2x1, n_1x1); |
|||
TEST(Ranges, check) { |
|||
RangesUnion all_cases; |
|||
const auto ranges = BasicRanges::instance().fetch(); |
|||
|
|||
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
|
|||
} |
|||
} |
|||
} |
|||
|
|||
std::ranges::stable_sort(ranges.begin(), ranges.end()); |
|||
|
|||
EXPECT_EQ(ranges, klotski::cases::BasicRanges::instance().fetch()); |
|||
|
|||
EXPECT_EQ(all_cases, AllCases::instance().fetch()); |
|||
} |
|||
|
|||
TEST(Ranges, reverse) { |
|||
|
|||
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]; |
|||
|
|||
TEST(Ranges, spawn) { |
|||
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) { |
|||
Ranges ranges; |
|||
ranges.spawn(n, n_2x1, n_1x1); |
|||
EXPECT_SORTED_AND_UNIQUE(ranges); |
|||
|
|||
Ranges reverse {ranges}; |
|||
for (auto &x : reverse) { |
|||
x = klotski::range_reverse(x); |
|||
for (const auto range : ranges) { |
|||
const auto [val_1x1, val_1x2, val_2x1] = get_block_num(range); |
|||
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) { |
|||
|
|||
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]; |
|||
|
|||
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) { |
|||
Ranges ranges; |
|||
ranges.spawn(n, n_2x1, n_1x1); |
|||
|
|||
ranges.reverse(); |
|||
|
|||
auto kk = klotski::cases::GroupUnion::unsafe_create(type_id); |
|||
|
|||
klotski::cases::RangesUnion result; |
|||
|
|||
for (int head = 0; head < 16; ++head) { |
|||
|
|||
if (head % 4 == 3) { |
|||
continue; |
|||
} |
|||
RangesUnion cases; |
|||
for (const auto head : Heads) { |
|||
ranges.derive(head, cases[head]); |
|||
EXPECT_SORTED_AND_UNIQUE(cases[head]); |
|||
EXPECT_COMMON_CODES(head, cases[head]); |
|||
} |
|||
|
|||
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) { |
|||
auto ranges = klotski::cases::BasicRanges::instance().fetch(); |
|||
|
|||
klotski::cases::RangesUnion all_cases; |
|||
TEST(Ranges, combine) { |
|||
Ranges all_ranges; |
|||
RangesUnion all_cases; |
|||
|
|||
for (int head = 0; head < 16; ++head) { |
|||
if (head % 4 == 3) { |
|||
continue; |
|||
} |
|||
all_ranges.reserve(BASIC_RANGES_NUM_); // pre reserve
|
|||
for (const auto head : Heads) { |
|||
all_cases[head].reserve(ALL_CASES_NUM[head]); // pre reserve
|
|||
} |
|||
|
|||
for (auto range : ranges) { |
|||
if (Ranges::check(head, klotski::range_reverse(range)) == 0) { |
|||
all_cases[head].emplace_back(range); |
|||
} |
|||
for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) { |
|||
Ranges ranges; |
|||
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
|
|||
|
Loading…
Reference in new issue