Reputation: 53
Making an uno game for a project.
all 108 cards are located in a filled array named cardDeck[108]
the start function pulls 7 cards at random and fills the players hand by adding them to an array called playerHand[]
I need help on this please.
1) I don't know how to make the cards dissappear out of the cardDeck array when they are pulled into playerHand[].
2) I also don't know how to create something that will count the non-null values in the cardDeck array so it will display the total number of cards left in the deck after both players draw cards.
Upvotes: 2
Views: 652
Reputation: 101466
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
Make the deck a vector
.
vector<Card> deck_;
Insert each card in to the deck.
deck_.push_back(Yellow0);
deck_.push_back(WildDraw4);
Shuffle the deck
srand((unsigned)time(0));
random_shuffle(deck_.begin(), deck_.end());
Take cards out of the deck & put them in the player's hand.
vector<Card> player_hand_;
for( size_t i = 0; i < 7; ++i )
{
player_hand_.push_back(deck_[0]);
deck_.erase(deck_.begin());
}
EDIT:
Per a request in the comments below, I'll explain why I believe there should be multiple vectors of stateless cards rather than a single vector of stateful cards.
One of the ideals that OOP tries to approach is to model the real world as closely as possible. Now obviously a vector
is not OOP, but the cards contained therein theoretically can be. And while it certainly can't be achieved perfectly in many cases, or should even be attempted to be perfectly achieved in as many cases, attempting to model software components after the real world IMO transcends any particular programming paradigm.
So consider the Uno cards. An Uno card has a couple of different properties. They have a rank (0-9) and they have a suit (red, blue, yellow, green). There are also a few cards that have special meaning and have neither suit nor rank (WildDraw4, Wild, Draw2, Skip, reverse).
So if we were to model an Uno card in C++, what would it look like? Uno cards don't change once they are printed. A yellow 2 will never be a green 5 or a Wild Draw 4, for example. If you take a green7 out of the box and hand it to your friend, it's still a green7. It's now in your friend's hand, but that's not something that's changed about the card. Rather, what's changed is your friend's hand. Instead of being empty, it now holds a Green 7 Uno card.
In the game of Uno, each player is dealt 7 cards. There is one card placed face-up that becomes the discard pile, and the remaining cards are placed face-down forming the draw pile. When you move a card from the draw pile to a player's hand, the card still hasn't changed even though it's location has. But don't get hung up on human language. "It's location" doesn't imply that the location of the card is a property of the card. But when you move the card, something has changed -- the states of the draw pile and the player's hand.
So we end up with a vector for the draw pile, another for the discard pile, and one vector for each player, representing their hand.
This design is easy to understand by a maintenance programmer, easy to extend, and easy to develop the first time. Because it is simple, it is also not especially prone to defects. After all, which is more likely to break down -- a skateboard or the Space Shuttle?
Upvotes: 12
Reputation: 5287
I assume you're using some sort of custom type for the cards. So lets call this t_card_kind
.
You could do some sort of array shift to "delete" the card from the players hand. But this doesn't make much sense.
What I would suggest is defining an enum like
namespace uno { enum play_status { DRAW_PILE,IN_HAND,DISCARD }; };
That way you can simply declare a new type-defined structure
typedef struct s {
t_card_kind card;
uno::play_status status;
} t_game_card;
... and lastly declare an array such as
t_game_card my_cards[108];
or a vector such as: vector my_cards[108];
and then populate it.
To change a card's status simply access the member.
e.g.
my_cards[i].status = uno::IN_HAND;
This design will execute much quicker and is more elegant.
To solve your second question just implement a method like
unsigned int count_status(const t_game_card * my_card,
uno::play_status status_kind) {
const unsigned int DECK_SIZE = 108; //size of a standard uno deck
unsigned int matches;
for (unsigned int counter=0; counter < DECK_SIZE;counter++) {
if (t_game_card[counter].play_status==status_kind) {
matches++;
}
}
return matches;
}
...Or just switch to vectors and use the erase() method.
EDIT 1
Based on my above discussion with John there is debates over this single container design versus a multi-container design. Here is my opinion on the topic:
I believe this design of using a single vector of cards with state info attached is more robust and easier maintain than a multi-vector based design. Here is why.
In your resulting code you would have something like:
namespace uno { enum play_status { DRAW_PILE,IN_HAND,DISCARD }; };
typedef struct s { t_card_kind card; uno::play_status status; } t_game_card;
static vector<t_game_card> my_cards[108];
Where a multi-vector based approach would have:
static t_game_card draw_pile[108];
static t_game_card in_hand;
static t_game_card discards;
...still three items, though mine is a bit longer line length wise.
As previously stated, to change a state with my approach you would simply use something like:
my_cards[i]=uno::IN_HAND;
whereas with a vector-based method you would have to do something like:
in_hand.append(draw_pile[i]);
draw_pile.erase(draw_pile.begin()+i);
This is longer, more computationally intensive, uses more memory, and in my opinion less intuitive.
Now let us consider extensibility. Let's say you come up with a uno variant that includes a "BONUS" pile. With my approach this is simple -- just add a value to the enum:
namespace uno { enum play_status { DRAW_PILE,IN_HAND,DISCARD,BONUS }; };
with the vector approach, you'll need to create and maintain an entire extra vector:
static t_game_card draw_pile[108];
static t_game_card in_hand;
static t_game_card discards;
static t_game_card bonus;
We now have 4 (!) variables with the vector based approach versus a single, intuitive variable with my approach.
Maintaining multiple containers for separate states takes up more memory, is computationally more expensive, and is less intuitive versus using a single container (vector, array, etc.) to hold structures with state info attached.
Upvotes: 1
Reputation: 48287
I would recommend something like this (it's a bit more complicated, but should work):
Add all cards to a deque (push back)
(optionally) Shuffle (randomly sort) the collection
Loop 7 times
{
For each player
{
Removing the first card from the collection and add it to the player's hand
}
}
You should end up with hands of 7 cards and everything left on the deck is very easy to count or work with (it's all left in the deque).
Now, I would recommend using vectors for the player's hands instead of arrays, although the difference here is pretty small, since you have a known number of elements. Vectors might be easier to work with. If you haven't already, I'd also make a Card
class and keep the deck and hands as deque<Card*>
and vector<Card*>
, which will make moving cards a breeze (copy the pointer, which is one instructions) and shuffling will be pretty fast too on small data types.
Upvotes: 0
Reputation: 504
you could assign the drawn cards a value/character that no card has. Thus telling you which cards have been drawn ...
Upvotes: 1
Reputation: 54168
Consider this alternate implementation and logic.
Define CardCollection
to be a class
that 'has a'
std::deque<boost::shared_ptr<Card> >
Define three CardCollection
s - one is
the deck, the other two are player
hands
Initialize the deck by push_back
of all
the Card
objects (in random order) to its deque
.
draw the Card
s from the
deck using pop_front() on its deque
move them to the corresponding
player hand by push_back
to that
deque
, erase in the deck deque
(this solves 1)
2) is solved by calling
size()
on the deck deque
when the hand ends, return Card
s to
the deck using push_back()
Upvotes: 1
Reputation:
Keep an int (let's call it N) representing the number of cards left in the deck. To remove a card from the deck, pick a random number r between 0 and N (ie 0 <= r < N), swap the last card in the array with the r'th, decrement N and return the card that you just swapped to the end of the array.
This way, you never introduce NULLs into the array and so avoid your problem entirely. If you use an stl vector instead of an array this becomes even easier as the vector knows its size without you having to store it yourself.
Upvotes: 1
Reputation: 224119
Just use a std::vector<card>
. It's a dynamically resizable container which you can remove elements from.
Upvotes: 2