안녕하십니까? 이번 포스팅에서는 지뢰찾기 구현을 위해 제가 만든 함수들을 살펴보겠습니다. 원래는 한꺼번에 설명하려고 했습니다만 워낙 소스코드가 길다보니 따로 빼서 설명하는 것이 좀더 나을 것 같아 먼저 설명드리겠습니다.
지뢰찾기에 쓰인 함수들은 다음과 같습니다.
void Createlandmine(void); - 지뢰배치함수
void Createlandmine2(void); - 지뢰를 이용하여 기타 타일을 배치하는 함수
void CreateButton(void); - 버튼을 생성하는 함수
void sp_check(void); - 주변에 지뢰가 없는 타일(공백타일)을 체크하는 함수
void victoryCheck(void); - 승리조건에 도달했는가를 체크하는 함수
Createlandmine와 Createlandmine2함수는 앞 포스팅에서 설명드렸으니 넘어가겠습니다. 자 이제 차례대로 함수를 살펴 봅시다.
- void CreateButton(void)
함수명 그대로 버튼을 만드는데 쓰이는 함수입니다.
- int i,j,k=0;
int m = 0;
int x=30,y=30;
for문에 쓰일 i,j 변수와 OpenClose_Tile, flagTile에 쓰일 인덱스변수 k를 선언하였습니다. OpenClose_Tile은 눌러진 타일인지, 안눌러진 타일인지의 여부를 저장하는 정수타입 전역 변수 배열 입니다. flagTile은 깃발이 있는지 없는지의 여부를 저장하는 정수타입 전역 변수 배열입니다.
m은 buttonHwnd의 인덱스 변수 겸 메뉴 이름입니다. buttonHwnd은 버튼들의 핸들을 저장시키는 핸들 타입 전역 변수 배열입니다.
x와 y는 버튼을 생성시킬 때의 좌표를 뜻합니다.
- for(i = 0; i <9 ;i++)
for(j = 0; j <9 ;j++)
초급지뢰찾기는 9*9 형태이므로 이 형태로 버튼을 만들어 출력하기 위해 2중 for문을 넣었습니다.
- buttonHwnd[m] = CreateWindow(L"button",NULL,WS_CHILD | WS_VISIBLE | BS_BITMAP,x,y,18,18,Global_hWnd, (HMENU) m,Global_hInstance,NULL);
CreateWindow함수로 버튼을 생성하여 buttonHwnd에 저장시킵니다. 이 때 버튼의 좌표는 아까 선언한 x와 y입니다. 크기는 18*18이이고 이름은 m입니다. 인자들을 보면 Global_hWnd와 Global_hInstance가 들어있는 것을 볼 수 있는데 Global_hWnd는 WndProc함수의 hWnd인자를 사용하기 위해 선언한 전역변수이고 Global_hInstance는 WinMain의 hInstance인자를 사용하기 위한 전역변수입니다.
그다음 눈에 띄는 인자는 바로 BS_BITMAP 입니다. MSDN의 설명을 봅시다.
버튼에 비트맵을 표시한다는 옵션이지요. 버튼에 원하는 색상을 주기 위해 이 옵션을 넣었습니다.
- SendMessage(buttonHwnd[m], BM_SETIMAGE,IMAGE_BITMAP,(LPARAM)LoadBitmap(Global_hInstance, MAKEINTRESOURCE(Tile)));
새로운 함수가 나왔습니다. 이 함수를 살펴봅시다.
말그대로 메세지를 보내는 함수입니다. 인자는 WndProc의 인자와 똑같습니다.
정리하면 이 SendMessage함수는 hWnd값의 윈도우 창에 메세지를 보내는 것입니다. 그러면 받은 hWnd값의 윈도우 창은 WndProc에서 받은 메세지를 처리하게 됩니다.
여기서 hWnd는 각 버튼의 핸들입니다. Msg로는 BM_SETIMAGE가 들어갔는데 이 메세지는 버튼의 이미지를 설정하는 메세지라고 보시면 됩니다. 세번째와 네번째 인자는 메세지의 추가정보 이지요. 먼저 wParam에는 IMAGE_BITMAP이 들어갔습니다. 앞서 메세지에서 BM_SETIMAGE인자를 넣었는데 그냥 이미지하면 커서인지, 아이콘인지, 비트맵인지 구별을 못하므로 비트맵 이미지를 버튼에 설정하기 위해 세번째 인자에 IMAGE_BITMAP가 들어갔습니다.
마지막 인자에선 LoadBitmap함수가 쓰였습니다. 말 그대로 이함수는 비트맵을 불러오는 함수인데 LoadIcon과 맥락이 비슷하다고 보시면 됩니다. 인자로는 MAKEINTRESOURCE(Tile)이 쓰였습니다. 즉 비트맵리소스를 가져오신다고 보시면 됩니다. 이 리소스는 나중에 지뢰찾기 코드를 설명할 때 자세히 설명드리겠습니다.
마지막으로 LoadBitmap함수는 반환값이 HBITMAP이므로 (LPARAM)으로 강제형변환을 해주었습니다.
정리하면 이 sendMessage함수는 버튼의 BS_BITMAP 옵션을 사용할 때 호출되는 BM_SETIMAGE에 추가정보를 주기 위해 사용되었다고 보시면 됩니다. 이렇게 보낸 BM_SETIMAGE메세지에 의해 버튼은 비트맵 이미지를 받아 출력하게 됩니다.
다음으로 넘어갑시다.
- OpenClose_Tile[k] = 1;
flagTile[k] = 1;
초기의 OpenClose_Tile, flagTile의 인덱스들의 값을 1로 설정합니다. 즉 OpenClose_Tile의 1은 버튼이 눌려지지 않은 상태임을 나타내고 flagTile의 1은 깃발이 꽃혀있지 않다는 것을 의미합니다.
- k = k+1;
m += 1;
다음 인덱스를 지정하기 위해 변수들을 1씩 증가시킵니다.
- x += 18;
다음 버튼을 만들기 위해 x의 좌표를 버튼의 크기만큼 증가시킵니다. 이렇게하면 버튼이 오른쪽으로 하나씩 생성됩니다.
- x = 30;
y += 18;
한줄의 버튼을 다 만들었다면 다음 줄의 버튼을 만들기 위해 x를 처음 버튼위치인 30으로 초기화 시키고, y를 버튼의 크기만큼 증가시킵니다.
최종적으로 이 CreateButton함수가 처리되고 나면 각각의 버튼 이름이 (0~80)인 버튼 81개가 생성됩니다.
다음 함수로 넘어갑시다.
- void sp_check(void)
이 함수는 모든 타일을 검사하여 타일이 9값이고 눌러진 상태일경우, 즉 주변에 지뢰가 없는 타일의 버튼이 벗겨진 경우 그 타일의 주변의 버튼을 없애는 기능을 합니다.
기존 지뢰찾기에서는 누르면 소리가 나면서 버튼이 크게 벗겨지지요? 그것을 구현한 겁니다.
- int i;
81개의 타일을 검사하기 위한 인덱스 변수를 선언합니다.
- for(i=0;i<81;i++)
81개의 타일을 검사하기 위해 선언하였습니다.
- if(buttonValue[i] == 9 && OpenClose_Tile[i] == 0)
buttonValue의 값이 9이고 OpenClose_Tile의 값이 0일 경우 if문을 실행합니다. 여기서 buttonValue는 Createlandmine2함수로 만들어진 arr[9][9]에 들어있는 값을 한 배열에 저장하기 위해 선언한 정수형 전역 변수 배열 입니다.
OpenClose_Tile의 값은 1과 0으로 나뉘어 지는데 1은 버튼이 안눌러진 상태이고 0은 버튼이 눌러진 상태를 의미합니다.
즉 정리하면 현재 타일이 버튼이 벗겨진 상태의 9값 타일 인 경우를 의미합니다.
이 다음부터는 전 포스팅에서 얘기했던 Createlandmine2의 알고리즘을 이용하여 주변 타일을 삭제하고 OpenClose_Tile의 값을 0으로 지정해주는 코드입니다. 그러므로 일부분만 설명드리겠습니다.
- if(i <= 8)
switch(i)
case 0:
if(DestroyWindow(buttonHwnd[i+1]))
OpenClose_Tile[i+1] = 0;
if(DestroyWindow(buttonHwnd[i+9]))
OpenClose_Tile[i+9] = 0;
if(DestroyWindow(buttonHwnd[i+10]))
OpenClose_Tile[i+10] = 0;
break;
case 8:
.......
break;
default:
break;
i가 8보다 작거나 같을 경우입니다. 아까 CreateButton함수로 인해 버튼들의 핸들은 0~80까지의 인덱스들에 각각 저장되어있습니다. 즉 i가 8보다 작거나 같다는 것은 첫번째 줄을 의미합니다.
switch(i)는 i값에 따라 처리를 다르게 하기 위해 선언하였습니다. 아까의 Createlandmine2알고리즘으로 따지자면 j값을 의미합니다.
이 i값이 0이라면 i=0,j=0을 뜻하고 이 i값이 8이면 i=0,j=8을 뜻하고 그 외의 값(1~7)이라면 i=0, j= (1~7)까지를 나타내지요.
자 이제 처리부분을 한 번 봅시다.
DestroyWindow함수를 사용하여 핸들을 삭제합니다. 이 함수는 말그대로 인자에 해당하는 핸들을 가진 윈도우 창을 부수는 함수입니다. 즉 버튼을 삭제한다는 뜻입니다. 버튼이 삭제된다는 것은 벗겨진다는 것을 의미하지요. 그리고 버튼을 삭제한 이후 해당하는 OpenClose_Tile의 값을 0으로 줍니다. 벗겨졌으니까 OpenClose_Tile의 값을 바꿔주어야 겠지요.
여기서 궁금점이 하나 생길 수 있는데 그것은 바로 if문입니다. 이러한 if문을 준 이유는 버튼을 지우다보면 이미 버튼이 지워진 부분도 다시 버튼을 지우고 OpenClose_Tile의 값을 0으로 주기 때문에 이를 방지하기 위해 if문을 넣었습니다. if문이 실패하면 OpenClose_Tile에 값을 지정하지 않도록 하기 위해서입니다. 사실 if문이 있든 말든 동작에 지장은 없기 때문에 이 부분은 딱히 신경쓰지 않으셔도 됩니다. 편하실 대로 하십시요.
나머지 case와 default는 비슷하기 때문에 생략하였습니다.
- else if(i >= 72)
switch(i)
case 72:
......
break;
case 80:
......
break;
default:
......
break;
i가 72보다 크거나 같을 경우입니다. 즉 마지막 줄을 의미합니다. i값이 바꼈으므로 case의 값도 그에 맞게 바꿔주어야 합니다. 처리코드는 비슷하기에 생략하였습니다.
- else
switch(i%9)
case 0:
......
break;
case 8:
......
break;
default:
......
break;
그 외의 줄을 처리하는 부분입니다. 즉 9<= i <= 71 인 부분이지요.
여기서 하나 유심히 보셔야 할부분은 switch(i%9)입니다. i값에 9를 나눈 나머지 값을 이용하여 case가 작동합니다. 이렇게 한 이유는 i값의 범위가 크다보니 일일이 case문을 작성해야하는데 너무 비효율적이라 생각하였기 때문입니다. i를 9로 나눈 나머지 값이 0이라면 첫번째 열을 의미하고, 나머지가 8이라면 마지막 열을 의미하고 그 외의 나머지 값은 중간열을 의미하므로 훨씬 간편하게 case문을 작성할 수 있습니다.
이제 마지막 함수를 살펴봅시다.
- void victoryCheck(void)
승리조건에 도달하였는지를 체크하는 함수입니다. 원리는 열려진 타일의 개수를 검사하여 열려진 타일이 10개(지뢰개수)일 경우 승리 메세지박스가 출력됩니다.
- int i;
int tilecount = 0;
검사를 하기 위한 인덱스 변수 i와 타일의 개수를 저장시킬 변수 tilecount를 선언하였습니다.
- for(i=0;i<81;i++)
if(OpenClose_Tile[i] == 1)
tilecount = tilecount+1;
타일을 검사하여 열린 타일의 개수를 체크합니다.
- if(tilecount == 10)
MessageBox(Global_hWnd,L"지뢰를 다찾았습니다. 승리!",
L"승리!",MB_ICONQUESTION | MB_OK);
for(i=0;i<81;i++)
if(buttonValue[i] == 0)
DestroyWindow(buttonHwnd[i]);
타일의 개수가 10개일 경우 승리 메세지박스를 띄우고 for문을 사용하여 남아있는 버튼을 전부 삭제합니다.
여기까지가 제가 작성한 함수들입니다. 다음포스팅에서는 리소스를 보여드리도록 하겠습니다.
'Programing > 프로그램 직접 개발하기(C)' 카테고리의 다른 글
지뢰찾기 만들기 - 소스 (3) | 2017.01.07 |
---|---|
지뢰찾기 만들기 - 리소스 (0) | 2017.01.07 |
지뢰찾기 만들기 - 알고리즘 (0) | 2016.12.24 |
지뢰찾기 만들기 - 시작 (0) | 2016.12.24 |
codeengn bagic 03 직접 개발하기 (완) (0) | 2016.12.11 |