コード例 #1
0
        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);
        }
コード例 #2
0
            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;
                }
            }
コード例 #3
0
        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;
        }
コード例 #4
0
        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();
            }
        }