move bits functions to separate file + fix missing defaults with disabled program_options on windows
This commit is contained in:
@ -18,6 +18,7 @@ option(DISABLE_BENCH "Disable building benchmarks" OFF)
|
||||
# Headers + Sources (excluding main.cpp)
|
||||
set(SOURCES
|
||||
src/backward.cpp
|
||||
src/bits.cpp
|
||||
src/graph_distances.cpp
|
||||
src/input_handler.cpp
|
||||
src/load_save.cpp
|
||||
|
||||
69
include/bits.hpp
Normal file
69
include/bits.hpp
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef BITS_HPP_
|
||||
#define BITS_HPP_
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
#include <concepts>
|
||||
|
||||
template <class T>
|
||||
requires std::unsigned_integral<T>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto create_mask(const uint8_t first, const uint8_t last) -> T
|
||||
{
|
||||
// If the mask width is equal the type width return all 1s instead of shifting
|
||||
// as shifting by type-width is undefined behavior.
|
||||
if (static_cast<size_t>(last - first + 1) >= sizeof(T) * 8) {
|
||||
return ~T{0};
|
||||
}
|
||||
|
||||
// Example: first=4, last=7, 7-4+1=4
|
||||
// 1 << 4 = 0b00010000
|
||||
// 32 - 1 = 0b00001111
|
||||
// 31 << 4 = 0b11110000
|
||||
// Subtracting 1 generates a consecutive mask.
|
||||
return ((T{1} << (last - first + 1)) - 1) << first;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires std::unsigned_integral<T>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto clear_bits(T& bits, const uint8_t first, const uint8_t last) -> void
|
||||
{
|
||||
const T mask = create_mask<T>(first, last);
|
||||
|
||||
bits = bits & ~mask;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
requires std::unsigned_integral<T> && std::unsigned_integral<U>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto set_bits(T& bits, const uint8_t first, const uint8_t last, const U value) -> void
|
||||
{
|
||||
const T mask = create_mask<T>(first, last);
|
||||
|
||||
// Example: first=4, last=6, value=0b1110, bits = 0b 01111110
|
||||
// mask = 0b 01110000
|
||||
// bits & ~mask = 0b 00001110
|
||||
// value << 4 = 0b 11100000
|
||||
// (value << 4) & mask = 0b 01100000
|
||||
// (bits & ~mask) | (value << 4) & mask = 0b 01101110
|
||||
// Insert position: ^^^
|
||||
// First clear the bits, then | with the value positioned at the insertion point.
|
||||
// The value may be larger than [first, last], extra bits are ignored.
|
||||
bits = (bits & ~mask) | ((static_cast<T>(value) << first) & mask);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires std::unsigned_integral<T>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto get_bits(const T bits, const uint8_t first, const uint8_t last) -> T
|
||||
{
|
||||
const T mask = create_mask<T>(first, last);
|
||||
|
||||
// We can >> without sign extension because T is unsigned_integral
|
||||
return (bits & mask) >> first;
|
||||
}
|
||||
|
||||
auto print_bitmap(uint64_t bitmap, uint8_t w, uint8_t h, const std::string& title) -> void;
|
||||
|
||||
#endif
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include "config.hpp"
|
||||
#include "util.hpp"
|
||||
#include "bits.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
@ -418,12 +419,12 @@ public:
|
||||
[[nodiscard]] auto try_toggle_wall(uint8_t x, uint8_t y) const -> std::optional<puzzle>;
|
||||
|
||||
// Playing
|
||||
[[nodiscard]] auto try_move_block_at(uint8_t x, uint8_t y, direction dir) const -> std::optional<puzzle>;
|
||||
[[nodiscard]] auto try_move_block_at(uint8_t x, uint8_t y, dir dir) const -> std::optional<puzzle>;
|
||||
|
||||
// Statespace
|
||||
[[nodiscard]] INLINE inline auto try_move_block_at_fast(uint64_t bitmap,
|
||||
uint8_t block_idx,
|
||||
direction dir,
|
||||
dir dir,
|
||||
bool check_collision = true) const -> std::optional<puzzle>;
|
||||
[[nodiscard]] static auto sorted_replace(std::array<uint16_t, MAX_BLOCKS> blocks,
|
||||
uint8_t idx,
|
||||
@ -456,7 +457,7 @@ public:
|
||||
* @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, direction dir) const -> bool;
|
||||
[[nodiscard]] INLINE inline auto bitmap_check_collision(uint64_t bitmap, block b, dir dir) const -> bool;
|
||||
|
||||
template <typename F>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
@ -473,7 +474,7 @@ public:
|
||||
continue;
|
||||
}
|
||||
const int dirs = r ? b.principal_dirs() : nor | eas | sou | wes;
|
||||
for (const direction d : {nor, eas, sou, wes}) {
|
||||
for (const dir d : {nor, eas, sou, wes}) {
|
||||
if (dirs & d) {
|
||||
if (auto moved = try_move_block_at_fast(bitmap, idx, d)) {
|
||||
callback(*moved);
|
||||
@ -717,7 +718,7 @@ inline auto puzzle::get_goal_y() const -> uint8_t
|
||||
|
||||
INLINE inline auto puzzle::try_move_block_at_fast(uint64_t bitmap,
|
||||
const uint8_t block_idx,
|
||||
const direction dir,
|
||||
const dir dir,
|
||||
const bool check_collision) const -> std::optional<puzzle>
|
||||
{
|
||||
const block b = block(repr.cooked.blocks[block_idx]);
|
||||
@ -853,7 +854,7 @@ INLINE inline auto puzzle::bitmap_check_collision(const uint64_t bitmap, const b
|
||||
|
||||
INLINE inline auto puzzle::bitmap_check_collision(const uint64_t bitmap,
|
||||
const block b,
|
||||
const direction dir) const -> bool
|
||||
const dir dir) const -> bool
|
||||
{
|
||||
const auto [x, y, w, h, t, i] = b.unpack_repr();
|
||||
const uint8_t width = get_width();
|
||||
|
||||
178
include/util.hpp
178
include/util.hpp
@ -7,112 +7,6 @@
|
||||
#define INLINE __attribute__((always_inline))
|
||||
#define PACKED __attribute__((packed))
|
||||
|
||||
enum ctrl
|
||||
{
|
||||
reset = 0,
|
||||
bold_bright = 1,
|
||||
underline = 4,
|
||||
inverse = 7,
|
||||
bold_bright_off = 21,
|
||||
underline_off = 24,
|
||||
inverse_off = 27
|
||||
};
|
||||
|
||||
enum fg
|
||||
{
|
||||
fg_black = 30,
|
||||
fg_red = 31,
|
||||
fg_green = 32,
|
||||
fg_yellow = 33,
|
||||
fg_blue = 34,
|
||||
fg_magenta = 35,
|
||||
fg_cyan = 36,
|
||||
fg_white = 37
|
||||
};
|
||||
|
||||
enum bg
|
||||
{
|
||||
bg_black = 40,
|
||||
bg_red = 41,
|
||||
bg_green = 42,
|
||||
bg_yellow = 43,
|
||||
bg_blue = 44,
|
||||
bg_magenta = 45,
|
||||
bg_cyan = 46,
|
||||
bg_white = 47
|
||||
};
|
||||
|
||||
inline auto ansi_bold_fg(const fg color) -> std::string
|
||||
{
|
||||
return std::format("\033[{};{}m", static_cast<int>(bold_bright), static_cast<int>(color));
|
||||
}
|
||||
|
||||
inline auto ansi_reset() -> std::string
|
||||
{
|
||||
return std::format("\033[{}m", static_cast<int>(reset));
|
||||
}
|
||||
|
||||
// Bit shifting + masking
|
||||
|
||||
template <class T>
|
||||
requires std::unsigned_integral<T>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto create_mask(const uint8_t first, const uint8_t last) -> T
|
||||
{
|
||||
// If the mask width is equal the type width return all 1s instead of shifting
|
||||
// as shifting by type-width is undefined behavior.
|
||||
if (static_cast<size_t>(last - first + 1) >= sizeof(T) * 8) {
|
||||
return ~T{0};
|
||||
}
|
||||
|
||||
// Example: first=4, last=7, 7-4+1=4
|
||||
// 1 << 4 = 0b00010000
|
||||
// 32 - 1 = 0b00001111
|
||||
// 31 << 4 = 0b11110000
|
||||
// Subtracting 1 generates a consecutive mask.
|
||||
return ((T{1} << (last - first + 1)) - 1) << first;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires std::unsigned_integral<T>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto clear_bits(T& bits, const uint8_t first, const uint8_t last) -> void
|
||||
{
|
||||
const T mask = create_mask<T>(first, last);
|
||||
|
||||
bits = bits & ~mask;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
requires std::unsigned_integral<T> && std::unsigned_integral<U>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto set_bits(T& bits, const uint8_t first, const uint8_t last, const U value) -> void
|
||||
{
|
||||
const T mask = create_mask<T>(first, last);
|
||||
|
||||
// Example: first=4, last=6, value=0b1110, bits = 0b 01111110
|
||||
// mask = 0b 01110000
|
||||
// bits & ~mask = 0b 00001110
|
||||
// value << 4 = 0b 11100000
|
||||
// (value << 4) & mask = 0b 01100000
|
||||
// (bits & ~mask) | (value << 4) & mask = 0b 01101110
|
||||
// Insert position: ^^^
|
||||
// First clear the bits, then | with the value positioned at the insertion point.
|
||||
// The value may be larger than [first, last], extra bits are ignored.
|
||||
bits = (bits & ~mask) | ((static_cast<T>(value) << first) & mask);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires std::unsigned_integral<T>
|
||||
// ReSharper disable once CppRedundantInlineSpecifier
|
||||
INLINE inline auto get_bits(const T bits, const uint8_t first, const uint8_t last) -> T
|
||||
{
|
||||
const T mask = create_mask<T>(first, last);
|
||||
|
||||
// We can >> without sign extension because T is unsigned_integral
|
||||
return (bits & mask) >> first;
|
||||
}
|
||||
|
||||
// std::variant visitor
|
||||
|
||||
// https://en.cppreference.com/w/cpp/utility/variant/visit
|
||||
@ -124,7 +18,7 @@ struct overloads : Ts...
|
||||
|
||||
// Enums
|
||||
|
||||
enum direction
|
||||
enum dir : uint8_t
|
||||
{
|
||||
nor = 1 << 0,
|
||||
eas = 1 << 1,
|
||||
@ -132,6 +26,53 @@ enum direction
|
||||
wes = 1 << 3,
|
||||
};
|
||||
|
||||
// Ansi
|
||||
|
||||
enum class ctrl : uint8_t
|
||||
{
|
||||
reset = 0,
|
||||
bold_bright = 1,
|
||||
underline = 4,
|
||||
inverse = 7,
|
||||
bold_bright_off = 21,
|
||||
underline_off = 24,
|
||||
inverse_off = 27
|
||||
};
|
||||
|
||||
enum class fg : uint8_t
|
||||
{
|
||||
black = 30,
|
||||
red = 31,
|
||||
green = 32,
|
||||
yellow = 33,
|
||||
blue = 34,
|
||||
magenta = 35,
|
||||
cyan = 36,
|
||||
white = 37
|
||||
};
|
||||
|
||||
enum class bg : uint8_t
|
||||
{
|
||||
black = 40,
|
||||
red = 41,
|
||||
green = 42,
|
||||
yellow = 43,
|
||||
blue = 44,
|
||||
magenta = 45,
|
||||
cyan = 46,
|
||||
white = 47
|
||||
};
|
||||
|
||||
inline auto ansi_bold_fg(const fg color) -> std::string
|
||||
{
|
||||
return std::format("\033[{};{}m", static_cast<int>(ctrl::bold_bright), static_cast<int>(color));
|
||||
}
|
||||
|
||||
inline auto ansi_reset() -> std::string
|
||||
{
|
||||
return std::format("\033[{}m", static_cast<int>(ctrl::reset));
|
||||
}
|
||||
|
||||
// Output
|
||||
|
||||
inline auto operator<<(std::ostream& os, const Vector2& v) -> std::ostream&
|
||||
@ -150,44 +91,29 @@ inline auto operator<<(std::ostream& os, const Vector3& v) -> std::ostream&
|
||||
template <typename... Args>
|
||||
auto traceln(std::format_string<Args...> fmt, Args&&... args) -> void
|
||||
{
|
||||
std::cout << std::format("[{}TRACE{}]: ", ansi_bold_fg(fg_cyan), ansi_reset()) << std::format(
|
||||
std::cout << std::format("[{}TRACE{}]: ", ansi_bold_fg(fg::cyan), ansi_reset()) << std::format(
|
||||
fmt, std::forward<Args>(args)...) << std::endl;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto infoln(std::format_string<Args...> fmt, Args&&... args) -> void
|
||||
{
|
||||
std::cout << std::format("[{}INFO{}]: ", ansi_bold_fg(fg_blue), ansi_reset()) << std::format(
|
||||
std::cout << std::format("[{}INFO{}]: ", ansi_bold_fg(fg::blue), ansi_reset()) << std::format(
|
||||
fmt, std::forward<Args>(args)...) << std::endl;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto warnln(std::format_string<Args...> fmt, Args&&... args) -> void
|
||||
{
|
||||
std::cout << std::format("[{}WARNING{}]: ", ansi_bold_fg(fg_yellow), ansi_reset()) << std::format(
|
||||
std::cout << std::format("[{}WARNING{}]: ", ansi_bold_fg(fg::yellow), ansi_reset()) << std::format(
|
||||
fmt, std::forward<Args>(args)...) << std::endl;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto errln(std::format_string<Args...> fmt, Args&&... args) -> void
|
||||
{
|
||||
std::cout << std::format("[{}ERROR{}]: ", ansi_bold_fg(fg_red), ansi_reset()) << std::format(
|
||||
std::cout << std::format("[{}ERROR{}]: ", ansi_bold_fg(fg::red), ansi_reset()) << std::format(
|
||||
fmt, std::forward<Args>(args)...) << std::endl;
|
||||
}
|
||||
|
||||
inline auto print_bitmap(const uint64_t bitmap, const uint8_t w, const uint8_t h, const std::string& title) -> void
|
||||
{
|
||||
traceln("{}:", title);
|
||||
traceln("{}", std::string(2 * w - 1, '='));
|
||||
for (size_t y = 0; y < w; ++y) {
|
||||
std::cout << " ";
|
||||
for (size_t x = 0; x < h; ++x) {
|
||||
std::cout << static_cast<int>(get_bits(bitmap, y * w + x, y * h + x)) << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << std::flush;
|
||||
traceln("{}", std::string(2 * w - 1, '='));
|
||||
}
|
||||
|
||||
#endif
|
||||
15
src/bits.cpp
Normal file
15
src/bits.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "bits.hpp"
|
||||
|
||||
auto print_bitmap(const uint64_t bitmap, const uint8_t w, const uint8_t h, const std::string& title) -> void {
|
||||
traceln("{}:", title);
|
||||
traceln("{}", std::string(2 * w - 1, '='));
|
||||
for (size_t y = 0; y < w; ++y) {
|
||||
std::cout << " ";
|
||||
for (size_t x = 0; x < h; ++x) {
|
||||
std::cout << static_cast<int>(get_bits(bitmap, y * w + x, y * h + x)) << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << std::flush;
|
||||
traceln("{}", std::string(2 * w - 1, '='));
|
||||
}
|
||||
27
src/main.cpp
27
src/main.cpp
@ -16,7 +16,6 @@
|
||||
namespace po = boost::program_options;
|
||||
#endif
|
||||
|
||||
|
||||
// TODO: Implement state discovery/enumeration
|
||||
// - Find all possible initial board states (single one for each possible statespace).
|
||||
// Currently wer're just finding all states given the initial state
|
||||
@ -47,8 +46,8 @@ constexpr std::optional<BS::thread_pool<>* const> thread_pool = &threads;
|
||||
constexpr std::optional<BS::thread_pool<>* const> thread_pool = std::nullopt;
|
||||
#endif
|
||||
|
||||
std::string preset_file;
|
||||
std::string output_file;
|
||||
std::string preset_file = "default.puzzle";
|
||||
std::string output_file = "clusters.puzzle";
|
||||
int max_blocks = 5;
|
||||
int board_width = 6;
|
||||
int board_height = 6;
|
||||
@ -231,21 +230,21 @@ enum class runmode
|
||||
|
||||
auto argparse(const int argc, char* argv[]) -> runmode
|
||||
{
|
||||
#ifndef WIN32
|
||||
#ifndef WIN32
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options() //
|
||||
("help", "produce help message") //
|
||||
|
||||
("presets", po::value<std::string>()->default_value("default.puzzle"), "load presets from file") //
|
||||
|
||||
("output", po::value<std::string>()->default_value("clusters.puzzle"), "output file for generated clusters") //
|
||||
("presets", po::value<std::string>()->default_value(preset_file), "load presets from file") //
|
||||
("output", po::value<std::string>()->default_value(output_file), "output file for generated clusters") //
|
||||
("space", po::value<std::string>()->value_name("rh|klotski"), "generate puzzle space with ruleset") //
|
||||
("w", po::value<int>()->default_value(6), "board width") //
|
||||
("h", po::value<int>()->default_value(6), "board height") //
|
||||
("gx", po::value<int>()->default_value(4), "board goal horizontal position") //
|
||||
("gy", po::value<int>()->default_value(2), "board goal vertical position") //
|
||||
("w", po::value<int>()->default_value(board_width)->value_name("[3, 8]"), "board width") //
|
||||
("h", po::value<int>()->default_value(board_height)->value_name("[3, 8"), "board height") //
|
||||
("gx", po::value<int>()->default_value(goal_x)->value_name("[0, w-1]"), "board goal horizontal position") //
|
||||
("gy", po::value<int>()->default_value(goal_y)->value_name("[0, h-1]"), "board goal vertical position") //
|
||||
("free", "allow free block movement") //
|
||||
("blocks", po::value<int>()->default_value(5), "block limit for puzzle space generation") //
|
||||
("blocks",
|
||||
po::value<int>()->default_value(max_blocks)->value_name("[1, 15]"),
|
||||
"block limit for puzzle space generation") //
|
||||
;
|
||||
|
||||
po::positional_options_description positional;
|
||||
@ -308,7 +307,7 @@ auto argparse(const int argc, char* argv[]) -> runmode
|
||||
if (vm.contains("presets")) {
|
||||
preset_file = vm["presets"].as<std::string>();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return runmode::USER_INTERFACE;
|
||||
}
|
||||
|
||||
@ -682,7 +682,7 @@ 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::try_move_block_at(const uint8_t x, const uint8_t y, const direction dir) const -> std::optional<puzzle>
|
||||
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);
|
||||
const auto [bx, by, bw, bh, bt, bi] = b->unpack_repr();
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
#include "util.hpp"
|
||||
#include "bits.hpp"
|
||||
|
||||
// =============================================================================
|
||||
// Catch2
|
||||
|
||||
@ -668,7 +668,7 @@ TEST_CASE("try_move_block_at_fast matches try_move_block_at", "[move]")
|
||||
|
||||
uint64_t bm = p3->blocks_bitmap();
|
||||
|
||||
for (const direction d : {nor, eas, sou, wes}) {
|
||||
for (const dir d : {nor, eas, sou, wes}) {
|
||||
// Block 0 is the first in sorted order
|
||||
auto slow = p3->try_move_block_at(1, 1, d);
|
||||
auto fast = p3->try_move_block_at_fast(bm, 0, d);
|
||||
|
||||
Reference in New Issue
Block a user