본문 바로가기

프로그램 만들기/C++

[c++] 지뢰찾기

c언어의 구조체, 함수, 파일 입출력 등등 기본적인 c언어의 기능을 연습하기 위해 만든 게임이다.


#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>

int height=10, width=10, mines=10, number=0;

#define MAX_H 27
#define MAX_W 30

struct Map
{
	bool mine;
	bool wond;
	bool flag;
	bool wall;
	int num;
} map[MAX_H+1][MAX_W+1];

struct Record
{
	char name[10];
	float time;
	int click;
	int h;
	int w;
	int m;
} record[1000];

struct state
{
	int time;
	int bomb;
} State;

void gotoxy(int x, int y);
void menu();
void ConsoleMenu();
char KeyInp();
void GameStart();
void Setting();
void ScoreBoard();
void ConsoleGame(int height, int width);
void MapInp();
void textcolor(int color_number);
void GameOver(int ypos, int xpos);
void EntZero(int ypos, int xpos);
void GameClear();
int startmine(int ypos, int xpos);

void ScoreBoard()
{
	int SBheight=height, SBwidth=width, SBmines=mines;
	system("cls");
	textcolor(15);

	gotoxy(0, 4);
	printf("num   height   width   mines     time     click     name\n");
	printf("--------------------------------------------------------\n");
	for(int i=1; i<number; i++)
		printf("%2d      %2d      %2d     %3d      %.3f    %4d     %s\n", i, record[i].h, record[i].w, record[i].m, record[i].time, record[i].click, record[i].name);
	system("pause");
	menu();
}

void MapInp()
{
	for(int i=0; i<=height+1; i++)
	{
		for(int j=0; j<=width+1; j++)
		{
			map[i][j].mine=0;
			map[i][j].wond=0;
			map[i][j].flag=0;
			map[i][j].wall=0;
			map[i][j].num=0;
			if((i==0||j==0)||(i==height+1||j==width+1)){
				map[i][j].wall=1; map[i][j].wond=1;
            }
		}
	}

	int maxmine=mines;
	int mx, my;
	srand(time(NULL));
	while(maxmine)
	{
		mx=(rand())%height+1; my=(rand())%width+1;
		if(map[mx][my].mine==0){
			map[mx][my].mine=1; maxmine--;
		}
	}
	for(int i=1; i<=height; i++)
	{
		for(int j=1; j<=width; j++)
		{
			if(map[i][j].mine==0)
			{
				for(int k=-1; k<=1; k++)
				{
					for(int l=-1; l<=1; l++)
					{
						if(k==0&&l==0) continue;
						map[i][j].num+=map[i+k][j+l].mine;
					}
				}
			}
		}
	}
}

int main()
{
	FILE *fp1=fopen("minesweeper.txt", "at");
	fclose(fp1);
	FILE *fp=fopen("minesweeper.txt", "rt");
	while (feof(fp) == 0){
		number++;
		fscanf(fp, "%d %d %d %f %d %s", &record[number].h, &record[number].w, &record[number].m, &record[number].time, &record[number].click, record[number].name);
	}
	if(number==0) number=-1; 
	fclose(fp);
	menu(); 
}

void Setting()
{
	system("mode con: cols=100 lines=30");
	int option=0, End = 1;
	gotoxy(40, 10);
	printf("= 높이 : %d =", height);
	gotoxy(40, 13); 
	printf("= 넓이 : %d =", width);
	gotoxy(40, 16);
	printf("= 지뢰 갯수 : %d =", mines);
	gotoxy(35, 10+option*3);
	printf("->");
	while(End)
    	{
		if(kbhit()==1)
		{
			char key=KeyInp();
			switch(key){
				case 'u':
					option--;
					break;
				case 'd': 
					option++; 
					break;
				case 'r':
					switch(option){
						case 0:
							height++;
							if(height>MAX_H) height--;
							break;
						case 1:
							width++;
							if(width>MAX_W) width--;
							break;
						case 2:
							mines++;
							if(mines>MAX_H*MAX_W) mines--;
							break;
					}
					break;
				case 'l':
					switch(option){
						case 0:
							height--;
							if(height<1) height++;
							break;
						case 1:
							width--;
							if(width<1) width++;
							break;
						case 2:
							mines--;
							if(mines<1) mines++;
							break;
					}
					break;
				case 'o':
					End = 0;
					break;
			}
			if(mines>=height*width) mines=height*width-1;
			if(option==3) option-=3;
			if(option==-1) option+=3;
			system("cls");
			gotoxy(40, 10);
			printf("= 높이 : %d =", height);
			gotoxy(40, 13); 
			printf("= 넓이 : %d =", width);
			gotoxy(40, 16);
			printf("= 지뢰 갯수 : %d =", mines);
			gotoxy(35, 10+option*3);
			printf("->");
		}
	}
	menu();
	system("cls");
}

