Tarot Card Program Design

Hello! I am working on a final project for my intermediate C++ class. For the project concepts to be utilized we are supposed to implement the following:

Opening screen with a description of the application and instructions
Menu for the user to choose options
At least 4 classes total
Inheritance (minimum 2 derived classes)
Polymorphism (Overloading and overriding)
Encapsulation
File input and output processing
Multi-Threading
Exception handling
Abstraction

I used an example of a Black Jack game in one of my books to get started
for the file I/O I created and saved a document as an input file with information pertaining to the tarot cards to display.
I intend on allowing the user to save readings to an output file.
I was thinking of somehow trying to figure out how to implement multithreading within the cards being drawn but I need to clean up the mess I have made first. So far only the opening screen runs correctly.
Your help is sincerely greatly appreciated. I'm hoping I can get this completed in time.


//Header File
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <ctime>
#include <fstream>

using namespace std;

//MENU OPTIONS

//functions for menu options
void startReading();
void retrieveData();
void viewTarot();

//menu options
char menu()
{
char response;
cout << "Would you like to - ";
cout << "[S]tart a new reading, [R]etrieve a previous reading, [V]iew up tarot card information or [Q]uit?" << endl;
cin >> response;
cin.ignore(256, '\n');
return toupper(response);
}

//starts card game simulation
void startReading()
{
system("pause");
system("CLS");
}

//displays last saved reading
void retrieveData()
{
system("pause");
system("CLS");
}

//view tarot card input file
void viewTarot()
{
system("pause");
system("CLS");
}

//add messages to saved output file
void dataLog()
{
system("pause");
system("CLS");
}

//File I/O

class TarotLogg
{
private:
string writeLine; //input string

public:

//function to open and output file
void startRem()
{

ifstream infile("TarotDiv.txt"); //input file stream
ofstream outfile("TarotLog.txt"); //output file stream

//opens text file
if (infile.is_open())
{
//outputs text file to console
if (outfile.is_open())
{
//reads through file until the end
while (getline(infile, writeLine))
{
cout << writeLine << endl;
outfile << writeLine << "\n"; //prints line


} //end of while

outfile.close(); //closes output file

} //end of outfile

//error if output file can not be opened
else cout << "Unable to open output file. \n" << endl;

infile.close(); //closes input file

} //end of infile

//error if input file can not be opened
else cout << "Unable to open input file. \n" << endl;
system("pause");
system("CLS");

} //end of void

}; //end of base class


//Base class - Opening menu to simulator
class startSim
{
private:
string name;

public:
//user input function
void getInput()
{
cout << "Welcome to Tarot Divination\n" << endl;
cout << "Please enter your name: " << endl;
cin >> name;
cout << "\nHello " << name << "!\n" << endl;
cout << "Prepare to enter your path to destiny..." << endl;

system("pause");
system("CLS");
}

};

//Cards and suits
class Card
{
public:
enum rank {ACE = 1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, PAGE, KNIGHT, QUEEN, KING};

enum arcana {FOOL = 1, MAGICIAN = 2, HIGH_PRIESTESS = 3, EMPRESS = 4, EMPEROR = 5, HIEROPHANT = 6, LOVERS = 7, CHARIOT = 8,
STRENGTH = 9, HERMIT = 10, WHEEL_OF_FORTUNE = 11, JUSTICE = 12, HANGED_MAN = 13, DEATH = 14, TEMPERANCE = 15, DEVIL = 16,
TOWER = 17, STAR = 18, MOON = 19, SUN = 20, JUDGEMENT = 21, WORLD = 22};

enum suit {WANDS, CUPS, PENTACLES, SWORDS};

//overloading operator to send card object to standard output
friend ostream& operator<<(ostream& os, const Card& aCard);

Card(rank r = ACE, suit s = WANDS, arcana a = FOOL, bool ifu = true);

//flips a card; if face up, becomes face down, vice versa
void Flip();

//returns value of card
int GetValue() const;

private:
rank m_rank;
arcana m_arcana;
suit m_suit;
bool m_ifu;

};

