LoginSignup
4
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-04-25

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

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

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

乱数周りについては前回のコメント欄で @yumetodo さんに示して頂いた例をそのまま使用(勉強不足)。

コード


#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;

}



実行例

>./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といった特殊動作を実装したいところ。
いい加減標準入力の例外処理をするべきか……

4
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3