功能无法始终如一地打印正确的输出

时间:2016-06-17 18:17:52

标签: c linked-list

经过几个小时和许多尝试调试的咖啡,我和我的朋友们都可以知道出了什么问题。

该程序的快速概述,它是一个用C编写的小型纸牌游戏,并使用链接列表来保存CardDeck中的Hand。在main()我有一个循环打印两个玩家的Hand,然后从他们的Hand播放随机卡并将其放在Deck的末尾。为此,我有一个函数playCard()

问题是,它打印的Card是播放器Hand中不存在的。我只能想象Card中必须存在Deck,但我不知道为什么要打印它。我还确保列表的末尾NULL也可以阻止这样的事情发生。

有谁知道这里出了什么问题?

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

typedef struct CARD Card;

struct CARD
{
    int value;
    int id;
    Card *next_card;
};

typedef struct Deck
{
    int size;
    Card *cards;
}Deck;

typedef struct Hand
{
    int size;
    Card *cards;
}Hand;


Card* createCard();
Deck* createDeck();
Hand* createHand();
int addCard(Card* card, Deck* deck);
int cardExists(int id, Deck* deck);
int cardExistsC(Card* card, Deck* deck);
void shuffle(Deck* deck);
int fillHand(int size, Hand* hand, Deck* deck);
void showHand(Hand* hand);
int playCard(int id, Hand* hand, Deck* deck);


int main(int argc, char** argv)
{
    // Initialise seed for random number generator
    srand(time(NULL));
    int playable = 1;

    Deck* deck = createDeck();
    Hand* player1 = createHand();
    Hand* player2 = createHand();

    if (deck == NULL || player1 == NULL || player2 == NULL)
    {
        if (deck != NULL)
            free(deck);
        if (player1 != NULL)
            free(player1);
        if (player2 != NULL)
            free(player2);
        playable = 0;
    }

    if (!playable)
    {
        return -1;
    }
    else
    {
        int i = 0;
        for (i = 0; i < 52; i++)
        {
            Card* temp = createCard();
            addCard(temp, deck);
        }

        shuffle(deck);

        fillHand(7, player1, deck);
        fillHand(7, player2, deck);

        for (i = 0; i < 7; i++)
        {
            showHand(player1);
            showHand(player2);

            playCard(-1, player1, deck);
            playCard(-1, player2, deck);
        }

        free(deck);
        free(player1);
        free(player2);
    }

    return 0;
}


// Create a new card generating a random ID (0-10,000)and value from 0-10
Card* createCard()
{
    Card* card = NULL;
    card = (Card*)malloc(sizeof(Card));
    card->id = rand() % 10000;
    card->value = rand() % 10;
    card->next_card = NULL;
    return card;
}

// Creates a new deck and sets the size to 0, creates a list within the deck
Deck* createDeck()
{
    Deck* deck = NULL;
    deck = (Deck*)malloc(sizeof(Deck));
    deck->size = 0;
    deck->cards = NULL;
    return deck;
}

// Creates a new hand and sets the size to 0, creates a list within the hand
Hand* createHand()
{
    Hand* hand = NULL;
    hand = (Hand*)malloc(sizeof(Hand));
    hand->size = 0;
    hand->cards = NULL;
    return hand;
}

// Adds a created card to a deck, returns 1 if card was added, 0 if card wasn't, -1 if a duplicate id was detected
int addCard(Card* card, Deck* deck)
{
    // If the deck or the card is not initialised, the card cannot be added
    if (deck == NULL || card == NULL) return 0;

    // If deck size 0, this must be the first card
    if (deck->size == 0)
    {
        // Add the card and increment the deck size
        deck->cards = card;
        deck->size++;

        // Check if the card was added successfully
        if (cardExists(card->id, deck) == 1 || cardExistsC(card, deck) == 1) return 1;

        // Returns 0 if card check failed
        return 0;
    }

    // If deck contains at least a card, then add it to the end of the List of cards
    if (deck->size > 0)
    {
        // First check if a duplicate ID exists
        if (cardExists(card->id, deck) == 1 || cardExistsC(card, deck) == 1) return -1;

        // Traverse to the last card and add the new card
        Card *p = deck->cards;
        while (p->next_card != NULL)
        {
            p = p->next_card;
        }
        p->next_card = card;
        deck->size++;

        // Check if the card was added successfully
        if (cardExists(card->id, deck) == 1 || cardExistsC(card, deck) == 1) return 1;
    }

    // If nothing runs successfully, return 0
    return 0;
}

