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.