尽管我知道这样做会带来更多工作上的麻烦,但我还是很早就决定支持野蛮的小丑。我还想支持大量的纸牌,例如,如果您有7张纸牌并选择其中最好的5张纸牌。当然,可以使用组合技术(7 nCr 5 = 35种组合),但是我想要一个更好的方法。
所以要检查可能的扑克手,我真的不想做别人犯过同样的错误。和往常一样,我也倾向于使用灵活的解决方案。很快,策略模式警报就浮现在我的脑海。
我花了很多时间编写测试来确保它能正常工作。现在,我相信它可以正常工作(或者至少是我想要的方式)。
类概述
定义一张ace的值是什么(在这里不是很有用,但在其他卡片项目中非常有用)
AceValue
-心脏,黑桃,钻石,棍棒的枚举+通配符和其他趣味的枚举Suite
-包含ClassicCard
和等级(Suite
)的类。还包含不同等级的常量int
-负责确定给定PokerHandEval
数组的PokerHandResult
的类。 ClassicCard
-可用的标准扑克手类型的枚举PokerHandAnalyze
-持有以下类别的比较类扑克手的结果。包括PokerHandEval
+参数(“满屋子是什么?”)+“踢球者”(肯定是6s对,但是您其他最高的牌是什么?)以下三个类实现的策略模式PokerHandType
-寻找配对,两对,三种,四种和全屋的策略PokerHandResult
-寻找直系的策略/>
PokerHandType
-查找同花顺,同花顺和皇家同花顺的策略-使用PokerHandResultProducer
确定同花顺。 >由于我以前在Java中一直使用卡片,所以我认为周末挑战赛的结果会在以后的某个时候派上用场,所以我决定再次使用Java,并使用已经拥有的一些代码。 > ClassicCard.java
public class ClassicCard {
private final Suite suite;
private final int rank;
public static final int RANK_ACE_LOW = 1;
public static final int RANK_2 = 2;
public static final int RANK_3 = 3;
public static final int RANK_4 = 4;
public static final int RANK_5 = 5;
public static final int RANK_6 = 6;
public static final int RANK_7 = 7;
public static final int RANK_8 = 8;
public static final int RANK_9 = 9;
public static final int RANK_10 = 10;
public static final int RANK_JACK = 11;
public static final int RANK_QUEEN = 12;
public static final int RANK_KING = 13;
public static final int RANK_ACE_HIGH = 14;
public static final int RANK_WILDCARD = 20;
public ClassicCard(Suite suite, int rank) {
if (suite == null)
throw new NullPointerException("Suite cannot be null");
if (!suite.isWildcard() && rank == RANK_WILDCARD)
throw new IllegalArgumentException("Rank cannot be RANK_WILDCARD when suite is " + suite);
this.suite = suite;
this.rank = rank;
}
public int getRank() {
return rank;
}
public int getRankWithAceValue(AceValue aceValue) {
if (isAce())
return aceValue.getAceValue();
return rank;
}
public boolean isAce() {
return this.rank == RANK_ACE_LOW || this.rank == RANK_ACE_HIGH;
}
public boolean isWildcard() {
return suite.isWildcard();
}
public Suite getSuite() {
return suite;
}
}
AceValue.java
public enum AceValue {
LOW(ClassicCard.RANK_ACE_LOW), HIGH(ClassicCard.RANK_ACE_HIGH);
private final int aceValue;
private final int minRank;
private final int maxRank;
private final int[] ranks;
private AceValue(int value) {
this.aceValue = value;
this.minRank = Math.min(2, getAceValue());
this.maxRank = Math.max(ClassicCard.RANK_KING, getAceValue());
this.ranks = new int[52 / 4];
for (int i = 0; i < ranks.length; i++)
ranks[i] = this.minRank + i;
}
public int getMaxRank() {
return maxRank;
}
public int getMinRank() {
return minRank;
}
public int getAceValue() {
return this.aceValue;
}
public int[] getRanks() {
return Arrays.copyOf(this.ranks, this.ranks.length);
}
}
public enum Suite {
SPADES, HEARTS, DIAMONDS, CLUBS, EXTRA;
public boolean isBlack() {
return this.ordinal() % 2 == 0 && !isWildcard();
}
public boolean isWildcard() {
return this == EXTRA;
}
public boolean isRed() {
return !isBlack() && !isWildcard();
}
public static int suiteCount(boolean includingWildcards) {
int i = 0;
for (Suite suite : Suite.values()) {
if (!suite.isWildcard() || includingWildcards) {
++i;
}
}
return i;
}
}
代码
总长度:8个文件中的309行代码(不包括注释和空格)。
PokerFlush.java
/**
* Checks for FLUSH, ROYAL_FLUSH and STRAIGHT_FLUSH. Depends on {@link PokerStraight} for the straight analyze.
*/
public class PokerFlush implements PokerHandResultProducer {
private final PokerHandResultProducer straight = new PokerStraight();
@Override
public PokerHandResult resultFor(PokerHandAnalyze analyze) {
List<PokerHandResult> results = new ArrayList<PokerHandResult>();
for (Suite suite : Suite.values()) {
if (suite.isWildcard())
continue;
PokerHandAnalyze suiteHand = analyze.filterBySuite(suite);
if (suiteHand.size() < HAND_SIZE)
continue; // Not enough cards to make a complete hand
// We have a complete hand, now let's create a HandResult for it.
PokerHandResult straightResult = straight.resultFor(suiteHand);
if (straightResult != null) {
PokerHandType type = straightResult.getPrimaryRank() == AceValue.HIGH.getAceValue() ? PokerHandType.ROYAL_FLUSH : PokerHandType.STRAIGHT_FLUSH;
results.add(new PokerHandResult(type, straightResult.getPrimaryRank(), 0, null)); // We have a straight so we don't need to provide any kickers.
}
else results.add(new PokerHandResult(PokerHandType.FLUSH, 0, 0, suiteHand.getCards()));
}
if (results.isEmpty())
return null;
return PokerHandResult.returnBest(results);
}
}
PokerHandAnalyze.java
/**
* A helper class to analyze ranks and suits for an array of {@link ClassicCard}s. Create new using the static method {@link #analyze(ClassicCard...)}
*/
public class PokerHandAnalyze {
private final int[] ranks = new int[ClassicCard.RANK_ACE_HIGH];
private final int[] suites = new int[Suite.values().length];
private final ClassicCard[] cards;
private int wildcards;
private PokerHandAnalyze(ClassicCard[] cards2) {
this.cards = Arrays.copyOf(cards2, cards2.length);
}
/**
* Create a new instance and analyze the provided cards
* @param cards The cards to analyze
* @return Organized analyze of the provided cards
*/
public static PokerHandAnalyze analyze(ClassicCard... cards) {
PokerHandAnalyze hand = new PokerHandAnalyze(cards);
for (ClassicCard card : cards) {
if (card.isWildcard()) {
hand.wildcards++;
}
else if (card.isAce()) {
hand.ranks[AceValue.HIGH.getAceValue() - 1]++;
hand.ranks[AceValue.LOW.getAceValue() - 1]++;
}
else hand.ranks[card.getRank() - 1]++;
hand.suites[card.getSuite().ordinal()]++;
}
return hand;
}
public int[] getRanks() {
return ranks;
}
public int getWildcards() {
return wildcards;
}
public ClassicCard[] getCards() {
return cards;
}
public int size() {
return cards.length;
}
/**
* Create a sub-analyze which only includes wildcards and the specified suite. Useful to check for the FLUSH {@link PokerHandType}
* @param suite The suite to filter by
* @return A new analyze object
*/
public PokerHandAnalyze filterBySuite(Suite suite) {
List<ClassicCard> cards = new ArrayList<ClassicCard>();
for (ClassicCard card : this.cards) {
if (card.isWildcard() || card.getSuite().equals(suite)) {
cards.add(card);
}
}
return analyze(cards.toArray(new ClassicCard[cards.size()]));
}
}
PokerHandEval.java
/**
* Class to analyze poker hands by using a collection of {@link PokerHandResultProducer}s and return a {@link PokerHandResult}
*/
public class PokerHandEval {
public void addTest(PokerHandResultProducer test) {
this.tests.add(test);
}
private final List<PokerHandResultProducer> tests = new ArrayList<PokerHandResultProducer>();
private PokerHandResult evaluate(PokerHandAnalyze analyze) {
if (tests.isEmpty())
throw new IllegalStateException("No PokerHandResultProducers added.");
List<PokerHandResult> results = new ArrayList<PokerHandResult>();
for (PokerHandResultProducer test : tests) {
PokerHandResult result = test.resultFor(analyze);
if (result != null)
results.add(result);
}
return PokerHandResult.returnBest(results);
}
/**
* Test a bunch of cards and return the best matching 5-card {@link PokerHandResult}
* @param cards The cards to test
* @return The best matching 5-card Poker Hand
*/
public PokerHandResult test(ClassicCard... cards) {
return evaluate(PokerHandAnalyze.analyze(cards));
}
/**
* Factory method to create an evaluator for the default poker hand types.
* @return
*/
public static PokerHandEval defaultEvaluator() {
PokerHandEval eval = new PokerHandEval();
eval.addTest(new PokerPair());
eval.addTest(new PokerStraight());
eval.addTest(new PokerFlush());
return eval;
}
}
PokerHandResul t.java
/**
* Data for a found poker hand. Provides data for the type of poker hand, the primary rank and secondary rank, and kickers. Including methods for sorting. Also implements hashCode and equals.
*/
public class PokerHandResult implements Comparable<PokerHandResult> {
private final PokerHandType type;
private final int primaryRank;
private final int secondaryRank;
private final int[] kickers;
public PokerHandResult(PokerHandType type, int primaryRank, int secondaryRank, ClassicCard[] cards) {
this(type, primaryRank, secondaryRank, cards, PokerHandResultProducer.HAND_SIZE);
}
public PokerHandResult(PokerHandType type, int primaryRank, int secondaryRank, ClassicCard[] cards, int numKickers) {
this.type = type;
this.primaryRank = primaryRank;
this.secondaryRank = secondaryRank;
this.kickers = kickers(cards, new int[]{ primaryRank, secondaryRank }, numKickers);
Arrays.sort(this.kickers);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + primaryRank;
result = prime * result + secondaryRank;
result = prime * result + Arrays.hashCode(kickers);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof PokerHandResult))
return false;
PokerHandResult other = (PokerHandResult) obj;
if (type != other.type)
return false;
if (primaryRank != other.primaryRank)
return false;
if (secondaryRank != other.secondaryRank)
return false;
if (!Arrays.equals(kickers, other.kickers))
return false;
return true;
}
private static int compareKickers(int[] sorted1, int[] sorted2) {
int index1 = sorted1.length - 1;
int index2 = sorted2.length - 1;
int compare = 0;
while (compare == 0 && index1 >= 0 && index2 >= 0) {
// If one of them is bigger than another we will stop comparing, so decreasing both indexes is perfectly OK.
compare = Integer.compare(sorted1[index1--], sorted2[index2--]);
}
return compare;
}
@Override
public int compareTo(PokerHandResult other) {
// compare order: HandType, primary rank (int), secondary (used for two pair and full house), kickers
int compare = this.type.compareTo(other.type);
if (compare == 0)
compare = Integer.compare(this.primaryRank, other.primaryRank);
if (compare == 0)
compare = Integer.compare(this.secondaryRank, other.secondaryRank);
if (compare == 0)
compare = compareKickers(this.kickers, other.kickers);
return compare;
}
public PokerHandType getType() {
return type;
}
/**
* Return the best {@link PokerHandResult} of a list of results. The method first orders the list and then returns the last result.
* @param results A list of PokerHandResults
* @return The best result from the list
*/
public static PokerHandResult returnBest(List<PokerHandResult> results) {
if (results.isEmpty())
return null;
Collections.sort(results);
return results.get(results.size() - 1);
}
/**
* Create an integer array of "kickers", to separate FOUR_OF_A_KIND with Ace-kicker vs. King-kicker
* @param cards The cards in your hand. If null, an empty array will be returned
* @param skip Ranks that will be skipped (for example, if you have a pair of 4s then you can skip those 4s)
* @param count How many kickers that should be included. This should ideally be 5 - number of cards required for the {@link PokerHandType} the kickers are provided for
* @return An array of the ranks that will be used as kickers. Wildcards and the ranks in the skip array are excluded
*/
private static int[] kickers(ClassicCard[] cards, int[] skip, int count) {
if (cards == null)
return new int[]{};
int[] result = new int[cards.length];
Arrays.sort(skip);
for (int i = 0; i < cards.length; i++) {
int rank = cards[i].getRankWithAceValue(AceValue.HIGH);
// Check if we should skip this rank in the kicker-data.
if (cards[i].isWildcard() || Arrays.binarySearch(skip, rank) >= 0)
continue;
result[i] = rank;
}
Arrays.sort(result);
return Arrays.copyOfRange(result, Math.max(result.length - count, 0), result.length);
}
public int getPrimaryRank() {
return primaryRank;
}
public int getSecondaryRank() {
return secondaryRank;
}
@Override
public String toString() {
return String.format("PokerHand: %s. %d, %d. Kickers: %s", type, primaryRank, secondaryRank, Arrays.toString(kickers));
}
}
PokerHandResultProducer.java
/**
* Interface for scanning for Poker hands.
*/
public interface PokerHandResultProducer {
/**
* Constant for how big our hands should be.
*/
final int HAND_SIZE = 5;
/**
* Method which does the job of finding a matching Poker hand for some analyze data.
* @param analyze {@link PokerHandAnalyze} object containing data for which we should try to find a matching Poker hand.
* @return {@link PokerHandResult} for the best poker hand we could find.
*/
PokerHandResult resultFor(PokerHandAnalyze analyze);
}
PokerHandType.java
public enum PokerHandType {
HIGH_CARD, PAIR, TWO_PAIR, THREE_OF_A_KIND, STRAIGHT, FLUSH, FULL_HOUSE, FOUR_OF_A_KIND, STRAIGHT_FLUSH, ROYAL_FLUSH;
}
PokerPair.java
/**
* Checks for PAIR, THREE_OF_A_KIND, FOUR_OF_A_KIND, and FULL_HOUSE. Returns HIGH_CARD if nothing better was found.
*/
public class PokerPair implements PokerHandResultProducer {
@Override
public PokerHandResult resultFor(PokerHandAnalyze analyze) {
List<PokerHandResult> results = new ArrayList<PokerHandResult>();
List<PokerHandResult> pairs = new ArrayList<PokerHandResult>();
List<PokerHandResult> threeOfAKinds = new ArrayList<PokerHandResult>();
int[] ranks = analyze.getRanks();
int remainingWildcards = analyze.getWildcards();
// Find out how many we should look for, primarily
int[] sortedCounts = Arrays.copyOf(ranks, ranks.length);
Arrays.sort(sortedCounts);
int countForWildcards = sortedCounts[sortedCounts.length - 1];
for (int index = ranks.length - 1; index >= 0; index--) {
int count = ranks[index];
int useWildcards = (count == countForWildcards ? remainingWildcards : 0);
if (count + useWildcards >= 4) {
remainingWildcards += count - 4;
results.add(new PokerHandResult(PokerHandType.FOUR_OF_A_KIND, index + 1, 0, analyze.getCards(), 1));
}
// If there already exists some four of a kinds, then there's no need to check three of a kinds or pairs.
if (!results.isEmpty())
continue;
if (count + useWildcards == 3) {
remainingWildcards += count - 3;
threeOfAKinds.add(new PokerHandResult(PokerHandType.THREE_OF_A_KIND, index + 1, 0, analyze.getCards(), 2));
}
else if (count + useWildcards == 2) {
remainingWildcards += count - 2;
pairs.add(new PokerHandResult(PokerHandType.PAIR, index + 1, 0, analyze.getCards(), 3));
}
}
return checkForFullHouseAndStuff(analyze, pairs, threeOfAKinds, results);
}
private PokerHandResult checkForFullHouseAndStuff(PokerHandAnalyze analyze, List<PokerHandResult> pairs, List<PokerHandResult> threeOfAKinds, List<PokerHandResult> results) {
if (!results.isEmpty())
return PokerHandResult.returnBest(results);
PokerHandResult bestPair = PokerHandResult.returnBest(pairs);
PokerHandResult bestThree = PokerHandResult.returnBest(threeOfAKinds);
if (bestPair != null && bestThree != null) {
return new PokerHandResult(PokerHandType.FULL_HOUSE, bestThree.getPrimaryRank(), bestPair.getPrimaryRank(), null, 0); // No kickers because it's a complete hand.
}
if (bestThree != null)
return bestThree;
if (pairs.size() >= 2) {
Collections.sort(pairs);
int a = pairs.get(pairs.size() - 1).getPrimaryRank();
int b = pairs.get(pairs.size() - 2).getPrimaryRank();
return new PokerHandResult(PokerHandType.TWO_PAIR, Math.max(a, b), Math.min(a, b), analyze.getCards(), 1);
}
if (bestPair != null)
return bestPair;
// If we have a wildcard, then we always have at least PAIR, which means that it's fine to ignore wildcards in the kickers here as well
return new PokerHandResult(PokerHandType.HIGH_CARD, 0, 0, analyze.getCards());
}
}
PokerStraight.java-(已删除)由于问题大小限制。请参见单独的问题
测试
PokerHandTest.java(175行)
public class PokerHandTest {
private static final ClassicCard WILDCARD = new ClassicCard(Suite.EXTRA, ClassicCard.RANK_WILDCARD);
private int findHighestIndexForStraight(int[] ranks, int wildcards) {
int res = PokerStraight.findHighestIndexForStraight(ranks, wildcards);
return res == -1 ? res : res - 1;
}
@Test
public void moreCards() {
PokerHandEval eval = PokerHandEval.defaultEvaluator();
assertPoker(PokerHandType.THREE_OF_A_KIND, 2, eval.test(card(DIAMONDS, RANK_JACK), card(HEARTS, RANK_ACE_HIGH), card(SPADES, RANK_7),
card(DIAMONDS, RANK_2), card(HEARTS, RANK_2), card(DIAMONDS, RANK_4), WILDCARD));
assertPoker(PokerHandType.TWO_PAIR, 12, 10, eval.test(card(DIAMONDS, RANK_3), card(SPADES, RANK_2), card(DIAMONDS, RANK_10),
card(CLUBS, RANK_10), card(CLUBS, RANK_7), card(SPADES, RANK_5),
card(SPADES, RANK_QUEEN), card(HEARTS, RANK_QUEEN),
card(HEARTS, RANK_ACE_HIGH), card(DIAMONDS, RANK_7)));
PokerHandResult eq1;
PokerHandResult eq2;
eq1 = eval.test(card(CLUBS, RANK_10), card(CLUBS, RANK_7), card(SPADES, RANK_KING),
card(SPADES, RANK_QUEEN), card(HEARTS, RANK_QUEEN), card(HEARTS, RANK_ACE_HIGH), card(DIAMONDS, RANK_ACE_HIGH));
eq2 = eval.test(card(CLUBS, RANK_JACK), card(CLUBS, RANK_7), card(SPADES, RANK_KING),
card(SPADES, RANK_QUEEN), card(HEARTS, RANK_QUEEN), card(HEARTS, RANK_ACE_HIGH), card(DIAMONDS, RANK_ACE_HIGH));
assertEquals(eq1, eq2);
eq1 = eval.test(WILDCARD, card(SPADES, RANK_QUEEN), card(HEARTS, RANK_QUEEN), card(HEARTS, RANK_7), card(DIAMONDS, RANK_4));
eq2 = eval.test(card(DIAMONDS, RANK_QUEEN), card(CLUBS, RANK_QUEEN), card(SPADES, RANK_QUEEN), card(HEARTS, RANK_7), card(DIAMONDS, RANK_4));
assertPoker(PokerHandType.THREE_OF_A_KIND, RANK_QUEEN, eq1);
assertEquals(eq2, eq1);
PokerHandResult result;
result = eval.test(WILDCARD, WILDCARD, WILDCARD, WILDCARD, card(DIAMONDS, RANK_6));
assertPoker(PokerHandType.STRAIGHT_FLUSH, result);
result = eval.test(WILDCARD, WILDCARD, WILDCARD, card(HEARTS, RANK_10), card(DIAMONDS, RANK_6));
assertPoker(PokerHandType.FOUR_OF_A_KIND, result);
result = eval.test(WILDCARD, WILDCARD, WILDCARD, WILDCARD, WILDCARD);
assertPoker(PokerHandType.ROYAL_FLUSH, result);
result = eval.test(WILDCARD, WILDCARD, WILDCARD, WILDCARD, card(SPADES, RANK_10));
assertPoker(PokerHandType.ROYAL_FLUSH, result);
}
public void assertPoker(PokerHandType type, int primary, PokerHandResult test) {
assertPoker(type, test);
assertEquals(primary, test.getPrimaryRank());
}
public void assertPoker(PokerHandType type, int primary, int secondary, PokerHandResult test) {
assertPoker(type, primary, test);
assertEquals(secondary, test.getSecondaryRank());
}
@Test
public void royalAndFlushStraights() {
PokerHandEval eval = PokerHandEval.defaultEvaluator();
PokerHandResult result = eval.test(card(HEARTS, RANK_10), card(HEARTS, RANK_JACK), card(HEARTS, RANK_QUEEN), card(HEARTS, RANK_KING), card(HEARTS, RANK_ACE_LOW));
assertPoker(PokerHandType.ROYAL_FLUSH, result);
result = eval.test(card(HEARTS, RANK_2), card(HEARTS, RANK_3), card(HEARTS, RANK_4), card(HEARTS, RANK_5), card(HEARTS, RANK_6));
assertPoker(PokerHandType.STRAIGHT_FLUSH, result);
}
@Test
public void rankHands() {
PokerHandEval eval = PokerHandEval.defaultEvaluator();
PokerHandResult highCard = eval.test(card(HEARTS, RANK_7), card(CLUBS, RANK_JACK), card(HEARTS, RANK_6), card(HEARTS, RANK_4), card(DIAMONDS, RANK_2));
PokerHandResult pairLowKicker = eval.test(card(HEARTS, RANK_7), card(CLUBS, RANK_7), card(HEARTS, RANK_6), card(HEARTS, RANK_4), card(HEARTS, RANK_2));
PokerHandResult pairHighKicker = eval.test(card(HEARTS, RANK_7), card(CLUBS, RANK_7), card(HEARTS, RANK_KING), card(HEARTS, RANK_4), card(HEARTS, RANK_2));
PokerHandResult pairHigher = eval.test(card(HEARTS, RANK_KING), card(CLUBS, RANK_KING), card(HEARTS, RANK_6), card(HEARTS, RANK_4), card(HEARTS, RANK_2));
PokerHandResult twoPair = eval.test(card(HEARTS, RANK_KING), card(CLUBS, RANK_KING), card(HEARTS, RANK_6), card(DIAMONDS, RANK_6), card(HEARTS, RANK_2));
PokerHandResult threeOfAKind = eval.test(card(HEARTS, RANK_KING), card(CLUBS, RANK_KING), card(SPADES, RANK_KING), card(HEARTS, RANK_4), card(HEARTS, RANK_2));
PokerHandResult flush = eval.test(card(HEARTS, RANK_7), card(HEARTS, RANK_2), card(HEARTS, RANK_6), card(HEARTS, RANK_9), card(HEARTS, RANK_QUEEN));
PokerHandResult fourOfAKind = eval.test(card(HEARTS, RANK_7), card(SPADES, RANK_7), card(DIAMONDS, RANK_7), card(CLUBS, RANK_7), card(HEARTS, RANK_QUEEN));
PokerHandResult straight = eval.test(card(HEARTS, RANK_2), card(CLUBS, RANK_3), card(HEARTS, RANK_4), card(HEARTS, RANK_5), card(DIAMONDS, RANK_6));
PokerHandResult straightWild = eval.test(card(HEARTS, RANK_2), card(CLUBS, RANK_3), WILDCARD, card(HEARTS, RANK_5), card(DIAMONDS, RANK_6));
assertEquals(straight, straightWild);
PokerHandResult straightLow = eval.test(card(HEARTS, RANK_ACE_HIGH), card(CLUBS, RANK_2), card(HEARTS, RANK_3), card(HEARTS, RANK_4), card(DIAMONDS, RANK_5));
PokerHandResult straightFlush = eval.test(card(HEARTS, RANK_8), card(HEARTS, RANK_9), card(HEARTS, RANK_10), card(HEARTS, RANK_JACK), card(HEARTS, RANK_QUEEN));
PokerHandResult royalFlush = eval.test(card(HEARTS, RANK_10), card(HEARTS, RANK_JACK), card(HEARTS, RANK_QUEEN), card(HEARTS, RANK_KING), WILDCARD);
PokerHandResult fullHouse = eval.test(card(HEARTS, RANK_10), card(CLUBS, RANK_10), WILDCARD, card(HEARTS, RANK_KING), card(HEARTS, RANK_KING));
assertPoker(PokerHandType.FULL_HOUSE, fullHouse);
assertEquals(RANK_KING, fullHouse.getPrimaryRank());
assertEquals(RANK_10, fullHouse.getSecondaryRank());
// Add hands to list
List<PokerHandResult> results = new ArrayList<PokerHandResult>();
assertAdd(results, PokerHandType.HIGH_CARD, highCard);
assertAdd(results, PokerHandType.PAIR, pairLowKicker);
assertAdd(results, PokerHandType.PAIR, pairHighKicker);
assertAdd(results, PokerHandType.PAIR, pairHigher);
assertAdd(results, PokerHandType.TWO_PAIR, twoPair);
assertAdd(results, PokerHandType.THREE_OF_A_KIND, threeOfAKind);
assertAdd(results, PokerHandType.FLUSH, flush);
assertAdd(results, PokerHandType.FOUR_OF_A_KIND, fourOfAKind);
assertAdd(results, PokerHandType.STRAIGHT, straightLow);
assertAdd(results, PokerHandType.STRAIGHT, straight);
assertAdd(results, PokerHandType.STRAIGHT, straightWild);
assertAdd(results, PokerHandType.STRAIGHT_FLUSH, straightFlush);
assertAdd(results, PokerHandType.ROYAL_FLUSH, royalFlush);
// Shuffle just for the fun of it
Collections.shuffle(results);
// Sort the list according to the HandResult comparable interface
Collections.sort(results);
// Assert the list
Iterator<PokerHandResult> it = results.iterator();
assertEquals(highCard, it.next());
assertEquals(pairLowKicker, it.next());
assertEquals(pairHighKicker, it.next());
assertEquals(pairHigher, it.next());
assertEquals(twoPair, it.next());
assertEquals(threeOfAKind, it.next());
assertEquals(straightLow, it.next());
assertEquals(straight, it.next());
assertEquals(straightWild, it.next());
assertEquals(flush, it.next());
assertEquals(fourOfAKind, it.next());
assertEquals(straightFlush, it.next());
assertEquals(royalFlush, it.next());
// Make sure that we have processed the entire list
assertFalse("List is not completely processed", it.hasNext());
}
private static void assertAdd(List<PokerHandResult> results, PokerHandType type, PokerHandResult result) {
assertPoker(type, result);
results.add(result);
}
private static void assertPoker(PokerHandType type, PokerHandResult result) {
if (type == null) {
assertNull(result);
return;
}
assertNotNull("Expected " + type, result);
assertEquals(result.toString(), type, result.getType());
}
private static ClassicCard card(Suite suite, int rank) {
return new ClassicCard(suite, rank);
}
}
代码审查问题)
我试图充分利用策略模式和某些工厂方法(
PokerPair
和PokerStraight
),我想知道它们是否“正确”。如果您在不知不觉中看到了其他常用的模式,我也想听听。但最重要的是:在这里我能做得更好吗?而且您能找到任何错误吗?
#1 楼
AceValue这个课让我感到困惑。我不确定
ranks[]
的用途是什么。...有魔术数字,没有评论? Suite
这具有
isBlack()
方法,但这表明DIAMONDS是黑色的...但是它们是红色的!更简单:return Suite.values - (includingWildcards ? 0 : 1);
常规
对了,此后,复习变得非常复杂...坦率地说,在30分钟内就太难理解或更少...然后将代码拉到本地以便我可以运行它也不容易...并且当我决定这样做时,我已经花了太长时间了。这本身就是一个有趣的观察。我试图通读这些类,了解它们如何“挂在一起”,然后决定理解代码的唯一方法是对其进行调试和逐步调试。
然后我意识到没有主要方法,我猜您使用JUnit来运行它吗?
最后,我从未玩过扑克...(甚至没有剥离...),所以我没有本能的想法
最重要的是,代码需要对问题有一种本能/本能的理解,以便理解代码。本身表明存在结构/表达问题。
唯一合理的事情是对面值进行评论,而不是我想要的更深入...
PokerFlush
我不喜欢从其他类“隐式”提取的常量。在这种情况下,“神奇地”出现了
suiteCount(boolean)
,我看到它在HAND_SIZE
类实现的PokerHandResultProducer
接口中是一个常数。对于PokerFlush
来说,这是一个较差的位置,并且是引用它的较差的方法。它应该类似于HAND_SIZE
。我不喜欢结果
PokerRules.HAND_SIZE
和List
的组合。我认为有更好的方法。一个叫做PokerHandResult.returnBest
的类,它在添加手时简单地选择最佳手牌...然后具有Best<T>
方法。您的代码变为:Best<PokerHandResult> results = new Best<PokerHandResult>();
....
return results.best();
best()
实例具有“自然顺序”(实现可比较),因此通用类PokerHandResult
仅需运行Best<T>
方法并保持最好。PokerHandAnalyze
private final int[] ranks = new int[ClassicCard.RANK_ACE_HIGH]
我花了一些时间才明白...我知道这对您来说很有意义,即使我仔细看,也无法理解为什么不是
compareTo()
。至少这行需要一个好的注释,更好的是一个具有更好名称的常量..即,wtf是否与数组大小有关?在这里,但是它被错误地使用... 工厂方法对于掩盖接口的物理实现或创建复杂的不可变对象很有用。在这种情况下,工厂方法实际上只是通过将所有逻辑移到工厂方法来简化构造函数。实际上,这并不能简化类,而只是将构造函数逻辑移至非逻辑位置。
在我的评估中,应删除该工厂方法,并将逻辑移至构造函数。然后将所有实例字段定为最终字段,您将拥有一个不错的不可变类。如果您确实想保留工厂方法,那么您的字段应该仍然是最终字段,但是您需要将它们全部传递给构造函数。
ClassicCard.RANK_WILDCARD
应该返回数组的副本,以使其不可变状态被保留(您已将数组标记为最终状态,但未保护数组中数据的最终状态)据我所知,
RANK_ACE_HIGH
数组为“死”代码(完全未使用)。getRanks()
还应该通过返回数组的副本来保护数据。PokerHandEval
在任何情况下都不要使用名称为“ test”的类/方法,除非它旨在测试您的代码(而不是测试数据).... ;-)这只会导致混乱。该类称为“ ... Eval”,所以为什么不能使用
suites[]
或某些变体? 。您首先寻找排名最低的结果,然后寻找越来越高的结果。如果您取消订单,则可以“缩短”流程并在获得第一个结果时退出(即,如果已经有同花顺,为什么还要寻找一对?)。再次,在这里您还可以使用'Best'类来保持最佳结果。 (
getCards()
)我不喜欢此类的
evaluate
。应该替换为public final class PokerHandResult
或将其移至中央returnBest(List<PokerHandResult> results)
静态类中。否则,它是一个很好的接口(对此不多...)。PokerHandType
PokerPair
您已将此方法分为两部分,这只是降低“复杂性”的一种廉价方法。但是,实际上,它增加了复杂性。...
Best<T>
...真的吗?该方法没有附加值,它只会为用户创建两个级别的“返回”。有时方法只是很复杂。...做的第一件事是如果已经找到另一个结果,则返回。除此之外,它不使用
PokerRules
列表。在其他情况下,对于
HAND_SIZE
,checkForFullHouseAndStuff
和results
来说,Best<T>
类将很有用。结论
对于您使用的其他模式的问题,我真的帮不上太大忙,至于您如何使用策略和工厂模式,我觉得策略很好(除了
results
和评估顺序)。工厂模式在某些地方很麻烦。我没有看到任何会影响游戏的错误。黑钻石是我看到的唯一问题...(这对滑雪很有帮助)。
评论
\ $ \ begingroup \ $
我们很幸运有您@rolfl。很棒的评论!
\ $ \ endgroup \ $
– Mathieu Guindon♦
2013年12月9日,3:30
\ $ \ begingroup \ $
钻石是黑色的,而紫罗兰是蓝色的,我写错了方法,谢谢。我再也不会将this.ordinal()用于这种方法了!总会有某人(就是我自己)迟早会交换元素以使其不正确。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2013年12月9日15:30
\ $ \ begingroup \ $
AceValue用于处理ace最高和最低的两个纸牌游戏。 getRanks返回一个可以循环通过的数组,该数组提供所有可用的等级(2到ACE_HIGH或ACE_LOW到KING)。由于面临的挑战是要成为一名评估员,因此我只写了代码,并不在乎添加任何与用户进行交互的UI。但是是的,这可能会使代码更易于理解,因为会有一个更清晰的“入口点”(是的,我目前仅使用单元测试)
\ $ \ endgroup \ $
–西蒙·福斯伯格
2013年12月9日15:51
\ $ \ begingroup \ $
@SimonAndréForsberg。好的,这些注释消除了我提到的问题。另一方面,您确实必须添加注释以使其清晰可见,因此代码本身可能应该以;-)开始添加注释。
\ $ \ endgroup \ $
–rolfl
2013年12月9日在16:40
\ $ \ begingroup \ $
@rolfl如果不问正确的问题,很难写出正确的评论。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2013年12月9日17:50
#2 楼
差不多四年后...我发现了一个错误。请考虑以下测试:assertEquals(PokerHandType.THREE_OF_A_KIND, eval.evaluate(
card(Suite.CLUBS, ClassicCard.RANK_ACE_HIGH),
card(Suite.HEARTS, ClassicCard.RANK_5),
card(Suite.SPADES, ClassicCard.RANK_KING),
card(Suite.DIAMONDS, ClassicCard.RANK_ACE_HIGH),
card(Suite.SPADES, ClassicCard.RANK_ACE_HIGH)
).getType());
这是三种,应该通过,对吧?确实可以。
下一个:
assertEquals(PokerHandType.THREE_OF_A_KIND, eval.evaluate(
card(Suite.CLUBS, ClassicCard.RANK_ACE_HIGH),
card(Suite.SPADES, ClassicCard.RANK_ACE_HIGH),
card(Suite.SPADES, ClassicCard.RANK_KING),
card(Suite.HEARTS, ClassicCard.RANK_8),
card(Suite.HEARTS, ClassicCard.RANK_5),
card(Suite.SPADES, ClassicCard.RANK_2),
card(Suite.EXTRA, ClassicCard.RANK_WILDCARD)
).getType());
哦,通配符!但这仍然被认为是三种,对吗?错误。它应该是三种,但是代码认为这是一个满屋,因为ace可以是高(14)和低(1),因此我们有一个满屋,其中有三个14和两个1。 br />我们甚至可以摆脱一些额外的卡,看看其中的一张:低一点。
那么简单吗?不,代码说是两对。同样,有两对高和低的ace。在
checkForFullHouseAndStuff
中的PokerPair
中,首先通过以下方法传递结果:assertEquals(PokerHandType.THREE_OF_A_KIND, eval.evaluate(
card(Suite.CLUBS, ClassicCard.RANK_ACE_HIGH),
card(Suite.SPADES, ClassicCard.RANK_ACE_HIGH),
card(Suite.EXTRA, ClassicCard.RANK_WILDCARD)
).getType());
此方法首先检查结果是否同时包含高位和低位Ace,如果是,它会返回只有主要等级的退化扑克手。所以满屋变成了三种,而两对变成了一对。
评论
假设您正在玩5张牌(剩下的代码不是...),是我还是没有“踢牌手”?您无法告诉7张牌交易的赢家,即使两位球员的第6张牌是红桃王牌,而另一位球员的第6张牌是2家具乐部,两位选手的直线相同。对还是错? :p@retailcoder标记:按设计状态。我只将完整的“扑克手”视为五张牌。如果您有七张牌,那么其中只有五张将包含在扑克手中。因此,如果您有直牌(五张牌),则没有踢脚。 (我在想德克萨斯扑克,可能还会有其他变化)
@retailcoder灵活的代码,正确的代码(咳嗽),短代码-无法全部使用。
不要使用前缀代替软件包名称。那就是将类PokerXyz打包到扑克中,并且仅使用Xyz。
细心:术语是西装,而不是西装。