// Determines if a card exists within a deck given an id or a card* returns 1, otherwise 0
int cardExists(int id, Deck* deck)
{
    // If deck is not initialised, return 0
    if (deck == NULL) return 0;

    // Traverse through the cards checking if the id matches any of the current cards
    Card *p = deck->cards;
    if (p->id == id) return 1; // Check if id matches the first card
    while (p->next_card != NULL)
    {
        p = p->next_card; // Move on to the next_card
        if (p->id == id) return 1; // If id matches, return 1
    }

    // No duplicate cards return 0
    return 0;
}

int cardExistsC(Card* card, Deck* deck)
{
    // If deck is not initialised, return
    if (deck == NULL || card == NULL) return 0;

    // Traverse through the cards checking if the card matches any of the current cards
    Card *p = deck->cards;
    while (p->next_card != NULL)
    {
        if (p == card) return 1; // If id matches, return 1
        p = p->next_card; // Else move on to the next_card
    }

    // No duplicate cards return 0
    return 0;
}

// Shuffles deck - size*100 randoms swaps, or a shuffling algorithm
void shuffle(Deck* deck)
{
    // If deck is not initialised, return
    if (deck == NULL) return;

    // Declare vars for use
    int d, i, j, x, r1, r2;
    Card *o = deck->cards; // The List of cards
    Card *p1, *p2, *t; // Temp cards
    d = deck->size; // Deck size
    i = 0; // Loop var
    j = d * 100; // Amount of swaps needed
    x = 0; // Inner loop var

           // Initialise pointers
    p1 = NULL;
    p2 = NULL;
    t = NULL;

    // Swaps two cards while less than amount of reqired swaps
    while (i < j)
    {
        // Create two random numbers
        r1 = rand() % d;
        r2 = rand() % d;

        // Traverse through the List od cards in the deck r1 and r2 number of times
        while (x <= r1 || x <= r2 && o->next_card != NULL)
        {
            if (x == r1) p1 = o; // p1 == o when x == r1
            if (x == r2) p2 = o; // p2 == o when x == r2
            o = o->next_card;
            x++;
        }

        // Hold p1 in t
        t = p1;
        // Replace p1 with p2
        p1->id = p2->id;
        p1->value = p2->value;
        // Replace p2 with t
        p2->id = t->id;
        p2->value = t->value;

        i++;
    }
}

// Moves the top x cards of the deck to the hand structure returns hand size
int fillHand(int size, Hand* hand, Deck* deck)
{
    // If deck and hand is not initialised or size less than 0, return
    if (deck == NULL || !(size >= 0)) return 0;

    // Initialise vars for use
    Card *h = hand->cards;
    Card *d = deck->cards;
    int x = 1;

    // Make h == d
    h = d;
    // Start the hand here
    hand->cards = h;

    // Traverse 'size' amount through the hand and deck
    while (x < size && h->next_card != NULL && d->next_card != NULL)
    {
        h = h->next_card;
        d = d->next_card;
        x++;
    }
    // Update hand->size
    hand->size = x;
    // Move the card after the new hand list up to be the new top of deck
    deck->cards = d->next_card;
    deck->size = deck->size - x;
    // End the hand here
    h->next_card = NULL;
    return hand->size;
}

// prints the hand to the console showing ID and value, one card per line in the format ###\tID-Value
void showHand(Hand* hand)
{
    // If hand is not initialised, return
    if (hand == NULL) return;

    // Declare vars for use
    int val, id;
    Card *p = hand->cards;
    // Traverse through the List of Cards printing each one's value and id
    while (p->next_card != NULL)
    {
        val = p->value;
        id = p->id;
        printf("###\t %d-%d\n", id, val);
        p = p->next_card;
    }
    printf("\n");
}