Card::Card(rank r, suit s, arcana a, bool ifu) : m_rank(r), m_suit(s), m_arcana(a), m_ifu(ifu) {}

int Card::GetValue() const
{
//if card is face down its value is 0
int value = 0;
if (m_ifu)
{
value = m_rank;
if (value > 10)
{
value = 10;
}
}
return value;
}

void Card::Flip()
{
m_ifu = !(m_ifu);
}

//designed for a collection of cards
class Hand
{
public:
Hand();
virtual ~Hand();

//adds a card to the hand
void Add(Card* pCard);

//clears hand of all cards
void Clear();

//gets hand total value
int GetTotal() const;

protected:
vector<Card*> m_cards;
};

Hand::Hand()
{
m_cards.reserve(5);
}

Hand::~Hand()
{
Clear();
}

void Hand::Add(Card* pCard)
{
m_cards.push_back(pCard);
}

void Hand::Clear()
{
//iterate through vector, freeing all memory on the heap
vector<Card*>::iterator iter = m_cards.begin();
for (iter = m_cards.begin(); iter != m_cards.end(); ++iter)
{
delete *iter;
*iter = 0;
}

//clear vector of pointers
m_cards.clear();
}

int Hand::GetTotal() const
{
//if no cards in hand return 0
if (m_cards.empty())
{
return 0;
}

//if first card has a value of 0, card is face down; return 0;
if (m_cards[0]->GetValue() == 0)
{
return 0;
}

//add card values, treat aces as 1
int total = 0;

vector<Card*>::const_iterator iter;
for(iter = m_cards.begin(); iter != m_cards.end(); ++iter)
{
total += (*iter)->GetValue();
}

//determine if hand contains an ace
bool containsAce = false;
for (iter = m_cards.begin(); iter != m_cards.end(); ++iter)
{
if ((*iter)->GetValue() == Card::ACE)
{
containsAce = true;
}
}

//wrote same bool param for every Major Arcana here as well

return total;
}

class Deck : public Hand
{
public:
Deck();

virtual ~Deck();

//Create tarot deck of 78 cards
void populate();

//shuffle cards
void shuffle();

//deal one card
void deal(Hand& aHand);
};

Deck::Deck()
{
m_cards.reserve(78);
populate();
}

Deck::~Deck() {}

void Deck::populate()
{
Clear();
//create tarot deck

for (int s = Card::WANDS; s <= Card::SWORDS; ++s)
{
for (int r = Card::ACE; r <= Card::KING; ++r)
{
for (int a = Card::FOOL; a <= Card::WORLD; ++a)
{
Add(new Card(static_cast<Card::rank>(r), static_cast<Card::suit>(s), static_cast<Card::arcana>(a)));
}
}
}
}

void Deck::shuffle()
{
random_shuffle(m_cards.begin(), m_cards.end());
}

void Deck::deal(Hand& aHand)
{
if (!m_cards.empty())
{
aHand.Add(m_cards.back());
m_cards.pop_back();
}
else
{
cout << "Out of cards, unable to deal." << endl;
}
}

class Player : public Hand
{
public:
Player(const string& name = "");

virtual ~Player();

//returns weather or not player wants to draw another card
virtual bool drawCard() const;

protected:
string m_name;
};

Player::Player(const string& name) : m_name(name) {}

Player::~Player() {}

bool Player::drawCard() const
{
cout << m_name << ", do you want to draw another card? [Y/N] " << endl;
char respond;
cin >> respond;
return toupper(respond);

}

class TarotReading
{
public:
TarotReading(const vector<string>& name);
~TarotReading();

//starts tarot reading
void start();

private:
Deck m_deck;
vector<Player> m_player;
};

