C - 打乱结构体数组

标签 c arrays pointers struct

我正在用 C 语言为学校做一个项目,基本上我需要做的是创建一个代理网格(人类、僵尸或没有),并随机选择哪些是我手动控制的或由“AI”控制的。基本上,人类需要逃离僵尸,而僵尸则必须追逐人类来感染它们,当没有人类留下时游戏结束。

问题是,在每一回合之前,应该随机选择谁先玩,为此我需要对代理进行洗牌(不接触网格,因为代理位置保持不变,唯一改变的是他们在游戏中的位置)我应该用它来选择谁先玩)。

我在随机播放方面遇到了一些麻烦,因为我调用了该函数,并且在对代理进行随机播放后,我打印了他们的 ID。它只是给了我很多很多的 0 和一些随机数字,比如 3、6、10 等等,还有一些。

代码如下:

主文件:

#include "showworld.h"
#include "example.h"
#include "shuffle.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/** Horizontal world size. */
#define WORLD_X 20

/** Vertical world size. */
#define WORLD_Y 20

/**
 * Structure defining agent properties.
 *
 * @note This is an example which will probably not work in a fully functional
 * game. Students should develop their own implementation of
 * ::get_agent_info_at() and agent/world data structures.
 * */
typedef struct {
    AGENT_TYPE type;        /**< Agent type.        */
    unsigned char playable; /**< Is agent playable? */
    unsigned short id;      /**< Agent ID.          */
} AGENT;

/**
 * Structure defining world properties.
 *
 * @note This is an example which will probably not work in a fully functional
 * game. Students should develop their own implementation of
 * ::get_agent_info_at() and agent/world data structures.
 * */
typedef struct {
    AGENT *grid;        /**< World is a grid composed of agents. */
    unsigned int xsize; /**< Horizontal world size.              */
    unsigned int ysize; /**< Vertical world size.                */
} WORLD;

/* This function is an implementation of the definition provided by the
 * ::get_agent_info_at() function pointer. It only works for AGENT and WORLD
 * example structures defined in this file. */
unsigned int example_get_ag_info(void *world, unsigned int x, unsigned int y);
int main() {

    /* An instance of a WORLD structure. */
    WORLD my_world;

    /* An instance of a SHOWWORLD world display. */
    SHOWWORLD *sw = NULL;

    /* A by-dimensional array of agents, representing agents in a grid. */
    AGENT agent_grid[WORLD_X][WORLD_Y];

    /* Number of agents created so far. */
    unsigned int nagents = 0;

    /* Initialize world display. */
    sw = showworld_new(WORLD_X, WORLD_Y, example_get_ag_info);

    /* Initialize random number generator. */
    srand(time(NULL));

    /* **************************************************************** */
    /* Cycle through all cells in grid and randomly place agents in it. */
    /* **************************************************************** */
    for (int i = 0; i < WORLD_X; ++i) {
        for (int j = 0; j < WORLD_Y; ++j) {

            /* Possible agent in grid. By default we assume there is none. */
            AGENT ag = {None, 0, 0};

            /* Obtain a probability between 0 and 99. */
            unsigned char probability = rand() % 100;

            /* There is 10% probability of creating an agent. */
            if (probability < 10) {

                /* If we got here, an agent will be placed at (i,j). */

                /* Randomly define agent type. */
                ag.type = (rand() % 2 == 0) ? Human : Zombie;

                /* Give 10% probablity of agent being playable by user. */
                ag.playable = (rand() % 10 == 0);

                /* Assign agent ID and then increment number of agents so
                   far. */
                ag.id = nagents++;

            }

            /* Assign possible agent to grid at (i,j). */
            agent_grid[i][j] = ag;
        }
    }

    /* ******************************* */
    /* Populate the my_world variable. */
    /* ******************************* */

    /* A bidimensional array of agents can be interpreted as a pointer to
       agents. */
    my_world.grid = (AGENT *) agent_grid;

    /* World size is defined by constants in this example. */
    my_world.xsize = WORLD_X;
    my_world.ysize = WORLD_Y;

    /* ********************************************************************* */
    /* Show world using the simple_show_world() function. This function can  */
    /* be used in the first part of the project.                             */
    /* ********************************************************************* */
    showworld_update(sw, &my_world);

    shuffle(my_world.grid, nagents);

    /* Before finishing, ask user to press ENTER. */
    printf("Press ENTER to continue...");
    getchar();

    /* Destroy world display. */
    showworld_destroy(sw);

    /* Bye. */
    return 0;
}