void textcolor(int color_number)
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color_number);
}

void MapOutp()
{
	system("cls");
	for(int i=0; i<=height+1; i++, puts(""))
	{
		for(int j=0; j<=width+1; j++)
		{
			if(map[i][j].wall==1) {textcolor(8); printf("■");}
			else if(map[i][j].wond==0)
			{
				if(map[i][j].flag==1) {textcolor(9); printf("¶");}
				else{
					{textcolor(3); printf("■");}
				}
			}
			else
			{
				if(map[i][j].num>0){
					switch(map[i][j].num){
						case 1: textcolor(15); break;
						case 2: textcolor(14); break;
						case 3: textcolor(13); break;
						case 4: textcolor(12); break;
						case 5: textcolor(11); break;
						case 6: textcolor(10); break;
						case 7: textcolor(9); break;
						case 8: textcolor(8); break;
					}
					printf("%2d", map[i][j].num);
				}
				else printf("  ");
			}
		}
	}
	textcolor(15);
	gotoxy((width+5)*2, 1);
	printf("┌──────┐");
	gotoxy((width+5)*2, 2);
	printf("│ time  %3d  │", State.time/1000);
	gotoxy((width+5)*2, 3);
	printf("│ mine  %3d  │", State.bomb);
	gotoxy((width+5)*2, 4);
	printf("└──────┘");
}

void GameStart()
{
	int st=0;
	clock_t timest, timeed, timenow;
	int xpos=1, ypos=1, solve=0;
	MapInp();
	system("cls");
	for(int i=0; i<=height+1; i++, puts(""))
	{
		for(int j=0; j<=width+1; j++)
		{
			if(map[i][j].wall==1) {textcolor(8); printf("■");}
			else if(map[i][j].wond==0)
			{
				{textcolor(3); printf("■");}
			}
		}
	}
	textcolor(15);
	gotoxy((width+5)*2, 1);
	printf("┌──────┐");
	gotoxy((width+5)*2, 2);
	printf("│ time  %3d  │", State.time/1000);
	gotoxy((width+5)*2, 3);
	printf("│ mine  %3d  │", State.bomb);
	gotoxy((width+5)*2, 4);
	printf("└──────┘");
	
	gotoxy(xpos*2, ypos);
	record[number].click=0;
	State.time=0;
	State.bomb=mines;
	while(1)
	{
		if(st==1) {
			st=2; timest=clock(); 
		}
		if(kbhit()==1)
		{
			char key=KeyInp();
			
			switch(key){
				case 'l':
					if(xpos>1) xpos--; break;
				case 'r':
					if(xpos<width) xpos++; break;
				case 'u':
					if(ypos>1) ypos--; break;
				case 'd':
					if(ypos<height) ypos++; break;
				default:
					break;
			}
			if(key=='e')
			{
				record[number].click++;
				ONE:
				if(st!=2) st=1;
				if(map[ypos][xpos].wond==0)
				{
					map[ypos][xpos].wond=1;
					if(map[ypos][xpos].mine==1){
						if(st==1){
							map[ypos][xpos].mine=0;
							map[ypos][xpos].wond=0;
							if(startmine(ypos, xpos)==1){
								int num=0;
								for(int i=-1; i<=1; i++)
									for(int j=-1; j<=1; j++){
										num+=map[ypos+i][xpos+j].mine;
										if(map[ypos+i][xpos+j].num>0) map[ypos+i][xpos+j].num--;
									}
										
								map[ypos][xpos].num=num;
							}
							goto ONE;
						}
						else{
							timeed=clock();
							record[number].time=(float)(timeed-timest)/1000;
							GameOver(ypos, xpos);
							return;
						}
					}
					else if(map[ypos][xpos].flag==1) continue;
					else if(map[ypos][xpos].num==0) EntZero(ypos, xpos);
				}
				else if(map[ypos][xpos].num>0)
				{
					int Flag=0;
					int mine=0;
					for(int i=-1; i<=1; i++)
					{
						for(int j=-1; j<=1; j++)
						{
							if(map[ypos+i][xpos+j].flag!=map[ypos+i][xpos+j].mine) mine=1;
							Flag+=map[ypos+i][xpos+j].flag;
						}
					}
					if(Flag==map[ypos][xpos].num)
					{
						if(mine==1) GameOver(ypos, xpos);
						for(int i=-1; i<=1; i++)
						{
							for(int j=-1; j<=1; j++)
							{
								if(i==0&&j==0) continue;
								if(map[ypos+i][xpos+j].wond==0&&map[ypos+i][xpos+j].flag==0)
								{
									map[ypos+i][xpos+j].wond=1;
									if(map[ypos+i][xpos+j].num==0) EntZero(ypos+i, xpos+j);
								}
							}
						}
					}
				}
			}
			else if(key=='s')
			{
				record[number].click++;
				if(st!=2) st=1;
				if(map[ypos][xpos].wond==0)
				{
					if(map[ypos][xpos].flag==0) {
						map[ypos][xpos].flag=1; State.bomb--;
					}
					else {
						map[ypos][xpos].flag=0; State.bomb++;
					}
				}
			}
			
			solve=0;
			for(int i=1; i<=height; i++)
			{
				for(int j=1; j<=width; j++)
				{
					if(map[i][j].wond==1) solve++;
				}
			}
			if(height*width==solve+mines){
				timeed=clock();
				record[number].time=(float)(timeed-timest)/1000;
				record[number].h=height;
				record[number].w=width;
				record[number].m=mines;
				GameClear(); return;
			}
			MapOutp();
			gotoxy(xpos*2, ypos);
		}
		if(st==2){
			timenow=clock();
			if((timenow-timest)-State.time>=1000){
				State.time+=1000;
				gotoxy((width+5)*2+9, 2);
				printf("%3d", State.time/1000);
				gotoxy(xpos*2, ypos);
			}
		}
	}
}

int startmine(int ypos, int xpos)
{
	if(map[ypos-1][xpos].wall==0){
		if(map[ypos-1][xpos].mine==0)
		{
			map[ypos-1][xpos].mine=1;
			for(int i=-1; i<=1; i++)
				for(int j=-1; j<=1; j++)
					if(map[ypos-1+i][xpos+j].mine==0&&map[ypos-1+i][xpos+j].wall==0)
						map[ypos-1+i][xpos+j].num++;
			return 1;
		}
		else{
			if(startmine(ypos-1, xpos)==1) return 1;
		}
	}
	else if(map[height][xpos-1].wall==0)
	{
		if(map[height][xpos-1].mine==0)
		{
			map[height][xpos-1].mine=1;
			for(int i=-1; i<=1; i++)
				for(int j=-1; j<=1; j++)
					if(map[ypos-1+i][xpos+j].mine==0&&map[ypos-1+i][xpos+j].wall==0)
						map[ypos-1+i][xpos+j].num++;
			return 1;
		}
		else{
			if(startmine(height, xpos-1)==1) return 1;
		}
	}
	else if(map[height][width].mine==0)
	{
		map[height][width].mine=1;
		for(int i=-1; i<=1; i++)
				for(int j=-1; j<=1; j++)
					if(map[ypos-1+i][xpos+j].mine==0&&map[ypos-1+i][xpos+j].wall==0)
						map[ypos-1+i][xpos+j].num++;
		return 1;
	}
	else
	{
		if(startmine(height, width)==1) return 1;
	}
	
}

void GameClear()
{
	
	system("cls");
	for(int i=0; i<=height+1; i++, puts(""))
	{
		for(int j=0; j<=width+1; j++)
		{
			if(map[i][j].wall==1) {textcolor(3); printf("■");}
			else if(map[i][j].mine==1) {textcolor(12); printf("¶");}
			else if(map[i][j].num==0) {textcolor(15); printf("  ");}
			else {textcolor(15); printf("%2d", map[i][j].num);}
		}
	}
	textcolor(15);
	gotoxy((width+5)*2, 1);
	printf("┌──────┐");
	gotoxy((width+5)*2, 2);
	printf("│ time  %3d  │", State.time/1000);
	gotoxy((width+5)*2, 3);
	printf("│ mine  %3d  │", State.bomb);
	gotoxy((width+5)*2, 4);
	printf("└──────┘");
	gotoxy(0, height+2);
	textcolor(3);
	printf("%.3f ", record[number].time);
	printf("(*^-^*)\n");
	printf("6글자 영문으로 이름을 입력해주세요 : ");
	scanf("%s", &record[number].name);
	
	FILE *fp=fopen("minesweeper.txt", "at");
	fprintf(fp, "%d %d %d %f %d %s\n", record[number].h, record[number].w, record[number].m, record[number].time, record[number].click, record[number].name);
	fclose(fp);
	
	system("pause");
	number++;
	menu();
}

void EntZero(int ypos, int xpos)
{
	for(int i=-1; i<=1; i++)
	{
		for(int j=-1; j<=1; j++)
		{
			if(map[ypos+i][xpos+j].mine==1) continue;
			if(map[ypos+i][xpos+j].flag==1) continue;
			if(i==0&&j==0) continue;
			if(map[ypos+i][xpos+j].wond==1) continue;
			map[ypos+i][xpos+j].wond=1;
			if(map[ypos+i][xpos+j].num==0) EntZero(ypos+i, xpos+j);
		}
	}
	return;
}

void GameOver(int ypos, int xpos)
{
	system("cls");
	for(int i=0; i<=height+1; i++, puts(""))
	{
		for(int j=0; j<=width+1; j++)
		{
			if(map[i][j].wall==1) {textcolor(5); printf("■");}
			else if(map[i][j].mine==1){
				if(i==ypos&&j==xpos) {textcolor(76); printf("※");}
				else {textcolor(12); printf("※");}
			}
			else
			{
				if(map[i][j].flag==1){textcolor(4); printf("¶");}
				else if(map[i][j].num>0){ 
					textcolor(16-map[i][j].num);
					if(i==ypos&&j==xpos) {textcolor(80-map[i][j].num);}
					printf("%2d", map[i][j].num);
				}				
				else {textcolor(15); printf("  ");}
			}
		}
	}
	textcolor(15);
	gotoxy((width+5)*2, 1);
	printf("┌──────┐");
	gotoxy((width+5)*2, 2);
	printf("│ time  %3d  │", State.time/1000);
	gotoxy((width+5)*2, 3);
	printf("│ mine  %3d  │", State.bomb);
	gotoxy((width+5)*2, 4);
	printf("└──────┘");
	gotoxy(0, height+2);
	textcolor(3);
	printf("%.3f ", record[number].time);
	printf("(ㅠ_ㅠ)\n");
	system("pause");
	menu();
}

void gotoxy(int x, int y)
{
	COORD pos={x, y};
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

void menu()
{
	textcolor(15);
	ConsoleMenu();
	int menu=0, End = 1;
	system("cls");
	gotoxy(40, 10);
	printf("=게임 시작=");
	gotoxy(40, 13); 
	printf("=설정=");
	gotoxy(40, 16);
	printf("=스코어보드=");
	gotoxy(35, 10+menu*3);
	printf("->");
	while(End)
	{
		if(kbhit()==1)
		{
			char key=KeyInp();
			switch(key){
				case 'u':
					menu--;
					break;
				case 'd': 
					menu++; 
					break;
				case 'e':
					End = 0;
					break;
			}
			if(menu==3) menu-=3;
			if(menu==-1) menu+=3;
			system("cls");
			gotoxy(40, 10);
			printf("=게임 시작=");
			gotoxy(40, 13); 
			printf("=설정=");
			gotoxy(40, 16);
			printf("=스코어보드=");
			gotoxy(35, 10+menu*3);
			printf("->");
		}
	}
	system("cls");
	if(menu==0) GameStart();
	if(menu==1) Setting();
	if(menu==2) ScoreBoard();
}

char KeyInp()
{	
	int key=getch();
	if(key==224)
	{
		key=getch();
		switch(key){
			case 72:
				return 'u';
				break;
			case 75:
				return 'l';
				break;
			case 77:
				return 'r';
				break;
			case 80:
				return 'd';
				break;
		}
	}
	else if(key==13)
	{
		return 'e';
	}
	else if(key==27)
	{
		return 'o';
	}
	else if(key==32)
	{
		return 's';
	}

}

void ConsoleMenu()
{
	system("mode con: cols=100 lines=30");
	system("title 지뢰찾기");
}

 

함수는 크게 4가지로 나뉜다. 게임 이전 설정, 게임 중 알고리즘, 게임 외적 부분, 도구 함수

게임 이전 설정에는 ConsoleMenu, Setting, MapInp, Startmine

게임 중 알고리즘에는 GameStart, GameOver, EntZero, GameClear, MapOutp

게임 외적 부분에는 Menu, ScoreBoard, main

도구 함수에는 gotoxy, KeyInp, textcolor

프로그램의 구조