続いてデッキクラスを作りましょう。
デッキのデータはカードクラスの配列が必要です。
これまで色々なデータ構造(コンテナ)を説明しましたから、何が一番向いているか考えたいと思います。
トランプを使ったゲームをイメージした時に、カードを順番に配ったり、重ねたカードの上から順番に取ったりする事が多いと思います。
この事から、配列ベースの構造が良いと思います。
配列のように添え字でアクセス出来るのは「配列」「ベクタ」「デック」があります。
※「連想配列」は添え字も使えますが、添え字が「キー」になるのでイメージが違います。
「配列」はサイズが変えられませんが、「ベクタ」「デック」であればカードを配った後でpopする事によりカードを減らす事が出来ます。
もちろん、pushすればカードを増やす事も出来ますので、カードをデッキに戻す事も出来ます。
「ベクタ」は最後尾への追加・削除が得意ですが、「デック」は先頭と最後尾への追加・削除が得意です。
様々な状況に対応出来るのは「デック」だと思いますので、今回は「デック」を使いましょう。
Deck.hを開いて、クラスの宣言を書きましょう。
カードクラスを使いますから、Card.hをインクルードする必要があります。
また、カードの総数も持っておきたいです。
<sample program cpp059-01>
/* Deck.h */
#pragma once #include "Card.h" #include <deque> class Deck { public: static const int CARD_MAX; private: std::deque<Card> m_deqCard; }; |
Deck.cppを開いて「CARD_MAX」の初期値を入れましょう。
<sample program cpp059-02>
/* Deck.cpp */
#include "Deck.h" const int Deck::CARD_MAX = Card::SUIT * Card::NUMBER; |
これは前にやりましたね。
トランプのデッキに必要なメンバ関数を考えると、
・初期化(カードを全セット用意する) ・シャッフル ・カードを配る(1枚) ・カードを戻す(1枚) ・デッキを表示する(デバッグ用) |
色々なゲームに対応したデッキを作ろうとすると、このくらいは必要だと思います。
デッキの初期化は、m_deqCardにスペードからクラブまでの1〜13のカードをセットします。
Deck.hにプロトタイプ宣言を書きます。
<sample program cpp059-03>
/* Deck.h */
#pragma once
#include "Card.h"
#include <deque>
class Deck {
public:
static const int CARD_MAX;
void Initialize();
private:
std::deque<Card> m_deqCard;
};
|
次に本体をDeck.cppに追加します。
<sample program cpp059-04>
/* Deck.cpp */
#include "Deck.h" const int Deck::CARD_MAX = Card::SUIT * Card::NUMBER; void Deck::Initialize() { Card work; for (int i = 0; i < Card::SUIT; i++) { for (int j = 0; j < Card::NUMBER; j++) { work.SetData(i, j + 1); m_deqCard.push_back(work); } } } |
次はシャッフルですが、これはアルゴリズムでやった「random_shuffle」を使えば簡単に作れます。
乱数を使いますので、乱数の種も設定しましょう。
<sample program cpp059-05>
/* Deck.h */
#pragma once #include "Card.h" #include <deque> #include <cstdlib> #include <ctime> class Deck { public: static const int CARD_MAX; Deck(); void Initialize(); void Shuffle(); private: std::deque<Card> m_deqCard; }; |
乱数の種は1度だけ設定すれば良いので、コンストラクタ関数で設定したいと思います。
<sample program cpp059-06>
/* Deck.cpp */
#include "Deck.h" #include <algorithm> const int Deck::CARD_MAX = Card::SUIT * Card::NUMBER; Deck::Deck() { srand((unsigned int)time(NULL)); } void Deck::Initialize() { Card work; for (int i = 0; i < Card::SUIT; i++) { for (int j = 0; j < Card::NUMBER; j++) { work.SetData(i, j + 1); m_deqCard.push_back(work); } } } void Deck::Shuffle() { std::random_shuffle(m_deqCard.begin(), m_deqCard.end()); } |
この時点で、デッキが正しく出来ているかどうかチェックするため、デバッグ用関数を先に作ります。
関数名はカードの表示と同じくShowにします。
C言語の時は、「ShowHand」とか「ShowDeck」という関数名を使いましたが、クラスに名前が付いていますので、動詞だけで分かります。
また、クラスが異なっても同じ機能を同じ関数名にする事で意味が統一されます。
それでは、Deck.hを開いてください。
<sample program cpp059-07>
/* Deck.h */
#pragma once
#include "Card.h"
#include <deque>
#include <cstdlib>
#include <ctime>
class Deck {
public:
static const int CARD_MAX;
Deck();
void Initialize();
void Shuffle();
void Show() const;
private:
std::deque<Card> m_deqCard;
};
|
Deck.cppで中身を追加します。
<sample program cpp059-08>
/* Deck.cpp */
#include "Deck.h"
#include <algorithm>
const int Deck::CARD_MAX = Card::SUIT * Card::NUMBER;
Deck::Deck()
{
srand((unsigned int)time(NULL));
}
void Deck::Initialize()
{
Card work;
for (int i = 0; i < Card::SUIT; i++) {
for (int j = 0; j < Card::NUMBER; j++) {
work.SetData(i, j + 1);
m_deqCard.push_back(work);
}
}
}
void Deck::Shuffle()
{
std::random_shuffle(m_deqCard.begin(), m_deqCard.end());
}
void Deck::Show() const
{
for (int i = 0; i < CARD_MAX; i++) {
m_deqCard[i].Show();
}
}
|
それでは、main関数を変更してデッキが上手く出来ているかどうかのチェックをしましょう。
まず、Main.hを変更します。
<sample program cpp059-09>
/* Main.h */
#pragma once
#include "Deck.h"
|
基本的にデッキ経由でしかカードは扱いませんので、デッキだけをインクルードします。
次にMain.cppを書き換えます。
<sample program cpp059-10>
/* Main.cpp */
#include "Main.h"
int main()
{
Deck deck;
deck.Initialize();
deck.Shuffle();
deck.Show();
return 0;
}
|
<実行結果>
スペード:K スペード:2 スペード:10 ク ラ ブ:J スペード:A ダ イ ヤ:2 ダ イ ヤ:J ダ イ ヤ:6 ダ イ ヤ:4 ク ラ ブ:7 ハ ー ト:6 ク ラ ブ:8 ダ イ ヤ:7 ク ラ ブ:2 ハ ー ト:J ク ラ ブ:K ダ イ ヤ:A ク ラ ブ:3 ク ラ ブ:10 ハ ー ト:5 スペード:9 ク ラ ブ:9 ハ ー ト:10 ダ イ ヤ:9 ダ イ ヤ:5 スペード:8 スペード:7 スペード:Q スペード:6 スペード:5 スペード:3 ハ ー ト:9 ク ラ ブ:4 ダ イ ヤ:10 ク ラ ブ:Q ク ラ ブ:A ハ ー ト:K スペード:4 ク ラ ブ:5 ハ ー ト:4 ダ イ ヤ:K ダ イ ヤ:Q ハ ー ト:2 ハ ー ト:A ハ ー ト:Q ハ ー ト:3 ダ イ ヤ:3 ハ ー ト:7 ハ ー ト:8 ク ラ ブ:6 スペード:J ダ イ ヤ:8 続行するには何かキーを押してください・・・
シャッフル後の状態を表示しましたが、シャッフル前の状態も見ておくと確実です。
残りの関数は次回にしましょう。