/**

 * ACSL RUMMY

 * AMERICAN COMPUTER SCIENCE LEAGUE - CONTEST 4

 *

 * ROBERT BADEA

 * APRIL 15, 2013

 *

 */

 

import java.util.*;

 

public class RummyPlayer {

 

    LinkedList<LinkedList<Card>> runsCopy;

    LinkedList<TreeSet<Card>> setsCopy;

 

    LinkedList<LinkedList<Card>> runs;

    LinkedList<TreeSet<Card>> sets;

 

    // create a new rummy player

    @SuppressWarnings("unchecked")

    public RummyPlayer(Card[] cards) {

        runs = new LinkedList<LinkedList<Card>>();

        sets = new LinkedList<TreeSet<Card>>();

 

        for (Card card : cards) {

        //for (int i = 0; i < cards.length; ++i) {

            boolean addedInRuns = false;

            for (LinkedList<Card> run : runs) {

                if (card.linksForRunBefore(run.getFirst())){

                    run.addFirst(card);

                    addedInRuns = true;

                }

                else if (card.linksForRunAfter(run.getLast())) {

                    run.addLast(card);

                    addedInRuns = true;

                }

            }

            if (!addedInRuns) {

                LinkedList<Card> newRun = new LinkedList<Card>();

                newRun.addLast(card);

                runs.add(newRun);

            }

 

            boolean addedInSets = false;

            for (TreeSet<Card> set : sets) {

                if (card.linksForSet(set.first())) {

                    set.add(card);

                    addedInSets = true;

                }

            }

            if (!addedInSets) {

                TreeSet<Card> newSet = new TreeSet<Card>();

                newSet.add(card);

                sets.add(newSet);

            }

        }

 

        runsCopy = new LinkedList<LinkedList<Card>>();

        setsCopy = new LinkedList<TreeSet<Card>>();

 

        for (LinkedList<Card> run : runs) {

            runsCopy.addLast((LinkedList)run.clone());

        }

 

        for (TreeSet<Card> set : sets) {

            setsCopy.addLast((TreeSet)set.clone());

        }

 

    }

 

    @SuppressWarnings("unchecked")

    public void reset() {

        runs.clear();

        sets.clear();

 

        for (LinkedList<Card> run : runsCopy) {

            Object clone = run.clone();

            if (clone instanceof LinkedList)

                runs.addLast((LinkedList)clone);

        }

 

        for (TreeSet<Card> set : setsCopy) {

            sets.addLast((TreeSet)set.clone());

        }

    }

 

    // class for a playing card

    public static class Card implements Comparable<Card> {

        private int rank;

        private int suit;

 

        public Card(String c) {

            c = c.replaceAll("(,)?", "");

 

            if (c == null)       throw new NullPointerException();

            if (c.length() != 2) throw new IllegalArgumentException();

 

            char rank = c.charAt(0);

            char suit = c.charAt(1);

 

            // Yes, switching this is ugly, but it is also straightforward

            switch (rank) {

                case 'A':   this.rank = 1;

                    break;

                case '2':   this.rank = 2;

                    break;

                case '3':   this.rank = 3;

                    break;

                case '4':   this.rank = 4;

                    break;

                case '5':   this.rank = 5;

                    break;

                case '6':   this.rank = 6;

                    break;

                case '7':   this.rank = 7;

                    break;

                case '8':   this.rank = 8;

                    break;

                case '9':   this.rank = 9;

                    break;

                case 'T':   this.rank = 10;

                    break;

                case 'J':   this.rank = 12;

                    break;

                case 'Q':   this.rank = 13;

                    break;

                case 'K':   this.rank = 14;

                    break;

                default:    throw new IllegalArgumentException();

            }

 

            switch (suit) {

                case 'S':   this.suit = 4;

                    break;

                case 'H':   this.suit = 3;

                    break;

                case 'C':   this.suit = 2;

                    break;

                case 'D':   this.suit = 1;

                    break;

                default:    throw  new IllegalArgumentException();

            }

        }

        public String toString() {

 

            String result = "";

 

            // Yes, switching this is ugly too, I know

            switch (rank) {

                case 1:   result = result + "A";

                    break;

                case 2:   result = result + "2";

                    break;

                case 3:   result = result + "3";

                    break;

                case 4:   result = result + "4";

                    break;

                case 5:   result = result + "5";

                    break;

                case 6:   result = result + "6";

                    break;

                case 7:   result = result + "7";

                    break;

                case 8:   result = result + "8";

                    break;

                case 9:   result = result + "9";

                    break;

                case 10:   result = result + "T";

                    break;

                case 12:   result = result + "J";

                    break;

                case 13:   result = result + "Q";

                    break;

                case 14:   result = result + "K";

                    break;

                default:    throw new IllegalStateException();

            }

 

            switch (suit) {

                case 4:   result = result + "S";

                    break;

                case 3:   result = result + "H";

                    break;

                case 2:   result = result + "C";

                    break;

                case 1:   result = result + "D";

                    break;

                default:    throw  new IllegalStateException();

            }

 

            return result;

        }

 

