LoginSignup
4
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-04-21

動機

プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし

C++については入門書を読んだ程度のpython人間(ただしオブジェクト指向的な機能は使ったことがない)として、学習の題材としてちょうどよさそうだったので。

ルール

ルールのおさらい(以下引用)

  • 初期カードは52枚。引く際にカードの重複は無いようにする
  • プレイヤーとディーラーの2人対戦。プレイヤーは実行者、ディーラーは自動的に実行
  • 実行開始時、プレイヤーとディーラーはそれぞれ、カードを2枚引く。引いたカードは画面に表示する。ただし、ディーラーの2枚目のカードは分からないようにする
  • その後、先にプレイヤーがカードを引く。プレイヤーが21を超えていたらバースト、その時点でゲーム終了
  • プレイヤーは、カードを引くたびに、次のカードを引くか選択できる
  • プレイヤーが引き終えたら、その後ディーラーは、自分の手札が17以上になるまで引き続ける
  • プレイヤーとディーラーが引き終えたら勝負。より21に近い方の勝ち
  • JとQとKは10として扱う
  • Aはとりあえず「1」としてだけ扱う。「11」にはしない
  • ダブルダウンなし、スピリットなし、サレンダーなし、その他特殊そうなルールなし

コード

元記事のルール以下の部分は見ずに自分なりに書いてみたコードがこちら



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


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

class Card_holder{

    private:
        int hand;
        bool bursted = false;

    public:

    bool stayed = false;

    int open_hand(){
        return hand;
    };

    int isbursted(){
        return bursted;
    };


    int valueof(int card){
        int cardtonum;
        int cardtotype;

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

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

        if(cardtonum >= 11) cardtonum = 10;

        return cardtonum;

    };

    void hit(vector<int>& deck){
        int new_card;
        new_card = deck.back();
        deck.pop_back();
        hand = hand + valueof(new_card);
        if(hand > 21) bursted = true;

    };

};



int main() {

    //
    // 1-13のカード4種類をランダムにシャッフル
    //

    vector<int> cardlist(13*4);

    for(int i = 0; i < 13*4; i++) cardlist[i] = i;

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

    Card_holder player;
    Card_holder dealer;


    //
    // ディーラーのカードを一枚公開
    //

    cout << "dealer card" << endl;
    dealer.hit(cardlist);

    //
    // プレーヤーのターン
    //

    cout << "Player turn" << endl;
    cout << "player card" << endl;
    player.hit(cardlist);
    player.hit(cardlist);
    cout << "player hand is " << player.open_hand() << endl;


    char stay = 'n';

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

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



    //
    // バーストしてなければディーラーのターン
    //

    cout << "dealer turn" << endl;


    while(dealer.open_hand() < 17){
        dealer.hit(cardlist);
        cout << "dealer hand is " << dealer.open_hand() << endl;;

    };

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


    //
    // 勝敗判定
    //

    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;



    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

dealer card
Card is 2 of 0
Player turn
player card
Card is 13 of 2
Card is 10 of 3
player hand is 20
if stay, type y
y
dealer turn
Card is 6 of 0
dealer hand is 8
Card is 7 of 3
dealer hand is 15
Card is 13 of 0
dealer hand is 25
Dealer bursted
player win


>./a.out

dealer card
Card is 3 of 2
Player turn
player card
Card is 9 of 1
Card is 6 of 2
player hand is 15
if stay, type y
n
Card is 11 of 3
player hand is 25
player bursted
player lose
  • 面倒だったので絵柄は数字[0, 1, 2, 3]で代用。
    (スペードの1 -> 1 of 0, ハートの5 -> 5 of 2のようなイメージ)

  • 山はstd::vectorで扱い、カードを引く動作をpop_back()で。

  • ディーラーとプレーヤーを同一のクラスで扱っている。

  • バースト時の処理がややこしくなりそうだったのでgotoを使った。

課題

オブジェクト指向っぽさが弱い……。

エースの処理を実際のBJに近くする辺りが面倒そう。

現状でもマルチプレーヤー化は簡単だが、それをどうオブジェクト指向っぽくやるか。

ディーラーが引いている時とプレーヤーが引いている場合を分けた方が変更には強そう。

今回のコード、厳密には提示されたBJのルールに従っていない。
というのも、

実行開始時、プレイヤーとディーラーはそれぞれ、カードを2枚引く。引いたカードは画面に表示する。ただし、ディーラーの2枚目のカードは分からないようにする

がルールだが、今回はディーラーの2枚目以降はプレーヤーの動作の後に引かれることになっている。このおかげでカード引きの処理をディーラとプレーヤーで統一できている & カードがちゃんとランダムにシャッフルされるゲームなら問題はない、ので一概に悪いとはいえないのだけれど…

4
3
0

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