IT/Back-end

[C++] Bingo game 빙고게임

#바로 2022. 3. 28. 01:16

Problem#1
Bingo Game
( 빙고 게임 )

#include<iostream>
#include<cstdlib>
#include<time.h>
#include<iomanip>
using namespace std;

class Rnd
{ 
private:
	int start;
	int end;
public:
	Rnd()//생성될떄 1~25 까지 범위 초기화
	{
		start =1;end = 25;
	}
	int getRNumber()//난수생성
	{
		int rn = (int)( (rand() % (end-start+1)) + start );
		return rn;
	}
};

class Bingo 
{
private:
	int bingoNum[5][5];  // 임의로 생성된 숫자를 저장하는 배열
	int nStatus[5][5];  // bingoNum[5][5]의 숫자가 불렸는지를 체크하는 배열
	int num[25]; // 1배열에 25개 난수를 저장한다.
	int WBingoCount[5];//가로줄 빙고 확인
	int HBingoCount[5];//세로줄 빙고 확인
	int RBingoCount;//오른쪽 대각선 확인
	int LBingoCount;//왼쪽 대각선 확인

public:
	Bingo()// 이 생성자에서는 bingoNum[5][5] 및 그 이외의 변수를 초기화한다. 
	{
		Rnd R;
		int i, j;
		int x;

		for(i = 0; i <25; i++)//1차 배열에 중복없는 난수를 저장한다.
		{
			x = R.getRNumber();
			num[i] = x;
			for(j = 0; j < i; j++)
			{
				if(num[j] == x)
				{
					i--;
					break;
				}
			}
		}
		for(i = 0; i < 5; i++)//1차배열을 2차 배열로 만들기
		{
			for(j = 0; j < 5; j++)
			{
				bingoNum[i][j] = num[(i*5)+j];
				nStatus[i][j] = bingoNum[i][j];
			}
		}
		//----------------------------초기화
		for(i=0 ; i<5 ; i++)//배열 초기화
		{
			WBingoCount[i]=0;	
			HBingoCount[i]=0;
		}
		RBingoCount=0;
		LBingoCount=0;

	}

