/* * Copyright (c) 2021, Gunnar Beutner * * SPDX-License-Identifier: BSD-2-Clause */ #include "Player.h" #include "Helpers.h" #include #include namespace Hearts { NonnullRefPtrVector Player::pick_cards_to_pass(PassingDirection) { auto sorted_hand = hand_sorted_by_points_and_value(); NonnullRefPtrVector cards; cards.append(*sorted_hand[0].card); cards.append(*sorted_hand[1].card); cards.append(*sorted_hand[2].card); return cards; } Vector Player::hand_sorted_by_points_and_value() const { Vector sorted_hand; for (size_t i = 0; i < hand.size(); i++) { auto& card = hand[i]; if (card) sorted_hand.empend(*card, i); } quick_sort(sorted_hand, [](auto& cwi1, auto& cwi2) { if (hearts_card_points(*cwi2.card) < hearts_card_points(*cwi1.card)) return true; if (hearts_card_points(*cwi1.card) == hearts_card_points(*cwi2.card) && hearts_card_value(*cwi2.card) < hearts_card_value(*cwi1.card)) return true; return false; }); return sorted_hand; } size_t Player::pick_lead_card(Function valid_play, Function prefer_card, Function lower_value_card_in_play) { auto sorted_hand = hand_sorted_by_points_and_value(); if constexpr (HEARTS_DEBUG) { dbgln("Sorted hand:"); for (auto& cwi : sorted_hand) dbgln("{}", *cwi.card); dbgln("----"); } ssize_t fallback_index = -1; ssize_t last_index = -1; for (auto& cwi : sorted_hand) { if (!valid_play(*cwi.card)) continue; if (lower_value_card_in_play(*cwi.card)) { // Allow falling back to this card if no other suitable card is in play. fallback_index = cwi.index; continue; } if (prefer_card(*cwi.card)) { dbgln_if(HEARTS_DEBUG, "Preferring card {}", *cwi.card); return cwi.index; } last_index = cwi.index; } if (last_index == -1) { dbgln_if(HEARTS_DEBUG, "Falling back to card {}", *hand[fallback_index]); return fallback_index; } else return last_index; } Optional Player::pick_low_points_high_value_card(Optional type) { int min_points = -1; Optional card_index; for (ssize_t i = hand.size() - 1; i >= 0; i--) { auto& card = hand[i]; if (card.is_null()) continue; if (type.has_value() && card->type() != type.value()) continue; auto points = hearts_card_points(*card); if (min_points == -1 || points < min_points) { min_points = points; card_index = i; } } VERIFY(card_index.has_value() || type.has_value()); return card_index; } Optional Player::pick_lower_value_card(Card& other_card) { for (ssize_t i = hand.size() - 1; i >= 0; i--) { auto& card = hand[i]; if (card.is_null()) continue; if (card->type() == other_card.type() && hearts_card_value(*card) < hearts_card_value(other_card)) return i; } return {}; } Optional Player::pick_slightly_higher_value_card(Card& other_card) { for (size_t i = 0; i < hand.size(); i++) { auto& card = hand[i]; if (card.is_null()) continue; if (card->type() == other_card.type() && hearts_card_value(*card) > hearts_card_value(other_card)) return i; } return {}; } size_t Player::pick_max_points_card() { auto queen_of_spades_maybe = pick_specific_card(Card::Type::Spades, CardValue::Queen); if (queen_of_spades_maybe.has_value()) return queen_of_spades_maybe.value(); if (has_card_of_type(Card::Type::Hearts)) return pick_last_card(); return pick_low_points_high_value_card().value(); } Optional Player::pick_specific_card(Card::Type type, CardValue value) { for (size_t i = 0; i < hand.size(); i++) { auto& card = hand[i]; if (card.is_null()) continue; if (card->type() == type && hearts_card_value(*card) == value) return i; } return {}; } size_t Player::pick_last_card() { for (ssize_t i = hand.size() - 1; i >= 0; i--) { auto& card = hand[i]; if (card.is_null()) continue; return i; } VERIFY_NOT_REACHED(); } bool Player::has_card_of_type(Card::Type type) { auto matching_card = hand.first_matching([&](auto const& other_card) { return !other_card.is_null() && other_card->type() == type; }); return matching_card.has_value(); } void Player::remove_cards(const NonnullRefPtrVector& cards) { for (auto& card : cards) { hand.remove_first_matching([&card](auto& other_card) { return other_card == card; }); } } }