0

In this task i should simulate a simple dice rolling game using only pthread barriers for synchronization. The game is started with n players, specified as a command line argument. Each round all of the players roll a die (generate a random number between 1 and 6 for this). The player(s) with the smallest score are then eliminated from the game. If all remaining players roll the same number, the round is repeated until a winner has emerged. The game is finished if only one player is left.

With some restrictions -The main thread only does setup and cleanup. -Each player is simulated by one dedicated thread. -One of the players (not the main thread) determines which player(s) are eliminated. I should use pthread_barrier_wait's return value for this. -No global variables

#define _POSIX_C_SOURCE 200112L
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <stdatomic.h>



#define ERANGE_ERROR 22

typedef struct {
    bool *valid_player;
    pthread_barrier_t barrier;
    int *index;
    int game_over;
    int num_players;
    volatile int *rolls;
} game_state;

void* thread_func(void* arg){
    game_state *gs = (game_state *)arg;
    int index = (int*) arg;
    atomic_int players_alive =gs->num_players;
    while(gs->game_over==0){

        if(gs->valid_player[index]){
            gs->rolls[index] = rand()%6+1;
        }

        if (pthread_barrier_wait(&gs->barrier)==PTHREAD_BARRIER_SERIAL_THREAD){
            for(int i =0; i < gs->num_players;i++){
                if(gs->valid_player[i]){
                    printf("Player %d rolled a %d\n",i,gs->rolls[i]);
                }
            }

            int min_value =7;
            for(int i =1; i< gs->num_players;i++){
                if(min_value>gs->rolls[i]){
                    min_value = gs->rolls[i];
                }
            }

            for(int i =1; i< gs->num_players;i++){
                if(gs->rolls[i]==min_value){
                    printf("Eliminating player %d\n", i);
                    gs->valid_player[i]=false;
                    gs->rolls[i] = -1;
                    players_alive--;
                }
            }

            printf("--------------------\n");
            if (players_alive<=1){                      //checking players remaining
                if (players_alive<1){
                    printf("All players were eliminated!\n");
                }
                else{
                    for(int i =0; i < gs->num_players;i++){
                        if(gs->valid_player[i]==true){
                            printf("Player %d has won the game!\n", i);
                        }
                    }
                }
            }
        }
        pthread_barrier_wait(&gs->barrier);
    }
    return NULL;

}


int main(int argc, const char** argv)
{
    if (argc != 2) {
        printf("Invalid number of arguments\n");
        return EXIT_FAILURE;
    }
    //reading in the 2 inputs from the command line and saving them as integers + error handling if the are no integers
    char *endptr;
    errno = 0;
    srand(time(NULL));

    unsigned long int num_players = strtoul(argv[1],&endptr,10);
    if(num_players == 0 && strcmp(argv[1], "0") != 0)
    {
        perror("Invalid input for num_players");
        return EXIT_FAILURE;
    }
    if (*endptr != '\0')
    {
        perror("Error: argument is not a valid integer");
        return EXIT_FAILURE;
    }
    if (errno == ERANGE)
    {
        perror("Operand out of range");
        return ERANGE_ERROR;
    }

    if (num_players <= 0)
    {
        printf("Invalid input: %s \n", argv[1]);
        return EXIT_FAILURE;
    }
    //int num_players = strtoint(argv[1]);

    /*if (num_players == 0 && strcmp(argv[1], "0") != 0) {
        printf("Invalid input: %s\n", argv[1]);
        return 1;
    }*/
    printf("Number of players: %ld\n", num_players);
    //pthread_barrier_t barrier;
    //pthread_barrier_init(&barrier, NULL, num_players); //Missing potential error handling
    //malloc the game state
    pthread_t threads[num_players];
    game_state *gs=malloc(sizeof(game_state)+num_players*sizeof(int));
    if (gs == NULL) {
        fprintf(stderr, "Could not allocate memory.\n");
        free(gs);
        exit(EXIT_FAILURE);
    }
    gs->rolls = (volatile int*)malloc(num_players * sizeof(volatile int));
    if (gs->rolls == NULL) {
        fprintf(stderr, "Could not allocate memory for rolls.\n");
        free(gs);
        exit(EXIT_FAILURE);
    }
    gs->valid_player = (bool*)malloc(sizeof(bool)*num_players);
    if (gs->valid_player == NULL) {
        fprintf(stderr, "Could not allocate memory for valid_player.\n");
        free(gs);
        exit(EXIT_FAILURE);
    }
    gs->index = (int*)malloc(num_players * sizeof(int));
    if (gs->index == NULL) {
        fprintf(stderr, "Could not allocate memory for rolls.\n");
        free(gs);
        exit(EXIT_FAILURE);
    }

    //initializing the game state
    //set all rolls to 0 to indicate that they have not been rolled yet
    if (pthread_barrier_init(&gs->barrier, NULL, num_players) !=0){

        perror("pthread_barrier_init");
        exit(EXIT_FAILURE);
    } 

    gs->game_over=0;
    gs->num_players=num_players;
    for (int i = 0; i < num_players; i++)
    {
        gs->rolls[i] = 0;
        gs->valid_player[i] = true;
        //gs->index[i]=i;
        if(pthread_create(&threads[i], NULL, thread_func,&gs->index[i])!=0)
        {
            perror("pthread_create");
            exit(EXIT_FAILURE);
        }
    }

    //play the game unitl there is only one player left and then finish it
    for (uint32_t i = 0; i < num_players; i++)
    {
        if(pthread_join(threads[i], NULL))
        {
            fprintf(stderr, "Error: pthread_create failed\n");
            exit(EXIT_FAILURE);
        }
    }
    pthread_barrier_destroy(&gs->barrier);
    free(gs->index);
    free(gs->rolls);
    free(gs->valid_player);
    return EXIT_SUCCESS;  
}

I tried several different types of mem allocation but i apparently was not smart enough and still get segemntation faults.

Pur
  • 1
  • perhaps not a problem, but `*sizeof(int)` in `game_state *gs=malloc(sizeof(game_state)+num_players*sizeof(int));` is weird. That should be `game_state *gs=malloc(sizeof(game_state) * num_players);`, or just use a VLA like you did for the threads. – yano May 06 '23 at 17:10
  • `game_state *gs = (game_state *)arg;` is wrong .. you're passing the address of an `int` to `thread_func`, not a `game_state` pointer. Try `pthread_create(&threads[i], NULL, thread_func,gs+i)` to pass the `game_state` pointer. – yano May 06 '23 at 17:18

0 Answers0