tags:

views:

74

answers:

4
int Table::addPlayer(Player const& player, int position)
{
    if (position > 0 || position < 11) {
        deque<Player>::iterator it = playerList.begin()+position;
        deque<Player>::iterator itStart = playerList.begin()+postion;

        while(*it != "(empty seat)") {
            it++;
            if (it == playerList.end()) {
                it = playerList.begin();
            }
            if (it == itStart) {
cout << "Table full" << endl;
                return -1;
            }
        }
        //TODO overload Player assignment, << operator
        *it = player;
cout << "Player " << player << " sits at position " << it - playerList.begin() << endl;
            return it - playerList.begin();
    } else {
cout << "Position not a valid position, must be 1-10" << endl;
    return -1;
    }
}

int Table::removePlayer(Player const& player)
{
    for (deque<Player>::iterator it = playerList.begin();it != playerList.end(); it++) {
        //TODO Do I need to overload == in Player?
        if(*it == player) {
            *it = "(empty seat)";
            int pos = it - playerList.begin();
cout << "Player " << player << " stands up from position " << pos << endl;
            return pos;
        }
    }
cout << "Player " << player << " not found" << endl;
    return -1;
}

Would like some feedback on these two member functions of a Table class for Texas Hold Em Poker simulation. Any information syntax, efficiency or even common practices would be much appreciated.

+1  A: 

Your first while loop in addPlayer() is dereferencing an iterator that hasn't been checked for validity. If a value of position is passed in that is greater than the number of elements in the container you will likely have a crash. This might be controlled by the caller, but it is much better practice to control it at the point of reference.

Amardeep
good eye (plus 1)
baash05
Thanks Amardeep. See my revisions.
George
A: 
  • Indent your code properly, it makes it far easier to read and understand later. If you're inexperienced, consider adopting a style guide (e.g. Google's C++ style guide).
  • Checking that the player iterator dereferences to "(empty seat)" is questionable, you may want to consider a few alternatives:
    • Keep a separate list of empty chairs and allocate them on AddPlayer().
    • Let polymorphism fly and use a EmptySeatPlayer class.
    • Allow null players to be stored directly in the tableList.
  • I'm unclear why AddPlayer needs a position parameter, if it just allocates the next available seat (until it finds one). Maybe remove the parameter entirely and let the Table figure it out.
  • You'll eventually probably want to decouple your game-play text from the business logic.
  • You probably shouldn't be using position directly, you should be operating on the players and the table. One might consider it a detail of the class that shouldn't be exposed by functions.
  • Rather than while (*it != player) and checking for end in each iteration, use std::find.
  • Also you're doing a lot of 'pass-by-value', it's good practice to pass const Player& to avoid unnecessary copies.