TarotReading::TarotReading(const vector<string>& name)
{

//creates vector of players from vector of names
vector<string>::const_iterator pName;
for (pName = name.begin(); pName != name.end(); ++pName)
{
m_player.push_back(Player(*pName));
}

//seed the random number generator
srand(static_cast<unsigned int>(time(0)));
m_deck.populate();
m_deck.shuffle();
}

TarotReading::~TarotReading() {}

void TarotReading::start()
{
//deal cards to player
vector<Player>::iterator pPlayer;
for (int i = 0; i < 5; ++i)
{
for (pPlayer = m_player.begin(); pPlayer != m_player.end(); ++pPlayer)
{
m_deck.deal(*pPlayer);
}
}

cout << endl;

//display hand
for (pPlayer = m_player.begin(); pPlayer != m_player.end(); ++pPlayer)
{
cout << *pPlayer << endl;
//cout << "Your Hand" << endl;
}

//remove cards
for (pPlayer = m_player.begin(); pPlayer != m_player.end(); ++pPlayer)
{
pPlayer->Clear();
}
}
This is what my CPP source file looks like right now after deleting the messes

#include "tarot.h"

//declare overloaded operator in main function
ostream& operator<<(ostream& os, const Card& aCard);

int main()
{
//Start Simulator
startSim start;
start.getInput();

//////////////////////////////////////////////////////////////



/*

//menu selection switch
bool run = true;
do
{
switch (menu())
{
case 'S': startReading(); break;
case 'R': retrieveData(); break;
case 'V': viewTarot(); break;
case 'Q': run = false; break;
}
} while (run);


*/


return 0;
}

//overloading operator so cards can be sent to cout
ostream& operator<<(ostream& os, const Card& aCard)
{
const string RANKS[] = { "0", "ACE", "2", "3", "4", "5", "6", "7", "8", "9", "10", "PAGE", "KNIGHT", "QUEEN", "KING" };
const string SUITS[] = { "WANDS", "PENTACLES", "CUPS", "SWORDS" };
const string ARCANA[] = { "FOOL", "MAGICIAN", "HIGH_PRIESTESS", "EMPRESS", "EMPEROR", "HIEROPHANT", "LOVERS", "CHARIOT",
"STRENGTH", "HERMIT", "WHEEL_OF_FORTUNE", "JUSTICE", "HANGED_MAN", "DEATH", "TEMPERANCE", "DEVIL",
"TOWER", "STAR", "MOON", "SUN", "JUDGEMENT", "WORLD" };

if (aCard.m_ifu)
{
os << RANKS[aCard.m_rank] << SUITS[aCard.m_suit] << ARCANA[aCard.m_arcana];
}
else
{
os << "XX";
}

return os;

}
Well, for starters:

PLEASE learn to use code tags, they make reading and commenting on source code MUCH easier.

http://www.cplusplus.com/articles/jEywvCM9/
http://www.cplusplus.com/articles/z13hAqkS/

HINT: you can edit your post and add code tags.

Some formatting indentation would not hurt either
I was thinking of somehow trying to figure out how to implement multithreading within the cards being drawn but I need to clean up the mess I have made first. So far only the opening screen runs correctly.


First design, then code. It looks like you're 'coding on the hoof' which isn't the way to do it. You're ended up with loads of code that doesn't do what you want - and as you say, "is a mess".

Design the game. What input/output/processing/algorithms/data structures/classes are needed? If more than one class, what are the relationships between them?

Once you have the design, then you start to code from the design. You code in small parts and after each part you have a working program. The more your code the nearer the required result. Each part builds upon the previous part which you know is correct because you're tested it before you start to code the next part.

If something doesn't compile work, then you know the problem is with the last part that you coded.

This way, when you're coded the last part you have (or should have if your design is right) a correct working program.

What you don't do is to start off writing the code for the whole program, then set about testing/debugging/changing it.
Last edited on
Topic archived. No new replies allowed.