Search code examples
cc99poker

Classifying Poker Hands [ROYAL FLUSH IS FAILING]


I was following programming projects from the book C programming: A Modern Approach and I have incorporated 2 questions into my program but failed to achieve the 3rd that is to finding a royal flush hand.

This is what I have done so far:

/* Classifies a poker hand */

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#define NUM_RANKS 13
#define NUM_SUITS 4
#define NUM_CARDS 5
#define ROYAL_FLUSH ('A' || 'a' || 'K' || 'k' || 'Q' || 'q' || 'J' || 'j' || 'T' || 't') 
#define ACE_LOW (('A' || 'a') && '2' && '3' && '4' && '5')

/* external variables */
/* int ROYAL_FLUSH[8, 9, 10, 11, 12]
int num_in_rank[NUM_RANKS];
int num_in_suit[NUM_SUITS]; */
bool royal_flush, ace_low, straight, flush, four, three;
int pairs;  /* can be 0, 1 or 2 */

/* Prototypes */
int read_cards(int num_in_rank[NUM_RANKS], int num_in_suit[NUM_SUITS]);
int analyze_hand(int num_in_rank[NUM_RANKS], int num_in_suit[NUM_SUITS]);
void print_result(void);


int main(void)
{

  int num_in_rank[NUM_RANKS];
  int num_in_suit[NUM_SUITS];
  for(;;) {
    read_cards(num_in_rank, num_in_suit);
    analyze_hand(num_in_rank, num_in_suit);
    print_result();
  }
}

int read_cards(int num_in_rank[NUM_RANKS], int num_in_suit[NUM_SUITS])
{

  bool card_exists[NUM_RANKS][NUM_SUITS];
  char ch, rank_ch, suit_ch;
  int rank, suit;
  bool bad_card;
  int cards_read = 0;

  for (rank = 0; rank < NUM_RANKS; rank++) {
    num_in_rank[rank] = 0;
    for (suit = 0; suit < NUM_SUITS; suit++)
      card_exists[rank][suit] = false;
  }

  for (suit = 0; suit < NUM_SUITS; suit++)
    num_in_suit[suit] = 0;

  while (cards_read < NUM_CARDS) {
    bad_card = false;

    printf("Enter a card:  ");

    rank_ch = getchar();
    switch (rank_ch) {
    case '0': exit(EXIT_SUCCESS);
    case '2': rank = 0; break;
    case '3': rank = 1; break;
    case '4': rank = 2; break;
    case '5': rank = 3; break;
    case '6': rank = 4; break;
    case '7': rank = 5; break;
    case '8': rank = 6; break;
    case '9': rank = 7; break;
    case 't': case 'T': rank = 8; break;
    case 'j': case 'J': rank = 9; break;
    case 'q': case 'Q': rank = 10; break;
    case 'k': case 'K': rank = 11; break;
    case 'a': case 'A': rank = 12; break;
    default: bad_card = true;
    }

    suit_ch = getchar();
    switch (suit_ch) {
    case 'c': case 'C': suit = 0; break;
    case 'd': case 'D': suit = 1; break;
    case 'h': case 'H': suit = 2; break;
    case 's': case 'S': suit = 3; break;
    default: bad_card = true;
    }

    while ((ch = getchar()) != '\n')
      if (ch != ' ') bad_card = true;

    if (bad_card)
      printf("Bad card; ignored.\n");
    else if (card_exists[rank][suit])
      printf("Duplicate card; ignored.\n");
    else {
      num_in_rank[rank]++;
      num_in_suit[suit]++;
      card_exists[rank][suit] = true;
      cards_read++;
    }
  }
  return 0;
}

