From c1ac69a146eb2b09f70365080f56fe22203c7dbf Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Mon, 21 Nov 2022 22:04:05 -0800
Subject: [PATCH] Winning

---
 src/dawn/poker/Card.cpp         |  56 +++++++-
 src/dawn/poker/Card.hpp         |  58 +++++++-
 src/dawn/poker/PokerPlayer.cpp  | 230 +++++++++++++++-----------------
 src/dawn/poker/PokerWinning.cpp |  33 +++++
 src/dawn/poker/PokerWinning.hpp |  10 ++
 5 files changed, 259 insertions(+), 128 deletions(-)

diff --git a/src/dawn/poker/Card.cpp b/src/dawn/poker/Card.cpp
index 05e7de7e..7a2f3846 100644
--- a/src/dawn/poker/Card.cpp
+++ b/src/dawn/poker/Card.cpp
@@ -8,16 +8,64 @@
 using namespace Dawn;
 
 void Card::fillDeck(std::vector<struct Card> *deck) {
+  assertNotNull(deck);
+  
   for(uint8_t i = 0; i < CARD_DECK_SIZE; i++) {
     deck->push_back(Card(i));
   }
 }
 
+int32_t Card::contains(std::vector<struct Card> *deck, struct Card c) {
+  assertNotNull(deck);
+  assertTrue(deck->size() > 0);
 
-void Card::sort(std::vector<struct Card> *deck) {
-  std::sort(deck->begin(), deck->end(), &cardSorter);
+  auto it = deck->begin();
+  while(it != deck->end()) {
+    if(it->cardValue == c.cardValue) return (int32_t)(it - deck->begin());
+    ++it;
+  }
+  return -1;
 }
 
-bool_t cardSorter(struct Card left, struct Card right) {
+int32_t Card::containsNumber(
+  std::vector<struct Card> *deck,
+  enum CardValue number
+) {
+  assertNotNull(deck);
+  assertTrue(number < CARD_COUNT_PER_SUIT);
+
+  auto it = deck->begin();
+  while(it != deck->end()) {
+    if(it->getValue() == number) return (int32_t)(it - deck->begin());
+    ++it;
+  }
+  return -1;
+}
+
+std::vector<struct Card> Card::countPairs(
+  std::vector<struct Card> *deck,
+  enum CardValue val
+) {
+  std::vector<struct Card> pairs;
+
+  assertNotNull(deck);
+  assertTrue(deck->size() > 0);
+  
+  auto it = deck->begin();
+  while(it != deck->end()) {
+    if(it->getValue() == val) pairs.push_back(*it);
+    ++it;
+  }
+
+  return pairs;
+}
+
+bool_t Card::cardSorter(struct Card left, struct Card right) {
   return left.cardValue < right.cardValue;
-}
\ No newline at end of file
+}
+
+void Card::sort(std::vector<struct Card> *deck) {
+  assertNotNull(deck);
+  assertTrue(deck->size() > 1);
+  std::sort(deck->begin(), deck->end(), &Card::cardSorter);
+}
diff --git a/src/dawn/poker/Card.hpp b/src/dawn/poker/Card.hpp
index c3713486..a1209344 100644
--- a/src/dawn/poker/Card.hpp
+++ b/src/dawn/poker/Card.hpp
@@ -5,6 +5,7 @@
 
 #pragma once
 #include "dawnlibs.hpp"
+#include "assert/assert.hpp"
 
 namespace Dawn {
   enum CardSuit {
@@ -43,14 +44,56 @@ namespace Dawn {
     public:
       uint8_t cardValue;
 
-      static void fillDeck(std::vector<struct Card> *deck); 
+      /**
+       * Shuffles a hand / deck
+       * 
+       * @param deck Array of cards to shuffle.
+       */
       static void shuffle(std::vector<struct Card> *deck);
-      static bool_t contains(std::vector<struct Card> *deck, struct Card);
+
+      /**
+       * Fills a vector with all of the cards in a deck, in order.
+       * 
+       * @param deck Deck to fill.
+       */
+      static void fillDeck(std::vector<struct Card> *deck); 
+
+      /**
+       * Check if an array of cards contains a specific card.
+       * 
+       * @param deck Deck/Hand/Array of cards to check.
+       * @param card Card to look for
+       * @returns The index within the array that the card is. -1 if not found.
+       */
+      static int32_t contains(std::vector<struct Card> *deck, struct Card card);
+
+      /**
+       * Check if the array of cards contains a specific number.
+       * 
+       * @param deck Array of cards to check
+       * @param number The number to look for.
+       * @returns The index that the first card is. -1 if not found.
+       */
+      static int32_t containsNumber(
+        std::vector<struct Card> *deck,
+        enum CardValue number
+      );
+
+      /**
+       * Counts the amount of times a card's number appears within the given 
+       * hand.
+       * 
+       * @param deck The hand to check
+       * @param val Value of pairs to find.
+       * @return Card pairs in the deck.
+       */
       static std::vector<struct Card> countPairs(
         std::vector<struct Card> *deck,
-        enum CardValue value
+        enum CardValue val
       );
 
+      static bool_t cardSorter(struct Card left, struct Card right);
+
       /**
        * Sort a hand of cards. Cards are ordered in descending weight, aces are
        * high. Cards will be grouped by their suits, e.g. CARD_CLUBS_TWO will
@@ -71,14 +114,21 @@ namespace Dawn {
         assertTrue(cv < CARD_DECK_SIZE);
       }
 
+      /**
+       * Returns the number of a given card.
+       * @returns The card number.
+       */
       CardValue getValue() {
         return (CardValue)(cardValue % CARD_COUNT_PER_SUIT);
       }
 
+      /**
+       * Returns the suit of a given card.
+       * @returns The suit.
+       */
       CardSuit getSuit() {
         return (CardSuit)(cardValue / CARD_COUNT_PER_SUIT);
       }
   };
 
-  bool_t cardSorter(struct Card left, struct Card right);
 }
\ No newline at end of file
diff --git a/src/dawn/poker/PokerPlayer.cpp b/src/dawn/poker/PokerPlayer.cpp
index 1a524799..888e012d 100644
--- a/src/dawn/poker/PokerPlayer.cpp
+++ b/src/dawn/poker/PokerPlayer.cpp
@@ -239,206 +239,196 @@ int32_t PokerPlayer::getSumOfChips() {
 
 struct PokerWinning PokerPlayer::getWinning() {
   struct PokerWinning winning;
-  uint8_t i, j, l;
+  struct Card card(0x00);
+  uint8_t i, j;
+  int32_t index;
+  enum CardValue number, look;
+  enum CardSuit suit;
+  std::vector<struct Card> pairs;
 
   // Get the full poker hand (should be a 7 card hand, but MAY not be)
-  auto itHand = this->hand.begin();
-  while(itHand != this->hand.end()) {
-    winning.full.push_back(*itHand);
-    ++itHand;
+  for(i = 0; i < this->pokerGame->community.size(); i++) {
+    winning.full.push_back(this->pokerGame->community[i]);
   }
-
-  auto itCommunity = this->pokerGame->community.begin();
-  while(itCommunity != this->pokerGame->community.end()) {
-    winning.full.push_back(*itCommunity);
-    ++itCommunity;
+  for(i = 0; i < this->hand.size(); i++) {
+    winning.full.push_back(this->hand[i]);
   }
   Card::sort(&winning.full);
   
   //////////////////////// Now look for the winning set ////////////////////////
 
   // Royal / Straight Flush
-  auto it = winning.full.begin();
-  while(it != winning.full.end()) {
-    auto number = it->getValue();
-    auto suit = it->getSuit();
-    if(number < CARD_FIVE) {
-      ++it;
-      continue;
-    }
+  for(i = 0; i < winning.full.size(); i++) {
+    card = winning.full[i];
+    number = card.getValue();
+    if(number < CARD_FIVE) continue;
 
+    suit = card.getSuit();
+    
     winning.set.clear();
-    winning.set.push_back(*it);
+    winning.set.push_back(card);
 
     // Now look for the matching cards (Reverse order to order from A to 10)
     for(j = 1; j <= 4; j++) {
-      l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low.
-      auto c = Card(suit, (CardValue)l);
-      if(!Card::contains(&winning.full, c)) break;
-      winning.set.push_back(c);
+      // Ace low.
+      look = (
+        number == CARD_FIVE && j == 4 ? 
+        (enum CardValue)CARD_ACE :
+        (enum CardValue)(number - j)
+      );
+      index = Card::contains(&winning.full, Card(suit, look));
+      if(index == -1) break;
+      winning.set.push_back(winning.full[index]);
     }
 
     // Check if has all necessary cards.
-    if(winning.set.size() != 5) {
-      ++it;
-      continue;
-    }
+    if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
 
     // Add self to array
     winning.type = (
       number == CARD_ACE ? POKER_WINNING_TYPE_ROYAL_FLUSH : 
       POKER_WINNING_TYPE_STRAIGHT_FLUSH
     );
-    pokerWinnerFillRemaining(winning);
+    winning.fillRemaining();
     return winning;
   }
 
   // Four of a kind.
-  it = winning.full.begin();
-  while(it != winning.full.end()) {
-    auto pairs = Card::countPairs(&winning.full, it->getValue());
-    if(pairs.size() < CARD_SUIT_COUNT) {
-      ++it;
-      continue;
-    }
+  for(i = 0; i < winning.full.size(); i++) {
+    card = winning.full[i];
+    number = card.getValue();
+    pairs = Card::countPairs(&winning.full, number);
+    if(pairs.size() < CARD_SUIT_COUNT) continue;
 
     winning.set = pairs;
     winning.type = POKER_WINNING_TYPE_FOUR_OF_A_KIND;
-    pokerWinnerFillRemaining(winning);
+    winning.fillRemaining();
     return winning;
   }
 
   // Full House
   winning.set.clear();
-  it = winning.full.begin();
-  while(it != winning.full.end()) {
+  for(i = 0; i < winning.full.size(); i++) {
     // Check we haven't already added this card.
-    if(Card::contains(&winning.set, *it) != -1) {
-      ++it;
+    card = winning.full[i];
+    if(Card::contains(&winning.set, card) != -1) {
       continue;
     }
 
-    auto pairs = Card::countPairs(&winning.full, it->getValue());
+    number = card.getValue();
+    pairs = Card::countPairs(&winning.full, number);
 
     // Did we find either two pair or three pair?
-    if(pairs.size() != 2 && pairs.size() != 3) {
-      ++it;
-      continue;
-    }
-    if(winning.set.size() == 3) {
-      winning.set.pop_back();
+    if(pairs.size() != 2 && pairs.size() != 3) continue;
+    if(winning.set.size() == 3) {//Clamp to 5 max.
+      pairs.pop_back();
     }
 
     // Copy found pairs.
-    auto itPairs = pairs.begin();
-    while(itPairs != pairs.end()) {
-      winning.set.push_back(*itPairs);
-      ++itPairs;
+    for(j = 0; j < pairs.size(); j++) {
+      winning.set.push_back(pairs[j]);
     }
 
     // Winned?
-    if(winning.set.size() != 5) {
-      ++it;
-      continue;
-    }
+    if(winning.set.size() != POKER_WINNING_SET_SIZE) continue;
     winning.type = POKER_WINNING_TYPE_FULL_HOUSE;
-    pokerWinnerFillRemaining(winning);
+    winning.fillRemaining();
     return winning;
   }
 
   // Flush (5 same suit)
-  for(i = 0; i < winning->fullSize; i++) {
-    card = winning->full[i];
-    suit = cardGetSuit(card);
-    winning->setSize = 1;
-    for(j = i+1; j < winning->fullSize; j++) {
-      if(cardGetSuit(winning->full[j]) != suit) continue;
-      winning->set[winning->setSize++] = winning->full[j];
-      if(winning->setSize == POKER_WINNING_SET_SIZE) break;
+  for(i = 0; i < winning.full.size(); i++) {
+    card = winning.full[i];
+    suit = card.getSuit();
+    
+    winning.set.clear();
+    winning.set.push_back(card);
+
+    for(j = i+1; j < winning.full.size(); j++) {
+      if(winning.full[j].getSuit() != suit) continue;
+      winning.set.push_back(winning.full[j]);
+      if(winning.set.size() == POKER_WINNING_SET_SIZE) break;
     }
-    if(winning->setSize < POKER_WINNING_SET_SIZE) continue;
-    winning->set[0] = winning->full[i];
-    winning->type = POKER_WINNING_TYPE_FLUSH;
-    pokerWinnerFillRemaining(winning);
-    return;
+    if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
+    winning.type = POKER_WINNING_TYPE_FLUSH;
+    winning.fillRemaining();
+    return winning;
   }
 
   // Straight (sequence any suit)
-  winning->setSize = 0;
-  for(i = 0; i < winning->fullSize; i++) {
-    card = winning->full[i];
-    number = cardGetNumber(card);
+  for(i = 0; i < winning.full.size(); i++) {
+    card = winning.full[i];
+    number = card.getValue();
     if(number < CARD_FIVE) continue;
-    winning->setSize = 1;
+
+    winning.set.clear();
+    winning.set.push_back(card);
     
     for(j = 1; j <= 4; j++) {
-      l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low.
-      index = cardContainsNumber(winning->full, winning->fullSize, l);
+      // Ace low.
+      look = (
+        number == CARD_FIVE && j == 4 ?
+        (enum CardValue)CARD_ACE :
+        (enum CardValue)(number - j)
+      );
+      index = Card::containsNumber(&winning.full, look);
       if(index == -1) break;
-      winning->set[j] = winning->full[index];
-      winning->setSize++;
+      winning.set.push_back(winning.full[index]);
     }
 
     // Check if has all necessary cards.
-    if(winning->setSize < POKER_WINNING_SET_SIZE) continue;
-    winning->set[0] = winning->full[i];
-    winning->type = POKER_WINNING_TYPE_STRAIGHT;
-    pokerWinnerFillRemaining(winning);
-    return;
+    if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
+    winning.type = POKER_WINNING_TYPE_STRAIGHT;
+    winning.fillRemaining();
+    return winning;
   }
 
   // Three of a kind
-  winning->setSize = 0;
-  for(i = 0; i < winning->fullSize; i++) {
-    card = winning->full[i];
-    number = cardGetNumber(card);
-    pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs);
-    if(pairCount != 3) continue;
+  for(i = 0; i < winning.full.size(); i++) {
+    card = winning.full[i];
+    number = card.getValue();
+    pairs = Card::countPairs(&winning.full, number);
+    if(pairs.size() != 3) continue;
 
-    winning->setSize = pairCount;
-    for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]];
-    winning->type = POKER_WINNING_TYPE_THREE_OF_A_KIND;
-    pokerWinnerFillRemaining(winning);
-    return;
+    winning.set = pairs;
+    winning.type = POKER_WINNING_TYPE_THREE_OF_A_KIND;
+    winning.fillRemaining();
+    return winning;
   }
 
   // Two Pair
-  winning->setSize = 0;
-  for(i = 0; i < winning->fullSize; i++) {
-    card = winning->full[i];// Check we haven't already added this card.
+  winning.set.clear();
+  for(i = 0; i < winning.full.size(); i++) {
+    card = winning.full[i];// Check we haven't already added this card.
     if(
-      winning->setSize > 0 &&
-      cardContains(winning->set, winning->setSize, card) != -1
+      winning.set.size() > 0 &&
+      Card::contains(&winning.set, card) != -1
     ) {
       continue;
     }
 
-    number = cardGetNumber(card);
-    pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs);
-    if(pairCount != 2) continue;
-    
-    for(j = 0; j < pairCount; j++) {
-      winning->set[winning->setSize+j] = winning->full[pairs[j]];
+    number = card.getValue();
+    pairs = Card::countPairs(&winning.full, number);
+    if(pairs.size() != 2) continue;
+    for(j = 0; j < pairs.size(); j++) {
+      winning.set.push_back(pairs[j]);
     }
-    winning->setSize += pairCount;
-    if(winning->setSize != 4) continue;
-
-    winning->type = POKER_WINNING_TYPE_TWO_PAIR;
-    pokerWinnerFillRemaining(winning);
-    return;
+    if(winning.set.size() != 4) continue;
+    winning.type = POKER_WINNING_TYPE_TWO_PAIR;
+    winning.fillRemaining();
+    return winning;
   }
 
   // Pair
-  if(winning->setSize == 2) {
-    winning->type = POKER_WINNING_TYPE_PAIR;
-    pokerWinnerFillRemaining(winning);
-    return;
+  if(winning.set.size() == 2) {
+    winning.type = POKER_WINNING_TYPE_PAIR;
+    winning.fillRemaining();
+    return winning;
   }
 
   // High card
-  winning->setSize = 0;
-  pokerWinnerFillRemaining(winning);
-  winning->type = POKER_WINNING_TYPE_HIGH_CARD;
-
-  return;
+  winning.set.clear();
+  winning.fillRemaining();
+  winning.type = POKER_WINNING_TYPE_HIGH_CARD;
+  return winning;
 }
\ No newline at end of file
diff --git a/src/dawn/poker/PokerWinning.cpp b/src/dawn/poker/PokerWinning.cpp
index 35521a35..bf1c02dc 100644
--- a/src/dawn/poker/PokerWinning.cpp
+++ b/src/dawn/poker/PokerWinning.cpp
@@ -30,4 +30,37 @@ float_t PokerWinning::getWinningTypeConfidence(enum PokerWinningType type) {
     default:
       return POKER_WINNING_CONFIDENCE_HIGH_CARD;
   }
+}
+
+void PokerWinning::fillRemaining() {
+  uint8_t i, highest, current;
+  struct Card highestCard(0x00);
+  struct Card currentCard(0x00);
+
+  // Set the kicker
+  this->kicker = 0xFF;
+
+  // Fill the remaining cards
+  while(this->set.size() < POKER_WINNING_SET_SIZE) {
+    highest = 0xFF;
+
+    for(i = 0; i < this->full.size(); i++) {
+      currentCard = this->full[i];
+      if(Card::contains(&this->set, currentCard) != -1) continue;
+
+      if(highest == 0xFF) {
+        highestCard = currentCard;
+        highest = highestCard.getValue();
+      } else {
+        current = currentCard.getValue();
+        if(current != CARD_ACE && current < highest) continue;
+        highestCard = currentCard;
+        highest = current;
+      }
+    }
+
+    if(highest == 0xFF) break;
+    this->set.push_back(highestCard);
+  }
+  Card::sort(&this->set);
 }
\ No newline at end of file
diff --git a/src/dawn/poker/PokerWinning.hpp b/src/dawn/poker/PokerWinning.hpp
index cbca6bf3..021980c3 100644
--- a/src/dawn/poker/PokerWinning.hpp
+++ b/src/dawn/poker/PokerWinning.hpp
@@ -18,6 +18,9 @@
 #define POKER_WINNING_CONFIDENCE_PAIR 0.2f
 #define POKER_WINNING_CONFIDENCE_HIGH_CARD 0.1f
 
+/** How many cards in the winning set */
+#define POKER_WINNING_SET_SIZE 5
+
 namespace Dawn {
   enum PokerWinningType {
     POKER_WINNING_TYPE_NULL,
@@ -38,7 +41,14 @@ namespace Dawn {
       enum PokerWinningType type;
       std::vector<struct Card> full;
       std::vector<struct Card> set;
+      struct Card kicker;
+      
+      PokerWinning() : kicker(0x00) {
+
+      }
       
       static float_t getWinningTypeConfidence(enum PokerWinningType type);
+
+      void fillRemaining();
   };
 }
\ No newline at end of file