minecurses.c (7639B)
1 #include <err.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <time.h> 5 6 #include <curses.h> 7 8 #include "defs.h" 9 10 /* TODO: 11 * - play again 12 * - win fix 13 * - first open 14 */ 15 16 struct minecurses { 17 WINDOW *gw; 18 char **dispboard; 19 char **mineboard; 20 int x; 21 int y; 22 int xmax; 23 int ymax; 24 int wxmax; 25 int wymax; 26 int rows; 27 int cols; 28 int nmines; 29 int ndef; 30 int move; 31 int gameover; 32 }; 33 34 static void cursesinit(struct minecurses *); 35 static void gwinit(struct minecurses *); 36 static void gamereset(struct minecurses *); 37 static void gamestart(struct minecurses *); 38 static int valset(int, int, const char *, int, int); 39 static int adjcount(const struct minecurses *, int, int); 40 static void boardsdealloc(struct minecurses *); 41 static void cellreveal(const struct minecurses *); 42 static void boardprint(const struct minecurses *); 43 static void menuopts(void); 44 static void endscreen(struct minecurses *, int); 45 static void *emalloc(size_t); 46 47 static void 48 cursesinit(struct minecurses *m) 49 { 50 if (!initscr()) 51 errx(1, "initscr"); 52 noecho(); 53 cbreak(); 54 55 m->xmax = getmaxx(stdscr); 56 m->ymax = getmaxy(stdscr); 57 } 58 59 static void 60 gwinit(struct minecurses *m) 61 { 62 int wr, wc, wy, wx; 63 64 wr = SCRSPACE_Y(m->rows); 65 wc = SCRSPACE_X(m->cols); 66 wy = CENTER(m->ymax, wr); 67 wx = CENTER(m->xmax, wc); 68 if ((m->gw = newwin(wr, wc, wy, wx)) == NULL) 69 errx(1, "newwin"); 70 71 m->wxmax = getmaxx(m->gw); 72 m->wymax = getmaxy(m->gw); 73 } 74 75 static void 76 gamereset(struct minecurses *m) 77 { 78 size_t i, j, r, c; 79 80 echo(); 81 m->cols = valset(m->ymax, 4, MSG_COLS, 5, ARRSPACE_X(m->xmax) - 2); 82 m->rows = valset(m->ymax, 3, MSG_ROWS, 5, ARRSPACE_Y(m->ymax) - 3); 83 m->nmines = valset(m->ymax, 2, MSG_MINES, 1, m->rows * m->cols - 15); 84 m->x = m->y = 0; 85 m->ndef = 0; 86 m->gameover = 0; 87 noecho(); 88 89 gwinit(m); 90 menuopts(); 91 /* allocate memory for the boards */ 92 m->dispboard = emalloc(m->rows * sizeof(char *)); 93 m->mineboard = emalloc(m->rows * sizeof(char *)); 94 for (i = 0; i < m->rows; i++) { 95 m->dispboard[i] = emalloc(m->cols); 96 m->mineboard[i] = emalloc(m->cols); 97 } 98 99 /* place mines */ 100 srand(time(NULL)); 101 for (i = 0; i < m->nmines; i++) { 102 r = rand() % m->rows; 103 c = rand() % m->cols; 104 m->mineboard[r][c] = CELL_MINE; 105 } 106 107 /* TODO: do it in one loop */ 108 /* add numbers */ 109 for (i = 0; i < m->rows; i++) { 110 for (j = 0; j < m->cols; j++) { 111 if (!IS_MINE(m, i, j)) 112 m->mineboard[i][j] = adjcount(m, i, j) + '0'; 113 /* in the meantime, initialize dispboard */ 114 m->dispboard[i][j] = CELL_BLANK; 115 } 116 } 117 118 /* fill spaces */ 119 for (i = 0; i < m->rows; i++) 120 for (j = 0; j < m->cols; j++) 121 if (!IS_MINE(m, i, j) && m->mineboard[i][j] == '0') 122 m->mineboard[i][j] = '-'; 123 } 124 125 #define bx m->x 126 #define by m->y 127 128 static void 129 gamestart(struct minecurses *m) 130 { 131 static int y = 1, x = 2; 132 133 for (;;) { 134 werase(m->gw); 135 erase(); 136 137 boardprint(m); 138 CURS_UPDATE(m, y, x); 139 bx = ARRSPACE_X(x); 140 by = ARRSPACE_Y(y); 141 142 /* session info */ 143 mvprintw(0, 0, MSG_CURPOS, bx, by); 144 mvprintw(0, CENTER(m->xmax, strlen(MSG_NDEFUSED) - 2), 145 MSG_NDEFUSED, m->ndef, m->nmines); 146 mvprintw(0, m->xmax - strlen(OPT_CTRLS), OPT_CTRLS); 147 148 refresh(); 149 wrefresh(m->gw); 150 151 /* handle moves */ 152 switch (m->move = wgetch(m->gw)) { 153 case MOVE_UP: 154 if (--y < 1) 155 y = 1; 156 break; 157 case MOVE_DOWN: 158 if (++y > m->wxmax - 2) 159 y = m->wymax - 2; 160 break; 161 case MOVE_LEFT: 162 x -= 3; 163 if (x < 2) 164 x = 2; 165 break; 166 case MOVE_RIGHT: 167 x += 3; 168 if (x > m->wxmax - 3) 169 x = m->wxmax - 3; 170 break; 171 case MOVE_ENTER: /* FALLTHROUGH */ 172 case MOVE_OPEN_CELL: 173 m->dispboard[by][bx] = m->mineboard[by][bx]; 174 m->gameover = IS_MINE(m, by, bx); 175 cellreveal(m); 176 break; 177 case MOVE_FLAG_CELL: 178 if (IS_FLAGGED(m, by, bx)) 179 m->dispboard[by][bx] = CELL_BLANK; 180 else if (!IS_FLAGGED(m, by, bx) && !IS_BLANK(m, by, bx)) 181 break; 182 else 183 m->dispboard[by][bx] = CELL_FLAGGED; 184 cellreveal(m); 185 break; 186 case MOVE_DEFUSE_CELL: 187 if (IS_FLAGGED(m, by, bx) && IS_MINE(m, by, bx)) { 188 m->ndef++; 189 m->dispboard[by][bx] = MINE_DEFUSED; 190 m->mineboard[by][bx] = MINE_DEFUSED; 191 cellreveal(m); 192 } else if (IS_FLAGGED(m, by, bx) && !IS_MINE(m, by, bx)) 193 m->gameover = 1; 194 break; 195 case MOVE_OPEN_MENU: 196 menuopts(); 197 boardprint(m); 198 break; 199 } 200 201 if (OUT_OF_BOUNDS(m, by, bx) 202 || m->gameover 203 || m->ndef == m->nmines 204 || m->move == MOVE_QUIT) 205 break; 206 } 207 208 if (m->gameover) 209 endscreen(m, GAME_LOST); 210 else if (m->ndef == m->nmines) 211 endscreen(m, GAME_WON); 212 } 213 214 #undef bx 215 #undef by 216 217 static int 218 valset(int ymax, int offy, const char *msg, int min, int max) 219 { 220 int val; 221 222 do { 223 mvprintw(ymax - offy, 1, msg, min, max); 224 scanw("%d", &val); 225 } while (val < min || val > max); 226 227 return (val); 228 } 229 230 static int 231 adjcount(const struct minecurses *m, int r, int c) 232 { 233 int n = 0; 234 235 if (!OUT_OF_BOUNDS(m, r, c-1) && IS_MINE(m, r, c-1)) n++; // north 236 if (!OUT_OF_BOUNDS(m, r, c+1) && IS_MINE(m, r, c+1)) n++; // south 237 if (!OUT_OF_BOUNDS(m, r+1, c) && IS_MINE(m, r+1, c)) n++; // east 238 if (!OUT_OF_BOUNDS(m, r-1, c) && IS_MINE(m, r-1, c)) n++; // west 239 if (!OUT_OF_BOUNDS(m, r+1, c-1) && IS_MINE(m, r+1, c-1)) n++; // north-east 240 if (!OUT_OF_BOUNDS(m, r-1, c-1) && IS_MINE(m, r-1, c-1)) n++; // north-west 241 if (!OUT_OF_BOUNDS(m, r+1, c+1) && IS_MINE(m, r+1, c+1)) n++; // south-east 242 if (!OUT_OF_BOUNDS(m, r-1, c+1) && IS_MINE(m, r-1, c+1)) n++; // south-west 243 244 return (n); 245 } 246 247 static void 248 boardsdealloc(struct minecurses *m) 249 { 250 size_t i = 0; 251 252 if (m->dispboard && m->mineboard) { 253 for (; i < m->rows; i++) { 254 free(m->dispboard[i]); 255 free(m->mineboard[i]); 256 } 257 free(m->dispboard); 258 free(m->mineboard); 259 } 260 } 261 262 static void 263 cellreveal(const struct minecurses *m) 264 { 265 mvwaddch(m->gw, m->y + 1, SCRSPACE_X(m->x), m->dispboard[m->y][m->x]); 266 } 267 268 static void 269 boardprint(const struct minecurses *m) 270 { 271 size_t i, j, x, y; 272 273 wattroff(m->gw, A_BOLD); 274 box(m->gw, 0, 0); 275 for (i = 1; i <= m->rows; i++) { 276 wmove(m->gw, i, 1); 277 for (j = 0; j < m->cols; j++) 278 waddstr(m->gw, GRID_BOX); 279 } 280 wattron(m->gw, A_BOLD); 281 for (i = 0, y = 1; i < m->rows; i++, y++) 282 for (j = 0, x = 2; j < m->cols; j++, x += 3) 283 mvwaddch(m->gw, y, x, m->dispboard[i][j]); 284 wattroff(m->gw, A_BOLD); 285 } 286 287 static void 288 menuopts(void) 289 { 290 WINDOW *opts; 291 int w, h, wy, wx; 292 293 w = 36; 294 h = 12; 295 wy = CENTER(getmaxy(stdscr), h); 296 wx = CENTER(getmaxx(stdscr), w); 297 opts = newwin(h, w, wy, wx); 298 box(opts, 0, 0); 299 300 /* fill menu */ 301 mvwprintw(opts, 1, 1, OPT_QUIT); 302 mvwprintw(opts, 2, 1, OPT_MOVE_UP); 303 mvwprintw(opts, 3, 1, OPT_MOVE_DOWN); 304 mvwprintw(opts, 4, 1, OPT_MOVE_LEFT); 305 mvwprintw(opts, 5, 1, OPT_MOVE_RIGHT); 306 mvwprintw(opts, 6, 1, OPT_FLAG_CELL); 307 mvwprintw(opts, 7, 1, OPT_DEFUSE); 308 mvwprintw(opts, 8, 1, OPT_OPEN_CELL); 309 mvwprintw(opts, 10, 1, MSG_QUIT_MENU); 310 311 (void)wgetch(opts); 312 delwin(opts); 313 } 314 315 static void 316 endscreen(struct minecurses *m, int state) 317 { 318 curs_set(0); 319 wclear(m->gw); 320 wrefresh(m->gw); 321 attron(A_BOLD); 322 switch (state) { 323 case GAME_WON: 324 mvprintw(YMID(stdscr)-2, XMID(stdscr)-11, MSG_WIN_1); 325 mvprintw(YMID(stdscr)-1, XMID(stdscr)-3, MSG_WIN_2); 326 break; 327 case GAME_LOST: 328 mvprintw(YMID(stdscr)-2, XMID(stdscr)-24, MSG_LOSE_1); 329 mvprintw(YMID(stdscr)-1, XMID(stdscr)-4, MSG_LOSE_2); 330 // TODO: print mine board 331 break; 332 } 333 mvprintw(YMID(stdscr), XMID(stdscr)-11, MSG_CONT); 334 refresh(); 335 attroff(A_BOLD); 336 getchar(); 337 } 338 339 static void * 340 emalloc(size_t nb) 341 { 342 void *p; 343 344 if ((p = malloc(nb)) == NULL) 345 err(1, "malloc"); 346 347 return (p); 348 } 349 350 int 351 main(int argc, char *argv[]) 352 { 353 struct minecurses *m; 354 355 m = emalloc(sizeof(struct minecurses)); 356 cursesinit(m); 357 358 gamereset(m); 359 gamestart(m); 360 361 boardsdealloc(m); 362 delwin(m->gw); 363 endwin(); 364 free(m); 365 366 return (0); 367 }