본문 바로가기
공부

C++ 블랙잭 게임 복습

by MY블로그 2022. 11. 16.

지난번 과제인 블랙잭 게임의 풀이 및 복습.

기존 과제에서는 플레이어1,딜러1 의 1:1 구조였다면

이번 과제는 플레이어의 수를 입력받고 플레이어의 이름을 입력받아

딜러1 vs 플레이어 다수 의 구조가 되는 방식이다.

오늘 과제 풀이에 딜러와 플레이어가 동점일경우에도 플레이어의 패배 및 딜러의 승리로 추가 수정하였다.

오랜만에 동적할당 해제를 끝부분에 섰는데 사용법이 맞는지는 모르겠다..

어려웠고 몇번 복습해서 OOP 구조에대해 좀더 익숙해 져야 할것같다.

 

#include <iostream>
#include <time.h>
#include <string>
#include <vector>
#define SET 3

using namespace std;

enum Shape //모양 함수 4종류
{
	DIA,
	CLOVER,
	HEART,
	SPADE
};

enum Num //특정숫자는 문자출력을 위한 함수
{
	A = 1,
	J = 11,
	Q,
	K
};

struct Card // 구조체 카드 (숫자, 모양, 특정숫자는 문자변환출력)
{
	int num;
	string shape;

	void Print()
	{
		switch (num)
		{
		case A:
			cout << shape << "A" << "\t";
			break;
		case J:
			cout << shape << "J" << "\t";
			break;
		case Q:
			cout << shape << "Q" << "\t";
			break;
		case K:
			cout << shape << "K" << "\t";
			break;
		default:
			cout << shape << num << "\t";
			break;
		}
	}
};

struct Player //플레이어 구조체 (이름 돈 카드 점수 승패 카드패출력)
{
	string name;
	int money;
	vector<Card>card;
	int jokbo = 0; //카드의합 초기화 = 처음상태는 0
	bool win; //승패용 변수

	void MakeJokbo(bool dealer = 0)
	{
		jokbo = 0;
		int select; //플레이어의 패중에 A의카드가있을시 1또는 11점수 선택용 변수
		for (int i = 0; i < card.size(); i++) //카드갯수만큼 반복시킬 반복문
		{
			if (card[i].num > 10) //카드의 넘버가 10이상인경우는 무조건 10만 더해지도록설정
			{
				jokbo += 10;
			}
			else if (card[i].num == 1) //카드가 1 즉,카드가 A패일 경우
			{
				if (!dealer) // 딜러가아닐때 = 즉, 플레이어일때
				{
					for (int i = 0; i < card.size(); i++) // 카드패만큼반복하면서 출력시킴
					{
						card[i].Print();
					}
					
					cout << "A카드획득. 카드점수선택. [1]=1점 / [2]=11점 "; //점수선택 출력
					cin >> select; //선택 입력

					if (select == 1) //1번선택
					{
						jokbo += 1; // 1번카드는 점수1점
					}
					else if (select == 2) // 2번선택
					{
						jokbo += 11; // 1번카드는 점수11점
					}
				}
				else
				{
					jokbo += 1; //그외에는 그냥 A카드 1점더하기
				}
			}
			else // a카드가 아니고 다른카드일경우 그냥 일반점수더하기
			{
				jokbo += card[i].num;
			}

		}
	}

	void Print(bool dealer = 0)
	{
		cout <<"["<< name<<"]" << "\t";
		for (int i = 0; i < card.size(); i++) // 딜러도 플레이어도 점수는 출력된다
		{
			card[i].Print();
		}
		cout << "점수 : " << jokbo << "\t";
		if (!dealer) //딜러가아니라면 = 플레이어라면 소지금 출력
		{
			cout << "소지금 : " << money << "\t";
		}
		cout << endl;
	}
};

Card MakeDeck(int num, string shape) //카드덱 만들기, 구조체안에있는 숫자 모양 불러오기
{
	Card temp; // 카드덱의 빈 인덱스 생성
	temp.num = num; // 빈인덱스의 숫자생성
	temp.shape = shape; // 빈인덱스의 모양생성
	return temp; // 빈인덱스 반환
}

void Shuffle(vector<Card>& deck) //카드섞기(셔플) 벡터로할당된 카드구조체의 덱주소
{
	for (int i = 0; i < 1000; i++) //대충 천번정도 셔플해주기
	{
		int sour = rand() % (52 * SET); //52개의 카드 총 SET갯수 전부 셔플해준다.
		int dest = rand() % (52 * SET);

		Card temp = deck[sour]; //기본셔플형식. 단 카드구조체셔플.
		deck[sour] = deck[dest];
		deck[dest] = temp; 
	}
}

