4

I'm making a dice game. There are 5 dice in which I have all the values for and I need to determine if they make up a full house (3 of one and 2 of another), small straight (1-4, 2-6 or 3-6) or a large straight (1-5, 2-6).

Perhaps the best way to approach this seems to be to use regular expressions.

Does anyone know how I would go about representing these rules in regex?

Or if you can provide a better solution, I'd appreciate that.

Examples:

  • Full house = 44422 or 11166 or 12212 etc.
  • Small Straight = 12342 or 54532 etc.
  • Large Straight = 12345 or 52643 etc.

Edit
Changed wording to highlight that this is my inexperienced opinion.

I know how to achieve this using code, but it seems like such a long winded solution, I'm wondering if there's a more simplistic approach.

Lloyd Powell
  • 18,270
  • 17
  • 87
  • 123
  • 6
    Why did you decide that regexes would be the best approach, if you don't know how to write the required regexes? (IMO, regexes are a very *bad* approach here.) – Oliver Charlesworth Jun 26 '11 at 10:17
  • 4
    I hate this new "free question downvote" system. What is wrong with this question people? Why do you vote it down? I know why, because it's free – Armen Tsirunyan Jun 26 '11 at 10:20
  • No need for Regular Expressions here. – Caspar Kleijne Jun 26 '11 at 10:21
  • 1
    @ThePower - Have a look at this [The Great Poker Hand Evaluator Roundup](http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup) - you even get some ninjas :) –  Jun 26 '11 at 10:21
  • 2
    @Armen, I agree, the question is ok, The OP should be delivered a proper solution, however his approach is wrong. Instead of downvoting ppl should answer and perhaps teach. – Caspar Kleijne Jun 26 '11 at 10:22
  • @Oli Charlesworth - So what is the best approach... – Lloyd Powell Jun 26 '11 at 11:05

7 Answers7

5

I would order all the numbers decreasing and then do some linear criteria matching on each value as you go along it whether it be in an int[] or a string.

