|
 |
|
 |
08-10-2005, 02:57 PM
|
#1 (permalink)
|
|
Registered User
Join Date: Jun 2005
Posts: 26
|
Inheritance and functions...
Hey everyone,
I might as well explain what I'm doing. To practice and learn as I go along, I'm trying to write a card game similar to Solitaire. Right now, I'm just concentrating on getting everything coded as a console-based app, but I hope this project will eventually be my transition to using some actual graphics and stuff. (*gasp*) Anyways, the problem I'm working on right now:
I've got three classes corresponding to the different types of piles of cards this game uses. My Pile class is my base class, and would be the Solitaire equivalent of the main seven piles most of the cards are originally dealt to. My other two classes, ExPile and TopPile, inherit Pile. ExPile is the extra pile of cards you can draw from, and TopPile is the four piles at the top where you want all the cards to end up. Anyways, the problem:
I'm working on an addCard() function that adds a card to the top of a pile. Called by this function, before it adds a card, is checkAdd(). checkAdd() checks to see if this card can legally be added to the top of this pile. The way I had originally designed it, addCard() was written in Pile, and of course inherited by TopPile and ExPile. Then, I had a seperate version of checkAdd() in Pile, TopPile, and ExPile, each with different requirements for the move to be valid. Anyways, I swear the actual problem is coming up:
Well, this didn't work, because addCard() always called the version of checkAdd() that was written in Pile, even if I was dealing with an object of type TopPile or ExPile. So, is there any way to make this work the way I want it to? That is, addCard() calls the version of checkAdd() that corresponds with the object type...
And if not, how else could I make this work? My first thought would be to have different versions of checkAdd()-like functions in Pile, then determine the object type and call the appropriate one. So, if there is no other way to do this, how do you determine the object type? (Doesn't Java have some sort of isClassType? function that does this?)
Thanks so much for reading through this rambly message, and please tell me if I've been unclear about anything.
Thankies!
-Stacy
|
|
|
08-10-2005, 04:36 PM
|
#2 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
Notice that you may have to learn the ways of virtual methods.
But most importantly, you misunderstood the concept of "type" probably.
You see, the cards you draw, put back, put on a spot, they are all contained withing the same type: Deck
And a Deck is nothing but a collection of 52 Cards.
So you need only two classes:
- Card
- Deck
Once you have the Deck defined, just instantiate an array or vector of Decks. And then transfer the cards from one deck to the other. This way you don't have to mess with inherritance at all.
If you do it with inherritance as you do now, you are basically saying:
Quote:
|
An ExPile (and a TopPile) are special Decks!
|
But that's not true! They are Decks, although they may be not complete. But that has nothing to do with design. I'll try to get my system up and running as fast as I can but it won't be tomorrow because I need to carry more stuff to help someone else move to another home. I just bought his home.
__________________
Last edited by Valmont; 08-10-2005 at 07:22 PM.
|
|
|
08-10-2005, 04:45 PM
|
#3 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
Yah, for the moment you could use this basic setup of deck and card:
Code:
#ifndef CARD_H
#define CARD_H
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
class Card
{
public:
Card(pair<string, unsigned> crd) : aCard(crd) {}
friend ostream &operator<<(ostream &os, Card const &c);
public:
string getSuit() const { return aCard.first; }
unsigned getValue() const { return aCard.second; }
pair<string, unsigned> getCard() const { return aCard; }
private:
pair<string, int> aCard;
};
ostream &operator<<(ostream &os, Card const &c)
{
cout<<resetiosflags(ios::right);
cout<<setiosflags(ios::left);
string sName;
char suitName[6];
switch(c.aCard.second)
{
default:
itoa(c.aCard.second, suitName, 10); break;
case 11:
strcpy(suitName, "Jack"); break;
case 12:
strcpy(suitName, "Queen"); break;
case 13:
strcpy(suitName, "King"); break;
case 14:
strcpy(suitName, "Ace"); break;
}
sName = suitName;
int len = c.aCard.first.length()+sName.length()+1;
cout<<setw(40-len/2)<<""<<c.aCard.first<<" "<<suitName;
resetiosflags(ios::left);
return os;
}
#endif //CARD_H
Code:
#ifndef DECK_H
#define DECK_H
#include "card.h"
#include <ctime>
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
//#define _STLP_USE_DEBUG_LIB
using namespace std;
class Deck
{
public:
Card& operator [] (unsigned index) { return theDeck[index]; }
Deck() { CreateDeck(); }
void RemoveTopCard()
{ theDeck.erase(theDeck.begin()); }
void CreateDeck()
{
int cardtype;
string suit;
for (int i=0; i<52; i++)
{
// First 13 cards are Clubs, next 13 are Spades etc.
cardtype = i/13;
switch(cardtype)
{
case 0: suit = "Club"; break;
case 1: suit = "Spade"; break;
case 2: suit = "Diamond"; break;
case 3: suit = "Heart"; break;
}
theDeck.push_back(Card(pair<string,unsigned>(suit, 2+i%13) ));
}
}
void Shuffle()
{
static bool seeded;
if (!seeded)
{
srand(time(0));
seeded = true;
}
random_shuffle(theDeck.begin(), theDeck.end());
}
void Show(ostream& os)
{
// 1) "copy()" theDeck (from begin() to end() ) to ostream_iterator.
// 2) o_i fetches a "Card" from theDeck (start at "begin()" )...
// 3) ... and forwards it to os (BlackJack forwards a "cout" to here).
// 4) cout<<Card<<"\n"; is executed.
// 5) But there is no default "<<" for "Card" defined by C++...
// 6) ... so we defined our own in class Card.
copy(theDeck.begin(), theDeck.end(), ostream_iterator<Card>(os, "\n") );
}
vector<Card> getDeck()
{ return theDeck; }
private:
vector<Card> theDeck;
};
#endif //DECK_H
This should do the trick nicely. The next step is to creat a class Player (very simple) and a class "Solitaire" that basically is the game. But to keep it short (I cant type no code right now) the card and deck setup is healthy so study it and use it.
__________________
|
|
|
08-13-2005, 02:18 PM
|
#4 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
What do you think Anviel? I am always interested.
__________________
|
|
|
08-13-2005, 04:16 PM
|
#5 (permalink)
|
|
Registered User
Join Date: Jun 2005
Posts: 26
|
Yeek!  I've been way too busy; I've just now had time to really look through your replies. First of all, thanks so much for taking the time to put in that code! It's greatly appreciated, and I'll probably reference it at some point for design solutions.
Also, thanks for the suggestion to learn virtual methods...I've just read the 'virtual functions and polymorphism' chapter in one of my books and they look to be exactly what I need for this.
And thanks for bringing this up:
Quote:
But most importantly, you misunderstood the concept of "type" probably.
You see, the cards you draw, put back, put on a spot, they are all contained withing the same type: Deck
|
I sort of wondered about this as I wrote up my general design...My base class Pile pretty much *is* the Deck class; it has most of the functions and variables as your Deck. The reason I decided to write different classes for the different types of piles was more for organization than anything else. I saw the various piles as special or different decks because the behave pretty differently.
Basically, my thought process was: I realize I could write everything as simply a Deck, but then how would I determine what type of Deck it was? Well, I could simply add a variable that tells me. But then how would I control the behavior of the different kinds of Decks? (Since they behave quite differently...especially in my version of Solitaire.  ) Each function would have to have a mess of if statements, determining how the Deck should behave depending on that variable.
But that seemed like it would be less elegant than just seperating the Decks into different classes. Now, I'm quite a newb; is there a better way than that ^ to organize this when there's only one Deck class?
Thanks a bunch!
-Stacy
|
|
|
08-14-2005, 06:40 AM
|
#6 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
Cards are cards and decks are decks. You need only two classes to define a Deck. The Deck class and the Card class.
Let the game engine (the machine that enforces the game rules) manage different piles of cards. You'll be always set. Just move Cards from one Deck to others (which may start as an empty deck). Altough the other pile of cards don't have to be a Deck at all! They can be just simple arrays holding the cards you like (or don't like). And then let the engine deal with them when it's time to do so (like putting back in the original Deck for a new "try" within that round).
I'll write some code today. I'll be back. In the mean time, telll me what to do with cards that don't fit in any of the 4 rows. I don't know this game basically. Do I discard them on a new pile and reshuffle them later to try again?
__________________
|
|
|
08-14-2005, 09:53 AM
|
#7 (permalink)
|
|
Registered User
Join Date: Jun 2005
Posts: 26
|
I'm not sure I know what 4 rows you're talking about...?
|
|
|
08-14-2005, 10:22 AM
|
#8 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
__________________
|
|
|
08-14-2005, 10:43 AM
|
#9 (permalink)
|
|
Registered User
Join Date: Jun 2005
Posts: 26
|
Ah...
My game is a bit different. I'm writing up some rules now...I'll check back in a bit.
Thanks!
|
|
|
08-14-2005, 11:40 AM
|
#10 (permalink)
|
|
Registered User
Join Date: Jun 2005
Posts: 26
|
Alrighty. Here's how my game is played. I'm sure some of the rules I've written up are unclear, because I just sat here and wrote them while talking on the phone...So, ask if you're not sure.  Please don't feel obligated to code any of this; I can do the messy part myself. I'd much rather take advantage of your expertise by figuring out design and stuff.
For instance, what would a game engine look like? (psuedo-code or just an explanation is fine  )
Thanks much,
Stacy
(Yeah, yeah, I'm linking to another page because I can't find my photobucket login info...  )
Game Rules
|
|
|
08-14-2005, 05:10 PM
|
#11 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
Quote:
|
For instance, what would a game engine look like?
|
Think in terms of behaviour. Always do that. Object oriented programming is thinking in terms of behaviour.
Let the engine start up the game process, let it enforce your game rules, let it do all the smart things a Deck or Card or even a Player can't (or shouldn't) do. The engine decides whether a Player is allowed to pick a new card for example. The engine moves cards from one spot to a different spot. And so forth. It is a true manager/controller. Make something work. Then much later see if it needs re-design. It is impossible to design everything in one turn. This goes for all levels in the programming world.
For now we are not interested in how the piles look like actually. We are only interested in its use case: behaviour that is. Get some basic behaviour up and running. From there, it will become easy.
__________________
|
|
|
08-14-2005, 07:30 PM
|
#12 (permalink)
|
|
Registered User
Join Date: Jun 2005
Posts: 26
|
So, does a game engine have a main method? Is it the file that actually runs the game?
Thanks for all your help on this...
|
|
|
08-14-2005, 08:00 PM
|
#13 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
No it is just a class that manages the interaction between all your models (Player, Card, Deck, etc).
First start with setting up a Player, Deck and Card, and see if you can transfer a card from the main deck to the hand of the player. If the player has a card then the main deck doesn't contain that card anymore so you'll need to remove that card from the main deck.
Start with this.
__________________
|
|
|
08-14-2005, 08:43 PM
|
#14 (permalink)
|
|
[code][/code] enforcer
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
|
To get you started, here is some working code. Your job is to excercise with it.
1) Show the original contents of the Deck immediatly after the shuffle.
2) Show the contents of the Deck after the player grabbed the top card from the deck.
3) Show what the player has in his/her hand.
4) Do this all in main.cpp.
Then we talk a bit about engines. Does it sound good to you?
Card.h
Code:
#ifndef CARD_H
#define CARD_H
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
class Card
{
public:
Card(pair<string, unsigned> crd) : aCard(crd) {}
friend ostream &operator<<(ostream &os, Card const &c);
public:
string getSuit() const { return aCard.first; }
unsigned getValue() const { return aCard.second; }
pair<string, unsigned> getCard() const { return aCard; }
private:
pair<string, int> aCard;
};
#endif //CARD_H
Card.cpp
Code:
#include "card.h"
ostream &operator<<(ostream &os, Card const &c)
{
cout<<resetiosflags(ios::right);
cout<<setiosflags(ios::left);
string sName;
char suitName[6];
switch(c.aCard.second)
{
default:
itoa(c.aCard.second, suitName, 10); break;
case 11:
strcpy(suitName, "Jack"); break;
case 12:
strcpy(suitName, "Queen"); break;
case 13:
strcpy(suitName, "King"); break;
case 14:
strcpy(suitName, "Ace"); break;
}
sName = suitName;
int len = c.aCard.first.length()+sName.length()+1;
cout<<setw(40-len/2)<<""<<c.aCard.first<<" "<<suitName;
resetiosflags(ios::left);
return os;
}
Deck.h
Code:
#ifndef DECK_H
#define DECK_H
#include "card.h"
#include <ctime>
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
using namespace std;
class Deck
{
public:
Deck();
void CreateDeck();
void RemoveTopCard();
void Shuffle();
void Show(ostream& os);
vector<Card> getDeck() const;
Card& operator [] (unsigned index);
private:
vector<Card> theDeck;
};
#endif //DECK_H
Deck.cpp
Code:
#include "Deck.h"
Deck::Deck()
{
CreateDeck();
}
//--------------------------------------------------------
void Deck::CreateDeck()
{
int cardtype;
string suit;
for (int i=0; i<52; i++)
{
// First 13 cards are Clubs, next 13 are Spades etc.
cardtype = i/13;
switch(cardtype)
{
case 0: suit = "Club"; break;
case 1: suit = "Spade"; break;
case 2: suit = "Diamond"; break;
case 3: suit = "Heart"; break;
}
theDeck.push_back(Card(pair<string,unsigned>(suit, 2+i%13) ));
}
}
//-------------------------------------------------------
void Deck::RemoveTopCard()
{
theDeck.erase(theDeck.begin());
}
//-------------------------------------------------------
void Deck::Shuffle()
{
static bool seeded;
if (!seeded)
{
srand(time(0));
seeded = true;
}
random_shuffle(theDeck.begin(), theDeck.end());
}
//----------------------------------------------------------
void Deck::Show(ostream& os)
{
copy(theDeck.begin(), theDeck.end(), ostream_iterator<Card>(os, "\n") );
}
//-----------------------------------------------------------
vector<Card> Deck::getDeck() const
{
return theDeck;
}
//-----------------------------------------------------------
Card& Deck::operator [] (unsigned index)
{
return theDeck[index];
}
player.h
Code:
#ifndef PLAYER_H
#define PLAYER_H
#include "card.h"
#include "Deck.h"
#include <string>
class Player
{
public:
Player();
~Player();
public:
void grab_one_card(const Deck& ExDeck);
protected:
std::pair<std::string, int> myCard_;
};
#endif // PLAYER_H
player.cpp
Code:
#include "player.h"
Player::Player()
{
//ctor
}
//-----------------------------------------
Player::~Player()
{
//dtor
}
//-----------------------------------------
void Player::grab_one_card(const Deck& ExDeck)
{
myCard_.first = (*ExDeck.getDeck().begin()).getSuit();
myCard_.second = (*ExDeck.getDeck().begin()).getValue();
}
main.cpp
Code:
#include <iostream>
#include "card.h"
#include "Deck.h"
#include "player.h"
int main()
{
//First we create a deck and shuffle the cards in the deck.
Deck theDeck;
theDeck.Shuffle();
//Then we create a player.
Player thePlayer;
//Player grabs a card from the (main) Deck.
thePlayer.grab_one_card(theDeck);
//ENGINE removes the top card of the (main) Deck because player has one in hand.
theDeck.RemoveTopCard();
return 0;
}
__________________
Last edited by Valmont; 08-15-2005 at 08:51 AM.
|
|
|
08-15-2005, 03:47 PM
|
#15 (permalink)
|
|
Registered User
Join Date: Jun 2005
Posts: 26
|
Thanks for the walkthru you're giving me here. I really appreciate it.
I've never learned ostream. (...like in Deck::Show(ostream& os)) Can you tell me how to work with this stuff real quick?
|
|
|
| Thread Tools |
|
|
| Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -8. The time now is 04:08 PM.
|
Copyright © 2000-2008, Milano Interactive
Web Hosting provided by Portal 360 Web Hosting
|
 |
|