Thread: Uno Card Game - Can this be simpler?

  1. #1
    Registered User
    Join Date
    Nov 2012
    Location
    Porto Alegre, Brazil
    Posts
    5

    Question Uno Card Game - Can this be simpler?

    Hello!

    I'm trying to make a UNO Card Game, here are the rules if you do not know them: Uno (card game) - Wikipedia, the free encyclopedia

    Anyway, i'm trying to build the basic structure of the card/deck to later be able to easily compare the card/plays with integers. It's all good, but i was wondering if anyone can give me a tip if i could make this simpler and, possibly, shorter somewhere:

    Code:
    #include <stdio.h>
    
    int main()
    {
        enum Cards
        {
            Zero, One, Two, Tree, Four, Five, Six, Seven, Eight, Nine, Skip, Draw2, WildDraw4, Reverse, Wild
        };
        
        enum Values
        {
            Special=20, Joker=50
        };
        
        enum Colors
        {
            Blue, Red, Yellow, Green, Black
        };
        
        typedef struct {
            enum Cards Card;
            enum Values Value;
            enum Colors Color;
        } Card;
        
        Card card[108];
        int i, j;
            
        // Initializes the cards from 0-9 of all colors
        for(i=0;i<=39;i++)
        {
            for(j=0;i<=4;j++)
            {
                // Just ignore the wrong definition below...
                card[i] =  (Card){ 0, 20, j};
            }
        }
    
        // Other FORs
        
        return 0;
    }
    It looked good when i created the Enums, but when i got to the point of initializing the 108 cards deck, i was like "oh man!". I will have to do a bunch of FORs and some manual initialization.

    Anyone more experienced with this type of problem has any hints for me?

    Thanks in advance!
    Last edited by ScorchedPsyche; 11-06-2012 at 03:33 PM. Reason: Just to fix code indent.

  2. #2
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    How about you pack each card into an unsigned char?
    Code:
    typedef unsigned char  card_t;
    
    #define   COLOR_MASK   112U
    
    #define   COLOR_NONE     0U
    #define   COLOR_RED     16U
    #define   COLOR_GREEN   32U
    #define   COLOR_YELLOW  48U
    #define   COLOR_BLUE    64U
    
    #define   RANK_MASK     15U
    
    #define   RANK_ZERO      0U
    #define   RANK_ONE       1U
    #define   RANK_TWO       2U
    #define   RANK_THREE     3U
    #define   RANK_FOUR      4U
    #define   RANK_FIVE      5U
    #define   RANK_SIX       6U
    #define   RANK_SEVEN     7U
    #define   RANK_EIGHT     8U
    #define   RANK_NINE      9U
    
    #define   RANK_SKIP     10U
    #define   RANK_DRAWTWO  11U
    #define   RANK_REVERSE  12U
    
    #define   RANK_WILD     13U
    #define   RANK_WILDFOUR 14U
    
    
    static const card_t  uno_deck[108] = {
    
        COLOR_RED    | RANK_ZERO,
        COLOR_RED    | RANK_ONE,     COLOR_RED    | RANK_ONE,
        COLOR_RED    | RANK_TWO,     COLOR_RED    | RANK_TWO,
        COLOR_RED    | RANK_THREE,   COLOR_RED    | RANK_THREE,
        COLOR_RED    | RANK_FOUR,    COLOR_RED    | RANK_FOUR,
        COLOR_RED    | RANK_FIVE,    COLOR_RED    | RANK_FIVE,
        COLOR_RED    | RANK_SIX,     COLOR_RED    | RANK_SIX,
        COLOR_RED    | RANK_SEVEN,   COLOR_RED    | RANK_SEVEN,
        COLOR_RED    | RANK_EIGHT,   COLOR_RED    | RANK_EIGHT,
        COLOR_RED    | RANK_NINE,    COLOR_RED    | RANK_NINE,
        COLOR_RED    | RANK_SKIP,    COLOR_RED    | RANK_SKIP,
        COLOR_RED    | RANK_DRAWTWO, COLOR_RED    | RANK_DRAWTWO,
        COLOR_RED    | RANK_REVERSE, COLOR_RED    | RANK_REVERSE,
    
        COLOR_GREEN  | RANK_ZERO,
        COLOR_GREEN  | RANK_ONE,     COLOR_GREEN  | RANK_ONE,
        COLOR_GREEN  | RANK_TWO,     COLOR_GREEN  | RANK_TWO,
        COLOR_GREEN  | RANK_THREE,   COLOR_GREEN  | RANK_THREE,
        COLOR_GREEN  | RANK_FOUR,    COLOR_GREEN  | RANK_FOUR,
        COLOR_GREEN  | RANK_FIVE,    COLOR_GREEN  | RANK_FIVE,
        COLOR_GREEN  | RANK_SIX,     COLOR_GREEN  | RANK_SIX,
        COLOR_GREEN  | RANK_SEVEN,   COLOR_GREEN  | RANK_SEVEN,
        COLOR_GREEN  | RANK_EIGHT,   COLOR_GREEN  | RANK_EIGHT,
        COLOR_GREEN  | RANK_NINE,    COLOR_GREEN  | RANK_NINE,
        COLOR_GREEN  | RANK_SKIP,    COLOR_GREEN  | RANK_SKIP,
        COLOR_GREEN  | RANK_DRAWTWO, COLOR_GREEN  | RANK_DRAWTWO,
        COLOR_GREEN  | RANK_REVERSE, COLOR_GREEN  | RANK_REVERSE,
    
        COLOR_YELLOW | RANK_ZERO,
        COLOR_YELLOW | RANK_ONE,     COLOR_YELLOW | RANK_ONE,
        COLOR_YELLOW | RANK_TWO,     COLOR_YELLOW | RANK_TWO,
        COLOR_YELLOW | RANK_THREE,   COLOR_YELLOW | RANK_THREE,
        COLOR_YELLOW | RANK_FOUR,    COLOR_YELLOW | RANK_FOUR,
        COLOR_YELLOW | RANK_FIVE,    COLOR_YELLOW | RANK_FIVE,
        COLOR_YELLOW | RANK_SIX,     COLOR_YELLOW | RANK_SIX,
        COLOR_YELLOW | RANK_SEVEN,   COLOR_YELLOW | RANK_SEVEN,
        COLOR_YELLOW | RANK_EIGHT,   COLOR_YELLOW | RANK_EIGHT,
        COLOR_YELLOW | RANK_NINE,    COLOR_YELLOW | RANK_NINE,
        COLOR_YELLOW | RANK_SKIP,    COLOR_YELLOW | RANK_SKIP,
        COLOR_YELLOW | RANK_DRAWTWO, COLOR_YELLOW | RANK_DRAWTWO,
        COLOR_YELLOW | RANK_REVERSE, COLOR_YELLOW | RANK_REVERSE,
    
        COLOR_BLUE   | RANK_ZERO,
        COLOR_BLUE   | RANK_ONE,     COLOR_BLUE   | RANK_ONE,
        COLOR_BLUE   | RANK_TWO,     COLOR_BLUE   | RANK_TWO,
        COLOR_BLUE   | RANK_THREE,   COLOR_BLUE   | RANK_THREE,
        COLOR_BLUE   | RANK_FOUR,    COLOR_BLUE   | RANK_FOUR,
        COLOR_BLUE   | RANK_FIVE,    COLOR_BLUE   | RANK_FIVE,
        COLOR_BLUE   | RANK_SIX,     COLOR_BLUE   | RANK_SIX,
        COLOR_BLUE   | RANK_SEVEN,   COLOR_BLUE   | RANK_SEVEN,
        COLOR_BLUE   | RANK_EIGHT,   COLOR_BLUE   | RANK_EIGHT,
        COLOR_BLUE   | RANK_NINE,    COLOR_BLUE   | RANK_NINE,
        COLOR_BLUE   | RANK_SKIP,    COLOR_BLUE   | RANK_SKIP,
        COLOR_BLUE   | RANK_DRAWTWO, COLOR_BLUE   | RANK_DRAWTWO,
        COLOR_BLUE   | RANK_REVERSE, COLOR_BLUE   | RANK_REVERSE,
    
        COLOR_NONE   | RANK_WILD,
        COLOR_NONE   | RANK_WILD,
        COLOR_NONE   | RANK_WILD,
        COLOR_NONE   | RANK_WILD,
    
        COLOR_NONE   | RANK_WILDFOUR,
        COLOR_NONE   | RANK_WILDFOUR,
        COLOR_NONE   | RANK_WILDFOUR,
        COLOR_NONE   | RANK_WILDFOUR
    };
    
    #define   RANK(card)   ((card) & RANK_MASK)
    #define   COLOR(card)  ((card) & COLOR_MASK)

  3. #3
    Registered User
    Join Date
    Nov 2012
    Location
    Porto Alegre, Brazil
    Posts
    5

    Thumbs up

    Sorry it took me so long to answer, for I didn't have the time to study the code before, since I didn't fully understand it at first.

    But that's a very interesting approach indeed. I would use less memory this way swapping from Int32 to uChar. However, I don't fully understand the use of the pipe in there. I realized it does adding, but how?

    What does the two last #define lines do? How do they work?
    Code:
    #define   RANK(card)   ((card) & RANK_MASK)
    #define   COLOR(card)  ((card) & COLOR_MASK)
    Thanks for the reply!

  4. #4
    Registered User
    Join Date
    Sep 2001
    Posts
    4,912
    The pipe is a bitwise-OR operator. It takes two binary inputs and one binary output. Any bit that is set in either of the inputs will be set in the output (e.g. 10010110 | 11001100 = 11011110). This is often used to combine "flags". What you do is define constants that each represent one of the bits. If you look at the constants used above, each number when converted to binary just as a single '1' bit. When you use the bitwise-OR operator, you can add these flags together into a single value that has all the right '1' bits. You can subsequently test that value for a specific flag by '&'ing (bitwise-AND) the value with one of the constants. If the result equals the constant, that bit was set (this is called masking).

    The #define lines are called macros in C. They define simple functions, but rather than calling these functions at runtime, the preprocessor will literally replace occurences of RANK() or COLOR() with the code on the right-hand side. These macros are doing the "masking" I described above.

    Note that this is not REALLY "adding" in the arithmetic sense. When you add real numbers you have to take into account carrying and the binary operations become a little more complex.

  5. #5
    Registered User
    Join Date
    Nov 2012
    Location
    Porto Alegre, Brazil
    Posts
    5
    Wow! Thanks for that explanation!

    I'm going to work to try and get the suggested version of the code working!

    Thanks a lot for the input!

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by ScorchedPsyche View Post
    I don't fully understand the use of the pipe in there.
    Like sean said, it does a bitwise OR.

    Quote Originally Posted by ScorchedPsyche View Post
    What does the two last #define lines do? How do they work?
    They define preprocessor macros you can use to get the rank or color of a card.

    The basic idea behind the scheme is to use eight (seven) bits, packed into an unsigned char, to represent each possible card. The low five (four) bits denote the rank, and the high three bits the color:
    Code:
    111000002 = 224 = COLOR_MASK
    
    000000002 =   0 = COLOR_NONE
    001000002 =  32 = COLOR_RED
    010000002 =  64 = COLOR_GREEN
    011000002 =  96 = COLOR_YELLOW
    100000002 = 128 = COLOR_BLUE
    
    000011112 = 15 = RANK_MASK
    
    000000002 =  0 = RANK_ZERO
    000000012 =  1 = RANK_ONE
    000000102 =  2 = RANK_TWO
    000000112 =  3 = RANK_THREE
    000001002 =  4 = RANK_FOUR
    000001012 =  5 = RANK_FIVE
    000001102 =  6 = RANK_SIX
    000001112 =  7 = RANK_SEVEN
    000010002 =  8 = RANK_EIGHT
    000010012 =  9 = RANK_NINE
    000010102 = 10 = RANK_SKIP
    000010112 = 11 = RANK_DRAWTWO
    000011002 = 12 = RANK_REVERSE
    
    000011012 = 13 = RANK_WILD
    000011102 = 14 = RANK_WILDFOUR
    To get any card, you OR (|) one of the COLOR_ constants and one of the RANK_ constants.

    I left the fifth bit unused by accident, but I guess it may come in handy if you support the new Uno deck, too. (Just set the rank mask to 000111112 = 31 instead, and you have 16 more ranks you can use.)

    To determine the rank of any card, you ignore the color bits. You do this by doing a bitwise AND (&) between the card and RANK_MASK . The RANK() macro does exactly this for you.

    To determine the color of any card, you do the same, but with the other mask. The COLOR() macro does exactly that.

    For example, assume you have two cards, card1 and card2. Then,
    Code:
        if (card1 == card2) {
            /* The cards have the same color and rank */
        }
    
        if (RANK(card1) == RANK(card2)) {
            /* The cards have the same rank */
        }
    
        if (COLOR(card1) == COLOR(card2)) {
            /* The cards have the same color */
        }
    
        if (card1 == (COLOR_NONE | RANK_WILDFOUR)) {
            /* card 1 is the wild four */
        }
    
        if (card2 == (COLOR_RED | RANK_FIVE)) {
            /* card 2 is red five. */
        }

  7. #7
    Registered User
    Join Date
    Nov 2012
    Location
    Porto Alegre, Brazil
    Posts
    5
    Yeah, I understood the logic behind it.

    It truly is an ingenious way to solve this problem. I would have never seen this (even so because I didn't actually understand what the single comparison operators did - I had read once that they did bit-wise operations but I never saw it being used like this).

    This was the most instructive post I've ever had on programming and this has definitely opened up a gigantic array of possibilities and ideas I'd like to tamper with!

    Thanks a lot guys!

  8. #8
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    why does 16u (unsigned char) equal to 32 bits, for example?

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by thames View Post
    why does 16u (unsigned char) equal to 32 bits, for example?
    I'm sorry, I don't understand what you mean. Could you rephrase the question?

    After rereading this thread, I did realize my code uses four bits for rank and three for color, i.e.
    Code:
    111 00002 = 112 = COLOR_MASK
    
    000 00002 =  0 = COLOR_NONE
    001 00002 = 16 = COLOR_RED
    010 00002 = 32 = COLOR_GREEN
    011 00002 = 48 = COLOR_YELLOW
    100 00002 = 64 = COLOR_BLUE
    
    000 11112 = 15 = RANK_MASK
    
    000 00002 =  0 = RANK_ZERO
    000 00012 =  1 = RANK_ONE
    000 00102 =  2 = RANK_TWO
    000 00112 =  3 = RANK_THREE
    000 01002 =  4 = RANK_FOUR
    000 01012 =  5 = RANK_FIVE
    000 01102 =  6 = RANK_SIX
    000 01112 =  7 = RANK_SEVEN
    000 10002 =  8 = RANK_EIGHT
    000 10012 =  9 = RANK_NINE
    000 10102 = 10 = RANK_SKIP
    000 10112 = 11 = RANK_DRAWTWO
    000 11002 = 12 = RANK_REVERSE
    
    000 11012 = 13 = RANK_WILD
    000 11102 = 14 = RANK_WILDFOUR
    The mismatch occurred because the test program I used to verify the logic used five bits for the rank, but the snippet I listed first in this thread uses just four. Sorry about that.

    Remember, bit N has value 2N == 1 << N, with lowest bit being bit 0 (rightmost in above figures). The 2 just indicates it is binary, not a decimal number.

    Binary AND is used for masking, only bits that are set in both operands will be set in the result, and the other bits will be zero. In other words, a binary 1 in a mask keeps that bit, and a binary 0 clears it. Put another way, 0???????2 & 000011112 = 0000????2 and 0???????2 & 011100002 = 0???00002.

  10. #10
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    Quote Originally Posted by Nominal Animal View Post
    I'm sorry, I don't understand what you mean. Could you rephrase the question?

    After rereading this thread, I did realize my code uses four bits for rank and three for color, i.e.

    Code:
    111 00002 = 112 = COLOR_MASK
    
    000 00002 =  0 = COLOR_NONE
    001 00002 = 16 = COLOR_RED
    010 00002 = 32 = COLOR_GREEN
    011 00002 = 48 = COLOR_YELLOW
    100 00002 = 64 = COLOR_BLUE
    
    000 11112 = 15 = RANK_MASK
    
    000 00002 =  0 = RANK_ZERO
    000 00012 =  1 = RANK_ONE
    000 00102 =  2 = RANK_TWO
    000 00112 =  3 = RANK_THREE
    000 01002 =  4 = RANK_FOUR
    000 01012 =  5 = RANK_FIVE
    000 01102 =  6 = RANK_SIX
    000 01112 =  7 = RANK_SEVEN
    000 10002 =  8 = RANK_EIGHT
    000 10012 =  9 = RANK_NINE
    000 10102 = 10 = RANK_SKIP
    000 10112 = 11 = RANK_DRAWTWO
    000 11002 = 12 = RANK_REVERSE
    
    000 11012 = 13 = RANK_WILD
    000 11102 = 14 = RANK_WILDFOUR
    I see, now I understand. 001 0000 is 16U I was worried about with the other binary you wrote: 001 00000 that you said it was 16U but is 32U actually, right? you just typed one more zero to include 16 more cards if that was the case however forgot to point that 001 00000 is 32U instead of 16U. If all this is false, then I'm lost.

    E.g.:

    You could have written:

    Code:
     
    #define   COLOR_MASK   224U
     
    #define   COLOR_NONE     0U
    #define   COLOR_RED     32U
    #define   COLOR_GREEN   64U
    #define   COLOR_YELLOW  96U
    #define   COLOR_BLUE    128U
    if you considered 001 00000

    instead of

    Code:
    #define   COLOR_MASK   112U
     
    #define   COLOR_NONE     0U
    #define   COLOR_RED       16U
    #define   COLOR_GREEN   32U
    #define   COLOR_YELLOW  48U
    #define   COLOR_BLUE    64U
    if you considered 001 0000
    Last edited by thames; 11-12-2012 at 08:16 AM.

  11. #11
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by thames View Post
    I see, now I understand.
    Yes, you have it right.

    Quote Originally Posted by thames View Post
    I was worried about with the other binary you wrote: 001 00000 that you said it was 16U
    I didn't say that!

    It's just that because of a stupid oversight I made, the descriptions in post #6 do not match the code in post #2. Both posts are correct if you read them standalone.

    Anyway, sorry for the confusion. I'm glad you have it sorted out!

  12. #12
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    Quote Originally Posted by Nominal Animal View Post
    Anyway, sorry for the confusion. I'm glad you have it sorted out!
    no problem man. You're awesome.

  13. #13
    Registered User
    Join Date
    Nov 2012
    Location
    Porto Alegre, Brazil
    Posts
    5

    Question Why isn't the deck being shuffled?

    I proceeded into developing a little bit more of the code, but the shuffle is not working. It's probably because I am making a double call do the definition code, but I couldn't fix it. Sorry if it's another language.

    Main:
    Code:
    #include <stdio.h>
    #include "baralho.h"
    
    int main()
    {
        int i;    
    
        embaralhar();
        printf("-----------------------------------\n");        
    
        for (i=0;i<TAMBAR;i++)
        {
            printf("%d : %d\n", i, baralho[i]);
        }
    
        return 0;
    }
    Functions:
    Code:
    #include <stdio.h>
    #include <stdlib.h> 
    #include <time.h>
    #include "baralho.h"
    
    void embaralhar()
    {
        int i, pos1, pos2;
        unsigned char tmp;
    
        srand(time(NULL));
    
        for (i=0;i<TAMBAR;i++)
        {
            printf("%d\n", baralho[i]);
        }
        printf("--------------------\n");
    
        for (i=0;i<TAMBAR;i++)
        {
            do
            {
                pos1 = rand () % (TAMBAR-1);
                pos2 = rand () % (TAMBAR-1);
            } while (pos1==pos2);
            printf("pos1: %d\n", pos1);
            printf("pos2: %d\n", pos2);
    
            tmp=baralho[pos1];
            baralho[pos1]=baralho[pos2];
            baralho[pos2]=tmp;
        }
        printf("--------------------\n");
    
        for (i=0;i<TAMBAR;i++)
        {
            printf("%d\n", baralho[i]);
        }
    }
    Prototypes:
    Code:
    typedef unsigned char card_t;
     
    #define    COR_MASCARA        112U
        
    #define    COR_PRETO        0U
    #define    COR_VERMELHO        16U
    #define    COR_VERDE        32U
    #define    COR_AMARELO        48U
    #define    COR_AZUL        64U
     
    #define    RANK_MASCARA        15U
     
    #define    RANK_ZERO        0U
    #define    RANK_UM            1U
    #define    RANK_DOIS        2U
    #define    RANK_TRES        3U
    #define    RANK_QUATRO        4U
    #define    RANK_CINCO        5U
    #define    RANK_SEIS        6U
    #define    RANK_SETE        7U
    #define    RANK_OITO        8U
    #define    RANK_NOVE        9U
     
    #define    RANK_PULAR        10U
    #define    RANK_PESCARDOIS        11U
    #define    RANK_INVERTER        12U
     
    #define    RANK_CORINGA        13U
    #define    RANK_CORINGAQUATRO    14U
     
    #define TAMBAR             108
    
    static card_t baralho[TAMBAR] = {
    
        COR_VERMELHO | RANK_ZERO,
        COR_VERMELHO | RANK_UM,        COR_VERMELHO | RANK_UM,
        COR_VERMELHO | RANK_DOIS,        COR_VERMELHO | RANK_DOIS,
        COR_VERMELHO | RANK_TRES,        COR_VERMELHO | RANK_TRES,
        COR_VERMELHO | RANK_QUATRO,        COR_VERMELHO | RANK_QUATRO,
        COR_VERMELHO | RANK_CINCO,        COR_VERMELHO | RANK_CINCO,
        COR_VERMELHO | RANK_SEIS,        COR_VERMELHO | RANK_SEIS,
        COR_VERMELHO | RANK_SETE,        COR_VERMELHO | RANK_SETE,
        COR_VERMELHO | RANK_OITO,        COR_VERMELHO | RANK_OITO,
        COR_VERMELHO | RANK_NOVE,        COR_VERMELHO | RANK_NOVE,
        COR_VERMELHO | RANK_PULAR,        COR_VERMELHO | RANK_PULAR,
        COR_VERMELHO | RANK_PESCARDOIS,    COR_VERMELHO | RANK_PESCARDOIS,
        COR_VERMELHO | RANK_INVERTER,    COR_VERMELHO | RANK_INVERTER,
     
        COR_VERDE | RANK_ZERO,
        COR_VERDE | RANK_UM,        COR_VERDE | RANK_UM,
        COR_VERDE | RANK_DOIS,        COR_VERDE | RANK_DOIS,
        COR_VERDE | RANK_TRES,        COR_VERDE | RANK_TRES,
        COR_VERDE | RANK_QUATRO,        COR_VERDE | RANK_QUATRO,
        COR_VERDE | RANK_CINCO,        COR_VERDE | RANK_CINCO,
        COR_VERDE | RANK_SEIS,        COR_VERDE | RANK_SEIS,
        COR_VERDE | RANK_SETE,        COR_VERDE | RANK_SETE,
        COR_VERDE | RANK_OITO,        COR_VERDE | RANK_OITO,
        COR_VERDE | RANK_NOVE,        COR_VERDE | RANK_NOVE,
        COR_VERDE | RANK_PULAR,        COR_VERDE | RANK_PULAR,
        COR_VERDE | RANK_PESCARDOIS,    COR_VERDE | RANK_PESCARDOIS,
        COR_VERDE | RANK_INVERTER,        COR_VERDE | RANK_INVERTER,
     
        COR_AMARELO | RANK_ZERO,
        COR_AMARELO | RANK_UM,        COR_AMARELO | RANK_UM,
        COR_AMARELO | RANK_DOIS,        COR_AMARELO | RANK_DOIS,
        COR_AMARELO | RANK_TRES,        COR_AMARELO | RANK_TRES,
        COR_AMARELO | RANK_QUATRO,        COR_AMARELO | RANK_QUATRO,
        COR_AMARELO | RANK_CINCO,        COR_AMARELO | RANK_CINCO,
        COR_AMARELO | RANK_SEIS,        COR_AMARELO | RANK_SEIS,
        COR_AMARELO | RANK_SETE,        COR_AMARELO | RANK_SETE,
        COR_AMARELO | RANK_OITO,        COR_AMARELO | RANK_OITO,
        COR_AMARELO | RANK_NOVE,        COR_AMARELO | RANK_NOVE,
        COR_AMARELO | RANK_PULAR,        COR_AMARELO | RANK_PULAR,
        COR_AMARELO | RANK_PESCARDOIS,    COR_AMARELO | RANK_PESCARDOIS,
        COR_AMARELO | RANK_INVERTER,    COR_AMARELO | RANK_INVERTER,
     
        COR_AZUL | RANK_ZERO,
        COR_AZUL | RANK_UM,            COR_AZUL | RANK_UM,
        COR_AZUL | RANK_DOIS,        COR_AZUL | RANK_DOIS,
        COR_AZUL | RANK_TRES,        COR_AZUL | RANK_TRES,
        COR_AZUL | RANK_QUATRO,        COR_AZUL | RANK_QUATRO,
        COR_AZUL | RANK_CINCO,        COR_AZUL | RANK_CINCO,
        COR_AZUL | RANK_SEIS,        COR_AZUL | RANK_SEIS,
        COR_AZUL | RANK_SETE,        COR_AZUL | RANK_SETE,
        COR_AZUL | RANK_OITO,        COR_AZUL | RANK_OITO,
        COR_AZUL | RANK_NOVE,        COR_AZUL | RANK_NOVE,
        COR_AZUL | RANK_PULAR,        COR_AZUL | RANK_PULAR,
        COR_AZUL | RANK_PESCARDOIS,        COR_AZUL | RANK_PESCARDOIS,
        COR_AZUL | RANK_INVERTER,        COR_AZUL | RANK_INVERTER,
     
        COR_PRETO | RANK_CORINGA,
        COR_PRETO | RANK_CORINGA,
        COR_PRETO | RANK_CORINGA,
        COR_PRETO | RANK_CORINGA,
     
        COR_PRETO | RANK_CORINGAQUATRO,
        COR_PRETO | RANK_CORINGAQUATRO,
        COR_PRETO | RANK_CORINGAQUATRO,
        COR_PRETO | RANK_CORINGAQUATRO
    };
     
    #define   RANK(carta)   ((carta) & RANK_MASCARA)
    #define   COR(carta)      ((carta) & COR_MASCARA)
    
    void embaralhar();
    Basically, I am including the prototypes file (baralho.h) into the main file (main.c) which includes the definition of the deck (baralho). But the problem is that when I call the function to shuffle (embaralhar()), the shuffling of the deck only happens inside the functions file, and when I try to call the deck on the main file, the deck is "reset". Why exactly is this happening?

    Sorry for the code in another language. If you're unable to understand, I will translate it.

    Thanks in advance!
    Last edited by ScorchedPsyche; 11-20-2012 at 09:56 AM.

  14. #14
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    You seem to be overly fond of global variables. Get rid of that habit, and start using function parameters. It will be a liberating, empowering experience. I promise.

    (Note that if TAMBAR is the number of cards, then rand() % TAMBAR yields an integer between 0 and TAMBAR-1, inclusive; it will never return TAMBAR. I noticed you used rand() % (TAMBAR-1) in your shuffle function.)

    Quote Originally Posted by ScorchedPsyche View Post
    Basically, I am including the prototypes file (baralho.h) into the main file (main.c) which includes the definition of the deck (baralho). But the problem is that when I call the function to shuffle (embaralhar()), the shuffling of the deck only happens inside the functions file, and when I try to call the deck on the main file, the deck is "reset". Why exactly is this happening?
    You have three files, main.c, functions.c (or whatever name you use for it), and baralho.h.

    The declaration of the deck is static card_t baralho[TAMBAR] = ... ; in the baralho.h file. Both main.c and functions.c have #include "baralho.h", and they are compiled separately. Right?

    The static keyword means the deck is only available in the current compilation unit -- the .c file only.

    Effectively, your functions.c and main.c have their own decks. The variables have the same name, but they are two different arrays.

    If you just remove the static keyword, you'll still try to initialize it twice.

    The correct solution is to use baralho.h only for the declarations:
    Code:
    #ifndef   BARALHO_H
    #define   BARALHO_H
    
    #define   TAMBAR             108
    
    #define   COR_MASCARA        112U
         
    #define   COR_PRETO            0U
    #define   COR_VERMELHO        16U
    #define   COR_VERDE           32U
    #define   COR_AMARELO         48U
    #define   COR_AZUL            64U
      
    #define   RANK_MASCARA        15U
      
    #define   RANK_ZERO            0U
    #define   RANK_UM              1U
    #define   RANK_DOIS            2U
    #define   RANK_TRES            3U
    #define   RANK_QUATRO          4U
    #define   RANK_CINCO           5U
    #define   RANK_SEIS            6U
    #define   RANK_SETE            7U
    #define   RANK_OITO            8U
    #define   RANK_NOVE            9U
      
    #define   RANK_PULAR          10U
    #define   RANK_PESCARDOIS     11U
    #define   RANK_INVERTER       12U
      
    #define   RANK_CORINGA        13U
    #define   RANK_CORINGAQUATRO  14U
      
    #define   RANK(carta)   ((carta) & RANK_MASCARA)
    #define   COR(carta)    ((carta) & COR_MASCARA)
     
    
    /* Card type definition */
    typedef unsigned char  card_t;
    
    /* We have one deck of cards initialized, read-only */
    extern const card_t  uno[TAMBAR];
    
    /* Shuffle function, works on any deck of card_t's */
    extern void embaralhar(card_t *, size_t);
    
    #endif /* BARALHO_H */
    The outermost #ifndef .. #endif is there so you can #include this in other #include files if they happen to need for example the card constants. This way the constants are defined only once, even if included more than once (say, because both .c and another .h file included by the .c file includes the above header file).

    The initialization of the uno deck you put in a separate file, say baralho.c:
    Code:
    #include <stdlib.h>
    #include "baralho.h"
    
    const card_t  uno[TAMBAR] = {
     
        COR_VERMELHO | RANK_ZERO,
        COR_VERMELHO | RANK_UM,         COR_VERMELHO | RANK_UM,
        COR_VERMELHO | RANK_DOIS,       COR_VERMELHO | RANK_DOIS,
        COR_VERMELHO | RANK_TRES,       COR_VERMELHO | RANK_TRES,
        COR_VERMELHO | RANK_QUATRO,     COR_VERMELHO | RANK_QUATRO,
        COR_VERMELHO | RANK_CINCO,      COR_VERMELHO | RANK_CINCO,
        COR_VERMELHO | RANK_SEIS,       COR_VERMELHO | RANK_SEIS,
        COR_VERMELHO | RANK_SETE,       COR_VERMELHO | RANK_SETE,
        COR_VERMELHO | RANK_OITO,       COR_VERMELHO | RANK_OITO,
        COR_VERMELHO | RANK_NOVE,       COR_VERMELHO | RANK_NOVE,
        COR_VERMELHO | RANK_PULAR,      COR_VERMELHO | RANK_PULAR,
        COR_VERMELHO | RANK_PESCARDOIS, COR_VERMELHO | RANK_PESCARDOIS,
        COR_VERMELHO | RANK_INVERTER,   COR_VERMELHO | RANK_INVERTER,
      
        COR_VERDE | RANK_ZERO,
        COR_VERDE | RANK_UM,            COR_VERDE | RANK_UM,
        COR_VERDE | RANK_DOIS,          COR_VERDE | RANK_DOIS,
        COR_VERDE | RANK_TRES,          COR_VERDE | RANK_TRES,
        COR_VERDE | RANK_QUATRO,        COR_VERDE | RANK_QUATRO,
        COR_VERDE | RANK_CINCO,         COR_VERDE | RANK_CINCO,
        COR_VERDE | RANK_SEIS,          COR_VERDE | RANK_SEIS,
        COR_VERDE | RANK_SETE,          COR_VERDE | RANK_SETE,
        COR_VERDE | RANK_OITO,          COR_VERDE | RANK_OITO,
        COR_VERDE | RANK_NOVE,          COR_VERDE | RANK_NOVE,
        COR_VERDE | RANK_PULAR,         COR_VERDE | RANK_PULAR,
        COR_VERDE | RANK_PESCARDOIS,    COR_VERDE | RANK_PESCARDOIS,
        COR_VERDE | RANK_INVERTER,      COR_VERDE | RANK_INVERTER,
      
        COR_AMARELO | RANK_ZERO,
        COR_AMARELO | RANK_UM,          COR_AMARELO | RANK_UM,
        COR_AMARELO | RANK_DOIS,        COR_AMARELO | RANK_DOIS,
        COR_AMARELO | RANK_TRES,        COR_AMARELO | RANK_TRES,
        COR_AMARELO | RANK_QUATRO,      COR_AMARELO | RANK_QUATRO,
        COR_AMARELO | RANK_CINCO,       COR_AMARELO | RANK_CINCO,
        COR_AMARELO | RANK_SEIS,        COR_AMARELO | RANK_SEIS,
        COR_AMARELO | RANK_SETE,        COR_AMARELO | RANK_SETE,
        COR_AMARELO | RANK_OITO,        COR_AMARELO | RANK_OITO,
        COR_AMARELO | RANK_NOVE,        COR_AMARELO | RANK_NOVE,
        COR_AMARELO | RANK_PULAR,       COR_AMARELO | RANK_PULAR,
        COR_AMARELO | RANK_PESCARDOIS,  COR_AMARELO | RANK_PESCARDOIS,
        COR_AMARELO | RANK_INVERTER,    COR_AMARELO | RANK_INVERTER,
      
        COR_AZUL | RANK_ZERO,
        COR_AZUL | RANK_UM,             COR_AZUL | RANK_UM,
        COR_AZUL | RANK_DOIS,           COR_AZUL | RANK_DOIS,
        COR_AZUL | RANK_TRES,           COR_AZUL | RANK_TRES,
        COR_AZUL | RANK_QUATRO,         COR_AZUL | RANK_QUATRO,
        COR_AZUL | RANK_CINCO,          COR_AZUL | RANK_CINCO,
        COR_AZUL | RANK_SEIS,           COR_AZUL | RANK_SEIS,
        COR_AZUL | RANK_SETE,           COR_AZUL | RANK_SETE,
        COR_AZUL | RANK_OITO,           COR_AZUL | RANK_OITO,
        COR_AZUL | RANK_NOVE,           COR_AZUL | RANK_NOVE,
        COR_AZUL | RANK_PULAR,          COR_AZUL | RANK_PULAR,
        COR_AZUL | RANK_PESCARDOIS,     COR_AZUL | RANK_PESCARDOIS,
        COR_AZUL | RANK_INVERTER,       COR_AZUL | RANK_INVERTER,
      
        COR_PRETO | RANK_CORINGA,
        COR_PRETO | RANK_CORINGA,
        COR_PRETO | RANK_CORINGA,
        COR_PRETO | RANK_CORINGA,
      
        COR_PRETO | RANK_CORINGAQUATRO,
        COR_PRETO | RANK_CORINGAQUATRO,
        COR_PRETO | RANK_CORINGAQUATRO,
        COR_PRETO | RANK_CORINGAQUATRO
    };
    
    void embaralhar(card_t *deck, size_t cards)
    {
        size_t  i = cards;
    
        while (i > 1) {
            const size_t  k = rand() % i;
            const card_t  temp = deck[k];
            i--;
    
            deck[k] = deck[i];
            deck[i] = temp;
        }
    }
    Note the rewritten shuffle function. It is a basic Fisher-Yates shuffle, Dustenfeld's variant (with O(n) time complexity).

    That shuffle function takes a deck and the number of cards as parameters, and shuffles those cards in the deck. You can use it to shuffle any deck, not just a full one.

    Finally, here is a test main() you can compile and run to see how it all works:
    Code:
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <time.h>
    #include "baralho.h"
    
    const char *card_name(const card_t card)
    {
        static char  buffer[128];
    
        switch (RANK(card)) {
        case RANK_ZERO:          strcpy(buffer, "zero");          break;
        case RANK_UM:            strcpy(buffer, "um");            break;
        case RANK_DOIS:          strcpy(buffer, "dois");          break;
        case RANK_TRES:          strcpy(buffer, "tres");          break;
        case RANK_QUATRO:        strcpy(buffer, "quatro");        break;
        case RANK_CINCO:         strcpy(buffer, "cinco");         break;
        case RANK_SEIS:          strcpy(buffer, "seis");          break;
        case RANK_SETE:          strcpy(buffer, "sete");          break;
        case RANK_OITO:          strcpy(buffer, "oito");          break;
        case RANK_NOVE:          strcpy(buffer, "nove");          break;
        case RANK_PULAR:         strcpy(buffer, "pular");         break;
        case RANK_PESCARDOIS:    strcpy(buffer, "pescardois");    break;
        case RANK_INVERTER:      strcpy(buffer, "inverter");      break;
        case RANK_CORINGA:       strcpy(buffer, "coringa");       break;
        case RANK_CORINGAQUATRO: strcpy(buffer, "coringaquatro"); break;
        default:                 strcpy(buffer, "?");             break;
        }
    
        switch (COR(card)) {
        case COR_VERMELHO: strcat(buffer, " vermelho"); break;
        case COR_VERDE:    strcat(buffer, " verde");    break;
        case COR_AMARELO:  strcat(buffer, " amarelo");  break;
        case COR_AZUL:     strcat(buffer, " azul");     break;
        }
    
        /* Because buffer is declared static, it remains between calls.
         * Therefore, we can return a pointer to it.
         * The next call to this function will overwrite the previous value.
        */
        return (const char *)buffer;
    }
    
    int main(void)
    {
        card_t  baralho[TAMBAR];
        int     i;
    
        /* Randomize using time in seconds. */
        srand(time(NULL));
    
        /* Copy original deck to b. */
        memcpy(baralho, uno, sizeof uno);
    
        /* Shuffle all the cards in baralho. */
        embaralhar(baralho, TAMBAR);
    
        /* Print the deck. */
        for (i = 0; i < TAMBAR; i++)
            printf("%3d. %s\n", i + 1, card_name(baralho[i]));
    
        return 0;
    }
    In the card_name() function, the static keyword means that buffer is in static storage. It exists in this compilation unit (not just within the function), but the name to it is only visible inside that function (because it was declared in that function).

    (If you use a non-static buffer, and return a pointer to it, you'll get undefined results. Non-static buffers do not exist outside the function; the pointer points to where the buffer was.)

    To all who speak Portuguese: sorry about the card name strings. I just converted the constants to lower case, and constructs the name using rank first.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. War card game
    By bigzcoder in forum C Programming
    Replies: 3
    Last Post: 04-19-2012, 09:38 PM
  2. Card game war
    By Dr Saucie in forum C Programming
    Replies: 3
    Last Post: 02-11-2010, 11:25 PM
  3. Card shuffle game
    By newbie30 in forum C Programming
    Replies: 5
    Last Post: 08-13-2009, 04:19 PM
  4. Card game help
    By aaroroge in forum Game Programming
    Replies: 9
    Last Post: 07-16-2005, 06:37 PM
  5. card game
    By rugger78 in forum C++ Programming
    Replies: 4
    Last Post: 12-07-2004, 04:50 PM

Tags for this Thread