// Removes the card from hand and displays the card ID and Value in the format ***ID-Value, a value of -1 in id indicates a random card, returns played card id
int playCard(int id, Hand* hand, Deck* deck)
{
    // If hand is not initialised, return
    if (hand == NULL || deck == NULL) return 0;

    // Setup vars for use
    Card *d, *h, *p;
    int i, cid, cval;
    d = deck->cards;
    h = hand->cards;
    p = h;
    i = 0;

    // If card is to be randomly chosen
    if (id == -1)
    {
        i = rand() % hand->size; // Get a random number
        i++;
        while (i > 0 && h->next_card != NULL) // Move i times through the list
        {
            p = h; // make p == current card so it becomes the previous card
            h = h->next_card; // Move to the next card
            i--; // Decrement i
        }

        // Go to end of the deck
        while (d->next_card != NULL)
        {
            d = d->next_card;
        }

        // Get card data
        cid = h->id;
        cval = h->value;
        // Print the card data
        printf("***%d-%d\n\n", cid, cval);

        // Put the randomly chosen card from the hand to the end of deck
        d->next_card = h;
        // Remove the card from hand
        p->next_card = h->next_card;
        // Move to the card added to the deck
        d = d->next_card;
        // Ensure the end card is the last in the list
        d->next_card = NULL;
        // Increment deck size
        deck->size++;
        // Decrement hand size
        hand->size--;
        return -1;
    }
    else
    {
        // Go to end of the deck
        while (d->next_card != NULL)
        {
            d = d->next_card;
        }

        // Get card data
        cid = h->id;
        cval = h->value;
        // Print the card data
        printf("***%d-%d\n\n", cid, cval);

        // Put the randomly chosen card from the hand to the end of deck
        d->next_card = h;
        // Remove the card from hand and make the following card the new head of the list
        hand->cards = h->next_card;
        // Move to the card added to the deck
        d = d->next_card;
        // Ensure the end card is the last in the list
        d->next_card = NULL;
        // Increment deck size
        deck->size++;
        // Decrement hand size
        hand->size--;
        return cid;
    }
}

1 个答案:

答案 0 :(得分:2)

您的某些循环中存在一些用于访问链接列表的错误。

您的Shuffle中存在明确的错误,会产生重复的卡片。

我对playCard进行了粗略的检查,看起来没问题

在我修复了其他一些错误之后,我肯定会得到不同的输出(注意:我更改了srand以使用固定值,以便您的版本与我的版本之间的比较会工作)

使用-Wall编译器选项,您的一个while循环被标记为不明确。我已对其进行了注释,并根据我认为您想要的内容添加了括号。

可能还有其他一些错误,但我已经注释了我能找到的错误。请注意,我使用了以下[尽可能]:

// NOTE/BUG: ...
#if 0
// original code
#else
// fixed code
#endif

无论如何,这是修改后的代码[请原谅无偿风格的清理]:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

typedef struct CARD Card;

struct CARD {
    int value;
    int id;
    Card *next_card;
};

typedef struct Deck {
    int size;
    Card *cards;
} Deck;

typedef struct Hand {
    int size;
    Card *cards;
} Hand;

Card *createCard();
Deck *createDeck();
Hand *createHand();
int addCard(Card *card, Deck *deck);
int cardExists(int id, Deck *deck);
int cardExistsC(Card *card, Deck *deck);
void shuffle(Deck *deck);
int fillHand(int size, Hand *hand, Deck *deck);
void showHand(Hand *hand);
int playCard(int id, Hand *hand, Deck *deck);

int
main(int argc, char **argv)
{
    // Initialise seed for random number generator
    srand(3);
    int playable = 1;

    Deck *deck = createDeck();
    Hand *player1 = createHand();
    Hand *player2 = createHand();

    if (deck == NULL || player1 == NULL || player2 == NULL) {
        if (deck != NULL)
            free(deck);
        if (player1 != NULL)
            free(player1);
        if (player2 != NULL)
            free(player2);
        playable = 0;
    }

    if (!playable) {
        return -1;
    }

    int i = 0;

    // NOTE/BUG: does not fill deck if a duplicate is found (i.e. ignores
    // addCard return)
#if 0
    for (i = 0; i < 52; i++) {
        Card *temp = createCard();
        addCard(temp, deck);
    }
#else
    for (i = 0; i < 52; i++) {
        while (1) {
            Card *temp = createCard();
            if (addCard(temp, deck) > 0)
                break;
            printf("DUPLICATE createCard\n");
        }
    }
#endif

    shuffle(deck);

    fillHand(7, player1, deck);
    fillHand(7, player2, deck);

    for (i = 0; i < 7; i++) {
        showHand(player1);
        showHand(player2);

        playCard(-1, player1, deck);
        playCard(-1, player2, deck);
    }

    free(deck);
    free(player1);
    free(player2);

    return 0;
}

// Create a new card generating a random ID (0-10,000)and value from 0-10
Card *
createCard()
{
    Card *card = NULL;

    card = (Card *) malloc(sizeof(Card));
    card->id = rand() % 10000;
    card->value = rand() % 10;
    card->next_card = NULL;
    return card;
}

// Creates a new deck and sets the size to 0, creates a list within the deck
Deck *
createDeck()
{
    Deck *deck = NULL;

    deck = (Deck *) malloc(sizeof(Deck));
    deck->size = 0;
    deck->cards = NULL;
    return deck;
}

