#include #include #include #include #define CLICK_KNOWN_MINE -2 #define CLICK_INVALID -1 #define CLICK_CONTINUE 0 #define CLICK_LOSE 1 #define KNOWN_MINE -3 #define HAS_MINE -2 #define UNKNOWN -1 #define IS_MINE(x) ((x) == HAS_MINE || (x) == KNOWN_MINE) struct _board_t { int ** board; int width; int height; int totalMines; }; typedef struct _board_t board_t; void addRandomMine(board_t * b) { int x; int y; //we could have a board too small for the number //of mines that we request. try w*h*10 times before //we give up int limit = b->width * b->height * 10; do { x = random() % b->width; y = random() % b->height; assert(limit > 0); limit--; } while(b->board[y][x] == HAS_MINE); b->board[y][x] = HAS_MINE; } board_t * makeBoard(int w, int h, int numMines) { board_t * b = malloc(sizeof(board_t)); b->width = w; b->height = h; b->totalMines = numMines; b->board = malloc(h*sizeof(int *)); for (int i = 0; i < h; i++) { b->board[i] = malloc(w * sizeof(int)); } for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { b->board[i][j] = UNKNOWN; } } for (int i = 0; i < numMines; i++) { addRandomMine(b); } return b; } void printBoard(board_t * b) { int found = 0; printf(" "); for (int x = 0; x < b->width; x++) { printf("%d",x/10); } printf("\n "); for (int x = 0; x < b->width; x++) { printf("%d",x%10); } printf("\n----"); for (int x = 0; x < b->width; x++) { printf("-"); } printf("\n"); for (int y = 0; y < b->height; y++) { printf("%2d: ", y %100); for (int x = 0; x < b->width; x++) { if (b->board[y][x] == KNOWN_MINE) { printf("*"); found++; } else if (b->board[y][x] < 0) { printf("?"); } else if (b->board[y][x] == 0) { printf(" "); } else { printf("%d", b->board[y][x]); } } printf("\n"); } printf("\n----"); for (int x = 0; x < b->width; x++) { printf("-"); } printf("\n"); printf(" "); for (int x = 0; x < b->width; x++) { printf("%d",x/10); } printf("\n "); for (int x = 0; x < b->width; x++) { printf("%d",x%10); } printf("\nFound %d of %d mines\n", found, b->totalMines); } int countMines(board_t * b, int x, int y) { int count = 0; for (int i = -1; i < 2; i++) { for (int j = -1; j < 2; j++) { if (x + i >=0 && x+i <= b->width-1) { if (y + j >=0 && y+j <= b->height-1) { if (IS_MINE(b->board[y+j][x+i])){ count++; } } } } } return count; } int click (board_t * b, int x, int y) { if (x < 0 || x >= b->width || y < 0 || y >= b->height) { return CLICK_INVALID; } if (b->board[y][x] == KNOWN_MINE) { return CLICK_KNOWN_MINE; } if (b->board[y][x] == HAS_MINE) { return CLICK_LOSE; } if (b->board[y][x] != UNKNOWN) { return CLICK_CONTINUE; } b->board[y][x] = countMines(b,x,y); return CLICK_CONTINUE; } int checkWin(board_t * b) { for (int i = 0; i < b->width; i++) { for (int j = 0; j < b->height; j++) { if (b->board[j][i] == UNKNOWN) { return 0; } } } return 1; } void freeBoard(board_t * b) { for (int i = 0; i < b->height; i++) { free(b->board[i]); } free(b->board); free(b); } int readInt(char ** linep, size_t * lineszp) { if (getline (linep, lineszp, stdin) == -1) { fprintf (stderr,"End of file from keyboard reading a number. Quitting\n"); exit(EXIT_FAILURE); } char * endptr = NULL; long int x = strtol (*linep, &endptr, 10); if (endptr == *linep) { fprintf (stderr,"You did not enter any valid number\n"); printf ("Please try again\n"); return readInt (linep, lineszp); } if (*endptr != '\n') { fprintf( stderr, "Input was not entirely a number (junk at end)\n"); printf ("Please try again\n"); return readInt (linep, lineszp); } if (x > INT_MAX) { fprintf(stderr,"%ld is too big for an int!\n", x); printf("Please try again\n"); return readInt(linep, lineszp); } return x; } void doReveal(board_t * b, int x, int y, int revealMines){ for (int dy = -1; dy <=1 ; dy++) { for (int dx = -1; dx <=1 ; dx++) { int nx = x + dx; int ny = y + dy; if (nx >= 0 && nx < b->width && ny >= 0 && ny < b->height) { if (revealMines) { assert(b->board[ny][nx] != UNKNOWN); if (b->board[ny][nx] == HAS_MINE){ b->board[ny][nx] = KNOWN_MINE; } } else { assert(b->board[ny][nx] != HAS_MINE); if (b->board[ny][nx] == UNKNOWN) { b->board[ny][nx] = countMines(b,nx,ny); } } } } } } int maybeReveal(board_t * b, int x, int y) { int unknownSquares = 0; int knownMines = 0; for (int dy = -1; dy <=1 ; dy++) { for (int dx = -1; dx <=1 ; dx++) { int nx = x + dx; int ny = y + dy; if (nx >= 0 && nx < b->width && ny >= 0 && ny < b->height) { if (b->board[ny][nx] == UNKNOWN || b->board[ny][nx] == HAS_MINE) { unknownSquares++; } else if(b->board[ny][nx] == KNOWN_MINE) { knownMines++; } } } } assert(knownMines + unknownSquares >= b->board[y][x]); assert(knownMines <= b->board[y][x]); if (unknownSquares > 0) { int revealMines = (knownMines + unknownSquares == b->board[y][x]); int allKnown = knownMines == b->board[y][x]; if(revealMines || allKnown) { assert(!revealMines || !allKnown); doReveal(b,x,y, revealMines); return 1; } } return 0; } void determineKnownMines(board_t * b) { int foundMore = 0; for (int y = 0; y < b->height; y++) { for (int x = 0; x < b->width; x++) { if (b->board[y][x] >= 0) { foundMore = maybeReveal(b,x,y) || foundMore; } } } if (foundMore) { determineKnownMines(b); } } void revealMines(board_t * b) { for (int y = 0; y < b->height; y++) { for (int x = 0; x < b->width; x++) { if (b->board[y][x] == HAS_MINE) { b->board[y][x] = KNOWN_MINE; } } } } int playTurn(board_t * b, char ** linep, size_t *lineszp) { printf("Current board:\n"); printBoard(b); printf("x coordinate:\n"); int x = readInt(linep, lineszp); printf("y coordinate:\n"); int y = readInt(linep, lineszp); int result = click(b,x,y); determineKnownMines(b); if (result == CLICK_LOSE) { printf("Oh no! That square had a mine. You lose!\n"); revealMines(b); printBoard(b); return 1; } else if (result == CLICK_INVALID) { printf("That is not a valid square, please try again\n"); } else if (result == CLICK_KNOWN_MINE) { printf("You already know there is a mine there!\n"); } else if(checkWin(b)) { printBoard(b); printf("You win!\n"); return 1; } return 0; } int main(int argc, char ** argv) { if (argc != 4) { fprintf(stderr,"Usage: minesweeper width height numMines\n"); return EXIT_FAILURE; } int width = atoi(argv[1]); int height = atoi(argv[2]); int numMines = atoi(argv[3]); if (width <= 0 || height <= 0 || numMines <= 0) { fprintf(stderr, "Width, height, and numMines must all be positive ints\n"); return EXIT_FAILURE; } char * line = NULL; size_t linesz = 0; do { board_t * b = makeBoard (width, height, numMines); int gameOver = 0; while (!gameOver) { gameOver = playTurn(b, &line, &linesz); } freeBoard(b); do { printf("Do you want to play again?\n"); } while(getline(&line, &linesz, stdin) == -1); } while(line[0] == 'Y' || line[0] == 'y'); free(line); return EXIT_SUCCESS; }