random

:-)
git clone read: git://git.margiolis.net/random.git
Log | Files | Refs | LICENSE

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 }