| 122 | } |
| 123 | |
| 124 | auto pbn_to_deal( |
| 125 | const std::string& remain_cards, |
| 126 | const int trump, |
| 127 | const int first, |
| 128 | const py::sequence& current_trick_suit, |
| 129 | const py::sequence& current_trick_rank) -> DealPBN |
| 130 | { |
| 131 | // Validate trump and first (same validation as dict_to_deal) |
| 132 | if (trump < 0 || trump > DDS_STRAINS - 1) { |
| 133 | throw py::value_error( |
| 134 | "trump has invalid value " + std::to_string(trump) + |
| 135 | " (expected range 0.." + std::to_string(DDS_STRAINS - 1) + ")"); |
| 136 | } |
| 137 | if (first < 0 || first > DDS_HANDS - 1) { |
| 138 | throw py::value_error( |
| 139 | "first has invalid value " + std::to_string(first) + |
| 140 | " (expected range 0.." + std::to_string(DDS_HANDS - 1) + ")"); |
| 141 | } |
| 142 | |
| 143 | // Validate remain_cards length (PBN format expects specific size) |
| 144 | constexpr std::size_t expected_size = sizeof(DealPBN::remainCards) - 1U; |
| 145 | if (remain_cards.size() > expected_size) { |
| 146 | throw py::value_error( |
| 147 | "remain_cards PBN string is too long (" + std::to_string(remain_cards.size()) + |
| 148 | " bytes, maximum " + std::to_string(expected_size) + ")"); |
| 149 | } |
| 150 | |
| 151 | DealPBN deal{}; |
| 152 | deal.trump = trump; |
| 153 | deal.first = first; |
| 154 | |
| 155 | const auto trick_suit = sequence_to_bounded_int_vector( |
| 156 | current_trick_suit, |
| 157 | 3, |
| 158 | 0, |
| 159 | DDS_SUITS - 1, |
| 160 | "current_trick_suit"); |
| 161 | const auto trick_rank = sequence_to_bounded_int_vector( |
| 162 | current_trick_rank, |
| 163 | 3, |
| 164 | 0, |
| 165 | 14, |
| 166 | "current_trick_rank"); |
| 167 | for (const int value : trick_rank) { |
| 168 | if (value != 0 && (value < 2 || value > 14)) { |
| 169 | throw py::value_error( |
| 170 | "current_trick_rank has invalid value " + std::to_string(value) + |
| 171 | " (expected 0 or 2..14)"); |
| 172 | } |
| 173 | } |
| 174 | for (int i = 0; i < 3; ++i) { |
| 175 | deal.currentTrickSuit[i] = trick_suit[static_cast<std::size_t>(i)]; |
| 176 | deal.currentTrickRank[i] = trick_rank[static_cast<std::size_t>(i)]; |
| 177 | } |
| 178 | |
| 179 | std::memset(deal.remainCards, 0, sizeof(deal.remainCards)); |
| 180 | const std::size_t copy_size = std::min(remain_cards.size(), sizeof(deal.remainCards) - 1U); |
| 181 | std::memcpy(deal.remainCards, remain_cards.c_str(), copy_size); |
no test coverage detected