// Programming Assignment 1 - Example Solution.
// CS 176, Summer 2001

#include <ctype.h>


static const int number_of_ranks = 13;


static int kinds(char suits[], int hand_size) {

  // Return the number of points due to n-of-a-kind cards in array suits
  // containing hand_size suits.

  // Assume all suits are the same, in which case the number of points is equal
  // to the number of cards in the hand.

     int points = hand_size;

  // If there's any mismatch in suits, the number of points is zero.

     for (int i = 1; i < hand_size; i++)
       if (tolower(suits[i]) != tolower(suits[0])) {
	 points = 0;
	 break;
         }

  return points;
  }


static int pairs(int ranks[], int hand_size) {
  
  // Return the number of points due to pairs in array ranks containing
  // hand_size ranks.

  int pair_cnt = 0;

  // Count the number of pairs in the hand. For each card ranks[i], find the
  // number of pairs that it can form with the remaining cards, then throw
  // ranks[i] away and repeat the process with one of the remaining cards.
  // Stop when there's no cards left.

     for (int i = 0; i < hand_size; i++)
       for (int j = i + 1; j < hand_size; j++)
	 if (ranks[i] == ranks[j])
	   pair_cnt++;

  // The number of points is the number of pairs times two.

     return pair_cnt*2;
  }


static int runs(int ranks[], int hand_size) {

  // Return the number of points due to runs in array ranks containing
  // hand_size ranks.

  // The easiest way to compute runs is to sort the hand by rank and count the
  // number of times each rank is repeated.  Becuase there's a fixed number of
  // ranks (thirteen - ace through king), we can use an array to both sort and
  // count at the same time. The index i - 1 represents rank i, and the value
  // rank_counts[i - 1] represents the number of times rank i appears in the
  // hand.  For example, rank_count[10]= 3 means that jacks appear three times
  // in the hand.

     int rank_counts[number_of_ranks] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
     int i;

     for (i = 0; i < hand_size; i++)
       rank_counts[ranks[i] - 1]++;

  // A sequence of consecutive non-zero values in the rank_counts array
  // represents a run.  run_start is the index of the start of a run, and
  // run_end is the index of the end of the run plus one.

     int run_points = 0;
     int run_start = 0;

     while (run_start < number_of_ranks) {

       int run_end = run_start + 1;

       if (rank_counts[run_start] > 0) {
	 while ((run_end < number_of_ranks) && (rank_counts[run_end] > 0))
	   run_end++;

	 // A run has to contain at least three sequential ranks, and the value
	 // of the run is the number of ranks in the sequence.

	    int points = run_end - run_start;
	    if (points > 2) {

	      // The value of a run is multiplied by the number of times each
	      // rank appears in the run.

		 for (i = run_start; i < run_end; i++)
		   points *= rank_counts[i];
		 run_points += points;
	      }
	 }

       run_start = run_end;
       }

  return run_points;
  }


static bool is_suit(char suit) {

  // Return true if suit is legal suit, false otherwise; case is ignored.

  suit = tolower(suit);
  return ((suit == 'c') || (suit == 'd') || (suit == 'h') || (suit == 's'));
  }


int count_hand(int ranks[], char suits[], int hand_size) {

  // Return the number of points in a hand of hand_size cards.  Card i has rank
  // ranks[i] and suit suits[i].  Return -1 if the hand's broken.

  // Do some sanity checks on the hand: hand size, legal card ranks and suits.

     if ((hand_size < 0) || (hand_size > 52))
       return -1;

     for (int i = 0; i < hand_size; i++) {
       if ((ranks[i] < 1) || (number_of_ranks < ranks[i]))
	 return -1;
       if (!is_suit(suits[i]))
	 return -1;
       }
  
  // Hand points come from runs, pairs, and repeated suits.

     return runs(ranks, hand_size) +
	    pairs(ranks, hand_size) +
	    kinds(suits, hand_size);
  }


// If you've understood the previous code, then you'll understand why you can
// use the rank_counts array computed in runs() to also count the number of
// pairs.  This leads to a simpler implementation of count_hand() than is shown
// above.


static int new_pairs(int rank_counts[]) {
  
  // Return the number of points due to pairs in array ranks containing
  // hand_size ranks.

  // Set pair_points[i] to be the number of points i cards of the same rank
  // create due to pairs.

     int pair_points[] = { 0, 0, 2, 6, 12 };

  // This code really should check to make sure rank_counts[i] is a number
  // between 0 and 4 inclusive.

     int points = 0;

     for (int i = 0; i < number_of_ranks; i++)
       points += pair_points[rank_counts[i]];

  return points;
  }


static int new_runs(int rank_counts[]) {

  // Return the number of points due to runs in array ranks containing
  // hand_size ranks.

  int run_points = 0;
  int run_start = 0;

  // The same code as before, excpet the rank_counts computation has been moved
  // to count_hands() and rank_counts is passed in as an argument.

  while (run_start < number_of_ranks) {

    int run_end = run_start + 1;

    if (rank_counts[run_start] > 0) {
      while ((run_end < number_of_ranks) && (rank_counts[run_end] > 0))
	run_end++;

      int points = run_end - run_start;
      if (points > 2) {
	for (int i = run_start; i < run_end; i++)
	  points *= rank_counts[i];
	run_points += points;
	}
      }

    run_start = run_end;
    }

  return run_points;
  }


int new_count_hand(int ranks[], char suits[], int hand_size) {

  // Return the number of points in a hand of hand_size cards.  Card i has rank
  // ranks[i] and suit suits[i].  Return -1 if the hand's broken.

  // Skip the sanity checks.

  // Set rank_counts[i] to be the number of cards in the hand that have 
  // rank i + 1.

     int rank_counts[number_of_ranks] = { 0 };
     for (int i = 0; i < hand_size; i++)
       rank_counts[ranks[i] - 1]++;
  
  return new_runs(rank_counts) +
         new_pairs(rank_counts) +
         kinds(suits, hand_size);
  }



syntax highlighted by Code2HTML, v. 0.9