続・C++でブラックジャックを作りたい

##動機

本家様
[プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし【追記有】](https://qiita.com/hirossyi73/items/cf8648c31898216312e5)

前記事
[C++でブラックジャックを作りたい](https://qiita.com/fj91/items/16f3e8976d0552af5182)

で、とりあえずレギュレーションを満たすものは作れたが、

- もっとオブジェクト指向っぽくしたい
- BJが存在しないBJとは一体……

ということで、

- 「Shooter」クラスを使って山をプレーヤー、ディーラーから隠蔽
- A(エース)の処理を追加し、実際のBJに近づける

べく以下のコードを作成。ちなみに今回も、元記事のルール以下は見ずに挑戦。

Aが入ると手が分岐するので、std::vector\<int\> myhandsに現在の可能な(バーストしていない)手の組み合わせを格納するようにした。


##コード

```C++


#include <iostream>
#include <vector>
#include <random>
using namespace std;


//
// シューター = BJでカードを配るときに使うアレ
//


class Shooter{

    private:
        vector<int> cardlist;

    public:

        Shooter(){
            for(int i = 0; i < 13*4; i++) cardlist.push_back(i);
            shuffledeck();
        };

        void shuffledeck(){

            random_device seed_gen;
            mt19937 engine(seed_gen());
            shuffle(cardlist.begin(), cardlist.end(), engine);


        };

        int hit(){
            cout << "new card " << endl;
            int new_card;
            new_card = cardlist.back();

            cardlist.pop_back();
            return new_card;

        };


};


//
// プレーヤーとディーラーを共通して扱うクラス
//

class Card_holder{

    private:
        vector<int> mycards;
        vector<int> myhands;
        bool bursted;

    public:

    Card_holder(){
        myhands.push_back(0);
        bursted = false;
    }

    int open_hand(){ //バーストしていない組み合わせで最強のもの
        return *max_element(myhands.begin(), myhands.end());
    };

    bool isbursted(){
        return bursted;
    };


    void checkbursted(){

        if(*min_element(myhands.begin(), myhands.end()) > 21) bursted = true;

    };

    vector<int> deletetrash(){ // バーストしている組み合わせをmyhandsから削除

        vector<int> non_burstedhands;
        for(int x: myhands){
            if(x <= 21) non_burstedhands.push_back(x);
        }
        sort(non_burstedhands.begin(), non_burstedhands.end());
        non_burstedhands.erase(std::unique(non_burstedhands.begin(), non_burstedhands.end()), non_burstedhands.end());
        return non_burstedhands;
    };


    int valueof(int card){
        int cardtonum;
        cardtonum = card % 13 + 1;
        if(cardtonum >= 11) cardtonum = 10;
        return cardtonum;

    };

    void hit(Shooter& shooter){   // 分割したほうが良さそう?
        int new_card = shooter.hit();

        int cardtonum;
        int cardtotype;

        cardtonum = new_card % 13 + 1;
        cardtotype = new_card/13;

        cout << "Card is " << cardtonum << " of " << cardtotype << endl;

        mycards.push_back(new_card);
        myhands = calc_hands();
        checkbursted();
        myhands = deletetrash();

        if(myhands.size() >0){
            cout <<"hand is " ;
            for(int x: myhands) cout << x  << ", ";
            cout <<endl;
        }

    };


    vector<int> calc_hands(){   // Aを処理し、可能な手の組み合わせを生成
        vector<int> calculated;

        for(int x: myhands){
            if(0 != mycards.back() % 13) {

                calculated.push_back(x + valueof(mycards.back()));
            }
            else{

                calculated.push_back(x + 1);
                calculated.push_back(x + 11);
            };
        };

        return calculated;

    };

};



int main() {

    cout << "Game start" << endl;

    Shooter shooter;

    Card_holder player;
    Card_holder dealer;


    //
    // ディーラーのカードを一枚公開
    //
    cout << "--------------------------------" << endl;
    cout << "dealer card open" << endl;
    dealer.hit(shooter);
    dealer.open_hand();


    //
    // プレーヤーのターン
    //
    cout << "--------------------------------" << endl;
    cout << "Player turn" << endl;
    player.hit(shooter);
    player.hit(shooter);
    char stay = 'n';


    if(player.open_hand() == 21){
        cout << "Black Jack!" << endl;
        goto PLAYER_WIN;
    };


    while(!player.isbursted()){
        cout << "if stay, type y" << endl;
        cin >> stay;
        if(stay == 'y') break;
        player.hit(shooter);
    };

    if(stay != 'y') {
        cout << "player bursted" << endl;
        goto PLAYER_LOSE;
    }



    //
    // バーストしていなければディーラーのターン
    //
    cout << "--------------------------------" << endl;
    cout << "dealer turn" << endl;


    while(dealer.open_hand() < 17){
        dealer.hit(shooter);
    };

    if(dealer.isbursted()) {
        cout << "dealer bursted" << endl;
        goto PLAYER_WIN;
    }


    //
    // 勝敗判定
    //
    cout << "--------------------------------" << endl;
    cout << "Result" << endl;
    cout << "player card is " << player.open_hand() << endl;
    cout << "dealer card is " << dealer.open_hand() << endl;


    if(player.open_hand() > dealer.open_hand()) goto PLAYER_WIN ;
    if(player.open_hand() == dealer.open_hand()) goto NO_GAME;
    if(player.open_hand() < dealer.open_hand()) goto PLAYER_LOSE;


    cout << "--------------------------------" << endl;
    cout << "Result" << endl;
    PLAYER_WIN:
        cout << "player win" << endl;
        return 0;
    PLAYER_LOSE:
        cout << "player lose" << endl;
        return 0;
    NO_GAME:
        cout << "no game" << endl;
        return 0;


}


```

##実行例

```shell

>./a.out
Game start
--------------------------------
dealer card open
new card 
Card is 11 of 0
hand is 10, 
--------------------------------
Player turn
new card 
Card is 9 of 2
hand is 9, 
new card 
Card is 1 of 3
hand is 10, 20, 
if stay, type y
y
--------------------------------
dealer turn
new card 
Card is 4 of 2
hand is 14, 
new card 
Card is 7 of 2
hand is 21, 
--------------------------------
Result
player card is 20
dealer card is 21
player lose


>./a.out

Game start
--------------------------------
dealer card open
new card 
Card is 2 of 1
hand is 2, 
--------------------------------
Player turn
new card 
Card is 1 of 1
hand is 1, 11, 
new card 
Card is 12 of 1
hand is 11, 21, 
Black Jack!
player win

```

ということで、無事にBJが存在するBJができた。
doubleやsplit, insuranceの前にチップのシステムを実装しなければならないがどうするか……

Card_holderのprivateなメンバ変数としてtipsを定義してgotoの先で処理するのが一番簡単だろうか。

insuranceやらが入ってくると、プレーヤーとディーラーの立場の違いがはっきりしてくるので、両者を違うクラスで扱った方が良くなりそう。