// This file is part of Timmi. // // Timmi is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. #include #include #include #include bool Card::operator==(const Card &other) const { if (joker != other.joker) { return false; } if (is_joker(*this) && !is_card(*this) && !is_card(other)) { // Comparison of non-assigned jokers // Already tested that they are same type of jokers return true; } if (is_joker(*this) && (!is_card(*this) || !is_card(other))) { // Comparison of assigned and non-assigned joker return is_valid_joker(other) || is_valid_joker(*this); } return other.rank == rank && other.suit == suit; } bool TableCard::operator==(const TableCard &other) const { return other.tobeat == tobeat && other.beater == beater; } //----------------------- // Card-related functions bool is_card(const Card &card) { // Valid card, normal or valid joker return card.suit != NOSUIT && card.rank != NORANK && (!is_joker(card) || is_valid_joker(card)); } bool is_joker(const Card &card) { return card.joker != NOJOKER; } bool is_valid_joker(const Card &card) { if (card.rank == NORANK || card.suit == NOSUIT) { return false; } if (card.joker == REDJOKER) { return card.suit == DIAMONDS || card.suit == HEARTS; } else if (card.joker == BLACKJOKER) { return card.suit == SPADES || card.suit == CLUBS; } else { return true; } } Card unassign_joker(Card card) { if (is_joker(card)) { card.rank = NORANK; card.suit = NOSUIT; } return card; } bool can_beat(const Card &tobeat, const Card &beater, Suit trump) { if (is_joker(beater) && !is_card(beater)) { // Unassigned joker, try to assign Card newbeater = beater; newbeater.suit = trump; newbeater.rank = ACE; if (!is_valid_joker(newbeater)) { // Can't be trump, so should be tobeat's suit newbeater.suit = tobeat.suit; } if (is_valid_joker(newbeater)) { return can_beat(tobeat, newbeater, trump); } else { return false; } } if (!is_card(tobeat) || !is_card(beater)) { return false; } if (tobeat.suit == beater.suit) { return rank_higher(beater.rank, tobeat.rank); } if (beater.suit == trump) { return true; } return false; } bool rank_higher(Rank a, Rank b) { // Return true if a is higher than b // Regard ACE as 14 if (a == ACE && b != ACE) { return true; } if (b != ACE && a > b) { return true; } return false; } //----------------------------------- // Vector of cards -related functions void init_deck(std::vector &deck, bool jokers) { int rank; Card card = {NOJOKER, NOSUIT, NORANK}; for (rank = ACE; rank <= KING; rank += 1) { card.rank = static_cast(rank); card.suit = HEARTS; deck.push_back(card); card.suit = DIAMONDS; deck.push_back(card); card.suit = CLUBS; deck.push_back(card); card.suit = SPADES; deck.push_back(card); } if (jokers) { card.suit = NOSUIT; card.rank = NORANK; card.joker = REDJOKER; deck.push_back(card); card.joker = BLACKJOKER; deck.push_back(card); card.joker = JOKER; deck.push_back(card); } } void remove_card(std::vector &cards, Card card) { std::vector::iterator iter; for (iter = cards.begin(); iter < cards.end(); iter++) { if (*iter == card) { cards.erase(iter); return; } } debug(CRITICAL, "Card to remove not found on list"); } void shuffle_deck(std::vector &deck) { int n = deck.size(); Random rng; while (--n > 0) { int k = rng.random_in_range(0, n); Card temp = deck.at(n); deck.at(n) = deck.at(k); deck.at(k) = temp; } } //---------------------------------------- // Vector of TableCards -related functions bool all_beaten(const std::vector &table) { if (table.size() == 0) return false; int i; for (i = 0; i < table.size(); i++) { if (!is_card(table.at(i).beater)) { return false; } } return true; } Rank biggest_rank_on_table(std::vector &table) { if (table.size() == 0) { debug(WARNING, "Tried to find biggest rank on empty table"); return NORANK; } Rank result = table.at(0).tobeat.rank; int i; for (i = 1; i < table.size(); i++) { if (rank_higher(table.at(i).tobeat.rank, result)) { result = table.at(i).tobeat.rank; } if (is_card(table.at(i).beater) && rank_higher(table.at(i).beater.rank, result)) { result = table.at(i).beater.rank; } } return result; } Card assign_joker_hit(std::vector &table, Card card, Suit trump) { if (table.size() == 0) { card.rank = ACE; } else { card.rank = biggest_rank_on_table(table); } card.suit = trump; if (!is_valid_joker(card)) { if (card.joker == REDJOKER) { card.suit = DIAMONDS; } else { card.suit = SPADES; } } return card; } Card assign_joker_beat(std::vector &table, Card card, Card tobeat, Suit trump) { card.rank = biggest_rank_on_table(table); card.suit = trump; if (!is_valid_joker(card)) { card.suit = tobeat.suit; } if (!can_beat(tobeat, card, trump)) { card.rank = ACE; } return card; } // Card to beat on table, not beaten already bool card_on_table(const std::vector &table, const Card &card) { int i; for (i = 0; i < table.size(); i++) { if (table.at(i).tobeat == card && !is_card(table.at(i).beater)) { return true; } } debug(INFO, "Card not on table"); return false; } // Player with hand can beat all cards on table // Implementation is not perfect - may return false negatives bool can_beat_all(const std::vector &table, const std::vector &hand, Suit trump) { std::vector tmphand(hand); int t, h; for (t = 0; t < table.size(); t++) { if (is_card(table.at(t).beater)) { continue; } bool found = false; for (h = 0; h < tmphand.size(); h++) { if (can_beat(table.at(t).tobeat, tmphand.at(h), trump)) { found = true; tmphand.erase(tmphand.begin() + h); break; } } if (!found) { return false; } } return true; } // One rank only and none beaten bool one_rank_only(const std::vector &table, Rank rank) { int i; for (i = 0; i < table.size(); i++) { if (is_card(table.at(i).beater) || table.at(i).tobeat.rank != rank) { return false; } } return true; } bool rank_exists_on_table(const std::vector &table, Rank rank) { int i; for (i = 0; i < table.size(); i++) { if (table.at(i).beater.rank == rank) return true; if (table.at(i).tobeat.rank == rank) return true; } return false; } bool rank_exists_on_table(const std::vector &table, const Card &card) { return rank_exists_on_table(table, card.rank); } //--------------------------------------- // Sorting vector of cards // Sort worst card last, beatwise CMPWorst::CMPWorst(Suit trump): trump_(trump) {} bool CMPWorst::operator()(Card a, Card b) const { // Return true if a should be before b return CMPSuit(trump_)(b, a); } // Sort cards by suit and rank, trump suit first CMPSuit::CMPSuit(Suit trump): trump_(trump) {} bool CMPSuit::operator()(Card a, Card b) const { // Return true if a should be before b if (is_joker(a) && !is_joker(b)) { return true; } else if (!is_joker(a) && is_joker(b)) { return false; } else if (is_joker(a) && is_joker(b)) { // Generic jokers before non-generic if (a.joker == JOKER && b.joker != JOKER) { return true; } else if (a.joker != JOKER && b.joker == JOKER) { return false; } else if (!is_card(a) && !is_card(b)) { // No order defined, consider equal return false; } } if (!is_card(a) || !is_card(b)) { debug(CRITICAL, "Tried to sort non-cards"); } if (a.suit == trump_ && b.suit != trump_) { return true; } else if (a.suit != trump_ && b.suit == trump_) { return false; } return rank_higher(a.rank, b.rank); } //--------------------------------------- // Sorting vector of tablecards // Beaten first, then by CMPWorst CMPTable::CMPTable(Suit trump): trump_(trump) {} bool CMPTable::operator()(TableCard a, TableCard b) const { // Return true if a should be before b if (is_card(a.beater) && !is_card(b.beater)) { return true; } else if (!is_card(a.beater) && is_card(b.beater)) { return false; } return rank_higher(a.tobeat.rank, b.tobeat.rank); }