| 1 | #include <string.h> |
| 2 | #include <ncurses.h> |
| 3 | #include <stdbool.h> |
| 4 | |
| 5 | |
| 6 | /* BEGIN CONFIG */ |
| 7 | #ifndef Y |
| 8 | #define Y 16 |
| 9 | #endif /* Y */ |
| 10 | |
| 11 | #ifndef X |
| 12 | #define X 16 |
| 13 | #endif /* X */ |
| 14 | |
| 15 | #ifndef DELAY |
| 16 | #define DELAY 250 |
| 17 | #endif /* DELAY */ |
| 18 | |
| 19 | #ifndef LIVE_C |
| 20 | #define LIVE_C '#' |
| 21 | #endif /* LIVE_C */ |
| 22 | |
| 23 | #ifndef DEAD_C |
| 24 | #define DEAD_C ' ' |
| 25 | #endif /* DEAD_C */ |
| 26 | /* END CONFIG */ |
| 27 | |
| 28 | |
| 29 | #define ARRLEN(a) (sizeof(a)/sizeof(*a)) |
| 30 | |
| 31 | #define LIVE 1<<0 |
| 32 | #define DEAD 1<<1 |
| 33 | #define TODIE 1<<2 |
| 34 | #define TOLIVE 1<<3 |
| 35 | |
| 36 | |
| 37 | enum { |
| 38 | GREEN = 1, |
| 39 | }; |
| 40 | |
| 41 | |
| 42 | struct { |
| 43 | int x, y; |
| 44 | } around[8] = { |
| 45 | {.x = -1, .y = -1}, {.x = 0, .y = -1}, {.x = +1, .y = -1}, |
| 46 | {.x = -1, .y = 0}, {.x = +1, .y = 0}, |
| 47 | {.x = -1, .y = +1}, {.x = 0, .y = +1}, {.x = +1, .y = +1} |
| 48 | }; |
| 49 | |
| 50 | |
| 51 | void display(const char grid[X][Y], bool edit) { |
| 52 | for (int x = 0; x < X; x++) addstr("+ - "); |
| 53 | addstr("+\n"); |
| 54 | for (int y = 0; y < Y; y++) { |
| 55 | for (int x = 0; x < X; x++) { |
| 56 | addstr("| "); |
| 57 | char c = grid[x][y]; |
| 58 | int cp = 0; |
| 59 | if (edit) cp = COLOR_PAIR(GREEN); |
| 60 | attron(cp); |
| 61 | addch(c & LIVE ? LIVE_C : DEAD_C); |
| 62 | attroff(cp); |
| 63 | addch(' '); |
| 64 | } |
| 65 | addstr("|\n"); |
| 66 | for (int x = 0; x < X; x++) addstr("+ - "); |
| 67 | addstr("+\n"); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | |
| 72 | int main() { |
| 73 | initscr(); |
| 74 | noecho(); |
| 75 | curs_set(0); |
| 76 | keypad(stdscr, TRUE); |
| 77 | |
| 78 | mousemask(BUTTON1_CLICKED, NULL); |
| 79 | |
| 80 | use_default_colors(); |
| 81 | start_color(); |
| 82 | init_pair(GREEN, COLOR_GREEN, -1); |
| 83 | |
| 84 | char grid[X][Y]; |
| 85 | memset(grid, DEAD, sizeof(grid)); |
| 86 | char initial_grid[X][Y]; |
| 87 | memset(initial_grid, DEAD, sizeof(grid)); |
| 88 | |
| 89 | clear(); |
| 90 | |
| 91 | bool edit_initial = true; |
| 92 | while (true) { |
| 93 | // initialize the board |
| 94 | while (true) { |
| 95 | erase(); |
| 96 | display(grid, true); |
| 97 | |
| 98 | move(0, (X*4+2)/2 - 5); |
| 99 | attron(A_REVERSE); |
| 100 | addstr("BOARD EDIT"); |
| 101 | move(Y*2, (X*4+2)/2 - 21); |
| 102 | addstr("Enter to start | r to restart | q to quit"); |
| 103 | attroff(A_REVERSE); |
| 104 | refresh(); |
| 105 | |
| 106 | int c = getch(); |
| 107 | if (c == KEY_ENTER || c == '\n') break; |
| 108 | if (c == 'q') goto terminate; |
| 109 | if (c == 'r') { |
| 110 | memset(grid, DEAD, sizeof(grid)); |
| 111 | continue; |
| 112 | } |
| 113 | if (c != KEY_MOUSE) continue; |
| 114 | MEVENT e; |
| 115 | if (getmouse(&e) != OK) continue; |
| 116 | |
| 117 | if (e.x % 4 == 0 || e.y % 2 == 0) continue; |
| 118 | int x = (e.x - 2) / 4; |
| 119 | int y = (e.y - 1) / 2; |
| 120 | if ((e.x - 1) % 4 == 0) x++; |
| 121 | if (x > X || y > Y) continue; |
| 122 | |
| 123 | if (grid[x][y] == LIVE) grid[x][y] = DEAD; |
| 124 | else grid[x][y] = LIVE; |
| 125 | } |
| 126 | |
| 127 | if (edit_initial) memcpy(initial_grid, grid, sizeof(grid)); |
| 128 | |
| 129 | timeout(0); |
| 130 | |
| 131 | while (true) { |
| 132 | // determine new cell states |
| 133 | for (int x = 0; x < X; x++) { |
| 134 | for (int y = 0; y < Y; y++) { |
| 135 | int neighbors = 0; |
| 136 | for (int i = 0; i < ARRLEN(around); i++) { |
| 137 | int nx = x + around[i].x; |
| 138 | int ny = y + around[i].y; |
| 139 | if (nx < 0 || ny < 0 || nx >= X || ny >= Y) continue; |
| 140 | if (grid[nx][ny] & LIVE) neighbors++; |
| 141 | } |
| 142 | |
| 143 | if (neighbors == 3) grid[x][y] |= TOLIVE; |
| 144 | else if (neighbors == 2 && grid[x][y] & LIVE) grid[x][y] |= TOLIVE; |
| 145 | else grid[x][y] |= TODIE; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | // update all cells |
| 150 | for (int x = 0; x < X; x++) { |
| 151 | for (int y = 0; y < Y; y++) { |
| 152 | if (grid[x][y] & TOLIVE) grid[x][y] = LIVE; |
| 153 | else grid[x][y] = DEAD; |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | erase(); |
| 158 | display(grid, false); |
| 159 | |
| 160 | move(Y*2, (X*4+2)/2 - 23); |
| 161 | attron(A_REVERSE); |
| 162 | addstr("q to quit | i to edit initial | e to edit now"); |
| 163 | attroff(A_REVERSE); |
| 164 | refresh(); |
| 165 | |
| 166 | int c = getch(); |
| 167 | if (c != ERR) { |
| 168 | if (c == 'q') goto terminate; |
| 169 | if (c == 'i') { |
| 170 | edit_initial = true; |
| 171 | break; |
| 172 | } |
| 173 | if (c == 'e') { |
| 174 | edit_initial = false; |
| 175 | break; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | napms(DELAY); |
| 180 | } |
| 181 | |
| 182 | timeout(-1); |
| 183 | if (edit_initial) memcpy(grid, initial_grid, sizeof(grid)); |
| 184 | } |
| 185 | |
| 186 | terminate: |
| 187 | echo(); |
| 188 | curs_set(1); |
| 189 | endwin(); |
| 190 | } |
| 191 | |