2024春晚 刘谦&尼格买提 扑克魔术 C++ 复现
std::rotate
知乎
// clang-format off
/*
rm -rf *.o *.out;g++ poker_magic.cpp -o ./poker_magic.out -Wall -g3 -gdwarf-2 -m64 -std=c++17 -Werror=reorder -Werror=sign-compare -Werror=strict-aliasing -fdiagnostics-color -fsanitize=address -fno-elide-constructors -fsanitize=undefined -fsanitize=leak -Wconversion -pedantic -Wextra -Werror;./poker_magic.out
*/
#include <any>
#include <tuple>
#include <deque>
#include <vector>
#include <random>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <unordered_set>
// clang-format on
namespace NS_PokerMagic
{
inline namespace _utility
{
inline static std::random_device random_device_{};
inline static std::mt19937 generator_{random_device_()};
uint64_t generate_rand_number()
{
static std::uniform_int_distribution<uint64_t> distribute(1ul, 13ul); // 使用均匀分布生成[1, 13]范围内的随机整数
return distribute(generator_);
}
uint64_t generate_specified_random_number(const std::vector<uint64_t>& specified_set)
{
std::uniform_int_distribution<uint64_t> distribute(0ul, specified_set.size() - 1ul);
return specified_set.at(distribute(generator_));
}
template <typename T>
void print_deque(const std::deque<T>& pokers, const bool enable = true)
{
if (!enable) return;
for (auto&& element : pokers)
{
std::cout << element << " ";
}
std::cout << std::endl;
}
enum class NameWords : uint64_t
{
kTwo = 2ul,
kThree = 3ul,
kSeven = 7ul
};
enum class Area : uint64_t
{
kNorth = 1ul,
kSouth = 2ul,
kUnknown = 3ul
};
enum class Gender : uint64_t
{
kGirl = 0ul,
kBoy = 1ul
};
} // namespace _utility
class Poker
{
public:
Poker() { reset(); }
~Poker() = default;
Poker(const Poker&) = delete;
Poker& operator=(const Poker&) = delete;
Poker(Poker&&) noexcept = delete;
Poker& operator=(Poker&&) noexcept = delete;
void reset()
{
std::unordered_set<uint64_t> four_random_pokers;
while (four_random_pokers.size() < 4ul)
{
four_random_pokers.insert(generate_rand_number());
}
poker_.assign(four_random_pokers.begin(), four_random_pokers.end());
std::copy(poker_.begin(), poker_.end(), std::back_inserter(poker_));
assert(poker_.size() == 8ul);
std::cout << "Pokers = ";
print_deque(poker_, true);
}
operator std::deque<uint64_t>&() { return poker_; }
std::deque<uint64_t>& get_poker() { return poker_; }
private:
std::deque<uint64_t> poker_{};
};
class Person
{
public:
Person() { random_infomation(); }
~Person() = default;
Person(const NameWords& name_words, const Area& area, const Gender& gender) : Person(static_cast<uint64_t>(name_words), static_cast<uint64_t>(area), static_cast<uint64_t>(gender)) {}
Person(const Person&) = delete;
Person& operator=(const Person&) = delete;
Person(Person&&) noexcept = delete;
Person& operator=(Person&&) noexcept = delete;
void show(Poker& pk, const bool enable_print = 0)
{
std::deque<uint64_t>& poker = pk;
// 1. 根据名字内字的个数 将牌移到后面
std::rotate(poker.begin(), poker.begin() + name_words_, poker.end());
print_deque(poker, enable_print);
// 2. 将前三张牌插入中间
auto middle = (poker.begin() + 3ul) + (poker.size() - 3ul) / 2ul;
std::rotate(poker.begin(), poker.begin() + 3, middle);
print_deque(poker, enable_print);
// 3. 取出预留目标牌 (即最上面的一张)
const uint64_t target = poker.at(0ul);
std::cout << "Target = " << target << std::endl;
poker.pop_front();
print_deque(poker, enable_print);
// 3. 根据地区:北方/南方/不明, 将前 1/2/3 张插入中间
middle = (poker.begin() + area_) + (poker.size() - area_) / 2ul;
std::rotate(poker.begin(), poker.begin() + area_, middle);
print_deque(poker, enable_print);
// 4. boys 丢掉一张或 girls 丢掉两张
if (gender_ == 1ul) // boys
{
poker.pop_front();
}
else // girls
{
poker.pop_front();
poker.pop_front();
}
print_deque(poker, enable_print);
// 5. 见证奇迹的时刻
constexpr std::string_view seven_character_mantra{"见证奇迹的时刻"};
constexpr uint64_t seven_size = (seven_character_mantra.size() / 3ul);
for (uint64_t i = 0; i < seven_size; ++i)
{
std::rotate(poker.begin(), poker.begin() + 1, poker.end());
print_deque(poker, enable_print);
}
#if 0 // 现场
if (gender_ == 1ul)
{
while (poker.size() != 2ul)
{
std::rotate(poker.begin(), poker.begin() + 1ul, poker.end());
poker.pop_front();
}
}
else
{
while (poker.size() != 1)
{
std::rotate(poker.begin(), poker.begin() + 1ul, poker.end());
poker.pop_front();
}
}
if (gender_ == 1ul)
{
assert(poker.size() == 2ul);
std::rotate(poker.begin(), poker.begin() + 1ul, poker.end());
poker.pop_front();
}
else
{
assert(poker.size() == 1ul);
}
#else
// 6. 好运留下来 烦恼丢出去
while (poker.size() != 1ul)
{
std::rotate(poker.begin(), poker.begin() + 1ul, poker.end());
poker.pop_front();
}
#endif
// 7. 预期 assert
assert(poker.size() == 1ul);
std::cout << "Remain = ";
print_deque(poker, true);
assert(poker.at(0ul) == target);
}
static void test_magic_show()
{
Poker poker;
Person person_1;
person_1.show(poker);
Person person_2(NameWords::kTwo, Area::kNorth, Gender::kBoy);
poker.reset();
person_2.show(poker);
Person person_3(NameWords::kThree, Area::kNorth, Gender::kGirl);
poker.reset();
person_3.show(poker);
}
private:
Person(const uint64_t name_words, const uint64_t area, const uint64_t gender) : name_words_(name_words), area_(area), gender_(gender)
{
assert(name_words == 2ul || name_words == 3ul || name_words == 7ul);
assert(area == 1ul || area == 2ul || area == 3ul);
assert(gender == 1ul || gender == 0ul);
}
void random_infomation()
{
#define CAST(arg) static_cast<uint64_t>(arg)
const std::vector<uint64_t> name_word_collect{CAST(NameWords::kTwo), CAST(NameWords::kThree), CAST(NameWords::kSeven)};
const std::vector<uint64_t> area_collect{CAST(Area::kNorth), CAST(Area::kSouth), CAST(Area::kUnknown)};
const std::vector<uint64_t> gender_collect{CAST(Gender::kGirl), CAST(Gender::kBoy)};
#undef CAST
name_words_ = generate_specified_random_number(name_word_collect);
area_ = generate_specified_random_number(area_collect);
gender_ = generate_specified_random_number(gender_collect);
}
private:
uint64_t name_words_;
uint64_t area_;
uint64_t gender_;
};
} // namespace NS_PokerMagic
int main([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])
{
NS_PokerMagic::Person::test_magic_show();
return 0;
}
输出
Pokers = 8 13 2 7 8 13 2 7
Target = 2
Remain = 2
Pokers = 1 4 2 5 1 4 2 5
Target = 4
Remain = 4
Pokers = 3 11 1 4 3 11 1 4
Target = 1
Remain = 1