Dynamically allocate and initialize new object with 30% probability

Viewed 76

I'm writing a program that will simulate a randomized race between runners who are climbing up a mountain where dwarf orcs (dorcs) are coming down the mountain to attack the runners. It begins with two runners named harold and timmy at the bottom of the mountain. The runners make their way up the mountain in randomized moves where they may make progress forward up the mountain, or they may slide back down the mountain. Dorcs are randomly generated, and they inflict damage on a runner if they collide. The simulation ends when one of the runners reaches the top of the mountain, or when both runners are dead.

I'm struggling with a part where I have to implement the actual race loop. Once the race is initialized, the race loop will iterate until the race is over. This happens when either a winner has been declared, or when all runners are dead.

Every iteration of the race loop will do the following:

with 30% probability, dynamically allocate a new dorc as an EntityType structure, and initialize it as follows:

(a) a dorc’s avatar is always “d”

(b) each dorc begins the race at the top of the mountain, which is at row 2

(c) with equal probability, the dorc may be placed either in the same column as timmy, or in the same column as the harold, or in the column exactly half-way between the two

(d) add the new dorc to the race’s array of dorcs

(e) using the pthread_create() function, create a thread for the new dorc, and save the thread pointer in the dorc’s entity structure; the function that each dorc thread will execute is the void* goDorc(void*) function that you will implement in a later step; the parameter to the goDorc() function will be the EntityType pointer that corresponds to that dorc

I guess I'm confused with the logic of how to approach this. I decided to make a function called isOver() to indicate if the race is over, and then a separate function called addDorc() to initialize the Dorc elements and do all the requirements above.

In isOver(), I attempt to add a dorc object to the dorcs array by doing addDorc(race); with every iteration of the race loop/if the race hasn't ended or no one died. But I keep getting the error:

control.c:82:3: error: too few arguments to function ‘addDorc’
   addDorc(race);

The problem is I don't think I can manually declare all the parameters in addDorc() because some elements like the "path" argument are based on probability. As mentioned above, with equal probability, the dorc may be placed either in the same column as timmy, or in the same column as the harold, or in the column exactly half-way between the two. The issue is I don't know how to factor this random value when calling addDorc() and would appreciate some help. I also don't know if I'm doing the "with 30% probability, dynamically allocate a new dorc as an EntityType structure" correctly and would be grateful for some input on that as well.

defs.h

typedef struct {
  pthread_t thr;
  char avatar[MAX_STR];
  int  currPos; 
  int  path; 
} EntityType;

typedef struct {
  EntityType ent;
  char name[MAX_STR];
  int  health;
  int  dead; 
} RunnerType;

typedef struct {
  int numRunners;
  RunnerType *runners[MAX_RUNNERS];  
  int numDorcs;
  EntityType *dorcs[MAX_DORCS]; 
  char winner[MAX_STR]; 
  int  statusRow; 
  sem_t mutex;
} RaceInfoType;

void launch();
int addDorc(RaceInfoType*, char*, int, int);
int isOver(RaceInfoType*);
void initRunners(RaceInfoType*);
int addRunner(RaceInfoType*, char*, char*, int, int, int, int);

int  randm(int);

void *goRunner(void*);
void *goDorc(void*);

RaceInfoType *race;

control.c

void launch(){

    race = malloc(sizeof(RaceInfoType));
    race->numRunners = 0;
    
    initRunners(race);
    
    if (sem_init(&race->mutex, 0, 1) < 0) {
        printf("semaphore initialization error\n");
        exit(1);
    }
    
    strcpy(race->winner, " ");
    
    
    srand((unsigned)time(NULL));
    
    int i;
    for(i = 0; i < race->numRunners; ++i){
        pthread_create(&(race->runners[i]->ent.thr), NULL, goRunner, " ");
    }
    
    race->numDorcs = 0;
}



