LoginSignup
3
1

More than 5 years have passed since last update.

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

Posted at

動機

本家様
プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし【追記有】

前記事
C++でブラックジャックを作りたい

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

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

ということで、

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

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

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

コード



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


}


実行例


>./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やらが入ってくると、プレーヤーとディーラーの立場の違いがはっきりしてくるので、両者を違うクラスで扱った方が良くなりそう。

3
1
2

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
3
1