0

I trying to create a little poker Texas Holdem simulator ..

In my solution have some class : enter image description here

Premise : I know about Fisher Yates algorithm. I will implement it later

Keep in mind that the next step I would like to implement a

  • Hands Evaluator for compare Players Hands
  • Odds Calculator for some odds like Hand vs Hand or when player go all in before river coming..
  • so on ..

I ask myself if have sense to implements a Class Hand which contains logic about compare Player Hands or continue to use a List.

Working with a List it's very comfortable but I think it's a little out of context of problem.

In poker world I talk about Hand , best Hand and so on ..

There is to implement also a CommunityCard/Board That we have Flop with 3 community Card , Turn: one card and River: one card..

And how this could interact with the eventual class Hand or other , for implements a Hand Evaluator ..

Does it make sense to create a Board or CommunityCard class? How would you implement it? With a List(5)? Would you use interfaces? How ?

My idea is to create a Hand and Board class. Hand class with a two private member Card.

Board class probably with a List. I don't know yet.

In Hand I know How to compare two different Hand but I don't know how compare 6 different Hand .. Have an idea ? Compare two by two and create a Class ranking that have a List with a ranking about 6 Hands?

The alternative without Hand class is continue to use a List and write all the logic about compare hand in a module Hand Evaluation.

So I wonder, How to best model the problem now?

I have some confusion about ..

I have particular difficulty thinking about using the interface and delegate when modeling the problem. Do you have any other suggestions on how to design well and useful tools in addition to the class diagram, I listen to all the advice ..

2
  • 2
    This question isn't a good fit for this site, in my opinion, as its too broad in that it asks "How to best model the problem now?". There is no one best way. I would suggest that you start writing some real code and then post a question here, with code samples, if you get stuck on something. – Graham Dec 23 '19 at 13:48
  • 1
    I agree with @Graham. This is too broad. Perhaps asking another question and limiting it to a specific area of the game would be better. – Greg Burghardt Jan 22 '20 at 13:58
1

Object oriented design

The alternative without Hand class is continue to use a List and write all the logic about compare hand in a module Hand Evaluation.

What you suggest is not impossible, but it's orthogonal to an object oriented approach. In OOP, you define concepts (objects) which represent a state. A hand, a given selection of cards, is a prime example of such an object.

If you were to avoid making classes like Hand and instead focused on list/array manipulation, that'd be the opposite of OOP and would tend more towards functional programming.

Functional programming isn't bad, but your question is specifically asking about OOP. So in scope of this question, keeping working with lists and arrays is not a good idea.

Consistent terminology

In poker world I talk about Hand , best Hand and so on ..

My idea is to create a Hand and Board class. Hand class with a two private member Card.

These two definitions of "hand" collide.

In the first instance, a hand is referring to any group of 5 cards which can be ranked against another hand (e.g. royal flush, full house, ...)

In the second instance, a hand is referring to are the two "non community" cards you are dealt.

Due to existing poker jargon, I suggest sticking with a hand being defined as a group of five cards used for scoring/ranking purposes.
In Texas Hold 'Em, the two "non community" cards you are dealt are often referred to as the "card in the hole" or "hole cards" (I've heard pocket cards too, infrequently).

I ask myself if have sense to implements a Class Hand which contains logic about compare Player Hands or continue to use a List.

It's sort of both. The list is used to store the specific cards, whereas the class can be used to treat this list as an object with its own business logic. As a rudimentary example:

public class Hand
{
    private IEnumerable<Card> cards;        

    public Hand(IEnumerable<Card> cards)
    {
        if (cards.Count() != 5)
            throw new ArgumentException("cards");

        this.cards = cards;
    }

    public bool Beats(Hand otherHand)
    {
        // comparison logic
    }
}

Note that I use IEnumerable since I want to make it impossible for the collection to be altered.


Separating the events from the data

There is to implement also a CommunityCard/Board That we have Flop with 3 community Card , Turn: one card and River: one card..

Gameplay wise, the flop/turn/river distinction is irrelevant for resolving the game. The only way in which this matter is when certain cards are revealed.

There is no purpose to tracking which card was revealed as part of the flop, turn, or river. Unless you specifically want to display this in your game, but that is not part of the rules of the game.

It suffices to track a single collection of 5 cards, which you add cards to whenever new cards are revealed. A rudimentary example:

public class Table
{
    public IReadOnlyList<Card> CommunityCards { get; } => _communityCards;
    private IList<Card> communityCards = new List<Card>();

    public void NewGame()
    {
        this.communityCards.Clear();
    }

    public void AddCommunityCards(params Card[] newCards)
    {
        this.communityCards.AddRange(newCards);
    }
}

Note that I omitted safety checks such as ensuring you can't add more than 5 cards to this list, etc.

The flop/turn/river are events that essentially trigger the addition of new cards. For example, if you were to use an event-driven approach:

public class Game
{
    public void OnFlop(EventArgs e)
    {
        myDeck.Burn(1);
        myTable.AddCommunityCards(myDeck.TakeNext(3));
    }

    public void OnTurn(EventArgs e)
    {
        myDeck.Burn(1);
        myTable.AddCommunityCards(myDeck.TakeNext(1));
    }

    public void OnRiver(EventArgs e)
    {
        myDeck.Burn(1);
        myTable.AddCommunityCards(myDeck.TakeNext(1));
    }
}

