動機
本家様
プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし【追記有】
で、とりあえずレギュレーションを満たすものは作れたが、
- もっとオブジェクト指向っぽくしたい
- 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やらが入ってくると、プレーヤーとディーラーの立場の違いがはっきりしてくるので、両者を違うクラスで扱った方が良くなりそう。