	int checkBingo()// 이함수는 nStatus[5][5]를 이용하여 빙고를 부를 수 있는지 검사한다.
	{	           // 빙고이면 return 1, 아니면 return 0
		int i,j;
		int Wcount = 0;
		int Hcount = 0;
		int RCcount = 0;
		int LCcount = 0;
		int bingo =0;

		for(i=0 ; i<5 ; i++)// 가로,세로줄 빙고 체크
		{
			Wcount = 0;
			Hcount = 0;
			for(j=0 ; j<5 ; j++)
			{
				if(nStatus[i][j] == 0)//가로체크
				{
					Wcount++;
				}
				if(nStatus[j][i] == 0)//세로체크
				{
					Hcount++;
				}
				if(Wcount==5)
				{
					bingo++;
				}
				if(Hcount==5)
				{
					bingo++;
				}
			}
		}

		for(j=4,i=0 ; j>=0,i<5 ; j--,i++)
		{
			if(nStatus[j][j]==0)//왼쪽 대각선 체크
			{
				LCcount++;
			}
			if(nStatus[i][j]==0)//오른쪽 대각선 체크
			{
				RCcount++;
			}
			if(LCcount==5)
			{
				bingo++;
			}
			if(RCcount==5)
			{
				bingo++;
			}
		}
		if(bingo>=3)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	void printBingoGrid() // 이 함수는 nStatus[5][5]를 출력한다.
	{
		for(int i = 0; i < 5; i++)
		{
			for(int j = 0; j < 5; j++)
			{
				cout << setw(3) << nStatus[i][j];
			}
			cout << "\n";
		}
		cout << "\n";
	}

	int callNumber(int number)
	{
		// 이 함수는 int number를 입력으로 받아 들인다.
		// 그리고, bingoNum[5][5]의 숫자가 호출됐는지 검사하고 nStatus[5][5]에 기록한다.
		// int number가 이상이 없으면 return 1
		// 	    이상이 있으면 return 0 
		//              (1~25 사이의 숫자가 아닌 경우, 문자가 입력된 경우)

		int i, j;

		for(i=0 ; i<5 ; i++)
		{
			for(j=0 ; j<5 ; j++)
			{
				if(nStatus[i][j] == 0 && bingoNum[i][j]==number)
				{
					cout<<"오류!! 중복됩니다"<<endl;
					return 0;
				}
				if(nStatus[i][j] == number)
				{
					nStatus[i][j]=0;
					return 1;
				}
			}
		}
		cout << "오류!! 1~25 중에 숫자를 넣으세요"<<endl;
		return 0;
	}

	int AI()//컴퓨터의 [5][5]배열을 통해서 가로, 세로 , 대각선의
	{       //0개수를 파악하여 0이 가장 많은 줄에 0을 넣게하는 알고리즘.
		int i,j;
		int zerocount[4]={0};//가장 큰 가로 세로 외쪽 오른쪽 배열에 넣기
		int Wzerocount[5]={0};//가로줄 0 개수 저장
		int Hzerocount[5]={0};//세로줄 0 개수 저장
		int Wbigline = 0; // 가장 0이 많은 줄 저장
		int Hbigline = 0; // 가장 0이 많은 줄 저장
		int temp=0;
		int bignum=-1;//가로 세로 대각선중 가장 큰 쪽을 저장
		int result;
		int Wbigcount=0;//가로줄 중 0이 가장 많은 줄의 0갯수 저장
		int Hbigcount=0;//세로줄 중 0이 가장 많은 줄의 0갯수 저장
		int Rbigcount=0;//오른쪽 0 개수 저장
		int Lbigcount=0;//왼쪽 0 개수 저장
		int centercount=0; // 중앙에 {11,12,13,21,22,23 31,32,33}배열 0 숫자 카운트

		if(nStatus[2][2]==0)
		{

			for(i=1 ; i<4 ; i++)// 중앙에 {11,12,13 21,22,23 31,32,33}배열 0 숫자 카운트
			{
				for(j=1 ; j<4 ; j++)
				{
					if(nStatus[i][j]==0)
					{
						centercount++;
					}
				}
			}
			if(centercount<2)
			{
				for(i=1 ; i<4 ; i++)//중앙 {11,12,13,21,22,23 31,32,33}배열에 숫자를 넣는다
				{
					for(j=1 ; j<4 ; j++)
					{
						if(nStatus[i][j]!=0)
						{
							result = nStatus[i][j];
							nStatus[i][j]=0;
							return result;
						}
					}
				}
			}

			for(i=0 ; i<5 ; i++)//가로 세로 0 체크
			{
				for(j=0; j<5 ; j++)
				{
					if(nStatus[i][j]==0 && WBingoCount[i]==0)
					{
						Wzerocount[i]++;
						if(Wzerocount[i]==5)
						{
							WBingoCount[i]++;// 만약 가로줄 중에 빙고가 된다면 다시 못들어오게한다.
							Wzerocount[i]=0;//그리고 가로줄 라인은 갯수를 0으로 만든다. 
						}
					}
					if(nStatus[j][i]==0 && HBingoCount[i]==0)
					{
						Hzerocount[i]++;
						if(Hzerocount[i]==5)
						{
							HBingoCount[i]++;// 만약 세로줄 줄에 빙고가 된다면 다시 못들어오게한다.
							Hzerocount[i]=0;//그리고 세로줄라인은 갯수를 0으로 만든다.
						}
					}
				}
			}

			Wbigcount=Wzerocount[0];//처음 가로줄을 가장큰수로 잡는다.
			Hbigcount=Hzerocount[0];//처음 세로줄을 가장큰수로 잡는다.

			for(i=0 ; i<5 ; i++)//가장 많이 0이된 줄을 찾는다.
			{
				if( Wbigcount<Wzerocount[i] )
				{
					Wbigcount=Wzerocount[i];
					Wbigline = i;
				}
				if( Hbigcount<Hzerocount[i] )
				{
					Hbigcount=Hzerocount[i];
					Hbigline = i;
				}
			}

			zerocount[0]=Wbigcount;// 가로 세로 대각선을 비교하기 위해서
			zerocount[1]=Hbigcount;// 가로 세로를 저장한다.
//----------------------------여기서 부터 대각선------------

			for(i=4,j=0 ; i>=0,j<5 ; i--,j++)
			{
				if(nStatus[j][j]==0 && LBingoCount == 0)//왼쪽 대각선
				{
					Lbigcount++;
					if(Lbigcount==5)
					{
						LBingoCount++;
						Lbigcount=0;
					}
				}
				if(nStatus[i][j]==0 && RBingoCount ==0)//오른쪽 대각선
				{
					Rbigcount++;
					if(Rbigcount==5)
					{
						RBingoCount++;
						Rbigcount=0;
					}
				}
			}

			zerocount[2]=Lbigcount;//왼쪽 대각선의 0개 개수를 저장한다.
			zerocount[3]=Rbigcount;//오른쪽 대각선의 0개 개수를 저장한다.

			//-------------------------------------------------------
			//zerocount[0]는 가로줄의 0개수
			//zerocount[1]는 세로줄의 0개수
			//zerocount[2]는 왼쪽 대각선 0개수
			//zerocount[3]는 오른쪽 대각선 0개수
			temp=zerocount[0];
			for(i=0 ; i<4 ; i++)
			{
				if(zerocount[i] >= temp)
				{
					temp=zerocount[i];//가로 세로 왼쪽 오른쪽 중에 가장 0이 많은곳을 찾는다.
					bignum=i;//이때 가장큰곳을 bignum에 저장한다.
				}
			}			

			if(bignum==0)//가로에 0이 없는 곳에 넣는다.
			{
				for(j=0 ; j<5 ; j++)
				{
					if(nStatus[Wbigline][j]!=0)
					{
						result = nStatus[Wbigline][j];
						nStatus[Wbigline][j]=0;
						return result;
					}
				}
			}
			else if(bignum==1)//세로에 0이 없는 곳에 넣는다.
			{
				for(i=0 ; i<5 ; i++)
				{
					if(nStatus[i][Hbigline]!=0)
					{
						result = nStatus[i][Hbigline];
						nStatus[i][Hbigline]=0;
						return result;
					}
				}
			}
			else if(bignum==2)//왼쪽대각선에 0이 없는 곳에 넣는다.
			{
				for(i=0 ; i<5 ; i++)
				{
					if(nStatus[i][i]!=0)
					{
						result=nStatus[i][i];
						nStatus[i][i]=0;
						return result;
					}
				}
			}
			else if(bignum==3)//오른쪽 개각선에 0이 없는 것에 넣는다.
			{
				for(i=0,j=4 ; i<5,j>=0 ; i++,j--)
				{
					if(nStatus[i][j]!=0)
					{
						result=nStatus[i][j];
						nStatus[i][j]=0;
						return result;
					}
				}
			}

		}
		else
		{
			result=nStatus[2][2];
			nStatus[2][2]=0; // 첫번째로 nStatus[2][2]에 0이 없으면 0을 넣는다.
			return result;
		}

	}
};



int main()
{
	srand((unsigned)time(NULL));

	Bingo user;
	Bingo com;

	int x;
	int num;
	int a;
	int y;

	while(1)
	{
		fflush(stdin); // 버퍼를 비워준다.
		user.printBingoGrid();
		com.printBingoGrid();

		cout << "숫자를 선택하세요 : ";
		x=scanf("%d",&num); // scanf는 지형 형식자 이외에 모두 입력 거부되어 0또는 1이 들어간다.

		cout << "----------------------------"<<endl;
		if(x==1)//x가 1일때에는 정수를 입력했으므로 다시 정수를 입력시킨다.
		{
			a=num;
		}
		if(x==0)//x가 문자나 0일 때는 초과된 26을 넣는다.
		{
			a=26;
		}
		if(user.callNumber(a)==0)
		{
			continue;
		}

		//----------------------------컴퓨터---------------------------
		com.callNumber(a);
		if(user.checkBingo()==1)// 사용자가 먼저 빙고를 부르면 승리!!를 체크
		{
			cout << "----------------------------"<<endl;
			cout << "빙고!! 승리하셨습니다!"<<endl;
			user.printBingoGrid();
			cout << "----------------------------"<<endl;
			break;
		}
		if(com.checkBingo()==1)
		{
			cout << "----------------------------"<<endl;
			cout << "빙고!! 패배하셨습니다!"<<endl;
			com.printBingoGrid();
			cout << "----------------------------"<<endl;
			break;
		}

		y=com.AI();//컴퓨터 실행
		cout << "컴퓨터가 부른 수는? : " << y<<endl;
		user.callNumber(y);

		if(com.checkBingo()==1)//컴퓨터가 먼저 빙고를 부르면 승리!!를 체크
		{
			cout << "----------------------------"<<endl;
			cout << "빙고!! 패배하셨습니다!"<<endl;
			com.printBingoGrid();
			cout << "----------------------------"<<endl;
			break;
		}
		if(user.checkBingo()==1)
		{
			cout << "----------------------------"<<endl;
			cout << "빙고!! 승리하셨습니다!"<<endl;
			user.printBingoGrid();
			cout << "----------------------------"<<endl;
			break;
		}

	}
}

■ 빙고 게임 프로그램 개요
1. 컴퓨터와 사용자가 경기를 진행한다.
2. 한 명씩 돌아가면서 숫자를 부르는 방식으로 진행한다.
3. 컴퓨터와 사용자의 빙고판의 크기는  5x5이며, 1~25의 숫자가 임의로 배열된다.
4. 숫자를 부르면 0이 표기 된다.
5. 대각선, 가로, 세로 중 3개 줄 이상의 숫자들이 모두 불렀을 때 빙고를 외치면 이긴다.
6. 빙고는 숫자를 부른 쪽이 먼저 외칠 수 있는 우선순위가 있다.
7. 컴퓨터는 똑같은 숫자를 부르지 않는다.
8. 숫자를 부르는 방식은 컴퓨터의 경우에 가로, 세로, 대각선의 0개 개수를 파악하여 0이 가장 많은 줄에 0을 넣게 하는 알고리즘으로 이루어진다.
9. 사용자는 키보드를 통해 숫자를 입력한다.

■ 구현한 클레스 설명
 ● Bingo 클래스는 빙고의 구현, 승패, 컴퓨터의 인공지능을 하는 역활을
    포함하고 있다. 기본생성자, checkBingo, printBingoGrid, callNumber,
    AI로 구성되어 있다.
1. 기본생성자
  -기본 생성자에는  bingoNum[5][5]에 중복 없는 난수를 넣고 그 외의
   변수를 초기화 시킨다. 그리고 bingoNum[5][5]를 nStatus[5][5]에 복사
   시킨다.

2. int checkBingo()
  