Again, this is just a simplified example. But you can see how using proper naming helps you with building a clear structure in which the intention of the code is easy to read. Even a non-developer would be able to spot a mistake in the rules here (e.g. dealing 2 cards on the river).


Does it make sense to create a Board or CommunityCard class?

Board, yes. It's essentially what I called Table in the above code.

Community card? No. It's still just a card, regardless of whether it's in your hand or on the table. There is no need for a second class to represent cards.

The distinction between a community card and a hole card is made by the variable/field/property in which the cards are stored, not the type of the card object itself.

How would you implement it? With a List(5)?

There is no way to natively limit the collection size of a list. You could use an array, but index handling is unnecessarily clumsy here. You could create your own derived list with a maximum size, but you could instead just validate your operations so that a list never grows outside of its intended boundaries.

The choice is up to you.

Would you use interfaces? How ?

Interfaces are useful for clean coding in general, but they don't really change the implementation of the board itself. I infer that your question is conflating the purpose of an interface and its implementation.

Given the simplicity of the current application, I'm inclined to ignore interfaces for now as it further complicates the issues you're already struggling with.


Finding the biggest

In Hand I know How to compare two different Hand but I don't know how compare 6 different Hand

Let me rephrase this to an equivalent statement which seems a lot more approachable:

I know How to compare two different numbers but I don't know how compare 6 different numbers.

Whether you're comparing hands or numbers, the comparison logic for 6 items remains the same. It's quite straightforward:

  • Pick up item 1
  • Compare the held item to item 2. Keep the biggest, discard the smallest.
  • Compare the held item to item 3. Keep the biggest, discard the smallest.
  • Compare the held item to item 4. Keep the biggest, discard the smallest.
  • Compare the held item to item 5. Keep the biggest, discard the smallest.
  • Compare the held item to item 6. Keep the biggest, discard the smallest.
  • The held item is the biggest item of them all.

Or, in code:

public int GetBiggestNumber(int[] numbers)
{
    int theBiggestNumberSoFar = numbers[0];

    for(int i = 1; i < numbers.Length; i++)
    {
        if(numbers[i] > theBiggestNumberSoFar)
        {
            theBiggestNumberSoFar = numbers[i];
        }
        // else: we do nothing
    }

    return theBiggestNumberSoFar;
}

Maybe unsurprisingly, this is such a common algorithm that there is a pre-existing implementation of it (using LINQ):

var theBiggestNumber = numbers.Max();

The main takeaway here is that finding the biggest or smallest value from a collection is a fairly simple and straightforward process. Here's the kicker:

If we are able to reduce a Hand to a numerical value, it becomes easy to find the best/worst hand by comparing these numerical values.

I'm not going to write the entire algorithm here, but the essential part is that you could create an enum for possible hand rankings and attach a numerical value to them:

public enum HandRanking
{
    RoyalFlush = 100,
    StraightFlush = 99,
    FourOfAKind = 98,
    // and so on...
}

Let's assume your Hand can tell you its ranking:

public class Hand
{
    public HandRanking GetRanking() { ... }
}

Checking two hands is simple:

(int)player1Hand.GetRanking() > (int)player2Hand.GetRanking()

And since we're comparing int values, we can essentially repeat the earlier algorithm for finding the "largest" hand value:

public Hand GetBestHand(Hand[] hands)
{
    Hand theBestHandSoFar= hands[0];

    for(int i = 1; i < hands.Length; i++)
    {
        if((int)hands[i].GetRanking() > theBestHandSoFar.GetRanking())
        {
            theBestHandSoFar= hands[i];
        }
        // else: we do nothing
    }

    return theBestHandSoFar;
}

If you want the easy LINQ alternative (although I do suggest you first learn to do with without LINQ, for training purposes):

var theBestHand = hands.OrderByDescending(hand => (int)hand.GetRanking()).First();

General advice

I have particular difficulty thinking about using the interface and delegate when modeling the problem.

It's good that you're trying to go the clean route from the beginning. Really good.

However, given your presumed skill level right now, I suggest first trying to build a working version (even if it's ugly) before you start trying to build a clean working version.
Even 10 years down the line, as a developer I often still first build a dirty hack before I then clean it up. Simply, this is because I can then focus on different things at different times, rather than trying to do it all at once.

Do you have any other suggestions on how to design well and useful tools in addition to the class diagram, I listen to all the advice

Again, the intention is good, but the question is so vague that it encompasses any tip about anything development related, which is not meaningfully answerable in scope of a single question on a Q&A site.

Instead, it may be better to just try and build it, and then come back to us with concrete questions about things you've done.

2
  • Don't forget you need to compare values of the card job case of combination name is the same. E.g. not all straight flushes are equal – RiaD Jan 22 '20 at 15:47
  • @RiaD: I have in general omitted overly detailed implementation as the question mostly focuses on the big picture. I'm providing a simple example for OP to get started with, which he can then tweak as he sees fit. But most importantly, OP seems to be in some state of analysis paralysis, so it seems more pertinent to present a simple start instead of piling on the complications. – Flater Jan 22 '20 at 15:50
0

I found the following exercise quite helpful when learning to model things in an object-oriented way: Just avoid any getters (i.e. any methods that return an object that already existed before the call). This is equivalent to adhering to the Law of Demeter.

I'm not saying you should do this all the time, I'm just saying if you want to learn, that is as simple as it gets. Also, it will feel weird, and will be much more difficult to do than just accessing data whenever you need it. However it will result in a good model almost automatically, and will sooner or later change the way you think about software and object-orientation. :)

Have fun! :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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