#ifndef DYNAMIC_PROGRAM_HANABI_TYPES_H #define DYNAMIC_PROGRAM_HANABI_TYPES_H #include #include #include #include #include namespace Hanabi { using rank_t = std::uint8_t; using suit_t = std::uint8_t; using clue_t = boost::rational; using player_t = std::uint8_t; using hand_index_t = std::uint8_t; using probability_base_type = unsigned long; using rational_probability = boost::rational; /** * Define macro * NUSE_RATIONAL_PROBABILITIES * to use floating-point arithmetic for the stored probabilities * instead of rational representations */ #ifndef NUSE_RATIONAL_PROBABILITIES using probability_t = boost::rational; #else using probability_t = double; #endif std::ostream & print_probability(std::ostream & os, const rational_probability & prob); std::ostream & print_probability(std::ostream & os, double prob); template std::ostream & print_probability(std::ostream & os, const std::optional & prob); /** * We will generally assume that stacks are played from n to 0 * Playing a 0 will yield a clue * Therefore, for the default hanabi, we will play 4,3,2,1,0 in that order * on each stack. A stack with no cards played implicitly has value 5 on it * This is just easier to implement, since then the remaining number of cards * to be played is always the current number of the stack */ constexpr rank_t starting_card_rank = 5; constexpr suit_t max_suit_index = 5; constexpr size_t max_card_duplicity = 3; const clue_t max_num_clues = 8; constexpr unsigned max_num_strikes = 2; /** Maximum number of allowed strikes */ constexpr hand_index_t invalid_hand_idx = std::numeric_limits::max(); // We might want to change these at runtime to adapt to other variants. // However, a global variable is used so that we can have an output operator for cards reading from here // Note that this is therefore not static so that we have external linking inline std::array suit_initials = {'r', 'y', 'g', 'b', 'p', 't'}; struct Card { suit_t suit; rank_t rank; // These attributes are not needed in general for a card, // they represent internal states during backtracking. uint8_t local_index; bool in_starting_hand; bool initial_trash; /** * @brief Compares cards *only* regarding suit and rank. * This is inlined as this is a runtime critical function when backtracking. */ inline bool operator==(const Card & other) const; inline bool operator!=(const Card & other) const; }; enum class ActionType : std::uint8_t { play = 0 , discard = 1 , clue = 2 , color_clue = 2 , rank_clue = 3 , end_game = 4 , vote_terminate_players = 5 , vote_terminate = 10 , }; struct Action { ActionType type{}; Card card{}; }; // Output utilities for Cards and Actions std::string to_string(const Card & card); std::ostream & operator<<(std::ostream & os, const Card & card); std::ostream & operator<<(std::ostream & os, const Action & action); std::ostream & operator<<(std::ostream & os, const ActionType & action_type); namespace Cards { static constexpr Card r0 = {0, 5}; static constexpr Card r1 = {0, 4}; static constexpr Card r2 = {0, 3}; static constexpr Card r3 = {0, 2}; static constexpr Card r4 = {0, 1}; static constexpr Card r5 = {0, 0}; static constexpr Card y0 = {1, 5}; static constexpr Card y1 = {1, 4}; static constexpr Card y2 = {1, 3}; static constexpr Card y3 = {1, 2}; static constexpr Card y4 = {1, 1}; static constexpr Card y5 = {1, 0}; static constexpr Card g0 = {2, 5}; static constexpr Card g1 = {2, 4}; static constexpr Card g2 = {2, 3}; static constexpr Card g3 = {2, 2}; static constexpr Card g4 = {2, 1}; static constexpr Card g5 = {2, 0}; static constexpr Card b0 = {3, 5}; static constexpr Card b1 = {3, 4}; static constexpr Card b2 = {3, 3}; static constexpr Card b3 = {3, 2}; static constexpr Card b4 = {3, 1}; static constexpr Card b5 = {3, 0}; static constexpr Card p0 = {4, 5}; static constexpr Card p1 = {4, 4}; static constexpr Card p2 = {4, 3}; static constexpr Card p3 = {4, 2}; static constexpr Card p4 = {4, 1}; static constexpr Card p5 = {4, 0}; static constexpr Card t0 = {5, 5}; static constexpr Card t1 = {5, 4}; static constexpr Card t2 = {5, 3}; static constexpr Card t3 = {5, 2}; static constexpr Card t4 = {5, 1}; static constexpr Card t5 = {5, 0}; static constexpr Card unknown = {std::numeric_limits::max(), 0}; static constexpr Card trash = {std::numeric_limits::max(), std::numeric_limits::max() }; } //// INLINE SECTION bool Card::operator==(const Card & other) const { return suit == other.suit and rank == other.rank; } bool Card::operator!=(const Card & other) const { return not (*this == other); } template std::ostream & print_probability(std::ostream & os, const std::optional & prob) { if (prob.has_value()) { return print_probability(os, prob.value()); } else { os << "unknown"; } return os; } } // namespace Hanabi #endif //DYNAMIC_PROGRAM_HANABI_TYPES_H