【Cardクラスを定義】C++でクラスを使ってトランプカードの山札を作りたい【未完成】
c++をやるならclassをちゃんと作れるようにしないとね(逃げるな)
ポーカーにハマっているので、最終的に簡単なポーカー(テキサスホールデムが好き)ゲームを作ることを目標に
今回はジョーカーなしの13*4=52枚のカードデータの雛形のクラスを作ります。
class Cardに必要なもの
- 固有id(識別用)
- 柄(スーツ、♠♦♥♣)
- 数字(A23456789TJQK)
ポーカーではAが最強なので実際にはKよりAがでかくなるよう番号を振るほうが
良いように思いますが頭がおかしくなるのでAは1として扱い、Kは13とします。
別のクラスでルールを制定するときに改めて数字の強さを示すvalueを作ったほうが多分良いのかも。
データ構造のイメージ
id | A(1) | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | T(10) | J(11) | Q(12) | K(13) |
♠(1) | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 |
♦(2) | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
♥(3) | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
♣(4) | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
プログラミングのお作法的には0始まりにしたほうが絶対良いけどやはり慣れないので1スタートで。
Cardクラスのメンバ変数初期化の値を-1にしておいて、-1を検出したらエラーを返す。
……となると数値0が手持ち無沙汰になるのでやっぱり0スタートの-1エラー扱いが効率がいい。
もしくはオール0をジョーカーにするか。
ま、まあ初心者はまずどんな形でも完成させることが重要だからね。細かい修正は後にしよう。
最小限のclass Card
class Card{ public: int cardID; int cardSuit; int cardNumber; };
classではメンバ変数はprivateにしてset/get関数で入出力するというお作法があります。
お作法を全て無視したものがこちらです。カプセル化とかオブジェクト指向という概念に中指を立てすぎている。
動くかテストするコードはこれ
#include <bits/stdc++.h> using namespace std; class Card{ public: int cardID; int cardSuit; int cardNumber; }; int main(){ Card ASpade; ASpade.cardID=1; ASpade.cardSuit=1; ASpade.cardNumber=1; cout << "cardID="<<ASpade.cardID<<endl; cout << "cardSuit="<<ASpade.cardSuit<<endl; cout << "cardNumber="<<ASpade.cardNumber<<endl; }
出力結果
cardID=1
cardSuit=1
cardNumber=1
まあ動きます。シンプルですね。後は全てのカードをCard型で設定すれば山札が完成します。
こんなclassじゃ人に見せられない
ちゃんとclassの特性を活かしたコードにブラッシュアップしていきます。
#include <bits/stdc++.h> using namespace std; class Card{ private://メンバ変数はprivateに int cardID; int cardSuit; int cardNumber; public: Card(const int& id,const int& suit,const int& num) :cardID(id),cardSuit(suit),cardNumber(num) {//(id,suit,num)を受け取りその値で初期化 } void get_Card(){cout <<cardID<<cardSuit<<cardNumber<<endl;return;} }; int main(){ Card As(1,1,1); As.get_Card(); }
111
publicで宣言するclass名と同名のCard():{}は初回呼び出し時にメンバ変数を初期化します。
今回はCard(id,suit,num)を受け取り、その値でメンバ変数を初期化して宣言して
それらのメンバ変数が初回以降書き換わらないようにしてあるはずです。多分。
void get_Card()は設定されている(id,suit,num)をcoutするだけです。
これで最低限のcard情報を扱うclass Cardができました。
次にやることは52個のCard nを定義してデッキをつくることです。
次回vectorにCard(独自クラス)型を入れたい
コンストラクタ周りの理解が足りず、どうやらコピーコンストラクタをうんぬんすることで
独自クラスのデータをvectorで管理できるようだが実装できていない。
独習c++を読み直します。
またコメント等でもっと美しく機能的なclassを作る方法などご指南いただけると幸いです
とか思ってたらすぐにできた
#include <bits/stdc++.h> using namespace std; class Card{ private://メンバ変数はprivateに int cardID; int cardSuit; int cardNumber; public: Card(int& id,int& suit,int& num) :cardID(id),cardSuit(suit),cardNumber(num) {//(id,suit,num)を受け取ったらその値で初期化 } Card(const Card &cd):cardID(cd.cardID),cardSuit(cd.cardSuit),cardNumber(cd.cardNumber) {}//コピーコンストラクタ void get_Card(){cout <<cardID<<" "<< cardSuit <<" "<<cardNumber<<endl;return;} }; int main(){ vector<Card> Deck;//Card型データをまとめるvector int count=1;//id用 for(int i=1;i<5;++i){ for(int j=1;j<14;++j){ int id=count; int suit=i; int num=j; Deck.push_back(Card(id,suit,num)); count++; } } for(int i=0;i<52;++i){ Deck.at(i).get_Card(); } }
一応メイン関数にデッキ構築処理を書いていますがいずれ関数として切り分けます。
vector
二重ループのiはsuit番号,jはnumberに対応、idだけ別個にループ1回につきインクリメント処理されるcountを用意。
vec.push_back(x);はvectorの後ろにxを追加する操作。
xにclass(コンストラクタに渡したい引数)を入れることでコピーコンストラクタが頑張ってvectorにデータを渡している、はず。
順序的には初期化しているコンストラクタでまずCard(id,suit,num)を受け取って変数を初期化したCard型データができて、
それをコピーコンストラクタが参照してvectorに渡している……でいいのかな。
やはりもうちょっと動作の部分について学ぶべき。