I wanted to write a simple implementation for a high card game. Basically two cards are drawn and the higher one wins. Additional rules: support for multiple decks, if same suits drawn then the higher colour wins, if same colour and suit (possible as there's multiple decks) then resolve a tie by dealing a further card to each player until a max number of tries reached. Also there exists a wild card that beats all others.
It's trivial but really I'd like to get some feedback on how it's tested. I tried to come up with a way to test the logic that depends on what rand() returns, not sure if that's the correct approach, so any feedback appreciated.
#include <stdlib.h>
#include <iostream>
#include <sstream>
#define NUM_OF_CARDS 52
#define NUM_OF_RETRIES 2
class HighCard
{
public:
HighCard() = default;
~HighCard() = default;
bool Play( int (*f) (void) = rand, std::ostream& os = std::cout );
};
bool HighCard::Play( int (*f) (void), std::ostream& os )
{
for (int k = 0; k < NUM_OF_RETRIES; k++)
{
int rank1 = -1, rank2 = -1, suit1 = -1, suit2 = -1;
rank1 = f() % NUM_OF_CARDS + 1;
rank2 = f() % NUM_OF_CARDS + 1;
suit1 = f() % 4 + 1;
suit2 = f() % 4 + 1;
os << "rank1 is " << rank1 << " rank2 is " << rank2 << "\n";
os << "suit1 is " << suit1 << " suit2 is " << suit2 << "\n";
if (rank1 == rank2 && suit1 == suit2)
{
os << "a tie so need to draw again" << "\n";
continue;
}
if (rank1 == 2 && suit1 == 2)
{
os << "a wild card drawn " << "\n";
return false;
}
else if (rank2 == 2 && suit2 == 2)
{
os << "a wild card drawn " << "\n";
return true;
}
if (rank1 != rank2)
return ( rank1 < rank2 );
else
return ( suit1 < suit2 );
}
os << "a tie repeated too many times..." << "\n";
return true;
}
// function used for testing instead of rand()
// ints passed in instantiation will be returned in subsequent calls
template<int n1, int n2, int n3, int n4,
int n5 = -1, int n6 = -1, int n7 = -1, int n8 = -1>
int drawNums()
{
static int i = -1;
i++;
if (i % 8 == 0)
return n1;
else if (i % 8 == 1)
return n2;
else if (i % 8 == 2)
return n3;
else if (i % 8 == 3)
return n4;
else if (i % 8 == 4)
return n5;
else if (i % 8 == 5)
return n6;
else if (i % 8 == 6)
return n7;
else
return n8;
}
void test_win ()
{
HighCard card;
std::ostringstream oss;
oss.str("");
// rank1 > rank2, suit1 < suit2
assert(card.Play( drawNums<22,3,0,1>, oss ) == false);
assert(oss && oss.str() == "rank1 is 23 rank2 is 4\n"
"suit1 is 1 suit2 is 2\n");
oss.str("");
// rank1 > rank2, suit1 > suit2
assert(card.Play( drawNums<22,3,1,0>, oss ) == false);
assert(oss && oss.str() == "rank1 is 23 rank2 is 4\n"
"suit1 is 2 suit2 is 1\n");
oss.str("");
// rank1 > rank2, suit1 = suit2
assert(card.Play( drawNums<22,3,0,0>, oss ) == false);
assert(oss && oss.str() == "rank1 is 23 rank2 is 4\n"
"suit1 is 1 suit2 is 1\n");
oss.str("");
// rank1 < rank2, suit1 < suit2
assert(card.Play( drawNums<3,22,0,1>, oss ) == true);
assert(oss && oss.str() == "rank1 is 4 rank2 is 23\n"
"suit1 is 1 suit2 is 2\n");
oss.str("");
// rank1 < rank2, suit1 > suit2
assert(card.Play( drawNums<3,22,1,0>, oss ) == true);
assert(oss && oss.str() == "rank1 is 4 rank2 is 23\n"
"suit1 is 2 suit2 is 1\n");
oss.str("");
// rank1 < rank2, suit1 = suit2
assert(card.Play( drawNums<3,22,0,0>, oss ) == true);
assert(oss && oss.str() == "rank1 is 4 rank2 is 23\n"
"suit1 is 1 suit2 is 1\n");
oss.str("");
// rank1 = rank2, suit1 < suit2
assert(card.Play( drawNums<22,22,0,1>, oss ) == true);
assert(oss && oss.str() == "rank1 is 23 rank2 is 23\n"
"suit1 is 1 suit2 is 2\n");
oss.str("");
// rank1 = rank2, suit1 > suit2
assert(card.Play( drawNums<22,22,1,0>, oss ) == false);
assert(oss && oss.str() == "rank1 is 23 rank2 is 23\n"
"suit1 is 2 suit2 is 1\n");
}
void test_wildcard()
{
HighCard card;
std::ostringstream oss;
oss.str("");
// rank1 < rank2, suit1 < suit2
assert(card.Play( drawNums<1,3,1,3>, oss ) == false);
assert(oss && oss.str() == "rank1 is 2 rank2 is 4\n"
"suit1 is 2 suit2 is 4\n"
"a wild card drawn \n");
oss.str("");
// rank1 > rank2, suit1 > suit2
assert(card.Play( drawNums<1,0,1,0>, oss ) == false);
assert(oss && oss.str() == "rank1 is 2 rank2 is 1\n"
"suit1 is 2 suit2 is 1\n"
"a wild card drawn \n");
oss.str("");
// rank1 > rank2, suit1 > suit2
assert(card.Play( drawNums<3,1,3,1>, oss ) == true);
assert(oss && oss.str() == "rank1 is 4 rank2 is 2\n"
"suit1 is 4 suit2 is 2\n"
"a wild card drawn \n");
oss.str("");
// rank1 < rank2, suit1 < suit2
assert(card.Play( drawNums<0,1,0,1>, oss ) == true);
assert(oss && oss.str() == "rank1 is 1 rank2 is 2\n"
"suit1 is 1 suit2 is 2\n"
"a wild card drawn \n");
}
void test_retries_limit()
{
HighCard card;
std::ostringstream oss;
oss.str("");
assert(card.Play( drawNums<2,2,2,2, 2,2,2,2>, oss ) == true);
assert(oss && oss.str() == "rank1 is 3 rank2 is 3\n"
"suit1 is 3 suit2 is 3\n"
"a tie so need to draw again\n"
"rank1 is 3 rank2 is 3\n"
"suit1 is 3 suit2 is 3\n"
"a tie so need to draw again\n"
"a tie repeated too many times...\n");
// wild card
oss.str("");
assert(card.Play( drawNums<1,1,1,1, 1,1,1,1>, oss ) == true);
assert(oss && oss.str() == "rank1 is 2 rank2 is 2\n"
"suit1 is 2 suit2 is 2\n"
"a tie so need to draw again\n"
"rank1 is 2 rank2 is 2\n"
"suit1 is 2 suit2 is 2\n"
"a tie so need to draw again\n"
"a tie repeated too many times...\n");
}
void test_retries()
{
HighCard card;
std::ostringstream oss;
oss.str("");
assert(card.Play( drawNums<2,2,2,2 ,23,2,2,2>, oss ) == false);
assert(oss && oss.str() == "rank1 is 3 rank2 is 3\n"
"suit1 is 3 suit2 is 3\n"
"a tie so need to draw again\n"
"rank1 is 24 rank2 is 3\n"
"suit1 is 3 suit2 is 3\n");
// wild card
oss.str("");
assert(card.Play( drawNums<1,1,1,1, 1,2,1,3>, oss ) == false);
assert(oss && oss.str() == "rank1 is 2 rank2 is 2\n"
"suit1 is 2 suit2 is 2\n"
"a tie so need to draw again\n"
"rank1 is 2 rank2 is 3\n"
"suit1 is 2 suit2 is 4\n"
"a wild card drawn \n");
}
int main (int argc, char **argv)
{
srand (time(NULL));
test_win();
test_wildcard();
test_retries();
test_retries_limit();
return 0;
}