int addDorc(RaceInfoType* race, char *avatar, int path, int currPos){ 
    if(race->numDorcs == MAX_DORCS){ 
        printf("Error: Maximum dorcs already reached. \n");
        return 0;
    }       
    race->dorcs[race->numDorcs] = malloc(sizeof(EntityType));
    
    int timmysColumn = race->dorcs[race->numDorcs]->currPos;
    int haroldsColumn = race->dorcs[race->numDorcs]->currPos;
    int halfwayColumn = (timmysColumn+haroldsColumn)/2;
    
    int r = rand()%100;
    
    pthread_t dorc;
    
    if(r <= 30){
        strcpy(race->dorcs[race->numDorcs]->avatar, "d");
        race->dorcs[race->numDorcs]->currPos = 2;
        if(r <= 33){
            race->dorcs[race->numDorcs]->path = timmysColumn;
        }else if(r <= 66){
            race->dorcs[race->numDorcs]->path = haroldsColumn;
        }else{
            race->dorcs[race->numDorcs]->path = halfwayColumn;
        }
        pthread_create(&dorc, NULL, goDorc, " ");
    }
    race->numRunners++;
}

int isOver(RaceInfoType* race){ 
    int i;
    for(i = 0; i < race->numRunners; ++i){
        if((race->winner != " ") || (race->runners[race->numRunners]->dead = 1)){
            return 1;
        }
        addDorc(race);
        return 0;
    }
}
void initRunners(RaceInfoType* r){
    addRunner(r, "Timmy", "T", 10, 35, 50, 0); 
    addRunner(r, "Harold", "H", 14, 35, 50, 0); 
}


int addRunner(RaceInfoType* race, char *name, char *avatar, int path, int currPos, int health, int dead){
    if(race->numRunners == MAX_RUNNERS){ 
        printf("Error: Maximum runners already reached. \n");
        return 0;
    }       
    race->runners[race->numRunners] = malloc(sizeof(RunnerType));
    strcpy(race->runners[race->numRunners]->name, name);
    strcpy(race->runners[race->numRunners]->ent.avatar, avatar);
    race->runners[race->numRunners]->ent.path = path;
    race->runners[race->numRunners]->ent.currPos = currPos;
    race->runners[race->numRunners]->health = health;
    race->runners[race->numRunners]->dead = dead;
    race->numRunners++;
    return 1;
}
1 Answers

Caveat: Because there's so much missing [unwritten] code, this isn't a complete solution.

But, I notice at least two bugs: the isOver bugs in my top comments. And, incrementing race->numRunners in addDorc.

isOver also has the return 0; misplaced [inside the loop]. That should go as the last statement in the function. If you had compiled with -Wall [which you should always do], that should have been flagged by the compiler (e.g. control reaches end of non-void function)

From that, only one "dorc" would get created (for the first eligible runner). That may be what you want, but [AFAICT] you want to try to create more dorcs (one more for each valid runner).

Also, the bug the compiler flagged is because you're calling addDorc(race); but addDorc takes more arguments.

It's very difficult to follow the code when you're doing (e.g.) race->dorcs[race->numDorcs]->whatever everywhere.

Better to do (e.g.):

EntityType *ent = &race->dorcs[race->numDorcs];
ent->whatever = ...;

Further, it's likely that your thread functions would like a pointer to their [respective] control structs (vs. just passing " ").


Anyway, I've refactored your code to incorporate these changes. I've only tried to fix the obvious/glaring bugs from simple code inspection, but I've not tried to recompile or address the correctness of your logic.

So, there's still more work to do, but the simplifications may help a bit.

void
launch(void)
{

    race = malloc(sizeof(RaceInfoType));
    race->numRunners = 0;

    initRunners(race);

    if (sem_init(&race->mutex,0,1) < 0) {
        printf("semaphore initialization error\n");
        exit(1);
    }

    strcpy(race->winner," ");

    srand((unsigned)time(NULL));

    int i;
    for (i = 0;  i < race->numRunners;  ++i) {
        RunnerType *run = &race->runners[i];
        EntityType *ent = &run->ent;
        pthread_create(&ent->thr,NULL,goRunner,ent);
    }

    race->numDorcs = 0;
}

