Text based solitaire game for windows and linux

_Nestor 0 Tallied Votes 4K Views Share

This is a text based solitaire game I wrote.
It tested it on Ubuntu Linux and Windows
and it worked okay for me.

Move cards between rows using their row number (7 for the deck)

Let me know what people think.
Critiques welcome..... don't be too harsh ;)

/*=================main.cpp===========================*/
#include <iostream>
#include "Solitaire.h"

using namespace std;

void HandleMove(Solitaire& game);
void HandleMoveToAnswer(Solitaire& game);
void ClearScreen();

int main(int argc, char** argv)
{	
	Solitaire game = Solitaire();
	game.PrintAllDetails();
	
	bool running = true;
	do{
		char option = ' ';
		cout<<endl<<endl;
		cout<<"1. move card between rows";		
		cout<<"\n2. move to answers";
		cout<<"\n3. deal";
		cout<<"\n0. quit";
		cout<<"\nEnter option:";
		cin >> option;
		
		/* 48 - 55 ascii code is 0 - 7 */

		if(game.GameCompleted())
		{
			ClearScreen();
			cout<<"\n\n\n\t\t\t CONGRATULATIONS YOU WON!!!\n\n";
			cin.get();
			running = false;
		}

		else
		{
			switch((int)option)
			{
			case 48:
				running = false;
				break;
			case 49:
				HandleMove(game);
				break;		
			case 50:
				HandleMoveToAnswer(game);
				break;		
			case 51:
				game.Deal(3);
				break;
			default:
				cout<<"\ninvalid option";
				cin.ignore(80,'\n');
				break;
			}
			ClearScreen();
			game.PrintAllDetails();			
		}

	}while(running);
	
	return 0;
}

void HandleMove(Solitaire& game)
{
	char from = '0' ,to = '0';
	cout<<endl <<endl;
	cout<<"From (7 for deck):";
	cin >> from;
	cout<< "To: ";
	cin>> to;

	if( ((int)from >= 48 && (int)from <= 55) &&
		((int)from >= 48 && (int)to <= 54) )

	{
		game.MakeMoveRowToRow((int)from - 48,(int)to - 48);
	}

	else
	{
		cout<<"invalid input";
		cin.get();
		cin.get();
	}
	
}

void HandleMoveToAnswer(Solitaire& game)
{
	char from = 0;
	cout<<endl <<endl;
	cout<< "From (7 for deck): ";
	cin>> from;
	
	if((int)from >= 48 && (int)from <= 55)
		game.MakeSuitMove((int)from - 48);
	
	else
	{
		cout<<"invalid input";
		cin.get();
		cin.get();
	}
	
}

void ClearScreen()
{
#ifdef __linux__ 
	system("clear");
#elif _WIN32
	system("cls");
#endif
}

/*=============================================*/
/*=================Card.h=======================*/
#pragma once
#include <iostream>

#ifdef _WIN32
#include<windows.h>
const static int BLACK = 0;
const static int BLUE = 9;
const static int RED = 12;
const static int WHITE = 15;
#elif __linux__
#include "stdlib.h"
#endif

class Card
{
public:
	Card(void);
	Card(char rank,char suit);	
	~Card(void);

	void Flip();
	bool GetIsFaceUp();

	int GetSolitaireValue();
	char GetCardSuit();
	char GetCardRank();
	
	friend std::ostream & operator<< (std::ostream & os, Card& c); 
    
	/* 
	   Used to navigate up and
	   down the cards in a row
	 */
	Card* parent;
	Card* child;
	
private:
	char rank;
	char suit;
	bool isFaceUp;

};

/*=============================================*/
/*====================Card.cpp===================*/
#include "Card.h"

Card::Card(void)
{
}

Card::Card(char r, char s)
:rank(r)
,suit(s)
,isFaceUp(false)
,child(NULL)
,parent(NULL)
{
}

Card::~Card(void)
{
}

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

bool Card::GetIsFaceUp()
{
	return isFaceUp;
}

char Card::GetCardSuit()
{
	return suit;
}

char Card::GetCardRank()
{
	return rank;
}

int Card::GetSolitaireValue()
{
	if(rank == 'A')
		return 1;
	else if(rank == 'T')
		return 10;
	else if(rank == 'J')
		return 11;
	else if(rank == 'Q')
		return 12;
	else if(rank == 'K')
		return 13;
	else
	{
		char c[] = {rank, '\0'};
		return atoi(c);
	}
}