// Creates a new hand and sets the size to 0, creates a list within the hand
Hand *
createHand()
{
    Hand *hand = NULL;

    hand = (Hand *) malloc(sizeof(Hand));
    hand->size = 0;
    hand->cards = NULL;
    return hand;
}

// Adds a created card to a deck, returns 1 if card was added, 0 if card wasn't, -1 if a duplicate id was detected
int
addCard(Card *card, Deck *deck)
{
    // If the deck or the card is not initialised, the card cannot be added
    if (deck == NULL || card == NULL)
        return 0;

    // If deck size 0, this must be the first card
    if (deck->size == 0) {
        // Add the card and increment the deck size
        deck->cards = card;
        deck->size++;

        // Check if the card was added successfully
        // NOTE/BUG: superfluous check -- this will always return 1
        if (cardExists(card->id, deck) == 1 || cardExistsC(card, deck) == 1)
            return 1;

        // Returns 0 if card check failed
        // NOTE/BUG: otherwise, this is _fatal_
        return 0;
    }

    // If deck contains at least a card, then add it to the end of the List of cards
    if (deck->size > 0) {
        // First check if a duplicate ID exists
        if (cardExists(card->id, deck) == 1 || cardExistsC(card, deck) == 1)
            return -1;

        // Traverse to the last card and add the new card
        Card *p = deck->cards;

        while (p->next_card != NULL) {
            p = p->next_card;
        }
        p->next_card = card;
        deck->size++;

        // Check if the card was added successfully
        if (cardExists(card->id, deck) == 1 || cardExistsC(card, deck) == 1)
            return 1;
    }

    // If nothing runs successfully, return 0
    return 0;
}

// Determines if a card exists within a deck given an id or a card* returns 1, otherwise 0
int
cardExists(int id, Deck *deck)
{
    // If deck is not initialised, return 0
    if (deck == NULL)
        return 0;

    // Traverse through the cards checking if the id matches any of the current cards
    Card *p = deck->cards;

#if 0
    if (p->id == id)
        return 1;                       // Check if id matches the first card
    while (p->next_card != NULL) {
        p = p->next_card;               // Move on to the next_card
        if (p->id == id)
            return 1;                   // If id matches, return 1
    }
    if (p->id == id)
        return 1;                       // Check if id matches the first card
#else
    while (p != NULL) {
        if (p->id == id)
            return 1;                   // If id matches, return 1
        p = p->next_card;               // Move on to the next_card
    }
#endif

    // No duplicate cards return 0
    return 0;
}

int
cardExistsC(Card *card, Deck *deck)
{
    // If deck is not initialised, return
    if (deck == NULL || card == NULL)
        return 0;

    // Traverse through the cards checking if the card matches any of the current cards
    Card *p = deck->cards;

#if 0
    while (p->next_card != NULL) {
        if (p == card)
            return 1;                   // If id matches, return 1
        p = p->next_card;               // Else move on to the next_card
    }
#else
    while (p != NULL) {
        if (p == card)
            return 1;                   // If id matches, return 1
        p = p->next_card;               // Else move on to the next_card
    }
#endif

    // No duplicate cards return 0
    return 0;
}

