콘솔 창을 통해 진행할 수 있는 빙고 게임을 만들겠습니다.
기본적으로 3 x 3 크기의 빙고로 진행됩니다.
빙고 한 줄의 사이즈만큼 빙고를 완성한 사람이 승리합니다.
3 x 3 크기의 빙고니까 3개의 빙고를 먼저 완성하는 쪽이 승리하죠.
게임이 시작되면 메인 메뉴에서 플레이어는
고를 수 있는 빙고 카테고리는 3개가 있습니다.
동물, 과일, 숫자이죠.

선택한 후에는 빙고 칸에 원하는 데이터를
하나씩 입력하게 됩니다. 모두 입력하면 본격적으로 빙고 게임이
시작되는데 먼저 CPU 빙고가 하나 오픈이 되며 플레이어는
방향키와 엔터를 통해 오픈하고 싶은 데이터를 선택합니다.

이후 다시 CPU가 자신의 빙고를 하나 오픈하고,
이를 플레이어와 CPU 중 빙고 3개가 먼저 완성될 때까지
반복합니다. 다음에는 승자를 판별하고 게임이 종료됩니다.

게임이 종료된 후에는 엔터를 누르면 다시 메인 메뉴 화면으로
넘어가고 빙고 카테고리를 골라 게임을 재시작할 수 있습니다.
작성한 파일은 총 네 개입니다.
게임이 진행되는 파일인 'main.cpp' 파일과
빙고가 진행되기 위해 필요한 모든 클래스와 객체, 함수가
포함된 'Bingo.h' 파일, main.cpp 파일과 Bingo.h 파일에서
사용되는 상수와 외부 변수, 구조체, 함수의 선언은 'BingoFunc.h' 파일에
이들에 대한 정의는 'BingoFunc.cpp' 파일에 있습니다.
그럼 먼저 main 함수를 살펴 볼까요?

Bingo.h 파일은 빙고를 진행하는데 필요한 클래스가
선언되고 정의된 파일입니다.
12번째 줄에 있는 setlocale 함수는 wcout을 통해
한글을 잘 출력하게 하기 위한 함수이죠.
14번째 줄에 있는 tGAME_MODE형
selectedGameMode 객체가 무엇인지 봅시다.

tGAME_MODE 구조체는 BingoFunc.h 파일에 선언되어 있습니다.
플레이어가 메인 메뉴에서 어떤 빙고의 카테고리를
고르냐에 따라 객체 selectedGameMode의 멤버 변수의
값이 정해집니다. 이후에 멤버 변수의 값을 통해
다양한 빙고 카테고리 중 하나가 선택되어 진행이 되는 것이죠.
근데 플레이어 입장에서는 동물, 과일, 숫자 총 세 개의 카테고리만
보이지만 이 중 동물과 과일은 카테고리는 한글 단어에 속하고
숫자는 숫자에 속하죠. 이것을 모드로 분리할 겁니다.
즉 동물과 과일은 '단어 모드'이고 숫자는 '숫자 모드'인 것이죠.
분리하는 이유는 모드에 따라 경고 메시지를 다르게 출력하기 위함이죠.
main 함수에서 16번째 줄을 보시면
Make_GameMode 함수가 있습니다.
Make_GameMode 함수는
int형 인자를 두 개 받습니다.
첫 번째 인자는 단어 모드의 게임 개수,
두 번째 인자는 숫자 모드의 게임 개수입니다.

현재 단어 모드는 동물과 과일, 총 두 개의 카테고리입니다.
그래서 상수 WORD_GAME_MODE_COUNT는 2를 나타냅니다.
숫자 모드는 숫자로 한 개이죠.
그래서 상수 NUM_GAME_MODE_COUNT는 1을 나타냅니다.

먼저 for 문을 통해 단어 모드의 게임을 만듭니다.
i값이 1부터 makeWordGameModeCount와 같거나 작을 때까지
계속 반복하게 되죠. 여기서 makeWordGameModeCount의 값은 2겠죠?
121번째 줄을 보면 gameModeKind라는 객체가 나옵니다.

