001package ball.riddler538.ant.taskdefs;
002/*-
003 * ##########################################################################
004 * Solutions for the 538 Riddler
005 * %%
006 * Copyright (C) 2015 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import ball.game.card.Card;
022import ball.game.card.poker.Deck;
023import ball.game.card.poker.Evaluator;
024import ball.game.card.poker.Ranking;
025import ball.swing.table.SimpleTableModel;
026import ball.util.ant.taskdefs.AntTask;
027import java.util.Arrays;
028import java.util.Collections;
029import java.util.List;
030import java.util.LinkedList;
031import java.util.TreeMap;
032import lombok.Getter;
033import lombok.NoArgsConstructor;
034import lombok.Setter;
035import lombok.ToString;
036import org.apache.tools.ant.BuildException;
037
038/**
039 * {@link.uri http://ant.apache.org/ Ant} {@link org.apache.tools.ant.Task}
040 * to solve
041 * {@link.uri https://fivethirtyeight.com/features/hark-two-holiday-puzzles/ Riddler Express}
042 * <p>
043 * From Jeffrey Hope, open up your junk drawer and pull out those decks of
044 * cards:
045 * </p>
046 * <p>
047 * Your challenge is to take any 50 cards from a standard 52-card deck and
048 * arrange them into 10 poker hands, one of each type from a royal flush
049 * down to a lowly high card.2 The hands you build always rank as the
050 * highest type of hand possible,3 and a card may not be reused across the
051 * hands. (This means, for example, that a four-of-a-kind better than four
052 * nines is impossible because it’d use a card you’d need to make a royal
053 * flush.) As in actual poker, it doesn’t matter what order the cards are
054 * arranged within a hand.
055 * </p>
056 * <p>
057 * Sounds easy enough, and indeed there is more than one solution. But:
058 * Exactly how many solutions are there?
059 * </p>
060 *
061 * {@ant.task}
062 *
063<pre>
064Ranking       Hand
065----------------------------------------
066RoyalFlush    [A-♤, K-♤, Q-♤, J-♤, 10-♤]
067StraightFlush [K-♡, Q-♡, J-♡, 10-♡, 9-♡]
068FourOfAKind   [8-♤, 8-♡, 8-♢, 8-♧, J-♧]
069FullHouse     [A-♡, A-♢, A-♧, K-♢, K-♧]
070Flush         [Q-♢, J-♢, 10-♢, 9-♢, 7-♢]
071Straight      [7-♤, 6-♤, 5-♤, 4-♤, 3-♤]
072ThreeOfAKind  [6-♡, 6-♢, 6-♧, 10-♧, 5-♧]
073TwoPair       [9-♤, 9-♧, 7-♡, 7-♧, 4-♡]
074Pair          [5-♡, 5-♢, 4-♢, 4-♧, 3-♡]
075HighCard      [Q-♧, 3-♢, 3-♧, 2-♤, 2-♡]
076Remaining     [2-♢, 2-♧]
077</pre>
078 *
079 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
080 */
081@AntTask("solve-express-2017-12-22")
082@NoArgsConstructor @ToString
083public class SolveExpress20171222Task extends AbstractTask {
084    @Override
085    public void execute() throws BuildException {
086        super.execute();
087
088        try {
089            SimpleTableModel model = new SimpleTableModel(new Object[][] { }, "Ranking", "Hand");
090            TreeMap<Ranking,List<Card>> map = new TreeMap<>();
091            List<Card> deck = new Deck();
092
093            deck.sort(Evaluator.CARD);
094            Collections.reverse(deck);
095
096            List<Ranking> rankings = Arrays.asList(Ranking.values());
097
098            Collections.reverse(rankings);
099
100            for (Ranking ranking : rankings) {
101                List<Card> hand = ranking.find(deck);
102
103                if (! hand.isEmpty()) {
104                    hand = hand.subList(0, ranking.required());
105
106                    map.put(ranking, hand);
107
108                    deck.removeAll(hand);
109                }
110            }
111
112            for (Ranking ranking : rankings) {
113                List<Card> hand = map.get(ranking);
114
115                if (hand != null) {
116                    if (hand.size() < 5) {
117                        hand = new LinkedList<>(hand);
118
119                        List<Card> draw = deck.subList(0, 5 - hand.size());
120
121                        hand.addAll(draw);
122                        draw.clear();
123                    }
124
125                    model.row(ranking, hand);
126                }
127            }
128
129            model.row("Remaining", deck);
130
131            log(model);
132        } catch (BuildException exception) {
133            throw exception;
134        } catch (Throwable throwable) {
135            throwable.printStackTrace();
136            throw new BuildException(throwable);
137        }
138    }
139}