int
addDorc(RaceInfoType* race,char *avatar,int path,int currPos)
{
    if (race->numDorcs == MAX_DORCS) {
        printf("Error: Maximum dorcs already reached. \n");
        return 0;
    }

    EntityType *ent = malloc(sizeof(*ent));
    race->dorcs[race->numDorcs] = ent;

    int timmysColumn = ent->currPos;
    int haroldsColumn = ent->currPos;
    int halfwayColumn = (timmysColumn + haroldsColumn) / 2;

    int r = rand()%100;

#if 0
    pthread_t dorc;
#endif

    if (r <= 30) {
        strcpy(ent->avatar,"d");
        ent->currPos = 2;
        if (r <= 33) {
            ent->path = timmysColumn;
        } else if (r <= 66) {
            ent->path = haroldsColumn;
        } else {
            ent->path = halfwayColumn;
        }
        pthread_create(&ent->thr,NULL,goDorc,ent);
    }

#if 0
    race->numRunners++;
#else
    race->numDorcs += 1;
#endif
}

int
isOver(RaceInfoType* race)
{
    int i;

    for (i = 0;  i < race->numRunners;  ++i) {
#if 0
        if ((race->winner != " ") ||
            (race->runners[race->numRunners]->dead = 1))
            return 1;
#else
        RunnerType *run = &race->runners[i];
        if ((race->winner != " ") || (run->dead == 1))
            return 1;
#endif
        addDorc(race);
#if 0
        return 0;
#endif
    }

#if 1
    return 0;
#endif
}

void
initRunners(RaceInfoType* r)
{
    addRunner(r,"Timmy","T",10,35,50,0);
    addRunner(r,"Harold","H",14,35,50,0);
}

int
addRunner(RaceInfoType* race,char *name,char *avatar,int path,int currPos,
    int health,int dead)
{

    if (race->numRunners == MAX_RUNNERS) {
        printf("Error: Maximum runners already reached. \n");
        return 0;
    }

    RunnerType *run = malloc(sizeof(*run));
    race->runners[race->numRunners] = run;

    strcpy(run->name,name);

    EntityType *ent = &run->ent;
    strcpy(ent->avatar,avatar);
    ent->path = path;
    ent->currPos = currPos;

    run->health = health;
    run->dead = dead;

    race->numRunners++;

    return 1;
}

UPDATE:

I noticed in addDorc(), you put pthread_t dorc; in an if statement. I don't quite understand what my if statement is actually supposed to be checking though.

I forgot to mention/explain. I wrapped your/old code and my/new code with preprocessor conditionals (e.g.):

#if 0
// old code
#else
// new code
#endif

After the cpp stage, the compiler will only see the // new code stuff. Doing this was an instructional tool to show [where possible] what code you had vs what I replaced it with. This was done to show the changes vs. just rewriting completely.

If we never defined NEVERWAS with a #define NEVERWAS, then the above block would be equivalent to:

#ifdef NEVERWAS
// old code ...
#else
// new code
#endif

Would it still be under the if(r <= 30) part like I did in my original code?

Yes, hopefully now, it is more clear. #if is a cpp directive to include/exclude code (as if you had edited that way). But, a "real" if is an actual executable statement that is evaluated at runtime [as it was before], so no change needed.

My other concern is it doesn't look like dorc is used anywhere in the function because you write pthread_create(&ent->thr,NULL,goDorc,ent); which seems to use ent instead?

That is correct. It is not used/defined and the value goes to ent->thr. As you had it, the pthread_t value set by pthread_create would be lost [when dorc goes out of scope]. So, unless it's saved somewhere semi-permanent (e.g. in ent->thr), there would be no way to do a pthread_join call later.

Related