private CardButton CreateCardButton(CardFace f1, CardFace f2, bool suited) { CardButton button = new CardButton(); button.HoverColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); button.Selected = false; button.SelectedColor = System.Drawing.Color.Yellow; button.Size = new System.Drawing.Size(BUTTON_SIZE, BUTTON_SIZE); button.TabIndex = 0; String value; if (suited) { value = HoldemHand.ConvertToString(f1, CardSuit.Clubs, f2, CardSuit.Clubs); } else { value = HoldemHand.ConvertToString(f1, CardSuit.Clubs, f2, CardSuit.Diamonds); } button.Name = "btn" + value; button.Value = value; return(button); }
protected override Hand CreateHandFromCardList(CardList cardList) { // We need exactly two cards in hold'em to have a hand if (cardList.Count == 2) { HoldemHand hand = new HoldemHand(cardList[0], cardList[1]); return hand; } else return null; }
protected void DisplayOdds(HoldemHand playerHand, HoldemBoard board) { // Display odds List<Statistic> odds = ((HoldemOddsCalculator)oddsCalculator).Calculate(playerHand, board); StatisticItemListDisplay statisticListDisplay = CreateStatisticItemListDisplay(); statisticListDisplay.Add(odds); handControlLayout.Controls.Add(statisticListDisplay); }
protected void DisplayOdds(HoldemHand playerHand, HoldemBoard board) { // Display odds List <Statistic> odds = ((HoldemOddsCalculator)oddsCalculator).Calculate(playerHand, board); StatisticItemListDisplay statisticListDisplay = CreateStatisticItemListDisplay(); statisticListDisplay.Add(odds); handControlLayout.Controls.Add(statisticListDisplay); }
/* Returns true if the hand is a gapped connector and is within the range of hands (inclusive) * specified, ex. between 54 and JT (only the upper cards are checked) */ public bool IsGappedConnectorsInRange(int gap, HoldemHand lower, HoldemHand upper) { if (this.GetHigher().GetFaceValue() >= lower.GetHigher().GetFaceValue() && this.GetHigher().GetFaceValue() <= upper.GetHigher().GetFaceValue()) { return(IsGappedConnectors(gap)); } else { return(false); } }
protected override Hand CreateHandFromCardList(CardList cardList) { // We need exactly two cards in hold'em to have a hand if (cardList.Count == 2) { HoldemHand hand = new HoldemHand(cardList[0], cardList[1]); return(hand); } else { return(null); } }
/* Checks that every possible hand is covered in the table */ public static void TestPercentiles() { for (CardFace face1 = CardFace.Ace; face1 <= CardFace.King; face1++) { for (CardSuit suit1 = CardSuit.Clubs; suit1 <= CardSuit.Spades; suit1++) { Card c1 = new Card(face1, suit1); for (CardFace face2 = CardFace.Ace; face2 <= CardFace.King; face2++) { for (CardSuit suit2 = CardSuit.Clubs; suit2 <= CardSuit.Spades; suit2++) { Card c2 = new Card(face2, suit2); HoldemHand hand = new HoldemHand(c1, c2); Trace.Assert(HoldemHand.preflopPercentiles.ContainsKey(hand.ToString()), "Percentile not found for: " + hand.ToString()); } } } } }
public List<Statistic> Calculate(HoldemHand hand, HoldemBoard board) { List<Statistic> result = new List<Statistic>(); // What game phase are we in (returns empty list if the board is invalid) HoldemGamePhase phase = HoldemGamePhase.Flop; if (board.Count == 3) phase = HoldemGamePhase.Flop; else if (board.Count == 4) phase = HoldemGamePhase.Turn; else if (board.Count == 5) phase = HoldemGamePhase.River; else { Trace.WriteLine("We were asked to calculate the odds for " + hand.ToString() + " and " + board.ToString() + " but the board seems to be invalid. Returning empty."); return result; } HoldemHand.ClassificationPostflop classification = (HoldemHand.ClassificationPostflop)hand.GetClassification(phase, board); HoldemHand.ClassificationPostflop.HandType handType = classification.GetHand(); HoldemHand.ClassificationPostflop.DrawType drawType = classification.GetDraw(); HoldemHand.ClassificationPostflop.StraightDrawType straightDrawType = classification.GetStraightDraw(); result.Add(new Statistic(new StatisticsDescriptiveData("Your hand", classification.GetHandDescription()))); if (phase == HoldemGamePhase.Flop || phase == HoldemGamePhase.Turn) { result.Add(new Statistic(new StatisticsDescriptiveData("Draws", classification.GetDrawsDescription()))); if (handType == HoldemHand.ClassificationPostflop.HandType.HighCard) { // Hold high card, hope to make a pair result.Add(CreateOutsStatistic("Improve to a pair", 6, phase)); } else if (handType == HoldemHand.ClassificationPostflop.HandType.Pair) { // Hold a pair, hope to make three of a kind result.Add(CreateOutsStatistic("Improve to three of a kind", 2, phase)); } else if (handType == HoldemHand.ClassificationPostflop.HandType.TwoPair) { // Hold two pair, hope to make a full house result.Add(CreateOutsStatistic("Improve to a full house", 4, phase)); } if (drawType == HoldemHand.ClassificationPostflop.DrawType.Flush) { result.Add(CreateOutsStatistic("Improve to a flush", 9, phase)); } else if (drawType == HoldemHand.ClassificationPostflop.DrawType.Straight) { if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.OpenEndedStraightDraw) { result.Add(CreateOutsStatistic("Improve to a straight", 8, phase)); } else if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.InsideStraightDraw) { result.Add(CreateOutsStatistic("Improve to a straight", 4, phase)); } else { Trace.WriteLine("Warning! Straight draw detected when calculating odds but none of the cases was matched?"); } } else if (drawType == HoldemHand.ClassificationPostflop.DrawType.FlushAndStraight) { // Open ended straight flush draw if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.OpenEndedStraightDraw) { // 9 outs for the flush + 6 outs (2 outs for the straight draw are the suit we need to make the flush) result.Add(CreateOutsStatistic("Improve to a flush or straight", 15, phase)); } else if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.InsideStraightDraw) { // inside straight flushd draw, 9 outs + 3 (1 out is the suit we need) result.Add(CreateOutsStatistic("Improve to a flush or straight", 12, phase)); } else { Trace.WriteLine("Warning! Flush and straight draw detected when calculating odds but none of the cases was matched?"); } } } return result; }
public List <Statistic> Calculate(HoldemHand hand, HoldemBoard board) { List <Statistic> result = new List <Statistic>(); // What game phase are we in (returns empty list if the board is invalid) HoldemGamePhase phase = HoldemGamePhase.Flop; if (board.Count == 3) { phase = HoldemGamePhase.Flop; } else if (board.Count == 4) { phase = HoldemGamePhase.Turn; } else if (board.Count == 5) { phase = HoldemGamePhase.River; } else { Trace.WriteLine("We were asked to calculate the odds for " + hand.ToString() + " and " + board.ToString() + " but the board seems to be invalid. Returning empty."); return(result); } HoldemHand.ClassificationPostflop classification = (HoldemHand.ClassificationPostflop)hand.GetClassification(phase, board); HoldemHand.ClassificationPostflop.HandType handType = classification.GetHand(); HoldemHand.ClassificationPostflop.DrawType drawType = classification.GetDraw(); HoldemHand.ClassificationPostflop.StraightDrawType straightDrawType = classification.GetStraightDraw(); result.Add(new Statistic(new StatisticsDescriptiveData("Your hand", classification.GetHandDescription()))); if (phase == HoldemGamePhase.Flop || phase == HoldemGamePhase.Turn) { result.Add(new Statistic(new StatisticsDescriptiveData("Draws", classification.GetDrawsDescription()))); if (handType == HoldemHand.ClassificationPostflop.HandType.HighCard) { // Hold high card, hope to make a pair result.Add(CreateOutsStatistic("Improve to a pair", 6, phase)); } else if (handType == HoldemHand.ClassificationPostflop.HandType.Pair) { // Hold a pair, hope to make three of a kind result.Add(CreateOutsStatistic("Improve to three of a kind", 2, phase)); } else if (handType == HoldemHand.ClassificationPostflop.HandType.TwoPair) { // Hold two pair, hope to make a full house result.Add(CreateOutsStatistic("Improve to a full house", 4, phase)); } if (drawType == HoldemHand.ClassificationPostflop.DrawType.Flush) { result.Add(CreateOutsStatistic("Improve to a flush", 9, phase)); } else if (drawType == HoldemHand.ClassificationPostflop.DrawType.Straight) { if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.OpenEndedStraightDraw) { result.Add(CreateOutsStatistic("Improve to a straight", 8, phase)); } else if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.InsideStraightDraw) { result.Add(CreateOutsStatistic("Improve to a straight", 4, phase)); } else { Trace.WriteLine("Warning! Straight draw detected when calculating odds but none of the cases was matched?"); } } else if (drawType == HoldemHand.ClassificationPostflop.DrawType.FlushAndStraight) { // Open ended straight flush draw if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.OpenEndedStraightDraw) { // 9 outs for the flush + 6 outs (2 outs for the straight draw are the suit we need to make the flush) result.Add(CreateOutsStatistic("Improve to a flush or straight", 15, phase)); } else if (straightDrawType == HoldemHand.ClassificationPostflop.StraightDrawType.InsideStraightDraw) { // inside straight flushd draw, 9 outs + 3 (1 out is the suit we need) result.Add(CreateOutsStatistic("Improve to a flush or straight", 12, phase)); } else { Trace.WriteLine("Warning! Flush and straight draw detected when calculating odds but none of the cases was matched?"); } } } return(result); }
public ClassificationPreflop(HoldemHand hand) { this.percentile = (float)HoldemHand.preflopPercentiles[hand.ToString()]; }
public ClassificationPostflop(HoldemHand hand, HoldemGamePhase phase, HoldemBoard board) { CardList communityCards = board.GetBoardAt(phase); // Default this.hand = HandType.Unknown; this.kicker = KickerType.Unknown; this.pair = PairType.Irrelevant; this.draw = DrawType.Irrelevant; this.straightDraw = StraightDrawType.None; Trace.Assert(communityCards.Count > 0, "Cannot classificate an empty list of community cards."); // Create a new list including the board cards and the cards from the hand CardList cards = new CardList(communityCards.Count + 2); foreach (Card c in communityCards) { cards.AddCard(c); } cards.AddCard(hand.GetFirstCard()); cards.AddCard(hand.GetSecondCard()); // --- Royal flush if (IsRoyalFlush(cards)) { this.hand = HandType.RoyalFlush; this.kicker = KickerType.Irrelevant; return; } // -- Four of a kind if (cards.HaveIdenticalFaces(4)) { this.hand = HandType.FourOfAKind; this.kicker = KickerType.Irrelevant; return; } // -- Full House // If we have three of a kind and two pair at the same time, we have a full house bool isThreeOfAKind = cards.HaveIdenticalFaces(3); bool isTwoPair = IsTwoPair(cards); if (isThreeOfAKind && isTwoPair) { this.hand = HandType.FullHouse; this.kicker = KickerType.Irrelevant; return; } // -- Flush for (int i = 0; i < cards.Count; i++) { int numCardsSameSuit = 0; for (int j = i + 1; j < cards.Count; j++) { if (cards[i].Suit == cards[j].Suit) { numCardsSameSuit++; } } if (numCardsSameSuit >= 4) { this.hand = HandType.Flush; this.kicker = KickerType.Irrelevant; return; } } // -- Straight if (IsStraight(cards)) { this.hand = HandType.Straight; this.kicker = KickerType.Irrelevant; return; } // Calculate draws (if we got until here, there might be some) // Also, no draws are possible at the river if (phase == HoldemGamePhase.River) { draw = DrawType.None; straightDraw = StraightDrawType.None; } else { draw = GetDrawType(cards); if (IsInsideStraightDraw(cards)) { straightDraw = StraightDrawType.InsideStraightDraw; } if (IsOpenEndedStraightDraw(cards)) { straightDraw = StraightDrawType.OpenEndedStraightDraw; } } // -- Trips if (isThreeOfAKind) { this.hand = HandType.ThreeOfAKind; this.kicker = KickerType.Irrelevant; return; } // -- Two pair if (isTwoPair) { this.hand = HandType.TwoPair; this.kicker = KickerType.Irrelevant; return; } // -- Pair Card matching; if (cards.HaveIdenticalFaces(2, out matching)) { // Sort list by face value (ace high first) cards.Sort(SortUsing.AceHigh); // Find kicker (check from end of the list where face values are higher) Card kicker = cards[0]; for (int i = cards.Count - 1; i >= 0; i--) { if (cards[i].Face != matching.Face) { kicker = cards[i]; break; } } this.hand = HandType.Pair; this.kicker = GetKickerTypeFromCard(kicker); this.pair = GetPairType(communityCards, matching, hand.GetFirstCard(), hand.GetSecondCard()); return; } // -- High card cards.Sort(SortUsing.AceHigh); Card highCard = cards.Last; this.hand = HandType.HighCard; this.kicker = GetKickerTypeFromCard(highCard); }
public bool IsConnectorsInRange(HoldemHand lower, HoldemHand upper) { return IsGappedConnectorsInRange(0, lower, upper); }
public bool IsConnectorsInRange(HoldemHand lower, HoldemHand upper) { return(IsGappedConnectorsInRange(0, lower, upper)); }
public override List <Statistic> Calculate(Hand hand) { HoldemHand holdemHand = (HoldemHand)hand; List <Statistic> result = new List <Statistic>(); // Depending on what hand we have, we generate only odds that are consistent with our hand /* Odds are taken from http://www.flopturnriver.com/Common-Flop-Odds.html * since they are better statisticians than I am */ // Unpaired hole cards if (!holdemHand.IsPaired()) { result.Add(new Statistic(new StatisticsOddsData("Flop pair to a hole card", 26.939f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop two pair to a hole card", 2.02f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop trips to a hole card", 1.347f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop full house", 0.092f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop quads", 0.01f, PRECISION))); } else { // Paired hole cards result.Add(new Statistic(new StatisticsOddsData("Flop two pair by pairing the board", 16.163f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop trips for your pair", 10.775f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop full house (set to your pair)", 0.735f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop full house (trips on board)", 0.245f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop quads", 0.245f, PRECISION))); } // Unsuited if (!holdemHand.IsSuited()) { result.Add(new Statistic(new StatisticsOddsData("Flop a four flush", 2.245f, PRECISION))); } else { // Suited result.Add(new Statistic(new StatisticsOddsData("Flop a flush", 0.842f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop a four flush", 10.449f, PRECISION))); } // Connectors from 54 to JT if (holdemHand.IsConnectorsInRange( new HoldemHand(new Card(CardFace.Five, CardSuit.Clubs), new Card(CardFace.Four, CardSuit.Clubs)), new HoldemHand(new Card(CardFace.Jack, CardSuit.Clubs), new Card(CardFace.Ten, CardSuit.Clubs)))) { result.Add(new Statistic(new StatisticsOddsData("Flop a straight", 1.306f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop an 8 out straight draw", 10.449f, PRECISION))); } // One gapped connectors from 53 to QT if (holdemHand.IsGappedConnectorsInRange(1, new HoldemHand(new Card(CardFace.Five, CardSuit.Clubs), new Card(CardFace.Three, CardSuit.Clubs)), new HoldemHand(new Card(CardFace.Queen, CardSuit.Clubs), new Card(CardFace.Ten, CardSuit.Clubs)))) { result.Add(new Statistic(new StatisticsOddsData("Flop a straight", 0.980f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop an 8 out straight draw", 8.08f, PRECISION))); } // Two gapped connectors from 52 to KT if (holdemHand.IsGappedConnectorsInRange(2, new HoldemHand(new Card(CardFace.Five, CardSuit.Clubs), new Card(CardFace.Two, CardSuit.Clubs)), new HoldemHand(new Card(CardFace.King, CardSuit.Clubs), new Card(CardFace.Ten, CardSuit.Clubs)))) { result.Add(new Statistic(new StatisticsOddsData("Flop a straight", 0.653f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop an 8 out straight draw", 5.224f, PRECISION))); } // Three gapped connectors from A5 to AT if (holdemHand.IsGappedConnectorsInRange(3, new HoldemHand(new Card(CardFace.Ace, CardSuit.Clubs), new Card(CardFace.Five, CardSuit.Clubs)), new HoldemHand(new Card(CardFace.Ace, CardSuit.Clubs), new Card(CardFace.Ten, CardSuit.Clubs)))) { result.Add(new Statistic(new StatisticsOddsData("Flop a straight", 0.327f, PRECISION))); result.Add(new Statistic(new StatisticsOddsData("Flop an 8 out straight draw", 2.612f, PRECISION))); } return(result); }
public override void ParseLine(string line) { // Call base class method FIRST base.ParseLine(line); // Declare match variable that will hold the results Match matchResult; /* Check game phase changes */ bool gamePhaseChanged = ParseForGamePhaseChanges(line); if (gamePhaseChanged) return; /* An all-in doesn't exclude a raise or a call, so it's not part of the if-elseif block * Ex. Player: raises and is all-in is both an all-in and a raise */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_all_in_push"), out matchResult)) { String playerName = matchResult.Groups["playerName"].Value; // Make sure that the game phase is a valid one (being all-in because the blinds force us to is not really a push) if (currentGamePhase == HoldemGamePhase.Preflop || currentGamePhase == HoldemGamePhase.Flop || currentGamePhase == HoldemGamePhase.Turn || currentGamePhase == HoldemGamePhase.River) OnPlayerPushedAllIn(playerName, currentGamePhase); } /* Compare line to extract game id or table id */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_game_id_token"), out matchResult)) { currentGameId = matchResult.Groups["gameId"].Value; } if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_table_token"), out matchResult)) { currentTableId = matchResult.Groups["tableId"].Value; String maxSeatingCapacity = matchResult.Groups["tableSeatingCapacity"].Value; Trace.WriteLine("Table: " + currentTableId); Trace.WriteLine("Max seating capacity: " + maxSeatingCapacity); // Note: often currentGameId will be null because we don't have that information OnNewTableHasBeenCreated(currentGameId, currentTableId); // Abemus max seating capacity? if (maxSeatingCapacity != String.Empty) { OnFoundTableMaxSeatingCapacity(Int32.Parse(maxSeatingCapacity)); } else { // We didn't find the exact seating capacity // If we have a more specific regex, we'll wait until we get the line containing the maximum number of seats // Otherwise, we need to infer if (!pokerClient.HasRegex("hand_history_max_seating_capacity")) { int inferredMaxCapacity = pokerClient.InferMaxSeatingCapacity(line, handhistoryFilename, currentGameId); Trace.WriteLine("Inferred max seating capacity: " + inferredMaxCapacity); OnFoundTableMaxSeatingCapacity(inferredMaxCapacity); } else { Trace.WriteLine("Seating capacity not found, but we will find it later with a specialized regex"); } } } /* Detect the exact max seating capacity */ if (pokerClient.HasRegex("hand_history_max_seating_capacity") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_max_seating_capacity"), out matchResult))) { String maxSeatingCapacity = matchResult.Groups["tableSeatingCapacity"].Value; Trace.Assert(maxSeatingCapacity != String.Empty, "Table max seating capacity regex found, but empty result."); Trace.WriteLine("Found certain max seating capacity from regex: " + maxSeatingCapacity); OnFoundTableMaxSeatingCapacity(Int32.Parse(maxSeatingCapacity)); } /* Detect the exact blind amounts */ if (pokerClient.HasRegex("hand_history_blind_amounts") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_blind_amounts"), out matchResult))) { float bigBlindAmount = float.Parse(matchResult.Groups["bigBlindAmount"].Value); float smallBlindAmount = float.Parse(matchResult.Groups["smallBlindAmount"].Value); Trace.WriteLine(String.Format("Found certain blind amounts: ({0}/{1})", smallBlindAmount, bigBlindAmount)); OnFoundBigBlindAmount(bigBlindAmount); OnFoundSmallBlindAmount(smallBlindAmount); InfereBlindsFromButton(lastButtonSeatNumber); } /* Detect raises, calls, folds, bets */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_call"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a call during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; float amount = float.Parse(matchResult.Groups["amount"].Value); Trace.WriteLine(playerName + " calls"); OnPlayerCalled(playerName, amount, currentGamePhase); } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_bet"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a bet during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; float amount = float.Parse(matchResult.Groups["amount"].Value); Trace.WriteLine(playerName + " bets " + amount); // Some poker clients mistakenly call a preflop raise a "bet", so we need to account for that if (currentGamePhase == HoldemGamePhase.Preflop) { OnPlayerRaised(playerName, amount, currentGamePhase); } else { OnPlayerBet(playerName, amount, currentGamePhase); } } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_fold"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a fold during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; Trace.WriteLine(playerName + " folds"); OnPlayerFolded(playerName, currentGamePhase); } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_check"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a check during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; OnPlayerChecked(playerName, currentGamePhase); } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_raise"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a raise during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; float raiseAmount = float.Parse(matchResult.Groups["raiseAmount"].Value); Trace.WriteLine(playerName + " raises " + raiseAmount); OnPlayerRaised(playerName, raiseAmount, currentGamePhase); } /* Search for table seating patterns */ else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_in_game"), out matchResult)) { // Retrieve player name String playerName = matchResult.Groups["playerName"].Value; int seatNumber = Int32.Parse(matchResult.Groups["seatNumber"].Value); // Some poker clients rewrite the list of players at the summary, but we don't need this extra information if (currentGamePhase != HoldemGamePhase.Summary) { // Raise event OnPlayerIsSeated(playerName, seatNumber); // Save playerSeats.Add(seatNumber, playerName); } } /* Search for a winner */ else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_hand_winner"), out matchResult)) { // Retrieve player name String playerName = matchResult.Groups["playerName"].Value; // If we don't have a regex for detecting mucked hands, this is where we call a more specialized routine if (!pokerClient.HasRegex("hand_history_detect_mucked_hand")) { List<KeyValuePair<string,string>> muckedHands = pokerClient.GetMuckedHands(currentGameId); foreach (var muckedHand in muckedHands) { List<Card> cards = GenerateCardsFromText(muckedHand.Value); // Sometimes a regex will return only one card (when a player decides to show only one card) if (cards.Count == 2) { Hand hand = new HoldemHand(cards[0], cards[1]); OnPlayerMuckHandAvailable(muckedHand.Key, hand); Trace.WriteLine("Retrieved mucked hand from alternative source (" + muckedHand.Key + ": " + muckedHand.Value + ")"); PlayerHasShowedThisRound = true; } } } // Raise event OnFoundWinner(playerName); } /* Search for mucked hands */ else if (pokerClient.HasRegex("hand_history_detect_mucked_hand") && LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_mucked_hand"), out matchResult)) { // Somebody had to show... keep track of this PlayerHasShowedThisRound = true; // Retrieve player name and hand String playerName = matchResult.Groups["playerName"].Value; String cardsText = matchResult.Groups["cards"].Value; List<Card> cards = GenerateCardsFromText(cardsText); // Sometimes a regex will return only one card (when a player decides to show only one card) if (cards.Count == 2){ Hand hand = new HoldemHand(cards[0], cards[1]); OnPlayerMuckHandAvailable(playerName, hand); } } /* Search for final board */ else if (pokerClient.HasRegex("hand_history_detect_final_board") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_final_board"), out matchResult))) { // Retrieve cards String cardsText = matchResult.Groups["cards"].Value; List<Card> cards = GenerateCardsFromText(cardsText); HandleFinalBoard(cards); } /* Detect small/big blind */ else if (pokerClient.HasRegex("hand_history_detect_small_blind") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_small_blind"), out matchResult))) { String playerName = matchResult.Groups["playerName"].Value; OnFoundSmallBlind(playerName); if (matchResult.Groups["smallBlindAmount"].Success) { float amount = float.Parse(matchResult.Groups["smallBlindAmount"].Value); OnFoundSmallBlindAmount(amount); } } else if (pokerClient.HasRegex("hand_history_detect_big_blind") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_big_blind"), out matchResult))) { String playerName = matchResult.Groups["playerName"].Value; OnFoundBigBlind(playerName); if (matchResult.Groups["bigBlindAmount"].Success) { float amount = float.Parse(matchResult.Groups["bigBlindAmount"].Value); OnFoundBigBlindAmount(amount); } } /* Find the button */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_button"), out matchResult)) { Trace.Assert(matchResult.Groups["seatNumber"].Success || matchResult.Groups["playerName"].Success, "Detect button line matched, but no seatNumber or playerName set"); int seatNumber = -1; if (matchResult.Groups["seatNumber"].Success) { seatNumber = Int32.Parse(matchResult.Groups["seatNumber"].Value); } else if (matchResult.Groups["playerName"].Success) { String playerName = matchResult.Groups["playerName"].Value; foreach (int seat in playerSeats.Keys) { if ((String)playerSeats[seat] == playerName) { seatNumber = seat; break; } } Trace.Assert(seatNumber != -1, "Button's player name found, but he's not in our players list"); } OnFoundButton(seatNumber); lastButtonSeatNumber = seatNumber; // Save } }
public override void ParseLine(string line) { // Call base class method FIRST base.ParseLine(line); // Declare match variable that will hold the results Match matchResult; /* Check game phase changes */ bool gamePhaseChanged = ParseForGamePhaseChanges(line); if (gamePhaseChanged) { return; } /* An all-in doesn't exclude a raise or a call, so it's not part of the if-elseif block * Ex. Player: raises and is all-in is both an all-in and a raise */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_all_in_push"), out matchResult)) { String playerName = matchResult.Groups["playerName"].Value; // Make sure that the game phase is a valid one (being all-in because the blinds force us to is not really a push) if (currentGamePhase == HoldemGamePhase.Preflop || currentGamePhase == HoldemGamePhase.Flop || currentGamePhase == HoldemGamePhase.Turn || currentGamePhase == HoldemGamePhase.River) { OnPlayerPushedAllIn(playerName, currentGamePhase); } } /* Compare line to extract game id or table id */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_game_id_token"), out matchResult)) { currentGameId = matchResult.Groups["gameId"].Value; } if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_table_token"), out matchResult)) { currentTableId = matchResult.Groups["tableId"].Value; String maxSeatingCapacity = matchResult.Groups["tableSeatingCapacity"].Value; Trace.WriteLine("Table: " + currentTableId); Trace.WriteLine("Max seating capacity: " + maxSeatingCapacity); // Note: often currentGameId will be null because we don't have that information OnNewTableHasBeenCreated(currentGameId, currentTableId); // Abemus max seating capacity? if (maxSeatingCapacity != String.Empty) { OnFoundTableMaxSeatingCapacity(Int32.Parse(maxSeatingCapacity)); } else { // We didn't find the exact seating capacity // If we have a more specific regex, we'll wait until we get the line containing the maximum number of seats // Otherwise, we need to infer if (!pokerClient.HasRegex("hand_history_max_seating_capacity")) { int inferredMaxCapacity = pokerClient.InferMaxSeatingCapacity(line, handhistoryFilename, currentGameId); Trace.WriteLine("Inferred max seating capacity: " + inferredMaxCapacity); OnFoundTableMaxSeatingCapacity(inferredMaxCapacity); } else { Trace.WriteLine("Seating capacity not found, but we will find it later with a specialized regex"); } } } /* Detect the exact max seating capacity */ if (pokerClient.HasRegex("hand_history_max_seating_capacity") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_max_seating_capacity"), out matchResult))) { String maxSeatingCapacity = matchResult.Groups["tableSeatingCapacity"].Value; Trace.Assert(maxSeatingCapacity != String.Empty, "Table max seating capacity regex found, but empty result."); Trace.WriteLine("Found certain max seating capacity from regex: " + maxSeatingCapacity); OnFoundTableMaxSeatingCapacity(Int32.Parse(maxSeatingCapacity)); } /* Detect the exact blind amounts */ if (pokerClient.HasRegex("hand_history_blind_amounts") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_blind_amounts"), out matchResult))) { float bigBlindAmount = float.Parse(matchResult.Groups["bigBlindAmount"].Value); float smallBlindAmount = float.Parse(matchResult.Groups["smallBlindAmount"].Value); Trace.WriteLine(String.Format("Found certain blind amounts: ({0}/{1})", smallBlindAmount, bigBlindAmount)); OnFoundBigBlindAmount(bigBlindAmount); OnFoundSmallBlindAmount(smallBlindAmount); InfereBlindsFromButton(lastButtonSeatNumber); } /* Detect raises, calls, folds, bets */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_call"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a call during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; float amount = float.Parse(matchResult.Groups["amount"].Value); Trace.WriteLine(playerName + " calls"); OnPlayerCalled(playerName, amount, currentGamePhase); } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_bet"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a bet during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; float amount = float.Parse(matchResult.Groups["amount"].Value); Trace.WriteLine(playerName + " bets " + amount); // Some poker clients mistakenly call a preflop raise a "bet", so we need to account for that if (currentGamePhase == HoldemGamePhase.Preflop) { OnPlayerRaised(playerName, amount, currentGamePhase); } else { OnPlayerBet(playerName, amount, currentGamePhase); } } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_fold"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a fold during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; Trace.WriteLine(playerName + " folds"); OnPlayerFolded(playerName, currentGamePhase); } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_check"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a check during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; OnPlayerChecked(playerName, currentGamePhase); } else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_raise"), out matchResult)) { Trace.Assert(currentGamePhase != HoldemGamePhase.Showdown && currentGamePhase != HoldemGamePhase.Summary, "We detected a raise during an impossible game phase"); String playerName = matchResult.Groups["playerName"].Value; float raiseAmount = float.Parse(matchResult.Groups["raiseAmount"].Value); Trace.WriteLine(playerName + " raises " + raiseAmount); OnPlayerRaised(playerName, raiseAmount, currentGamePhase); } /* Search for table seating patterns */ else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_player_in_game"), out matchResult)) { // Retrieve player name String playerName = matchResult.Groups["playerName"].Value; int seatNumber = Int32.Parse(matchResult.Groups["seatNumber"].Value); // Some poker clients rewrite the list of players at the summary, but we don't need this extra information if (currentGamePhase != HoldemGamePhase.Summary) { // Raise event OnPlayerIsSeated(playerName, seatNumber); // Save playerSeats.Add(seatNumber, playerName); } } /* Search for a winner */ else if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_hand_winner"), out matchResult)) { // Retrieve player name String playerName = matchResult.Groups["playerName"].Value; // If we don't have a regex for detecting mucked hands, this is where we call a more specialized routine if (!pokerClient.HasRegex("hand_history_detect_mucked_hand")) { List <KeyValuePair <string, string> > muckedHands = pokerClient.GetMuckedHands(currentGameId); foreach (var muckedHand in muckedHands) { List <Card> cards = GenerateCardsFromText(muckedHand.Value); // Sometimes a regex will return only one card (when a player decides to show only one card) if (cards.Count == 2) { Hand hand = new HoldemHand(cards[0], cards[1]); OnPlayerMuckHandAvailable(muckedHand.Key, hand); Trace.WriteLine("Retrieved mucked hand from alternative source (" + muckedHand.Key + ": " + muckedHand.Value + ")"); PlayerHasShowedThisRound = true; } } } // Raise event OnFoundWinner(playerName); } /* Search for mucked hands */ else if (pokerClient.HasRegex("hand_history_detect_mucked_hand") && LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_mucked_hand"), out matchResult)) { // Somebody had to show... keep track of this PlayerHasShowedThisRound = true; // Retrieve player name and hand String playerName = matchResult.Groups["playerName"].Value; String cardsText = matchResult.Groups["cards"].Value; List <Card> cards = GenerateCardsFromText(cardsText); // Sometimes a regex will return only one card (when a player decides to show only one card) if (cards.Count == 2) { Hand hand = new HoldemHand(cards[0], cards[1]); OnPlayerMuckHandAvailable(playerName, hand); } } /* Search for final board */ else if (pokerClient.HasRegex("hand_history_detect_final_board") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_final_board"), out matchResult))) { // Retrieve cards String cardsText = matchResult.Groups["cards"].Value; List <Card> cards = GenerateCardsFromText(cardsText); HandleFinalBoard(cards); } /* Detect small/big blind */ else if (pokerClient.HasRegex("hand_history_detect_small_blind") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_small_blind"), out matchResult))) { String playerName = matchResult.Groups["playerName"].Value; OnFoundSmallBlind(playerName); if (matchResult.Groups["smallBlindAmount"].Success) { float amount = float.Parse(matchResult.Groups["smallBlindAmount"].Value); OnFoundSmallBlindAmount(amount); } } else if (pokerClient.HasRegex("hand_history_detect_big_blind") && (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_big_blind"), out matchResult))) { String playerName = matchResult.Groups["playerName"].Value; OnFoundBigBlind(playerName); if (matchResult.Groups["bigBlindAmount"].Success) { float amount = float.Parse(matchResult.Groups["bigBlindAmount"].Value); OnFoundBigBlindAmount(amount); } } /* Find the button */ if (LineMatchesRegex(line, pokerClient.GetRegex("hand_history_detect_button"), out matchResult)) { Trace.Assert(matchResult.Groups["seatNumber"].Success || matchResult.Groups["playerName"].Success, "Detect button line matched, but no seatNumber or playerName set"); int seatNumber = -1; if (matchResult.Groups["seatNumber"].Success) { seatNumber = Int32.Parse(matchResult.Groups["seatNumber"].Value); } else if (matchResult.Groups["playerName"].Success) { String playerName = matchResult.Groups["playerName"].Value; foreach (int seat in playerSeats.Keys) { if ((String)playerSeats[seat] == playerName) { seatNumber = seat; break; } } Trace.Assert(seatNumber != -1, "Button's player name found, but he's not in our players list"); } OnFoundButton(seatNumber); lastButtonSeatNumber = seatNumber; // Save } }
public ClassificationPostflop(HoldemHand hand, HoldemGamePhase phase, HoldemBoard board) { CardList communityCards = board.GetBoardAt(phase); // Default this.hand = HandType.Unknown; this.kicker = KickerType.Unknown; this.pair = PairType.Irrelevant; this.draw = DrawType.Irrelevant; this.straightDraw = StraightDrawType.None; Trace.Assert(communityCards.Count > 0, "Cannot classificate an empty list of community cards."); // Create a new list including the board cards and the cards from the hand CardList cards = new CardList(communityCards.Count + 2); foreach (Card c in communityCards) cards.AddCard(c); cards.AddCard(hand.GetFirstCard()); cards.AddCard(hand.GetSecondCard()); // --- Royal flush if (IsRoyalFlush(cards)) { this.hand = HandType.RoyalFlush; this.kicker = KickerType.Irrelevant; return; } // -- Four of a kind if (cards.HaveIdenticalFaces(4)) { this.hand = HandType.FourOfAKind; this.kicker = KickerType.Irrelevant; return; } // -- Full House // If we have three of a kind and two pair at the same time, we have a full house bool isThreeOfAKind = cards.HaveIdenticalFaces(3); bool isTwoPair = IsTwoPair(cards); if (isThreeOfAKind && isTwoPair) { this.hand = HandType.FullHouse; this.kicker = KickerType.Irrelevant; return; } // -- Flush for (int i = 0; i < cards.Count; i++) { int numCardsSameSuit = 0; for (int j = i + 1; j < cards.Count; j++) { if (cards[i].Suit == cards[j].Suit) { numCardsSameSuit++; } } if (numCardsSameSuit >= 4) { this.hand = HandType.Flush; this.kicker = KickerType.Irrelevant; return; } } // -- Straight if (IsStraight(cards)) { this.hand = HandType.Straight; this.kicker = KickerType.Irrelevant; return; } // Calculate draws (if we got until here, there might be some) // Also, no draws are possible at the river if (phase == HoldemGamePhase.River) { draw = DrawType.None; straightDraw = StraightDrawType.None; } else { draw = GetDrawType(cards); if (IsInsideStraightDraw(cards)) { straightDraw = StraightDrawType.InsideStraightDraw; } if (IsOpenEndedStraightDraw(cards)) { straightDraw = StraightDrawType.OpenEndedStraightDraw; } } // -- Trips if (isThreeOfAKind) { this.hand = HandType.ThreeOfAKind; this.kicker = KickerType.Irrelevant; return; } // -- Two pair if (isTwoPair) { this.hand = HandType.TwoPair; this.kicker = KickerType.Irrelevant; return; } // -- Pair Card matching; if (cards.HaveIdenticalFaces(2, out matching)) { // Sort list by face value (ace high first) cards.Sort(SortUsing.AceHigh); // Find kicker (check from end of the list where face values are higher) Card kicker = cards[0]; for (int i = cards.Count - 1; i >= 0; i--) { if (cards[i].Face != matching.Face) { kicker = cards[i]; break; } } this.hand = HandType.Pair; this.kicker = GetKickerTypeFromCard(kicker); this.pair = GetPairType(communityCards, matching, hand.GetFirstCard(), hand.GetSecondCard()); return; } // -- High card cards.Sort(SortUsing.AceHigh); Card highCard = cards.Last; this.hand = HandType.HighCard; this.kicker = GetKickerTypeFromCard(highCard); }
/* Returns true if the hand is a gapped connector and is within the range of hands (inclusive) * specified, ex. between 54 and JT (only the upper cards are checked) */ public bool IsGappedConnectorsInRange(int gap, HoldemHand lower, HoldemHand upper) { if (this.GetHigher().GetFaceValue() >= lower.GetHigher().GetFaceValue() && this.GetHigher().GetFaceValue() <= upper.GetHigher().GetFaceValue()) { return IsGappedConnectors(gap); } else return false; }