/**
 * This function is an implementation of the ::get_agent_info_at() function
 * definition. It only works for ::AGENT and ::WORLD structures defined in this
 * example.
 *
 * It basically receives a pointer to a ::WORLD structure, obtains the AGENT
 * structure in the given coordinates, and returns the agent information in a
 * bit-packed `unsigned int`.
 *
 * @note This is an example which will probably not work in a fully functional
 * game. Students should develop their own implementation of
 * ::get_agent_info_at() and agent/world data structures.
 *
 * @param w Generic pointer to object representing the simulation world.
 * @param x Horizontal coordinate of the simulation world from where to fetch
 * the agent information.
 * @param y Vertical coordinate of the simulation world from where to fetch
 * the agent information.
 * @return An integer containing bit-packed information about an agent, as
 * follows: bits 0-1 (agent type), bit 2 (is agent playable), bits 3-18 (agent
 * ID). Bits 19-31 are available for student-defined agent extensions.
 * */
unsigned int example_get_ag_info(void *w, unsigned int x, unsigned int y) {

    /* The agent information to return. */
    unsigned int ag_info = 0;

    /* Convert generic pointer to world to a WORLD object. */
    WORLD *my_world = (WORLD *) w;

    /* Check if the given (x,y) coordinates are within bounds of the world. */
    if ((x >= my_world->xsize) || (y >= my_world->ysize)) {

        /* If we got here, then the coordinates are off bounds. As such we will
           report that the requested agent is of unknown type. No need to
           specify agent ID or playable status, since the agent is unknown. */
        ag_info = Unknown;

    } else {

        /* Given coordinates are within bounds, let's get and pack the request
           agent information. */

        /* Obtain agent at specified coordinates. */
        AGENT ag = my_world->grid[x * my_world->xsize + y];

        /* Is there an agent at (x,y)? */
        if (ag.type == None) {

            /* If there is no agent at the (x,y) coordinates, set agent type to
               None. No need to specify agent ID or playable status, since
               there is no agent here. */
            ag_info = None;

        } else {

            /* If we get here it's because there is an agent at (x,y). Bit-pack
               all the agent information as specified by the get_agent_info_at
               function pointer definition. */
            ag_info = (ag.id << 3) | (ag.playable << 2) | ag.type;

        }

    }

    /* Return the requested agent information. */
    return ag_info;
}

这是随机播放功能

#include "example.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void shuffle(AGENT *agents, unsigned int nagents) {
  printf("%s\n\n", "------------- Shuffling agents ----------------");
  unsigned int i=0;
  unsigned int j=0;
  AGENT temp;

  srand(time(NULL));

  for (i = nagents - 1; i > 0; i--) {
    j = (rand() % i);
    temp = agents[i];
    agents[i] = agents[j];
    agents[j] = temp;
  }

  for (i = 0; i < nagents; i++) {
    printf("\n\t%d", agents[i].id);
  }

这是 showworld 文件:

#include "showworld.h"
#include <stdio.h>
#include <stdlib.h>

/* The implementation of `SHOWWORLD` type used in this simple text-based world
 * visualization code. In this simple case, we only need to keep track of the
 * world dimensions and of the function pointer which knows how to read an
 * agent from the world data structure.
 *
 * For a more complex implementation, for example based on the g2 library,
 * it would also be necessary to keep the g2 device.
 * */
struct showworld {
    unsigned int xdim;
    unsigned int ydim;
    get_agent_info_at aginfo_func;
};

/* Create a new display/visualization object for the simulation world.
 *
 * This function obeys the `showworld_new()` prototype defined in
 * `showworld.h`. */
SHOWWORLD *showworld_new(
    unsigned int xdim,
    unsigned int ydim,
    get_agent_info_at aginfo_func) {

    SHOWWORLD *sw = NULL;
    sw = malloc(sizeof(SHOWWORLD));
    sw->xdim = xdim;
    sw->ydim = ydim;
    sw->aginfo_func = aginfo_func;
    return sw;

}

