강께르의 개발일지

[C++] 숫자야구 게임 본문

연습

[C++] 숫자야구 게임

강께르 2021. 6. 8. 17:48

1. 이것은 무엇인가?

 컴퓨터가 무작위로 고른 4자리의 숫자를 맞추는 게임이다. 자릿수와 숫자가 맞은 것 하나당 스트라이크 하나로 인정하고, 자릿수는 다르지만 숫자가 맞은 것은 볼 하나로 인정한다. 이렇게 해서 스트라이크 4개 즉, 모든 숫자와 자릿수를 알아 맞히면 홈런이라고 하여 정답 처리를 하고 게임을 끝낸다.

 숫자야구 진행 순서는 "시작 출력문 - 난이도 설정 - (플레이어 입력 - 입력한 값에서 정답 판단 처리 - 그에 대한 결과 출력) - 게임 종료 출력문"으로 이루어져있다.

 

2. 새로 알게 된 점

- int형배열을 4개의 요소로 하나 정의하고 그곳에 입력을 받으려고 시험삼아 1234를 입력했는데 아뿔싸, 첫번째 배열요소에 1234라고 때려박는 걸 발견했다.

- 이에 대한 고민으로 문자열은 숫자를 하나 하나 단일문자로 배열요소에 저장한다는 것을 알게 되어 문자 배열로 플레이어의 입력을 받고 정답 판단을 하려고 했다.

- 그런데 컴퓨터는 무작위로 설정하는 숫자는 int형이어서 플레이어 입력과 자료형이 달라 비교할 수 없던 것이다.

- 그래서 알아보니 단일 문자를 int형으로 캐스팅하면 아스키코드 값이 나오는데 그 아스키코드 값에 48을 빼면 정수값을 얻을 수 있다는 것이다.

 

3. 코드

#include<iostream>
#include<Windows.h>
#include<ctime>
using namespace std;

enum DIFF
{
	EASY = 1,
	NORMAL,
	HARD
};
enum COUNT
{
	STRIKE,
	BALL,
	OUTGAME
};

void SetConsoleView(void);
void ShowTitle(void);
void RandomChooseNum(int*);
void CountStkBall(int*, int*, int*);
bool IsGameover(int&, int);
bool ShowCntRes(int*);
int SetDifficulty(int*);
int ConvertCharInt(char);

const int arrLength = 4;

int main(void)
{
	int difficulty[3] = { 20, 15, 10 };
	int gameCount = 20;
	int countArr[3] = { 0, };
	bool isNotGameover = true;

	int computerChooseNum[arrLength] = { 0, };
	int playerChooseNum[arrLength] = { 0, }; // 아스키 코드값을 빼서 플레이어 선택한 진짜 정수를 저장
	char playerinputNum[arrLength] = { 0, }; // 플레이어의 입력을 받을 배열 
											// 문자열로 입력 시의 캐스팅할 필요 없이 손쉽게 저장
	RandomChooseNum(computerChooseNum);

	SetConsoleView();
	ShowTitle();
	gameCount = SetDifficulty(difficulty);
	cout << "===============================================================" << endl;
	Sleep(2000);

	while (isNotGameover)
	{
		cout << "당신에게 " << gameCount << "번의 기회가 있습니다. " << endl;
		cout << "컴퓨터가 생각하는 네 자리의 수를 입력 : ";
		for (int i = 0; i < arrLength; i++)
			cin >> playerinputNum[i];

		cout << "===============================================================" << endl;
		cout << "플레이어가 선택한 숫자 : ";
		for (int i = 0; i < arrLength; i++)
		{
			playerChooseNum[i] = ConvertCharInt(playerinputNum[i]);
			cout << playerChooseNum[i];
		}
		cout << endl;

		CountStkBall(computerChooseNum, playerChooseNum, countArr);

		if (ShowCntRes(countArr) == true)
		{
			cout << "HOME RUN!" << endl;
			Sleep(1000);
			cout << "컴퓨터가 생각한 숫자를 " << gameCount << "회 남겨놓고 모두 맞췄습니다!" << endl;
			Sleep(1000);
			cout << "컴퓨터가 생각한 숫자 : ";
			for (int i = 0; i < arrLength; i++)
				cout << computerChooseNum[i] << " ";
			cout << endl;
			cout << "===============================================================" << endl;
			break;
		}
		Sleep(2000);


		isNotGameover = !(IsGameover(gameCount, countArr[COUNT::OUTGAME]));
	}

}

// 게임오버 체크 함수
bool IsGameover(int& gameCnt, int outCnt)
{
	bool result = false;
	gameCnt--;
	if ((gameCnt < 1) || (outCnt > 3))
		result = true;
	return result;
}

