/// <summary> /// Returns an array containing all possible bit masks with active players. /// </summary> /// <param name="totalCount">Total number of players</param> /// <param name="minCount">Inclusive minimal number of active players</param> /// <param name="maxCount">Inclusive maximal number of active players</param> /// <returns></returns> public static UInt16[] Get(int totalCount, int minCount, int maxCount) { if (totalCount < minCount || totalCount < maxCount) { throw new ArgumentOutOfRangeException("Total count must be >= than min and max counts"); } int count = 0; for (int i = minCount; i <= maxCount; ++i) { count += (int)EnumAlgos.CountCombin(totalCount, i); } UInt16[] result = new ushort[count]; // Use a slow but simple algorithm UInt16 maxMask = (UInt16)((1 << totalCount) - 1); count = 0; for (UInt16 mask = 0; mask <= maxMask; ++mask) { int bitCount = CountBits.Count(mask); if (minCount <= bitCount && bitCount <= maxCount) { result[count++] = mask; } } return(result); }
public void Test_CountCombin() { Assert.AreEqual(1, EnumAlgos.CountCombin(0, 0)); Assert.AreEqual(1, EnumAlgos.CountCombin(1, 0)); Assert.AreEqual(2, EnumAlgos.CountCombin(2, 1)); Assert.AreEqual(1326, EnumAlgos.CountCombin(52, 2)); Assert.AreEqual(133784560, EnumAlgos.CountCombin(52, 7)); }
public void Test_Random() { int REPETITIONS = 100; #if DEBUG REPETITIONS = 8; #endif int seed = (int)DateTime.Now.Ticks; Console.WriteLine("Random seed {0}", seed); Random rand = new Random(seed); _cardRng = new SequenceRng(seed); _cardRng.SetSequence(StdDeck.Descriptor.FullDeckIndexes); for (int r = 0; r < REPETITIONS; ++r) { _enumCount = r % 7; int sharedCount = rand.Next(0, StdDeck.Descriptor.Size + 1 - _enumCount); int deadCount = rand.Next(0, StdDeck.Descriptor.Size + 1 - sharedCount - _enumCount); _cardRng.Shuffle(sharedCount + deadCount); int rc = 0; _shared = new CardSet(); for (int i = 0; i < sharedCount; ++i) { _shared |= StdDeck.Descriptor.CardSets[_cardRng.Sequence[rc++]]; } _dead = new CardSet(); for (int i = 0; i < deadCount; ++i) { _dead |= StdDeck.Descriptor.CardSets[_cardRng.Sequence[rc++]]; } Debug.Assert(rc == sharedCount + deadCount); Debug.Assert(!_shared.IsIntersectingWith(_dead)); //Console.WriteLine("B: {0:x16} D:{1:x16}", board, dead); _combinationsCount = 0; _lastCs = 0; CardEnum.Combin(StdDeck.Descriptor, _enumCount, _shared, _dead, VerifyCombination); Assert.AreEqual(EnumAlgos.CountCombin(StdDeck.Descriptor.Size - sharedCount - deadCount, _enumCount), _combinationsCount); _combinationsCount1 = 0; _lastCs1 = 0; CardEnum.Combin(StdDeck.Descriptor, _enumCount, _shared, _dead, VerifyCombinationParam, _combinationsCount); Assert.AreEqual(EnumAlgos.CountCombin(StdDeck.Descriptor.Size - sharedCount - deadCount, _enumCount), _combinationsCount1); _combinationsCount1 = 0; _lastCs1 = 0; int[] cards = new int[_enumCount + sharedCount].Fill(-1); StdDeck.Descriptor.GetIndexesAscending(_shared).ToArray().CopyTo(cards, 0); int[] deadIdx = StdDeck.Descriptor.GetIndexesAscending(_shared | _dead).ToArray(); CardEnum.Combin(StdDeck.Descriptor, _enumCount, cards, sharedCount, deadIdx, deadIdx.Length, VerifyCombinationParam, _combinationsCount); Assert.AreEqual(EnumAlgos.CountCombin(StdDeck.Descriptor.Size - sharedCount - deadCount, _enumCount), _combinationsCount1); } Console.WriteLine("{0} repetitions done.", REPETITIONS); }
public static float Calculate(CardSet board) { CalulateParam param = new CalulateParam(); int boardSize = board.CountCards(); int toDeal = 7 - boardSize; CardEnum.Combin(StdDeck.Descriptor, toDeal, board, CardSet.Empty, OnDeal, param); return((float)(param.Sum / EnumAlgos.CountCombin(52 - boardSize, toDeal))); }
/// <summary> /// Returns an array containing enumerated combinations. /// </summary> public static CardSet[] Combin(DeckDescriptor deckDescr, int count, CardSet sharedCards, CardSet deadCards) { CardSet unused = deadCards | sharedCards; int combCount = (int)EnumAlgos.CountCombin(deckDescr.Size - unused.CountCards(), count); CombinArrayParams p = new CombinArrayParams(); p.arr = new CardSet[combCount]; Combin(deckDescr, count, sharedCards, deadCards, OnCombinArray, p); return(p.arr); }
public void Test_CountCombinations_Idx() { int[] cards = new int[7]; for (int i = 0; i <= 6; ++i) { _combinationsCount1 = 0; CardEnum.Combin(StdDeck.Descriptor, i, cards, 0, null, 0, CountCombinationsParam, 33); Assert.AreEqual(EnumAlgos.CountCombin(52, i), _combinationsCount1); } }
double CalculateAverageHVO(int[] board) { CardSet boardCs = StdDeck.Descriptor.GetCardSet(board); CalculateAverageHsParam param = new CalculateAverageHsParam(); int toDealCount = 7 - board.Length; CardEnum.Combin(StdDeck.Descriptor, toDealCount, boardCs, CardSet.Empty, OnPocket, param); Assert.AreEqual(EnumAlgos.CountCombin(52 - board.Length, toDealCount), param.Count); return(param.Sum / param.Count); }
public void Omaha() { Reset(); Console.WriteLine("Calculate number of chance nodes from 1 player perspective for Omaha"); CardEnum.Combin(StdDeck.Descriptor, 4, CardSet.Empty, CardSet.Empty, SuitIsomorphismPreflopOmaha); Console.WriteLine("Preflop Chance Nodes: colored: {0}, color-isomorphic: {1} {2:0.##}%", _pocketCount, _normPreflopCount, 100.0 * _normPreflopCount / _pocketCount); Assert.AreEqual(EnumAlgos.CountCombin(52, 4), _pocketCount); // Value verified in Wiki, etc. Assert.AreEqual(16432, _normPreflopCount); }
public DealRecord(GameRecord cfg, DeckDescriptor deckDescr, SequenceRng randomDealer) { _cfg = cfg; _deckDescr = deckDescr; _randomDealer = randomDealer; // Loop through actions and // - find the fixed cards // - count random cards // - count enumerated cards for (int a = 0; a < _cfg.Actions.Count; ++a) { PokerAction action = _cfg.Actions[a]; if (!action.IsDealerAction()) { continue; } string[] cards = action.Cards.Split(_separator, StringSplitOptions.RemoveEmptyEntries); foreach (string card in cards) { if (card == "?") { _randomCount++; } else if (card.Length > 0 && card.Substring(0, 1) == "#") { int idx = int.Parse(card.Substring(1)); while (_enumCounts.Count <= idx) { _enumCounts.Add(0); _enumCombosCounts.Add(0); _enumCombos.Add(new List <CardSet>()); } _enumCounts[idx]++; } else { _fixedCards.UnionWith(_deckDescr.GetCardSet(card)); } } } // Count enumerated combinations. int deadCards = _fixedCards.CountCards(); for (int i = 0; i < _enumCounts.Count; ++i) { _enumCombosCounts[i] = EnumAlgos.CountCombin(_deckDescr.Size - deadCards, _enumCounts[i]); deadCards += _enumCounts[i]; } }
public void Test_CountCombinations_CardSet() { for (int i = 0; i <= 6; ++i) { _combinationsCount = 0; CardEnum.Combin(StdDeck.Descriptor, i, CardSet.Empty, CardSet.Empty, CountCombinations); Assert.AreEqual(EnumAlgos.CountCombin(52, i), _combinationsCount); _combinationsCount1 = 0; CardEnum.Combin(StdDeck.Descriptor, i, CardSet.Empty, CardSet.Empty, CountCombinationsParam, _combinationsCount); Assert.AreEqual(EnumAlgos.CountCombin(52, i), _combinationsCount1); } }
public void GenerateStates(int maxHandSize) { DateTime startTime = DateTime.Now; _maxHandSize = maxHandSize; _cardsToState = new CardsToState[_maxHandSize][]; for (int i = 0; i < _maxHandSize; ++i) { _cardsToState[i] = new CardsToState[EnumAlgos.CountCombin(52, i)]; } for (int i = 0; i < _maxHandSize; ++i) { _combinCount = 0; GenerateCombin(CardSet.Empty, 51, 0, i, OnCombinCreateStates); Debug.Assert(_combinCount == _cardsToState[i].Length); Console.WriteLine("{0}-hands generated", i); } Console.WriteLine("{0:#,#} states created", _states.Count); if (_maxHandSize == 7) { // Print some statistics about 7-hands. int max6EquivStatesCount = -1; int nonUniqueKeyCount = 0; foreach (List <State> l in _dict6.Values) { max6EquivStatesCount = Math.Max(max6EquivStatesCount, l.Count); if (l.Count > 1) { nonUniqueKeyCount++; } } Console.WriteLine("Number of non-unique 6-hands keys: {0}", nonUniqueKeyCount); Console.WriteLine("Max. number of equivalent 6-hands with same key: {0}", max6EquivStatesCount); } for (int i = 0; i < _maxHandSize; ++i) { _combinCount = 0; GenerateCombin(CardSet.Empty, 51, 0, i, OnCombinLinkStates); Debug.Assert(_combinCount == _cardsToState[i].Length); Console.WriteLine("{0}-hands linked", i); } double runTime = (DateTime.Now - startTime).TotalSeconds; Console.WriteLine("Generated in {0:0.000} seconds", runTime); }
/// <summary> /// Counts number of rank-equivalent hands that are comprised from the same cards /// ranks as the given hand, fully ignoring suits. /// For example, for AcKc or AsKh returns 16, for 2s2d returns 6. /// There must be no dead cards. /// </summary> /// <remarks>Works even if a deck contains different number of suits (<=4) for different ranks. /// </remarks> public static int CountEquiv(CardSet hand, DeckDescriptor deck) { int count = 1; for (int r = 0; r < 16; ++r) { UInt64 handRanks = hand.bits & _rankMasks[r]; UInt64 allRanks = deck.FullDeck.bits & _rankMasks[r]; int handRanksCount = CountBits.Count(handRanks); int allRanksCount = CountBits.Count(allRanks); int rankCount = (int)EnumAlgos.CountCombin(allRanksCount, handRanksCount); count *= rankCount; } return(count); }
public void Benchmark_Showdown() { HoldemGameRules gr = new HoldemGameRules(); int [][] hands = new int[2][]; UInt32[] ranks = new UInt32[2]; for (int p = 0; p < 2; ++p) { hands[p] = new int[7]; } hands[0][0] = 0; hands[0][1] = 1; hands[1][0] = 2; hands[1][1] = 3; // Force loading LUT UInt32 checksum = LutEvaluator7.Evaluate(0, 1, 2, 3, 4, 5, 6); DateTime startTime = DateTime.Now; int count = 0; for (int b0 = 4; b0 < 52 - 4; ++b0) { hands[0][2] = hands[1][2] = b0; for (int b1 = b0 + 1; b1 < 52 - 3; ++b1) { hands[0][3] = hands[1][3] = b1; for (int b2 = b1 + 1; b2 < 52 - 2; ++b2) { hands[0][4] = hands[1][4] = b2; for (int b3 = b2 + 1; b3 < 52 - 1; ++b3) { hands[0][5] = hands[1][5] = b3; for (int b4 = b3 + 1; b4 < 52 - 0; ++b4) { hands[0][6] = hands[1][6] = b4; gr.Showdown(_gd, hands, ranks); checksum += ranks[0] + ranks[1]; count++; } } } } } double runTime = (DateTime.Now - startTime).TotalSeconds; Assert.AreEqual(EnumAlgos.CountCombin(52 - 4, 5), count); Console.WriteLine("Showdown for 2 players, {0:#,#} hands, {1:#,#} h/s, time: {2:0.000} s, checksum: {3}", count, count / runTime, runTime, checksum); }
/// <summary> /// Precalculate and store tables. If the output already exists, will not overwrite. /// <remarks>Long-running. </remarks> /// </summary> /// <param name="round">Round (0, 1 or 2).</param> public static void Precalculate(int round) { DateTime startTime = DateTime.Now; string lutPath = GetLutPath(round); if (File.Exists(lutPath)) { // Do not ovewriting an existing file to save time. Console.WriteLine("LUT file {0} already exist, exiting. Delete the file to recalculate.", lutPath); return; } int POCKETS_COUNT = (int)HePocketKind.__Count; //POCKETS_COUNT = 1; // Test PrecalculationContext context = new PrecalculationContext { Round = round }; int[] listSize = new int[] { 169, 1361802, 15111642 }; context.list = round < 2 ? (object)new List <Entry01>(listSize[round]) : (object)new List <Entry2>(listSize[round]); Console.WriteLine("Calculating for round {0}: ", round); int boardSize = HeHelper.RoundToHandSize[round] - 2; for (int p = 0; p < POCKETS_COUNT; ++p) { context.pocketKind = (HePocketKind)p; context.pocket = HePocket.KindToCardSet((HePocketKind)p); context.pocketSei.Reset(); context.pocketSei.Convert(context.pocket); Console.Write("{0} ", HePocket.KindToString((HePocketKind)p)); CardEnum.Combin(StdDeck.Descriptor, boardSize, CardSet.Empty, context.pocket, OnPrecalculateBoard, context); } Console.WriteLine(); Debug.Assert(EnumAlgos.CountCombin(50, boardSize) * POCKETS_COUNT == context.count); if (round < 2) { WriteTable((List <Entry01>)context.list, lutPath); } else { WriteTable((List <Entry2>)context.list, lutPath); } Console.WriteLine("LUT file {0} written, calculated in {1:0.0} s", lutPath, (DateTime.Now - startTime).TotalSeconds); }
public void Test_CalculateFast_Array() { HePocketKind[] pockets1 = new HePocketKind[] { HePocketKind._55, HePocketKind._66 }; HePocketKind[] pockets2 = new HePocketKind[] { HePocketKind._76s, HePocketKind._76o }; PocketEquity.Result r = PocketEquity.CalculateFast(pockets1, pockets2); Assert.AreEqual(0.56058, r.Equity, 0.000005); Assert.AreEqual(246571776L / EnumAlgos.CountCombin(48,5), r.Count); pockets1 = new HePocketKind[] { HePocketKind._AKs, HePocketKind._AQs }; pockets2 = new HePocketKind[] { HePocketKind._88, HePocketKind._77 }; r = PocketEquity.CalculateFast(pockets1, pockets2); Assert.AreEqual(0.47681, r.Equity, 0.000005); Assert.AreEqual(164381184L / EnumAlgos.CountCombin(48, 5), r.Count); }
public void Holdem() { Reset(); Console.WriteLine("Calculate number of chance nodes from 1 player perspective for Holdem"); CardEnum.Combin(StdDeck.Descriptor, 2, CardSet.Empty, CardSet.Empty, SuitIsomorphismPreflopHoldem); Console.WriteLine("Preflop Chance Nodes: colored: {0}, color-isomorphic: {1} {2:0.##}%", _pocketCount, _normPreflopCount, 100.0 * _normPreflopCount / _pocketCount); int flopCount = (int)(EnumAlgos.CountCombin(52, 2) * EnumAlgos.CountCombin(50, 3)); Console.WriteLine("Flop Chance Nodes: colored: {0}, color-isomorphic: {1} {2:0.##}%", flopCount, _normFlopCount, 100.0 * _normFlopCount / flopCount); Assert.AreEqual(EnumAlgos.CountCombin(52, 2), _pocketCount); Assert.AreEqual(169, _normPreflopCount); // Value calculated by this test by many different implementations. Assert.AreEqual(1348620, _normFlopCount); }
public void Test_CalculateFast_VerifyAll() { double totalEquity = 0; uint totalCount = 0; int matchupsCount = 0; for (int pk1 = 0; pk1 < HePocket.Count; pk1++) { for (int pk2 = 0; pk2 < HePocket.Count; pk2++) { PocketEquity.Result r = PocketEquity.CalculateFast((HePocketKind)pk1, (HePocketKind)pk2); totalEquity += r.Equity; totalCount += r.Count; matchupsCount++; } } int expTotalCount = (int) (EnumAlgos.CountCombin(52, 2)*EnumAlgos.CountCombin(50, 2)); Assert.AreEqual(totalCount, expTotalCount); Assert.AreEqual((double)169*169/2, totalEquity, 0.00001); }
public void Test_Flop() { HeHsDeviation dev = new HeHsDeviation(); // Generate some unlucky flops. dev.ProcessHand(StdDeck.Descriptor.GetIndexes("7c 6c Ah 5s 5d")); dev.ProcessHand(StdDeck.Descriptor.GetIndexes("7c 6c Ad Kd Qd")); Assert.True(dev.AccDeviation[1] < 0); Assert.True(dev.AvDeviation[1] < 0); Assert.AreEqual(new int[] { 2, 2, 0, 0 }, dev.HandCount); dev.Reset(); // Generate some lucky flops. dev.ProcessHand(StdDeck.Descriptor.GetIndexes("7c 6c Ac 5c 4c")); dev.ProcessHand(StdDeck.Descriptor.GetIndexes("7c 6c Ah Kc Qc")); Assert.True(dev.AccDeviation[1] > 0); Assert.True(dev.AvDeviation[1] > 0); Assert.AreEqual(new int[] { 2, 2, 0, 0 }, dev.HandCount); dev.Reset(); // Deal all flops for a pocket and make sure the luck is zero. dev.Reset(); int[] hand = new int[5]; hand[0] = StdDeck.Descriptor.GetIndex("Ac"); hand[1] = StdDeck.Descriptor.GetIndex("Ad"); CardEnum.Combin(StdDeck.Descriptor, 3, hand, 2, hand, 2, OnCombinProcessHand, dev); Assert.AreEqual(0, dev.AccDeviation[1], 0.002); Assert.AreEqual(0, dev.AvDeviation[1], 1e-7); int hc = (int)EnumAlgos.CountCombin(50, 3); Assert.AreEqual(new int[] { hc, hc, 0, 0 }, dev.HandCount); // Deal all pockets and all flops, and make sure the luck is zero. // Check only average deviation, the accumulated one is too big because of low precision. dev.Reset(); hand = new int[5]; CardEnum.Combin(StdDeck.Descriptor, 2, hand, 0, null, 0, OnCombinFlop, dev); Assert.AreEqual(0, dev.AvDeviation[0], 0.0001); Assert.AreEqual(0, dev.AvDeviation[1], 0.0001); hc = (int)EnumAlgos.CountCombin(52, 2) * (int)EnumAlgos.CountCombin(50, 3); Assert.AreEqual(new int[] { hc, hc, 0, 0 }, dev.HandCount); }
static List <Entry> Precalculate(int boardSize) { int POCKETS_COUNT = (int)HePocketKind.__Count; //POCKETS_COUNT = 1; // Test PrecalculationContext context = new PrecalculationContext(); int[] listSize = new int[] { 169, -1, -1, 1361802, 15111642 }; context.list = new List <Entry>(listSize[boardSize]); for (int p = 0; p < POCKETS_COUNT; ++p) { context.pocketKind = (HePocketKind)p; context.pocket = HePocket.KindToCardSet((HePocketKind)p); context.pocketSei.Reset(); context.pocketSei.Convert(context.pocket); Console.WriteLine("Calculating for board size {0}, pocket {1}", boardSize, context.pocket); CardEnum.Combin(StdDeck.Descriptor, boardSize, CardSet.Empty, context.pocket, OnPrecalculateBoard, context); } Debug.Assert(EnumAlgos.CountCombin(50, boardSize) * POCKETS_COUNT == context.count); return(context.list); }
public static float Calculate(int[] hand, int handLength) { Debug.Assert(handLength >= 2 && handLength <= 7); int[] deckCopy = StdDeck.Descriptor.FullDeckIndexes.ShallowCopy(); for (int i = 0; i < handLength; ++i) { deckCopy[hand[i]] = -1; } int[] restDeck = RemoveDeadCards(deckCopy, handLength); int boardSize = handLength - 2; UInt32 result = 0; UInt32 boardStart = 0; for (int i = 0; i < boardSize; ++i) { boardStart = LutEvaluator7.pLut[boardStart + hand[2 + i]]; } DealBoard(hand, restDeck, deckCopy, boardStart, 0, restDeck.Length + boardSize - 4, ref result); UInt32 count = (UInt32)EnumAlgos.CountCombin(52 - handLength, 5 - boardSize) * (UInt32)EnumAlgos.CountCombin(45, 2); return((float)result / count / 2); }