diff options
Diffstat (limited to '31_minesweeper/minesweeper.c')
-rw-r--r-- | 31_minesweeper/minesweeper.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/31_minesweeper/minesweeper.c b/31_minesweeper/minesweeper.c new file mode 100644 index 0000000..3ea9490 --- /dev/null +++ b/31_minesweeper/minesweeper.c @@ -0,0 +1,335 @@ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <limits.h> + +#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; +} |