Compare commits
2 Commits
025cbfdf3b
...
c8d6541221
| Author | SHA1 | Date | |
|---|---|---|---|
|
c8d6541221
|
|||
|
950da499f0
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,3 +9,5 @@ cmake-build-release
|
||||
/perf.data
|
||||
/perf.data.old
|
||||
/clusters.puzzle
|
||||
/benchs.json
|
||||
/benchs.old.json
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
// ReSharper disable CppTooWideScope
|
||||
#include "puzzle.hpp"
|
||||
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
|
||||
static std::vector<std::string> puzzles = {
|
||||
// 0: RushHour 1
|
||||
@ -35,6 +39,113 @@ static std::vector<std::string> puzzles = {
|
||||
"S:[4x5] G:[1,3] M:[F] B:[{_ 2X2 _ _} {1x1 _ _ 1x1} {1x2 2x1 _ 1x2} {_ 2x1 _ _} {1x1 2x1 _ 1x1}]",
|
||||
};
|
||||
|
||||
template <uint8_t N>
|
||||
struct uint_hasher
|
||||
{
|
||||
int64_t nums;
|
||||
|
||||
auto operator()(const std::array<uint64_t, N>& ints) const noexcept -> size_t
|
||||
{
|
||||
size_t h = 0;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
puzzle::hash_combine(h, ints[i]);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
template <uint8_t N>
|
||||
static auto unordered_set_uint64(benchmark::State& state) -> void
|
||||
{
|
||||
std::random_device random_device;
|
||||
std::mt19937 generator(random_device());
|
||||
std::uniform_int_distribution<uint64_t> distribution(
|
||||
std::numeric_limits<std::uint64_t>::min(),
|
||||
std::numeric_limits<std::uint64_t>::max()
|
||||
);
|
||||
|
||||
std::unordered_set<std::array<uint64_t, N>, uint_hasher<N>> set;
|
||||
std::array<uint64_t, N> ints;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
ints[i] = distribution(generator);
|
||||
}
|
||||
|
||||
for (auto _ : state) {
|
||||
for (size_t i = 0; i < 100000; ++i) {
|
||||
set.emplace(ints);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(set);
|
||||
}
|
||||
}
|
||||
|
||||
template <uint8_t N>
|
||||
static auto unordered_flat_set_uint64(benchmark::State& state) -> void
|
||||
{
|
||||
std::random_device random_device;
|
||||
std::mt19937 generator(random_device());
|
||||
std::uniform_int_distribution<uint64_t> distribution(
|
||||
std::numeric_limits<std::uint64_t>::min(),
|
||||
std::numeric_limits<std::uint64_t>::max()
|
||||
);
|
||||
|
||||
boost::unordered_flat_set<std::array<uint64_t, N>, uint_hasher<N>> set;
|
||||
std::array<uint64_t, N> ints;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
ints[i] = distribution(generator);
|
||||
}
|
||||
|
||||
for (auto _ : state) {
|
||||
for (size_t i = 0; i < 100000; ++i) {
|
||||
set.emplace(ints);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(set);
|
||||
}
|
||||
}
|
||||
|
||||
static auto unordered_flat_set_block_hasher(benchmark::State& state) -> void
|
||||
{
|
||||
boost::unordered_flat_set<puzzle::block, block_hasher> set;
|
||||
const puzzle::block b = puzzle::block(2, 3, 1, 2, true, false);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (size_t i = 0; i < 100000; ++i) {
|
||||
set.emplace(b);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(set);
|
||||
}
|
||||
}
|
||||
|
||||
static auto unordered_falt_set_block_hasher2(benchmark::State& state) -> void
|
||||
{
|
||||
boost::unordered_flat_set<puzzle::block, block_hasher2, block_equal2> set;
|
||||
const puzzle::block b = puzzle::block(2, 3, 1, 2, true, false);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (size_t i = 0; i < 100000; ++i) {
|
||||
set.emplace(b);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(set);
|
||||
}
|
||||
}
|
||||
|
||||
static auto unordered_flat_set_puzzle_hasher(benchmark::State& state) -> void
|
||||
{
|
||||
boost::unordered_flat_set<puzzle, puzzle_hasher> set;
|
||||
const puzzle p = puzzle(puzzles[0]);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (size_t i = 0; i < 100000; ++i) {
|
||||
set.emplace(p);
|
||||
}
|
||||
|
||||
benchmark::DoNotOptimize(set);
|
||||
}
|
||||
}
|
||||
|
||||
static auto explore_state_space(benchmark::State& state) -> void
|
||||
{
|
||||
const puzzle p = puzzle(puzzles[state.range(0)]);
|
||||
@ -48,7 +159,6 @@ static auto explore_state_space(benchmark::State& state) -> void
|
||||
|
||||
static auto explore_rush_hour_puzzle_space(benchmark::State& state) -> void
|
||||
{
|
||||
// ReSharper disable once CppTooWideScope
|
||||
constexpr uint8_t max_blocks = 5;
|
||||
|
||||
constexpr uint8_t board_width = 4;
|
||||
@ -85,6 +195,15 @@ static auto explore_rush_hour_puzzle_space(benchmark::State& state) -> void
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(unordered_set_uint64<4>)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_set_uint64<8>)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_set_uint64<16>)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_flat_set_uint64<4>)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_flat_set_uint64<8>)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_flat_set_uint64<16>)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_flat_set_block_hasher)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_falt_set_block_hasher2)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(unordered_flat_set_puzzle_hasher)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(explore_state_space)->DenseRange(0, puzzles.size() - 1)->Unit(benchmark::kMicrosecond);
|
||||
BENCHMARK(explore_rush_hour_puzzle_space)->Unit(benchmark::kSecond);
|
||||
|
||||
|
||||
@ -113,7 +113,7 @@ rec {
|
||||
abbr -a run "${buildRelease} && ./cmake-build-release/masssprings"
|
||||
abbr -a run-clusters "${buildRelease} && ./cmake-build-release/masssprings --output=clusters.puzzle --space=rh --w=6 --h=6 --gx=4 --gy=2 --blocks=4"
|
||||
abbr -a runtests "${buildDebug} && ./cmake-build-debug/tests"
|
||||
abbr -a runbenchs "${buildRelease} && sudo cpupower frequency-set --governor performance && ./cmake-build-release/benchmarks; sudo cpupower frequency-set --governor powersave"
|
||||
abbr -a runbenchs "mv benchs.json benchs.old.json; ${buildRelease} && sudo cpupower frequency-set --governor performance && ./cmake-build-release/benchmarks --benchmark_out=benchs.json --benchmark_out_format=console; sudo cpupower frequency-set --governor powersave"
|
||||
abbr -a rungdb "${buildDebug} && gdb --tui ./cmake-build-debug/masssprings"
|
||||
abbr -a runvalgrind "${buildDebug} && valgrind --leak-check=full --show-reachable=no --show-leak-kinds=definite,indirect,possible --track-origins=no --suppressions=valgrind.supp --log-file=valgrind.log ./cmake-build-debug/masssprings && cat valgrind.log"
|
||||
abbr -a runperf "${buildRelease} && perf record -g ./cmake-build-release/masssprings && hotspot ./perf.data"
|
||||
|
||||
@ -418,6 +418,21 @@ public:
|
||||
[[nodiscard]] auto try_toggle_target(uint8_t x, uint8_t y) const -> std::optional<puzzle>;
|
||||
[[nodiscard]] auto try_toggle_wall(uint8_t x, uint8_t y) const -> std::optional<puzzle>;
|
||||
|
||||
// Bitmap
|
||||
[[nodiscard]] auto blocks_bitmap() const -> uint64_t;
|
||||
[[nodiscard]] auto blocks_bitmap_h() const -> uint64_t;
|
||||
[[nodiscard]] auto blocks_bitmap_v() const -> uint64_t;
|
||||
static INLINE inline auto bitmap_clear_bit(uint64_t& bitmap, uint8_t w, uint8_t x, uint8_t y) -> void;
|
||||
static INLINE inline auto bitmap_set_bit(uint64_t& bitmap, uint8_t w, uint8_t x, uint8_t y) -> void;
|
||||
[[nodiscard]] static INLINE inline auto bitmap_get_bit(uint64_t bitmap, uint8_t w, uint8_t x, uint8_t y) -> bool;
|
||||
INLINE inline auto bitmap_clear_block(uint64_t& bitmap, block b) const -> void;
|
||||
INLINE inline auto bitmap_set_block(uint64_t& bitmap, block b) const -> void;
|
||||
[[nodiscard]] INLINE inline auto bitmap_is_empty(uint64_t bitmap) const -> bool;
|
||||
[[nodiscard]] INLINE inline auto bitmap_is_full(uint64_t bitmap) const -> bool;
|
||||
[[nodiscard]] INLINE inline auto bitmap_check_collision(uint64_t bitmap, block b) const -> bool;
|
||||
[[nodiscard]] INLINE inline auto bitmap_check_collision(uint64_t bitmap, block b, dir dir) const -> bool;
|
||||
[[nodiscard]] auto bitmap_find_first_empty(uint64_t bitmap, int& x, int& y) const -> bool;
|
||||
|
||||
// Playing
|
||||
[[nodiscard]] auto try_move_block_at(uint8_t x, uint8_t y, dir dir) const -> std::optional<puzzle>;
|
||||
|
||||
@ -429,35 +444,6 @@ public:
|
||||
[[nodiscard]] static auto sorted_replace(std::array<uint16_t, MAX_BLOCKS> blocks,
|
||||
uint8_t idx,
|
||||
uint16_t new_val) -> std::array<uint16_t, MAX_BLOCKS>;
|
||||
[[nodiscard]] auto blocks_bitmap() const -> uint64_t;
|
||||
[[nodiscard]] auto blocks_bitmap_h() const -> uint64_t;
|
||||
[[nodiscard]] auto blocks_bitmap_v() const -> uint64_t;
|
||||
static INLINE inline auto bitmap_clear_bit(uint64_t& bitmap, uint8_t w, uint8_t x, uint8_t y) -> void;
|
||||
static INLINE inline auto bitmap_set_bit(uint64_t& bitmap, uint8_t w, uint8_t x, uint8_t y) -> void;
|
||||
[[nodiscard]] static INLINE inline auto bitmap_get_bit(uint64_t bitmap, uint8_t w, uint8_t x, uint8_t y) -> bool;
|
||||
INLINE inline auto bitmap_clear_block(uint64_t& bitmap, block b) const -> void;
|
||||
INLINE inline auto bitmap_set_block(uint64_t& bitmap, block b) const -> void;
|
||||
[[nodiscard]] INLINE inline auto bitmap_is_empty(uint64_t bitmap) const -> bool;
|
||||
[[nodiscard]] INLINE inline auto bitmap_is_full(uint64_t bitmap) const -> bool;
|
||||
|
||||
/**
|
||||
* Checks if b would collide with any block on the board.
|
||||
*
|
||||
* @param bitmap Board occupancy map
|
||||
* @param b Hypothetical block to check collision with
|
||||
* @return True if b would collide with any other block on the board
|
||||
*/
|
||||
[[nodiscard]] INLINE inline auto bitmap_check_collision(uint64_t bitmap, block b) const -> bool;
|
||||
|
||||
/**
|
||||
* Checks if b would collide with any block on the board after moving in direction dir.
|
||||
*
|
||||
* @param bitmap Board occupancy map
|
||||
* @param b Existing block to check collision with
|
||||
* @param dir Direction in which the block should be moved
|
||||
* @return True if b would collide with any other block on the board after moving in direction dir
|
||||
*/
|
||||
[[nodiscard]] INLINE inline auto bitmap_check_collision(uint64_t bitmap, block b, dir dir) const -> bool;
|
||||
|
||||
template <typename F>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
@ -491,8 +477,6 @@ public:
|
||||
// state with the numerically smallest binary representation.
|
||||
[[nodiscard]] auto get_cluster_id_and_solution() const -> std::pair<puzzle, bool>;
|
||||
|
||||
[[nodiscard]] auto bitmap_find_first_empty(uint64_t bitmap, int& x, int& y) const -> bool;
|
||||
|
||||
static auto generate_block_sequences(
|
||||
const boost::unordered_flat_set<block, block_hasher2, block_equal2>& permitted_blocks,
|
||||
block target_block,
|
||||
@ -716,72 +700,6 @@ inline auto puzzle::get_goal_y() const -> uint8_t
|
||||
return get_bits(repr.cooked.meta, GOAL_Y_S, GOAL_Y_E);
|
||||
}
|
||||
|
||||
INLINE inline auto puzzle::try_move_block_at_fast(uint64_t bitmap,
|
||||
const uint8_t block_idx,
|
||||
const dir dir,
|
||||
const bool check_collision) const -> std::optional<puzzle>
|
||||
{
|
||||
const block b = block(repr.cooked.blocks[block_idx]);
|
||||
const auto [bx, by, bw, bh, bt, bi] = b.unpack_repr();
|
||||
if (bi) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto [w, h, gx, gy, r, g] = unpack_meta();
|
||||
const int dirs = r ? b.principal_dirs() : nor | eas | sou | wes;
|
||||
|
||||
// Get target block
|
||||
int _target_x = bx;
|
||||
int _target_y = by;
|
||||
switch (dir) {
|
||||
case nor:
|
||||
if (!(dirs & nor) || _target_y < 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
--_target_y;
|
||||
break;
|
||||
case eas:
|
||||
if (!(dirs & eas) || _target_x + bw >= w) {
|
||||
return std::nullopt;
|
||||
}
|
||||
++_target_x;
|
||||
break;
|
||||
case sou:
|
||||
if (!(dirs & sou) || _target_y + bh >= h) {
|
||||
return std::nullopt;
|
||||
}
|
||||
++_target_y;
|
||||
break;
|
||||
case wes:
|
||||
if (!(dirs & wes) || _target_x < 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
--_target_x;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check collisions
|
||||
if (check_collision) {
|
||||
bitmap_clear_block(bitmap, b);
|
||||
if (bitmap_check_collision(bitmap, b, dir)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace block
|
||||
const std::array<uint16_t, MAX_BLOCKS> blocks = sorted_replace(repr.cooked.blocks,
|
||||
block_idx,
|
||||
block::create_repr(
|
||||
_target_x,
|
||||
_target_y,
|
||||
bw,
|
||||
bh,
|
||||
bt));
|
||||
|
||||
// This constructor doesn't sort
|
||||
return puzzle(std::make_tuple(w, h, gx, gy, r, g), blocks);
|
||||
}
|
||||
|
||||
INLINE inline auto puzzle::bitmap_clear_bit(uint64_t& bitmap, const uint8_t w, const uint8_t x, const uint8_t y) -> void
|
||||
{
|
||||
set_bits(bitmap, y * w + x, y * w + x, 0u);
|
||||
@ -892,6 +810,72 @@ INLINE inline auto puzzle::bitmap_check_collision(const uint64_t bitmap,
|
||||
return false;
|
||||
}
|
||||
|
||||
INLINE inline auto puzzle::try_move_block_at_fast(uint64_t bitmap,
|
||||
const uint8_t block_idx,
|
||||
const dir dir,
|
||||
const bool check_collision) const -> std::optional<puzzle>
|
||||
{
|
||||
const block b = block(repr.cooked.blocks[block_idx]);
|
||||
const auto [bx, by, bw, bh, bt, bi] = b.unpack_repr();
|
||||
if (bi) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto [w, h, gx, gy, r, g] = unpack_meta();
|
||||
const int dirs = r ? b.principal_dirs() : nor | eas | sou | wes;
|
||||
|
||||
// Get target block
|
||||
int _target_x = bx;
|
||||
int _target_y = by;
|
||||
switch (dir) {
|
||||
case nor:
|
||||
if (!(dirs & nor) || _target_y < 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
--_target_y;
|
||||
break;
|
||||
case eas:
|
||||
if (!(dirs & eas) || _target_x + bw >= w) {
|
||||
return std::nullopt;
|
||||
}
|
||||
++_target_x;
|
||||
break;
|
||||
case sou:
|
||||
if (!(dirs & sou) || _target_y + bh >= h) {
|
||||
return std::nullopt;
|
||||
}
|
||||
++_target_y;
|
||||
break;
|
||||
case wes:
|
||||
if (!(dirs & wes) || _target_x < 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
--_target_x;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check collisions
|
||||
if (check_collision) {
|
||||
bitmap_clear_block(bitmap, b);
|
||||
if (bitmap_check_collision(bitmap, b, dir)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace block
|
||||
const std::array<uint16_t, MAX_BLOCKS> blocks = sorted_replace(repr.cooked.blocks,
|
||||
block_idx,
|
||||
block::create_repr(
|
||||
_target_x,
|
||||
_target_y,
|
||||
bw,
|
||||
bh,
|
||||
bt));
|
||||
|
||||
// This constructor doesn't sort
|
||||
return puzzle(std::make_tuple(w, h, gx, gy, r, g), blocks);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Hash functions for sets and maps.
|
||||
|
||||
200
src/puzzle.cpp
200
src/puzzle.cpp
@ -682,6 +682,105 @@ auto puzzle::try_toggle_wall(const uint8_t x, const uint8_t y) const -> std::opt
|
||||
return puzzle(w, h, gx, gy, r, g, blocks);
|
||||
}
|
||||
|
||||
auto puzzle::blocks_bitmap() const -> uint64_t
|
||||
{
|
||||
uint64_t bitmap = 0;
|
||||
for (uint8_t i = 0; i < MAX_BLOCKS; ++i) {
|
||||
block b(repr.cooked.blocks[i]);
|
||||
if (!b.valid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto [x, y, w, h, t, im] = b.unpack_repr();
|
||||
const uint8_t width = get_width();
|
||||
|
||||
for (int dy = 0; dy < h; ++dy) {
|
||||
for (int dx = 0; dx < w; ++dx) {
|
||||
bitmap_set_bit(bitmap, width, x + dx, y + dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
auto puzzle::blocks_bitmap_h() const -> uint64_t
|
||||
{
|
||||
uint64_t bitmap = 0;
|
||||
for (uint8_t i = 0; i < MAX_BLOCKS; ++i) {
|
||||
block b(repr.cooked.blocks[i]);
|
||||
if (!b.valid()) {
|
||||
break;
|
||||
}
|
||||
const int dirs = b.principal_dirs();
|
||||
if (!(dirs & eas)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto [x, y, w, h, t, im] = b.unpack_repr();
|
||||
const uint8_t width = get_width();
|
||||
|
||||
for (int dy = 0; dy < h; ++dy) {
|
||||
for (int dx = 0; dx < w; ++dx) {
|
||||
bitmap_set_bit(bitmap, width, x + dx, y + dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
auto puzzle::blocks_bitmap_v() const -> uint64_t
|
||||
{
|
||||
uint64_t bitmap = 0;
|
||||
for (uint8_t i = 0; i < MAX_BLOCKS; ++i) {
|
||||
block b(repr.cooked.blocks[i]);
|
||||
if (!b.valid()) {
|
||||
break;
|
||||
}
|
||||
const int dirs = b.principal_dirs();
|
||||
if (!(dirs & sou)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto [x, y, w, h, t, im] = b.unpack_repr();
|
||||
const uint8_t width = get_width();
|
||||
|
||||
for (int dy = 0; dy < h; ++dy) {
|
||||
for (int dx = 0; dx < w; ++dx) {
|
||||
bitmap_set_bit(bitmap, width, x + dx, y + dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
auto puzzle::bitmap_find_first_empty(const uint64_t bitmap, int& x, int& y) const -> bool
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
// Bitmap is empty of first slot is empty
|
||||
if (bitmap_is_empty(bitmap) || !(bitmap & 1u)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bitmap is full
|
||||
if (bitmap_is_full(bitmap)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the next more significant empty bit (we know the first slot is full)
|
||||
int ls_set = 0;
|
||||
bool next_set = true;
|
||||
while (next_set && ls_set < get_width() * get_height() - 1) {
|
||||
next_set = bitmap & (1ul << (ls_set + 1));
|
||||
++ls_set;
|
||||
}
|
||||
|
||||
x = ls_set % get_width();
|
||||
y = ls_set / get_width();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto puzzle::try_move_block_at(const uint8_t x, const uint8_t y, const dir dir) const -> std::optional<puzzle>
|
||||
{
|
||||
const std::optional<block> b = try_get_block(x, y);
|
||||
@ -769,81 +868,10 @@ auto puzzle::sorted_replace(std::array<uint16_t, MAX_BLOCKS> blocks,
|
||||
return blocks;
|
||||
}
|
||||
|
||||
auto puzzle::blocks_bitmap() const -> uint64_t
|
||||
{
|
||||
uint64_t bitmap = 0;
|
||||
for (uint8_t i = 0; i < MAX_BLOCKS; ++i) {
|
||||
block b(repr.cooked.blocks[i]);
|
||||
if (!b.valid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto [x, y, w, h, t, im] = b.unpack_repr();
|
||||
const uint8_t width = get_width();
|
||||
|
||||
for (int dy = 0; dy < h; ++dy) {
|
||||
for (int dx = 0; dx < w; ++dx) {
|
||||
bitmap_set_bit(bitmap, width, x + dx, y + dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
auto puzzle::blocks_bitmap_h() const -> uint64_t
|
||||
{
|
||||
uint64_t bitmap = 0;
|
||||
for (uint8_t i = 0; i < MAX_BLOCKS; ++i) {
|
||||
block b(repr.cooked.blocks[i]);
|
||||
if (!b.valid()) {
|
||||
break;
|
||||
}
|
||||
const int dirs = b.principal_dirs();
|
||||
if (!(dirs & eas)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto [x, y, w, h, t, im] = b.unpack_repr();
|
||||
const uint8_t width = get_width();
|
||||
|
||||
for (int dy = 0; dy < h; ++dy) {
|
||||
for (int dx = 0; dx < w; ++dx) {
|
||||
bitmap_set_bit(bitmap, width, x + dx, y + dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
auto puzzle::blocks_bitmap_v() const -> uint64_t
|
||||
{
|
||||
uint64_t bitmap = 0;
|
||||
for (uint8_t i = 0; i < MAX_BLOCKS; ++i) {
|
||||
block b(repr.cooked.blocks[i]);
|
||||
if (!b.valid()) {
|
||||
break;
|
||||
}
|
||||
const int dirs = b.principal_dirs();
|
||||
if (!(dirs & sou)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto [x, y, w, h, t, im] = b.unpack_repr();
|
||||
const uint8_t width = get_width();
|
||||
|
||||
for (int dy = 0; dy < h; ++dy) {
|
||||
for (int dx = 0; dx < w; ++dx) {
|
||||
bitmap_set_bit(bitmap, width, x + dx, y + dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
auto puzzle::explore_state_space() const -> std::pair<std::vector<puzzle>, std::vector<std::pair<size_t, size_t>>>
|
||||
{
|
||||
std::vector<puzzle> state_pool;
|
||||
boost::unordered_flat_map<puzzle, std::size_t, puzzle_hasher> state_indices;
|
||||
boost::unordered_flat_map<puzzle, size_t, puzzle_hasher> state_indices;
|
||||
std::vector<std::pair<size_t, size_t>> links;
|
||||
|
||||
// Buffer for all states we want to call GetNextStates() on
|
||||
@ -914,34 +942,6 @@ auto puzzle::get_cluster_id_and_solution() const -> std::pair<puzzle, bool>
|
||||
return {min, solution};
|
||||
}
|
||||
|
||||
auto puzzle::bitmap_find_first_empty(const uint64_t bitmap, int& x, int& y) const -> bool
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
// Bitmap is empty of first slot is empty
|
||||
if (bitmap_is_empty(bitmap) || !(bitmap & 1u)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bitmap is full
|
||||
if (bitmap_is_full(bitmap)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the next more significant empty bit (we know the first slot is full)
|
||||
int ls_set = 0;
|
||||
bool next_set = true;
|
||||
while (next_set && ls_set < get_width() * get_height() - 1) {
|
||||
next_set = bitmap & (1ul << (ls_set + 1));
|
||||
++ls_set;
|
||||
}
|
||||
|
||||
x = ls_set % get_width();
|
||||
y = ls_set / get_width();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto puzzle::generate_block_sequences(
|
||||
const boost::unordered_flat_set<block, block_hasher2, block_equal2>& permitted_blocks,
|
||||
const block target_block,
|
||||
|
||||
Reference in New Issue
Block a user