Stephen
the position flag give preference to a loop addition. I can see how that would be useful. Also if I remove a player I could use the return value to add the next one in the same place, thus eliminating the looping.
baash05
For C++ Google Style guide is not considered a good candidate.
Martin York
@Martin York : it is at Google...
Stephen
@baash05 : I disagree with "preference" being useful. It doesn't model the real world and it doesn't help model the problem. In the real world, you choose one of the empty seats or one is assigned. That is, "seat-selection" is an algorithm over "seats-available" chosen by the player or the table. Unlike Blackjack, the table is treated as circular, so no preference is given to beginning or end. If there were, you would want to use backwards iteration with "end" preference. If you pass in `position`, it should be an error if the seat is not empty.
Stephen
@Martin York : by the way, [citation needed].
Stephen
Martin York
@Stephen: Google's style guide is good for them. It is designed to cope with some pretty specific situations and maintain backwards compatibility with existing code (from what I have read). It is not designed (I hope) as a general C++ style guide. (Foe example they advocate no exceptions (to me that's ridiculous))
Martin York
@Stephen: Not using exceptions leads to other problems like using an Init() function to fully construct the object. I could understand re-factoring constructor into a private Init() method used by all the constructors (fine). But they are advocating a two phase construction of an object. Which sort of goes against the whole point of using constructors in the first place. Also sort of defeats the purpose of RAII (which I hop everybody here thinks is a goof idea (bad name ;-) ))
Martin York
@Stephen: http://stackoverflow.com/questions/1453243/how-can-i-avoid-using-exceptions-in-c
Martin York
@Stephen: Sorry the indentation was messed up, getting used to stackoverflow's formatting. Should be fixed now though. As far as the player iterator goes, do I need to overload == so that it compares the string and the player's string?The reason why addPlayer has a position flag is because that should be the first place it tries to seat the player. The for loop is just error handling, to try and seat them somewhere if their seat of choice is taken. I'm keeping the couts in there for debugging, see my response to baash05
George
@Stephen: I asked Martin York this, but what are the advantages of using std::find? Also, I changed the Player arguments to const, pbr. Thanks for the advice.
George
@Martin York : Thanks for the discussion pointers. I read some of the thread and all of the SO question, it's all rehashing the same issues... and got rather boring (I had a lot of the same reactions when initially reading it). Few people provided much beyond "ZOMG! I think it's nuts! And I should know, I use exceptions all the time!" (if there were concrete data points - studies, etc, I'd like to read them... I'm on a 'whitepaper kick') continued...
Stephen
So, a bunch of people got together on a mailing list and argued over the merits of a very small subset of the style guide, where outside of banning exceptions (which has very technical reasons) is almost entirely subjective. Many commentators are also uninformed about the repercussions - you can use a custom allocator which can guarantee not to throw, you can use stl containers, ..., and in early c++ days exceptions amounted to a huge performance loss. Now consider migrating millions of lines of code to exception safe implementations. continued...
Stephen
So, while I appreciate your point-of-view, I'll leave it up to the individual coder (or organization) to weigh the merits of a style guide for a particular project and let them selectively adopt or reject certain style guidelines... the impact of each guideline is often less important than adopting a consistent style overall. I presented Google's style guide as an example, not as THE style guide. You're free to suggest alternatives. I'd like to read them too. continued...
Stephen
In the future, though, saying something like "For C++ Google Style guide is not considered a good candidate." is too dogmatic for my taste. You don't speak for the C++ community ;) and Knuth didn't reject it. Maybe if I ever finish that thread, I'll see I'm wrong. I think it would be more helpful to frame your criticism as an opinion, or characterizing the O(412) opinions on that thread. "Use X style guide, because google's style guide advocates no exceptions... and people on [thread] think it's nuts!". continued...
Stephen
Stephen
@George : The benefit of `std::find` is that it searches for an element without you having to write the loops. It'll return an iterator, which you can use to calculate index or "not found". Like all functions, it just minimizes the amount of code you write and might help readability. For example `it = std::find(begin, end, 5);` obviously searches for 5 in a range. Testing `it == end` is idiomatic "not found". As for your formatting, definitely indent `cout` to align with the block, you can use your editor's search to remove them later.
Stephen
@George : as for your efficiency question to Martin, you can ignore this level of efficiency in most cases (a general rule of thumb is: every case you'll ever come across). In reality, you'll see negligible (if any) performance loss. You could probably construct a benchmark to exploit cost of an extra function call, but in reality your performance is likely going to be dominated by the string comparisons (see my comment about not dereferencing `Player` to a string). We can't see the `Player` definition, so can't give you specific advice.
Stephen
@Stephen: Could you talk to me more about polymorphism with the Player class? Since eventually I would like this simulation to morph into a poker bot, it would be a good idea to have a MachinePlayer, HumanPlayer and EmptyPlayer as a children of a Player class. Basically I want a vector of the players, and since EmptyPlayer wouldn't share any of the member variables, functions of Machine, HumanPlayer, would the parent Player class not have anything in it?
George
@Stephen: And to wrap back around to dereferencing Player to a string, if I use operators like == or =, I'm going to have to overload them in the Player class? Ie, I need to define a default behavior for this class when these operators are used (say return a string of the player's name).
George
@George : If you can't accurately represent an EmptyPlayer, then I think you described a pretty good reason why `EmptyPlayer` may not be a real accurate model for your problem. That's probably ok, and you can take an alternate approach to storing the players at your table. WRT dereferencing: I'm unclear what you're asking. But, more importantly, I don't think you want to be using player names as matching criteria... Names should be confined to UI. Your business logic should probably operate on (and compare) instances of your classes.
Stephen
@Stephen: At the root of all this Player class business is the playerList, a vector in the Table class that should contain who is seating at which seat. As in the code above, I want to use an iterator to step through each element in the playerList (to deal a card, ask for bets, etc.), skipping seats that have no player. Is the best way to do this the polymorphism route? Or having a Player with a name of "(empty seat)"?
George
@Stephen: Also, are you saying in general, it's not kosher or even legal to use the iterator as a pointer like I did? And if it is legit, how does *it == "(empty string)" work? Shouldn't I have to define what the thing it points to (a Player) returns when the == operator is used? I realize now using strings to identify players in playerList might not be the best way (as you said, strcmp() is expensive) but I don't really know of a better way.
George
@George: I probably wouldn't use the polymorphic approach, because empty seats don't play. Consider your table as a container, and maybe provide an iterator approach over the players. Treat human and AI players the same, using polymorphism. WRT iterators as pointers, they're very close to the same concept. It's valid. `*it` dereferences the iterator, to get the value it references.
Stephen
Doing `== string` is also valid, provided you've overloaded the equality operator. But I don't think it's the right approach. Consider a reference from player to the table, so you can tell the player to leave the table and the player will tell the table he has left. The data can stay consistent.
Stephen
A: 

