Search code examples
cdebuggingstructruntime-errorvisual-studio-debugging

How to debug Run-Time Check Failure #2?


When running the following code to calculate the probability of no pair, one pair, two pair, three of a kind, full house and four of a kind in a random seven-card poker hand, Visual Studio returns the warning "Run-Time Check Failure #2 - Stack around the variable 'hand_counts' was corrupted" on the last line. However, the program can still output the results by clicking "Continue". I have checked the program for out-of-bounds array accesses, but have not found any. What could be causing the error?

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef enum suit
{
    spades,
    hearts,
    diamonds,
    clubs
} suit;
typedef struct card
{
    suit suit;
    short pip;
} card;
void deck_shuffle(card deck[52])
{
    srand(time(NULL));
    for (int i = 0; i < 52; i++)
    {
        const int j = rand() % 52;
        const card temp = deck[i];
        deck[i] = deck[j];
        deck[j] = temp;
    }
}
int possibility(card hand[7])
{
    int pair_type = 0;
    int count = 0;
    for (int i = 0; i < 6; i++)
    {
        for (int j = i + 1; j < 7; j++)
        {
            if (hand[i].pip == hand[j].pip)
            {
                count++;
            }
        }
    }
    // check for no pairs.
    if (count == 0)
    {
        pair_type = 1;
    }
    // check for one pair.
    if (count == 1)
    {
        pair_type = 2;
    }
    // check for two pair.
    if (count == 2)
    {
        pair_type = 3;
    }
    // check for three of a kind.
    if (count == 3)
    {
        pair_type = 4;
    }
    // check for full house.
    int rank1 = 0;
    int rank2 = 0;
    int temp1 = hand[0].pip;
    int temp2;
    int count1 = 0;
    int count2 = 0;
    for (int i = 0; i < 6; i++)
    {
        for (int j = i + 1; j < 7; j++)
        {
            if (hand[i].pip == hand[j].pip)
            {
                rank1 = hand[j].pip;
                if (temp1 == rank1)
                {
                    count1++;
                    temp1 = rank1;
                }
                else
                {
                    temp2 = rank2;
                    rank2 = hand[j].pip;
                    if (temp2 == rank2)
                    {
                        count2++;
                    }
                }
            }
        }
    }
    count2 += 1;
    if ((count1 == 3 && count2 == 1) || (count1 == 3 && count2 == 3))
    {
        pair_type = 5;
    }
    // check for four of a kind.
    if (count1 == 6 || count2 == 6)
    {
        pair_type = 6;
    }
    return pair_type;
}
int main(void)
{
    card deck[52];
    for (int i = 0; i < 52; i++)
    {
        deck[i].suit = i / 13;
        deck[i].pip = i % 13;
    }
    int hand_counts[6] = { 0 };
    for (int i = 0; i < 1000000; i++)
    {
        deck_shuffle(deck);
        card hand[7];
        for (int j = 0; j < 7; j++)
        {
            hand[j] = deck[j];
        }
        int hand_type = possibility(hand);
        hand_counts[hand_type - 1]++;
    }
    for (int i = 0; i < 6; i++)
    {
        printf("The probability of hand type %d: is %f\n", i + 1, (double)hand_counts[i] / 1000000);
    }
    return 0;
}

I tried running the same code in other IDEs, and there were no warnings. The results were also calculated faster than in Visual Studio. I am not sure if there is an error in my source code, or if it is a problem with Visual Studio itself.


