summaryrefslogtreecommitdiffstats
path: root/kpoker/poker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kpoker/poker.cpp')
-rw-r--r--kpoker/poker.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/kpoker/poker.cpp b/kpoker/poker.cpp
new file mode 100644
index 00000000..6143d342
--- /dev/null
+++ b/kpoker/poker.cpp
@@ -0,0 +1,531 @@
+/*
+ * 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 <stdio.h>
+#include <assert.h>
+
+#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
+
+
+QString 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;
+}