public double RankHand(Hand myHand, out string stringRank) { // keep track of the current max full bin..this is used for ranking the cards int start = DateTime.Now.Millisecond; int maxBin = -1; // keeps track of which suit, if any, is the flush int finalSuitIndex = -1; //long SortingStart = DateTime.Now.Ticks; myHand.HAND = SortHand(myHand.HAND); // high card will always be the first card in the hand since it is sorted in descending order bins.HighCardBin = myHand.HAND[0]; if (0 > maxBin) maxBin = 0; binFull[0] = true; int i = 0; while (i <= myHand.HAND.Count - 1) { Card card1 = myHand.HAND[i]; Card card2; // this is a special case where we need to check the first and last card since aces are high or low in straights. // if we are at the end of the hand assign card2 to the first card to see if it is an ace....otherwise continue scanning as normal if (i == myHand.HAND.Count - 1) card2 = myHand.HAND[0]; else card2 = myHand.HAND[i + 1]; int suitIndex = (int)card1.SUIT; int suitCard2 = (int)card2.SUIT; // checking for straight flushes...here we don't want to add aces until the end because they can be high or low. // aces won't get checked until we reach the end of the while loop. if (binFull[8] == false) { if (!card1.RANK.Equals(Card.Rank.Ace)) { List<Card> SFtemp = bins.StraightFlushBin[suitIndex, 0]; if (SFtemp.Count > 0) { if (Difference(SFtemp[SFtemp.Count - 1], card1) == 1 && (int)card1.SUIT == suitIndex) if (!SFtemp.Contains(card1)) { SFtemp.Add(card1); if (SFtemp.Count == SFtemp.Capacity) { maxBin = 8; finalSuitIndex = suitIndex; binFull[8] = true; } } } else SFtemp.Add(card1); } } // check to see if the bin is full List<Card> temp = bins.FlushBin[suitIndex, 0]; // if the bin is not full add the card to the flush bin if (binFull[5] == false) { temp.Add(card1); } // check to see if the flush bin is full if (temp.Count == temp.Capacity) { finalSuitIndex = suitIndex; binFull[5] = true; if (5 > maxBin) maxBin = 5; } // compute the difference between each hand an it's i+1 neighbor switch (Difference(card1, card2)) { // case where the face values differ by one here we would be concerned with only the straight bin or the straight //flush bin case 1: { // if the straight bin is not full if (binFull[4] == false) { // and the cards differ by one...and the bin doesn't contain that rank add them to the bin if (!bins.StraightBin.ContainsRank(card1.RANK)) bins.StraightBin.AddCard(card1); if (!bins.StraightBin.ContainsRank(card2.RANK)) bins.StraightBin.AddCard(card2); // check if the bin is full if (bins.StraightBin.HAND.Count == bins.StraightBin.HAND.Capacity) { binFull[4] = true; if (4 > maxBin) maxBin = 4; } } // otherwise clear the pair bin } // end case 1 ClearPairBins(); break; // special case where ace is low card in straight case 12: goto case 1; // case where the cards are equal...here we are interested in a multitude of different bins case 0: { // if pair bin is not full...add them to the pair bin if (binFull[1] == false) { bins.PairBin.AddCard(card1); bins.PairBin.AddCard(card2); // check to see if bin is full....which it should be. if (bins.PairBin.HAND.Count == bins.PairBin.HAND.Capacity) { binFull[1] = true; if (1 > maxBin) maxBin = 1; } } // case where two pair bin is not full if (binFull[2] == false) { // if twopair bin has a pair and the new found pair has a different rank than the current pair if (bins.TwoPairBin.HAND.Count == 2 && card1.RANK != bins.TwoPairBin.HAND[0].RANK) { // add the new pair to the bin bins.TwoPairBin.AddCard(card1); bins.TwoPairBin.AddCard(card2); } // else add the pair to the two pair bin because it should be empty else { if (bins.TwoPairBin.HAND.Count == 0) { bins.TwoPairBin.AddCard(card1); bins.TwoPairBin.AddCard(card2); } } // check if bin is full if (bins.TwoPairBin.HAND.Count == bins.TwoPairBin.HAND.Capacity) { binFull[2] = true; if (2 > maxBin) maxBin = 2; } } // case where three of a kind bin is not full if (binFull[3] == false) { // if there are no cards then add the pair. if (bins.ThreeKindBin.HAND.Count == 0) { bins.ThreeKindBin.AddCard(card1); bins.ThreeKindBin.AddCard(card2); } else { if (bins.ThreeKindBin.HAND[0].RANK == card1.RANK) { bins.ThreeKindBin.AddCard(card1); bins.ThreeKindBin.AddCard(card2); } else { bins.ThreeKindBin.HAND.Clear(); } } // check if bin is full if (bins.ThreeKindBin.HAND.Count == bins.ThreeKindBin.HAND.Capacity) { binFull[3] = true; if (3 > maxBin) maxBin = 3; } } // case where full house is a special case where if the three of a kind bin is full and the two pair bin is full // then we know that our full house is the three of a kind bin plus the pair in the two pair bin whos face value // is different than the three of a kind face value. if (binFull[6] == false) { if (binFull[3] == true && binFull[2] == true) { // immediatly add the three of a kind since we know they will be part of the full house. bins.FullHouseBin.AddCards(bins.ThreeKindBin.HAND); // if the first two cards in the sorted twopair bin have a different rank than the three of a kind... // then we know that is our maximum full house if (SortHand(bins.TwoPairBin.HAND)[0].RANK != bins.ThreeKindBin.HAND[0].RANK) bins.FullHouseBin.AddCards(SortHand(bins.TwoPairBin.HAND).GetRange(0, 2)); // otherwise the highest pair in the twopair bin is also containted in the three kind. // thus making the pair in our full house the lower of the two pair. else bins.FullHouseBin.AddCards(SortHand(bins.TwoPairBin.HAND).GetRange(2, 2)); } if (bins.FullHouseBin.HAND.Count == bins.FullHouseBin.HAND.Capacity) { binFull[6] = true; if (6 > maxBin) maxBin = 6; } } // case of four of a kind if (binFull[7] == false) { bins.FourKindBin.AddCard(card1); bins.FourKindBin.AddCard(card2); if (bins.FourKindBin.HAND.Count == bins.FourKindBin.HAND.Capacity) { binFull[7] = true; if (7 > maxBin) maxBin = 7; } } } // end case 0 break; default: ClearBins(); break; } // end switch i++; }// end while // final check to see it the straight flush bin needs any aces to make it's hand if (binFull[8] == false) { // case of high card being ace if (bins.HighCardBin.RANK.Equals(Card.Rank.Ace)) { List<Card> temp = bins.StraightFlushBin[(int)bins.HighCardBin.SUIT, 0]; if (temp.Count == 4) { if (Difference(bins.HighCardBin, temp[temp.Count - 1]) == 12) if (!temp.Contains(bins.HighCardBin)) temp.Add(bins.HighCardBin); if (Difference(bins.HighCardBin, temp[0]) == 1) if (!temp.Contains(bins.HighCardBin)) temp.Insert(0, bins.HighCardBin); } } // case of a pair of aces that need to be checked if (binFull[1]) { if (bins.PairBin.HAND[0].RANK.Equals(Card.Rank.Ace)) { foreach (Card item in bins.PairBin.HAND) { List<Card> temp = bins.StraightFlushBin[(int)item.SUIT, 0]; if (temp.Count == 4) { if (Difference(item, temp[temp.Count - 1]) == 12) if (!temp.Contains(item)) temp.Add(item); if (Difference(item, temp[0]) == 1) if (!temp.Contains(item)) temp.Add(item); } } // end for each }// end check if pair bin rank is ace } // end check if pair bin is full // case of three of a kind aces that all need to be checked if (binFull[3]) { if (bins.ThreeKindBin.HAND[0].RANK.Equals(Card.Rank.Ace)) { foreach (Card item in bins.ThreeKindBin.HAND) { List<Card> temp = bins.StraightFlushBin[(int)item.SUIT, 0]; if (temp.Count == 4) { if (Difference(item, temp[temp.Count - 1]) == 12) if (!temp.Contains(item)) temp.Add(item); if (Difference(item, temp[0]) == 1) if (!temp.Contains(item)) temp.Add(item); } } // end foreach } // end check for tkind rank Ace } // end if three kind bin full } // end check for final straight flush for (int j = 0; j < bins.StraightFlushBin.Length; j++) { if (bins.StraightFlushBin[j, 0].Count == bins.StraightFlushBin[j, 0].Capacity) { binFull[8] = true; finalSuitIndex = j; maxBin = 8; } } // remove the cards in the bin from the players current hand and then use the rest of the cards to rank the highest kickers. string finalRank = Convert.ToString(maxBin) + "."; Hand tempHand = new Hand(); // in this switch case the hand rank is built by converting the bin index to a string then appending "." to make is a double. // each sucessive cards in their ranking order are multiplied by .01 then parsed into a string and concatinated to the overall rank. // for example: AsJs10s9s4s would be ranked 5.1411100904 then converted to a double for easy comparison in dertermining a winning hand. // trim all the periods our of the double values char[] trims = { '.' }; switch (maxBin) { // case 0 and 5 are different cases because they have different types then the rest of the other bins. // case of a high card hand case 0: // in this case the first five cards will be your best hand. for (int j = 0; j < myHand.HAND.Count; j++) { string temp = Convert.ToString(.01 * (double)myHand.HAND[j].RANK).Replace("0.", ""); if (j < myHand.NumCardsToRank) finalRank += temp; else break; } break; // case where hand is a flush case 5: tempHand.HAND = bins.FlushBin[finalSuitIndex, 0]; foreach (Card item in tempHand.HAND) { string temp = Convert.ToString(.01 * (double)item.RANK).Replace("0.", ""); finalRank += temp; } break; // case where we have a straight flush case 8: tempHand.HAND = bins.StraightFlushBin[finalSuitIndex, 0]; foreach (Card item in tempHand.HAND) { string temp = Convert.ToString(.01 * (double)item.RANK).Replace("0.", ""); finalRank += temp; } break; // any other case is kept in the bin map default: tempHand = binMap[maxBin]; Hand newHand = new Hand(myHand.MaxHandSize, myHand.NumCardsToRank); foreach (Card item in tempHand.HAND) { string temp = Convert.ToString((.01 * (double)item.RANK)).Replace("0.", ""); newHand.AddCard(item); finalRank += temp; } List<Card> foo = myHand.RemoveCards(tempHand.HAND); int index = 0; while (newHand.HAND.Count < myHand.NumCardsToRank) { string temp = Convert.ToString(.01 * (double)myHand.HAND[index].RANK).Replace("0.", ""); finalRank += temp; newHand.AddCard(myHand.HAND[index]); index++; } myHand.AddCards(newHand.HAND); break; } int stop = DateTime.Now.Millisecond; //Console.WriteLine("It took {0} Milliseconds to Rank the hand", stop - start); //if (stop - start > 0) // Console.ReadLine(); stringRank = finalRank; return Convert.ToDouble(finalRank); }
public Bins() { pair = new Hand(2, 5); twoPair = new Hand(4, 5); threeKind = new Hand(3, 5); straight = new Hand(5, 5); fourKind = new Hand(4, 5); fullHouse = new Hand(5, 5); // this probably isnt the way to do this... highCard = new Card(); pair.HAND = new List<Card>(); pair.HAND.Capacity = 2; twoPair.HAND = new List<Card>(); twoPair.HAND.Capacity = 4; threeKind.HAND = new List<Card>(); threeKind.HAND.Capacity = 3; straight.HAND = new List<Card>(); straight.HAND.Capacity = 5; flush = new List<Card>[5, 1]; for (int i = 0; i < 5; i++) { flush[i, 0] = new List<Card>(); flush[i, 0].Capacity = 5; } fullHouse.HAND = new List<Card>(); fullHouse.HAND.Capacity = 5; fourKind.HAND = new List<Card>(); fourKind.HAND.Capacity = 4; straightFlush = new List<Card>[5, 1]; for (int i = 0; i < 5; i++) { straightFlush[i, 0] = new List<Card>(); straightFlush[i, 0].Capacity = 5; } }
public Dictionary<Hand, int> ComputeOuts(Hand h, List<Card> holecards, List<Card> community, out Dictionary<Hand, List<Card>> whichouts) { Deck d = new Deck(); HandEvaluator he = new HandEvaluator(); Dictionary<Hand, int> handOuts = new Dictionary<Hand, int>(); Dictionary<Hand, List<Card>> handWhichOuts = new Dictionary<Hand, List<Card>>(); Dictionary<Hand, double> handRanks = new Dictionary<Hand, double>(); Dictionary<Hand, double> handWithoutCard = new Dictionary<Hand, double>(); // C# WHAT? This codes no works //List<string> testing = new List<string>(); //testing.Add("t"); //testing.Add("a"); //Console.WriteLine("{0}", testing); //testing.Remove("t"); //Console.WriteLine("{0}", testing); //return handOuts; // END This codes no works // initialize dict and remove visible cards to everyone. Outs are only computed on cards we can't see. handOuts.Add(h, 0); handWhichOuts.Add(h, new List<Card>()); d.RemoveCards(community); d.RemoveCards(holecards); // for every card left in the deck not visible, see which cards unilaterally improve hands positions relative to eachother // the card that produces a move to the best hand gets counted for that hand. foreach (Card c in d.DECK) { string handRank; double boardRankwith = 0.0; double boardRankwithout = 0.0; // Rank with card h.AddCard(c); //Console.WriteLine("NumCardsToRank: {0}\nMaxHandSize: {1}", h.NumCardsToRank, h.MaxHandSize); he = new HandEvaluator(); handRanks[h] = he.RankHand(h, out handRank); // Rank without card h.RemoveCard(c); he = new HandEvaluator(); handWithoutCard[h] = he.RankHand(h, out handRank); // Rank Board Cards if (community.Count > 0) { // board rank without card he = new HandEvaluator(); boardRankwithout = he.RankHand(new Hand(community, 7, 5), out handRank); // board rank with card he = new HandEvaluator(); community.Add(c); boardRankwith = he.RankHand(new Hand(community, 7, 5), out handRank); community.Remove(c); // Any card that increases your position one level and your position is at least 2 hand levels better than the board with the card. // we do two levels because if the board pairs and is a favorable pair to your hole cards, you hand will increase 2x. 2-pair -> board pairs -> full house. // all other cases should be handled by the if statement below. if ((Math.Round(handRanks[h], 2) > Math.Round(handWithoutCard[h], 2)) && (Math.Abs((int)handRanks[h] - (int)boardRankwith)) >= 2) { //Console.WriteLine("Card: {0}\nHoleCards: {1}\nCommunity: {2}\nHandRankWith: {3}\nHandRankWithout: {4}\nBoardRankWith: {5}\nBoardRankWithout: {6}", c.CardToString(), new Hand(holecards, 7,5).HandToString(), new Hand(community, 7 ,5).HandToString(), handRanks[h], handWithoutCard[h], boardRankwith, boardRankwithout); handOuts[h]++; handWhichOuts[h].Add(c); //Console.ReadLine(); } // The above if block does not catch all cases. If you have the same rank as the board but a higher pair, three kind, etc.. these are still favorable outs. // Example: Holecards: Ac2s Board: As5d5c // Board has a pair and you have a pair, but your pair is better. if (Math.Round(handRanks[h], 2) > Math.Round(boardRankwith, 2) && (Math.Round(handRanks[h], 2) > Math.Round(handWithoutCard[h], 2)) && (int)handRanks[h] == (int)boardRankwith && ((int)handRanks[h] == 4 || (int)handRanks[h] == 5 || (int)handRanks[h] == 8)) { // your hand is better than boards. You hand with the card is better than your previous hand. you have the same hand as the board. and the hand is a straight, flush, or straight flush. //Console.WriteLine("Card: {0}\nHoleCards: {1}\nCommunity: {2}\nHandRankWith: {3}\nHandRankWithout: {4}\nBoardRankWith: {5}\nBoardRankWithout: {6}", c.CardToString(), new Hand(holecards, 7, 5).HandToString(), new Hand(community, 7, 5).HandToString(), handRanks[h], handWithoutCard[h], boardRankwith, boardRankwithout); handOuts[h]++; handWhichOuts[h].Add(c); //Console.ReadLine(); } } //// who ever is the winner after this card is delt gets that out. //// this is more for people watching the play-by-play rather than analysis. Players only know what cards they can see. //foreach (KeyValuePair<Hand, double> kv in handRanksSort) //{ // handCurrentPosition[kv.Key] = handRanksSort.IndexOf(kv); // // keep a list of the number of outs and the actual cards which are considered outs for that hand. // handOuts[handRanksSort[0].Key]++; // handWhichOuts[handRanksSort[0].Key].Add(c); //} } whichouts = handWhichOuts; return handOuts; }
static void Main(string[] args) { // takes a full path to a directory that contains raw *.txt poker history files from 888 poker. IHandHistoryParserFactory handHistoryParserFactory = new HandHistoryParserFactoryImpl(); // Get the correct parser from the factory. var handHistoryParser = new Poker888FastParserImpl(); HandHistoryParserFastImpl fastParser = handHistoryParser as HandHistoryParserFastImpl; try { // The true causes hand-parse errors to get thrown. If this is false, hand-errors will // be silent and null will be returned. string[] files = Directory.GetFiles(args[0], "*.txt", SearchOption.AllDirectories); int fileCount = files.Length; foreach (string file in files) { Console.WriteLine("number of files left {0} out of {1}", fileCount--, files.Length); string fileText = new StreamReader(file).ReadToEnd(); var hands = fastParser.SplitUpMultipleHandsToLines(fileText); var outputFile = new StreamWriter(file + ".csv"); outputFile.WriteLine("DateOfHandUtc,HandId,DealerButtonPosition,TableName,GameDescription,NumPlayersActive,NumPlayersSeated,Rake,ComumnityCards,TotalPot,PlayerName,HoleCards,StartingStack,SeatNumber,ActionNumber,Amount,HandActionType,Outs,CardOuts,CurrentHandRank,currentPostSize,Street,IsAggressiveAction,IsAllIn,IsAllInAction,IsBlinds,IsGameAction,IsPreFlopRaise,IsRaise,IsWinningsAction"); foreach (var hand in hands) { var parsedHand = fastParser.ParseFullHandHistory(hand, true); // probably not the best way to do this. This should be added to the ParseHandActions function or something. decimal currentPotSize = 0; int actionNumber = 0; foreach (var action in parsedHand.HandActions) { // I tried to do this (this = 'actionNumber++') rtdfgcvdrt properly via the ParseHandActions function in Poker888FastParserImpl.cs file to be 1-1 with the raw handlines, however // I was getting some un-expected behavior when the action was all in and the winning action the handline index would be 0 so you would end up with // actions sequences like 1,2,3,4,5,6,0,8,9,10. actionNumber++; if (action.HandActionType == HandHistories.Objects.Actions.HandActionType.ANTE || action.HandActionType == HandHistories.Objects.Actions.HandActionType.BET || action.HandActionType == HandHistories.Objects.Actions.HandActionType.SMALL_BLIND || action.HandActionType == HandHistories.Objects.Actions.HandActionType.RAISE || action.HandActionType == HandHistories.Objects.Actions.HandActionType.BIG_BLIND || action.HandActionType == HandHistories.Objects.Actions.HandActionType.CALL || action.HandActionType == HandHistories.Objects.Actions.HandActionType.ALL_IN) { currentPotSize += action.Amount; } // Don't judge me.... this code is just chaos...but it's works. string handRank; HandEvaluator.HandEvaluator he = new HandEvaluator.HandEvaluator(); string handstring = String.Format("{0}{1}", parsedHand.Players.First(p => p.PlayerName.Equals(action.PlayerName)).HoleCards, parsedHand.ComumnityCards); string flopstring; string turnstring; string riverstring; string currenthandstring; Hand h = new Hand(); // holecards Hand hc = new Hand(); Dictionary<Hand, int> handOuts = new Dictionary<Hand, int>(); Dictionary<Hand, List<Card>> cardouts; int outs = 0; double hr = 0.0; string couts = ""; if (handstring.Length >= 14) { currenthandstring = ""; flopstring = handstring.Substring(4, 6); turnstring = handstring.Substring(10, 2); riverstring = handstring.Substring(12, 2); // build the hand play-by-play so we can get current hand ranking and also build in hand outs. switch (action.Street) { case HandHistories.Objects.Cards.Street.Flop: currenthandstring = handstring.Substring(0, 4) + flopstring; h = new Hand(currenthandstring, 7, 5); hc = new Hand(handstring.Substring(0, 4), 7, 5); hr = he.RankHand(h, out handRank); handOuts = he.ComputeOuts(h, hc.HAND, new Hand(flopstring, 7, 5).HAND, out cardouts); outs = handOuts[h]; couts = new Hand(cardouts[h], 52, 5).HandToString(); break; case HandHistories.Objects.Cards.Street.Turn: currenthandstring = handstring.Substring(0, 4) + flopstring + turnstring; h = new Hand(currenthandstring, 7, 5); hc = new Hand(handstring.Substring(0, 4), 7, 5); hr = he.RankHand(h, out handRank); handOuts = he.ComputeOuts(h, hc.HAND, new Hand(flopstring + turnstring, 7, 5).HAND, out cardouts); outs = handOuts[h]; couts = new Hand(cardouts[h], 52, 5).HandToString(); break; case HandHistories.Objects.Cards.Street.River: currenthandstring = handstring.Substring(0, 4) + flopstring + turnstring + riverstring; h = new Hand(currenthandstring, 7, currenthandstring.Length / 2); hc = new Hand(handstring.Substring(0, 4), 7, 5); hr = he.RankHand(h, out handRank); outs = 0; // No need to compute outs on the river. No more cards to come. break; default: hr = 0.0; outs = 0; break; } } else { hr = 0.0; outs = 0; } outputFile.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28},{29}", parsedHand.DateOfHandUtc , parsedHand.HandId , parsedHand.DealerButtonPosition , parsedHand.TableName , parsedHand.GameDescription , parsedHand.NumPlayersActive , parsedHand.NumPlayersSeated , parsedHand.Rake , parsedHand.ComumnityCards , parsedHand.TotalPot , action.PlayerName , parsedHand.Players.First(p => p.PlayerName.Equals(action.PlayerName)).HoleCards , parsedHand.Players.First(p => p.PlayerName.Equals(action.PlayerName)).StartingStack , parsedHand.Players.First(p => p.PlayerName.Equals(action.PlayerName)).SeatNumber , actionNumber , action.Amount , action.HandActionType , outs.ToString() , couts , hr.ToString() , currentPotSize , action.Street , action.IsAggressiveAction , action.IsAllIn , action.IsAllInAction , action.IsBlinds , action.IsGameAction , action.IsPreFlopRaise , action.IsRaise , action.IsWinningsAction ); } } outputFile.Close(); } } catch (Exception ex) // Catch hand-parsing exceptions { Console.WriteLine("Parsing Error: {0}", ex.Message); // Example logging. Console.ReadLine(); } }