std::ostream & operator<< (std::ostream & os, Card& c)
{

#ifdef _WIN32
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);

	if(c.suit == 'c' || c.suit == 's')
		SetConsoleTextAttribute(handle, BLUE);
	else
		SetConsoleTextAttribute(handle,RED);

	os<< c.suit << c.rank;
	SetConsoleTextAttribute(handle,WHITE);

#elif __linux__

	if(c.suit == 'c' || c.suit == 's')
		os<<"\033[22;34m";	
	else
		os<<"\033[22;31m";
	os<< c.suit << c.rank;
	os<<"\033[22;30m";

#endif

	return os;
}

/*=============================================*/
/*====================Deck.h====================*/
#pragma once
#include "Card.h"
#include <ctime>
#include <iostream>
#include "TableCard.h"

const static int RANK_SIZE = 13;
const static int SUIT_SIZE = 4;
const static char RANKS[] = {'A','2','3','4','5','6','7','8','9','T','J','Q','K'};
const static char SUITS[] = {'h','d','s','c'};


class Deck
{
public:
	Deck(void);
	~Deck(void);

    void Populate(void);
	void Shuffle(void);
	void PrintDeck(void);

	void PopulateVector(TableCard& aDeck);
	
private:
	Card _deck[52];
	int currentIndex;
};

/*==============================================*/
/*====================Deck.cpp====================*/
#include "Deck.h"

Deck::Deck(void)
:currentIndex(0)
{
	srand((unsigned)time(0));
	Populate();
}


Deck::~Deck(void)
{
}

void Deck::Populate()
{
	int index = 0;
	for(int i=0; i<SUIT_SIZE; i++)
	{
		for(int j=0; j<RANK_SIZE;j++)
		{
			_deck[index] = Card(RANKS[j],SUITS[i]);
			index++;
		}
	}
}

void Deck::Shuffle()
{
	int max = SUIT_SIZE * RANK_SIZE;
	for(int i=0; i<max-1; i++)
	{
		int randNum = rand() % 52;
		std::swap(_deck[i],_deck[randNum]);		
	}
	
}

void Deck::PrintDeck(void)
{
	int max = SUIT_SIZE * RANK_SIZE;
	for(int i=0; i<max; i++)
	{
		if(i %13 == 0)
			std::cout<< '\n' <<'\n';
		std::cout<< _deck[i] << " " << _deck[i].GetBlackjackValue() << "  ";
	}
}

void Deck::PopulateVector(TableCard& aDeck)
{
	int max = SUIT_SIZE * RANK_SIZE;
	aDeck.Clear();
	for(int i=0; i<max; i++)
		aDeck.PushValueCopy(_deck[i]);
}
/*===============================================*/
/*====================TableCard.h===================*/
#pragma once
#include "Card.h"
#include <vector>

class TableCard
{
public:
	TableCard(int maxSize);
	~TableCard(void);

	Card& operator[](int index);
	bool Push(Card& card);	
	bool PushValueCopy(Card card);	
	int Size();
	bool empty();
	Card& top(void);
	
	bool RemoveAt(int index);
	bool Pop();
	void Clear();	


	static void MoveBetween(TableCard& from, TableCard& to);
	

private:
	int _maxSize;
	int _size;
	std::vector<Card*> _cards;

};
/*=============================================*/
/*====================TableCard.cpp================*/
#include "TableCard.h"

TableCard::TableCard(int maxSize)
:_size(0)
,_maxSize(maxSize)
{	
	_cards.resize(maxSize);
}

TableCard::~TableCard(void)
{
}

bool TableCard::Push(Card& card)
{
	if(_size < _maxSize)
	{
		_cards[_size] = &card;
		_size++;
		return true;
	}
	return false;
}

bool TableCard::PushValueCopy(Card card)
{
	if(_size < _maxSize);
	{
		_cards[_size] = new Card(card);
		_size++;
		return true;
	}
	return false;
}

void TableCard::Clear()
{
	for(int i=0; i<_size; i++)
		_cards[i] = NULL;
	
	_size = 0;
}

bool TableCard::empty()
{
	if(_size == 0)
		return true;
	return false;
}

Card& TableCard::top(void)
{
	if(_size > 0)
		return *_cards[_size-1];
}

bool TableCard::RemoveAt(int index)
{
	if(index >= 0 && index < _maxSize)
	{
		_cards[index] = NULL;
		_size--;
		return true;
	}
	return false;
}

int TableCard::Size()
{
	return _size;
}


bool TableCard::Pop()
{
	if(_size > 0)
	{
		_cards[_size - 1] = NULL;
		_size--;
		return true;
	}
	return false;
}

