A Circular Dilemma

Sunday, January 25, 2009

During an interview for a programming job, you're given the following problem:

Implement a simple "PokerTable" class in the language of your choice. Each PokerTable object should maintain an ordered collection of between 0 and 10 PokerSeat objects. Each PokerSeat object may be either empty, or occupied by a PokerPlayer object.

Included is a helpful UML diagram:

You give it some thought, paying careful attention to the UML circuitry, and produce the following bare-bones (but precise) code:

public class PokerTable
{
   public PokerSeat [] Seats { get; set; }
}

public class PokerSeat
{
   public PokerPlayer Player { get; set; }
}

public class PokerPlayer
{
   public PokerSeat Seat { get; set; }
} 

This code reflects each piece of information you were given in the problem and its associated diagram. The UML called for three classes (PokerTable, PokerSeat, and PokerPlayer) joined by two compositional associations with bidirectional navigability, and that's exactly what you've produced.

You turn your answer in. The interviewer reviews it, nods, then asks you a question.

What two lines of codewhat two propertiescan we add to this code in order to best improve the design?

How do you respond?

Tags: poker

60 comment(s)

I would answer: why have a dedicated PokerTable/PokerSeat class in the first place? Just because we have poker tables and poker seats in real life doesn't mean we need them in our code. A poker table is really just a group of players. Each player has a "stack" a "strategy" and so forth.

I don't see the need to represent either a PokerTable or a PokerSeat in code. These are implied in your collection of "players participating in the current game".

That's probably not the answer you're going for, but that's my take.

Gotta' disagree with anonymous. Thinking of things like PokerStars, there are probably many tables, and there are also many seats, and the collection and orientation of empty seats does make a difference. The current design seems to implicitly be doing as Anonymous says with "rooms". Not to mention that things like blinds and dealer position and pot and so forth need management that's specific to the particular rules of the game being played, so it's reasonable to have different types inheriting from PokerTable in the future.

To be perfectly honest, I'd ask for clarification on what is meant by design or what the scenario was. There are so many directions we could go with this, and we don't even know what kind of Poker is being played. Which is probably the point of the interview question, but nevertheless. I'm especially uncertain about "property" here as mixed with "design" -- are we looking to have 5 classes rather than 3 with appropriate ownership relations, such as a collection of side-pots, or a stack, or a deck?

That question would basically be my initial answer, except I'd be nervous as hell too.

I'd add a shortcut past the seats, so the Table has an array of Players and the Players have a Table.

I would say that the interviewer's question is ambiguous. The answer depends on the intended behavior of PokerTable, PokerSeat, and PokerPlayer.

Several things that occur to me, but I'll go with these two changes.

The first is that the code has no rule to enforce the "no more than 10 seats at a PokerTable" rule shown in the UML diagram. We also shouldn't allow the PokerTable's Seats array to be set, because I could replace it with a larger, smaller, or null array. The second is that while a PokerSeat may be empty, that is, have a null PokerPlayer, a PokerPlayer must have a seat. So the PokerPlayer's Seat property setter should not allow nulls.

I thought of other improvements but they fall outside of the scope of the description and UML diagram.

Cardinalities. Nowhere has there been defined in the code that each table may have max. 10 seats. I would add that check. Also: make sure that every seat is assigned to a table.

May be

public PokerTable Table { get; set; }

in each PokerSeat and PokerPlayer classes.

Best regards,

Paco

I would the variables in each class private.

The first thing I'd do to this is define the mutators in the PokerPlayer and PokerSeat classes. The possibility that player1->seat->player != player1 is worrying.

After assigning the seat/player appropriately, check that its player/seat is equal to you, updating it if it isn't.

As for properties to add, I do think that depends more the rest of your program design than anything else. My understanding of UML is a bit lacking, so I don't know if that diagram calls for a PokerSeat->table.

public class PokerSeat { public PokerSeat left { get; set; } public PokerSeat right { get; set; } public PokerPlayer Player { get; set; } }

