Last active
September 11, 2017 18:45
-
-
Save omaraflak/6cd10584a641be4826fd478d18f3932e to your computer and use it in GitHub Desktop.
Minesweeper in C for Unix systems
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <termios.h> | |
#include <time.h> | |
#include <stdbool.h> | |
const char BOMB = 'X'; | |
const char EMPTY = 'E'; | |
const char DISCOVERED = '0'; | |
const char FLAG = 'F'; | |
const char HIDDEN = '.'; | |
typedef struct Cursor{ | |
int x; | |
int y; | |
} Cursor; | |
typedef struct Game{ | |
char **board; | |
char **displayedBoard; | |
int width; | |
int height; | |
int bombs; | |
} Game; | |
bool allocateMemory(Game *game) | |
{ | |
int i; | |
if ((game->displayedBoard = malloc(game->width * sizeof(char*))) != NULL){ | |
for(i=0 ; i<game->width ; i++){ | |
if ((game->displayedBoard[i] = malloc(game->height * sizeof(char))) == NULL){ | |
return false; | |
} | |
} | |
} | |
if ((game->board = malloc(game->width * sizeof(char*))) != NULL){ | |
for(i=0 ; i<game->width ; i++){ | |
if ((game->board[i] = malloc(game->height * sizeof(char))) == NULL){ | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
void freeMemory(Game game) | |
{ | |
int i; | |
for(i=0 ; i<game.width ; i++) | |
{ | |
free(game.board[i]); | |
free(game.displayedBoard[i]); | |
} | |
free(game.board); | |
free(game.displayedBoard); | |
} | |
void generateRandomCoordinates(Game game, int *x, int *y) | |
{ | |
*x = rand()%game.width; | |
*y = rand()%game.height; | |
} | |
void generateBoardData(Game *game) | |
{ | |
int i,j,x,y; | |
// generate bombs | |
for(i=0 ; i<game->bombs ; i++){ | |
generateRandomCoordinates(*game, &x, &y); | |
game->board[x][y] = BOMB; | |
} | |
// set numbers | |
int counter; | |
for(i=0 ; i<game->width ; i++){ | |
for(j=0 ; j<game->height ; j++){ | |
counter=0; | |
if(game->board[i][j]!=BOMB){ | |
for(x=-1 ; x<2 ; x++){ | |
for(y=-1 ; y<2 ; y++){ | |
if(x!=0 || y!=0){ | |
if(i+x>=0 && i+x<game->width && j+y>=0 && j+y<game->height){ | |
if(game->board[i+x][j+y]==BOMB){ | |
counter++; | |
} | |
} | |
} | |
} | |
} | |
if(counter!=0){ | |
game->board[i][j]=counter+48; | |
} | |
else{ | |
game->board[i][j]=EMPTY; | |
} | |
} | |
game->displayedBoard[i][j] = HIDDEN; | |
} | |
} | |
} | |
void discoverSurroundingBoxes(Game *game, int x, int y) | |
{ | |
game->board[x][y]=DISCOVERED; | |
game->displayedBoard[x][y]=DISCOVERED; | |
int width = game->width; | |
int height = game->height; | |
int i; | |
// search left | |
if(x>0) | |
{ | |
for(i=x-1 ; i>=0 ; i--) | |
{ | |
if(game->board[i][y]==EMPTY && game->board[i][y]!=BOMB && game->board[i][y]!=FLAG){ | |
discoverSurroundingBoxes(game, i, y); | |
} | |
else if(game->board[i][y]!=BOMB && game->board[i][y]!=DISCOVERED && game->board[i][y]!=EMPTY && game->board[i][y]!=FLAG && game->board[i+1][y]==DISCOVERED){ | |
game->displayedBoard[i][y] = game->board[i][y]; | |
} | |
else{ | |
break; | |
} | |
} | |
} | |
// search right | |
if(x<width-1) | |
{ | |
for(i=x+1 ; i<width ; i++) | |
{ | |
if(game->board[i][y]==EMPTY && game->board[i][y]!=BOMB && game->board[i][y]!=FLAG){ | |
discoverSurroundingBoxes(game, i, y); | |
} | |
else if(game->board[i][y]!=BOMB && game->board[i][y]!=DISCOVERED && game->board[i][y]!=EMPTY && game->board[i][y]!=FLAG && game->board[i-1][y]==DISCOVERED){ | |
game->displayedBoard[i][y] = game->board[i][y]; | |
} | |
else{ | |
break; | |
} | |
} | |
} | |
// search top | |
if(y>0) | |
{ | |
for(i=y-1 ; i>=0 ; i--) | |
{ | |
if(game->board[x][i]==EMPTY && game->board[x][i]!=BOMB && game->board[x][i]!=FLAG){ | |
discoverSurroundingBoxes(game, x, i); | |
} | |
else if(game->board[x][i]!=BOMB && game->board[x][i]!=DISCOVERED && game->board[x][i]!=EMPTY && game->board[x][i]!=FLAG && game->board[x][i+1]==DISCOVERED){ | |
game->displayedBoard[x][i] = game->board[x][i]; | |
} | |
else{ | |
break; | |
} | |
} | |
} | |
// search bottom | |
if(y<height-1) | |
{ | |
for(i=y+1 ; i<height ; i++) | |
{ | |
if(game->board[x][i]==EMPTY && game->board[x][i]!=BOMB && game->board[x][i]!=FLAG){ | |
discoverSurroundingBoxes(game, x, i); | |
} | |
else if(game->board[x][i]!=BOMB && game->displayedBoard[x][i]!=DISCOVERED && game->board[x][i]!=EMPTY && game->board[x][i]!=FLAG && game->board[x][i-1]==DISCOVERED){ | |
game->displayedBoard[x][i] = game->board[x][i]; | |
} | |
else{ | |
break; | |
} | |
} | |
} | |
} | |
void printBoard(Game game, Cursor cursor) | |
{ | |
int i,j; | |
for(i=0 ; i<game.height ; i++){ | |
for(j=0 ; j<game.width ; j++){ | |
printf("%c ", game.displayedBoard[j][i]); | |
} | |
printf("\n"); | |
if(i==cursor.y){ | |
for(j=0 ; j<game.width ; j++){ | |
if(j==cursor.x){ | |
printf("-"); | |
break; | |
} | |
else | |
printf(" "); | |
} | |
printf("\n"); | |
} | |
} | |
} | |
void refreshScreen(Game game, Cursor cursor) | |
{ | |
system("clear"); | |
printBoard(game, cursor); | |
} | |
int getDiscoveredBoxes(struct Game game) | |
{ | |
int i, j, discoveredBoxes=0; | |
for(i=0 ; i<game.width ; i++) | |
{ | |
for(j=0 ; j<game.height ; j++) | |
{ | |
if(game.displayedBoard[i][j]==DISCOVERED || (game.displayedBoard[i][j]!=HIDDEN && game.displayedBoard[i][j]!=FLAG)){ | |
discoveredBoxes++; | |
} | |
} | |
} | |
return discoveredBoxes; | |
} | |
bool hasWinned(Game game) | |
{ | |
return getDiscoveredBoxes(game)==game.width*game.height-game.bombs; | |
} | |
bool saveGame(const char* filename, Game game) | |
{ | |
int i,j; | |
FILE *file = fopen(filename, "w+"); | |
if(file==NULL){ | |
return false; | |
} | |
fprintf(file, "%d\n%d\n%d\n", game.width, game.height, game.bombs); | |
for(i=0 ; i<game.width ; i++){ | |
for(j=0 ; j<game.height ; j++){ | |
fprintf(file, "%d\n", game.board[i][j]); | |
fprintf(file, "%d\n", game.displayedBoard[i][j]); | |
} | |
} | |
fclose(file); | |
return true; | |
} | |
bool loadGame(const char* filename, Game *game) | |
{ | |
int i,j,x; | |
FILE *file = fopen(filename, "r"); | |
if(file==NULL){ | |
return false; | |
} | |
fscanf(file, "%d", &(game->width)); | |
fscanf(file, "%d", &(game->height)); | |
fscanf(file, "%d", &(game->bombs)); | |
if(!allocateMemory(game)){ | |
return false; | |
} | |
for(i=0 ; i<game->width ; i++){ | |
for(j=0 ; j<game->height ; j++){ | |
fscanf(file, "%d", &x); | |
game->board[i][j] = x; | |
fscanf(file, "%d", &x); | |
game->displayedBoard[i][j] = x; | |
} | |
} | |
fclose(file); | |
return true; | |
} | |
int main() | |
{ | |
srand(time(NULL)); | |
Game game; | |
Cursor cursor; | |
bool gameFinished=false; | |
int code=0, choice=0; | |
char c; | |
cursor.x=0; | |
cursor.y=0; | |
/*************** | |
GAME MENU | |
***************/ | |
printf("###################################################\n"); | |
printf("################ Minesweeper ####################\n"); | |
printf("###################################################\n"); | |
printf("###### 1 - Easy 8x8 10 bombs ##########\n"); | |
printf("###### 2 - Medium 15x15 45 bombs ##########\n"); | |
printf("###### 3 - Hard 20x20 80 bombs ##########\n"); | |
printf("###### 4 - Custom game ##########\n"); | |
printf("###### 5 - Resume Saved Game ##########\n"); | |
printf("###################################################\n"); | |
printf("###################################################\n\n"); | |
printf("Choose: "); | |
scanf("%d", &choice); | |
if(choice==5){ | |
if(loadGame("game", &game)){ | |
refreshScreen(game, cursor); | |
} | |
else{ | |
gameFinished=true; | |
code=1; | |
} | |
} | |
else { | |
if(choice==4){ | |
bool isOk = false; | |
while(!isOk){ | |
printf("Board width : "); | |
scanf("%d", &(game.width)); | |
printf("Board height : "); | |
scanf("%d", &(game.height)); | |
printf("Bombs : "); | |
scanf("%d", &(game.bombs)); | |
if(game.width>0 && game.width<=100 && game.height>0 && game.height<=100 && game.bombs<game.width*game.height){ | |
isOk=true; | |
} | |
else{ | |
printf("\n/!\\ Width and Height must be smaller or equal to 100.\nThe number of bombs must be smaller than the total amount of rows.\n\n"); | |
} | |
} | |
} | |
else if(choice==3){ | |
game.width=20; | |
game.height=20; | |
game.bombs=80; | |
} | |
else if(choice==2){ | |
game.width=15; | |
game.height=15; | |
game.bombs=45; | |
} | |
else{ | |
game.width=8; | |
game.height=8; | |
game.bombs=10; | |
} | |
if(allocateMemory(&game)){ | |
generateBoardData(&game); | |
refreshScreen(game, cursor); | |
} | |
else{ | |
gameFinished=true; | |
code=1; | |
} | |
} | |
/*************** | |
GAME LOOP | |
***************/ | |
struct termios termios; | |
tcgetattr(0, &termios); | |
termios.c_lflag &= (~ICANON & ~ECHO); | |
tcsetattr(0, TCSANOW, &termios); | |
while (!gameFinished) | |
{ | |
c = getchar(); | |
if(c==68){ // left | |
cursor.x=cursor.x==0?0:cursor.x-1; | |
} | |
else if(c==65){ // up | |
cursor.y=cursor.y==0?0:cursor.y-1; | |
} | |
else if(c==67){ // right | |
cursor.x=cursor.x==game.width-1?cursor.x:cursor.x+1; | |
} | |
else if(c==66){ // down | |
cursor.y=cursor.y==game.height-1?cursor.y:cursor.y+1; | |
} | |
else if(c=='f'){ // "f" for flag | |
game.displayedBoard[cursor.x][cursor.y] = FLAG; | |
} | |
else if(c==' '){ // space | |
c = game.board[cursor.x][cursor.y]; | |
if(c==BOMB){ | |
game.displayedBoard[cursor.x][cursor.y] = game.board[cursor.x][cursor.y]; | |
gameFinished=true; | |
code=2; | |
} | |
else if(c==EMPTY){ | |
discoverSurroundingBoxes(&game, cursor.x, cursor.y); | |
} | |
else{ | |
game.displayedBoard[cursor.x][cursor.y] = game.board[cursor.x][cursor.y]; | |
} | |
} | |
else if(c=='q'){ | |
gameFinished=true; | |
printf("\nWould you like to save the game ? (Y/n) "); | |
c = getchar(); | |
if(c=='Y' || c=='y' || c==10){ | |
if(saveGame("game", game)){ | |
code=4; | |
} | |
else{ | |
code=1; | |
} | |
} | |
else{ | |
code=0; | |
} | |
} | |
refreshScreen(game, cursor); | |
if(hasWinned(game)){ | |
gameFinished=true; | |
code=3; | |
} | |
} | |
freeMemory(game); | |
if(code==1){ | |
printf("\nError happened...\n"); | |
} | |
else if(code==2){ | |
printf("\nYou lost !\n"); | |
} | |
else if(code==3){ | |
printf("\nYou winned !\n"); | |
} | |
else if(code==4){ | |
printf("\nGame saved !\n"); | |
} | |
printf("Press any key to exit\n"); | |
getchar(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment