Find the answer to your Linux question:
Results 1 to 8 of 8
Code: struct Players { int enemy, ally; int player_id; int health_points, magic_points, experience_points; int level, strength, stamina, agility, speed, wisdom, intelligence; char player_name[10]; }; I've got this structure, and some ...
  1. #1
    Linux Newbie SagaciousKJB's Avatar
    Join Date
    Aug 2007
    Location
    Yakima, WA
    Posts
    162

    Random access to structure members in C.

    Code:
    struct Players
    {
           int enemy, ally;
    
           int player_id;
    
           int health_points, magic_points, experience_points;
    
           int level, strength, stamina, agility, speed, wisdom, intelligence;
    
           char player_name[10];
    };
    I've got this structure, and some code to use it with both the structure and pointers to it.

    The real problem I'm having is trying to figure out a way to access the structure members at "random", or in other words, without having to explicitly write the names in the source code. Is there anyway to use variable substitution or something like it?

    For example, if I made my structures and their pointers in this way

    Code:
    struct Players player1;
    struct Players *player1_ptr = &player1;
    Then is there a way to use a random integer to substitute 1? Something like this...

    Code:
    attack(QK_PUNCH, player[i]_ptr, player[i]_ptr)
    Now, I'm borrowing from the ways arrays work just to demonstrate what I mean. In any case, as you can see, if "i" were to replaced by a random integer, then which player is being attacked wouldn't need to be scripted in such an explicit manner.

    I realize that the structure names are like variables names and can't have variables substitute part of their name, but I'm wondering if there is any other way I can facilitate choosing "random" members of the structure to do some kind of AI.

    I've been using the following to do testing, so if you could give an example using this code it would be much easier to understand.

    small_pof.c
    Code:
    #include <stdio.h>
    #include "players.h"
    #include "attacks.h"
    
    void print_vitals(void);
    struct Players player1, player2, player3;
    struct Players *player1_ptr = &player1, *player2_ptr = &player2, *player3_ptr = &player3;
    
    int main()
    {
    
            init_player(player1_ptr, 0, 0, 0);
            init_player_vitals(player1_ptr, 100, 100, 100);
    
            init_player(player2_ptr, 1, 0, 1);
            init_player_vitals(player2_ptr, 100, 100, 100);
    
            init_player(player3_ptr, 0, 1, 2);
            init_player_vitals(player3_ptr, 100, 100, 100);
    
            printf("\nBefore anyone attacks\nHP\tMP\tEXP\n");
    
            print_vitals();
    
            printf("\nLet's have Player1 be attacked once\n");
    
            attack(QK_PUNCH, player3_ptr, player1_ptr);
    
            print_vitals();
    
            printf("\nThen let's say Player1 kills Player3");
    
            attack(100, player1_ptr, player3_ptr);
    
            print_vitals();
    
            printf("\nThen let's have Player1 kill his ally Player2\n");
    
            attack(100, player1_ptr, player2_ptr);
    
            print_vitals();
    
            return 0;
    }
    
    void print_vitals(void)
    {
            printf("\nPlayer1\n%i\t%i\t%i\n", player1.health_points, player1.magic_points, player1.experience_points);
            printf("\nPlayer2\n%i\t%i\t%i\n", player2.health_points, player2.magic_points, player2.experience_points);
            printf("\nPlayer3\n%i\t%i\t%i\n", player3.health_points, player3.magic_points, player3.experience_points);
    }
    attacks.h
    Code:
    #define QK_PUNCH 1
    #define QK_KICK 2
    #define ST_PUNCH 5
    #define ST_KICK 10
    #define SPC_ATT 25
    
    void attack(int damage, struct Players *attacker, struct Players *target)
    {
             target->health_points -= damage;
    
            if(target->health_points == 0)
            {
                    /*target has been defeated*/
    
                    if(target->enemy == TRUE && attacker->enemy == FALSE)
                    {
                            target->experience_points--;
                            attacker->experience_points++;
                    }
                    else if(target->enemy == FALSE && attacker->enemy == TRUE)
                    {
                            target->experience_points--;
                            attacker->experience_points++;
                    }
                    else if(target->enemy == FALSE && attacker->ally == TRUE)
                    {
                            target->experience_points--;
                            attacker->experience_points++;
                    }
                    else if(target->enemy == FALSE && ( attacker->ally == FALSE && attacker->enemy == FALSE) )
                    {
                            target->experience_points--;
                            attacker->experience_points--;
                    }
    
            }
    }
    players.h
    Code:
    #define TRUE 1
    #define FALSE 0
    
    struct Players
    {
           int enemy, ally;
    
           int player_id;
    
           int health_points, magic_points, experience_points;
    
           int level, strength, stamina, agility, speed, wisdom, intelligence;
    
           char player_name[10];
    };
    
    void init_player_vitals(struct Players *player_struct, int health_points, int magic_points, int experience_points)
    {
            player_struct->health_points = health_points;
            player_struct->magic_points = magic_points;
            player_struct->experience_points = experience_points;
    
    }
    
    void init_player_atts(struct Players *player_struct, int level, int strength, int stamina, int agility, int speed, int wisdom, int intelligence)
    {
            player_struct->level = level;
            player_struct->strength = strength;
            player_struct->stamina = stamina;
            player_struct->agility = agility;
            player_struct->speed = speed;
            player_struct->wisdom = wisdom;
            player_struct->intelligence = intelligence;
    }
    
    void init_player(struct Players *player_struct, int ally, int enemy, int player_id)
    {
            player_struct->ally = ally;
            player_struct->enemy = enemy;
            player_struct->player_id = player_id;
    }

  2. #2
    Trusted Penguin Cabhan's Avatar
    Join Date
    Jan 2005
    Location
    Seattle, WA, USA
    Posts
    3,230
    You actually have the answer to your question in your own post:
    Now, I'm borrowing from the ways arrays work just to demonstrate what I mean. In any case, as you can see, if "i" were to replaced by a random integer, then which player is being attacked wouldn't need to be scripted in such an explicit manner.
    Arrays are exactly what you're looking for here. Instead of having an explicit player1, player2, and player3, have a 3-element array of players, and now you can use any integer to access them!
    DISTRO=Arch
    Registered Linux User #388732

  3. #3
    Linux Newbie SagaciousKJB's Avatar
    Join Date
    Aug 2007
    Location
    Yakima, WA
    Posts
    162
    I suppose, although later I would have the problem of having a maximum amount of players.

    The only question I have now is, what type of array would it be? I can only assume it would be an array of structures, but I don't really even know what that would look like or how it would work.

    Going off of my hazy memory, wouldn't it be something like

    Code:
    struct Players
    {
           int enemy, ally;
    
           int player_id;
    
           int health_points, magic_points, experience_points;
    
           int level, strength, stamina, agility, speed, wisdom, intelligence;
    
           char player_name[10];
    };
    
    struct Players players[16];
    That should make the players, and their members, accessible like so...

    Code:
    player[i].health_points = 100;
    Right? I'm away from my source code or a compiler, so I can't test right now.

    It also begs the question how to use the pointers to the structures. Something like

    Code:
    *player_ptr[i] = &player[i];
    doesn't seem to work.

  4. #4
    Trusted Penguin Cabhan's Avatar
    Join Date
    Jan 2005
    Location
    Seattle, WA, USA
    Posts
    3,230
    What problem of a maximum number of players? An array allows you to have as many entries as memory allows, as opposed to the number that you care to code in. You can always limit the number of players by having a check to make sure that you don't exceed some number.

    It would look something like this:
    Code:
    struct Player
    {
           int enemy, ally;
           int player_id;
           int health_points, magic_points, experience_points;
           int level, strength, stamina, agility, speed, wisdom, intelligence;
           char player_name[10];
    };
    
    struct Player players[MAX_PLAYERS]; /* array of players */
    
    struct Player *player_ptrs[MAX_PLAYERS]; /* array of pointers */
    
    ...
    
    populate_players();
    
    for(i = 0; i < MAX_PLAYERS; i++)
        player_ptrs[i] = &players[i];
    I'm not sure why you have an array of players and an array of pointers, though. Why not just have an array of pointers to players? I don't see what you gain by the duplication.
    DISTRO=Arch
    Registered Linux User #388732

  5. #5
    Linux Newbie SagaciousKJB's Avatar
    Join Date
    Aug 2007
    Location
    Yakima, WA
    Posts
    162
    Quote Originally Posted by Cabhan View Post
    What problem of a maximum number of players? An array allows you to have as many entries as memory allows, as opposed to the number that you care to code in. You can always limit the number of players by having a check to make sure that you don't exceed some number.

    It would look something like this:
    Code:
    struct Player
    {
           int enemy, ally;
           int player_id;
           int health_points, magic_points, experience_points;
           int level, strength, stamina, agility, speed, wisdom, intelligence;
           char player_name[10];
    };
    
    struct Player players[MAX_PLAYERS]; /* array of players */
    
    struct Player *player_ptrs[MAX_PLAYERS]; /* array of pointers */
    
    ...
    
    populate_players();
    
    for(i = 0; i < MAX_PLAYERS; i++)
        player_ptrs[i] = &players[i];
    I'm not sure why you have an array of players and an array of pointers, though. Why not just have an array of pointers to players? I don't see what you gain by the duplication.
    Well, I suppose having a large array for players would work, but it just seemed sloppy usage of memory; but even if I had 10000 players, that would still be an insignificant amount.

    I'm not sure what you mean with having both an array of players and an array of pointers. Wouldn't I need to have the player structures first to point to them, and thus need them both to be arrays? I don't think I understood you.

    The reason I want pointers to structures is so I can manipulate the members of two or more of them in a function, as opposed to sending one structure at a time to a function and returning it as a value, if you're asking why the pointers to them.

  6. #6
    Linux Guru
    Join Date
    Nov 2007
    Location
    Córdoba (Spain)
    Posts
    1,513
    If you don't want to have a static array, you can implement a linked list of players, and allocate the nodes dynamically, using malloc for example. It's the most efficient way to achieve this, regarding ram usage.

  7. #7
    Trusted Penguin Cabhan's Avatar
    Join Date
    Jan 2005
    Location
    Seattle, WA, USA
    Posts
    3,230
    The eternal tradeoff: arrays give you random access, but linked list give you indeterminate size.

    One possible solution would be to start at some size, and then whenever it fills up, use realloc() to grow the array. It's still not ideal, but you get the random access and the ability to grow (with the possibility of a slight hit whenever you grow).

    As for the pointer thing:
    Code:
    player_ptrs[i] = malloc(sizeof(struct Player));
    player_ptrs[i]->player_id = nextID();
    malloc() will allocate memory and return a pointer to it. This will use the same amount of space as your first approach, with two advantages:
    - The space for the actual struct doesn't have to be contiguous
    - You don't have a whole array floating around whose sole purpose is to be pointed to.

    Does this make sense?
    DISTRO=Arch
    Registered Linux User #388732

  8. #8
    Trusted Penguin Roxoff's Avatar
    Join Date
    Aug 2005
    Location
    Nottingham, England
    Posts
    3,392
    A couple of suggestions. First, don't use 'struct Player' use a typedef, it makes conceptualisation easier:

    Code:
    struct tagPlayers
    {
           int enemy, ally;
           int player_id;
           int health_points, magic_points, experience_points;
           int level, strength, stamina, agility, speed, wisdom, intelligence;
           char player_name[10];
    } Players;
    Then you only need to use:

    Code:
    Players * player;
    players players[100];
    ...etc...
    Cabhan is right about using linked lists, it's probably better and easier to manage. You may, of course, find the STL collection classes here useful, but that would mean moving to C++. But then again anything that uses instances of structures naturally fits nicely with an object oriented approach.

    If your players list is sorted in some way, you may also want to consider arranging your data in a tree format, i.e. a node has a 'left' and 'right' item, rather than just a 'next' item like you get with a linked list.

    If you're using unsorted lists and want to be able to walk the list quicker, consider a doubly-linked list, i.e. one which can be walked in either direction - store a 'prev' and 'next' pointer in each node.

    If you have _lots_ of these 'player' instances and only want to manipulate a few at a time, you could write them to a file and load in only the ones you're going to manipulate. If you're doing lots of list-walking the start-up overhead of the load process is far outweighed by the time saved not having to continually walk down a huge list.

    Consider a hash table, where a key field is hashed in some way to find it's entry quickly in a separate table. This can allow quick access to the array data, but could be problematic if the list becomes large and has to be re-hashed.
    Linux user #126863 - see http://linuxcounter.net/

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
...