public VillainProbabilityResult Calculate(HoldingHoles heroHoles, PlayerRoundProfile villainProfile, bool considerFolding = true) { var pkStage = new PkStage(_enumerate, Utils.EnumerateUnfoldedHoles); var villainRange = villainProfile.PlayerRange.CloneToPkRange(); var aliveRanges = villainRange.GetAliveGrids(); int foldCombos = 0, totalCombos = 0; foreach (var playerGrid in aliveRanges) { if (considerFolding) { SetFoldToBoard(playerGrid); foldCombos += playerGrid.FoldCombCount; } totalCombos += playerGrid.AvailableCombCount; } double villainFoldP = (double)foldCombos / totalCombos; double villainCallP = 1 - villainFoldP; var pkResult = pkStage.Pk(heroHoles, villainRange); int totalGrids = pkResult.HeroWinScenariosCount + pkResult.TiedScenariosCount + pkResult.VillainWinScenariosCount + pkResult.VillainFoldCount; double villainCallWinP = villainCallP * pkResult.VillainWinScenariosCount / totalGrids; double villainCallLoseP = villainCallP * pkResult.HeroWinScenariosCount / totalGrids; double tieP = villainCallP * pkResult.TiedScenariosCount / totalGrids; return(new VillainProbabilityResult(new Dictionary <ProbabilityEnum, double>() { { ProbabilityEnum.Fold, villainFoldP }, { ProbabilityEnum.CallWin, villainCallWinP }, { ProbabilityEnum.CallTie, tieP }, { ProbabilityEnum.CallLose, villainCallLoseP } }, villainProfile.Name)); }
private PlayerRoundProfile GenerateNewProfile(Player player, PositionEnum heroPosition) { var profile = new PlayerRoundProfile() { Name = player.Name, Position = player.Position, PlayerStatus = PlayerStatusEnum.Unpolled, IsHero = player.Position == heroPosition }; if (!profile.IsHero) { profile.InPositionAgainstHero = profile.Position > heroPosition; } var range = new PlayerRange(); range.Init(PlayerRangeGridStatusEnum.Neutral); profile.PlayerRange = range; profile.PreflopRange = range; return(profile); }
private void UpdateProfile(PlayerRoundProfile profile, Player player, Round round, RoundSetup roundSetup) { Logger.Instance.Log($"Updating profile: name={player.Name}, position={player.Position}, stage={round.StageEnum}."); if (profile.PlayerStatus == PlayerStatusEnum.Folded || profile.PlayerStatus == PlayerStatusEnum.AllIned) { //Unnecessary to update Logger.Instance.Log($"Unnecessary to update profile: name={player.Name}, PlayerStatus={profile.PlayerStatus}, " + $"position={player.Position}, stage={round.StageEnum}."); return; } Logger.Instance.Log($"Before updating profile: isAlive={profile.IsAlive}, PlayerStatus={profile.PlayerStatus}, stackSize={profile.StackSize}"); profile.StackSize = player.StackSize; Logger.Instance.Log($"After updating profile: isAlive={profile.IsAlive}, PlayerStatus={profile.PlayerStatus}, stackSize={profile.StackSize}"); switch (round.StageEnum) { case StageEnum.Preflop: profile.PreflopDecisions = new List <Decision>(round.PreflopMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.PreflopBet = player.ChipsBetByStage[StageEnum.Preflop]; profile.PreflopPlayerStatus = GetPlayerStreetStatus(profile.PreflopDecisions); var preflopLastRaiserName = round.PreflopMoves.LastOrDefault(m => m.Decision.DecisionType.IsRaiseMove())?.Player.Name; profile.IsPreflopRaiser = string.Equals(preflopLastRaiserName, profile.Name); break; case StageEnum.Flop: profile.FlopDecisions = new List <Decision>(round.FlopMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.FlopBet = player.ChipsBetByStage[StageEnum.Flop]; profile.FlopPlayerStatus = GetPlayerStreetStatus(profile.FlopDecisions); var flopLastRaiserName = round.FlopMoves.LastOrDefault(m => m.Decision.DecisionType.IsRaiseMove())?.Player.Name; profile.IsFlopRaiser = string.Equals(flopLastRaiserName, profile.Name); break; case StageEnum.Turn: profile.TurnDecisions = new List <Decision>(round.TurnMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.TurnBet = player.ChipsBetByStage[StageEnum.Turn]; profile.TurnPlayerStatus = GetPlayerStreetStatus(profile.TurnDecisions); var turnLastRaiserName = round.TurnMoves.LastOrDefault(m => m.Decision.DecisionType.IsRaiseMove())?.Player.Name; profile.IsTurnRaiser = string.Equals(turnLastRaiserName, profile.Name); break; case StageEnum.River: profile.RiverDecisions = new List <Decision>(round.RiverMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.RiverBet = player.ChipsBetByStage[StageEnum.River]; profile.RiverPlayerStatus = GetPlayerStreetStatus(profile.RiverDecisions); var riverLastRaiserName = round.RiverMoves.LastOrDefault(m => m.Decision.DecisionType.IsRaiseMove())?.Player.Name; profile.IsRiverRaiser = string.Equals(riverLastRaiserName, profile.Name); break; } if (profile.IsHero) { //unnecessary to squeeze hero's range Logger.Instance.Log($"Unnecessary to update hero's profile. name={profile.Name}."); return; } if (round.AllMoves.Any(m => string.Equals(m.Player.Name, player.Name))) { Move lastMove = round.AllMoves.LastOrDefault(m => string.Equals(m.Player.Name, player.Name)); if (lastMove != null && lastMove.Decision.DecisionType != DecisionType.Ante && lastMove.Decision.DecisionType != DecisionType.Fold) { //squeeze player's range according to his last move switch (lastMove.Stage) { case StageEnum.Preflop: SqueezePreflopRange(round, profile, lastMove); break; case StageEnum.Flop: SqueezeFlopRange(round, profile, lastMove, roundSetup); break; case StageEnum.Turn: SqueezeTurnRange(round, profile, lastMove, roundSetup); break; case StageEnum.River: SqueezeRiverRange(round, profile, lastMove, roundSetup); break; } } } }
public PlayerRange GenerateInitialRange(PlayerRoundProfile profile) { return(GenerateSmallBlindInitialRange()); }
private void UpdateProfile(PlayerRoundProfile profile, Player player, Round round) { if (profile.IsAlive == false || profile.IsAllIn) { //Unnecessary to update return; } profile.IsAlive = player.IsAlive; profile.IsAllIn = player.IsAllIn; profile.StackSize = player.StackSize; switch (round.StageEnum) { case StageEnum.Preflop: profile.PreflopDecisions = new List <Decision>(round.PreflopMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.PreflopBet = player.ChipsBetByStage[StageEnum.Preflop]; profile.PreflopPlayerStatus = GetPlayerStreetStatus(profile.PreflopDecisions); var preflopLastRaiserName = round.PreflopMoves.Last(m => m.Decision.DecisionType.IsRaiseMove()).Player.Name; profile.IsPreflopRaiser = string.Equals(preflopLastRaiserName, profile.Name); break; case StageEnum.Flop: profile.FlopDecisions = new List <Decision>(round.FlopMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.FlopBet = player.ChipsBetByStage[StageEnum.Flop]; profile.FlopPlayerStatus = GetPlayerStreetStatus(profile.FlopDecisions); var flopLastRaiserName = round.FlopMoves.LastOrDefault(m => m.Decision.DecisionType.IsRaiseMove())?.Player.Name; profile.IsFlopRaiser = string.Equals(flopLastRaiserName, profile.Name); break; case StageEnum.Turn: profile.TurnDecisions = new List <Decision>(round.TurnMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.TurnBet = player.ChipsBetByStage[StageEnum.Turn]; profile.TurnPlayerStatus = GetPlayerStreetStatus(profile.TurnDecisions); var turnLastRaiserName = round.TurnMoves.LastOrDefault(m => m.Decision.DecisionType.IsRaiseMove())?.Player.Name; profile.IsTurnRaiser = string.Equals(turnLastRaiserName, profile.Name); break; case StageEnum.River: profile.RiverDecisions = new List <Decision>(round.RiverMoves.Where(m => string.Equals(m.Player.Name, player.Name)).Select(m => m.Decision)); profile.RiverBet = player.ChipsBetByStage[StageEnum.River]; profile.RiverPlayerStatus = GetPlayerStreetStatus(profile.RiverDecisions); var riverLastRaiserName = round.RiverMoves.LastOrDefault(m => m.Decision.DecisionType.IsRaiseMove())?.Player.Name; profile.IsRiverRaiser = string.Equals(riverLastRaiserName, profile.Name); break; } if (profile.IsHero) { //unnecessary to squeeze hero's range return; } if (round.AllMoves.Any(m => string.Equals(m.Player.Name, player.Name))) { Move lastMove = round.AllMoves.LastOrDefault(m => string.Equals(m.Player.Name, player.Name)); if (lastMove != null && lastMove.Decision.DecisionType != DecisionType.Ante && lastMove.Decision.DecisionType != DecisionType.Fold) { //squeeze player's range according to his last move switch (lastMove.Stage) { case StageEnum.Preflop: SqueezePreflopRange(round, profile, lastMove); break; case StageEnum.Flop: SqueezeFlopRange(round, profile, lastMove); break; case StageEnum.Turn: SqueezeTurnRange(round, profile, lastMove); break; case StageEnum.River: SqueezeRiverRange(round, profile, lastMove); break; } } } }
/// <summary> /// 根據Board和Move是否match將一些Grid從PlayerRange中去掉 /// </summary> /// <param name="preflopRange"></param> /// <param name="tester"></param> /// <param name="playerProfile"></param> public void EliminateGrids(PlayerRange preflopRange, Func <RangeGrid, BoardRangeGridStatusEnum> tester, PlayerRoundProfile playerProfile) { foreach (var playerRangeGrid in preflopRange.GetAliveGrids()) { var outcome = tester(playerRangeGrid.Grid); if (!MoveMatchesOutcome(outcome, playerProfile)) { playerRangeGrid.PlayerRangeGridStatus.RankWiseStatus = PlayerRangeGridStatusEnum.Excluded; //todo support suit } } }
/// <summary> /// See if villain could still possibly fold /// </summary> /// <param name="raiser"></param> /// <param name="hero"></param> /// <param name="villain"></param> /// <returns></returns> public static bool VillainFoldable(PlayerRoundProfile raiser, PlayerRoundProfile hero, PlayerRoundProfile villain) { if (!villain.IsAlive) { throw new InvalidOperationException(); } if (villain.PlayerStatus == PlayerStatusEnum.AllIned) { return(false); } if (villain.Position == raiser.Position) { return(false); } PositionEnum position = raiser.Position; while (true) { //moving around position++; if (position == PositionEnum.Total) { position = PositionEnum.SmallBlind; } //villain is in a later position to hero, foldable if (position == hero.Position) { return(true); } //the villain is in an earlier position to hero, already called, not foldable if (position == villain.Position) { return(false); } } }