void SetConsoleView(void)
{
	system("mode con:cols=64 lines=100");
	system("title 숫자야구");
}

void ShowTitle(void)
{
	cout << "===============================================================" << endl;
	cout << "===============================================================" << endl;
	cout << "============숫========자==========야=========구================" << endl;
	cout << "===============================================================" << endl;
	cout << "===============================================================" << endl;
}

// 게임 시작 시 컴퓨터의 숫자를 미리 정해놓는 함수
void RandomChooseNum(int* arr)
{
	srand((unsigned int)time(NULL));
	for (int i = 0; i < arrLength; i++)
	{
		arr[i] = rand() % 10;
		if (i > 0)
		{
			for (int j = 0; j < i; j++)
			{
				if (arr[i] == arr[j]) // 지금 선택한 숫자가 이전에 있었냐?
				{
					i--;
					break;
				}
			}
		}
	}
}

// 플레이어가 입력한 수와 컴퓨터의 수를 비교해 스트라이크와 볼을 세는 함수
void CountStkBall(int* comArr, int* plyArr, int* cntArr)
{
	for (int comIdxCnt = 0; comIdxCnt < arrLength; comIdxCnt++) // 컴퓨터 인덱스
	{
		for (int plyIdxCnt = 0; plyIdxCnt < arrLength; plyIdxCnt++) // 플레이어 인덱스
		{
			if (comArr[comIdxCnt] == plyArr[plyIdxCnt]) // 숫자가 맞으면
			{
				if (comIdxCnt == plyIdxCnt) // 자릿수까지 맞으면 스트라이크
					cntArr[COUNT::STRIKE]++;
				else if (comIdxCnt != plyIdxCnt) // 자릿수가 다르면 볼
					cntArr[COUNT::BALL]++;
			}
		}
	}
}

// 스트라이크와 볼 갯수를 알려주는 함수, 홈런일 경우 true를 반환
bool ShowCntRes(int* cntArr)
{
	bool result = false;
	if ((cntArr[COUNT::STRIKE] == 0) && (cntArr[COUNT::BALL] == 0)) // 0 스트라이크 0 볼일때
	{
		cntArr[COUNT::OUTGAME]++;
		cout << "맞는 숫자가 없습니다!" << endl << endl;
		cout << cntArr[COUNT::OUTGAME] << "OUT!!" << endl;
	}
	else
	{
		cout << "맞는 숫자가 있습니다!" << endl << endl;;
		Sleep(2000);
		if (cntArr[COUNT::STRIKE] == arrLength)
			result = true;
		else if (cntArr[COUNT::STRIKE] > 0)
			cout << cntArr[COUNT::STRIKE] << "STRIKE ";
		if (cntArr[COUNT::BALL] > 0)
			cout << cntArr[COUNT::BALL] << "BALL";
		cntArr[COUNT::STRIKE] = 0;
		cntArr[COUNT::BALL] = 0;
		Sleep(1000);
		cout << endl;
		cout << "===============================================================" << endl;
	}

	return result;
}
// 난이도 설정으로 게임 횟수를 정함
int SetDifficulty(int* diffArr)
{
	int diff;
	int gameCnt = 0;
	cout << "난이도를 선택해주세요." << endl;
	cout << "맞출 수 있는 기회가 달라집니다." << endl;
	cout << "1. 20회(응애 난이도)\t\t2. 15회(보통 난이도)" << endl << "3. 10회(어려움 난이도)" << endl;
	cout << "선택 : ";
	cin >> diff;
	cout << "===============================================================" << endl;
	switch (diff)
	{
	case DIFF::EASY:
		gameCnt = diffArr[DIFF::EASY - 1];
		cout << "플레이어는 응애 난이도를 선택했습니다." << endl;
		break;
	case DIFF::NORMAL:
		gameCnt = diffArr[DIFF::NORMAL - 1];
		cout << "플레이어는 보통 난이도를 선택했습니다." << endl;
		break;
	case DIFF::HARD:
		gameCnt = diffArr[DIFF::HARD - 1];
		cout << "플레이어는 어려움 난이도를 선택했습니다." << endl;
		break;
	}
	cout << "플레이어에게 " << gameCnt << "번의 기회가 주어졌습니다." << endl;
	return gameCnt;
}

// 단일 문자를 아스키코드로 변환하여 아스키코드값만큼 빼서 정수로 변환
int ConvertCharInt(char chr)
{
	return (int)chr - 48;
}

 

4. 결과

어느 게임이든 개발자의 의도는 보통 난이도에 있다
??? : 니 잘못 불러준 거 아니야? 진짜 원 스트라이크 원 볼이라고?
내가 수학 좀 잘해