Solution

  • Sorry, but the multiple loops and variables toward the end of possibility() are somewhat difficult to follow. I stripped things down somewhat and put in a particular test case; 4 aces:

    int possibility( int hand[7]) {
        int pair_type = 0;
    
        /* ... */
    
        for (int k = 0; k < 7; k++) printf( "%d ", hand[k] );
    
        int rank1 = 0;
        int rank2 = 0;
        int temp1 = hand[0];
        int temp2;
        int count1 = 0;
        int count2 = 0;
        for (int i = 0; i < 6; i++) {
            for (int j = i + 1; j < 7; j++) {
                if (hand[i] == hand[j]) {
                    rank1 = hand[j];
                    if (temp1 == rank1) {
                        count1++;
                        temp1 = rank1;
                    } else {
                        temp2 = rank2;
                        rank2 = hand[j];
                        if (temp2 == rank2)
                            count2++;
                    }
                }
            }
        }
    
        count2 += 1;
        printf( "\nSuspicion: count1 = %d count2 = %d\n", count1, count2 );
        /* ... */
        return pair_type;
    }
    
    int main(void) {
        int hand[7] = { 12, 0, 0, 0, 0, 1, 2 };
    
        printf("hand type %d\n", possibility(hand) );
    
        return 0;
    }
    
    

    Result:

    12 0 0 0 0 1 2
    Suspicion: count1 = 0 count2 = 7   // <<<<
    hand type 0
    

    Then:

            int hand_type = possibility(hand);
            hand_counts[hand_type - 1]++;
    

    Will increment the location hand_counts[ 0 - 1 ]... Definitely a No-No.

    You need to go back to that nested for() loop and figure out how to avoid double counting. 4-of-a-kind can occur in any locations. There's too much double counting happening in your loops.

    Suggest you look into "histogram" tallying of the cards (int cnt[13] = {0}; would be a start. Then a single pass over the hand will give you the tallies you need.

    "or if it is a problem with Visual Studio itself." While that's always a possibility, it's a poor craftsman who starts by blaming his/her tools.


    Y'know... If you'd not tried to use an array dimensioned for precisely 6-and-only-6 "types", then gone on to subtract 1 to use 0-based indexing, you might have caught this yourself...

    int hand_counts[ 27 ] = { 0 }; // why not?
    /* ... */
        for (int i = 0; i < sizeof hand_counts/sizeof hand_counts[0]; i++)
        {
            printf("The probability of hand type %d: is %f\n", i, (double)hand_counts[i] / 1000000);
        }
    

    (Just noticed you "add 1" in your print statement! Too much "clever" overflows to become "dumb"...)


    Overtime:
    Intrigued, here's a stripped down version that may be suitable for elaborating into something bigger:

    void deck_shuffle( int deck[] ) {
        for( int i = 0; i < 52; i++ ) {
            const int j = rand() % 52;
            const int temp = deck[i];
            deck[i] = deck[j];
            deck[j] = temp;
        }
    }
    
    int cmp( const void *a, const void *b ) {
        return *(int*)b - *(int*)a; // descending sqnc
    }
    
    enum { nada, four, three, two, twoPair, fullhouse, nTallies };
    
    int possibility( int hand[] ) {
        int cnt[13] = { 0 };
    
        for( int i = 0; i < 7; i++ ) {
            printf( "%c ", "A234567890JQK"[ hand[i] ] );
            ++cnt[ hand[i] ];
        }
    
    //  qsort( cnt, sizeof cnt[0], 13, cmp ); // Wrong!
        qsort( cnt, 13, sizeof cnt[0], cmp ); // descending
    
        if( cnt[0] == 4 ) return four;
    
        if( cnt[0] == 3 )
            if( cnt[1] == 2 )
                return fullhouse;
            else
                return three;
    
        if( cnt[0] == 2 )
            if( cnt[1] == 2 )
                return twoPair;
            else
                return two;
    
        return nada;
    }
    
    int main( void ) {
        srand(time(NULL));
    
        int deck[52];
    
        for (int i = 0; i < 52; i++)
            deck[i] = i % 13;
    
        int hand_counts[ nTallies ] = { 0 };
    
        for( int r = 0; r < 100; r++ ) {
            deck_shuffle( deck );
    
            int p = possibility( deck ); // look at top 7 cards
            printf( " ... %2d\n", p );
    
            ++hand_counts[ p ];
        }
    
        for( int out = 0; out < nTallies; out++ )
            printf("The probability of hand type %d: is %f\n", out, (double)hand_counts[out] / 100);
    
        return 0;
    }