tags:

views:

181

answers:

2

Working through more book examples- this one is a partial poker program- This segment deals with straight hand....

First what was given- only relevant parts....will provide entire code if needed...

int suits[5]; //index 1..4- value start at 1
int values[14]; //index 1..13- value same as rank, A = 1, K = 13

cin.get(rankCh);
switch (toUpper(rankCh)) {
case 'A': values = 1; break;
case '2': values = 2; break;
case '3': values = 3; break;
case '4': values = 4; break;
case '5': values = 5; break;
case '6': values = 6; break;
case '7': values = 7; break;
case '8': values = 8; break;
case '9': values = 9; break;
case 'T': values = 10; break;
case 'J': values = 11; break;
case 'Q': values = 12; break;
case 'K': values = 13; break;
default:
badCard = true;
}

Other functions:

bool isFlush(int suits[]) {
    for(i = 1; i <= 4; i++)
     if (suits[i] == 5)       //5 here is Number of Cards 
      return true;
    return false;
}

Yeah, I know about the array declarations but that is how it is defined- nice justification for it in the text...starting to number at 1 I want my straight hand to handle both Ace high and low- right now as define above aces are low...

Two versions: 1st appears not sure correct with low aces...

CODE

bool isStraight(int values[]) //Version one only straight- low aces only
{
    int count = 0;

    for (i = 1; i <= 13; i++) {
     if (values[i] != 1) {
      count++;
     } else
      count = 0;

     if (count == 5) //5 is NUMCARDS
      return true;
    }
    return false;
}

Now this is the where I need some recommendation: to have a function to handle both ace high and low:

bool isStraight(int values[]) //Version handles both high and low
{
    int count = 0;

    for (i = 1; i <= 13; i++) {
     if (values[i] != 1) {
      count++;
      // if(i == 1 && values[1] != 0) //Check for high and low
      // count++;
     } else
      count = 0;

     if (count == 5) //5 is NUMCARDS
      return true;
    }
    return false;
}

Would what I have in comments work to handle both ace high and low...

Since i = 1 is represented as ace and not sure what values[1] is correct should it be values[13] or what...maybe something like

if (i == 1)
   values[13] //not sure...

Recommendations-

  • do not want wholesale changes- just to have minor changes with what I have...I do not want to sort or solve by brute force i.e like values[1] == 1 && values [2] ==1 you get the point- the text does that already but I am trying to rewrite it this way...

Thanks...Hope I am getting across my modification I would like...

+2  A: 

EDIT: I figured I'd would first answer your question directly. Lets first clear up how the original algorithm worked. Basically it loops from 1 to 13, and each time it sees a card in that slot, it adds to count. If anything ever breaks the sequence, it resets the counter. Finally, if the counter reaches 5, you have a straight.

I can't say off hand if your solution would work, I say give it a go. However, a simple quick patch to the original would probably go something like this:

//Version handles both high and low
bool isStraight(int values[]) {
    int count = 0;

    for (i = 1; i <= 13; i++) {
        if (values[i] != 1) {
                count++;
        } else
                count = 0;

        if (count == 5) //5 is NUMCARDS
                return true;
    }

        // handle ace high.
        if(count == 4 && values[1] != 0) {
            return true;
        }

    return false;
}

Basically what that does is say "if we already have 4 in a row, and we've just looked at the very last card (the loop is over), then check an ace is there, if so, we do have a straight and it is ace high".

ORIGINAL ANSWER: I think the easiest way to handle ace high and low is to have the "get rank" function have two modes, one which returns ace high, the other which returns ace low. Then just calculate the hand value for each case and take the better one.

Also, your get rank could be way simpler :-P.

int get_rank(char card) {
    static const char *cards = "A23456789TJQK";
    char *p = strchr(cards, toupper(card));
    if(p) {
        return (p - cards) + 1; 
    } else {
        return -1;
    }
}

so if you want to have a get_rank which has an ace_high or an ace_low, you could do this:

int get_rank(char card, bool ace_high) {
    static const char *cards_high = "23456789TJQKA";
    static const char *cards_low = "A23456789TJQK";

    const char *cards = ace_high ? cards_high : cards_low;

    char *p = strchr(cards, toupper(card));
    if(p) {
        return (p - cards) + 1; 
    } else {
        return -1;
    }
}

EDIT:

for fun, i've made a quick and dirty program which detects straights (handling both high and low ace). It is fairly simple, but could be shorter (also note that there is no attempt at buffer safety with these arrays, something of production quality should use something safer such as std::vector:

#include <algorithm>
#include <iostream>
#include <cstring>

int get_rank(char card, bool ace_high) {
    static const char *cards_high = "23456789TJQKA";
    static const char *cards_low = "A23456789TJQK";
    const char *cards = ace_high ? cards_high : cards_low;

    char *p = strchr(cards, toupper(card));
    if(p) {
        return (p - cards) + 1; 
    } else {
        return -1;
    }
}

bool is_rank_less_low(int card1, int card2) {
    return get_rank(card1, false) < get_rank(card2, false);
}

bool is_rank_less_high(int card1, int card2) {
    return get_rank(card1, true) < get_rank(card2, true);
}

bool is_straight(int hand[], bool ace_high) {

    std::sort(hand, hand + 5, ace_high ? is_rank_less_high : is_rank_less_low);
    int rank = get_rank(hand[0], ace_high);
    for(int i = 1; i < 5; ++i) {
     int new_rank = get_rank(hand[i], ace_high);
     if(new_rank != rank + 1) {
      return false;
     }
     rank = new_rank;
    }
    return true;
}

bool is_straight(int hand[]) {
    return is_straight(hand, false) || is_straight(hand, true);
}

int main() {
    int hand1[5] = { 'T', 'J', 'Q', 'K', 'A' };
    int hand2[5] = { 'A', '2', '3', '4', '5' };
    std::cout << is_straight(hand1) << std::endl;
    std::cout << is_straight(hand2) << std::endl;
}
Evan Teran
The value of count should be checked only when it is incremented, which is not every time through the loop.
Mark Ruzon
While you are that's a pretty minor optimization. The check is where it is, because that's where it was in the original code.
Evan Teran
I take this answer and see where it goes...
iwanttoprogram
+1  A: 

The case where an ace-high straight exists can be found by changing the final test:

if (count == 5 || count == 4 && values[1] == 1) // 2nd case handles ace-high straight
    return true;

It's a special case, so it must be handled separately.

Mark Ruzon
close, but not quite correct. That will return true if they have something like this: A,3,4,5,6 (note 4 in a row with ace). If you look at my most recent answer, it has a variation on this which does work (basically it does that same check *after* the loop is complete since if the loop just ended and we have a count of 4, then that means we have T,J,Q,K).
Evan Teran
I disagree. When i == 7 count will revert to 0, so it will only be 4 in the case of [~9],T,J,Q,K
Mark Ruzon
Oh, darn, I didn't see that it was inside the for loop.
Mark Ruzon
Thanks for the feedback here...
iwanttoprogram