Craig White
  • 13,492
  • 4
  • 23
  • 36
  • 4
    Since when do dice have Kings, Queens, Jacks and Aces? Everyone is reading Full House and automatically think it's poker and ignore the fact that dice are mentioned three times :) – Till Jun 26 '11 at 11:00
  • 2
    [Poker dice](http://en.wikipedia.org/wiki/Poker_dice) - I know what your saying but the techniques used in poker algorithms are well suited to dice –  Jun 26 '11 at 11:28
4

Don't know about c#, but in a scripting language I'd take the regexp route. For each side, calculate how many times it occurs in the combination and join the results together. For example, for the combination 12342 the counter string will be 121100. Then match the counter string against these patterns:

/5/         = Five of a kind
/4/         = Four of a kind
/20*3|30*2/ = Full house
/1{5}/      = Large Straight
/[12]{4}/   = Small Straight
/3/         = Three of a kind
/2[013]*2/  = Two pair
/2/         = One pair
user187291
  • 53,363
  • 19
  • 95
  • 127
  • I accepted this as the answer, not because of the regex (which is a good library matcher also), but for the simply fact that you mentioned the counter string. I like this approach a lot and makes life much easier. Thanks. – Lloyd Powell Jun 30 '11 at 11:42
0

You could try to put your values in to a list. This would allow you to quickly sort your values. And if you add the values that would give you the hand. 111AA = 29 and 222KK = 30. Just an idea.

0

Here is my Code:

    public static int   CalculateTotalOfSingles       (int pipNumber)
    {
        //
        var myScore = 0;
        foreach (var myDie in Roll5Player.MyHand.Dice)
        {
            {   if (myDie.Pips == pipNumber)
                myScore+= pipNumber;
            }
        }
        //
        return myScore;
    }
    public static int   CalculateDicePips       ()
    {
        //
        var myScore = 0;
        foreach (var myDie in Roll5Player.MyHand.Dice)
        {
            {myScore += myDie.Pips;
            }
        }
        //
        return myScore;
    }
    //
    //
    //
    public static int   CalculateTotalOfAllSingles    (int pipNumber)
    {
        //
        var myScore = 0;
        for (int i = 1; i <= 6; i++)
        {
            myScore += pipNumber;
        }
        //
        return myScore;
    }
    public static bool  CalculateIsNOfaKind     (int count)
    {
        //
        for (var index = 1; index <= 6; index++)
        {
            var cntr = 0;
            foreach (var myDie in Roll5Player.MyHand.Dice)
            {
                if (myDie.Pips == index)
                    cntr++;
            }
            //
            if (cntr == count)
            {
                return true;
                ;
            }
        }
        //
        return false;
    }
    public static int   CalculateNOfaKind       (int count  )
    {
        //
        var myScore = 0;
        for (var index = 1; index <= 6; index++)
        {
            var cntr = 0;
            foreach (var myDie in Roll5Player.MyHand.Dice)
            {
                    if (myDie.Pips == index)
                        cntr++;
            }
            //
            if (cntr >= count)
            {   myScore = CalculateDicePips();
                return myScore;
                ;
            }
        }
        //
        return myScore;
    }
    /// 
    public static int   CaluclateFullHouse      (           )
    {
        // 
        var myScore = 0;
        var cntr    = new int[6];
        for (var index = 1; index <= 6; index++)
        {
            foreach (var myDie in Roll5Player.MyHand.Dice)
            {
                if (myDie.Pips == index)
                    cntr[index-1]++;
            }
        }

        //
        var boolCondA = false;
        var boolCondB = false;
        foreach (var i in cntr)
        {
            if (i == 3)
            {boolCondA = true;
                break;
            }
        }
        if (boolCondA)
        {
            foreach (var i in cntr)
            {
                if (i == 2)
                {boolCondB = true;
                    break;
                }
            }
        }


        //
        if (boolCondB ) 
            myScore = CalculateDicePips();



        //
        //
        //
        return myScore;
    }
    public static int   CaluclateStraights      (int straightCount, int score)
    {
        // 
        var tempPip     = 0;
        var myScore     = 0;
        var isFirstIteration = true;
        var cntr = 0;
        int[] sortedDice = new int[5];
        var sortedDiceLise = new List<int>();


        foreach (var myDie in Roll5Player.MyHand.Dice)
        {
            sortedDiceLise.Add(myDie.Pips);
        }
        sortedDiceLise.Sort();







        foreach (var myDie in sortedDiceLise)
        {
            //
            //
            if (!isFirstIteration)
            {
                if (myDie == tempPip + 1)
                    cntr++;
            }
            //
            isFirstIteration = false;
            tempPip = myDie;
        }





        if (cntr == straightCount - 1)
        {myScore = score;
        }

        //
        //
        //
        return myScore;
    }

    public static int   CalculateYahtzee        ()
    {
        //
        for (var index = 1; index <= 6; index++)
        {
            var cntr = 0;
            foreach (var myDie in Roll5Player.MyHand.Dice)
            {
                if (myDie.Pips == index)
                    cntr++;
            }
            //
            if (cntr == 5)
            {
                return 50;
                ;
            }
        }
        //
        return 0;
    }
0

You could always do a LINQ aggregate query and count the number of same cards. It would be something similar to (can't test it):

var query =  from card in hand
             group card by card into groupedCards
             select new { Card = groupedCards.Key, Count = groupedCards.Count() };

This way you would easily know if you are dealing with a possible straight (or nothing at all), a pair, a triple, etc.

I am no LINQ expert and I can not test this bit of code at the moment, so I am not sure if it will compile as it is, but it might help you or give you an idea on how to approach the problem at hand.

For example:

  1. if count query = 5 : We are dealing with an empty hand, a flush or a straight => Run particular flush/straight logic.

  2. if count query = 4 : We are dealing with a single pair.

  3. if count query = 3 : We are dealing with a double pair or a triple => if max count =3 then triple

  4. if count query = 2 : We are dealing with a full house / poker. If max count = 4 then poker

InBetween
  • 32,319
  • 3
  • 50
  • 90
0

I won't comment on how you seek the results, but rather on how you store the result for later lookup.

Since you have only 46656 possible combinations and one byte can store the resulting hand strength, this problem is much easier than a poker hand problem.

You can have a lookup table, consisting of hands as indexes and associated with results (some hands can have multiple results) of that hand. Each byte can store all hand types as a binary representation (hopefully, if not use a short).

Each number you get (eg. 66655 - full house) is a number in base six (1-6), convert it into a base 10 number to get the index in the lookup table.

It will require about 46656 bytes (+ CPU alignment), and can fit into CPU L2 cache. Speed would be enourmous, since the only operation you would need to do is convert number base, and the binary OR operation to extract a hand strenght.

What you will miss is the real strength of a hand. Eg. 66655 is better than 66644. You can easily figure that out - you will need a bigger type to store result into :)

Marino Šimić
  • 7,318
  • 1
  • 31
  • 61
0

I decided to try myself, and I ended up not using regular expressions -- I thought maybe with the simplicity of the searches required, regular expressions would add more complexity than they save. I used similar logic to another answer though: count the quantity of each number and base all the scoring on that:

  enum RollScoreType
  {
     HighDie,
     Pair,
     TwoPair,
     ThreeOfAKind,
     SmallStright,
     PairSmallStriaght,
     FullHouse,
     LargeStraight,
     FourOfAKind,
     FiveOfAKind
  }

  struct RollScore
  {
     public RollScoreType type;
     public byte highestDie;
     public byte nextHighestDie;
     public RollScore(RollScoreType type, byte highest, byte next)
     {
        this.type = type;
        this.highestDie = highest;
        this.nextHighestDie = next;
     }
     public override string ToString()
     {
        return string.Format("{0} {1} {2}", type, highestDie, nextHighestDie);
     }
  }

  static RollScore GetDiceScore(string input)
  {
     char[] dice = input.ToCharArray();
     byte[] diceCount = new byte[6];

     for (int i = 0; i < dice.Length; i++)
        diceCount[int.Parse(dice[i].ToString())-1]++;

     if (Array.IndexOf(diceCount, (byte)5) >= 0)
        return new RollScore(RollScoreType.FiveOfAKind, (byte)(Array.IndexOf(diceCount, (byte)5) + 1), 0);
     else if (Array.IndexOf(diceCount, (byte)4) >= 0)
        return new RollScore(RollScoreType.FourOfAKind, (byte)(Array.IndexOf(diceCount, (byte)4) + 1), (byte)(Array.IndexOf(diceCount, (byte)1) + 1));
     else if (Array.IndexOf(diceCount, (byte)3) >= 0)
     {
        byte three = (byte)(Array.IndexOf(diceCount, (byte)3) + 1);
        if (Array.IndexOf(diceCount, (byte)2) >= 0)
        {
           byte pair = (byte)(Array.IndexOf(diceCount, (byte)2) + 1);
           return new RollScore(RollScoreType.FullHouse, Math.Max(pair, three), Math.Min(pair, three));
        }
        else
           return new RollScore(RollScoreType.ThreeOfAKind, three, (byte)(Array.LastIndexOf(diceCount, (byte)1) + 1));
     }
     else if (Array.IndexOf(diceCount, (byte)2) >= 0)
     {
        byte pair = (byte)(Array.IndexOf(diceCount, (byte)2) + 1);
        byte highPair = (byte)(Array.LastIndexOf(diceCount, (byte)2) + 1);
        if (highPair != pair)
           return new RollScore(RollScoreType.TwoPair, highPair, pair);
        else
        {
           byte lowMissingDie = (byte)Array.IndexOf(diceCount, (byte)0);
           byte highMissingDie = (byte)Array.LastIndexOf(diceCount, (byte)0);
           switch (lowMissingDie)
           {
              case 0:
                 if (highMissingDie == 5)
                    return new RollScore(RollScoreType.PairSmallStriaght, 5, 4);
                 if (highMissingDie == 1)
                    return new RollScore(RollScoreType.PairSmallStriaght, 6, 5);
                 break;
              case 4:
                 return new RollScore(RollScoreType.PairSmallStriaght, 4, 3);
           }
           return new RollScore(RollScoreType.Pair, pair, (byte)(Array.LastIndexOf(diceCount, (byte)1) + 1));
        }
     }
     byte missingDie = (byte)Array.IndexOf(diceCount, (byte)0);
     switch(missingDie)
     {
        case 0:
           return new RollScore(RollScoreType.LargeStraight, 6, 5);
        case 1:
           return new RollScore(RollScoreType.SmallStright, 6, 5);
        case 4:
           return new RollScore(RollScoreType.SmallStright, 4, 3);
        case 5:
           return new RollScore(RollScoreType.LargeStraight, 5, 4);                  
        default:
           return new RollScore(RollScoreType.HighDie, 6, (byte)(Array.LastIndexOf(diceCount, (byte)1, 3) + 1));
     }
  }

I discovered, to my surprise, that the probability of a small straight and a large straight are equal in 5-die rolls. Is that true!?

EDIT: Fixed; I see that when I include small straights that include a pair, the probability of a small straight goes up significantly.

When I think about it, a pair and a small straight should probably use the pair as the highest die and the highest number in the straight as the next highest (in order to [properly compare two rolls that are both a pair with a small straight). If so, I'd replace the block of code for handling PairSmallStraight with this:

           switch (lowMissingDie)
           {
              case 0:
                 if (highMissingDie == 5)
                    return new RollScore(RollScoreType.PairSmallStriaght, pair, 5);
                 if (highMissingDie == 1)
                    return new RollScore(RollScoreType.PairSmallStriaght, pair, 6);
                 break;
              case 4:
                 return new RollScore(RollScoreType.PairSmallStriaght, pair, 4);
           }
BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • Actually if the score is a "HighDie", I think you can hard code 6 and 5 as the highest two dice. I don't think you can get any roll that has no other score without having it contain a 5 and a 6. – BlueMonkMN Jun 26 '11 at 12:58
  • I just noticed that this will not detect if you have a pair and a small straight... I suppose I should work on that. – BlueMonkMN Jun 26 '11 at 12:59
  • That's better. Now I can see that small straights are more common even than a Full House. – BlueMonkMN Jun 26 '11 at 13:21