public ApproximationResults Approximate(CardCollection deadCards, params Range[] ranges) { var allApprox = ranges.Select(r => r.SelectedHoleCardBinaries .Where(b => !_cards.ContainsSome(b) && (deadCards == null || !deadCards.ContainsSome(b)))) .CartesianProduct() .Select(s => Approximate(s.ToArray(), deadCards, trials: 100)) .ToList(); var players = new List <float>(new float[ranges.Length]); float splitProb = 0f; foreach (var approx in allApprox) { splitProb += approx.SplitProbability; for (int p = 0; p < ranges.Length; p++) { players[p] += approx[p].Equity; } } players.ForEach(p => p /= allApprox.Count); splitProb /= allApprox.Count; return(new ApproximationResults(players.Select((eq, i) => new ApproximationResults.PlayerResults(i, eq, 3UL)), splitProb)); // Todo: add ranges to result }
public void ApproximateLive(Action <IEnumerable <float>, float> updateLiveResults, CardCollection deadCards, CancellationToken?cancellationToken, params Range[] ranges) { var rnd = new Random(); var nativeRanges = ranges.Select(r => r.SelectedHoleCardBinaries.Where(b => !_cards.ContainsSome(b) && (!deadCards?.ContainsSome(b) ?? true)).ToList()).ToList(); cancellationToken?.ThrowIfCancellationRequested(); ulong[]? localHoleCards = new ulong[nativeRanges.Count]; ulong allLocalHoleCards; float[]? totalEquity = new float[nativeRanges.Count]; float splitEquity = 0f; int firstSelectedPlayer = 0; int skippedSituations = 0; bool skipSituation = false; for (int i = 0; i < Int32.MaxValue; i++) { // Stop if requested cancellationToken?.ThrowIfCancellationRequested(); allLocalHoleCards = 0; // Select random hole cards from each player to test // Do not select from the same player first every time, // the equities can be affected by this (e.g. AA-KK vs AA-KK vs AA-KK) int j = firstSelectedPlayer; do { var validHoleCards = nativeRanges[j].Where(hc => (hc & allLocalHoleCards) == 0).ToList(); if (!validHoleCards.Any()) { if (skippedSituations > (i < 1000 ? 950 : i * 0.95f)) { throw new NashException("Ranges are too narrow. Please adjust."); } skipSituation = true; break; } localHoleCards[j] = validHoleCards[rnd.Next(validHoleCards.Count)]; allLocalHoleCards |= localHoleCards[j]; // if the last player is reached wrap around to the first one if (++j >= nativeRanges.Count) { j = 0; } } while (j != firstSelectedPlayer); // skip this situation if not all hole cards were chosen // try with same first player to not influence the results if (skipSituation) { skippedSituations++; skipSituation = false; continue; } // update first player for next run if (++firstSelectedPlayer >= nativeRanges.Count) { firstSelectedPlayer = 0; } // Add to total equity var results = Approximate(localHoleCards, deadCards, trials: 1000); for (int k = 0; k < nativeRanges.Count; k++) { totalEquity[k] += results[k].Equity; } splitEquity += results.SplitProbability; // Update live results if (i % 10 == 0) { int simulationCount = (i - skippedSituations + 1); updateLiveResults?.Invoke(totalEquity.Select(eq => Math.Min(eq / simulationCount, 1f)), Math.Min(splitEquity / simulationCount, 1f)); } } }