/* * This program 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 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "poker.h" // ================================================================ // Card classes CardDeck::CardDeck(KRandomSequence *random) { m_random = random; reset(); } CardDeck::~CardDeck() { } // ---------------------------------------------------------------- void CardDeck::reset() { int i; CardValue card; for (i = 0, card = lowestCard; i < numCards; i++, card = card + 1) m_cards[i] = card; m_topCard = 0; } void CardDeck::shuffle() { CardValue tmp; int card; if (m_topCard == numCards) return; for (int i = m_topCard; i < numCards - 1; i++) { // Choose a random card from the not-yet-shuffled ones. card = m_random->getLong(numCards - i) + i; // Exchange the random card with the current card. tmp = m_cards[i]; m_cards[i] = m_cards[card]; m_cards[card] = tmp; } } CardValue CardDeck::getTopCard() { if (m_topCard == numCards) return DECK; return m_cards[m_topCard++]; } // ================================================================ // Poker types TQString PokerHandNames[] = { "High Card", "Pair", "Two Pairs", "Three Of A Kind", "Straight", "Flush", "Full House", "Four Of A Kind", "Straight Flush", "Royal Flush" }; PokerHand::PokerHand() { clear(); } PokerHand::~PokerHand() { } // Compare two poker hands, and return true if the first one is less // valuable than the second one. Otherwise return false. bool PokerHand::operator<(PokerHand &hand2) { CardValue card1; CardValue card2; int i; // Make sure all relevant fields are initialized. if (m_changed) analyze(); if (hand2.m_changed) hand2.analyze(); #if 0 kdDebug() << "Hand 1: " << PokerHandNames[(int) m_type] << " (" << ((int) m_firstRank) + 2 << ", " << ((int) m_secondRank) + 2 << ")" << endl; kdDebug() << "Hand 2: " << PokerHandNames[(int) hand2.m_type] << " (" << ((int) hand2.m_firstRank) + 2 << ", " << ((int) hand2.m_secondRank) + 2 << ")" << endl; #endif // 1. If we have a better hand, then it is simple. if (m_type != hand2.m_type) return m_type < hand2.m_type; // 2. If the hands are equal, check the internal parts of the hand // type (like the first and second pair of two pairs). switch (m_type) { case HighCard: case Pair: case ThreeOfAKind: case Straight: case FourOfAKind: case StraightFlush: case RoyalFlush: if (m_firstRank != hand2.m_firstRank) return m_firstRank < hand2.m_firstRank; break; case TwoPairs: case FullHouse: // Compare the first rank first, and then the second. if (m_firstRank != hand2.m_firstRank) return m_firstRank < hand2.m_firstRank; if (m_secondRank != hand2.m_secondRank) return m_secondRank < hand2.m_secondRank; break; case Flush: card1 = ROOF; card2 = ROOF; for (i = 0; i < PokerHandSize; i++) { card1 = findNextBest(card1, true); card2 = hand2.findNextBest(card2, true); if (card1 != card2) return card1 < card2; } // If we get here all the card ranks are the same in both hands. // This means that they have to be of different suits. break; default: // Shouldn't get here. assert(0); } // 3. Ok, the hands themselves are the same. Now check if the cards // outside the hands differ. card1 = ROOF; card2 = ROOF; while (card1 != DECK) { card1 = findNextBest(card1, false); card2 = hand2.findNextBest(card2, false); if (card1 != card2) return card1 < card2; } // 4. *Every* rank is the same. Then they must differ in suit. card1 = rank2card(m_firstRank + 1); card2 = rank2card(hand2.m_firstRank + 1); return card1 < card2; } // ---------------------------------------------------------------- // Ordinary methods // Clear the hand - set all entries to DECK. void PokerHand::clear() { for (int i = 0; i < PokerHandSize; i++) m_cards[i] = DECK; m_changed = true; } /* Throw away card no 'cardno'. */ void PokerHand::clear(int cardno) { m_cards[cardno] = DECK; m_changed = true; } CardValue PokerHand::getCard(int cardno) const { return m_cards[cardno]; } void PokerHand::setCard(int cardno, CardValue card) { m_cards[cardno] = card; m_changed = true; } bool PokerHand::findRank(CardRank the_rank) const { for (int i = 0; i < PokerHandSize; i++) if (rank(m_cards[i]) == the_rank) return true; return false; } bool PokerHand::findSuit(CardSuit the_suit) const { for (int i = 0; i < PokerHandSize; i++) if (suit(m_cards[i]) == the_suit) return true; return false; } // Return the next lower card value below the card 'roof'. // // If 'getFound' is true, then search only those cards that are marked // as found, i.e. are among those that determine the hand type. // Otherwise search the cards that are *not* among these. // CardValue PokerHand::findNextBest(CardValue roof, bool getFound) const { CardValue next; next = DECK; for (int i = 0; i < PokerHandSize; i++) { if (m_cards[i] > next && m_cards[i] < roof && (getFound == m_foundCards[i])) next = m_cards[i]; } return next; } int PokerHand::testStraight() { CardRank lowest = ACE; /* Set ranks[i] to the value of each card and find the lowest value. */ for (int i = 0; i < PokerHandSize; i++) { if (rank(m_cards[i]) < lowest) lowest = rank(m_cards[i]); } // Look for special cases ace-2-3-4-5. // (very ugly but fast to write): if ((findRank(ACE) && findRank(TWO) && findRank(THREE) && findRank(FOUR) && findRank(FIVE))) { m_firstRank = FIVE; } else { for (int i = 0; i < PokerHandSize; i++) if (!findRank(lowest + i)) return 0; // did not find a straight m_firstRank = lowest + 4; } // Found a straight. Record it in foundCards[]. for (int i = 0; i < PokerHandSize; i++) addFoundCard(i); // Check for a royal flush if (lowest == TEN) return 2; // An ordinary straight: return 1. return 1; } bool PokerHand::testFlush() { int theSuit; CardValue highest_card; highest_card = m_cards[0]; theSuit = suit(m_cards[0]); for (int i = 1; i < PokerHandSize; i++) { if (theSuit != suit(m_cards[i])) return 0; if (m_cards[i] > highest_card) highest_card = m_cards[i]; } // Found a flush. Now record the cards. for (int i = 0; i < PokerHandSize; i++) addFoundCard(i); m_firstRank = rank(highest_card); return true; } PokerHandType PokerHand::analyze() { if (m_changed) m_type = do_analyze(); return m_type; } PokerHandType PokerHand::do_analyze() { CardValue card1; CardValue card2; int i; int j; cleanFoundCards(); m_changed = 0; int isStraight = testStraight(); // Detect special cases; if (isStraight) { if (testFlush()) { if (isStraight == 2) return RoyalFlush; else return StraightFlush; } else return Straight; } if (testFlush()) return Flush; /* Find number of matches. */ int matching = 0; for (i = 0; i < PokerHandSize; i++) { for (j = i + 1; j < PokerHandSize; j++) if (rank(m_cards[i]) == rank(m_cards[j])) { matching++; addFoundCard(i); addFoundCard(j); } } // The algorithm above gives the following results for each case below. switch (matching) { case 0: // High card card1 = findNextBest(ROOF, false); m_firstRank = rank(card1); m_secondRank = (CardRank) -1; // In this case, there are no marked cards. Since we need to mark // the best card, we have to search for it and then mark it. for (i = 0; i < PokerHandSize; i++) { if (m_cards[i] == card1) { addFoundCard(i); break; } } return HighCard; case 1: // Pair m_firstRank = rank(findNextBest(ROOF, true)); m_secondRank = (CardRank) -1; return Pair; case 2: // Two pairs card1 = findNextBest(ROOF, true); // Must do this twice, since the first card we get is the second // card of the first pair. card2 = findNextBest(card1, true); card2 = findNextBest(card2, true); m_firstRank = rank(card1); m_secondRank = rank(card2); return TwoPairs; case 3: // 3 of a kind m_firstRank = rank(findNextBest(ROOF, true)); return ThreeOfAKind; case 4: // Full house // This is the only tricky case since the value is determined more // by the 3 of a kind than by the pair. The rank of the 3 of a // kind can be lower then the pair, though. // Get the best and third best cards into val1 and val2. card1 = findNextBest(ROOF, true); card2 = findNextBest(card1, true); card2 = findNextBest(card2, true); // Now we have one of two different cases: // 1. rank(card1) == rank(card2): the 3 of a kind is biggest. // 2. rank(card1) > rank(card2): the pair is biggest. if (rank(card1) == rank(card2)) { m_firstRank = rank(card1); m_secondRank = rank(findNextBest(card2, true)); } else { m_firstRank = rank(card2); m_secondRank = rank(card1); } return FullHouse; case 6: // 4 of a kind m_firstRank = rank(findNextBest(ROOF, true)); return FourOfAKind; default: break; } // Shouldn't get here. assert(0); return (PokerHandType) -1; } // FIXME: When we fix the scores, in raise() and bet() to be higher // with better hands, then change the "rank(C_ACE) -" stuff // below. // int PokerHand::getCardScore() const { int score = 0; for (int i = 0; i < PokerHandSize; i++) { if (m_foundCards[i]) score += rank(C_ACE) - rank(m_cards[i]); } return score; } bool PokerHand::getFoundCard(int cardNum) const { return m_foundCards[cardNum]; } // ---------------------------------------------------------------- // PokerHand protected methods void PokerHand::cleanFoundCards() { for (int i = 0; i < PokerHandSize; i++) m_foundCards[i] = false; } void PokerHand::addFoundCard(int cardNum) { m_foundCards[cardNum] = true; }