/* * C program for Conway's ``game of life''. * * Input is from command line and (possibly) a file: * * Command-line arguments specify * (*) input source (more below) * (*) number of steps * (*) how often to print new board configuration (P means print results * every P steps) * (*) (optionally) output file for new board configurations * Input source can either be the name of an input file containing an * initial configuration or the keyword "random" and the following * parameters: * (*) size of board * (*) fraction of cells that should initially be "live" * (*) seed for generating random sequence * Output is the number of "live" cells initially and after each step, * and optionally (if an output file is specified) new board configurations * * If an input file is specified, it contains a representation of the initial * board configuration: N (size of board) and N*N values (each 0 or 1). * * Parallel version using OpenMP. */ #include #include #include #include #include "timer.h" #include /* * data structure for two-dimensional array of "short" * (what we really want here is bool, but MPI doesn't seem to support * sending those, so "short" seems like an okay substitute) */ typedef short array_type; typedef struct twoD_array { long rows; long cols; array_type ** elems; } twoD_array_t; /* function declarations (comments with code below) */ twoD_array_t * build_array(twoD_array_t * a, long rows, long cols); void free_array(twoD_array_t * a); bool read_board(FILE* infile, twoD_array_t *board); bool random_board(long size, double fraction, long seed, twoD_array_t *board); void update_board(twoD_array_t *board, twoD_array_t *new_board); void print_board(FILE* outfile, twoD_array_t *board); void clear_border(twoD_array_t *board); long live_cells(twoD_array_t *board); /* main */ int main(int argc, char* argv[]) { long steps, print_interval, size; FILE *outfile = NULL; twoD_array_t board1, board2; twoD_array_t *board = &board1; twoD_array_t *new_board = &board2; double start_time, init_done_time, compute_done_time, end_time; char* usage_fmt = "usage: %s input_args num_steps print_interval [outfile]\n" " (input_args is infile or 'random' boardsize fraction seed)\n"; char* end_ptr_for_strtol; start_time = get_time(); if (argc < 4) { fprintf(stderr, usage_fmt, argv[0]); exit(EXIT_FAILURE); } /* process command-line arguments and initialize board */ int steps_index; /* index of "steps" argument */ if (strcmp(argv[1], "random") != 0) { /* from file */ FILE* infile; infile = fopen(argv[1], "r"); if (infile == NULL) { fprintf(stderr, "unable to open input file %s\n", argv[1]); exit(EXIT_FAILURE); } steps_index = 2; if (!read_board(infile, board)) { fclose(infile); exit(EXIT_FAILURE); } size = board->rows-2; fclose(infile); printf("Input: board read from file %s\n", argv[1]); } else { /* with randomly-generated data */ long seed; double fraction; if (argc < 7) { fprintf(stderr, usage_fmt, argv[0]); exit(EXIT_FAILURE); } size = strtol(argv[2], &end_ptr_for_strtol, 10); if ((*end_ptr_for_strtol != '\0') || (size <= 0)) { fprintf(stderr, usage_fmt, argv[0]); exit(EXIT_FAILURE); } fraction = strtod(argv[3], &end_ptr_for_strtol); if ((*end_ptr_for_strtol != '\0') || (fraction < 0) || (fraction > 1)) { fprintf(stderr, usage_fmt, argv[0]); exit(EXIT_FAILURE); } seed = strtol(argv[4], &end_ptr_for_strtol, 10); if (*end_ptr_for_strtol != '\0') { fprintf(stderr, usage_fmt, argv[0]); exit(EXIT_FAILURE); } steps_index = 5; if (!random_board(size, fraction, seed, board)) exit(EXIT_FAILURE); printf("Input: " "'random' board of size %ld with fraction %f seed %ld\n", size, fraction, seed); } steps = strtol(argv[steps_index], &end_ptr_for_strtol, 10); if ((*end_ptr_for_strtol != '\0') || (steps <= 0)) { fprintf(stderr, usage_fmt, argv[0]); exit(EXIT_FAILURE); } print_interval = strtol(argv[steps_index+1], &end_ptr_for_strtol, 10); if ((*end_ptr_for_strtol != '\0') || (print_interval <= 0)) { fprintf(stderr, usage_fmt, argv[0]); exit(EXIT_FAILURE); } if (argc >= steps_index+3) { outfile = fopen(argv[steps_index+2], "w"); if (outfile == NULL) { fprintf(stderr, "cannot open output file %s\n", argv[steps_index+2]); exit(EXIT_FAILURE); } } printf("\n"); printf("%ld live cells initially\n", live_cells(board)); /* create "new board" and clear borders */ if (build_array(new_board, size+2, size+2) == NULL) { fprintf(stderr, "unable to allocate space for board of size %ld\n", size); exit(EXIT_FAILURE); } clear_border(new_board); /* print initial configuration */ if (outfile != NULL) { fprintf(outfile, "Initial board\n\n"); print_board(outfile, board); fprintf(outfile, "\n"); } int nthreads; #pragma omp parallel #pragma omp single { nthreads = omp_get_num_threads(); } init_done_time = get_time(); compute_done_time = init_done_time; /* mainly to pacify compiler */ /* loop to update board and print */ for (long step = 1; step <= steps; ++step) { /* update (results in new_board) */ update_board(board, new_board); compute_done_time = get_time(); /* print */ if ((step % print_interval) == 0) { if (outfile != NULL) { fprintf(outfile, "Board after step %ld\n\n", step); print_board(outfile, new_board); fprintf(outfile, "\n"); } } printf("%ld live cells after step %ld\n", live_cells(new_board), step); /* swap old and new boards */ { twoD_array_t *temp = board; board = new_board; new_board = temp; } } end_time = get_time(); /* * print timing information */ printf("\nTimes for OpenMP program with %d threads, " "board size %ld, %ld steps, print interval %ld:\n", nthreads, size, steps, print_interval); printf("Total %g seconds\n", end_time - start_time); printf("Not counting initialization or final printing %g seconds\n", compute_done_time - init_done_time); /* tidy up and return */ free_array(board); free_array(new_board); return EXIT_SUCCESS; } /* * constructs twoD_array structure. returns NULL if unable to allocate * space for elements, pointer to structure otherwise. */ twoD_array_t * build_array(twoD_array_t * a, long rows, long cols) { array_type * temp; a->rows = rows; a->cols = cols; if ((a->elems = malloc(rows * sizeof(a->elems[0]))) == NULL) { return NULL; } if ((temp = malloc(rows * cols * sizeof(temp[0]))) == NULL) { free (a->elems); return NULL; } for (long row = 0; row < rows; ++row, temp+=cols) { a->elems[row] = temp; } return a; } /* frees space pointed to by twoD_array structure */ void free_array(twoD_array_t * a) { free(a->elems[0]); free(a->elems); } /* * sets unused "edge" cells to 0 */ void clear_border(twoD_array_t *board) { for (long c = 0; c < board->cols; ++c) { board->elems[0][c] = 0; board->elems[board->rows-1][c] = 0; } for (long r = 0; r < board->rows; ++r) { board->elems[r][0] = 0; board->elems[r][board->cols-1] = 0; } } /* * counts "live" cells */ long live_cells(twoD_array_t *board) { long count = 0; #pragma omp parallel for reduction(+:count) for (long i = 1; i <= board->rows-2; ++i) { for (long j = 1; j <= board->cols-2; ++j) { count += board->elems[i][j]; } } return count; } /* * reads initial configuration from infile. * returns true if all is well, otherwise prints error message and returns * false. */ bool read_board(FILE* infile, twoD_array_t *board) { long size; if (fscanf(infile, "%ld", &size) != 1) { fprintf(stderr, "unable to read size of board\n"); return false; } if (build_array(board, size+2, size+2) == NULL) { fprintf(stderr, "unable to allocate space for board of size %ld\n", size); return false; } int temp; for (long i = 1; i <= size; ++i) { for (long j = 1; j <= size; ++j) { if (fscanf(infile, "%d", &temp) != 1) { fprintf(stderr, "unable to read values for board\n"); return false; } if ((temp == 0) || (temp == 1)) { board->elems[i][j] = temp; } else { fprintf(stderr, "unable to read values for board\n"); return false; } } } clear_border(board); return true; } /* * generates random board configuration for given size, fraction, and seed. * returns true if all is well, otherwise prints error message and returns * false. */ bool random_board(long size, double fraction, long seed, twoD_array_t *board) { if (build_array(board, size+2, size+2) == NULL) { fprintf(stderr, "unable to allocate space for board of size %ld\n", size); return false; } srand(seed); for (long i = 1; i <= board->rows-2; ++i) { for (long j = 1; j <= board->cols-2; ++j) { bool live = ((double) rand() / (double) (RAND_MAX)) < fraction; board->elems[i][j] = live ? 1 : 0; } } clear_border(board); return true; } /* * updates board configuration */ void update_board(twoD_array_t *board, twoD_array_t *new_board) { #pragma omp parallel for for (long i = 1; i <= board->rows-2; ++i) { for (long j = 1; j <= board->cols-2; ++j) { int nbrs = 0; nbrs += board->elems[i-1][j-1]; nbrs += board->elems[i-1][j]; nbrs += board->elems[i-1][j+1]; nbrs += board->elems[i][j-1]; nbrs += board->elems[i][j+1]; nbrs += board->elems[i+1][j-1]; nbrs += board->elems[i+1][j]; nbrs += board->elems[i+1][j+1]; if (board->elems[i][j] == 1) { if ((nbrs == 2) || (nbrs == 3)) new_board->elems[i][j] = 1; else new_board->elems[i][j] = 0; } else { if (nbrs == 3) new_board->elems[i][j] = 1; else new_board->elems[i][j] = 0; } } } } /* * prints current board configuration. */ void print_board(FILE* outfile, twoD_array_t *board) { for (long i = 1; i <= board->rows-2; ++i) { for (long j = 1; j <= board->cols-2; ++j) { if (board->elems[i][j] == 1) fprintf(outfile, "1 "); else fprintf(outfile, ". "); } fprintf(outfile, "\n"); } }