C++ 배열에서 카드 덱 표현
이 자습서에서는 C++ 배열을 통해 카드 데크를 표현합니다.
먼저 카드 데크 예선에 대해 논의한 다음 C++에서 가능한 카드 표현 방법에 대해 설명합니다. 마지막으로, 우리는 카드 덱을 표현하기 위한 실용적인 예를 제시할 것입니다.
카드 덱
표준 카드 덱에는 하트, 클럽, 스페이드 및 다이아몬드의 네 가지 모음 또는 유형이 있습니다. 각 세트에는 에이스, 2, 3, 4, 5, 6, 7, 8, 9, 10, 잭, 퀸, 킹 등 13개의 카드가 있습니다. 따라서 전체 데크에는 총 52장의 카드가 있습니다.
카드에는 많은 게임이 있습니다. 그러나 이에 대해 자세히 설명하지는 않습니다. 다른 게임에서 잭, 퀸, 킹은 각각 10, 11, 12의 값을 가집니다.
일부 게임에서 에이스는 13 또는 1 또는 둘 다로 간주될 수 있습니다.
C++의 개별 카드 표현
카드 유형/스위트 정보와 각 카드의 카드 번호/값을 저장해야 합니다. 카드 값은 단순히 정수형 변수에 담을 수 있는 숫자인 반면, 카드 종류는 0형, 1형, 2형, 3형으로 부호화할 수 있다.
type-0에 대해 0을 저장할 수 있습니다(하트에 대한 유형이라고 함). 유사하게, 우리는 스페이드와 다이아몬드에 대해 클럽 등을 위한 유형 1에 대해 1을 저장할 수 있습니다.
이 개념을 실현하기 위해 아래 코드를 살펴보겠습니다.
// Representation of jack of hearts
int type = 0;
int value = 11;
// Representation of king of club
int type = 1;
int value = 13;
// Representation of three of spade
int type = 2;
int value = 3;
이 표현은 모든 카드 게임을 전달하는 목적으로 사용될 수 있습니다. 예를 들어, 이제 value
변수를 비교하여 어떤 플레이어가 더 큰 가치의 카드를 가지고 있는지 확인할 수 있습니다.
과소 고려 중인 플레이어의 카드 유형을 확인하려면 type
변수를 확인할 수 있습니다.
C++의 카드 한 벌 배열 표현
단일 카드에 대한 표현에 대해 논의했습니다. 한 벌의 카드에 대한 다양한 C++ 표현을 살펴보겠습니다.
C++에서 병렬 배열을 통한 표현
카드 덱을 나타내기 위해 두 개의 병렬 배열을 사용할 수 있습니다. 하나의 배열은 카드 유형을 저장하고 다른 배열은 해당 위치에 값을 저장합니다.
다음은 이러한 병렬 배열에 대한 배열 선언 및 초기화입니다.
int type[52] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3};
int value[52] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
이 코드에서 type[i]
및 value[i]
는 덱의 i번째 카드를 나타냅니다.
C++에서 모듈러스와 정수 나눗셈을 사용한 표현
이 체계보다 더 나은 또 다른 현명한 표현 방법이 있습니다. 정수 배열에 0에서 51까지 저장할 수 있습니다.
정수 나누기와 나머지 연산을 사용하여 카드 유형과 값을 얻을 수 있습니다.
처음 13개의 인덱스에 0에서 12까지 할당합니다. C++의 산술 나누기 연산자는 기본적으로 정수 나누기를 수행합니다. 따라서 0에서 12까지의 숫자를 13으로 나누면 0이 카드 유형(즉, 이 경우 하트 수트)이 됩니다.
또한 13으로 나머지 숫자를 취하면 0에서 12가 됩니다. 이 나머지 값은 카드의 실제 값이 될 수 있습니다.
따라서 다음 13개의 색인에 13~25를 할당하고 나머지 카드에 대해 계속해서 할당합니다.
다시 말하지만, 이 카드를 13으로 정수 나누면 1의 값을 얻게 됩니다(즉, 이 경우 클럽의 카드 유형). 다시 13으로 나머지를 취하면 카드 값인 0에서 12까지가 됩니다.
이 아이디어를 요약한 예제 코드를 살펴보겠습니다.
int card[52] = {0, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16..., 51};
int type = card[i] / 13;
int value = card[i] % 13;
이 표현에서는 공간 복잡성이 훨씬 더 좋습니다(즉, 두 개의 병렬 배열을 사용할 필요가 없습니다).
이전 표현에서 한 배열의 요소를 교체하려면 두 번째 배열의 해당 요소를 교체하여 해당 위치에서 유형과 값을 일관되게 유지해야 했습니다. 이 두 번째 표현에서는 그것에 대해 걱정하지 않고 섞을 수 있습니다.
그 이유는 하나의 숫자만이 카드 유형과 가치를 모두 나타내기 때문입니다.
10, 11과 같은 숫자를 사용하면 코드의 가독성이 떨어집니다. 따라서 코드를 더 읽기 쉽게 만들기 위해 몇 가지 상수를 정의할 수 있습니다.
#define JACK 10
#define QUEEN 11
#define KING 12
#define ACE 1
#define HEART 0
#define DIAMOND 1
#define CLUB 2
#define SPADE 3
이제 다음과 같이 비교할 수 있습니다.
type = card[i] / 13;
value = card[i] % 13;
... if (type == CLUB)... if (value == JACK)...
마지막으로 카드를 인쇄할 문자열 배열을 정의할 수 있습니다.
string type_name[] = {"Heart", "Diamond", "Club", "Spade"};
string value_name[]{"Ace", "Two", ..., "Jack", "Queen", "King"};
... cout << value_name[value] << "of" << type_name[type] << '\n';
출력은 다음과 같습니다.
Three of Spade
Jack of Diamond
Queen of Diamond
...
완벽한 C++ 구현
이제 C++ 배열에서 카드 데크의 표현이 명확해야 합니다. 그러나 표현에 대한 인지적 관점을 갖기 위해 위의 모든 코드 청크를 컴파일 준비가 된 단일 코드로 결합해 보겠습니다.
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
void Shuffle(int* Deck) {
srand(time(NULL));
int shufflePosition1, shufflePosition2, temp;
int shuffleCount = 100;
for (int i = 0; i < shuffleCount; i++) {
shufflePosition1 = rand() % 52;
shufflePosition2 = rand() % 52;
// swap cards at the shuffle positions
temp = Deck[shufflePosition1];
Deck[shufflePosition1] = Deck[shufflePosition2];
Deck[shufflePosition2] = temp;
}
}
void ShowFirstTenCards(int* Deck, string* TypeName, string* ValueName) {
int valueNamePosition;
int typeNamePosition;
for (int t = 0; t <= 10; t++) {
valueNamePosition = Deck[t] % 13;
typeNamePosition = Deck[t] / 13;
cout << "Position " << t << ": ";
cout << ValueName[valueNamePosition] << " of ";
cout << TypeName[typeNamePosition] << endl;
}
}
int main() {
int Deck[52];
for (int itr = 0; itr <= 51; itr++) {
Deck[itr] = itr;
}
string TypeName[] = {"Heart", "Diamond", "Club", "Spade"};
string ValueName[]{"Ace", "Two", "Three", "Four", "Five", "six", "Seven",
"Eight", "Nine", "Ten", "Jack", "Queen", "King"};
cout << "Top 10 cards before shuffle:" << endl;
ShowFirstTenCards(Deck, TypeName, ValueName);
Shuffle(Deck);
cout << "\nTop 10 cards After shuffle:" << endl;
ShowFirstTenCards(Deck, TypeName, ValueName);
return 0;
}
위의 코드 예제는 카드 데크를 나타내고 셔플 작업을 수행하는 간단한 구현입니다. 자세한 내용을 논의하기 전에 출력을 살펴보겠습니다.
출력:
Top 10 cards before shuffle:
Position 0: Ace of Heart
Position 1: Two of Heart
Position 2: Three of Heart
Position 3: Four of Heart
Position 4: Five of Heart
Position 5: six of Heart
Position 6: Seven of Heart
Position 7: Eight of Heart
Position 8: Nine of Heart
Position 9: Ten of Heart
Position 10: Jack of Heart
Top 10 cards After shuffle:
Position 0: Queen of Diamond
Position 1: Eight of Spade
Position 2: Five of Heart
Position 3: King of Diamond
Position 4: Nine of Heart
Position 5: Eight of Heart
Position 6: Seven of Heart
Position 7: Seven of Diamond
Position 8: six of Spade
Position 9: Ace of Club
Position 10: Two of Heart
위의 프로그램에는 세 가지 주요 코드 세그먼트가 있습니다.
- 드라이버 코드(
main()
) ShowFirstTenCards()
함수셔플()
기능
위에서 아래로 코드 세그먼트에 대해 논의해 보겠습니다. 기본 드라이버 코드는 세 개의 배열을 생성합니다.
데크
배열: 이전 섹션에서 설명한 방법에 따라 카드의 스위트 정보와 액면가를 저장합니다.TypeName
: 사용 가능한 4개의 제품군 이름을 저장합니다.ValueName
: 액면가의 영문 이름을 저장하여 출력을 더 보기 좋게 표시합니다.
중요한 선언 후에 드라이버 코드는 세 개의 배열을 ShowFirstTenCards()
메서드에 대한 인수로 전달했습니다. 그런 다음 이 방법은 가치 및 수트 변환 전략을 사용하여 데크의 처음 10개 카드를 표시합니다.
그런 다음 드라이버 함수는 Deck
배열을 인수로 사용하여 Shuffle()
함수를 호출합니다. 이 Shuffle()
함수는 [0-51] 범위에서 두 위치를 무작위로 선택하고 해당 위치에서 콘텐츠를 교환합니다.
좋은 셔플을 보장하기 위해 동일한 과정을 100번 반복합니다.
또한 shuffle()
함수는 현재 UNIX 타임스탬프를 rand()
함수의 시드 값(srand()
함수에서)으로 사용하여 셔플이 다른 호출에서 고유하게 유지되도록 합니다.
이 타임스탬프를 시드로 rand()
함수에 전달하지 않으면 rand()
는 항상 임의의 위치에 대해 동일한 시퀀스를 생성하므로 이러한 확률적 게임에는 바람직하지 않습니다. rand()
함수와 그 시드에 대한 자세한 내용은 여기에서 찾을 수 있습니다.