翼度科技»论坛 编程开发 .net 查看内容

【算法】用c#实现德州扑克卡牌游戏规则

5

主题

5

帖子

15

积分

新手上路

Rank: 1

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

来源:https://www.cnblogs.com/lan80/archive/2023/08/07/17610735.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具