public static GeneralGameTypeEnum ParseGameType(this GeneralGameTypeEnum value, HandHistories.Objects.GameDescription.GameType gameType) { switch (gameType) { case HandHistories.Objects.GameDescription.GameType.CapPotLimitOmaha: case HandHistories.Objects.GameDescription.GameType.PotLimitOmaha: case HandHistories.Objects.GameDescription.GameType.FiveCardPotLimitOmaha: case HandHistories.Objects.GameDescription.GameType.NoLimitOmaha: case HandHistories.Objects.GameDescription.GameType.FixedLimitOmaha: return(GeneralGameTypeEnum.Omaha); case HandHistories.Objects.GameDescription.GameType.PotLimitOmahaHiLo: case HandHistories.Objects.GameDescription.GameType.FiveCardPotLimitOmahaHiLo: case HandHistories.Objects.GameDescription.GameType.NoLimitOmahaHiLo: case HandHistories.Objects.GameDescription.GameType.FixedLimitOmahaHiLo: return(GeneralGameTypeEnum.OmahaHiLo); case HandHistories.Objects.GameDescription.GameType.CapNoLimitHoldem: case HandHistories.Objects.GameDescription.GameType.PotLimitHoldem: case HandHistories.Objects.GameDescription.GameType.NoLimitHoldem: case HandHistories.Objects.GameDescription.GameType.FixedLimitHoldem: case HandHistories.Objects.GameDescription.GameType.Unknown: case HandHistories.Objects.GameDescription.GameType.SpreadLimitHoldem: case HandHistories.Objects.GameDescription.GameType.Any: default: return(GeneralGameTypeEnum.Holdem); } }
public Dictionary <string, EquityData> CalculateEquity(HandHistory handHistory) { var sw = new Stopwatch(); sw.Start(); if (handHistory == null) { throw new ArgumentNullException(nameof(handHistory)); } var result = new Dictionary <string, EquityData>(); if (handHistory.HandActions == null) { return(result); } var equityPlayers = GetEquityPlayers(handHistory); var pots = BuildPots(equityPlayers); // if there is no pots except main pot do nothing if (pots.Count == 0) { return(result); } // intialize equity for all plaeyers equityPlayers.ForEach(p => p.Equity = new decimal[pots.Count]); var gameType = new GeneralGameTypeEnum().ParseGameType(handHistory.GameDescription.GameType); CalculatePotEquity(pots, handHistory, gameType); CalculatePotRakes(pots, handHistory); pots.ForEach(pot => CalculateEvDiff(pot, handHistory, gameType)); result = equityPlayers.Select(x => new EquityData { Equity = x.Equity[0], EVDiff = x.EvDiff, PlayerName = x.Name }).ToDictionary(x => x.PlayerName); sw.Stop(); if (sw.ElapsedMilliseconds > 5000) { LogProvider.Log.Info($"Hand #{handHistory.HandId} was processed too slow (>5s) because of EquityCalculations"); } return(result); }
/// <summary> /// Calculates EV Diff for the players involved into the specified pots /// </summary> /// <param name="pot">Pots to calculate EV diff</param> /// <param name="handHistory">Hand history</param> /// <param name="gameType">Type of game</param> private void CalculateEvDiff(EquityPot pot, HandHistory handHistory, GeneralGameTypeEnum gameType) { if (pot.Players .Where(x => x.HoleCards != null) .Count() < 2) { return; } var pokerEvaluator = ServiceLocator.Current.GetInstance <IPokerEvaluator>(gameType.ToString()); pokerEvaluator.SetCardsOnTable(handHistory.CommunityCards); pot.Players .Where(x => x.HoleCards != null) .ForEach(x => pokerEvaluator.SetPlayerCards(x.Seat, x.HoleCards)); var winners = pokerEvaluator.GetWinners(); if (winners.Lo == null || winners.Lo.IsNullOrEmpty()) { CalculateEvDiffByPot(pot, handHistory, winners.Hi.ToList()); return; } var splitPot = new EquityPot { Index = pot.Index, Players = pot.Players, PlayersPutInPot = pot.PlayersPutInPot, Pot = pot.Pot / 2, Rake = pot.Rake / 2, Street = pot.Street }; CalculateEvDiffByPot(splitPot, handHistory, winners.Hi.ToList()); CalculateEvDiffByPot(splitPot, handHistory, winners.Lo.ToList()); }
private void UpdatePlayersEquityWin(ReplayerTableState state) { if (state == null) { return; } //preparing for formula Card on the Board in dependence of street in current state switch (state.CurrentAction.Street) { case Street.Preflop: CurrentBoard = new Card[] { }; CurrentBoardCards = string.Empty; break; case Street.Flop: CurrentBoard = CurrentGame.CommunityCards.Take(3).ToArray(); CurrentBoardCards = new string(CurrentGame.CommunityCardsString.Take(6).ToArray()); break; case Street.Turn: CurrentBoard = CurrentGame.CommunityCards.Take(4).ToArray(); CurrentBoardCards = new string(CurrentGame.CommunityCardsString.Take(8).ToArray()); break; case Street.River: CurrentBoard = CurrentGame.CommunityCards.ToArray(); CurrentBoardCards = CurrentGame.CommunityCardsString; break; case Street.Showdown: CurrentBoard = CurrentGame.CommunityCards.ToArray(); CurrentBoardCards = CurrentGame.CommunityCardsString; break; case Street.Summary: CurrentBoard = CurrentGame.CommunityCards.ToArray(); CurrentBoardCards = CurrentGame.CommunityCardsString; break; } // finding all players having hole cards ActivePlayerHasHoleCard = CurrentGame.Players.Where(pl => pl.hasHoleCards).ToList(); // searching for dead cards and removing this player from list of ActivePlayerHasHoleCard ActivePlayerHasHoleCardFolded = new List <Player>(); AllDeadCards.Clear(); foreach (ReplayerTableState replayerTableState in TableStateList) { Player playerInTableState = CurrentGame.Players.FirstOrDefault(x => x.PlayerName == replayerTableState.CurrentAction.PlayerName); if (playerInTableState != null && TableStateList.IndexOf(replayerTableState) <= TableStateList.IndexOf(state) && replayerTableState.CurrentAction.IsFold && playerInTableState.hasHoleCards) { ActivePlayerHasHoleCardFolded.Add(playerInTableState); ActivePlayerHasHoleCard.Remove(playerInTableState); AllDeadCards.Add(playerInTableState.HoleCards); AllDeadCardsString += playerInTableState.Cards; } } var equitySolver = ServiceLocator.Current.GetInstance <IEquitySolver>(); var gameType = new GeneralGameTypeEnum().ParseGameType(CurrentGame.GameDescription.GameType); var isShortDeck = CurrentGame.GameDescription.TableTypeDescriptors.Contains(HandHistories.Objects.GameDescription.TableTypeDescription.ShortDeck); var equitySolverParams = new EquitySolverParams { PlayersCards = ActivePlayerHasHoleCard.Select(x => x.HoleCards).ToArray(), BoardCards = CurrentBoard, Dead = isShortDeck ? AllDeadCards .Distinct(new LambdaComparer <HoleCards>((x, y) => x.ToString().Equals(y.ToString()))) .SelectMany(x => x) .Concat(CardGroup.GetDeadCardsForHoldem6Plus()) .ToArray() : AllDeadCards .Distinct(new LambdaComparer <HoleCards>((x, y) => x.ToString().Equals(y.ToString()))) .SelectMany(x => x) .ToArray(), GameType = gameType }; var equities = equitySolver.CalculateEquity(equitySolverParams) .Select(x => Math.Round((decimal)x.Equity * 100, 2)) .ToArray(); // updating states in replayer view if (equities != null) { RefreshBoard(equities, state.CurrentStreet); } //case of last state. Needed for All-in before River for some cases if (TableStateList.IndexOf(state) + 1 == TableStateList.Count && equities != null) { // updating states in replayer view RefreshBoard(equities, Street.Preflop); } }
private void CalculateEquity(List <EquityPlayer> players, Street street, BoardCards boardCards, HandHistories.Objects.Cards.Card[] dead, GeneralGameTypeEnum gameType, int potIndex) { var equitySolverParams = new EquitySolverParams { PlayersCards = players.Select(x => x.HoleCards).ToArray(), BoardCards = boardCards.ToArray(), Dead = dead, GameType = gameType }; var equity = CalculateEquity(equitySolverParams); for (var i = 0; i < players.Count; i++) { players[i].Equity[potIndex] = (decimal)equity[i].Equity; } }
/// <summary> /// Calculates equity for the specified player if possible /// </summary> /// <param name="equityPlayers">Players to calculate equity</param> /// <param name="handHistory">Hand history</param> private void CalculateEquity(List <EquityPlayer> equityPlayers, List <EquityPlayer> mainPotPlayers, HandHistory handHistory, GeneralGameTypeEnum gameType, int potIndex) { var playersByName = handHistory.Players .GroupBy(x => x.PlayerName) .ToDictionary(x => x.Key, x => x.FirstOrDefault()); // equity can be calculated only for player who didn't fold, whose last action was before river, whose hole cards are known var eligibleEquityPlayers = equityPlayers .Where(x => x.LastAction.Street != Street.River && !x.LastAction.IsFold && playersByName.ContainsKey(x.Name) && playersByName[x.Name].hasHoleCards) .ToList(); // equity can't be calculated for single player if (eligibleEquityPlayers.Count < 2) { return; } // set known hole cards eligibleEquityPlayers.ForEach(p => p.HoleCards = playersByName[p.Name].HoleCards); try { var street = eligibleEquityPlayers.Select(x => x.LastAction.Street).Distinct().Max(); var boardCards = CardHelper.IsStreetAvailable(handHistory.CommunityCards.ToString(), street) ? handHistory.CommunityCards.GetBoardOnStreet(street) : handHistory.CommunityCards; var deadCards = mainPotPlayers.Except(equityPlayers).Where(x => x.HoleCards != null).SelectMany(x => x.HoleCards).ToArray(); if (handHistory.GameDescription.TableType.Contains(HandHistories.Objects.GameDescription.TableTypeDescription.ShortDeck)) { deadCards = deadCards.Concat(CardGroup.GetDeadCardsForHoldem6Plus()).ToArray(); } if (pokerEvalLibLoaded) { CalculateEquity(eligibleEquityPlayers, street, boardCards, deadCards, gameType, potIndex); } else if (gameType == GeneralGameTypeEnum.Holdem) { CalculateHoldemEquity(eligibleEquityPlayers, street, boardCards, deadCards, potIndex); } else { CalculateOmahaEquity(eligibleEquityPlayers, street, boardCards, gameType == GeneralGameTypeEnum.OmahaHiLo, potIndex); } } catch (Exception e) { LogProvider.Log.Error(this, $"Could not calculate equity for hand #{handHistory.HandId}", e); } }
private void CalculatePotEquity(List <EquityPot> pots, HandHistory handHistory, GeneralGameTypeEnum gameType) { var mainPotPlayers = pots[0].Players; for (var potIndex = 0; potIndex < pots.Count; potIndex++) { CalculateEquity(pots[potIndex].Players, mainPotPlayers, handHistory, gameType, potIndex); } }