// Shuffles deck - size*100 randoms swaps, or a shuffling algorithm
void
shuffle(Deck *deck)
{
    // If deck is not initialised, return
    if (deck == NULL)
        return;

    // Declare vars for use
    int d,
     i,
     j,
     x,
     r1,
     r2;
    Card *o = deck->cards;              // The List of cards
    Card *p1, *p2;

    d = deck->size;                     // Deck size
    i = 0;                              // Loop var
    j = d * 100;                        // Amount of swaps needed
    x = 0;                              // Inner loop var

    // Initialise pointers
    p1 = NULL;
    p2 = NULL;

    // Swaps two cards while less than amount of reqired swaps
    while (i < j) {
        // Create two random numbers
        r1 = rand() % d;
        r2 = rand() % d;

        // Traverse through the List od cards in the deck r1 and r2 number of times
        // NOTE/BUG: this gets flagged by the compiler with -Wall and really
        // is ambiguous:
        // which is it?
        //   while (((x <= r1) || (x <= r2)) && (o->next_card != NULL))
        //
        //   while ((x <= r1) || ((x <= r2) && (o->next_card != NULL)))
#if 0
        while (x <= r1 || x <= r2 && o->next_card != NULL) {
#else
        while (((x <= r1) || (x <= r2)) && (o->next_card != NULL)) {
#endif
            if (x == r1)
                p1 = o;                 // p1 == o when x == r1
            if (x == r2)
                p2 = o;                 // p2 == o when x == r2
            o = o->next_card;
            x++;
        }

        // NOTE/BUG!!! -- this does _not_ preserve the values correctly for the
        // swap
#if 0
        // Hold p1 in t
        Card *t;                                    // Temp cards
        t = p1;

        // Replace p1 with p2
        p1->id = p2->id;
        p1->value = p2->value;

        // Replace p2 with t
        p2->id = t->id;
        p2->value = t->value;
#else
        // Hold p1 in t
        Card t;                                 // Temp cards
        t = *p1;

        // Replace p1 with p2
        p1->id = p2->id;
        p1->value = p2->value;

        // Replace p2 with t
        p2->id = t.id;
        p2->value = t.value;
#endif

        i++;
    }
}

// Moves the top x cards of the deck to the hand structure returns hand size
int
fillHand(int size, Hand *hand, Deck *deck)
{
    // If deck and hand is not initialised or size less than 0, return
    if (deck == NULL || !(size >= 0))
        return 0;

    // Initialise vars for use
    Card *h = hand->cards;
    Card *d = deck->cards;
    int x = 1;

    // Make h == d
    h = d;
    // Start the hand here
    hand->cards = h;

    // Traverse 'size' amount through the hand and deck
    while (x < size && h->next_card != NULL && d->next_card != NULL) {
        h = h->next_card;
        d = d->next_card;
        x++;
    }

    // Update hand->size
    hand->size = x;

    // Move the card after the new hand list up to be the new top of deck
    deck->cards = d->next_card;
    deck->size = deck->size - x;

    // End the hand here
    h->next_card = NULL;

    return hand->size;
}

// prints the hand to the console showing ID and value, one card per line in the format ###\tID-Value
void
showHand(Hand *hand)
{
    // If hand is not initialised, return
    if (hand == NULL)
        return;

    // Declare vars for use
    int val,
     id;
    Card *p = hand->cards;

    // Traverse through the List of Cards printing each one's value and id
    // NOTE/BUG: would fail if only one card in hand
#if 0
    while (p->next_card != NULL) {
        val = p->value;
        id = p->id;
        printf("###\t %d-%d\n", id, val);
        p = p->next_card;
    }
#else
    while (p != NULL) {
        val = p->value;
        id = p->id;
        printf("###\t %d-%d\n", id, val);
        p = p->next_card;
    }
#endif
    printf("\n");
}

// Removes the card from hand and displays the card ID and Value in the format ***ID-Value, a value of -1 in id indicates a random card, returns played card id
int
playCard(int id, Hand *hand, Deck *deck)
{
    // If hand is not initialised, return
    if (hand == NULL || deck == NULL)
        return 0;

    // Setup vars for use
    Card *d,
    *h,
    *p;
    int i,
     cid,
     cval;

    d = deck->cards;
    h = hand->cards;
    p = h;
    i = 0;

    // If card is to be randomly chosen
    if (id == -1) {
        i = rand() % hand->size;        // Get a random number
        i++;
        while (i > 0 && h->next_card != NULL)   // Move i times through the list
        {
            p = h;                      // make p == current card so it becomes the previous card
            h = h->next_card;           // Move to the next card
            i--;                        // Decrement i
        }

        // Go to end of the deck
        while (d->next_card != NULL) {
            d = d->next_card;
        }

        // Get card data
        cid = h->id;
        cval = h->value;
        // Print the card data
        printf("***%d-%d\n\n", cid, cval);

        // Put the randomly chosen card from the hand to the end of deck
        d->next_card = h;

        // Remove the card from hand
        p->next_card = h->next_card;

        // Move to the card added to the deck
        d = d->next_card;

        // Ensure the end card is the last in the list
        d->next_card = NULL;

        // Increment deck size
        deck->size++;

        // Decrement hand size
        hand->size--;

        return -1;
    }
    else {
        // Go to end of the deck
        while (d->next_card != NULL) {
            d = d->next_card;
        }

        // Get card data
        cid = h->id;
        cval = h->value;
        // Print the card data
        printf("***%d-%d\n\n", cid, cval);

        // Put the randomly chosen card from the hand to the end of deck
        d->next_card = h;
        // Remove the card from hand and make the following card the new head of the list
        hand->cards = h->next_card;

        // Move to the card added to the deck
        d = d->next_card;

        // Ensure the end card is the last in the list
        d->next_card = NULL;

        // Increment deck size
        deck->size++;

        // Decrement hand size
        hand->size--;

        return cid;
    }
}