import java.util.Arrays; import java.util.HashSet; import java.util.Random; /** Represents a deck of cards, and operations that can be performed * on it. * * @author Arran Stewart * */ public class Deck { /** The size of a deck of cards. Four suits of thirteen cards, * plus two jokers. */ public final static int DEFAULT_DECK_SIZE = 13 * 4 + 2; /** The value given to the first joker **/ public final static int JOKER1 = 13 * 4 + 1; /** The value given to the second joker **/ public final static int JOKER2 = 13 * 4 + 2; // Array holding ints representing the cards. // Card values should start from 1, and each int // should be unique within the array. private int cards[]; @Override public String toString() { return "Deck [cards=" + Arrays.toString(cards) + "]"; } /** Create a deck of cards in the default order. */ public Deck() { cards = new int[DEFAULT_DECK_SIZE]; for (int i = 0; i < DEFAULT_DECK_SIZE; i++) { cards[i] = i + 1; } validateCards(); } /** Returns true when all values of the array arr are * different to each other; returns false otherwise. * * @param arr An array of int values to be checked * @return Whether all values in the array are different */ public static boolean allDifferent(int arr[]) { // TODO: fill in method body HashSet hs = new HashSet<>(); for (int n : arr) { hs.add(n); } return hs.size() == arr.length; } /** Construct a deck of cards from a String of comma-separated values. * * @param s A string, consisting of comma-separated integers. */ public Deck(String inputString) { if (inputString.equals("")) { cards = new int[0]; return; } String[] strings = inputString.split(","); cards = new int[strings.length]; for (int i = 0; i < strings.length; i++) { cards[i] = Integer.parseInt( strings[i] ); } validateCards(); } /** This method should check whether all the * values in the "cards" instance variable * are different. * If they are not, it should throw an * IllegalArgumentException exception. * */ private void validateCards() { if (!allDifferent(cards)) { throw new IllegalArgumentException("values in cards are not all different"); } } public int size() { return cards.length; } public int getCard(int idx) { return cards[idx]; } /** Shuffles elements of an array into a random permutation. * * @param arr Array to be shuffled. */ public static void shuffleArray( int arr[] ) { // use the "Fisher-Yates/Knuth shuffle" -- // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle Random randGen = new Random(); int n = arr.length; while (n > 1) { int k = randGen.nextInt(n--); //decrements after using the value int temp = arr[n]; arr[n] = arr[k]; arr[k] = temp; } } public void shuffle() { shuffleArray(cards); } /** Find the position in the deck of the card * with value cardVal. * * @param cardVal The card to find * @return The position of the card to find, or -1 * if it wasn't in the deck. */ public int findCard(int cardVal) { for (int i = 0; i < cards.length; i++) { if (cards[i] == cardVal) { return i; } } return -1; } /** Shift a particular card down the deck by one place, * and if this would take you past the end of the deck, * treat the end of the deck as joining onto the beginning. * * In other words: for any card except the last card, * the card is swapped with the card immediately * after it. For the last card: it gets inserted after the * first card, and the second card and all subsequent cards * are "moved down one". * * If the argument passed is not found in the deck, * this method should throw an IllegalArgumentException * exception. * * @param cardVal The value of the card to be shifted. */ public void shiftDownOne(int cardVal) { int idx = findCard(cardVal); if (idx == -1) { throw new IllegalArgumentException("cardVal isn't a valid card value"); } // if the card is at any position but the last, // swap it with the card after it. if (idx < size() - 1) { // swap with successor. int tmp = cards[idx]; cards[idx] = cards[idx+1]; cards[idx+1] = tmp; } else { // else, if card is in last position: // shift it to the second position (i.e., index 1). int newCards[] = new int[size()]; newCards[0] = cards[0]; newCards[1] = cards[size()-1]; for (int i = 2; i < size(); i++) { newCards[i] = cards[i-1]; } cards = newCards; } } /** Perform a "triple cut": Given the positions of 2 cards in the deck, * divide the deck into three "chunks": * chunk "A", the "top" - cards before either position * chunk "B", the "middle" - cards lying between the 2 positions * chunk "C", the "bottom" - cards after either position. * * Reorder the deck by swapping the top and bottom chunks * (chunks "A" and "C"). * * @param pos1 Position of a "fixed" card, counting from 0. * @param pos2 Position of another "fixed" card, counting from 0. */ public void tripleCut(int pos1, int pos2) { int newCards[] = new int[size()]; int low = Math.min(pos1, pos2); int high = Math.max(pos1, pos2); int newIdx = 0; // copy in "bottom" -> "top"; for(int oldIdx = high + 1 ; oldIdx < size(); oldIdx++, newIdx++) { newCards[newIdx] = cards[oldIdx]; } // copy "middle" to "middle" for(int oldIdx = low; oldIdx <= high; oldIdx++, newIdx++) { newCards[newIdx] = cards[oldIdx]; } // copy "top" to "bottom" for(int oldIdx = 0; oldIdx < low; oldIdx++, newIdx++) { newCards[newIdx] = cards[oldIdx]; } cards = newCards; } /** Perform a "count cut". Let n be a number of cards. * Remove n cards from the top of the deck, and place them * just above the last card. * * @param numCards */ public void countCut(int numCards) { int newCards[] = new int[size()]; int newIdx = 0; // copy the "middle", cards n+1 through to second-last for(int oldIdx = numCards; oldIdx < size() - 1; oldIdx++, newIdx++){ newCards[newIdx] = cards[oldIdx]; } // copy the "top", the first through n cards. for(int oldIdx = 0; oldIdx < numCards; oldIdx++, newIdx++) { newCards[newIdx] = cards[oldIdx]; } // copy the last card over newCards[size() - 1] = cards[size() - 1]; cards = newCards; } }