C++기초

220318 - 빙고 게임 만들기

hyrule 2022. 3. 25. 16:28
////저번에 만든 빙고 게임에 AI를 추가해서 1:1 대전을 만들기. 이번에 추가한 주석은 // 두개를 추가하였음.

#include <iostream>
#include <time.h>

////AI의 난이도 설정
enum class AIType
{
	None,
	Easy,
	Hard,
	Max
	//Easy와 Hard는 정상적인 값, None와 Max는 잘못 입력되었을때의 값.
};


int main()
{
	srand((unsigned int)time(0));
	int Random = rand();


	////난이도 입력 받기
	int AIState = 0;
	while (true) ////잘못 선택되었을 떄를 대비한 while문
	{
		system("cls");
		std::cout << "1. 쉬움" << std::endl;
		std::cout << "2. 어려움" << std::endl;
		std::cout << "난이도를 선택하세요 : ";
		std::cin >> AIState;

		if (AIState > (int)AIType::None &&
			AIState < (int)AIType::Max)
			break;

	}
	/*
	* AI의 Easy와 Hard 모드
	Easy 모드는 그냥 남아있는 숫자 중에 랜덤하게 고름
	Hard 모드는 남아있는 숫자 중 빙고를 더 맞출 확률이 높은 숫자를 고름.
	*/


	// 1 ~ 25 까지의 숫자를 저장하기 위한 공간을 준비한다.
	////여기에 추가적으로 인공지능용 숫자판을 추가.
	int	Number[25] = {};
	int AINumber[25] = {};
	 
	// 1 ~ 25까지의 숫자를 배열에 넣어준다.
	for (int i = 0; i < 25; ++i)
	{
		Number[i] = i + 1;
		AINumber[i] = i + 1;
	}

	// 숫자를 랜덤하게 배치해준다.
	////AI의 빙고판도 섞어준다. 물론 플레이어의 빙고판을 섞은 뒤
	////난수를 새로 만들어야 개별적으로 셔플이 가능하다.
	for (int i = 0; i < 100; ++i)
	{
		int	Idx1 = rand() % 25;
		int	Idx2 = rand() % 25;

		int	Temp = Number[Idx1];
		Number[Idx1] = Number[Idx2];
		Number[Idx2] = Temp;

		Idx1 = rand() % 25;
		Idx2 = rand() % 25;

		Temp = AINumber[Idx1];
		AINumber[Idx1] = AINumber[Idx2];
		AINumber[Idx2] = Temp;
	}

	////AI 빙고라인 저장용 AI 추가.
	int	Line = 0, AILine = 0;


	////인공지능이 선택을 하기 위해서 현재 안바뀐 값을 저장할 배열을 만들어 준다.
	int SelectArray[25] = {};

	////AI가 현재 선택할 수 있는 숫자가 몇개인지를 저장할 변수를 만들어 준다.
	int SelectCount = 0;

	while (true)
	{
		system("cls");

		std::cout << "============= Player ================================================== AI =================" << std::endl;
		for (int i = 0; i < 5; ++i)
		{

			////플레이어 빙고판  한 줄 작성하고
			for (int j = 0; j < 5; ++j)
			{
				if (Number[i * 5 + j] == INT_MAX) //지운 숫자를 INT_MAX값으로 저장해놓고, 이후 *로 표시
					std::cout << "*\t";

				else
					std::cout << Number[i * 5 + j] << "\t";
			}

			////이후 AI의 빙고판을 한 줄 작성한다.
			std::cout << "\t\t";
			for (int j = 0; j < 5; ++j)
			{
				if (AINumber[i * 5 + j] == INT_MAX) //지운 숫자를 INT_MAX값으로 저장해놓고, 이후 *로 표시
					std::cout << "*\t";

				else
					//std::cout << AINumber[i * 5 + j] << "\t"; //디버그용. 컴퓨터 숫자 다 보임.
					std::cout << "*" << "\t";
			}

			std::cout << std::endl;
		}

		std::cout << "Bingo Line : " << Line << "\t\t\t\t\t\t";
		std::cout << "AI Bingo Line : " << Line << std::endl;

		////플레이어 체크 이후
		if (Line >= 5)
		{
			std::cout << "Player Win" << std::endl;
			break;
		}
		////AI 승리 체크 확인
		else if (AILine >= 5)
		{
			std::cout << "AI Win" << std::endl;
			break;
		}

		std::cout << "Input Number(0 : Exit) : ";
		int	CheckNumber = 0;
		std::cin >> CheckNumber;

		if (CheckNumber == 0)
			break;

		else if (CheckNumber < 0 || CheckNumber > 25)
			continue;

		// 배열의 각 요소와 체크값을 비교하여 해당 값이 있는지
		// 판단한다.
		bool	Check = false;
		for (int i = 0; i < 25; ++i)
		{
			if (Number[i] == CheckNumber)
			{
				// 숫자가 있다면 해당 숫자를 *로 바꿔준다.
				Number[i] = INT_MAX;
				Check = true;
				break;
			}
		}

		// 이미 입력한 숫자를 다시 입력했다면 Check 변수는
		// false를 유지하고 있을 것이다.
		// 즉 Check변수가 false일 경우 다시 입력받게 한다.
		//if (Check == false)
		//if (false == Check)
		if (!Check)
			continue;

		////정상적인 숫자를 입력하여 플레이어의 숫자가 *로 바뀌었을 경우 AI의 숫자에서도 해당 값을 찾아서 *로 바꾸어 준다.
		////-->> 두 빙고판을 모두 바꿔주어야 함.
		for (int i = 0; i < 25; ++i)
		{
			if (AINumber[i] == CheckNumber)
			{
				AINumber[i] = INT_MAX;
				break;
			}
		}

		////플레이어가 선택한 숫자를 모두 바꾸었다면 인공지능이 선택할 수 있도록 한다.
		switch ((AIType)AIState)
		{
		case AIType::Easy:
			////*우리가 해야할 것
			// 1.현재 입력 안된 값들을 찾아서 배열에 넣어준다
			// 
			// */
			SelectCount = 0;

			for (int i = 0; i < 25; ++i)
			{

				//// '*'이 아닌 일반 값일 경우 --> 아직 입력받은 값이 아닌 경우 AI가 고를 수 있는 배열에 해당 값을 넣어준다
				/*알고리즘 예시
				1. 3 * 7 8 * * 2
				2. 3 7 8 2 , SelectCount = 4
				3-1. easy 모드일 경우
					이후 random % SelectCount를 해서 랜덤한 값을 추출
				*/
				if (AINumber[i] != INT_MAX)
				{
					SelectArray[SelectCount] = AINumber[i];
					++SelectCount;
				}

			}
			CheckNumber = SelectArray[rand() % SelectCount];

			break;
		case AIType::Hard:
			////Hard 모드의 경우 숫자가 제일 적게 남은 줄을 체크해서 해당 줄의 넘버만 뽑음

			break;
		} ////AI의 숫자 선택 완료.
		
		//디버그
		/*std::cout << "AI Select : " << CheckNumber << std::endl;
		system("pause");*/

		
		////AI가 선택한 숫자를 *로 바꿔주도록 한다.(플레이어와 AI의 빙고판 모두)
		for (int i = 0; i < 25; ++i)
		{
			if (Number[i] == CheckNumber)
			{
				Number[i] = INT_MAX;
				break;
			}
		}
		for (int i = 0; i < 25; ++i)
		{
			if (AINumber[i] == CheckNumber)
			{
				AINumber[i] = INT_MAX;
				break;
			}
		}




		// 빙고 줄을 체크한다.
		// 빙고 줄을 0으로 초기화를 하고 모든 줄을 체크해본다.
		Line = 0;
		AILine = 0;

		for (int i = 0; i < 5; ++i)
		{ 
			int	CheckCount = 0, CheckCount1 = 0;
			int	AICheckCount = 0, AICheckCount1 = 0;

			for (int j = 0; j < 5; ++j)
			{
				////유저
				// 가로 줄 체크
				if (Number[i * 5 + j] == INT_MAX)
					++CheckCount;
				// 세로 줄 체크
				if (Number[j * 5 + i] == INT_MAX)
					++CheckCount1;

				////AI
				// 가로 줄 체크
				if (AINumber[i * 5 + j] == INT_MAX)
					++AICheckCount;
				// 세로 줄 체크
				if (AINumber[j * 5 + i] == INT_MAX)
					++AICheckCount1;
			}

			// 가로 한 줄을 체크했는데 CheckCount 변수가
			// 5라면 해당 줄은 모두 *로 변경되어 있다는 것이다.
			////유저
			if (CheckCount == 5)
				++Line;
			if (CheckCount1 == 5)
				++Line;

			////AI
			if (AICheckCount == 5)
				++AILine;
			if (AICheckCount1 == 5)
				++AILine;
		}

		////대각선 빙고체크. 마찬가지로 AI 체크카운트 추가
		// 왼쪽 상단 -> 오른쪽 하단
		int	CheckCount2 = 0, AICheckCount2 = 0;
		for (int i = 0; i < 25; i += 6)
		{
			if (Number[i] == INT_MAX)
				++CheckCount2;
			if (AINumber[i] == INT_MAX)
				++AICheckCount2;
		}

		if (CheckCount2 == 5)
			++Line;
		if (AICheckCount2 == 5)
			++AILine;

		CheckCount2 = 0, AICheckCount2 = 0;

		// 오른쪽 상단 -> 왼쪽 하단
		for (int i = 4; i <= 20; i += 4)
		{
			if (Number[i] == INT_MAX)
				++CheckCount2;
			if (AINumber[i] == INT_MAX)
				++AICheckCount2;
		}

		if (CheckCount2 == 5)
			++Line;
		if (AICheckCount2 == 5)
			++AILine;
	}

	return 0;
}