Card& TableCard::operator[](int index)
{
	if(index >= 0 && index <= _size)
		return *_cards[index];
}

void TableCard::MoveBetween(TableCard& from, TableCard& to)
{
	to.Push(from.top());
	from.Pop();
}

/*=============================================*/
/*====================Solitaire.h==================*/
#pragma once

#include <iostream>
#include <vector>

#include "Deck.h"
#include "Card.h"
#include "TableCard.h"

const static int TABLE_SIZE = 7;

class Solitaire
{
public:
	Solitaire(void);
	~Solitaire(void);

	void PopulateTable(void);
	void Deal(int numToDeal=1);
	
	
	/* Functions for validating and moving cards  */
	void MakeSuitMove(int from);
	void MakeMoveBetweenRows(int from, int to);
	void MakeMoveDeckToRow(int to);
	void MakeMoveRowToRow(int from, int to);
	bool ValidMove(int from, int to);	
	bool ValidRowToRowMove(Card* fromCard, int to);
	bool ValidSuitMove(int from);

	/* Checks to see if all answer sections full */
	bool GameCompleted();
	
	/* Functions for printing card details */
	void PrintAllDetails(void);
	void PrintSuitDetails(void);
	void PrintDeckDetails(void);

private:
	
	TableCard _deck;
	TableCard _discardedCards;			
	std::vector<TableCard> _tableCards;
	
	//Destination of cards. When all filled player wins
	std::vector<TableCard> _suitCards;


};
/*==============================================*/
/*====================Solitaire.cpp=================*/
#include "Solitaire.h"
using namespace std;
Solitaire::Solitaire(void)
:_deck(52)
,_discardedCards(52)
{
	//Uses the deck class to populate solitaire deck
	//Shuffle the deck to randomize 
	Deck deck = Deck();
	deck.Shuffle();	
	deck.PopulateVector(_deck);
		
	for(int i=0; i<TABLE_SIZE; i++)
	{
		TableCard tableCard = TableCard(20);
		_tableCards.push_back(tableCard);
	}
	
	//_suitCards.resize(4);
	for(int i=0; i<4; i++)
	{	
		TableCard suitCard = TableCard(13);
		_suitCards.push_back(suitCard);	
	}
	PopulateTable();
}

Solitaire::~Solitaire(void)
{
}

void Solitaire::PopulateTable(void)
{
	for(int i=0; i<TABLE_SIZE; i++)
	{		
		for(int j=0; j<TABLE_SIZE -i; j++)
		{	
			if(j == (TABLE_SIZE - i) - 1)
				_deck.top().Flip();

			_tableCards[j].Push(_deck.top());
			_deck.Pop();
		}
	}
}

void Solitaire::Deal(int numToDeal)
{
	int size = _deck.Size();
	for(int i=0; i<size; i++)
	{
		if(_deck.top().GetIsFaceUp())
		{
			_deck.top().Flip();
			_discardedCards.Push(_deck.top());
			_deck.Pop();
		}		
	}

	if(_deck.empty())
	{
		for(int i=_discardedCards.Size()-1; i>=0; i--)
			_deck.Push(_discardedCards[i]);			
		
		_discardedCards.Clear();
	}

	int index = 0;
	for(int i=0; i<numToDeal; i++)
	{
		index = _deck.Size() -i -1;
		if(index >= 0)
		{
			_deck[index].Flip();
		}
		else
			break;
	}
}

void Solitaire::PrintSuitDetails()
{
	cout<<"=============================================\n";
	for(int j=0; j<_suitCards.size(); j++)
	{
		switch(j)
		{
		case 0:
			cout<< "h :";
			break;
		case 1:
			cout<< "d :";
			break;
		case 2:
			cout<< "s :";
			break;
		case 3:
			cout<< "c :";
			break;
		}
		
		for(int i=0; i<_suitCards[j].Size(); i++)
		{
			if(&_suitCards[j][i] != NULL)
				cout<<_suitCards[j][i] << " ";
			else
				break;
		}
		cout<< endl;
	}
	cout<<"=============================================\n";
	cout<<endl;
}

void Solitaire::PrintDeckDetails()
{
	cout<<"--------------------------------------------\n";
	cout<<"|Deck: ";
	for(unsigned int i=0; i<_deck.Size(); i++)
	{		
		if(_deck[i].GetIsFaceUp())
			cout<<_deck[i] << " ";
		
	}
	cout<<"\n--------------------------------------------\n";
}