        // test if that card could be before or after this card in a set/run

        public boolean linksForRunBefore(Card that) {

            return (this.rank - that.rank == -1) && (this.suit == that.suit);

        }

        public boolean linksForRunAfter(Card that) {

            return (this.rank - that.rank == +1) && (this.suit == that.suit);

        }

        public boolean linksForSet(Card that) {

            return (this.rank == that.rank) && (this.suit != that.suit);

        }

 

        public int compareTo(Card that) {

 

            if (that == null) return -1;

 

            int rankDiff = this.rank - that.rank;

            if (rankDiff != 0) return rankDiff;

 

            return (this.suit - that.suit);

        }

    }

 

    // deletes the most useless card in the hand

    private void deleteMostUseless() {

 

        Card min = null;

        LinkedList<Card> runFromWhichMinIs = null;

 

        for (LinkedList<Card> run : runs) {

            if (run.size() < 3) {

                Card first = run.getFirst();

                if (first.compareTo(min) < 0) {

                    min = first;

                    runFromWhichMinIs = run;

                }

            }

        }

 

        if (runFromWhichMinIs == null)

            throw  new IllegalStateException();

 

        runFromWhichMinIs.remove(min);

 

        if (runFromWhichMinIs.isEmpty())

            runs.remove(runFromWhichMinIs);

 

        for (TreeSet<Card> set : sets) {

            if (set.remove(min))

                if (set.isEmpty()) {

                    sets.remove(set);

                    break;

                }

        }

    }

 

    public void addCard(Card card) {

 

        for (LinkedList<Card> run : runs) {

            int runSize = run.size();

            if (runSize > 1 && runSize < 4) {

                if (card.linksForRunBefore(run.getFirst())){

                    run.addFirst(card);

                    deleteMostUseless();

                    break;

                }

                else if (card.linksForRunAfter(run.getLast())) {

                    run.addLast(card);

                    deleteMostUseless();

                    break;

                }

            }

        }

 

        for (TreeSet<Card> set : sets) {

            int setSize = set.size();

            if (setSize > 1 && setSize < 4) {

                if (card.linksForSet(set.first())) {

                    set.add(card);

                    deleteMostUseless();

                    break;

                }

            }

        }

    }

 

    public String toString() {

 

        boolean[][] used = new boolean[4][14];

 

        StringBuilder sb = new StringBuilder();

        int appended = 0;

 

        for (int i = 0; i < 4; ++i)

            for (int j = 0; j < 14; ++j)

                used[i][j] = false;

 

        for (LinkedList<Card> run : runs) {

            if (run.size() > 2)

            for (Card card : run) {

                if (!used[card.suit - 1][card.rank]) {

                    sb.append(card.toString());

                    appended++;

                    if (appended < 7)

                        sb.append(" ");

                    used[card.suit - 1][card.rank] = true;

                }

            }

        }

 

        for (TreeSet<Card> set : sets) {

            if (set.size() > 2)

            for (Iterator<Card> it = set.descendingIterator(); it.hasNext();) {

                Card card = it.next();

                if (!used[card.suit - 1][card.rank]) {

                    sb.append(card.toString());

                    appended++;

                    if (appended < 7)

                        sb.append(" ");

                    used[card.suit - 1][card.rank] = true;

                }

            }

        }

 

        TreeSet<Card> rest = new TreeSet<Card>();

 

        for (LinkedList<Card> run : runs) {

            for (Card card : run) {

                if (!used[card.suit - 1][card.rank]) {

                    rest.add(card);

                }

            }

        }

 

        for (Iterator<Card> it = rest.descendingIterator(); it.hasNext();) {

            Card card = it.next();

            sb.append(card.toString());

            appended++;

            if (appended < 7)

                sb.append(" ");

        }

 

        return sb.toString();

    }

 

    public static void main(String[] args) {

 

        Card[] cards = new Card[7];

 

        Scanner scanner = new Scanner(System.in);

        System.out.println("Please enter the 7 two-character strings representing\nthe cards in a player's hand, separated by single spaces:");

 

        for (int i = 0; i < 7; ++i) {

            cards[i] = new Card(scanner.next());

        }

 

        RummyPlayer player = new RummyPlayer(cards);

 

        for (int i = 0; i < 5; ++i) {

            for (int j = 0; j < 5; ++j) {

                player.addCard(new Card(scanner.next()));

            }

            System.out.println(player.toString());

            player.reset();

        }

    }

}