위 코드에서 알 수 있듯이 map형이며
키값은 int이고 데이터는 tGAME_MODE로 된 객체이죠.
객체 gameModeKind에 먼저 단어 모드인 카테고리부터 넣어줄 겁니다.
처음에는 i값이 1부터이므로 키값이 4이고 second(tGAME_MODE)의 멤버 Mode의 값이 100,
멤버 Category의 값이 101인 노드가 만들어질 겁니다.
다음에는 i값이 2이므로 키값이 5이고 second(tGAME_MODE)의 멤버 Mode의 값이 100,
멤버 Category의 값이 102인 노드가 만들어질 겁니다.
이후 숫자 모드인 카테고리를 삽입해 줄 겁니다.
마지막 단어 모드 카테고리의 바로 뒤에 삽입이 되어야 하므로
end iterator로부터 바로 이전의 위치에 삽입하면 되겠죠?
(end iterator는 마지막 노드의 다음을 가리키고 있으므로.)
마지막에 삽입되었던 노드의 키값은 5입니다.
여기에 i값 1을 더하여 숫자 카테고리의 새로운 노드를
만드므로 키값이 6이고 second(tGAME_MODE)의 멤버 Mode의 값이 200,
멤버 Category의 값이 201인 노드가 만들어질 겁니다.
이제 메인 메뉴를 출력해 줄 겁니다.

이후 게임이 종료되어도 프로그램을 종료하지 않고
다시 재시작할 수 있도록 할 것이므로 while 문을 통해
계속 반복하도록 합니다.
그리고 Menu 함수를 호출합니다.

Menu 함수에서 selectedGameMode의 값이
정해질 겁니다. 그래서 selectedGameMode를 레퍼런스로
참조하는 것이죠. 해당 함수가 호출되면 우선
메인 메뉴 화면, 즉 문자열을 출력합니다.
141번째 줄에 있는 변수 KBD_input는 키보드 입력을 받을 변수입니다.
143번째 줄에 있는 Pos형 curPos에 첫 번째 빙고 카테고리의 좌표를
저장합니다. 여기서는 동물 카테고리겠죠.
이를 위해 게임 모드가 저장되어 있는 gameModeKind의
첫 번째 노드에 접근하여 first, 즉 첫 번째 노드의 키값을
얻습니다. 이 키값을 y 좌푯값으로 사용하는 것이죠.
y 좌푯값이 각 카테고리를 구분해 주는 고유의 값인 것이죠.
구조체 Pos는 두 개의 좌표를 저장하는 구조체입니다.

145번째 줄에 있는 Print_selectCharacter 함수는

선택할 것을 가리키는, 손가락 기호를 출력하는 함수입니다.
x 좌표와 y 좌푯값을 받아 해당 위치에 손가락 기호를 출력하죠.
gotoxy 함수는 커서를 해당 위치로 옮기는 함수이고,
textColor 함수는 글자 색과 배경 색 값을 인자로 받아
콘솔 상에서 출력되는 색을 바꿔주는 함수이죠.


다시 Menu 함수를 이어서 봅시다.

엔터가 입력될 때까지 방향키 입력을 받는 상태가 됩니다.
_kbhit 함수는 키보드가 입력되었는지 입력되지 않았는지
알려주는 함수입니다. 만약 키보드 입력이 들어오면
166번째 줄에 있는 if 문 안의 코드가 실행되겠죠.
키보드 입력이 들어오면 어떤 값이 들어왔는지
_getch 함수를 통해 변수 KBD_input에 저장합니다.
(_kbhit와 _getch 함수는 conio.h 파일을 include 해야 사용 가능합니다.)
170번째 줄에서 KBD_input의 값이 224와 같다는 조건이
있습니다. 이것이 의미하는 것은 키보드에서 방향키를 입력하면
두 개의 값이 반환됩니다. 우선 224를 받고 이후 방향에 대응하는 값이
반환되죠. 즉 224가 반환된다는 것은 방향키가 입력되었다는 것이죠.
방향에 대응하는 값은 아래와 같습니다.