I agree with Dean about enforcing multiplicity, but I think a player should absolutely be allowed to have no seat. He might be in the waiting line or created (sitting in cache) but not yet assigned to a table. In that case he doesn't really have a seat and the model should support that IMHO.

The thing I don't like about the above design is that it prevents a player from sitting at multiple tables. I'd tell the interview that the design doesn't work for a typical online poker multi-tabling situation, thereby demonstrating my uber knowledge of poker, and I'd tell them, listen, a-hole, you're dealing with a poker botter here. Don't f with me.

Already a decent reaction to this. You guys should start up your own open source poker client, haha.

The poker client would be easy, it's an open source poker server that's needed (now that throws up some interesting ideas).

As regards the original question:

I'm not sure there are just two properties needed. Things I can think of. Table needs a max seats property, 2, 6, 9 etc. Tables need a type (game type). Players can sit at more than one table (online anyway).

Firstly why only 2 properties? There are MANY needed to turn that design into workable code...

@Anonymous1: PokerTable is required for multitabling.... So yes, mutiple seats per player is needed...

Yes, accessors for PokerTable in the PokerSeat class (not neccesary in PokerPlayer) would be helpful, but also would be...

in PokerSeat, something like Ordinal { get; ?set;? } would be useful (as the index into the array held in PokerTable) and then left/right can be retrieved by Ordinal +/- 1, wrapping @ 0 and number of players of course, so...

and in PokerTable public int SeatedPlayers { get; set; }

But, as 3 classes each containing a single line of code, there is much needed to include that is not in the above design, hence is difficult to say which 2 would best improve it....

I would do this

public class PokerTable { public PokerSeat [] Seats { get; set; } }

public class PokerSeat { public bool IsEmpty { get { return true; } } }

public class PokerPlayer : PokerSeat { public bool IsEmpty { get { return false; } } }

hi everybody.

my solution isn't only adding two properties, but eleminating the bidrectional dependencies between the classes.

have a look at http://msdn.microsoft.com/en-us/library/ms998543.aspx

the gof pattern are widely used for eleminating dependencies and you should use them for improving your design. (and as a result, your code..)

best regards

erik

Wait, both relationships are not bidirectional in the code. The uml specifies the PokerSeat object has access to its parent PokerTable. Not so in the code.

Anyway, the leading question that states that adding properties will improve the design seems strange and ambiguous to me. Are we trying to flesh out the behavior to be more useful in a poker context (eg, PokerTable :> { IsFull, Hand, GameType, PlayerToBet, BigBlind, etc }) or are we just trying to improve on the existing spec without introducing new concepts?

PokerSeat provides no useful abstraction since the collection on PokerTable can be ordered and numbered and a seat being empty or occupied reduces to whether the given index has a null reference or live object. Thus I would eliminate the PokerSeat class all together and adjust the collection on PokerTable to be a collection of PokerPlayer objects. This would imply the PokerPlayer would get a reference to a PokerTable object (or realistically many tables) instead of the PokerSeat.

This makes sense given that the Poker table really only cares about the players sitting at it and the players don't care what seat they sit at unless its in the context of a certain game or hand (eg, table). So yeah eliminate the PokerSeat. No data, functionality, or understandability lost.

"it's an open source poker server that's needed "

hmmmm

Open source poker client? I don't think an open source client could ever be successful... Ever. The money made from huge business such as PokerStars leads to massive tournaments with huge prize pools. People aren't interested in small tournaments with few players and low GRTD monies.

Some really good answers here. You guys know your stuff. Personally I think this was the best idea:

"I'd add a shortcut past the seats, so the Table has an array of Players and the Players have a Table."

Based on the question and assumming I, as developer, don't know anything about the problem that has not been stated, the only possible solution would be adding properties to know which is the seat before and after any given seat:

public class PokerSeat { public PokerSeat PreviousSeat { get; set; } public PokerSeat NextSeat { get; set; } public PokerPlayer Player { get; set; } }

As an addendum to my previous post, since the title of the article is "A Circular Dilemma", it looks like James is going for the NextSeat(), PrevSeat() behavior.

What on Earth does knowing what the next and previous seat tell you about the table or even poker in general? The answer is nothing. Seriously guys, use your heads.

In this instance the PokerPlayer knows nothing about the table without a proxy via the seat object. Therefore, being that there is some valuable information from the table object, such as board cards, the button, other players and their actions, etc., it would best be suited to have a circular reference to the table object.

This thread has now entered the realm of mental masturbation.

ptable = Array.new(10){Player.new}

Is it anything as obvious as the fact that you can't find out which table a particular seat is at without doing a linear search of all the seat arrays for all the tables.

In short, the seat class should definitely have a table property.

Make the PokerTable object responsible for seating a player. And the PokerPlayer object responsible for leaving the seat.

public class PokerTable { public PokerSeat [] Seats { get; set; } public bool addPlayer(PokerPlayer newPlayer, int openSeatNumber) { lock(Seats) { if(Seats[openSeatNumber].Player == null) { Seats[openSeatNumber].Player = newPlayer; newPlayer.PokerSeat = Seats[openSeatNumber]; return true; } }

  return false;   

} }

public class PokerSeat { public PokerPlayer Player { get; set; } }

public class PokerPlayer { public PokerSeat Seat { get; set; }

public void leaveSeat() { Seat.PokerPlayer = null; Seat = null; } }

Psycho Face

Sorry could not indent the '{' and '}'

Psycho Face

Psycho Face, that isn't enough when the player is seated at several tables. You need a reference to a table just as jeremyp said.

Amazing Series and a most entertaining thread on this post. My hat is off to you JD.

It is about time someone 'Published' techniques used in the emerging SPR world. (Software Problem Resolution). That which we define as; identifying root cause of software issues often WITHOUT advantage of source code. Yes many say this is a pretty dress on the ugly pig of 'reverse engineering' but as you point out (and my personal work experience has made use of) is a vital tool in the professional software engineer toolbox.

To the question as posed... shame on you JD... LOL!... If I (and probably you) were in such an interview we would first ask to redefine the duties of the 'programmer position' are to include 'design'... Also, we should rely on the ITERATIVE nature of our world and lack of 'vision' such that, one would be remiss in suggesting any alterations to the 'design document' as presented.

I know Whatever! The posts are very assumptive of 'process' and as such I might be pressed into suggesting that enumerating player 'Tables' will be improved with a table reference in player... And that the Table could make use of a reference to players... For, example dealing to empty seats would be a logic bug.

limited to two lines, it would surely first be two fields in the Poker table class that would record the table activity, timestampstart timestampstop, but there is much more needed.

A superclass for game that hasa table that hasa seat that has_a player to start.

I'm fairly green for this, but that's where I'd start

I'm not sure.

I THINK (I have no .NET experience) the base solution is not correct as it does not satisfy the word 'Ordered' is the problem. If my assumption is correct we REQUIRE prior and next seat pointers. (As others have suggested)

If the original solution is correct (and my assumption is not) I would add prior and next PLAYER pointers to improve navigation.

hmmm, I'm already thinking beyond the stated problem: The only correct answer is adding seat pointers.