  -  nStatus[5][5]를 이용하여 빙고를 부를 수 있는지 검사한다.
     만약, 빙고면 return 1을 하고, 아니면 return 0을 한다.
3. void printBingoGrid()
 
 - nStatus[5][5]를 출력한다.
 
4. int callNumber(int number)
  
  
  - main에서 number를 받아들인다.
    그리고 bingoNum[5][5]의 숫자가 호출되었는지 검사하고 기록한다.
    number에 이상이 있으면 return 0, 없으면 return 1을 한다.
    (중복, 1~25 숫자가 아닌 경우, 문자가 입력된 경우)
5. int AI()
  - 컴퓨터의 [5][5]배열을 통해서 가로, 세로 대각선의 0개수를 파악하여
     0이 가장 많은 줄에 넣게 한다.
 ● Rnd 클래스는 1~25 숫자를 난수 생성하는 기능을 가진 클래스이다.
1. 기본 생성자
  - 1~25 까지 범위 초기화
2. get RNumber()
  - rand함수로 난수를 생성한다.
■ 메인함수
  -사용자가 숫자를 입력받고 숫자이면 입력을 하고,
   0또는 문자이면 26을 넣어 준다.
  - callNumber가 0이면 다시 반복한다. 1이면 다음으로 넘어간다.
  - 사용자의 숫자를 컴퓨터에 넣어준다.
  - 사용자 또는 컴퓨터가 checkBingo가 1이면 빙고를 외친다.
  - 컴퓨터가 실행하고 나온 숫자를 사용자에 넣어준다.
  - 컴퓨터 또는 사용자가 checkBingo가 1이면 빙고를 외친다.
  - 승패가 날 때까지 무한반복 한다.