5
\$\begingroup\$

Badugi is a 4 card low hand poker game.

i / 13 is suit and i % 13 is rank. Cards from 0 to 51 inclusive.

Straights and flushes do not count. The best hand is A234. The worst hand is KKKK.

If you match on either a suit or rank you take out a card. Any 4 card hand beats a 3 card hand. Any 3 card hand beats a 2 card hand....

The way I approached was to start with a score of 4 and take 1 away on a match. Need to match on the or (rank or suite) as a card that matches on both is still just one card eliminated.

Would use this to mark how many hands you are ahead and behind.

Please comment on style and speed.

public static int BadugiStrength(int[] cards)
{
    int score = 4;            
    int i = cards[0];
    for(int j = 1; j <= 3; j ++)
    {
        if(i % 13 == cards[j] % 13 || i / 13 == cards[j] / 13)
        {
            score--;
            break;
        }
    }
    i = cards[1];
    for (int j = 2; j <= 3; j++)
    {
        if (i % 13 == cards[j] % 13 || i / 13 == cards[j] / 13)
        {
            score--;
            break;
        }
    }
    if (cards[2] % 13 == cards[3] % 13 || cards[2] / 13 == cards[3] / 13)
    {
        score--;
    }
    return score;
}

public static int Badugi()
{
    int counter = 0;
    int[] hand = new int[4];
    int[] scores = new int[5];
    for (int i = 51; i >=3; i--)
    {
        hand[0] = i;
        for (int j = i - 1; j >= 2; j--)
        {
            hand[1] = j;
            for (int k = j - 1; k >= 1; k--)
            {
                hand[2] = k;
                for (int m = k - 1; m >= 0; m--)
                {
                    int score = BadugiStrength(hand);
                    scores[score]++;
                    counter++;
                }
            }
        }
    }
    Debug.WriteLine($"4 card {scores[4]}  3 card {scores[3]}  2 card {scores[2]}  1 card {scores[1]}  total {scores[4] + scores[3] + scores[2] + scores[1]} {counter}");
    return counter;
}
\$\endgroup\$
1
  • \$\begingroup\$ @BrunoCosta i / 13 is wuit and i % 13 is rank. From what I can tell it is working. \$\endgroup\$
    – paparazzo
    Commented Apr 16, 2018 at 19:21

1 Answer 1

4
\$\begingroup\$

I'm focusing on the BadugiStrength function.


I like that BadugiStrength is a separate function for calculating the score of a single hand. That makes it easy to reuse and test individually. Any refactoring you make can easily be verified through tests.


It seems a little cumbersome to store two kinds of information about the card (suit and rank) into a single int variable. Since this is C#, I encourage either a class or struct. That way you can have two separate properties for suit and rank. (You can use integers or make enumerations for the suit and rank).


If you stick with a single int for each card, I recommend pulling the suit and rank comparisons into separate functions (though this may be useful even if you switch away from using a single int).

For example, you an IsSameSuit function would handle just the % 13 checks.

private static bool IsSameSuit(int card1, int card2)
{
    return (card1 % 13) == (card2 % 13)
}

And similarly for an IsSameRank function.

(I find the extra parentheses helpful because I don't have to remember precedence rules, but that's a matter of opinion. Feel free to leave them out.)

Then your if blocks can be rewritten as

if(IsSameSuit(i, cards[j]) || IsSameRank(i, cards[j]))
{
    score--;
    break;
}

You have a loop for the second card of each comparison (the j loops). I'd like to see the same type of loop for the first card (the i variable).

public static int BadugiStrength(int[] cards)
{
    int score = 4;            
    for (int i = 0; i <= 2; i++)
    {
       for (int j = i + 1; j <= 3; j++)
       {
           if (cards[i] % 13 == cards[j] % 13 || cards[i] / 13 == cards[j] / 13)
           {
               score--;
               break;
           }
        }
    }

    return score;
}

There are a lot of magic numbers in the code (int score = 4, j <= 3). I've never heard of Badugi, so I do not know if there are variants with a different hand size. If you declare a constant for the hand size, passing a five card hand would be an easy change to support. A constant for max rank (13) can handle smaller/larger deck sizes. It also means later when searching the code base, you can look for the 13 that means "card rank" and keep it different from another type of 13 (such as shoe size, player count, game length, etc).


Even better than a constant, you could use the built in length property of the array. Start your score at cards.Length, and use the length to set your array indices.

Putting it all together:

public struct Card
{
    public int Suit;
    public int Rank;
}

public static int BadugiStrength(card[] cards)
{
    int score = cards.Length;            
    for (int i = 0; i < cards.Length; i++)
    {
        for (int j = i + 1; j < cards.Length; j++)
        {
            if(IsSameSuit(cards[i], cards[j]) || IsSameRank(cards[i], cards[j])
            {
                score--;
                break;
            }
        }
    }

    return score;
}

private static bool IsSameSuit(card card1, card card2)
{
    return card1.Suit == card2.Suit
}

private static bool IsSameRank(card card1, card card2)
{
    return card1.Rank == card2.Rank
}
\$\endgroup\$
4
  • \$\begingroup\$ If the hand size changed then the number of loops would need to change. Or go with a radical different approach that is not apparent to me. \$\endgroup\$
    – paparazzo
    Commented Apr 16, 2018 at 20:12
  • \$\begingroup\$ I added a couple more examples, including a final example that accumulates my suggestions. Does that help you understand my example of the hand size changing? \$\endgroup\$
    – Brian J
    Commented Apr 16, 2018 at 20:25
  • 1
    \$\begingroup\$ I got the same answer. I see this just blows over the J loop on the last pass. I like this. \$\endgroup\$
    – paparazzo
    Commented Apr 16, 2018 at 20:58
  • \$\begingroup\$ I forgot to commend you on having a separate, testable function! \$\endgroup\$
    – Brian J
    Commented Apr 16, 2018 at 22:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.