the remove could be done in a for loop..

for(deque<Player>::iterator it = playerList.begin(); it!= playerList.end(); it++){
    //if we've found what we're looking for
    if(*it == player){
     //then remove the player and return his/her position.
     *it = "(empty seat)";
     int pos = it - playerList.begin();
     cout << "Player " << player << " stands up from position " << pos << endl;
     return pos;
    }
}
cout << "Player " << player << " not found" << endl;
return -1;

I find this a bit cleaner, and I am personally a big fan of comments.

baash05
Oh.. for standarization.. I'd remove the couts.. and put them in the code that calls removeplayer. It makes the function more UI agnostic. Data manipulation and UI should be isolated as much as possible to allow you to change storage methods without changing GUI code.
baash05
Thanks baash05, check my revisions. Also, I want to keep the couts in for when I debug and to make sure it's simulating true to a real table.
George
Put the TRACE in your debug.. It sends info to the output window during debug and is compiled out during release.
baash05
A: 

1) If you are not going to modify a parameter in a method then pass by const reference:

int Table::addPlayer(Player const& player, int position)

This provides hidden benfits latter but also introduces the concept of const correctness.

2) Try and learn how to use the standard algorithms:

In this case your loops can be replaced with the use of std::find()

int Table::addPlayer(Player const& player, int position)
{       
    deque<Player>::iterator itStart = playerList.begin()+position;

    deque<Player>::iterator it      = std::find(itStart, playerList.end(), "(empty seat)");
    if (it == playerList.end())
    {
        it  = std::find(playerList.begin(), itStart, "(empty seat)");
        if (it == itStart)
        {
            cout << "Table full" << endl;
            return -1;
        }
    }
    ...

And

int Table::removePlayer(Player const& player)
{
    deque<Player>::iterator it = std::find(playerList.begin(), playerList.end(), "(empty seat)");
    if (it == playerList.end())
    {
        cout << "Player " << player << " not found" << endl;
        return -1;
    }
    .....
Martin York
Thanks Martin York, changed the Player arguments to const, pbr. What are the benefits of using standard algorithims? It seems like the layer of abstraction could be less efficient than coding down to the nitty-gritty, but please correct me if I'm wrong.
George