views:

382

answers:

4

I'm implementing a card game in C. There are lots of types of cards and each has a bunch of information, including some actions that will need to be individually scripted associated with it.

Given a struct like this (and I'm not certain I have the syntax right for the function pointer)

struct CARD {
    int value;
    int cost;
    // This is a pointer to a function that carries out actions unique
    // to this card
    int (*do_actions) (struct GAME_STATE *state, int choice1, int choice2);
};

I would like to initialize a static array of these, one for each card. I'm guessing this would look something like this

int do_card0(struct GAME_STATE *state, int choice1, int choice2)
{
    // Operate on state here
}

int do_card1(struct GAME_STATE *state, int choice1, int choice2)
{
    // Operate on state here
}

extern static struct cardDefinitions[] = {
    {0, 1, do_card0},
    {1, 3, do_card1}
};
  1. Will this work, and am I going about this the right way at all? I'm trying to avoid huge numbers of switch statements.

  2. Do I need to define the 'do_cardN' functions ahead of time, or is there some way to define them inline in the initialization of the struct (something like a lambda function in python)?

  3. I'll need read-only access to cardDefinitions from a different file - is 'extern static' correct for that?

I know this is a lot of questions rolled into one but I'm really a bit vague about how to go about this.

Thanks.

Edit:

To be clear, my goal is to be able to do something like

int cost = cardDefinitions[cardNumber].cost;

or

int result = cardDefinitions[cardNumber].do_action(state, choice1, choice2);

Instead of using huge switch statements all over the place.

+7  A: 

Your approach is exactly right.

  1. This will work, and is a good way to avoid huge switch statements.
  2. You can't define functions inline in C, they each must have a unique name.
  3. extern is what you want, not static. Change your body to be this:

    struct CARD cardDefinitions[] = { 
        {0, 1, do_card0}, 
        {1, 3, do_card1} 
    }; 
    

    then in an appropriate header file:

    extern struct CARD cardDefinitions[];
    
Greg Hewgill
You can actually have "inline" functions in C. The word you're looking for is "anonymous functions". Furthermore, I'd add {0, 0, NULL} as the array's last element so you don't need to store its size separately.
Philip
@Philip Well, those "inlines" are entirely unrelated to what he's talking about, but yes, you're right. And I prefer the "sizeof(cardDefinitions)/sizeof(cardDefinitions[0])" trick for cases like this
Michael Mrozek
@Michael: This trick doesn't work if you declare "extern struct CARD cardDefinitions[];".
Philip
+1  A: 
  1. That should work fine. It seems like you'd have a lot of functions if you're doing one per card, but maybe this particular game requires that level of control

  2. You can't define them inline, but you can just do a forward declaration. You need to do &func_name in the struct initialization though

  3. No; extern means a variable is declared in another file, so it doesn't make sense to have an extern variable that you're declaring at that location. Also, static means the variable is only accessible from the current file, which is the opposite of what you want. Making it read-only would require a getter function, but if you just want to make it accessible from another file declare it normally here (struct cardDefinitions[] = {...}) and in the other file use an extern (extern struct cardDefinitions[];)

Michael Mrozek
caf
1. Yeah, the game ("Dominion") is pretty ridiculous like that2. Thanks for clarifying the extern bit, that makes a lot more sense. My approach was just throw keywords at it until it worked...
russell_h
Oh wow, I actually started an implementation of that game a while ago, but decided it probably wouldn't be nearly as much fun as IRL. You probably don't need separate functions for every card, you can just define properties for how much money a card gives you, how many cards it makes you draw, how many buys and actions you get, and how many VPs it's worth, and that will cover most of the cases
Michael Mrozek
Michael Mrozek
caf
@Michael this is actually a class assignment (implement a specified subset of all three Dominion games), so in all probability you know more about it than I do. Like you say, every card has certain common parameters (draw x cards, etc). But nearly every "Kingdom Card" seems to describe some action like "Trash this card. Gain a card costing up to 5" - Looking over the list of cards I can't find a good way to generalize these, at least not given the API I have to implement.
russell_h
+1  A: 

Your approach is right and will work. Your function pointer syntax is right, except that you don't use parameter names - just types:

int (*do_actions)(struct GAME_STATE *, int, int);
caf
A: 

Team, One of my friends suggested me to go with above method for my requirement.

Could someone let me know when do we usually use above kind of design?

Philip, can you clarify below point. "I'd add {0, 0, NULL} as the array's last element so you don't need to store its size separately". We store the size of the array here? if so, when do we use it?

How is this approach related to switch case?

Can somebody help me in understanding this approach?

Teja
Teja: Welcome to SO. This isn't a traditional forum, so you should either add comments to existing question/answer to continue a more traditional discussion, or (better) start a new question that refers to this one and asks specific questions about code design there.
quixoto