What you're perhaps missing is holding some state for the zombies which can be more easily accessed than searching the map for the character 'Z'
. For example, hold the x,y coordinates in a structure, and have an array of them to represent zombies.
struct position {
int x, y;
};
#define NUM_ZOMBIES 5;
struct position zombies[NUM_ZOMBIES];
Inside main, you would then create a simple loop around your current code for creating zombies, where instead of directly setting a character in the map to 'Z', you set the zombie's locations
for (int i=0; i<NUM_ZOMBIES; i++) {
int p1, p2;
srand(time(0));
do {
p1 = rand() % 48 - 1;
p2 = rand() % 48 - 1;
} while (map[p1][p2] != ' ');
zombies[i] = { .x = p1, .y = p2 };
}
For convenience, lets also use the position for the player too.
struct position player = {.x = x0, .y = y0 };
You would then change your Refresh function to draw the zombies onto it, by adding the zombies to the map before you print them it to screen.
for (int i=0; i<NUM_ZOMBIES; i++) {
q[[zombies[i].x][zombies[i].y] = 'Z';
}
So the final part is testing whether they are in range. You have another loop inside your main loop which similarly loops through all the zombies, compares the distance to the player, and if they are within the range you want, you begin to follow
for (int i=0; i<NUM_ZOMBIES; i++) {
if (in_range(zombies[i], player)) {
zombie[i] = move_towards(zombies[i], player);
}
}
I'll leave you to implement in_range and move_towards, but here's their prototypes to help you.
bool in_range(struct position zombie, struct position player);
struct position move_towards(struct position zombie, struct position player);