001package ball.game.card.poker; 002/*- 003 * ########################################################################## 004 * Game Applications and Utilities 005 * %% 006 * Copyright (C) 2010 - 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.Rank; 022import ball.game.card.Card.Suit; 023import ball.game.card.Card; 024import ball.util.Comparators; 025import ball.util.ListOrderComparator; 026import java.util.Collection; 027import java.util.Comparator; 028import java.util.List; 029import java.util.Map; 030import java.util.Objects; 031import java.util.function.Predicate; 032import java.util.stream.Collectors; 033import java.util.stream.Stream; 034 035import static ball.game.card.Card.Rank.ACE; 036import static ball.game.card.Card.Rank.KING; 037import static ball.game.card.Card.Rank.SEQUENCE; 038 039/** 040 * Poker hand {@link Ranking} {@link Enum} and {@link Predicate}. 041 * 042 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 043 */ 044public enum Ranking implements Predicate<List<Card>> { 045 Empty(0, null, Collection::isEmpty), 046 HighCard(1, t -> true, t -> true), 047 Pair(2, Rank.SAME, Rank.SAME), 048 TwoPair(4, holding(2, Rank.SAME), Pair.with(Pair)), 049 ThreeOfAKind(3, Rank.SAME, Rank.SAME), 050 Straight(5, SEQUENCE, SEQUENCE), 051 Flush(5, Suit.SAME, Suit.SAME), 052 FullHouse(5, holding(3, Rank.SAME), ThreeOfAKind.with(Pair)), 053 FourOfAKind(4, Rank.SAME, Rank.SAME), 054 StraightFlush(5, 055 holding(ACE, KING).negate().and(SEQUENCE).and(Suit.SAME), 056 holding(ACE, KING).negate().and(Straight).and(Flush)), 057 RoyalFlush(5, 058 holding(ACE, KING).and(SEQUENCE).and(Suit.SAME), 059 holding(ACE, KING).and(Straight).and(Flush)), 060 FiveOfAKind(5, Rank.SAME, Rank.SAME); 061 062 private final int required; 063 private final Predicate<List<Card>> possible; 064 private final Predicate<List<Card>> is; 065 066 private Ranking(int required, Predicate<List<Card>> possible, Predicate<List<Card>> is) { 067 this.required = required; 068 this.possible = possible; 069 this.is = Objects.requireNonNull(is); 070 } 071 072 /** 073 * Method to find the best possible {@link.this} {@link Ranking} hand in 074 * the {@link Collection}. 075 * 076 * @param collection The {@link Collection} of {@link Card}s to 077 * evaluate. 078 * 079 * @return The best sorted hand as a {@link List} of {@link Card}s if 080 * a combination matching {@link.this} {@link Ranking} is 081 * found; the empty {@link List} otherwise. 082 */ 083 public List<Card> find(Collection<Card> collection) { 084 var evaluator = new Evaluator(collection, this); 085 var hand = evaluator.getScoring().isEmpty() ? evaluator.getScoring() : evaluator.getHand(); 086 087 return hand; 088 } 089 090 /** 091 * Returns the number of {@link Card} for {@link.this} {@link Ranking}. 092 * 093 * @return The number of {@link Card}s required. 094 */ 095 public int required() { return required; } 096 097 /** 098 * Method to return a {@link Predicate} to test if the {@link List} of 099 * {@link Card} is a possible {@link Ranking}. 100 * 101 * @return A {@link Predicate} that returns {@code false} if the hand 102 * cannot be {@link.this} {@link Ranking}; {@code true} 103 * otherwise. 104 */ 105 public Predicate<List<Card>> possible() { 106 return t -> (possible == null || possible.test(subListTo(t, required()))); 107 } 108 109 @Override 110 public boolean test(List<Card> list) { 111 return (list.size() >= required() && is.test(subListTo(list, required()))); 112 } 113 114 private Predicate<List<Card>> with(Predicate<List<Card>> that) { 115 return t -> test(t) && that.test(subListFrom(t, required())); 116 } 117 118 private static <T> Predicate<List<T>> holding(int count, Predicate<List<T>> predicate) { 119 return t -> (t.isEmpty() || predicate.test(subListTo(t, count))); 120 } 121 122 @SafeVarargs 123 @SuppressWarnings({ "varargs" }) 124 private static <T> Predicate<List<T>> holding(Predicate<T>... array) { 125 return holding(Stream.of(array).collect(Collectors.toList())); 126 } 127 128 private static <T> Predicate<List<T>> holding(List<Predicate<T>> list) { 129 return t -> ((list.isEmpty() || t.isEmpty()) 130 || (list.get(0).test(t.get(0)) && (holding(subListFrom(list, 1)).test(subListFrom(t, 1))))); 131 } 132 133 private static <T> List<T> subListTo(List<T> list, int to) { 134 return list.subList(0, Math.min(to, list.size())); 135 } 136 137 private static <T> List<T> subListFrom(List<T> list, int from) { 138 return list.subList(from, list.size()); 139 } 140 141 /** 142 * {@link Comparator} that orders {@link Ranking}s weakest to 143 * strongest. 144 */ 145 public static Comparator<Ranking> COMPARATOR = new ListOrderComparator<>(values()); 146}