//===============================================================================함수구성끝

int main() // 메인 시작
{
	srand(time(NULL));
	vector<Card>deck;
	Player* player;
	Player dealer;

	int cardCount = 0; //카드의 합계산을위한 초기화
	int input;
	int size;

	//덱만들기
	for (int i = 0; i < 52*SET; i++) // 총카드는 52개가한묶음으로 define SET 갯수만큼 곱해진다.
	{ // 52개의 묶음 *3 이기때문에 같은카드는 3개까지 나오게된다.
		string temp;
		int cut = i % 52; // 0~51까지 카운트후 다시 0부터 51까지만 카운트되도록

		switch (cut / 13) // 52개의 카드는 4종류의 모양으로 나뉜다. 초 4종 각각 13장씩 52장
		{
		case DIA:
			temp = "◆";
				break;
		case CLOVER:
			temp = "♣";
				break;
		case HEART:
			temp = "♥";
				break;
		case SPADE:
			temp = "♠";
				break;
		}
		deck.push_back(MakeDeck(i % 13 + 1, temp));
	}

	//========================================== 게임 시작전 기본세팅
	cout <<"\t" << "[블랙잭 게임]"<<"\t" << endl;
	cout << endl;
	cout << "게임에 참가할 인원수를 입력해 주세요 : " << "\t"; cin >> size;
	cout << endl;
	
	player = new Player[size]; //플레이어의 인원수입력에따른 동적할당.

	for (int i = 0; i < size; i++) //참가인원수 만큼 반복하며 이름 입력받기
	{
		cout << i + 1 << "번 참가자의 이름을 입력 : " << "\t";
		cin >> player[i].name; // 구조체에 동적할당 받았기때문에 직접 palyer[i].name 에 접촉이 가능
		player[i].money = 100000; // 플레이어들의 소지금은 단순히 통일시킴.
	}

	dealer.name = "딜러"; // 딜러의 이름은 단순히 딜러
	dealer.money = 100000; // 딜러도 일단 금액설정해둔다.

	//=========================================== 본게임 시작

	while (true) //게임은 게속 반복 되도록 설정
	{
		cardCount = 0; //카드나누어줄때 같은 배열에있는 카드가 겹치지않도록 카운트초기화
		//0번배열에있는 카드부터 순차대로 나눠준다.
		//카드는 나눠줄때마다 배열순서대로 나눠준다 (배열은 셔플되어있음)
		Shuffle(deck); // 게임시작전 덱셔플해주기

		for (int i = 0; i < size; i++) //게임 인원수 설정만큼 반복
		{
			cout << endl;
			cout << "=======================================" << player[i].name << "의 차례입니다." << endl;
			cout << endl;
			player[i].win = true; //게임플레이어의 상태는 기본 참값으로 하여 진행되도록한다.
			player[i].money -= 1000; // 새게임마다 1천원씩 소지금 감소
			
			player[i].card.clear(); //새게임시작시 기존의 카드패는 초기화
			player[i].card.push_back(deck[cardCount]); //새로운 카드 벡터로 1장받는다
			cardCount++; //카드배열 하나증가. (0번카드 받고 다음받을수있는카드는 1번카드)
			player[i].card.push_back(deck[cardCount]); //새로운 카드 벡터로 1장받는다
			cardCount++; //카드배열 하나증가. (1번카드 받고 다음받을수있는카드는 2번카드)

			//카드를 받았다면 현재 카드의 합을 보여준뒤 더받을지 멈출지 확인하기
			player[i].MakeJokbo(); //플레이어의 족보(카드의합)을 계산시킨다.
			
			//그리고나서 출력
			player[i].Print(); // 프린트 함수를 이용하여 소지카드 점수 소지금 등을 보이게한다.

			//출력이되어 확인하였으면 추가카드 받기여부
			while (true) //버스트하거나 그만받을때까지 반복한다
			{
				cout << "추가카드를 받으시겠습니까? [1]YES / [2]NO" << "\t"; cin >> input;
				if (input == 2)
				{
					break; // 카드를 더안받는선택시 반복문 탈출한다.
				}
				// 2번선택이 아니라면 아래 코드진행
				player[i].card.push_back(deck[cardCount]); //카드한장만 더받고
				cardCount++; // 이후 제공될 카드 인덱스카운트 증가
				player[i].MakeJokbo(); // 카드를 받아서 다시 점수 계산을하고
				player[i].Print(); // 출력시킨다 

				if (player[i].jokbo > 21) //플레이어의 카드점수가 21점이 넘어갔을경우
				{
					cout << player[i].name << "참가자가 버스트 ! " << endl;
					player[i].win = false; // 플레이어의 승리가 거짓값으로 바뀐다 = 패배.
					break; //진행중인 반복문을 탈출한다.
				}
				else
				{
					player[i].win = true; // 카드합계가 21이넘지않는한 플레이어의 상태는 그대로이다.
				}
			}
		}
		//플레이어 설정후 딜러 설정
		dealer.win = true; //딜러의 상태도 기본 참값이다.
		dealer.card.clear(); //딜러도 마찬가지로 게임시작때마다 패는 초기화 시킨다.
		dealer.card.push_back(deck[cardCount]);
		cardCount++;
		dealer.card.push_back(deck[cardCount]);
		cardCount++; // 딜러도 매판마다 카드 2장을 받으며 분배할카드 인덱스는 줄때마다증가.
		cout << endl;
		cout << "=======================================" << dealer.name << "의 차례입니다." << endl;
		cout << endl;
		//딜러패의 합계산
		dealer.MakeJokbo(true); // 함수의 매개변수가 참인이유는 함수는 플레이어 함수에 들어가지않게하기위해서

		//딜러패의 출력
		dealer.Print(true); // 매개변수가 참인이유는 위와 동일.

		//딜러는 카드의합이 17미만인경우 자동적으로 카드를 더 받도록 설정한다.
		while (dealer.jokbo < 17)
		{
			dealer.card.push_back(deck[cardCount]); //조건에일치하면 카드한장을 더받고
			cardCount++; //카드인덱스 1증가
			dealer.MakeJokbo(true); //딜러패의 점수 계산
			dealer.Print(true); //게산후 출력

			if (dealer.jokbo > 21) //딜러의 점수가 21점이 넘으면 버스트시키고
			{
				cout << dealer.name << " 의 버스트 ! " << endl;
				dealer.win = false; //딜러의 상태는 패배가된다.

				for (int i = 0; i < size; i++) //플레이어 인원수만큼 반복문을 돌려서
				{
					if (player[i].win) //만약 플레이어의 상태가 win 이라면
					{
						cout << player[i].name << " 승리 ! " << endl; //해당플레이어의 승리
						player[i].money += 3000; //승리한 플레이어는 소지금이 3천원 추가된다.
					}
				}
				break; // 반복문을 탈출한다.
			}
			else
			{
				dealer.win = true; // 점수의 합이 21만넘지않는다면 패배상태가 아니다.
			}
		}
		//반복문 내에서 승패 비교까지 이루어져야한다.
		for (int i = 0; i < size; i++) // 승패조건의검사는 플레이어의 수만큼 반복되서 검사된다
		{
			if (dealer.win && player[i].win) // 딜러와 플레이어 양쪽이 버스트가 아닌상태에서
			{
				if (dealer.jokbo < player[i].jokbo) // 딜러의 점수보다 플레이어의 점수가 높다면
				{
					cout << player[i].name << " 승리 ! " << endl; //해당플레이어의 승리
					player[i].money += 3000; //승리한 플레이어는 소지금이 3천원 추가된다.
				}
				else if (dealer.jokbo = player[i].jokbo)
				{
					cout << player[i].name << " 패배 ! " << "\t"; 
					cout << "동점은 딜러 승리 입니다. " << endl;
					player[i].win = false;
				}
				else
				{
					cout << "딜러 승리 입니다 ! " << endl;
					cout << player[i].name << " 패배 ! " << endl;
					player[i].win = false;
				}
			}

		}

	}
	for (int i = 0; i < size; i++)
	{
		delete[i] player;
	}
	delete[] player;

	return 0;
}

'공부' 카테고리의 다른 글

C++ 다수 컴퓨터 가위 바위 보 게임  (0) 2022.11.17
C++ 이전과제 클래스구조로 변환시키기  (0) 2022.11.16
C++ OOP 조사  (0) 2022.11.15
블랙잭 과제 (진행중)  (0) 2022.11.15
C++ 블랙잭(1:1) 코드 복습  (0) 2022.11.14

댓글