/* Destroy a display/visualization object for the simulation world.
 *
 * This function obeys the `showworld_destroy()` prototype defined in
 * `showworld.h`. */
void showworld_destroy(SHOWWORLD *sw) {
    free(sw);
}

/* Update the simulation world display/visualization.
 *
 * This function obeys the `showworld_update()` prototype defined in
 * `showworld.h`. */
void showworld_update(SHOWWORLD *sw, void *w) {

    printf("\n");

    /* Cycle through all the rows */
    for (unsigned int y = 0; y < sw->ydim; ++y) {

        /* Cycle through all the columns for the current row */
        for (unsigned int x = 0; x < sw->xdim; ++x) {

            /* Get state of the world (in bit packed fashion) using the user
               supplied function. */
            unsigned int item = sw->aginfo_func(w, x, y);

            /* Extract the agent type (2 bits). */
            AGENT_TYPE ag_type = item & 0x3;
            /* Extract whether the agent is playable (1 bit). */
            unsigned char playable = (item >> 2) & 0x1;
            /* Extract the agent ID (16 bits). */
            unsigned short ag_id = (item >> 3) & 0xFFFF;

            /* Determine the agent type. */
            switch (ag_type) {

                /* If no agent is present at (x,y) just print a dot. */
                case None:
                    printf(" .  ");
                    break;

                /* If human agent present at (x,y) print 'h' or 'H'. */
                case Human:
                    if (playable) {
                        /* Uppercase 'H' for player-controlled human agent. */
                        printf("H");
                    } else {
                        /* Lowercase 'h' for AI-controlled human agent. */
                        printf("h");
                    }
                    /* Print the agent ID in front of the 'h'/'H'. */
                    printf("%02X ", ag_id);
                    break;

                /* If zombie agent present at (x,y) print 'z' or 'Z'. */
                case Zombie:
                    if (playable) {
                        /* Uppercase 'Z' for player-controlled zombie agent. */
                        printf("Z");
                    } else {
                        /* Lowercase 'z' for AI-controlled zombie agent. */
                        printf("z");
                    }
                    /* Print the agent ID in front of the 'h'/'H'. */
                    printf("%02X ", ag_id);
                    break;

                /* Print '?' if unknown type detected. This should *never*
                   happen. */
                default:
                    printf("?   ");

            }
        }

        /* Print two newlines after each row. */
        printf("\n\n");

    }

    /* Print a newline after world is shown/updated. */
    printf("\n");

}

最佳答案

将所有代码剥离到相关的:

AGENT agent_grid[WORLD_X][WORLD_Y];
int nagents = populate_grid(agent_grid, WORLD_X, WORLD_Y, 10);
shuffle(agent_grid, nagents);

在这里,我删除了 my_world 以专注于网格,并创建了一个网格初始化函数,而不是进行内联初始化。假设的初始化函数 int populate_grid(AGENT *grid, int rows, int cols, int%) 用代理填充网格样本(其余的用 AGENT 对象填充)输入“无”)。然后它返回创建的样本大小。

然后,精确地使用对 shuffle 的调用来对网格进行打乱,该调用采用 AGENT 对象数组并对它们进行打乱。

从这个叙述中问题不是很明显吗? agent_grid 不是 shuffle 所期望的大小为 nagents 的数组。它是一个大小为 WORLD_X * WORLD_Y 的二维数组。实际上,这意味着您要对前 40 个(左右)网格槽进行洗牌,而其余 360 个则保持不变;我们可以预期这些网格槽中有 90% 是空的,这似乎与您描述的结果相符(“很多很多 0”)。

关于C - 打乱结构体数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48236807/

相关文章:

c - 赋值从 int 中生成指针 w/out a cast

c++ - 如何使用不同的指针算术语义

c - 将错误文本与 C 中的错误代码相关联

c - 从文件读取 IP 地址的最有效方法

javascript - 获取数组中出现次数相同(最高)的所有元素

PHP连接两个表,其中一个表中的两列包含from_ID和to_ID引用另一个表中的id

c - 如何填充两个数组删除重复的数字

C汇编代码

javascript - 数组添加额外的方括号

c++ - 当指针指向的对象被另一个类的实例删除时重新分配指针