/* * game.cpp * * */ #include "game.h" #include /* forward declaration of hands array, very interesting, I didn't know C++ needs this */ string *Game::hands[10]; string *Game::suits[5]; string *Game::winmsg[4]; Game::Game(void) : handsPlayed(0), handsWon(0) { hands[0] = new string("Royal Flush"); hands[1] = new string("Straight Flush"); hands[2] = new string("Four of a Kind"); hands[3] = new string("Full House"); hands[4] = new string("Flush"); hands[5] = new string("Straight"); hands[6] = new string("Three of a Kind"); hands[7] = new string("Two Pair"); hands[8] = new string("One Pair"); hands[9] = new string("High Card"); suits[0] = new string("Undefined"); suits[1] = new string("Clubs"); suits[2] = new string("Diamonds"); suits[3] = new string("Hearts"); suits[4] = new string("Spades"); winmsg[0] = new string("User is the winner!"); winmsg[1] = new string("Opponent 1 is the winner!"); winmsg[2] = new string("Opponent 2 is the winner!"); winmsg[3] = new string("Opponent 3 is the winner!"); } void Game::shufflePile(void) { /* interesting algorithm that was discussed in the class */ Card *tmp; int i, j; for (i = 0; i < 52; i++) { j = rand() % 52; tmp = getCardFromDeck(i); putCardToDeck(i, getCardFromDeck(j)); putCardToDeck(j, tmp); } } int Game::parseInt(string *msg, int rangeA, int rangeB) { int tmp; bool continueAsking = true; while (continueAsking) { if (msg) cout << *msg; if (!(cin >> tmp)) { cin.clear(); cin.ignore(std::numeric_limits::max(), '\n'); PRINT_COLOR(ANSI_COLOR_RED, "Invalid input!\n"); continue; } if ((tmp >= rangeA) && (tmp <= rangeB)) continueAsking = false; else PRINT_COLOR(ANSI_COLOR_YELLOW, "Number not in the range!\n"); } return tmp; } void Game::askForNumberOfOpponents(void) { bool continueAsking = true; while (continueAsking) { cout << "Enter the amount of opponents between 1 to 3: "; if (!(cin >> numOfOpponents)) { cin.clear(); cin.ignore(std::numeric_limits::max(), '\n'); PRINT_COLOR(ANSI_COLOR_RED, "Invalid character(s)!\n"); continue; } if ((numOfOpponents >= 1) && (numOfOpponents <= 3)) continueAsking = false; else PRINT_COLOR(ANSI_COLOR_YELLOW, "Number not in the range!\n"); } } void Game::repeatGame(bool *quit) { bool continueAsking = true; char play_again; cout << endl; while (continueAsking) { PRINT_COLOR(ANSI_COLOR_GREEN, "Play again? (y/n) "); cin >> play_again; play_again = tolower(play_again); if (play_again == 'y') continueAsking = false; else if (play_again == 'n') { continueAsking = false; *quit = true; } } } int Game::howManyCardsOfSameSuit(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nhowManyCardsOfSameSuit(): cards parameter is NULL!\n"); return -1; } char c, l; l = cards[0]->getType()[1]; int i; for (i = 1; i < 5; i++) { c = cards[i]->getType()[1]; if (c != l) break; } return i; } bool Game::pairOrBetter(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\npairOrBetter(): cards parameter is NULL!\n"); return false; } char c1, c2; int ret = 0; int i, j; for (i = 0; i < 5; i++) { c1 = cards[i]->getType()[0]; for (j = i+1; j < 5; j++) { c2 = cards[j]->getType()[0]; if (c1 == c2) { ret++; break; } } } return (ret != 0); } /* returns max number of same cards, returns 0 when all cards are different */ int Game::numOfSameCards(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nnumOfSameCards(): cards parameter is NULL!\n"); return -1; } char c1, c2; int same = 0; int max = 0; int i, j; for (i = 0; i < 5; i++) { c1 = cards[i]->getType()[0]; for (j = i+1; j < 5; j++) { c2 = cards[j]->getType()[0]; same = 1; if (c1 == c2) { same++; if (same > max) max = same; } else { same = 0; c1 = c2; } } } return max; } /* returns the max number of the same suit along with the suit itself */ struct countAndSuit Game::numOfSameSuit(Card **cards) { struct countAndSuit same[5] = { { Clubs, 0}, { Diamond, 0 }, { Hearts, 0 }, { Spades, 0 }, { Undef, -1 }, }; if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nnumOfSameSuit(): cards parameter is NULL!\n"); return same[4]; } int max = 0; enum Suit tmpSuit = Undef; /* go through all cards and count how many there is of each suit */ int i; for (i = 0; i < 5; i++) { tmpSuit = cards[i]->getSuit(); switch (tmpSuit) { case Clubs: same[0].count += 1; break; case Diamond: same[1].count += 1; break; case Hearts: same[2].count += 1; break; case Spades: same[3].count += 1; break; default: std::cerr << "could not determine the suit of the card" << endl; } } int whereMaxWasFound = 0; /* look for the max number of cards of the same suit */ for (i = 0; i < 4; i++) if (same[i].count > max) { max = same[i].count; whereMaxWasFound = i; } return same[whereMaxWasFound]; } /* returns the max count of a particular card in a hand */ struct countAndType Game::numOfSameType(Card **cards) { struct countAndType same[14] = { { 'A', 0 }, { 'K', 0 }, { 'Q', 0 }, { 'J', 0 }, { 'T', 0 }, { '9', 0 }, { '8', 0 }, { '7', 0 }, { '6', 0 }, { '5', 0 }, { '4', 0 }, { '3', 0 }, { '2', 0 }, { '0',-1 } /* undefined */ }; if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nnumOfSameTypes(): cards parameter is NULL!\n"); return same[13]; } int max = 0; char type = '0'; /* go through all cards and count how many there is of each suit */ int i; for (i = 0; i < 5; i++) { type = cards[i]->getType()[0]; switch (type) { case 'A': same[0].count += 1; break; case 'K': same[1].count += 1; break; case 'Q': same[2].count += 1; break; case 'J': same[3].count += 1; break; case 'T': same[4].count += 1; break; case '9': same[5].count += 1; break; case '8': same[6].count += 1; break; case '7': same[7].count += 1; break; case '6': same[8].count += 1; break; case '5': same[9].count += 1; break; case '4': same[10].count += 1; break; case '3': same[11].count += 1; break; case '2': same[12].count += 1; break; default: std::cerr << "could not determine the type of the card" << endl; break; } } int whereMaxWasFound = 0; /* look for the max number of cards of the same type */ for (i = 0; i < 13; i++) if (same[i].count > max) { max = same[i].count; whereMaxWasFound = i; } #ifdef DEBUG cout << ANSI_COLOR_CYAN << "\ndebug: \"" << same[whereMaxWasFound].type << "\" has highest occurence, occurs " << same[whereMaxWasFound].count << " time(s)" << ANSI_COLOR_RESET; #endif return same[whereMaxWasFound]; } bool Game::doWeHaveAnAce(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\ndoWeHaveAnAce(): cards parameter is NULL!\n"); return false; } char c; int i; for (i = 0; i < 5; i++) { c = cards[i]->getType()[0]; if (c == 'A') return true; } return false; } /* returns array which contains number of pairs, three of a kind, * or four of a kind, index that contains 1 should be discarded * since it's a unique card */ int *Game::whichCardsToDiscard(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nwhichCardsToDiscard(): cards parameter is NULL!\n"); return NULL; } int *howManyOfSameCards = new int[5](); howManyOfSameCards[0] = 1; howManyOfSameCards[1] = 1; howManyOfSameCards[2] = 1; howManyOfSameCards[3] = 1; howManyOfSameCards[4] = 1; char c1, c2; /* goal is to look later at indices that contain a 1 */ int i, j; for (i = 0; i < 5; i++) { c1 = cards[i]->getType()[0]; for (j = 0; j < 5; j++) { /* don't count itself */ if (j == i) continue; c2 = cards[j]->getType()[0]; if (c1 == c2) howManyOfSameCards[i] += 1; } } return howManyOfSameCards; } /* helper function for opponentAI */ void Game::oppDiscardFromPairOrBetter(Card **cards, int whichOpponent) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\ndiscardFromPairOrBetter(): cards parameter is NULL!\n"); return; } int numOfDiscarded = 0; int *howManyOfSameCards = whichCardsToDiscard(cards); int i; /* indices that contain a 1 should be discarded */ for (i = 0; i < 5; i++) { if (howManyOfSameCards[i] == 1) { discardAndDraw(cards, 1, i); numOfDiscarded++; } } cout << "Opponent " << whichOpponent << " has discarded " << numOfDiscarded << " card(s)" << endl; } /* make sure cards are in decreasing rank */ bool Game::sanityCheck(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nsanityCheck(): cards parameter is NULL!\n"); return false; } int back1, back2; back1 = cards[0]->getRank(); int i; for (i = 1; i < 5; i++) { back2 = cards[i]->getRank(); if (back2 >= back1) /* this could mean that cards are not sorted or * there a pair, which would be really bad since * opponentAI should catch that */ return false; back1 = back2; } #ifdef DEBUG PRINT_COLOR(ANSI_COLOR_CYAN, "\ndebug: sanity check passed, must be a High Hand"); #endif return true; } /*============================================================================* * Discard and Draw * *============================================================================*/ void Game::discardAndDraw(Card **cards, int amount, int offset) { if (offset + amount > 5) { PRINT_COLOR(ANSI_COLOR_RED, "amount of cards to be discarded is out of bounds!\n"); return; } else if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\ndiscardAndDraw(): cards parameter is NULL!\n"); return; } int i; /* discard cards starting from offset */ for (i = 0; i < amount; i++) { #ifdef DEBUG cout << ANSI_COLOR_CYAN << "debug: discarding card " << i+1 << " \"" << cards[offset+i]->getType() << "\" at cards[" << offset+i << "]" << endl << ANSI_COLOR_RESET; #endif putCardToDiscardPile(cards[offset+i]); } /* draw cards from the deck into starting at cards[offset] */ for (i = 0; i < amount; i++) { cards[offset+i] = PopCardFromDeck(); #ifdef DEBUG cout << ANSI_COLOR_CYAN << "debug: drawing card " << i+1 << " \"" << cards[offset+i]->getType() << "\" into cards[" << offset+i << "]" << endl << ANSI_COLOR_RESET; #endif } } /*============================================================================* * Computer AI * *============================================================================*/ void Game::opponentAI(int whichOpponent) { Card **cards = getOpponentsCards(whichOpponent); if (cards == NULL) return; struct countAndSuit tmp = numOfSameSuit(cards); if (pairOrBetter(cards)) { #ifdef DEBUG cout << ANSI_COLOR_CYAN << "\ndebug: opponent " << whichOpponent << ", there's a pair or better" << endl << ANSI_COLOR_RESET; #endif /* discard some cards*/ oppDiscardFromPairOrBetter(cards, whichOpponent); return; } else if ( (tmp = numOfSameSuit(cards)).count == 5) { #ifdef DEBUG cout << ANSI_COLOR_CYAN << "\ndebug: opponent " << whichOpponent << ", 5 cards are of the same suit!" << endl << ANSI_COLOR_RESET; #endif /* keep 5 cards of the same suit */ cout << "Opponent " << whichOpponent << " kept all 5 of the cards" << endl; return; } else if (tmp.count == 4) { #ifdef DEBUG cout << ANSI_COLOR_CYAN << "\ndebug: opponent " << whichOpponent << ", 4 cards are of the same suit" << endl << ANSI_COLOR_RESET; #endif /* keep 4 cards of the same suit and discard 1 of different suit */ /* we need to loop through cards[] and find a card with different suit */ int i; for (i = 0; i < 5; i++) { if (cards[i]->getSuit() != tmp.whatSuit) break; } /* no i should be the index of card that we want to discard */ discardAndDraw(cards, 1, i); cout << "Opponent " << whichOpponent << " has discarded 1 card" << endl; return; } else if (doWeHaveAnAce(cards)) { #ifdef DEBUG cout << ANSI_COLOR_CYAN << "\ndebug: opponent " << whichOpponent << ", High Hand with Ace detected" << endl << ANSI_COLOR_RESET; #endif /* discard 4 cards */ discardAndDraw(cards, 4, 1); cout << "Opponent " << whichOpponent << " has discarded 4 cards" << endl; return; } else if (sanityCheck(cards)) { #ifdef DEBUG cout << ANSI_COLOR_CYAN << "\ndebug: opponent " << whichOpponent << ", High Hand detected" << endl << ANSI_COLOR_RESET; #endif /* discard 3 cards */ discardAndDraw(cards, 3, 2); cout << "Opponent " << whichOpponent << " has discarded 3 cards" << endl; return; } #ifdef DEBUG else cout << ANSI_COLOR_RED << "\ndebug: opponent " << whichOpponent << ", FATAL: cards did not match any of the criterias!" << endl << ANSI_COLOR_RESET; #endif } /*============================================================================* * User AI * *============================================================================*/ void Game::userDiscardCards(Card **cards, int howManyUserCanDiscard) { int whatCard = 0; string *msg = new string("List the card number you wish to discard (0 to skip): "); int i; for (i = 0; i < howManyUserCanDiscard; i++) { whatCard = parseInt(msg, 0, 5); if (whatCard == 0) break; discardAndDraw(cards, 1, whatCard-1); } } void Game::userAI(Card **cards) { struct countAndSuit tmp = numOfSameSuit(cards); int howManyUserCanDiscard = 0; int i; if (isInOrderByOne(cards)) { howManyUserCanDiscard = 3; cout << "You have a straight, you should definitely\n" << "keep all of the cards but you can discard " << howManyUserCanDiscard << " card(s)" << endl; userDiscardCards(cards, howManyUserCanDiscard); return; } if (pairOrBetter(cards)) { int *cardsToDiscard = whichCardsToDiscard(cards); howManyUserCanDiscard = 0; /* indices that contain a one should be discarded */ for (i = 0; i < 5; i++) { if (cardsToDiscard[i] == 1) howManyUserCanDiscard++; } cout << "You have a pair or better and can discard " << howManyUserCanDiscard << " card(s)" << endl; userDiscardCards(cards, 3); return; } else if ( (tmp = numOfSameSuit(cards)).count == 5) { howManyUserCanDiscard = 3; cout << "You have 5 cards of the same suit, you should\n" << "keep all of the cards but you can discard " << howManyUserCanDiscard << " card(s)" << endl; userDiscardCards(cards, howManyUserCanDiscard); return; } else if (tmp.count == 4) { howManyUserCanDiscard = 3; cout << "You have 4 cards of the same suit, you should\n" << "discard card of the different suit" << endl; userDiscardCards(cards, howManyUserCanDiscard); return; } else if (doWeHaveAnAce(cards)) { /* discard 4 cards */ howManyUserCanDiscard = 4; cout << "Since you have an Ace you can keep the Ace\n" << "and discard 4 other cards" << endl; userDiscardCards(cards, howManyUserCanDiscard); return; } else { /* discard 3 cards */ howManyUserCanDiscard = 3; cout << "You have a High Hand, two highest cards should\n" << "be kept and you should discard 3 other cards" << endl; userDiscardCards(cards, howManyUserCanDiscard); return; } } bool Game::isInOrderByOne(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nisInOrderByOne(): cards parameter is NULL!\n"); return false; } int back1, back2; back1 = cards[0]->getRank(); int i; for (i = 1; i < 5; i++) { back2 = cards[i]->getRank(); if ((back2 + 1) != back1) return false; back1 = back2; } return true; } bool Game::isInOrder(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nisInOrder(): cards parameter is NULL!\n"); return false; } int back1, back2; back1 = cards[0]->getRank(); int i; for (i = 1; i < 5; i++) { back2 = cards[i]->getRank(); if (!(back2 < back1)) return false; back1 = back2; } return true; } bool Game::hasFullHouse(Card **cards, struct countAndType *threeCount) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nhasFullHouse(): cards parameter is NULL!\n"); return false; } int i; for (i = 0; i < 5; i++) if (cards[i]->getType()[0] != threeCount->type) break; int j; for (j = 0; j < 5; j++) if ((cards[j]->getType()[0] != threeCount->type) && i != j) break; bool ret = cards[i]->getType()[0] == cards[j]->getType()[0]; #ifdef DEBUG cout << ANSI_COLOR_CYAN << "\ndebug: hasFullHouse(): does " << cards[i]->getType() << " have the same value as " << cards[j]->getType() << "? " << ret << endl << ANSI_COLOR_RESET; #endif return ret; } bool Game::hasTwoPairs(Card **cards) { if (cards == NULL) { PRINT_COLOR(ANSI_COLOR_RED, "\nhasTwoPairs(): cards parameter is NULL!\n"); return false; } int *cardsToDiscard = whichCardsToDiscard(cards); int cnt = 0; int i; /* if there are 2 pairs, they should be one index that contains 1 */ for (i = 0; i < 5; i++) if (cardsToDiscard[i] == 1) cnt++; return (cnt == 1); } /* second param, 0 = user, 1~3 designate opponent */ void Game::evaluateHand(Card **cards, int who) { int whatHand = 9; bool haveAce = doWeHaveAnAce(cards); bool inOrderByOne = isInOrderByOne(cards); struct countAndSuit tmp = numOfSameSuit(cards); struct countAndType tmp2 = numOfSameType(cards); /* royal flush */ if (haveAce && inOrderByOne && (tmp.count == 5)) whatHand = 0; /* straight flush */ else if (!haveAce && inOrderByOne && (tmp.count == 5)) whatHand = 1; /* four of a kind */ else if (tmp2.count == 4) whatHand = 2; /* full house */ else if ((tmp2.count == 3) && hasFullHouse(cards, &tmp2)) whatHand = 3; /* flush */ else if (tmp.count == 5) whatHand = 4; /* straight */ else if (inOrderByOne) whatHand = 5; /* three of a kind */ else if (tmp2.count == 3) whatHand = 6; /* two pair */ else if (hasTwoPairs(cards)) whatHand = 7; /* one pair */ else if (tmp2.count == 2) whatHand = 8; else whatHand = 9; switch (who) { case 0: setUserHand(whatHand); break; case 1: setOpponentHand(whatHand, 1); break; case 2: setOpponentHand(whatHand, 2); break; case 3: setOpponentHand(whatHand, 3); break; default: std::cerr << "something went wrong in evaluation" << endl; } } void Game::showHands(void) { cout << "\n User has: " << *hands[getUserHand()] << " " << endl; int i; for (i = 0; i < numOfOpponents; i++) cout << "Opponent " << i+1 << " has: " << *hands[getOpponentHand(i+1)] << " " << endl; } void Game::determineWinner(void) { /* check for 'whatHand' variable from User and Opponent class */ int numOfPlayers = 1 + numOfOpponents; int *playerHands = new int[numOfPlayers](); int i; for (i = 0; i < numOfPlayers; i++) playerHands[i] = 0; playerHands[0] = getUserHand(); for (i = 1; i < numOfOpponents + 1; i++) playerHands[i] = getOpponentHand(i); bool *ties = new bool[numOfPlayers](); for (i = 0; i < numOfPlayers; i++) ties[i] = false; /* look for lowest hand */ int min = 9; int whatIndex = -1; for (i = 0; i < numOfPlayers; i++) if (playerHands[i] <= min) { min = playerHands[i]; whatIndex = i; } /* count number of ties */ int numOfTies = 0; for (i = 0; i < numOfPlayers; i++) if (playerHands[i] == min) { ties[i] = true; numOfTies++; } #ifdef DEBUG if (numOfTies > 1) cout << ANSI_COLOR_CYAN << "\ndebug: there's a tie between " << numOfTies << " players, same hands detected" << endl << ANSI_COLOR_RESET; #endif int maxFoundAt = -1; if (numOfTies > 1) { int tmpRank = 0; int maxRank = 0; int numOfAssignmentsMade = 0; Card **cards = NULL; int j; /* for all cards */ for (i = 0; i < 5; i++) { if (numOfAssignmentsMade > 1) break; numOfAssignmentsMade = 0; /* for all players */ for (j = 0; j < numOfPlayers; j++) { if (ties[j] == false) continue; if (j == 0) cards = getUserCards(); else cards = getOpponentsCards(j); tmpRank = cards[i]->getRank(); if (tmpRank > maxRank) { maxRank = tmpRank; maxFoundAt = j; numOfAssignmentsMade++; } } } } if (numOfTies > 1) whatIndex = maxFoundAt; cout << ANSI_COLOR_CYAN << endl << *winmsg[whatIndex] << endl << ANSI_COLOR_RESET; if (whatIndex == 0) handsWon++; handsPlayed++; }