void Solitaire::PrintAllDetails()
{
	
	PrintDeckDetails();
	PrintSuitDetails();


	cout<< "| 6 | \t | 5 | \t | 4 | \t | 3 | \t | 2 | \t | 1 | \t | 0 |\n";
	cout<< "----- \t ----- \t ----- \t ----- \t ----- \t ----- \t -----\n";
	
	//Cannot be unsigned as number is always greater than or equal to 0 --
	int colMax = _tableCards.size();
	int rowMax = 0;
	for(int i=0; i<colMax; i++)
	{
		if(_tableCards[i].Size() > rowMax)
		{
			rowMax = _tableCards[i].Size();
		}
	}

	for(int i=0; i<rowMax; i++)
	{		
		for(int j=colMax-1; j>=0; j--)
		{	
			if(i >= _tableCards[j].Size())
			{
				cout<< " \t " ;
				continue;
			}
					
			if(_tableCards[j][i].GetIsFaceUp())
				cout << "  "  << _tableCards[j][i] << " \t ";
			else
				cout << "  " <<(char)254 << " \t ";
		}
		cout<<endl;
	}
	cout<<endl<<endl;


	//SHOW DISCARDED CARDS
	/*cout<<"\nCards in the used pile: \n\n";
	for(unsigned int i=0; i<_discardedCards.Size(); i++)
		cout<<_discardedCards[i] << " ";
	cout<<endl<<endl;
	*/
		//DISPLAYS CHILDREN AND PARENTS
	/*for(int i=0; i<_tableCards.size(); i++)
	{
		cout<< i << ": \n" ; 
		for(int j= 0; j< _tableCards[i].Size(); j++)
		{
			if(_tableCards[i][j].child != NULL)
			{
				cout<< _tableCards[i][j] << "'s child is " << *_tableCards[i][j].child << endl;
			}
			if(_tableCards[i][j].parent != NULL)
			{
				cout<< _tableCards[i][j] << "'s parent is " << *_tableCards[i][j].parent << endl;
			}
		}
	}*/

}

bool Solitaire::ValidMove(int from, int to)
{	

	//start move and end move the same
	if(from == to && from != 7)
		return false;

	Card* toCard;
	Card* fromCard;

	if(from == 7)
	{
		if(!_deck.empty())
		{
			if(!_deck.top().GetIsFaceUp())
			{
				return false;
			}
			else
			{
				fromCard = &_deck.top();
			}
		}
		else
		{
			return false;
		}
	}
	else
	{
		//if no card in moving from row not valid
		if(!_tableCards[from].empty())
			fromCard = &_tableCards[from].top();
		else
			return false;
	}

	if(!_tableCards[to].empty())
		toCard = &_tableCards[to].top();

	//move king to empty space
	if(_tableCards[to].empty())
	{		
		if(fromCard->GetCardRank() == 'K')
			return true;
		else
			return false;
	}

	else if(fromCard->GetSolitaireValue() == toCard->GetSolitaireValue()-1 )
	{
		int toColor = 0, fromColor = 0;
		if(toCard->GetCardSuit() == 'h' || toCard->GetCardSuit() == 'd')
			toColor = 0;
		else
			toColor = 1;
		if(fromCard->GetCardSuit() =='h' || fromCard->GetCardSuit() == 'd')
			fromColor = 0;
		else
			fromColor = 1;

		if(fromColor == toColor)
			return false;
		else
			return true;
	}	
	return false;
}

bool Solitaire::ValidSuitMove(int from)
{
	Card* fromCard;
	if(from == 7)
	{
		if(!_deck.empty())
		{
			if(!_deck.top().GetIsFaceUp())
			{
				return false;
			}
			else
			{
				fromCard = &_deck.top();
			}
		}
		else
		{
			return false;
		}
	}
	else
	{
		if(!_tableCards[from].empty())
			fromCard = &_tableCards[from].top();
		else
			return false;
	}


	char suit = fromCard->GetCardSuit();
	int moveIndex = 0;
	switch(suit)
	{
	case 'h':
		moveIndex = 0;
		break;
	case 'd':
		moveIndex =	1;
		break;
	case 's':
		moveIndex = 2;
		break;
	case 'c':
		moveIndex = 3;
		break;
	}

	if(fromCard->GetSolitaireValue() == 
			_suitCards[moveIndex].Size() + 1 )
	{
		if(!_suitCards[moveIndex].Size() == 0)
		{
			if(fromCard->parent != NULL)
				fromCard->parent->child = NULL;
			fromCard->parent = &_suitCards[moveIndex].top();
			_suitCards[moveIndex].top().child = fromCard;
			fromCard->child = NULL;

		}
		if(from != 7){
			TableCard::MoveBetween(_tableCards[from],_suitCards[moveIndex]);
			if(!_tableCards[from].empty())
			{
				if(!_tableCards[from].top().GetIsFaceUp())
					_tableCards[from].top().Flip();
			}
		}
		else
		{
			_suitCards[moveIndex].Push(_deck.top());
			_deck.Pop();
		}
	}
			
	
	return false;	
}

