// cs151 homework 11 solutions

public class Card
{
  public int suit, rank;

  public Card () { 
    this.suit = 0;  this.rank = 0;
  }

  public Card (int suit, int rank) { 
    this.suit = suit;  this.rank = rank;
  }

  public static void printCard (Card c) {
    String[] suits = { "Clubs", "Diamonds", "Hearts", "Spades" };
    String[] ranks = { "narf", "Ace", "2", "3", "4", "5", "6",
		 "7", "8", "9", "10", "Jack", "Queen", "King" };

    System.out.println (ranks[c.rank] + " of " + suits[c.suit]);
  }

  public static int compareCards (Card c1, Card c2) {
    if (c1.suit > c2.suit) return 1;
    if (c1.suit < c2.suit) return -1;
    if (c1.rank > c2.rank) return 1;
    if (c1.rank < c2.rank) return -1;    
    return 0;
  }
	
  public static boolean sameCard (Card c1, Card c2) {
    return (c1.suit == c2.suit && c1.rank == c2.rank);
  }

  public static int findCard (Card[] deck, Card card) {
    for (int i = 0; i< deck.length; i++) {
      if (deck[i].equals (card)) return i;
    }
    return -1;
  }

  public static int findBisect (Card[] deck, Card card, int low, int high) {
    System.out.println (low + ", " + high);

    if (high < low) return -1;

    int mid = (high + low) / 2;
    int comp = compareCards (deck[mid], card);

    if (comp == 0) {
      return mid;
    } else if (comp > 0) {
      return findBisect (deck, card, low, mid-1);
    } else {
      return findBisect (deck, card, mid+1, high);
    }
  }

  public static Card[] buildDeck () {
    Card[] deck = new Card [52];

    int index = 0;
    for (int suit = 0; suit <= 3; suit++) {
      for (int rank = 1; rank <= 13; rank++) {
	deck[index] = new Card (suit, rank);
	index++;
      }
    }
    return deck;
  }

  public static void printDeck (Card[] deck) {
    for (int i=0; i<deck.length; i++) {
      printCard (deck[i]);
    }
  }

  public static void shuffleDeck (Card[] deck) {
    for (int i=0; i<deck.length; i++) {
      int j = randInt (i, deck.length-1);
      swapCards (deck, i, j);
    }
  }

  public static int randInt (int low, int high) {
    while (true) {
      int x = (int)(Math.random() * (high-low+1) + low);
      if (x >= low && x <= high) return x;
    } 
  }

  public static void sortDeck (Card[] deck) {
    for (int i=0; i<deck.length; i++) {
      int j = findLowestCard (deck, i, deck.length-1);
      swapCards (deck, i, j);
    }
  }

  public static void swapCards (Card[] deck, int i, int j) {
    Card temp = deck[i];
    deck[i] = deck[j];
    deck[j] = temp;
  }

  public static int findLowestCard (Card[] deck, int low, int high) {
    int winner = low;
    for (int i=low+1; i<=high; i++) {
      if (compareCards (deck[i], deck[winner]) < 0) {
	winner = i;
      }
    }
    return winner;
  }

  public static Card[] subdeck (Card[] deck, int low, int high) {
    Card[] sub = new Card[high-low+1];
	
    for (int i = 0; i<sub.length; i++) {
      sub[i] = deck[low+i];
    }
    return sub;
  }

    // the specification of drawHand is just a special case
    // of subdeck, which is in the book
  public static Card[] drawHand (Card[] deck) {
    return subdeck (deck, 0, 4);
  }

    // makeSuitHist and makeRankHist are likely to be useful
    // for other methods, so I broke them out into separate methods
  public static int[] makeSuitHist (Card[] hand) {
    int[] suitHist = new int[4];
    for (int i=0; i<hand.length; i++) {
      suitHist[hand[i].suit]++;
    }
    return suitHist;
  }

  public static int[] makeRankHist (Card[] hand) {
    int[] rankHist = new int[14];
    for (int i=0; i<hand.length; i++) {
      rankHist[hand[i].rank]++;
    }
    return rankHist;
  }

    // isFlush: traverse the suitHist looking for 5 alike
  public static boolean isFlush (Card[] hand) {
    int[] suitHist = makeSuitHist (hand);
    for (int i=0; i<4; i++) {
      if (suitHist[i] >= 5) return true;
    }
    return false;
  }

    // isThreeKind: traverse the rankHist looking for 3 alike
  public static boolean isThreeKind (Card[] hand) {
    int[] rankHist = makeRankHist (hand);
    for (int i=0; i<14; i++) {
      if (rankHist[i] >= 3) return true;
    }
    return false;
  }

  public static void main (String[] args) {
    int hands = 10000;
    int flushes = 0;
    int threeKinds = 0;

    Card[] deck = buildDeck ();

    for (int i=0; i<hands; i++) {
      shuffleDeck (deck);
      Card[] hand = drawHand (deck);
      if (isFlush (hand)) {
	flushes++;
      }
      if (isThreeKind (hand)) {
        threeKinds++;
      }
    }
    System.out.println (flushes + " flushes out of " + hands + " hands.");
    System.out.println (threeKinds + " three of a kind out of " +
			hands + " hands.");
  }
}

