/* * Simple(?) text-based implementation of Conway's game of life. * * Some functions are from "mylib" library; others are are from "ncurses" * library (and "man 3x foobar" gives more info on foobar()). */ #include #include #include #include #include #include "mylib.h" #define HELP_CHAR 'h' #define QUIT_CHAR 'q' #define CLEAR_CHAR 'c' #define EDIT_CHAR 'e' #define STEP_CHAR 's' #define EDIT_EXIT_CHAR 'x' /* * data structure for "board" * * cells are kept as a 1D array, with macros to access by row and column, * because of how awkward 2D arrays can be in C. */ typedef struct { int rows; int cols; bool *cells; /* current generation */ bool *new_cells; /* next generation */ } board_t; #define cell(r, c) board.cells[(r)*board.cols + (c)] #define new_cell(r, c) board.new_cells[(r)*board.cols + (c)] void initialize_prompts_and_help(); void show_prompt(char *prompt, char *errorprefix); void show_help(char *help_lines[]); /* NULL to indicate end of list */ void free_help_lines(char *help_lines[]); bool make_board(int rows, int cols); void clear_board(void); void free_board(void); void show_board(void); void edit_board(void); void step_board(void); WINDOW* top; WINDOW* center; board_t board = { -1, -1, NULL }; char *main_prompt = ""; char *edit_prompt = ""; char *empty_help[] = { NULL }; char **main_help = empty_help; char **edit_help = empty_help; /* * main program */ int main(void) { /* * initialize */ ncurses_start(); if (!make_board(LINES, COLS)) { ncurses_end(); fprintf(stderr, "cannot make board of size %d by %d\n", LINES, COLS); return EXIT_FAILURE; } top = newwin(3, COLS, 0, 0); center = newwin(LINES-3, COLS, 3, 0); refresh(); initialize_prompts_and_help(); /* * main processing loop */ show_prompt(main_prompt, NULL); show_board(); int ch; while ((ch = getch()) != QUIT_CHAR) { switch (ch) { case HELP_CHAR: show_help(main_help); show_board(); break; case QUIT_CHAR: break; case CLEAR_CHAR: clear_board(); show_board(); break; case EDIT_CHAR: edit_board(); break; case STEP_CHAR: step_board(); break; default: show_prompt(main_prompt, "Invalid selection"); break; } } /* * clean up and exit */ free(main_prompt); free(edit_prompt); free_help_lines(main_help); free_help_lines(edit_help); if (main_help != empty_help) free(main_help); if (edit_help != empty_help) free(edit_help); free_board(); delwin(top); delwin(center); ncurses_end(); return EXIT_SUCCESS; } void initialize_one_help(char *lines[], size_t count, char *** help_lines_p) { char **temp = malloc(sizeof(lines[0]) * count); if (temp != NULL) { for (int i = 0; i < count; ++i) { temp[i] = lines[i]; } *help_lines_p = temp; } } void initialize_prompts_and_help() { char *temp = allocate_and_format( "%c for help, %c to quit", HELP_CHAR, QUIT_CHAR); if (temp != NULL) main_prompt = temp; temp = allocate_and_format( "%c for help, %c to stop editing", HELP_CHAR, EDIT_EXIT_CHAR); if (temp != NULL) edit_prompt = temp; char *mlines[] = { allocate_and_format("%c to clear board", CLEAR_CHAR), allocate_and_format("%c to edit board", EDIT_CHAR), allocate_and_format("%c to step", STEP_CHAR), NULL }; initialize_one_help(mlines, sizeof(mlines) / sizeof(mlines[0]), &main_help); char *elines[] = { allocate_and_format("arrow keys to move cursor"), allocate_and_format("space to toggle cell"), allocate_and_format("%c to stop editing", EDIT_EXIT_CHAR), NULL }; initialize_one_help(elines, sizeof(elines) / sizeof(elines[0]), &edit_help); } /* * show prompt in top window * preceded by error msg if errorprefix is not NULL */ void show_prompt(char *prompt, char *errorprefix) { int rows, cols; getmaxyx(top, rows, cols); wclear(top); int msglen = (errorprefix) ? strlen(errorprefix) + strlen(prompt) + 3 : strlen(prompt); wmove(top, 1, (cols-2-msglen)/2 + 1); if (errorprefix) { wprintw(top, "%s (%s)", errorprefix, prompt); } else { wprintw(top, "%s", prompt); } box(top, 0, 0); wrefresh(top); } void show_help(char *help_lines[]) { wclear(center); wmove(center, 2, 0); for (int i = 0; help_lines[i] != NULL; ++i) { centerline(center, help_lines[i]); add_to_row(center, 1); } add_to_row(center, 1); centerline(center, "Press any key to exit help"); box(center, 0, 0); wrefresh(center); getch(); } void free_help_lines(char *help_lines[]) { for (int i = 0; help_lines[i] != NULL; ++i) { free(help_lines[i]); } } bool make_board(int rows, int cols) { board.rows = rows; board.cols = cols; board.cells = malloc(sizeof(board.cells[0]) * board.rows * board.cols); board.new_cells = malloc(sizeof(board.cells[0]) * board.rows * board.cols); if ((board.cells == NULL) || (board.new_cells == NULL)) { return false; } return true; } void clear_board(void) { for (int r = 0; r < board.rows; ++r) { for (int c = 0; c < board.cols; ++c) { cell(r,c) = false; new_cell(r,c) = false; } } } void free_board(void) { free(board.cells); free(board.new_cells); } void show_board(void) { wclear(center); if (board.cells != NULL) { /* border cells are always empty */ for (int r = 1; r < board.rows-1; ++r) { for (int c = 1; c < board.cols-1; ++c) { wmove(center, r, c); if (cell(r,c)) wattron(center, A_REVERSE); waddch(center, ' '); wattroff(center, A_REVERSE); } } } box(center, 0, 0); wrefresh(center); } void edit_board(void) { show_prompt(edit_prompt, NULL); int ch; int row = 1; int col = 1; wmove(center, row, col); curs_set(1); wrefresh(center); while (((ch = getch()) != EDIT_EXIT_CHAR) && (ch != QUIT_CHAR)) { switch (ch) { case HELP_CHAR: show_help(edit_help); show_board(); break; case ' ': cell(row,col) = !cell(row,col); show_board(); break; case KEY_UP: if (row > 1) { wmove(center, --row, col); } wrefresh(center); break; case KEY_DOWN: if (row < board.rows-1) { wmove(center, ++row, col); } wrefresh(center); break; case KEY_LEFT: if (col > 1) { wmove(center, row, --col); } wrefresh(center); break; case KEY_RIGHT: if (col < board.cols-1) { wmove(center, row, ++col); } wrefresh(center); break; default: show_prompt(edit_prompt, "invalid selection"); break; } } curs_set(0); show_prompt(main_prompt, NULL); wrefresh(center); if (ch == QUIT_CHAR) ungetch(ch); } void step_board(void) { if (board.cells != NULL) { /* border cells are always empty */ for (int r = 1; r < board.rows-1; ++r) { for (int c = 1; c < board.cols-1; ++c) { int nbrs = 0; if (cell(r-1,c-1)) ++nbrs; if (cell(r-1,c)) ++nbrs; if (cell(r-1,c+1)) ++nbrs; if (cell(r,c-1)) ++nbrs; if (cell(r,c+1)) ++nbrs; if (cell(r+1,c-1)) ++nbrs; if (cell(r+1,c)) ++nbrs; if (cell(r+1,c+1)) ++nbrs; new_cell(r,c) = (cell(r, c) && ((nbrs == 2) || (nbrs == 3))) || (!cell(r, c) && (nbrs == 3)); } } } bool *temp = board.cells; board.cells = board.new_cells; board.new_cells = temp; show_board(); }