From 1ae18c1228fe75513b161b8bfa53735ac1080725 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sat, 22 May 2021 11:21:56 +0200 Subject: [PATCH] Hearts: Let the AI pick better lead cards Instead of picking the card with the lowest value we should pick the card with the highest value for which we know no lower value card is in play anymore and that someone else still has an even higher value card. --- Userland/Games/Hearts/Game.cpp | 37 ++++++++++++++++++++++++++-- Userland/Games/Hearts/Game.h | 2 ++ Userland/Games/Hearts/Player.cpp | 41 ++++++++++++++++++++------------ Userland/Games/Hearts/Player.h | 2 +- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/Userland/Games/Hearts/Game.cpp b/Userland/Games/Hearts/Game.cpp index ee0469fd1e..d17fa9097b 100644 --- a/Userland/Games/Hearts/Game.cpp +++ b/Userland/Games/Hearts/Game.cpp @@ -161,6 +161,32 @@ void Game::timer_event(Core::TimerEvent&) update(); } +bool Game::other_player_has_lower_value_card(Player& player, Card& card) +{ + for (auto& other_player : m_players) { + if (&player != &other_player) { + for (auto& other_card : other_player.hand) { + if (other_card && card.type() == other_card->type() && hearts_card_value(*other_card) < hearts_card_value(card)) + return true; + } + } + } + return false; +} + +bool Game::other_player_has_higher_value_card(Player& player, Card& card) +{ + for (auto& other_player : m_players) { + if (&player != &other_player) { + for (auto& other_card : other_player.hand) { + if (other_card && card.type() == other_card->type() && hearts_card_value(*other_card) > hearts_card_value(card)) + return true; + } + } + } + return false; +} + #define RETURN_CARD_IF_VALID(card) \ do { \ auto card_index = (card); \ @@ -177,8 +203,15 @@ size_t Game::pick_card(Player& player) auto clubs_2 = player.pick_specific_card(Card::Type::Clubs, CardValue::Number_2); VERIFY(clubs_2.has_value()); return clubs_2.value(); - } else - return player.pick_low_points_low_value_card(); + } else { + auto valid_card = [this, &player](Card& card) { + return is_valid_play(player, card); + }; + auto prefer_card = [this, &player](Card& card) { + return !other_player_has_lower_value_card(player, card) && other_player_has_higher_value_card(player, card); + }; + return player.pick_lead_card(move(valid_card), move(prefer_card)); + } } auto* high_card = &m_trick[0]; for (auto& card : m_trick) diff --git a/Userland/Games/Hearts/Game.h b/Userland/Games/Hearts/Game.h index 560ca64ecc..79c323d71a 100644 --- a/Userland/Games/Hearts/Game.h +++ b/Userland/Games/Hearts/Game.h @@ -45,6 +45,8 @@ private: Player& current_player(); bool game_ended() const { return m_trick_number == 13; } bool is_winner(Player& player); + bool other_player_has_lower_value_card(Player& player, Card& card); + bool other_player_has_higher_value_card(Player& player, Card& card); void start_animation(NonnullRefPtrVector cards, Gfx::IntPoint const& end, Function did_finish_callback, int initial_delay_ms, int steps = 30); void stop_animation(); diff --git a/Userland/Games/Hearts/Player.cpp b/Userland/Games/Hearts/Player.cpp index ab129f4739..021d79001f 100644 --- a/Userland/Games/Hearts/Player.cpp +++ b/Userland/Games/Hearts/Player.cpp @@ -6,28 +6,39 @@ #include "Player.h" #include "Helpers.h" +#include namespace Hearts { -size_t Player::pick_low_points_low_value_card() +size_t Player::pick_lead_card(Function valid_play, Function prefer_card) { - int min_points = -1; - int min_value = -1; - int card_index = -1; + struct CardWithIndex { + RefPtr card; + size_t index; + }; + Vector sorted_hand; for (size_t i = 0; i < hand.size(); i++) { auto& card = hand[i]; - if (card.is_null()) - continue; - auto points = hearts_card_points(*card); - auto value = hearts_card_value(*card); - if (min_points != -1 && (points > min_points || static_cast(value) > min_value)) - continue; - min_points = points; - min_value = static_cast(value); - card_index = i; + if (card) + sorted_hand.empend(card, i); } - VERIFY(card_index != -1); - return card_index; + quick_sort(sorted_hand, [](auto& cwi1, auto& cwi2) { + if (hearts_card_points(*cwi1.card) >= hearts_card_points(*cwi2.card)) + return true; + if (hearts_card_value(*cwi1.card) >= hearts_card_value(*cwi2.card)) + return true; + return false; + }); + + size_t last_index = -1; + for (auto& cwi : sorted_hand) { + if (!valid_play(*cwi.card)) + continue; + if (prefer_card(*cwi.card)) + return cwi.index; + last_index = cwi.index; + } + return last_index; } Optional Player::pick_low_points_high_value_card(Optional type) diff --git a/Userland/Games/Hearts/Player.h b/Userland/Games/Hearts/Player.h index be1febd6a9..d7737951fe 100644 --- a/Userland/Games/Hearts/Player.h +++ b/Userland/Games/Hearts/Player.h @@ -21,7 +21,7 @@ public: { } - size_t pick_low_points_low_value_card(); + size_t pick_lead_card(Function, Function); Optional pick_low_points_high_value_card(Optional type = {}); Optional pick_lower_value_card(Card& other_card); Optional pick_slightly_higher_value_card(Card& other_card);