키보드 입력이 들어왔다는 것은
손가락 기호가 특정 방향으로 움직이는 것처럼 보여야 한다는 것입니다.
그래서 기존 위치에 존재하는 손가락 기호를 지우고
새로운 위치에 손가락 기호를 다시 출력해야 하죠.
그래서 Delete_selectCharacter 함수를 호출하여
현재 커서가 있는 곳에 있는 기호를 지웁니다.
Delete_selectCharacter 함수는

Pos형 객체 하나를 레퍼런스로 참조하여
해당 객체의 x 좌표와 y 좌표로 커서를 이동한 후,
공백을 출력함으로써 기존의 기호를 지웁니다.
여기서 글자색을 LIGHTGRAY로, 배경색을 BLACK으로
하는 이유는 기본 콘솔 창의 글자색과 배경색이 위와 같기 때문이죠.
다시 Menu 함수로 돌아와서,
사실 224와 같은지 비교하지 않아도 방향에 대응하는 값이
알아서 반환되며 KBD_input에 저장됩니다. 그럼에도 224와 같은 경우에만
코드를 실행하게 한 이유는 바로 아스키 코드 때문입니다.
177번째 줄의 switch 문을 통해 KBD_input의 값에 따라
코드가 동작하게 되어 있죠? 만약 아래를 입력하여 KBD_input에
80이 저장되었습니다. 그럼 184번째 줄에 있는 case가 실행되겠죠.
문제는 대문자 'P'의 값도 아스키 코드상에서 80입니다.
이 말은 아래 방향키가 아닌 대문자 P를 입력해도
184번째 줄에 있는 case가 실행된다는 것입니다. 이는 의도한 것이
아니므로 224와 같은 경우에만, 즉 방향키 입력인 경우에만
코드를 실행하게 한 것이죠.
방향키 입력이 맞아 170번째 줄의 if 문으로 들어오면
다시 _getch 함수를 통해 정확히 어떤 방향키가 입력되었는지
반환받아 KBD_input에 대입합니다.
이때 KBD_input의 값이 72면, 즉 방향키 위쪽 입력이 들어오면
179번째 줄에 있는 case가 실행되겠죠.
만약 이미 손가락 기호가 gameModeKind의 첫 번째 노드를
가리키고 있지 않다면, 즉 첫 게임 카테고리의 키값(y 좌푯값)과
현재 손가락 기호가 있는 curPos의 y 좌푯값과 같지 않은 경우에만
curPos의 y 좌푯값을 하나 뺍니다.
이후 204번째 줄에 있는 Print_selectCharacter 함수를 통해
y 좌푯값이 하나 줄어든 위치에 손가락 기호가 출력되겠죠.
방향키 아래 입력도 마찬가지로 작동합니다.
현재 손가락 기호가 있는 curPos의 y 좌푯값과 gameModeKind의
마지막 노드의 키값(y 좌푯값)과 같지 않은 경우에만
curPos의 y 좌푯값을 하나 더하죠.
이후 204번째 줄에 있는 Print_selectCharacter 함수를 통해
y 좌푯값이 하나 더해진 위치에 손가락 기호가 출력됩니다.
플레이어가 빙고 카테고리를 선택하여
엔터를 입력하면 195번째 줄에 있는 else if 문이 실행됩니다.
gameModeKind의 노드들 중에서 현재 가리키고 있던 y 좌푯값과
같은 키값을 가진 노드를 find 함수를 통해 찾습니다.
그리고 해당 노드의 second의 Mode로 접근하여
그 값을 인자로 받은 selectGameMode의 멤버 Mode에 대입합니다.
selectGameMode의 멤버 Category에도 마찬가지로 값을 대입합니다.
'C++ > 빙고 게임 제작' 카테고리의 다른 글
| C++ 빙고 게임 제작 : 게임 결과 출력 (0) | 2024.06.14 |
|---|---|
| C++ 빙고 게임 제작 : 빙고 게임 진행 (0) | 2024.06.13 |
| C++ 빙고 게임 제작 : 빙고 데이터 입력 (0) | 2024.06.12 |