void Solitaire::MakeSuitMove(int from)
{
	/* validSuit move also makes move */
	ValidSuitMove(from)	;
}



void Solitaire::MakeMoveBetweenRows(int from, int to)
{
	if(ValidMove(from,to))
	{
		if(!_tableCards[to].empty())
			_tableCards[to].top().child = &_tableCards[from].top();
		
		if(_tableCards[from].top().GetCardRank() != 'K')
			_tableCards[from].top().parent = &_tableCards[to].top();

		TableCard::MoveBetween(_tableCards[from], _tableCards[to]);
				
		if(!_tableCards[from].empty())
			if(!_tableCards[from].top().GetIsFaceUp())
				_tableCards[from].top().Flip();
	}
}

void Solitaire::MakeMoveDeckToRow(int to)
{
	if(ValidMove(7 ,to))
	{
		if(!_tableCards[to].empty())
			_tableCards[to].top().child = &_deck.top();	
		if(_deck.top().GetCardRank() != 'K')
			_deck.top().parent = & _tableCards[to].top();
		
		_tableCards[to].Push(_deck.top());
		_deck.Pop();
			

	}
}

void Solitaire::MakeMoveRowToRow(int from, int to)
{
	if(from == 7)
	{	
		MakeMoveDeckToRow(to);
		return;
	}
	else if(_tableCards[from].Size() == 0)
		return;

	Card* fromCard;
	fromCard = &_tableCards[from].top();

	if(fromCard->parent == NULL)
		MakeMoveBetweenRows(from,to);
	else
	{
		int pos = _tableCards[from].Size()-1;
		bool checkParent = true;
		bool found = false;
		while(checkParent && !found)
		{					
			if(ValidRowToRowMove(fromCard,to))
				found = true;
			else if(fromCard->parent == NULL)
				checkParent = false;
			else
			{
				fromCard = fromCard->parent;
				pos--;
			}
			
		}

		if(found)
		{
			bool hasChildren = true;
			if(fromCard->parent != NULL)
					fromCard->parent->child = NULL;
			
			while(hasChildren)
			{	
				
				if(!_tableCards[to].empty())
				{
					fromCard->parent = &_tableCards[to].top();
					_tableCards[to].top().child = fromCard;
				}
				_tableCards[to].Push(*fromCard);

				_tableCards[from].RemoveAt(pos);
				fromCard = fromCard->child;
		
									
				pos++;
				if(fromCard == NULL)
					hasChildren = false;
			}

		}

		if(!_tableCards[from].empty())
			if(!_tableCards[from].top().GetIsFaceUp())
				_tableCards[from].top().Flip();
	}
}

bool Solitaire::ValidRowToRowMove(Card* fromCard, int to)
{
	Card* toCard;	

	if(!_tableCards[to].empty())
		toCard = &_tableCards[to].top();

	//move king to empty space
	if(_tableCards[to].empty())
	{
		if(fromCard->GetCardRank() == 'K')
			return true;
		else
			return false;
	}
	
	else if(fromCard->GetSolitaireValue() == toCard->GetSolitaireValue()-1 )
	{
		int toColor = 0, fromColor = 0;
		if(toCard->GetCardSuit() == 'h' || toCard->GetCardSuit() == 'd')
			toColor = 0;
		else
			toColor = 1;
		if(fromCard->GetCardSuit() =='h' || fromCard->GetCardSuit() == 'd')
			fromColor = 0;
		else
			fromColor = 1;

		if(fromColor == toColor)
			return false;
		else
			return true;
	}	
	return false;

}

bool Solitaire::GameCompleted()
{
	for(int i=0; i<_suitCards.size(); i++)
		if(_suitCards[i].Size() < 13)
			return false;
	return true;
}
/*==============================================*/
_Nestor 12 Light Poster

One line of needs to be changed. In the print deck function of Deck.cpp the .GetBlackjack function should be removed as the function no longer exists

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.