20/02/11 C++공부
강좌를 통해 빙고 게임(with AI)를 만들어 봤다.
강좌를 보며 따라하다가 몇몇 부분은 나혼자서 해봤다.
간단한 게임조차도 이렇게나 버겁다.
특히 AI적을 만드는 것도 여간 골치아픈 일이다.
기본적으로 코드 줄이 길다. 이후 최적화나 함수, 표현 방법으로 깔끔하게 만들것 같다.
#include <iostream>
#include <time.h>
enum AM
{
AM_EASY = 1,
AM_HARD
};
enum LINE_NUMBER
{
LN_H1,
LN_H2,
LN_H3,
LN_H4,
LN_H5,
LN_V1,
LN_V2,
LN_V3,
LN_V4,
LN_V5,
LN_LT,
LN_RT
};
using namespace std;
int main()
{
srand((unsigned int)time(NULL));
int iNumber[25] = {};
int iAINumber[25] = {};
int iAIMode;
for (int i = 0; i < 25; i++)
{
iNumber[i] = i + 1;
iAINumber[i] = i + 1;
}
int iTemp, idx1, idx2;
for (int i = 0; i < 100; i++)
{
idx1 = rand() % 25;
idx2 = rand() % 25;
iTemp = iNumber[idx1];
iNumber[idx1] = iNumber[idx2];
iNumber[idx2] = iTemp;
idx1 = rand() % 25;
idx2 = rand() % 25;
iTemp = iAINumber[idx1];
iAINumber[idx1] = iAINumber[idx2];
iAINumber[idx2] = iTemp;
}
int iBingo = 0, iAIBingo = 0;
//AI 난이도를 선택한다.
while (true)
{
cout << "1.Easy" << endl;
cout << "2.Hard" << endl;
cout << "AI 모드를 선택하세요 : ";
cin >> iAIMode;
if (iAIMode >= AM_EASY && iAIMode <= AM_HARD)
break;
}
/*
AI Easy 모드는 현재 AI의 숫자목록중 *로 바뀌지 않은 숫자 목록을 만들어서
그 목록중 하나를 선택하게 한다.(랜덤하게)
*/
// 선택안된 목록 배열을 만들어준다.
int iNoneSelect[25] = {};
// 선택되지 않는 목록 개수
int iNoneSelectCount = 0;
while (true)
{
system("cls");
cout << "================ player ===================" << endl;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (iNumber[i * 5 + j] == INT_MAX)
cout << "*\t";
else
cout << iNumber[i * 5 + j] << '\t';
}
cout << endl;
}
cout << "Bingo LIne : " << iBingo << endl;
//AI 빙고 판을 출력
cout << "================ AI ===================" << endl;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (iAINumber[i * 5 + j] == INT_MAX)
cout << "*\t";
else
cout << iAINumber[i * 5 + j] << '\t';
}
cout << endl;
}
//AI빙고 줄 수를 출력한다.
cout << "AIBingo LIne " << iAIBingo << endl;
if (iBingo >= 5)
{
cout << "Player 승리" << endl;
break;
}
else if (iAIBingo >= 5)
{
cout << "AI 승리" << endl;
break;
}
cout << "숫자를 입력하세요(0 : 종료) : ";
int iInput;
cin >> iInput;
if (iInput == 0)
break;
//예외처리
else if (iInput < 1 || iInput > 25)
continue;
//중복입력을 체크하기 위한 변수.
//기본적으로 중복되었다라고 하기위해
//true로 지정
bool bAcc = true;
//모든 숫자를 차례대로 검사해서
//입력한 숫자와 같은 숫자가 있는지를 찾아낸다.
for (int i = 0; i < 25; ++i)
{
if (iInput == iNumber[i])
{
bAcc = false;
iNumber[i] = INT_MAX;
break;
}
}
if (bAcc)
continue;
//중복이 아니라면 AI의 숫자도 찾아서 *로 바꿔준다.
for (int i = 0; i < 25; ++i)
{
if (iAINumber[i] == iInput)
{
iAINumber[i] = INT_MAX;
break;
}
}
//AI가 선택을 한다. AI가 선택하는 것은 AI모드에 따라서 달라진다.
switch (iAIMode)
{
case AM_EASY :
iNoneSelectCount = 0;
for (int i = 0; i < 25; ++i)
{
//현재 숫자가 *이 아닐 경우
if (iAINumber[i] != INT_MAX)
{
// *이 아닌 숫자일 경우 iNoneSelectCount를 인덱스로 활용
//한다. 선택 안된 목록에 추가할 때마다 개수를 1씩 증가
//시켜서 총 선택안된 개수를 구해준다.
//그런데 0부터 카운트가 시작이므로 0번에 넣고 ++해서 1개
//추가되었다라고 해준다.
iNoneSelect[iNoneSelectCount++] = iAINumber[i];
}
}
iInput = iNoneSelect[rand() % iNoneSelectCount];
break;
case AM_HARD:
// 하드모드는 현재 숫자 중 빙고줄 완성 가능성이 가장 높은 줄을
// 찾아서 그 줄에 있는 숫자중 하나를 *로 만들어준다.
int iLine = 0;
int iStarCount = 0;
int iSaveCount = 0;
//가로 라인
for (int i = 0; i < 5; ++i)
{
iStarCount = 0;
for (int j = 0; j < 5; ++j)
{
if (iAINumber[i * 5 + j] == INT_MAX)
++iStarCount;
}
if (iStarCount < 5 && iSaveCount < iStarCount)
{
iLine = i;
iSaveCount = iStarCount;
}
}
//가로 라인중 가장 별이 많은 라인은 이미 구했다.
//이제 세로 라인중 가장 별이 많은 라인과 비교한다.
for (int i = 0; i < 5; ++i)
{
iStarCount = 0;
for (int j = 0; j < 5; ++j)
{
if (iAINumber[j * 5 + i] == INT_MAX)
++iStarCount;
}
if (iStarCount < 5 && iSaveCount < iStarCount)
{
iLine = i + 5;
iSaveCount = iStarCount;
}
}
//왼쪽 -> 오른쪽 대각선 체크
iStarCount = 0;
for (int i = 0; i < 25; i += 6)
{
if (iAINumber[i] == INT_MAX)
++iStarCount;
}
if (iStarCount < 5 && iSaveCount < iStarCount)
{
iLine = LN_LT;
iSaveCount = iStarCount;
}
//오른쪽 -> 왼쪽 대각선
iStarCount = 0;
for (int i = 4; i <= 20; i += 4)
{
if (iAINumber[i] == INT_MAX)
++iStarCount;
}
if (iStarCount < 5 && iSaveCount < iStarCount)
{
iLine = LN_RT;
iSaveCount = iStarCount;
}
if (iLine <= LN_H5)
{
for (int i = 0; i < 5; ++i)
{
if (iAINumber[iLine * 5 + i] != INT_MAX)
{
iInput = iAINumber[iLine * 5 + i];
break;
}
}
}
else if (iLine <= LN_V5)
{
for (int i = 0; i < 5; ++i)
{
if (iAINumber[i * 5 + (iLine - 5)] != INT_MAX)
{
iInput = iAINumber[i * 5 + (iLine - 5)];
break;
}
}
}
else if (iLine == LN_LT)
{
for (int i = 0; i < 25; i += 6)
{
if (iAINumber[i] != INT_MAX)
{
iInput = iAINumber[i];
break;
}
}
}
else if (iLine == LN_RT)
{
for (int i = 4; i <= 20; i += 4)
{
if (iAINumber[i] != INT_MAX)
{
iInput = iAINumber[i];
break;
}
}
}
}
for (int i = 0; i < 25; ++i)
{
if (iNumber[i] == iInput)
{
iNumber[i] = INT_MAX;
break;
}
}
for (int i = 0; i < 25; ++i)
{
if (iAINumber[i] == iInput)
{
iAINumber[i] = INT_MAX;
break;
}
}
//빙고 줄 수를 체크하는 것은 매번 숫자를 입력할 떄 마다 처음부터
//새로 카운트를 할 것이다. 그러므로 iBingo 변수를 0으로 매번
//초기화하고 새롭게 줄 수를 구해주도록 한다.
iBingo = 0;
iAIBingo = 0;
//가로, 세로 줄 수를 구해준다.
int iCheck1 = 0, iCheck2 = 0,
iCheck3 = 0, iCheck4 = 0;
for (int i = 0; i < 5; ++i)
{
iCheck1 = iCheck2 = 0;
for (int j = 0; j < 5; ++j)
{
//가로 별 개수를 구해준다.
if (iNumber[i * 5 + j] == INT_MAX)
++iCheck1;
//세로 별 개수를 구해준다.
if (iNumber[j * 5 + i] == INT_MAX)
++iCheck2;
}
if (iNumber[i * 5 + i] == INT_MAX)
++iCheck3;
if (iNumber[i * 5 + (4 - i)] == INT_MAX)
++iCheck4;
if (iCheck1 == 5)
++iBingo;
if (iCheck2 == 5)
++iBingo;
if (iCheck3 == 5)
++iBingo;
if (iCheck4 == 5)
++iBingo;
}
//가로, 세로 줄 수를 구해준다.
int iAICheck1 = 0, iAICheck2 = 0,
iAICheck3 = 0, iAICheck4 = 0;
for (int i = 0; i < 5; ++i)
{
iAICheck1 = iAICheck2 = 0;
for (int j = 0; j < 5; ++j)
{
//가로 별 개수를 구해준다.
if (iAINumber[i * 5 + j] == INT_MAX)
++iAICheck1;
//세로 별 개수를 구해준다.
if (iAINumber[j * 5 + i] == INT_MAX)
++iAICheck2;
}
if (iAINumber[i * 5 + i] == INT_MAX)
++iAICheck3;
if (iAINumber[i * 5 + (4 - i)] == INT_MAX)
++iAICheck4;
if (iAICheck1 == 5)
++iAIBingo;
if (iAICheck2 == 5)
++iAIBingo;
if (iAICheck3 == 5)
++iAIBingo;
if (iAICheck4 == 5)
++iAIBingo;
}
}
return 0;
}