implement discarding dupes and sacrifice discards

This commit is contained in:
Maximilian Keßler 2024-01-12 15:18:30 +01:00
parent 43477b1023
commit 8398173939
Signed by: max
GPG key ID: BCC5A619923C0BA5

View file

@ -194,7 +194,7 @@ namespace Hanabi
_actions_log.emplace(ActionType::play, played_card, index, _num_clues == 8, strike);
if (is_playable(played_card))
if (!strike)
{
--_stacks[played_card.suit];
_score++;
@ -951,31 +951,81 @@ namespace Hanabi
// Check for discards now
if (_pace > 0 and _num_clues < max_num_clues)
{
for (std::uint8_t index = 0; index < hand_size; index++)
// This will hold the index of trash to discard
std::uint8_t const invalid_index = std::numeric_limits<std::uint8_t>::max();
std::uint8_t discard_index = invalid_index;
for (hand_index_t index = 0; index < hand_size; index++)
{
if (is_trash(hand[index]))
{
probability_t sum_of_probabilities = 0;
do_for_each_potential_draw(index, false, [this, &sum_of_probabilities](const unsigned long multiplicity) {
sum_of_probabilities += evaluate_state() * multiplicity;
});
const unsigned long total_weight = std::max(static_cast<unsigned long>(_weighted_draw_pile_size), 1ul);
const probability_t probability_discard = sum_of_probabilities / total_weight;
best_probability = std::max(best_probability, probability_discard);
best_probability = std::max(best_probability, probability_discard);
if (best_probability == 1)
{
update_tablebase(id_of_state, best_probability);
return best_probability;
};
discard_index = index;
// All discards are equivalent, do not continue searching for different trash
break;
}
}
// If no trivial trash found, check for duplicates next
if (discard_index == invalid_index) {
for (std::uint8_t index = 0; index < hand_size; index++) {
Card const card = _hands[_turn][index];
auto it = std::find_if(_hands[_turn].begin() + index + 1, _hands[_turn].end(), [&card, this](Card const & card_in_hand) {
return card_in_hand == card;
});
if (it != _hands[_turn].end()) {
// found a duplicate to discard
discard_index = index;
// Since we are discarding essentially trash, we do not have to consider further actions
break;
}
}
}
// Discard if we found trash now
if (discard_index != invalid_index) {
probability_t sum_of_probabilities = 0;
do_for_each_potential_draw(discard_index, false, [this, &sum_of_probabilities](const unsigned long multiplicity) {
sum_of_probabilities += evaluate_state() * multiplicity;
});
const unsigned long total_weight = std::max(static_cast<unsigned long>(_weighted_draw_pile_size), 1ul);
const probability_t probability_discard = sum_of_probabilities / total_weight;
best_probability = std::max(best_probability, probability_discard);
best_probability = std::max(best_probability, probability_discard);
if (best_probability == 1)
{
update_tablebase(id_of_state, best_probability);
return best_probability;
};
} else {
// If we reach this state, then there are no dupes in hand, so we need to check if we want to
// sacrifice cards in hand
for(hand_index_t index = 0; index < hand_size; ++index) {
if(!is_critical(hand[index])) {
std::cout << "Considered sacrificing card " << hand[index] << "in the following state:\n" << *this << std::endl;
// consider discarding this
probability_t sum_of_probabilities = 0;
do_for_each_potential_draw(index, false, [this, &sum_of_probabilities](const unsigned long multiplicity) {
sum_of_probabilities += evaluate_state() * multiplicity;
});
const unsigned long total_weight = std::max(static_cast<unsigned long>(_weighted_draw_pile_size), 1ul);
const probability_t probability_discard = sum_of_probabilities / total_weight;
best_probability = std::max(best_probability, probability_discard);
best_probability = std::max(best_probability, probability_discard);
if (best_probability == 1)
{
update_tablebase(id_of_state, best_probability);
return best_probability;
};
}
}
}
}
// Last option is to stall