I would use structs to represent the cards, rather than just arbitrary arrays. Here's a blackjack game I wrote a few years ago for school that might give you the right idea.
/*************************************************************************
Project: Lab 4
Files: main.cpp
Date: March. 24, 2008
Author: Kyle Wesley Class: 2D
Instructor: Cindy Chung Course: CNT252
Description: Blackjack game that uses structures, type def, and binary write
techniques to load and save the deck of cards.
*************************************************************************/
#include<iostream>
#include<fstream>
#include<ctime>
#include<iomanip>
using namespace std;
//The card's structure
struct SDeck
{
int m_Value; //value of the card
char m_Suit[10];//suit of the card
char m_Face[10];//face of the card
bool is_used; //flag if its used
bool is_ace; //flag if its an ace
};
//total dump of game data. used to save/load
struct SGame
{
double m_dCash; //total cash
int m_iCards; //total cards played
SDeck CurrentDeck[52]; //dump of current deck of cards
};
//makes more sense to refer to cards as cards...
typedef SDeck CARD;
int GetRand(int, int);
double GetDouble(char [], double, double);
void DoLoad (SDeck [], double &, int &);
bool InitDeck(SDeck []);
void DoSave (SGame);
void DrawCard(SDeck [], CARD &);
void ShowCard(const CARD);
const char* DECKLOCATION = "Lab4CardDeckTextFile.txt";
int main()
{
//****************************************************************
//Initialize the game
//****************************************************************
double dMoney = 110, //self explanitory...
dBet = 0; //bet placed
bool bHasAce = false, //flag if the player (comp or user) has an ace
bBust = false, //flag if the user busts by drawing too many cards
bFiveCardWin = false, // auto win if you get 5 cards
bBlackJack = false;//flag if the user gets a blackjack, thus getting 1.5x bet
int iCardsPlayed = 0, //total cards played so far
iHandValue = 0, //Value of the players hand
iCompHandValue = 0; //Value of the computers had
char cAnother = 0;
int i = 0;
SGame Game = {0};
SDeck Deck[52] = {0}; //init the deck array struct
CARD Card = {0}; //init your card
CARD CompCard = {0}; //init the computers card
srand(static_cast<unsigned int>(time(0))); //makes seed for randomized number
if (InitDeck(Deck)) //load the deck, check if it worked
{
//****************************************************************
//Start of actual game code...
//****************************************************************
DoLoad(Deck, dMoney, iCardsPlayed); //load the game, if wanted
do
{
//-------------
//reset everything that need to be reset
//-------------
bHasAce = false;
iHandValue = 0;
bBust = false;
bBlackJack = false;
iCompHandValue = 0;
//startup text
cout << " Blackjack";
if (iCardsPlayed >= 42) //hardcoded shuffle-point so the deck
//doesnt run out of cards
{
cout << "\nThe deck has been re-shuffled...";
InitDeck(Deck); //reshuffle the deck
iCardsPlayed = 0; //cards played resets
}
//display cash, get bet
cout << "\nPlay a hand of blackjack...";
cout << "\n\nYou have $" << setprecision(2) << fixed << dMoney - 10 << endl;
dBet = GetDouble("Enter bet: ", 5.0, dMoney);
DrawCard(Deck, Card); //draw the card from the deck
cout << "\nCard " << 1 << ": "; //card 1 is hardcoded because its mandatory
iHandValue += Card.m_Value; //add to the card value
ShowCard (Card); //display the card
//*********************************************
if (Card.is_ace) //ace detection, I chose not to have any sort of
{ //ace value entry. I thought it would be much more
//useful to always have the ace to your advantage,
bHasAce = true; //so I wrote an algorithm to do so
iHandValue -= 10; //*********************************************
}
iCardsPlayed++; //inrement cards played
cout << endl;
for (i = 1; i < 5; i++) //start loop of drawing cards, up to 5
{
iCardsPlayed++; //increment cards played
DrawCard(Deck, Card); //draw a card from the deck...
cout << "\nCard " << i + 1 << ": ";
iHandValue += Card.m_Value; //add the value to the hand
ShowCard (Card); //display the card
if (Card.is_ace) //*ace detection*
{
bHasAce = true;
iHandValue -= 10;
}
//Some code for deciding a bust or a blackjack, or a 5 card win, which cancels the
//rest of the cards, and skips the computer.
//Flags and a break from the loop are used to do this.
if (i == 1 && iHandValue == 11 && bHasAce == true)
{
cout << "\nBlackjack!";
dMoney += (dBet * 1.5); //if I remember blackjack correctly, if a player
//gets a blackjack, they get 150% of the bet
bBlackJack = true;
iHandValue += 10; //autocorrects hand value since it has an ace
break;
}
if (iHandValue > 21)
{
cout << "\nBust! You lose!";
dMoney -= dBet; //they lose...we get their money!
bBust = true;
break;
}
if (i == 5 && iHandValue <= 21) //test the 5 card win rule
{
bFiveCardWin = true;
cout << "\nFive cards, no bust, you win!";
dMoney += dBet;
}
else
{
//weird looking way to make the Another Card always line up
cout << setw(50 - (strlen(Card.m_Face) + strlen(Card.m_Suit) + 4))
<< right << "Another card (y/n)? ";
cin >> cAnother;
cAnother = tolower(cAnother);//makes it easier to detect valid entry
//sentinel loop (theres a lot of this same loop in this program. I regret not
//making a function out of it.
while ((cAnother != 'y' && cAnother != 'n') || cin.rdbuf()->in_avail() > 1 || cin.fail())
{
cin.clear();
cin.ignore(cin.rdbuf()->in_avail());
cout << "Invalid Entry...\n";
cout << "Another card (y/n)? ";
cin >> cAnother;
}
//flush the buffer!
cin.ignore(cin.rdbuf()->in_avail());
if (cAnother == 'n')
break;
}
}
if (bHasAce && (iHandValue <= 11)) //automatically make the ace work to your advantage
iHandValue += 10;
//Display the hand value
cout << "\nHand Value: " << iHandValue << endl;
if (i == 5 && iHandValue <= 21)
{
bFiveCardWin = true;
cout << "\nFive cards, no bust, you win!";
dMoney += dBet;
}
if (!bBust && !bBlackJack && !bFiveCardWin) //these 3 thinks mean the computer doesnt play
{
bHasAce = false;
cout << "\nThe Computer will now play...";
for (i = 0; i < 5; i++)
{
//**********************
//computer algorithm begins
//**********************
iCardsPlayed++; //increment the cards again
DrawCard (Deck, CompCard); //get a card for the computer
cout << "\nCard " << i + 1 << ": ";
ShowCard (CompCard);
if (CompCard.is_ace) //add a bit of strategy when the dealer gets an ace
{ //so he will use it to his advantage (I'm pretty sure
//thats part of the rules? Makes it funner I think)
bHasAce = true;
iCompHandValue -= 10;
}
cout << endl;
iCompHandValue += CompCard.m_Value; //add the to computers hand value
if (iCompHandValue > iHandValue && bHasAce) //if he's got a better hand
//with a low ace, he will continue
//to play with the ace as a 1
bHasAce = false;
//if he needs it to beat you, the computer will make an ace a 11
if ((iCompHandValue <= 11) && (iCompHandValue < iHandValue) && bHasAce)
iCompHandValue += 10;
//standard house rules, dealer stays at 17
if (iCompHandValue >= 17)
break;
}
cout << "\nComputer Hand Value: " << iCompHandValue;
//**********
//hand comparison begins
//***********
if ((iHandValue > iCompHandValue) && iHandValue <= 21)
{
cout << "\nYou win $" << dBet << " !";
dMoney += dBet;
}
else if (iCompHandValue > 21)
{
cout << "\nDealer busts, you win $" << dBet << " !";
dMoney += dBet;
}
else if ((iCompHandValue > iHandValue) && iCompHandValue <= 21)
{
cout << "\nDealer wins !";
dMoney -= dBet;
}
else if (iCompHandValue == iHandValue)
cout << "\nPush!";
}
if (dMoney > 15) //if you have money, offer another hand, otherwise they get kicked out
{
cout << "\nYou now have $" << dMoney - 10 << " would you like to play another hand (y/n) ?";
//same loop again. i use cAnother for all of em. no point in wasting memory
cin >> cAnother;
cAnother = tolower(cAnother);
while ((cAnother != 'y' && cAnother != 'n') || cin.rdbuf()->in_avail() > 1 || cin.fail())
{
cin.clear();
cin.ignore(cin.rdbuf()->in_avail());
cout << "Invalid Entry...\n";
cout << "Another hand (y/n)? ";
cin >> cAnother;
}
//clear the buffer
cin.ignore(cin.rdbuf()->in_avail());
cout << "\n\n\n\n\n\n\n";
}
} while (cAnother != 'n' && dMoney != 10.00);
//load the deck into the game variable
//and prepare the Game variable for saving
if (dMoney > 15)
{
for (int ii = 0; ii < 52; ii++)
Game.CurrentDeck[ii] = Deck[ii];
Game.m_iCards = iCardsPlayed;
Game.m_dCash = dMoney;
DoSave(Game); //save game
}
else //display a message if you run out of money
{
cout << "\nGame over! You gambled away your savings!";
cout << "\nYou don't have enough money to make a valid bet!";
cin.get();
}
}
else
//wait for input to quit
cout << "\n\nPress enter to quit...";
cin.ignore(cin.rdbuf()->in_avail());
cin.get();
return 0;
}
/*************************************************************************
Function: void DoLoad (SDeck Deck[], double &dMoney, int &iCards)
Paramaters: SDeck Deck[] - The Deck structure for loading to
Double &dMoney - reference to the total money
int iCards - total cars played so far
Description: Loads a binary file into SGame, then from there disperses the deck,
money and cards to their respective variables
Returns: void
*************************************************************************/
void DoLoad (SDeck Deck[], double &dMoney, int &iCards)
{
char cAnother; //used in the y/n loop to load the game
SGame Game; //game struct holding all game status
char szFileName[255] = {0}; //filename to load from
ifstream InFile; //file opened thats being loaded from
cout << "Would you like to load a game (y/n)? ";
cin >> cAnother;
cAnother = tolower(cAnother);//same sentinel again
while ((cAnother != 'y' && cAnother != 'n') || cin.rdbuf()->in_avail() > 1 || cin.fail())
{
cin.clear();
cin.ignore(cin.rdbuf()->in_avail());
cout << "Invalid Entry...\n";
cout << "Would you like to load a game (y/n)? ";
cin >> cAnother;
cAnother = tolower(cAnother);
}
//clear the buffers
cin.clear();
cin.ignore(cin.rdbuf()->in_avail());
if (cAnother == 'y')
{
cout << "\nEnter filename to load from: ";
cin.getline(szFileName, 255);
if (!InFile.fail())
{
//open the file for reading and read in the structs size worth of bytes
//which is (hopefully) all of them.
InFile.open(szFileName, ios::in | ios::binary);
InFile.read(reinterpret_cast<char*>(&Game), sizeof(SGame));
InFile.close();
//spread out the freshly loaded data
Deck = Game.CurrentDeck;
dMoney = Game.m_dCash;
iCards = Game.m_iCards;
cout << "\nGame loaded successfully!\n\n";
}
else //error opening file
cout << "\nError opening file...";
}
return;
}
/*************************************************************************
Function: void DoSave (SGame Game)
Paramaters: SGame Game - The game status all in one package
Description: Does a binary dump of all the game's status
Returns: void
*************************************************************************/
void DoSave (SGame Game)
{
char cAnother;
char szFileName[255] = {0};
ofstream OutFile; //open the file
cout << "\nWould you like to save(y or n)?";
cin >> cAnother;
cAnother = tolower(cAnother); //some old loop
while ((cAnother != 'y' && cAnother != 'n') || cin.rdbuf()->in_avail() > 1 || cin.fail())
{
cin.clear();
cin.ignore(cin.rdbuf()->in_avail());
cout << "Invalid Entry...\n";
cout << "Would you like to save (y/n)? ";
cin >> cAnother;
}
cin.ignore(cin.rdbuf()->in_avail());
if (cAnother == 'y')
{
//get the filename to save to
cout << "\nEnter filename to save to: ";
cin.getline(szFileName, 255);
//open it
OutFile.open(szFileName, ios::out | ios::binary);
if (!OutFile.fail())
{
//write all the bytes in the Game struct to the file
OutFile.write(reinterpret_cast<char*>(&Game), sizeof(SGame));
OutFile.close();
cout << "File Saved Succeffully!";
}
else //access violation or no admin status might cause this
cout << "\nError opening file for saving...";
}
return;
}
/*************************************************************************
Function: double GetDouble(char szDisplay[], double dMin, double dMax)
Paramaters: char szDisplay[] - text to be displayed
double dMin - minimum entry allowed
double dMax - maximum entry allowed
Description: Loads a double for the bet entry, with a sentinel
Returns: dValue
*************************************************************************/
double GetDouble(char szDisplay[], double dMin, double dMax)
{
double dValue = 0; //value to be input
cout << szDisplay;
cin >> dValue;
//sentinel loop
while (cin.fail() || cin.rdbuf()->in_avail() > 1 || dValue < dMin || dValue > dMax)
{
cin.clear();
cin.ignore(cin.rdbuf()->in_avail());
cout << "Invalid Bet...\n";
cout << szDisplay;
cin >> dValue;
}
cin.ignore(cin.rdbuf()->in_avail());
return dValue;
}
/*************************************************************************
Function: void InitDeck(SDeck Deck [])
Paramaters: SDeck Deck [] - deck (array of cards) to be initialized
Description: Loads all the cards from the card txt file. Hardcoded max of
52 cards. I guess this could be made dynamic if a custom/more
decks were wanted to be used
Returns: void
*************************************************************************/
bool InitDeck(SDeck Deck [])
{
bool bNoError = true;
ifstream InFile;
InFile.open(DECKLOCATION); //uses the hardcoded constant DECKLOCATION location
if (!InFile.fail())
{
for (int i = 0; i < 52; i++)
{
//load the values
InFile >> Deck[i].m_Suit >> Deck[i].m_Face >> Deck[i].m_Value;
Deck[i].is_used = false; //not in use, default not an ace
Deck[i].is_ace = false;
if (Deck[i].m_Value == 11) //ace detection
Deck[i].is_ace = true; //use this for clarity in other ace
//detections
}
}
else //couldnt find the file
{
cout << "Error!\nFile: " << DECKLOCATION << " could not be opened.\nThere's n o Deck to be used!\n";
bNoError = false;
}
InFile.close();
return bNoError;
}
/*************************************************************************
Function: void ShowCard(const CARD Card)
Paramaters: const CARD Card - the card, constant for safety
Description: Show the card in a more readable form
Returns: dValue
*************************************************************************/
void ShowCard(const CARD Card)
{
cout << Card.m_Face << " of " << Card.m_Suit;
return;
}
/*************************************************************************
Function: void DrawCard(SDeck Deck[], CARD &Card)
Paramaters: SDeck Deck[] - the deck to be drawn from
CARD &Card - the card to draw, passed by reference
Description: Randomly draws a card from the deck to be used int he game
Returns: void
*************************************************************************/
void DrawCard(SDeck Deck[], CARD &Card)
{
int iRandomIndex; //index to try and use
do
{
iRandomIndex = GetRand(0, 51); //makes a random number to be used as an index
Card = Deck[iRandomIndex]; //sets the card to the card at that index in the deck
} while (Deck[iRandomIndex].is_used); //makes sure that card isnt in use
Deck[iRandomIndex].is_used = true; //set the card in use flag
Card.is_used = true; //not really used for anything...but set to
//be safe
return;
}
/*************************************************************************
Function: int GetRand(int iMin, int iMax)
Paramaters: int iMin - minimum number
int iMax - maximum number
Description: Generates a random integer between iMin and iMax
Returns: iResult - the random number...
*************************************************************************/
int GetRand(int iMin, int iMax)
{
int iResult;
//the seed was set in main, since it only needs to be set once
iResult = iMin + rand()%(iMax - iMin + 1); //generate the number
return iResult;
}