From 950da499f06f31475e6f70fa4b42a06f8edfb55c Mon Sep 17 00:00:00 2001 From: Christoph Urlacher Date: Thu, 5 Mar 2026 20:03:16 +0100 Subject: [PATCH] reorder puzzle functions --- include/puzzle.hpp | 178 ++++++++++++++++++---------------------- src/puzzle.cpp | 200 ++++++++++++++++++++++----------------------- 2 files changed, 181 insertions(+), 197 deletions(-) diff --git a/include/puzzle.hpp b/include/puzzle.hpp index 2df39eb..3200c63 100644 --- a/include/puzzle.hpp +++ b/include/puzzle.hpp @@ -418,6 +418,21 @@ public: [[nodiscard]] auto try_toggle_target(uint8_t x, uint8_t y) const -> std::optional; [[nodiscard]] auto try_toggle_wall(uint8_t x, uint8_t y) const -> std::optional; + // 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; @@ -429,35 +444,6 @@ public: [[nodiscard]] static auto sorted_replace(std::array blocks, uint8_t idx, uint16_t new_val) -> std::array; - [[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 // 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; - [[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& 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 -{ - 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 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 +{ + 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 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. diff --git a/src/puzzle.cpp b/src/puzzle.cpp index 7d1fb7d..1066e05 100644 --- a/src/puzzle.cpp +++ b/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 { const std::optional b = try_get_block(x, y); @@ -769,81 +868,10 @@ auto puzzle::sorted_replace(std::array 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>> { std::vector state_pool; - boost::unordered_flat_map state_indices; + boost::unordered_flat_map state_indices; std::vector> 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 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& permitted_blocks, const block target_block,