【算法】用c#实现德州扑克卡牌游戏规则
德州扑克是一种牌类游戏,可多人参与,它的玩法是,玩家每人发两张底牌,桌面依次发5张公共牌,玩家用自己的两张底牌和5张公共牌自由组合,按大小决定胜负。使用c#完成功能Hand()以返回手牌类型和按重要性递减顺序排列的等级列表,用于与同类型的其他手牌进行比较,即最佳手牌。
可能的手牌按价值降序排列:
同花顺(同一套衣服的连续五个等级)。级别越高越好。
四张(四张等级相同的牌)。平局决胜先是等级,然后是剩余牌的等级。
满座(三张等级相同的牌,两张等级相同)。决胜局首先是三张牌的等级,然后是一对牌的等级。
同花顺(五张同花色的牌)。从高到低,级别越高越好。
直(连续五个等级)。级别越高越好。
三张牌(三张等级相同的牌)。决胜局是三张牌中排名第一的,然后是其他排名最高的,然后才是其他排名第二的。
两对(两张相同等级的牌,两张不同等级的牌)。决胜局首先是高牌对的等级,然后是低牌对的级别,然后是剩余牌的等级。
配对(两张等级相同的牌)。平局决胜是先是两张牌的等级,然后是其他三张牌的级别。
算法实现:
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 public static class Edm
7 {
8 public static (string type, string[] ranks) Hand(string[] holeCards, string[] communityCards)
9 {
10 Card[] allCards = holeCards.Concat(communityCards).Select( x=> new Card(x)).OrderByDescending(card =>card.value).ToArray();
11
12 var rulesChain = createChainOfCommand();
13 var powerhands = rulesChain.Execute(allCards);
14 return (powerhands.Item1, getReturnlist(allCards, powerhands.Item2));
15
16 }
17 public static string[] getReturnlist(Card[] cards, Card[] powerhand)
18 {
19 var remainderHand = cards.Where(x => !powerhand.Any(y => y.Equals(x))).Take(5-powerhand.Length);
20 var result = powerhand.Select(x =>x.number).Distinct().Concat(remainderHand.Select(x=>x.number)).Take(5).Select(x => x.ToString()).ToArray();
21 return result;
22 }
23
24 public static Rule createChainOfCommand()
25 {
26 Rule straightFlush = new StraightFlushRule();
27 Rule fourOfAKind = new FourOfAKindRule();
28 Rule fullHouse = new FullHouseRule();
29 Rule flush = new FlushRule();
30 Rule straight = new StraightRule();
31 Rule threeOfAKind = new ThreeOfAKindRule();
32 Rule pairTwoPair = new PairTwoPairRule();
33 straightFlush.SetSuccessor(fourOfAKind);
34 fourOfAKind.SetSuccessor(fullHouse);
35 fullHouse.SetSuccessor(flush);
36 flush.SetSuccessor(straight);
37 straight.SetSuccessor(threeOfAKind);
38 threeOfAKind.SetSuccessor(pairTwoPair);
39 return straightFlush;
40 }
41 }
42 public abstract class Rule
43 {
44 private Rule nextRule;
45 public void SetSuccessor(Rule next)
46 {
47 nextRule = next;
48 }
49 public virtual (string, Card[]) Execute(Card[] cards)
50 {
51 if (nextRule != null)
52 {
53 return nextRule.Execute(cards);
54 }
55 return ("nothing", cards.Take(5).ToArray());
56 }
57 }
58
59 public class PairTwoPairRule : Rule
60 {
61 public override (string, Card[]) Execute(Card[] cards)
62 {
63 var pairs = cards.GroupBy(x => x.number).Where(g => g.Count() >= 2).SelectMany(card => card).ToList();
64 if (pairs.Any())
65 {
66 if(pairs.Count() >= 4)
67 {
68 return ("two pair", pairs.Take(4).ToArray());
69 }
70 return ("pair", pairs.Take(2).ToArray());
71 }
72 return base.Execute(cards);
73 }
74 }
75 public class ThreeOfAKindRule : Rule
76 {
77 public override (string, Card[]) Execute(Card[] cards)
78 {
79 var triple = cards.GroupBy(x => x.number).Where(g => g.Count() >= 3).SelectMany(card => card).ToList();
80 if (triple.Any())
81 {
82 return ("three-of-a-kind", triple.Take(3).ToArray());
83 }
84 return base.Execute(cards);
85 }
86 }
87 public class StraightRule : Rule
88 {
89 public override (string, Card[]) Execute(Card[] cards)
90 {
91 for (int i = 0; i < cards.Length - 4; i++)
92 {
93 List<Card> rtnList = new List<Card>() { cards }; //"A♥","J♦","10♥" "9♠", "9♥", "8♠", "7♣"
94 int counter = 4;
95 int j = i;
96 while (counter >= 0 && j < cards.Length - 1)
97 {
98 if (cards.value - cards.value == 1)
99 {
100 rtnList.Add(cards);
101
102 if (rtnList.Count() == 5)
103 {
104 return ("straight", rtnList.ToArray());
105 }
106 counter--;
107 j++;
108 }
109 else if (cards.value - cards.value == 0)
110 {
111 j++;
112 }
113 else
114 {
115 break;
116 }
117 }
118 }
119 return base.Execute(cards);
120 }
121 }
122 public class FlushRule : Rule
123 {
124 public override (string, Card[]) Execute(Card[] cards)
125 {
126 var flush = cards.GroupBy(x => x.suit).Where(g => g.Count() >= 5).SelectMany(card => card).ToList();
127 if (flush.Any())
128 {
129 return ("flush", flush.ToArray());
130 }
131 return base.Execute(cards);
132 }
133 }
134
135 public class FullHouseRule : Rule
136 {
137 public override (string, Card[]) Execute(Card[] cards)
138 {
139 var triple = new ThreeOfAKindRule();
140 var pair = new PairTwoPairRule();
141
142 var powerhands = triple.Execute(cards);
143 if (!powerhands.Item1.Equals("nothing"))
144 {
145 if (powerhands.Item2.Count() == 6) // then 2 three of a kind found
146 {
147 return ("full house", powerhands.Item2.Take(5).ToArray());
148 }
149 var remainderHand = cards.Where(x => !powerhands.Item2.Any(y => y.Equals(x))).ToArray();
150 var pairHand = pair.Execute(remainderHand);
151 if (!pairHand.Item1.Equals("nothing"))
152 {
153 var fullhouseHand = powerhands.Item2.Concat(pairHand.Item2.Take(2)).ToArray();
154 return ("full house", fullhouseHand.Take(5).ToArray());
155 }
156 }
157 return base.Execute(cards);
158 }
159 }
160 public class FourOfAKindRule : Rule
161 {
162 public override (string, Card[]) Execute(Card[] cards)
163 {
164 var fourOfAKind = cards.GroupBy(x => x.number).Where(g => g.Count() >= 4).SelectMany(card => card).ToList();
165 if (fourOfAKind.Any())
166 {
167 return ("four-of-a-kind", fourOfAKind.Take(4).ToArray());
168 }
169 return base.Execute(cards);
170 }
171 }
172 public class StraightFlushRule : Rule
173 {
174 public override (string, Card[]) Execute(Card[] cards)
175 {
176 var flushRule = new FlushRule();
177 var straightRule = new StraightRule();
178 var flushHand = flushRule.Execute(cards);
179 var straightHand = straightRule.Execute(flushHand.Item2);
180 if (!straightHand.Item1.Equals("nothing") && !flushHand.Item1.Equals("nothing"))
181 {
182 return ("straight-flush", straightHand.Item2.Take(5).ToArray());
183 }
184 return base.Execute(cards);
185 }
186 }
187
188 public class Card{
189 public String number { get; set; }
190 public int value { get; set; }
191 public char suit { get; set; }
192 public Dictionary<char, int> mapping = new Dictionary<char, int>()
193 {
194 { 'A',14 },
195 { 'K',13 },
196 { 'Q',12 },
197 { 'J',11 },
198 { '1', 10}
199 };
200 public Card(String s)
201 {
202 number = (s == '1')? "10": Char.ToString(s);
203 value = mapping.ContainsKey(s)? mapping]: (int) Char.GetNumericValue(s);
204 suit = s;
205 }
206 public override string ToString()
207 {
208 return number.ToString();
209 }
210
211 public bool equals(Card s)
212 {
213 return this.value == s.value && this.suit.Equals(s.suit);
214 }
215 }测试用例:
1 namespace Solution
2 {
3 using NUnit.Framework;
4 using System;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Linq;
8 using System.Text;
9
10
11 public class SolutionTest
12 {
13 #region Sample Tests
14
15
16 public void FixedTests()
17 {
18 SampleTest(("nothing", new[] { "A", "K", "Q", "J", "9" }),new[] { "K♠", "A♦" },new[] { "J♣", "Q♥", "9♥", "2♥", "3♦" });
19 SampleTest(("pair", new[] { "Q", "K", "J", "9" }), new[] { "K♠", "Q♦" },new[] { "J♣", "Q♥", "9♥", "2♥", "3♦" });
20 SampleTest(("two pair", new[] { "K", "J", "9" }), new[] { "K♠", "J♦" },new[] { "J♣", "K♥", "9♥", "2♥", "3♦" });
21 SampleTest(("three-of-a-kind", new[] { "Q", "J", "9" }), new[] { "4♠", "9♦" },new[] { "J♣", "Q♥", "Q♠", "2♥", "Q♦" });
22 SampleTest(("straight", new[] { "K", "Q", "J", "10", "9" }), new[] { "Q♠", "2♦" },new[] { "J♣", "10♥", "9♥", "K♥", "3♦" });
23 SampleTest(("flush", new[] { "Q", "J", "10", "5", "3" }), new[] { "A♠", "K♦" },new[] { "J♥", "5♥", "10♥", "Q♥", "3♥" });
24 SampleTest(("full house", new[] { "A", "K" }), new[] { "A♠", "A♦" },new[] { "K♣", "K♥", "A♥", "Q♥", "3♦" });
25 SampleTest(("four-of-a-kind",new[] { "2", "3" }), new[] { "2♠", "3♦" },new[] { "2♣", "2♥", "3♠", "3♥", "2♦" });
26 SampleTest(("straight-flush",new[] { "J", "10", "9", "8", "7" }), new[] { "8♠", "6♠" },new[] { "7♠", "5♠", "9♠", "J♠", "10♠" });
27 }
28
29 private static void SampleTest((string type, string[] ranks) expected, string[] holeCards, string[] communityCards)
30 {
31 var actual = Act(holeCards, communityCards);
32 Verify(expected, actual, holeCards, communityCards);
33 }
34
35 #endregion
36
37 private static readonly StringBuilder template = new StringBuilder();
38 private static readonly StringBuilder buffer = new StringBuilder();
39 private static readonly string[] ranks = new string[] { "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3", "2" };
40 private static readonly string[] types = new string[] { "straight-flush", "four-of-a-kind", "full house", "flush", "straight", "three-of-a-kind", "two pair", "pair", "nothing" };
41 private static readonly Dictionary<string, int> ranksLookup = ranks.ToDictionary(x => x, x => Array.FindIndex(ranks, y => y == x));
42 private static string Show(string str) => $@"""{str}""";
43 private static string ShowSeq(IEnumerable<string> seq) => $"[{string.Join(", ", seq.Select(Show))}]";
44 private static (string type, string[] ranks) Act(string[] holeCards, string[] communityCards) => Edm.Hand(holeCards.Select(m=>m).ToArray(), communityCards.Select(m=>m).ToArray());
45
46 private static string Error(string message)
47 {
48 buffer.Clear();
49 buffer.Append(template.ToString());
50 buffer.AppendLine($"Error: {message}");
51 return buffer.ToString();
52 }
53
54 private static void Verify(
55 (string type, string[] ranks) expected, (string type, string[] ranks) actual, string[] holeCards, string[] communityCards)
56 {
57 Debug.Assert(holeCards.Concat(communityCards).Distinct().Count() == 7, "Invalid input");
58 template.Clear();
59 template.AppendLine($"\tHole cards: {ShowSeq(holeCards)}");
60 template.AppendLine($"\tCommunity cards: {ShowSeq(communityCards)}");
61 template.AppendLine($"Expected: (type: {Show(expected.type)}, ranks: {ShowSeq(expected.ranks)})");
62 Assert.IsNotNull(actual.type, Error("Type must not be null"));
63 Assert.IsNotNull(actual.ranks, Error("Ranks must not be null"));
64 template.AppendLine($"Actual: (type: {Show(actual.type)}, ranks: {ShowSeq(actual.ranks)})");
65 Assert.IsTrue(types.Any(x => string.Equals(x, actual.type)),
66 Error($"{Show(actual.type)} is not valid, valid options are: {ShowSeq(types)}"));
67 Assert.AreEqual(expected.type, actual.type, Error("Type is incorrect"));
68 Assert.AreEqual(expected.ranks.Length, actual.ranks.Length, Error("Number of ranks is incorrect"));
69 for (var i = 0; i < expected.ranks.Length; i++)
70 Assert.IsTrue(ranks.Any(x => string.Equals(x, actual.ranks)),
71 Error($"{Show(actual.ranks)} is not valid, valid options are: {ShowSeq(ranks)}"));
72 for (var i = 0; i < expected.ranks.Length; i++)
73 Assert.AreEqual(expected.ranks, actual.ranks, Error($"Rank at position {i} is incorrect"));
74 }
75
76 #region Test Cases
77
78 private static readonly string[] suits = new string[] { "♠", "♦", "♥", "♣" };
79 private static Dictionary<string, int> stats = new Dictionary<string, int>();
80
81
82 public void FixedEdgeCaseTests()
83 {
84 // ace low straight invalidated
85 SampleTest(("nothing", new[] { "A", "8", "7", "5", "4" }), new[] { "A♠", "2♦" }, new[] { "3♣", "4♥", "5♥", "7♥", "8♦" });
86 // non straight around
87 SampleTest(("nothing", new[] { "A", "K", "8", "7", "4" }), new[] { "A♠", "K♦" }, new[] { "3♣", "4♥", "2♥", "7♥", "8♦" });
88
89 // pair on board
90 SampleTest(("pair", new[] { "4", "A", "9", "7" }), new[] { "A♠", "2♦" }, new[] { "3♣", "4♥", "9♥", "7♥", "4♦" });
91 // pair made with 1 hole card
92 SampleTest(("pair", new[] { "4", "A", "10", "9" }), new[] { "A♠", "4♦" }, new[] { "3♣", "4♥", "9♥", "7♥", "10♦" });
93 // pair made with 2 hole cards
94 SampleTest(("pair", new[] { "4", "A", "10", "9" }), new[] { "4♠", "4♦" }, new[] { "3♣", "A♥", "9♥", "7♥", "10♦" });
95
96 // two pair on board
97 SampleTest(("two pair", new[] { "Q", "2", "K" }), new[] { "K♠", "J♦" }, new[] { "Q♣", "Q♥", "9♥", "2♥", "2♦" });
98 // two pair made with 1 hole card and 1 pair on board
99 SampleTest(("two pair", new[] { "Q", "2", "K" }), new[] { "K♠", "Q♦" }, new[] { "J♣", "Q♥", "9♥", "2♥", "2♦" });
100 // two pair made with 2 hole cards
101 SampleTest(("two pair", new[] { "Q", "2", "K" }), new[] { "2♠", "Q♦" }, new[] { "J♣", "Q♥", "9♥", "2♥", "K♦" });
102 // two pair made with pair in hole cards and 1 pair on board
103 SampleTest(("two pair", new[] { "Q", "2", "K" }), new[] { "Q♠", "Q♦" }, new[] { "K♣", "J♥", "9♥", "2♥", "2♦" });
104 // two pair made with 2 hole cards, invalidating a 3th pair on board
105 SampleTest(("two pair", new[] { "K", "J", "9" }), new[] { "K♠", "J♦" }, new[] { "J♣", "K♥", "9♥", "2♥", "2♦" });
106
107 // three-of-a-kind on board
108 SampleTest(("three-of-a-kind", new[] { "Q", "K", "J" }), new[] { "K♠", "J♦" }, new[] { "Q♣", "Q♥", "9♥", "2♥", "Q♦" });
109 // three-of-a-kind made with 1 hole card and 1 pair on board
110 SampleTest(("three-of-a-kind", new[] { "Q", "K", "J" }), new[] { "K♠", "Q♦" }, new[] { "Q♣", "Q♥", "9♥", "2♥", "J♦" });
111 // three-of-a-kind made with 2 hole cards
112 SampleTest(("three-of-a-kind", new[] { "Q", "K", "J" }), new[] { "Q♣", "Q♦" }, new[] { "K♠", "Q♥", "9♥", "2♥", "J♦" });
113
114 // board straight cancels out pocket aces
115 SampleTest(("straight", new[] { "A", "K", "Q", "J", "10" }), new[] { "A♥", "A♠" }, new[] { "A♣", "K♥", "Q♥", "J♥", "10♦" });
116 // super straight
117 SampleTest(("straight", new[] { "A", "K", "Q", "J", "10" }), new[] { "A♠", "Q♥" }, new[] { "K♥", "10♠", "J♠", "9♠", "8♦" });
118 // high straight
119 SampleTest(("straight", new[] { "7", "6", "5", "4", "3" }), new[] { "6♠", "7♥" }, new[] { "3♥", "4♠", "5♠", "10♠", "10♦" });
120 // low straight
121 SampleTest(("straight", new[] { "6", "5", "4", "3", "2" }), new[] { "2♠", "3♥" }, new[] { "4♥", "5♠", "6♠", "10♠", "10♦" });
122 // outside straight
123 SampleTest(("straight", new[] { "6", "5", "4", "3", "2" }), new[] { "2♠", "6♥" }, new[] { "4♥", "5♠", "3♠", "10♠", "10♦" });
124 // inside straight
125 SampleTest(("straight", new[] { "6", "5", "4", "3", "2" }), new[] { "4♠", "3♥" }, new[] { "2♥", "5♠", "6♠", "10♠", "10♦" });
126 // interspersed straight
127 SampleTest(("straight", new[] { "6", "5", "4", "3", "2" }), new[] { "4♠", "2♥" }, new[] { "3♥", "5♠", "6♠", "10♠", "10♦" });
128
129 // seven deuce runner runner
130 SampleTest(("full house", new[] { "2", "7" }), new[] { "7♥", "2♠" }, new[] { "A♣", "K♥", "2♦", "7♣", "2♥" });
131 // full house with 2 pairs on board where pockets make the triple
132 SampleTest(("full house", new[] { "A", "K" }), new[] { "A♠", "A♦" }, new[] { "K♣", "K♥", "A♥", "Q♥", "Q♦" });
133 // full house with 1 pair on board where pockets make the triple
134 SampleTest(("full house", new[] { "A", "K" }), new[] { "A♠", "A♦" }, new[] { "K♣", "K♥", "A♥", "J♥", "Q♦" });
135 // full house with 1 hole card making triple and other making pair
136 SampleTest(("full house", new[] { "K", "A" }), new[] { "A♠", "K♦" }, new[] { "K♣", "K♥", "A♥", "J♥", "Q♦" });
137 // full house with better triple than board
138 SampleTest(("full house", new[] { "A", "K" }), new[] { "A♠", "A♦" }, new[] { "K♣", "K♥", "A♥", "Q♥", "K♦" });
139
140 // flush and straight combo
141 SampleTest(("flush", new[] { "J", "10", "9", "8", "6" }), new[] { "8♠", "6♠" }, new[] { "7♦", "5♠", "9♠", "J♠", "10♠" });
142 // power flush
143 SampleTest(("flush", new[] { "A", "K", "Q", "J", "9" }), new[] { "A♠", "Q♠" }, new[] { "K♠", "4♠", "J♠", "9♠", "3♠" });
144
145 // four-of-a-kind on board
146 SampleTest(("four-of-a-kind", new[] { "A", "K" }), new[] { "K♠", "9♥" }, new[] { "A♥", "A♣", "A♠", "A♦", "3♥" });
147 // four-of-a-kind with 1 hole card and triple on board
148 SampleTest(("four-of-a-kind", new[] { "A", "K" }), new[] { "K♠", "A♥" }, new[] { "9♥", "A♣", "A♠", "A♦", "3♥" });
149 // carré
150 SampleTest(("four-of-a-kind", new[] { "A", "K" }), new[] { "A♠", "A♦" }, new[] { "A♥", "A♣", "K♠", "9♥", "3♥" });
151
152 // royal flush
153 SampleTest(("straight-flush", new[] { "A", "K", "Q", "J", "10" }), new[] { "A♠", "Q♠" }, new[] { "K♠", "10♠", "J♠", "9♠", "3♦" });
154
155 // regression tests
156 SampleTest(("straight", new[] { "6", "5", "4", "3", "2" }), new[] { "3♠", "4♥" }, new[] { "6♣", "5♠", "2♣", "2♦", "3♦" });
157 SampleTest(("straight", new[] { "10", "9", "8", "7", "6" }), new[] { "6♣", "10♠" }, new[] { "9♠", "8♦", "5♦", "7♥", "9♦" });
158 SampleTest(("straight", new[] { "K", "Q", "J", "10", "9" }), new[] { "2♦", "J♦" }, new[] { "Q♥", "9♠", "K♥", "10♥", "J♥" });
159 }
160
161
162 public void RandomBatch1Tests()
163 {
164 var rand = new Random((int)(DateTime.Now.Ticks % int.MaxValue));
165 var bulkSize = 500;
166 for (var i = 0; i < bulkSize; i++)
167 {
168 var hand = GenerateRandomHand(rand);
169 var holeCards = hand.Take(2).ToArray();
170 var communityCards = hand.Skip(2).ToArray();
171 Test(holeCards, communityCards);
172 }
173 }
174
175
176 public void RandomBatch2Tests()
177 {
178 var rand = new Random((int)(DateTime.Now.Ticks % int.MaxValue));
179 var bulkSize = 500;
180 for (var i = 0; i < bulkSize; i++)
181 {
182 do
183 {
184 var hand = GenerateRandomHand(rand);
185 var holeCards = hand.Take(2).ToArray();
186 var communityCards = hand.Skip(2).ToArray();
187 var expected = Expect(holeCards, communityCards);
188
189 if (new[] { "nothing", "pair", "two pair", "three-of-a-kind" }.Contains(expected.type))
190 {
191 continue;
192 }
193 else
194 {
195 Test(holeCards, communityCards);
196 break;
197 }
198 } while (true);
199 }
200 }
201
202
203 public void RandomBatch3Tests()
204 {
205 var rand = new Random((int)(DateTime.Now.Ticks % int.MaxValue));
206 var hands = new List<string[]>();
207 var batchSize = 100;
208 for (var i = 0; i < batchSize; i++) hands.Add(GenerateStraightFlush(rand));
209 for (var i = 0; i < batchSize; i++) hands.Add(GenerateFourOfAKind(rand));
210 for (var i = 0; i < batchSize; i++) hands.Add(GenerateFullHouse(rand));
211 for (var i = 0; i < batchSize; i++) hands.Add(GenerateFlush(rand));
212 for (var i = 0; i < batchSize; i++) hands.Add(GenerateStraight(rand));
213 hands = hands.Select(x => x.OrderBy(y => rand.Next()).ToArray()).OrderBy(x => rand.Next()).ToList();
214 foreach (var hand in hands)
215 {
216 var holeCards = hand.Take(2).ToArray();
217 var communityCards = hand.Skip(2).ToArray();
218 Test(holeCards, communityCards);
219 }
220 }
221
222 private static Dictionary<int, (string rank, string suit, int id)> Deck()
223 {
224 var id = 0;
225 var hand = new List<string>();
226 return (from suit in suits
227 from rank in ranks
228 select (rank, suit, id: id++)).ToDictionary(x => x.id);
229 }
230
231 private static void RemoveSuit(Dictionary<int, (string rank, string suit, int id)> deck, int suit)
232 {
233 var list = deck.Values.Where(card => card.id / ranks.Length == suit).ToList();
234 foreach (var card in list)
235 {
236 deck.Remove(card.id);
237 }
238 }
239
240 private static void RemoveRank(Dictionary<int, (string rank, string suit, int id)> deck, int rank)
241 {
242 var list = deck.Values.Where(card => card.id % ranks.Length == rank).ToList();
243 foreach (var card in list)
244 {
245 deck.Remove(card.id);
246 }
247 }
248
249 private static (string rank, string suit, int id) RandomCard(Dictionary<int, (string rank, string suit, int id)> deck, Random rand)
250 {
251 return deck.Skip(rand.Next(0, deck.Count)).First().Value;
252 }
253
254 private static string[] GenerateRandomHand(Random rand)
255 {
256 var hand = new List<string>();
257 var deck = Deck();
258
259 while (hand.Count < 7)
260 {
261 var next = RandomCard(deck, rand);
262 deck.Remove(next.id);
263 hand.Add($"{next.rank}{next.suit}");
264 }
265
266 return hand.ToArray();
267 }
268
269 private static string[] GenerateStraightFlush(Random rand)
270 {
271 var hand = new List<string>();
272 var deck = Deck();
273 var suit = rand.Next(0, suits.Length);
274 var rank = rand.Next(0, ranks.Length - 5);
275 var head = suit * ranks.Length + rank;
276 // 5 cards make the straight flush
277 for (var i = 0; i < 5; i++)
278 {
279 var current = head + i;
280 var card = deck;
281 deck.Remove(current);
282 hand.Add($"{card.rank}{card.suit}");
283 }
284 // any 2 other cards may be added
285 for (var i = 0; i < 2; i++)
286 {
287 var card = RandomCard(deck, rand);
288 deck.Remove(card.id);
289 hand.Add($"{card.rank}{card.suit}");
290 }
291 return hand.ToArray();
292 }
293
294 private static string[] GenerateFourOfAKind(Random rand)
295 {
296 var hand = new List<string>();
297 var deck = Deck();
298 var rank = rand.Next(0, ranks.Length);
299 var head = rank;
300 // 4 cards make the four-of-a-kind
301 for (var i = 0; i < 4; i++)
302 {
303 var current = head + i * ranks.Length;
304 var card = deck;
305 deck.Remove(current);
306 hand.Add($"{card.rank}{card.suit}");
307 }
308 // any 3 other cards may be added
309 for (var i = 0; i < 3; i++)
310 {
311 var card = RandomCard(deck, rand);
312 deck.Remove(card.id);
313 hand.Add($"{card.rank}{card.suit}");
314 }
315 return hand.ToArray();
316 }
317
318 private static string[] GenerateFullHouse(Random rand)
319 {
320 var hand = new List<string>();
321 var deck = Deck();
322 var rank = rand.Next(0, ranks.Length);
323 var head = rank;
324 // 3 cards make the triple
325 for (var i = 0; i < 3; i++)
326 {
327 var current = head + i * ranks.Length;
328 var card = deck;
329 deck.Remove(current);
330 hand.Add($"{card.rank}{card.suit}");
331 }
332 // remaining rank would result in a four-of-a-kind
333 RemoveRank(deck, rank);
334 // 2 cards make a pair
335 var rank2 = Array.IndexOf(ranks, RandomCard(deck, rand).rank);
336 var head2 = rank2;
337 for (var i = 0; i < 2; i++)
338 {
339 var current = head2 + i * ranks.Length;
340 var card = deck;
341 deck.Remove(current);
342 hand.Add($"{card.rank}{card.suit}");
343 }
344 // remaining rank would result in a three-of-a-kind
345 RemoveRank(deck, rank2);
346 // any 2 other cards may be added
347 for (var i = 0; i < 2; i++)
348 {
349 var card = RandomCard(deck, rand);
350 deck.Remove(card.id);
351 hand.Add($"{card.rank}{card.suit}");
352 }
353 return hand.ToArray();
354 }
355
356 private static string[] GenerateFlush(Random rand)
357 {
358 var hand = new List<string>();
359 var deck = Deck();
360 var primaryDeck = Deck();
361 var suit = rand.Next(0, suits.Length);
362 for (var i = 0; i < 4; i++)
363 {
364 if (i != suit) RemoveSuit(primaryDeck, i);
365 }
366 // 5 cards make a flush
367 for (var i = 0; i < 5; i++)
368 {
369 var card = RandomCard(primaryDeck, rand);
370 primaryDeck.Remove(card.id);
371 deck.Remove(card.id);
372 hand.Add($"{card.rank}{card.suit}");
373 }
374 // any 2 other cards may be added
375 // small chance on straight flush, but that's ok
376 for (var i = 0; i < 2; i++)
377 {
378 var card = RandomCard(deck, rand);
379 deck.Remove(card.id);
380 hand.Add($"{card.rank}{card.suit}");
381 }
382 return hand.ToArray();
383 }
384
385 private static string[] GenerateStraight(Random rand)
386 {
387 var hand = new List<string>();
388 var deck = Deck();
389 var rank = rand.Next(0, ranks.Length - 5);
390 var head = rank;
391 // 5 cards make the straight
392 for (var i = 0; i < 5; i++)
393 {
394 var suit = rand.Next(0, suits.Length);
395 var current = head + i + suit * ranks.Length;
396 var card = deck;
397 deck.Remove(current);
398 hand.Add($"{card.rank}{card.suit}");
399 }
400 // any 2 other cards may be added
401 // small chance on straight flush, but that's ok
402 for (var i = 0; i < 2; i++)
403 {
404 var card = RandomCard(deck, rand);
405 deck.Remove(card.id);
406 hand.Add($"{card.rank}{card.suit}");
407 }
408 return hand.ToArray();
409 }
410
411 private static void Test(string[] holeCards, string[] communityCards)
412 {
413 var expected = Expect(holeCards, communityCards);
414 var actual = Act(holeCards, communityCards);
415 Verify(expected, actual, holeCards, communityCards);
416 if (!stats.TryGetValue(expected.type, out var cnt)) cnt = 0;
417 stats = cnt + 1;
418 }
419
420 private static (string type, string[] ranks) Expect(string[] holeCards, string[] communityCards)
421 {
422 var cards = holeCards.Concat(communityCards).Select(Parse).OrderBy(x => ranksLookup).ToArray();
423 var cardsByRank = cards.ToLookup(x => x.rank);
424 var cardsBySuit = cards.ToLookup(x => x.suit);
425 var ans = findStraightFlush();
426 if (ans == null) ans = findFourOfAKind();
427 if (ans == null) ans = findFullHouse();
428 if (ans == null) ans = findFlush();
429 if (ans == null) ans = findStraight();
430 if (ans == null) ans = findThreeOfAKind();
431 if (ans == null) ans = findTwoPair();
432 if (ans == null) ans = findPair();
433 if (ans == null) ans = findNothing();
434 return ans.GetValueOrDefault(default);
435
436 (string rank, string suit) Parse(string card) => (card.Substring(0, card.Length - 1), card.Substring(card.Length - 1, 1));
437
438 (string type, string[] ranks)? findStraightFlush()
439 {
440 var flush = cardsBySuit.SingleOrDefault(x => x.Count() >= 5)?.ToArray();
441 if (flush == null) return null;
442 for (var i = 0; i + 4 < flush.Length; i++)
443 {
444 var match = true;
445 for (var j = 1; j <= 4; j++)
446 {
447 if (!flush.Any(card => ranksLookup == ranksLookup.rank] + j))
448 {
449 match = false;
450 break;
451 }
452 }
453 if (match) return ("straight-flush", Enumerable.Range(0, 5).Select(k => ranks.rank]]).ToArray());
454 }
455 return null;
456 }
457
458 (string type, string[] ranks)? findFourOfAKind()
459 {
460 var t4_cards = cardsByRank.SingleOrDefault(x => x.Count() == 4);
461 if (t4_cards == null) return null;
462 var t4 = t4_cards.First().rank;
463 var h1 = cardsByRank.First(x => x.Key != t4).Key;
464 return ("four-of-a-kind", new[] { t4, h1 });
465 }
466
467 (string type, string[] ranks)? findFullHouse()
468 {
469 var t3_set = cardsByRank.Where(x => x.Count() == 3);
470 if (!t3_set.Any()) return null;
471 var t3 = t3_set.First().First().rank;
472 var t2_ranks = cardsByRank.Where(x => x.Count() == 2).Select(x => x.Key).ToList();
473 if (t3_set.Count() > 1) t2_ranks.Add(t3_set.Skip(1).First().Key);
474 if (!t2_ranks.Any()) return null;
475 var t2 = t2_ranks.OrderBy(x => ranksLookup).First();
476 return ("full house", new[] { t3, t2 });
477 }
478
479 (string type, string[] ranks)? findFlush()
480 {
481 var flush = cardsBySuit.SingleOrDefault(x => x.Count() >= 5)?.ToArray();
482 if (flush == null) return null;
483 return ("flush", flush.Take(5).Select(x => x.rank).ToArray());
484 }
485
486 (string type, string[] ranks)? findStraight()
487 {
488 for (var i = 0; i + 4 < cards.Length; i++)
489 {
490 var match = true;
491 for (var j = 1; j <= 4; j++)
492 {
493 if (!cards.Any(card => ranksLookup == ranksLookup.rank] + j))
494 {
495 match = false;
496 break;
497 }
498 }
499 if (match) return ("straight", Enumerable.Range(0, 5).Select(k => ranks.rank]]).ToArray());
500 }
501 return null;
502 }
503
504 (string type, string[] ranks)? findThreeOfAKind()
505 {
506 var t3_cards = cardsByRank.SingleOrDefault(x => x.Count() == 3);
507 if (t3_cards == null) return null;
508 var t3 = t3_cards.First().rank;
509 var h1 = cardsByRank.First(x => x.Key != t3).Key;
510 var h2 = cardsByRank.First(x => x.Key != t3 && x.Key != h1).Key;
511 return ("three-of-a-kind", new[] { t3, h1, h2 });
512 }
513
514 (string type, string[] ranks)? findTwoPair()
515 {
516 var t2_set = cardsByRank.Where(x => x.Count() == 2);
517 if (t2_set.Count() < 2) return null;
518 var t2_high = t2_set.First().First().rank;
519 var t2_low = t2_set.Skip(1).First().First().rank;
520 var h1 = cardsByRank.First(x => x.Key != t2_high && x.Key != t2_low).Key;
521 return ("two pair", new[] { t2_high, t2_low, h1 });
522 }
523
524 (string type, string[] ranks)? findPair()
525 {
526 var t2_cards = cardsByRank.SingleOrDefault(x => x.Count() == 2);
527 if (t2_cards == null) return null;
528 var t2 = t2_cards.First().rank;
529 var h1 = cardsByRank.First(x => x.Key != t2).Key;
530 var h2 = cardsByRank.First(x => x.Key != t2 && x.Key != h1).Key;
531 var h3 = cardsByRank.First(x => x.Key != t2 && x.Key != h1 && x.Key != h2).Key;
532 return ("pair", new[] { t2, h1, h2, h3 });
533 }
534
535 (string type, string[] ranks) findNothing()
536 {
537 return ("nothing", cards.Take(5).Select(x => x.rank).ToArray());
538 }
539 }
540
541 #endregion
542 }
543 }
来源:https://www.cnblogs.com/lan80/archive/2023/08/07/17610735.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页:
[1]