int analyze_hand(int num_in_rank[NUM_RANKS], int num_in_suit[NUM_SUITS])
{

  int num_consec = 0;
  int rank, suit;

  royal_flush = false;
  straight = false;
  ace_low = false;
  flush = false; 
  four = false;
  three = false;
  pairs = 0;


  /*check for royal flush */
  /* int arr[5] = {8, 9, 10, 11, 12};
     int i; */
  if (flush)
    for (rank = 0; rank < NUM_RANKS; rank++)
      if (num_in_rank[rank] == ROYAL_FLUSH)
          continue;
      else
          break;

    royal_flush = true;


  /* check for flush */

  for (suit = 0; suit < NUM_SUITS; suit++)
    if (num_in_suit[suit] == NUM_CARDS)
      flush = true;

 /* check for ace-low straight */

  for (rank = 0; rank < NUM_RANKS; rank++)
    if (num_in_rank[rank] == ACE_LOW)
      ace_low = true;

  /* check for straight */

  rank = 0;
  while (num_in_rank[rank] == 0) rank++;
  for (; rank < NUM_RANKS && num_in_rank[rank] > 0; rank++)
    num_consec++;
  if (num_consec == NUM_CARDS) {
    straight = true;
    return 0;
  }

  /* check for 4 of a kind, 3 of a kind and pairs */

  for (rank = 0; rank < NUM_RANKS; rank++) {
    if (num_in_rank[rank] == 4) 
      four = true;
    if (num_in_rank[rank] == 3) 
      three = true;
    if (num_in_rank[rank] == 2)
      pairs++;
  }
  return 0;
}

void print_result(void)
{

  if (royal_flush && flush && straight)
    printf("\nRoyal Flush");
  else if (straight && flush && royal_flush == false) 
    printf("\nStraight Flush");
  else if (four) 
    printf("\nFour of a kind");
  else if (three && pairs == 1) 
    printf("\nFull House");
  else if (flush) 
    printf("\nFlush");
  else if (straight) 
    printf("\nStraight");
  else if (ace_low)
    printf("\nAce-low straight");
  else if (three) 
    printf("\nThree of a kind");
  else if (pairs == 2) 
    printf("\nTwo Pairs");
  else if (pairs == 1) 
    printf("\nPair");
  else 
    printf("\nHigh card");

printf("\n\n");

}

My code prints out Royal Flush when the inputted cards are not even royal flush cards but are actually flush cards.

I am new to c so please go easy on the criticism.

Language: c99 ; Compiler: gcc


Solution

  • This:

    if (num_in_rank[rank] == ROYAL_FLUSH)
    

    which is the same as this:

    if (num_in_rank[rank] == ('A' || 'a' || 'K' || 'k' || 'Q' || 'q' || 'J' || 'j' || 'T' || 't'))
    

    Does not do what you think it does. It does not compare num_in_rank[rank] against each of A, a, etc. What is does is perform a logical OR first between A and a, then a logical OR of that result with K, etc.

    You need to compare your variable against each of those individually.

    You could do this:

    if ((num_in_rank[rank] == 'A') || (num_in_rank[rank] == 'a') || ...
    

    Or this:

    char in_royal[] = { 'A', 'a', ... };
    
    for (i=0;i<sizeof(in_royal);i++) {
        if (num_in_rank[rank] == in_royal[i]) {
            ...
    

    There's also an issue with how this is laid out:

      if (flush)
        for (rank = 0; rank < NUM_RANKS; rank++)
          if (num_in_rank[rank] == ROYAL_FLUSH)
              continue;
          else
              break;
    
        royal_flush = true;
    

    The last line is outside of the if block, and you won't capture the case you want. Make sure to use braces:

      if (flush) {
        royal_flush = true;
        for (rank = 0; rank < NUM_RANKS; rank++) {
          if ((num_in_rank[rank] == 'A') || (num_in_rank[rank] == 'a') || ...) {
              continue;
          } else {
              royal_flush = false;
              break;
          }
        }
      }
    

    As an improvement, rather than checking for all cards in a royal flush individually, just check for an ace. That along with the separate checks for straight and flush (which aren't currently in your code) will capture that. Then you can do this:

    if (straight && flush && has_ace)
      printf("\nRoyal Flush");