はじめに
今日はロベールとブラックジャックをやります。
BlackJack【1日目】
ここはクラスで作ったほうがいいの?とかここは構造体使ったほうがいいの?とか関数作ったほうが便利じゃない?とか、考えてもよくわからなかったので、main関数だけで処理を作った。しかし、まだ完成していなく、自分がカードを引いて21になったら負けというだけの段階。
まだ、マークとかj~kはないのもとして扱っているのでマークとかも取り入れたい。
コード
#include <iostream>
#include <array>
#include <vector>
#include <numeric>
#include <random>
int main(){
constexpr char* hart = "♥";
constexpr char* spade = "♠";
constexpr char* clover = "♣";
constexpr char* dia = "◆";
char choose = 'd';//dで初期化
int count = 0;//動的配列の要素数
int card = 0;//自分で引いた数を格納する場所
int sum = 0;//引いたカードの合計値を格納する
std::vector<int> dealer = {0};//ディーラーの持ち札を入れる
std::vector<int> myself = {0};//自分の持ち札を入れる
std::uniform_int_distribution<int> dist(1,10);//i~10のランダム生成
std::mt19937 mt{ std::random_device{}() };//ランダム作るときに必要
std::cout << "BrackJackを始めます" << std::endl;
while(1) {
//myself[count] =
card = dist(mt);//引いたカードの数を決定
myself[count] = card;//動的配列の前から順番に引いたカードの値を代入
sum += card;//引いたカードの数字を合計値に足す
std::cout << "カードを一枚引きました。" << std::endl
<< "数字は" << card<< "です" <<std::endl
<< "合計値は" << sum <<"です" << std::endl;//std::accumulate(myself.begin(), myself.end(), 0)は使えなかった
if(sum > 21){//合計値が21より大きくなると負け
std::cout << "21より大きくなりました。あなたの負けです。" << std::endl;
break;
}
else if(sum == 21)break;//合計値が21丁度になると強制的に終了
std::cout << "引き続きカードを引きますか?(y/n)" << std::endl;//カードを引くかどうかの意思決定
std::cin >> choose;//y/nをchoooseに代入させる
if(choose == 'n') break;//もしnならカードを引くことを終了
count++;//配列の要素数を1つ上げる。
}
}
一日目を終えて
今日やってみた感想。
実際に自分の書いたコードを見ることでクラスをどのように作ればいいか見えてきた。
カードを引く動作はディーラと自分で同じなので基底クラスにvirtualつけることでなんとかできそう。
動的配列で、格納した値をstd::accumulate(myself.begin(), myself.end(), 0)
で出力しようとしたが、出力結果がおかしくなったので、sumを使った。
クラスを作るって難しい。てか、使い所がいまいちわからない。関数でよくない?と思いがちになる。明日は、実際に関数とかクラスを作ってみようと思う。
ロベールのC++
07-06 純粋仮想関数
いままでStreamクラス(基底クラス)に仮想関数を使用するためだけに実装内容を書いてきた。しかし、これは無駄である。なので、純粋仮想関数を使うことで、このムダを省くことができる。
#ifndef STREAM_H_
#define STREAM_H_
//0以上の値を順次取得していく処理行うための基底クラス
class Stream {
public:
double Get() const;//設定された値を取得
bool Set();
public:
double m_n; //現在の値
protected:
virtual void SetBase() = 0;//純粋仮想関数
};
#endif
#include "Stream.h"
#include <iostream>
//設定された値を取得
double Stream::Get() const {
return m_n;
}
//値を設定
bool Stream::Set() {
SetBase();
return m_n >= 0;
}
#include "InputStream.h"
#include <iostream>
int main(){
InputStream istream;
std::cout << "> " << std::flush;
istream.Set();
std::cout << istream.Get() << std::endl;
}
同じように実行された。
基底クラスで実体がなくても派生クラスでオーバーライドしていればなにの問題もない。
しかし、純粋仮想関数があるとそのクラスのオブジェクトは作れない!
Stream stream;
こんなことができなくなるのか。
このようなクラスを「抽象クラス」という。
また、純粋仮想関数が一つでもオーバーライドされていなければ派生クラスも抽象クラスになる。
抽象クラスでは、「このクラス群はこんなかんじ」とクラスの抽象的な形を定義し、具体的な動作は定義しない。
そして、具体的な動作は、派生クラスで定義する。(has-a関係っぽい)
constやアクセス指定子、純粋仮想関数はコンパイル時にエラーを出してくれるので、積極的に使う。
終わりに
明日は「継承とコンストラクタ」が3節あるので一気に終わらせる。
おやすみなさい。