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

前々回
[C++でブラックジャックを作りたい](https://qiita.com/fj91/items/16f3e8976d0552af5182)
前回
[続・C++でブラックジャックを作りたい] (https://qiita.com/fj91/items/054d7c5e6ef52a8f6b0b)

前回でA(エース)の処理が実装され、無事にBJが出るBJが作れた。
今回は、チップの概念を導入し、継続して複数回のプレイが可能な形に変更する。
現実のBJ同様、使用するshooterは各ゲームで同一にしたいので、shooter内のカード枚数がゼロになったら新しい山を補充するという機能を追加。


- Card_holderのメンバ関数としてtipsを定義し、各ゲームでの勝敗に応じてそれを変更していくというスタイルを取る。
- ゲームの進行をクラスGameで管理し、main関数の可読性を向上。
- 勝敗処理に使っていたgotoを排除。
- ただし、ゲームを継続するか否かの処理はgotoが一番シンプルそうなので使用。

乱数周りについては[前回] (https://qiita.com/fj91/items/054d7c5e6ef52a8f6b0b)のコメント欄で @yumetodo さんに示して頂いた例をそのまま使用(勉強不足)。

##コード

```C++

#include "random_device.hpp"
#include <iostream>
#include <vector>
#include <array>
#include <random>
#include <algorithm>

using namespace std;

using seed_v_t = std::array<cpprefjp::random_device::result_type, sizeof(std::mt19937)/sizeof(cpprefjp::random_device::result_type)>;

seed_v_t create_seed_v()
{
  cpprefjp::random_device rnd;
  seed_v_t sed_v;
  std::generate(sed_v.begin(), sed_v.end(), std::ref(rnd));
  return sed_v;
}
std::mt19937 create_random_engine()
{
  const auto sed_v = create_seed_v();
  std::seed_seq seq(sed_v.begin(), sed_v.end());
  return std::mt19937(seq);
}
std::mt19937& random_engine() {
  static thread_local std::mt19937 engine = create_random_engine();
  return engine;
}




class Shooter{

    private:

        static constexpr size_t max_size = 4*13;

        vector<int> cardlist;

        void shuffledeck(vector<int>& deck){
            shuffle(deck.begin(), deck.end(), random_engine());
        };

    public:

        Shooter(){
            for(int i = 0; i < max_size; i++) this->cardlist.emplace_back(i);
            shuffledeck(this->cardlist);
        };

        int hit(){

            if(this->cardlist.size() == 0){
                cout << "No cards, new cards filled" << endl;
                for(int i = 0; i < max_size; i++) this->cardlist.emplace_back(i);
                shuffledeck(this->cardlist);
            }

            cout << "new card " << endl;
            int new_card;
            new_card = this->cardlist.back();
            this->cardlist.pop_back();
            return new_card;

        };


};

class Card_holder{

    private:
        vector<int> mycards;
        vector<int> myhands;
        bool busted;
        double tips;
        bool check_busted() const noexcept{
            return (*min_element(this->myhands.begin(), this->myhands.end()) > 21);
        };
        static int valueof(int card) noexcept{
            return min(card % 13 + 1, 10);
        };

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

            vector<int> non_bustedhands;
            non_bustedhands.reserve(myhands.size());

            copy_if(myhands.begin(), myhands.end(), back_inserter(non_bustedhands), [](int n){ return n <= 21; });

            sort(non_bustedhands.begin(), non_bustedhands.end());
            non_bustedhands.erase(std::unique(non_bustedhands.begin(), non_bustedhands.end()), non_bustedhands.end());
            return non_bustedhands;
        };

        static vector<int> calc_hands(vector<int>&& myhands, vector<int>& mycards) noexcept{   // Aを処理し、可能な手の組み合わせを生成

            if(0 != mycards.back() % 13){
                const auto inc =  valueof(mycards.back());
                for(int& x: myhands) x += inc;
                    return myhands;
            }
            else{
                vector<int> calculated;
                calculated.reserve(myhands.size() * 2);
                for(const int x: myhands){
                    calculated.emplace_back(x + 1);
                    calculated.emplace_back(x + 11);
                }
                return calculated;
            }

        };

    public:

        Card_holder(): mycards(), myhands(1),busted(false),tips(100){};

        void cleaning() noexcept{
            this->myhands = vector<int>(0);
            this->myhands.push_back(0);
            busted = false;
        }

        double showtips() const noexcept{
            return this->tips;
        }

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

        bool isbusted() const noexcept{
            return this->busted;
        };

        void tipchange(double value, double bet) noexcept {
            this->tips += value * bet;
        }



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

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


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

            this->mycards.emplace_back(new_card);
            this->myhands = calc_hands(move(this->myhands), mycards);
            this->busted = check_busted();
            this->myhands = deletetrash(this->myhands);
            if(this->myhands.size() >0){
                cout <<"hand is " ;
                for(int x: this->myhands) cout << x  << ", ";
                cout <<endl;
            }

        };


};

class Game{   //勝利判定やゲームの進行をまとめたクラス


    public:

        Game(Card_holder& dealer, Card_holder& player, double& bet){

            player.cleaning();
            dealer.cleaning();
            cout << "Game Start" << endl;
            cout << "Your tips amout is " << player.showtips() << endl;
            cout << "Please bet" << endl;

            cin >> bet;
        };


        int judge(Card_holder& dealer, Card_holder& player) const noexcept{

            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()) return 1 ;
            if(player.open_hand() == dealer.open_hand()) return 0;
            if(player.open_hand() < dealer.open_hand()) return -1;

            return 10;


        }

        int player_turn(Shooter& shooter, Card_holder& player) noexcept{

            cout << "--------------------------------" << endl;
            cout << "Player turn" << endl;
            player.hit(shooter);
            player.hit(shooter);


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

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

            if(stay != 'y') {
                cout << "player busted" << endl;
                return -1;
            }

            cout << "player hand is " << player.open_hand() << endl;

            return 0;


        }

    int dealer_turn(Shooter& shooter, Card_holder& dealer) noexcept{

        cout << "--------------------------------" << endl;
        cout << "dealer turn" << endl;


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

        if(dealer.isbusted()) {
            cout << "dealer busted" << endl;
            return 1;
        }

        cout << "dealer hand is " << dealer.open_hand() << endl;


        return 0;

      }

};



int main() {

    Shooter shooter;

    Card_holder player;
    Card_holder dealer;


    NEXT_GAME:

        double bet;
        int win_lose_flag;   //決着がついていない状態を0で持つ。2: player BJ, 1: player win, -1: dealer win

        Game game(dealer, player, bet);

        win_lose_flag = 0;

        cout << "--------------------------------" << endl;
        cout << "dealer card open" << endl;
        dealer.hit(shooter);

        win_lose_flag = game.player_turn(shooter, player);

        if(win_lose_flag == 0 || win_lose_flag == 1) win_lose_flag = game.dealer_turn(shooter, dealer);

        if(win_lose_flag == 0) win_lose_flag = game.judge(dealer, player);

        switch (win_lose_flag)
              {
                case 2:
                    cout << "player win" << endl;
                    player.tipchange(1.5, bet);
                    break;
                case 1:
                    cout << "player win" << endl;
                    player.tipchange(1.0, bet);
                    break;
                case 0:
                    cout << "no game" << endl;
                    break;
                case -1:
                    cout << "player lose" << endl;
                    player.tipchange(-1.0, bet);
                    break;
                default:
                    cout << "Something wrong happened" << endl;
              }


        cout << "Your tips amout is " << player.showtips() << endl;

        char next_game;
        cout << "--------------------------------" << endl;
        cout << "Next game? press y or n" << endl;;
        cin >> next_game;

        if(next_game == 'y') goto NEXT_GAME;

        return 0;

}



```

##実行例

```shell
>./a.out
Game Start
Your tips amout is 100
Please bet
10
--------------------------------
dealer card open
new card 
Card is 5 of 0
hand is 5, 
--------------------------------
Player turn
new card 
Card is 6 of 0
hand is 6, 
new card 
Card is 4 of 0
hand is 10, 
if stay, type y
n
new card 
Card is 3 of 0
hand is 13, 
if stay, type y
y
player hand is 13
--------------------------------
dealer turn
new card 
Card is 2 of 0
hand is 7, 
new card 
Card is 8 of 0
hand is 15, 
new card 
Card is 9 of 0
dealer busted
player win
Your tips amout is 110
--------------------------------
Next game? press y or n
y
Game Start
Your tips amout is 110
Please bet
10
--------------------------------
dealer card open
new card 
Card is 7 of 0
hand is 7, 
--------------------------------
Player turn
new card 
Card is 10 of 0
hand is 10, 
new card 
Card is 1 of 0
hand is 11, 21, 
Black Jack!
player win
Your tips amout is 125
--------------------------------
Next game? press y or n
y
Game Start
Your tips amout is 125
Please bet
15
--------------------------------
dealer card open
No cards, new cards filled
new card 
Card is 4 of 0
hand is 4, 
--------------------------------
Player turn
new card 
Card is 8 of 0
hand is 8, 
new card 
Card is 1 of 0
hand is 9, 19, 
if stay, type y
y
player hand is 19
--------------------------------
dealer turn
new card 
Card is 3 of 0
hand is 7, 
new card 
Card is 10 of 0
hand is 17, 
dealer hand is 17
--------------------------------
Result
player card is 19
dealer card is 17
player win
Your tips amout is 140
```

3連勝......

##今後

勝敗判定のフラグはGameのメンバ変数にしたほうが良かったかもしれない。

次はsurrender, double, splitといった特殊動作を実装したいところ。
いい加減標準入力の例外処理をするべきか……