(In reality I'd add player pointers to my design anyway, but thats based on my own requirements)

Ditch the seat. Give the Table an array of Players. Each array location is a location at the table 0 - 9. Each array position, or theoretical seat, is either null or has a player in it. Add a reference to table for each player. Program in a language like C# where circular dependency is not going to break your application...

[quote]public class PokerTable { public PokerSeat [] Seats { get; set; } // comparing Seats.Length with public PokerSeat [] OpenSeats { get; set; } // OpenSeats.Length will tell you if table is open }

public class PokerSeat { public PokerTable Table { get; set; } // make it match UML public PokerPlayer Player { get; set; } }

public class PokerPlayer { public PokerSeat [] Seats { get; set; } // allow player to sit at multiple tables }

// sorry if wrong syntax, C++ background[/quote]

Two lines for improvement: First would be a property for the number of players at the table - this would allow us to quickly determine if a table had fewer than the maximum number of players. (sure at 1 table and 10 players it's easy - at 80 tables and 800 players we're starting to talk about performance...)

The expectation is that left and right seats can be deduced numerically so I'd skip that.

However, I think there would be a desire to maintain the location of the deal, so adding something to the table to indicate which seat was the current 'dealer' would seem appropriate. but I'm guessing that's wrong.

The second is either the player or the seat needs to be associated with the table... I would tend to associate the seat since it won't change tables.

I would think about the practicality of the design in the context of Poker AI development and punch the following question at the interviewers:

What's the use of knowing at which tables a player sits (in exact this order)?

When we invoke the game handle event of the player, we need to pass the PokerTable either way to know to which exact table the event references to.

Also, I'm failing to see how we would remove a player from all the tables he sits in. So the practicality of a PokerPlayer => PokerTable(s) mapping is imo not very useful (I assume this is what the PokerPlayer => PokerSeat(s) mapping intends to do indirectly).

do you not have to make sure that if the seat has a player in it, then the player is in the same seat?

PokerPlayerID, PokerTableID; some sort of persistent identifiers (unique keys in database parlance) to the PokerPlayer and PokerTable objects need to be assigned upon instantiation if one wants to support a journaled, auditable game transaction history.

Are we on tilt again? I hope it does not last a couple of months this time!!

Considering the topic we would probably have to make this into a "circular dilemma" thus giving the player knowledge of its seat. Or table. Alternatively give the seat knowledge of the table. Perhaps both? However a player can be seated at more than one table. And I dont really see how this would "Best improve the design".

Id go with

pupublic class PokerSeat { public PokerTable Table { get; set; } public PokerPlayer Player { get; set; } }

public class PokerPlayer { public PokerSeat[] Seats { get; set; } }

unless I get more information on what he ment with "Best improvement of the design".

I like poker dilemma's!

explicit garbage collection with reference counting or latent tracing garbage collection.

one can handle cyclic references, one can't

are we done yet?

You obviously don't know what you're talking about...

I do not even know what he's talking about. LOL.

I had this issue when coding equivalent paradigm in C++. I solved it by creating table class containing vector of players. Player needs to get information on cards, button, position etc, so I passed the table object's self-pointer to player, who could then get all the information needed.

Something like this:

template <int N = 10> class Table { public: Table(); ~Table();

    ...
    void PlayHand()
    {
        for (int i = 0; i < N; i++) {
            ...
            switch (_seats[i]->GetDecision(this)) {
                ...
            }
        }
    }
private:
    int _button;
    std::vector<Player *> _seats[N];
    ...

};

class Player { public: explicit Player(const std::string& name = ""); virtual ~Player();

    ...
    virtual int GetDecision(Table* table) = 0;

};

You'll get the idea...

Two properties need to be added to the PokerPlayer.

Public Property Doomswitch() As PokerPlayer.DoomSwitch Get Return PokerPlayer.DoomSwitch End Get Set(ByVal Value As Boolean) PokerPlayer.DoomSwitch = TRUE End Set End Property

Public Property CashoutCurse() As PokerPlayer.CashoutCurse Get Return PokerPlayer.CashoutCurse End Get Set(ByVal Value As Boolean) PokerPlayer.CashoutCurse = TRUE End Set End Property

ROFL! @ Anonymous on 2/22/2009 7:14:55 AM

enum GameStyle {Straight, HousePlayerAdvantage, DoubleHousePlayerAdvantage, Zerg}; enum PlayerStyle {Fish, Rock, LAG, TAG, PermaTilt, GameTheorist, Baller, Haxxor, Whale, House, Bot};

class PokerTable { // ... public GameStyle Style {get(); set();} }

class PokerPlayer { // ... public PlayerStyle Style {get(); set();} }

But who knows; the devlin is in the details and he's mia, probably crushed himself under a hundred idapro breakpoints... thanks for all the blog and hope you come back for fish!

Have you finally got that job at PokerStars or what????????

Say "Best improvement of the design" equals "making life easy" in a real poker client application, I would add:

public class PokerSeat { public PokerPlayer Player { get; set; } public PokerTable Table { get; set; } //new1 }

public class PokerPlayer { public PokerSeat Seat { get; set; } public PokerTable[] Tables { get; set; } //new2 }

If a player disconnects, the poker-application needs to know which tables are effected (new2). If a player gets busted the poker-application needs to the table where the seat must be removed (new1).

Allthough this is a bit redundant, it would be my suggestion.

Have you got the job at PokerStars yes, or what :) ??

Kind regards ..

Uh Oh... James is MIA again :/

Table.SeatCount and Player.SittingIn

RIP: Coding The Wheel Date: March 16, 2009

You will be missed.

+1 on the you will be missed...

Latent tracing garbage collection vs. reference counting. You don't get it? C# is a managed system with a garbage collection algorithm that traces references to find objects in use and as such is immune to circular references. COM, as an example, is a reference counted system and breaks if you have a circular reference.

What was so hard about that?

You obviously don't know what you're talking about... Go back to school :D

Sorry... this may be my simple "non programmers mind" talking... but how can a poker game be played without cards? And there needs to be direction. So even though I cannot come up with code... I'd say this:

  • 1 line of code for cards
  • 1 line of code, stating what direction it should go

And still we are 1 line short... because the max amount of players need to be added too. So this is what I would ask:

Are we playing poker with or without cards? Since the answer will be obvious... there's obviously something missing.

The problem is that the requirements are not clear at all. It's not clear either how "adding two lines" [sic] could improve the design. The design is completely orthogonal to the OOP (the implementation). You're mixing 3GL artefacts (C# / Java / C++ are all 3GLs) with design. OOP != OOD. I'm sorry but that's OO 101.

You don't "improve the design" by adding lines of codes. You improve the design by having a better OOA (OO Analysis) and, well, coming up with a better OOD (OO Design). How you implement it in a 3GL, at the OOP level, is a detail that has nothing to do with circular dependencies that your broken design may have.

For what it's worth I happen to be working in the field and I can tell you that the DSM (Dependency Structure Matrix) for our complete poker server/client shows exactly zero circular dependencies.

Your OOA is inexistent, your requirements are blurry, your OOD is broken. It's not "adding two lines" that are going to fix this.

-- "In the kingdom of the blind, the one-eyed is king."

Sorry to dig up a fortnight-old post with a pointless comment, but:

bool thisAlgorithmWillBecomeSkynet = FALSE; bool iWillGetTheJob = TRUE;

added anywhere you find suitable ought to do.

Its having good description regarding this topic.It is informative and helpful.I have known many information from this. Thanks for shearing folic acid tablets

Use the form below to leave a comment.






Coding the Wheel has appeared on the New York Time's Freakonomics blog, Jeff Atwood's Coding Horror, and the front page of Reddit, Slashdot, Digg.

On Twitter

Thanks for reading!

If you enjoyed this post, consider subscribing to Coding the Wheel by RSS or email. You can also follow us on Twitter and Facebook. And even if you didn't enjoy this post, better subscribe anyway. Keep an eye on us.

Question? Ask us.

About

Poker

Coding the Wheel =
Code, poker, technology, games, design, geekery.


Hire

You've read our technical articles, you've tolerated our rants and raves. Now you can hire us anytime, day or night, for any project large or small.

Learn more

We Like

Speculation, by Edmund Jorgensen.