【Python】クラス・インスタンス化の学習のため、トランプゲーム「ブラックジャック」をコーディングしてみた

# 概要
現在、プログラミングの勉強をしているが、クラスの概念について理解が乏しく、クラスを使って実現できるコーディングしやすいゲームは何かを考えたとき、ブラックジャックが頭に浮かび、早速コーディングしてみようと思った。

今回はターミナル上でコード実行することを想定してコーディングを進めた。

# 目次
[・ そもそもどういう遊びか？](#そもそもどういう遊びか？)
[・ コード作成フロー](#コード作成フロー)
[・ 実際のコーディング](#実際のコーディング)
[・ 動作結果](#動作結果)
[・ さらに良いコードにするために](#さらに良いコードにするために)
[・ まとめ](#まとめ)

# そもそもどういう遊びか？

ブラックジャックは最初に2枚のトランプカードが配られ、追加でカードを引いていくことで手持ちのカードの合計を21に近づけていくゲームである。
J（ジャック）、Q（クイーン）、K（キング）は10として扱う。
A（エース）は1か10として扱い、基本的に10で扱って21を超えてしまう場合は1として扱う。
21を超えてしまうとドボンで、最下位扱いとなる。


# コード作成フロー

本来はディーラーとプレイヤーに分かれたり、カードは周りのプレイヤーが見られないルールだが、今回は練習のためカードをオープンにした状態でプレイするものとする。

コーディングのフローは、

まずはプレイヤーを生成するクラスを作成し、プレイヤーの数を決める。
↓
各プレイヤーの名前を入力
↓
初期処理でカードを2枚ずつ配る。
↓
プレイヤーのターンで、カードを引くかどうか決める
　→引く: 21を超えてなければ再度カードを引くことができ、21を超えていたらドボンとなり、スコア0として確定する
　→引かない: プレイヤーのスコアを確定する
↓
プレイヤーの中でwinnerを決める

# 実際のコーディング
```Python3
import random

# トランプカードの生成
spade = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
heart = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
club = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
diamond = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
trump = spade + heart + club + diamond
random.shuffle(trump)


class Player():
    def __init__(self, name):
        self.name = name
        self.l = []
        self.sum = 0
        self.lnum = []
        self.lcourt = []
        self.lace = []
        self.lsort = []

    def addSum(self):

        card = trump.pop(0)
        self.l.append(card)

        if(card == "J" or card == "Q" or card == "K"):
            self.lcourt.append(card)
        elif(card == "A"):
            self.lace.append(card)
        else:
            self.lnum.append(card)

        self.lsort = self.lnum + self.lcourt + self.lace

        self.sum = 0

        for i in range(len(self.lsort)):
            if(self.lsort[i] == "J" or self.lsort[i] == "Q" or self.lsort[i] == "K"):
                self.sum = self.sum + 10
            elif(self.lsort[i] == "A" and self.sum <= 11):
                self.sum = self.sum + 10
            elif(self.lsort[i] == "A" and self.sum > 11):
                self.sum = self.sum + 1
            else:
                self.sum = self.sum + int(self.lsort[i])
        return card


# プレイヤー数の設定
print("何人で遊びますか?  1~4")
playerNum = input()

while(int(playerNum) > 4):
    print("4人以下で設定してください")
    playerNum = input()

lplayer = []

for i in range(1, int(playerNum)+1):

    print(str(i)+"番目のプレイヤーの名前を入力してください。")
    name = input()
    lplayer.append(name)

print("***********************************************")
print(lplayer)
print("上記"+playerNum+"人のプレイヤーでゲームを始めます。")
print("***********************************************")
input()

players = []
for i in range(len(lplayer)):
    players.append(Player(lplayer[i]))

# 各プレイヤーは最初に2枚のカードを引く
isStop = []
score = []
for i in range(len(lplayer)):
    players[i].addSum()
    players[i].addSum()
    print(lplayer[i]+"さんの手札は" + str(players[i].l) +
          "で、合計は" + str(players[i].sum) + "です。")
    isStop.append("Y")
    score.append(players[i].sum)
input()
print("***********************************************")

# 判定がYのプレイヤーだけ、カードを追加で引くかどうか決める
while "Y" in isStop:

    for i in range(len(isStop)):
        if(isStop[i] == "Y"):
            print(lplayer[i]+"さんのターンです。")
            print(lplayer[i]+"さんの手札は" + str(players[i].l) +
                  "で、合計は" + str(players[i].sum) + "です。")
            print("追加でカードを引きますか?  y/n")
            draw = input()
            if(draw == "y"):
                addCard = players[i].addSum()
                print("追加で" + str(addCard) + "を引きました。現在の手札は" +
                      str(players[i].l) + "です。")
                print("合計は" + str(players[i].sum)+"です。")
                score[i] = players[i].sum
                if(players[i].sum > 21):
                    print("21を超えてしまいました。ドボンです。")
                    print("***********************************************")
                    isStop[i] = "N"
                    score[i] = 0
                    input()
                    break
                print("***********************************************")
                input()
            if(draw == "n"):
                print(lplayer[i]+"さんのスコアは" + str(players[i].sum)+"です。")
                print("***********************************************")
                isStop[i] = "N"
                score[i] = players[i].sum
                input()

# 勝敗を決める
winner = [lplayer[i] for i, v in enumerate(score) if v == max(score)]

print("優勝は...!!")
input()
print(winner)
print("")
scoreBoard = [[lplayer[i], score[i]] for i in range(len(lplayer))]
print(scoreBoard)
print("***********************************************")
```


# 動作結果

まずは人数を決め、
```Python3
何人で遊びますか?  1~4
3
```

プレイヤーごとの名前を入力。
```Python3
1番目のプレイヤーの名前を入力してください。
AAA
2番目のプレイヤーの名前を入力してください。
BBB
3番目のプレイヤーの名前を入力してください。
CCC
***********************************************
['AAA', 'BBB', 'CCC']
上記3人のプレイヤーでゲームを始めます。
***********************************************
```

初手の手札が2枚配られる
```Python3
AAAさんの手札は['K', '3']で、合計は13です。
BBBさんの手札は['3', '9']で、合計は12です。
CCCさんの手札は['7', '3']で、合計は10です。
```

プレイヤーAAAはカードを引いたがドボン。（運が悪い）
```Python3
***********************************************
AAAさんのターンです。
AAAさんの手札は['K', '3']で、合計は13です。
追加でカードを引きますか?  y/n
y
追加で10を引きました。現在の手札は['K', '3', '10']です。
合計は23です。
21を超えてしまいました。ドボンです。
***********************************************
```

プレイヤーBBBはセーフ
```Python3
BBBさんのターンです。
BBBさんの手札は['3', '9']で、合計は12です。
追加でカードを引きますか?  y/n
y
追加で6を引きました。現在の手札は['3', '9', '6']です。
合計は18です。
***********************************************
```

プレイヤーCCCは手札を引き、BBBを超える合計得点
```Python3
CCCさんのターンです。
CCCさんの手札は['7', '3']で、合計は10です。
追加でカードを引きますか?  y/n
y
追加で9を引きました。現在の手札は['7', '3', '9']です。
合計は19です。
***********************************************
```

プレイヤーAAAはドボンのため、プレイヤーBBBのターンに戻る。
プレイヤーCCCが得点を上回っているためカードを引くが、ドボン。
```Python3
BBBさんのターンです。
BBBさんの手札は['3', '9', '6']で、合計は18です。
追加でカードを引きますか?  y/n
y
追加で6を引きました。現在の手札は['3', '9', '6', '6']です。
合計は24です。
21を超えてしまいました。ドボンです。
***********************************************
```

プレイヤーCCCは価値が確定しているので、カードを引かずに終了。
```Python3
CCCさんのターンです。
CCCさんの手札は['7', '3', '9']で、合計は19です。
追加でカードを引きますか?  y/n
n
CCCさんのスコアは19です。
***********************************************
```

優勝したメンバーの発表と、全プレイヤーの合計得点を表示。
（今回はプレイヤーCCCが優勝）
```Python3
優勝は...!!

['CCC']

[['AAA', 0], ['BBB', 0], ['CCC', 19]]
***********************************************
```

# さらに良いコードにするために

・今回のコーディングだと、最高得点のメンバーが2人以上いた場合に同列優勝という扱いにしているが、引いたカードの絵柄で差をつけたい。
（スペード＞ハート＞クラブ＞ダイヤ　のような順位付け）

最初に各絵柄のトランプを準備してまとめてシャッフルする処理をしているが、絵柄で分けた意味があまり活かされていないので、上記のような判断基準に使用したいと思った。


・今回はプレイヤー同士で点数を競うようにコーディングしたが、本来はディーラーとプレイヤーに分かれてプレイし、持ち点を賭けて遊ぶゲームである。
ディーラーのクラスを作り、また、プレイヤーのクラスに持ち点の変数を加え、ディーラーに勝てば賭けた点数の2倍、負ければ0 といったような遊び方に変えてコードを改修するのも面白いと思った。


# まとめ

クラスはフレーム、インスタンスは実体。みたいな例えをよく目にするが、今回のコーディング練習で少し理解ができた気がした。
クラス内にある「self」って何？どう扱うの？と疑問に思っていたが、インスタンス化して生成された各オブジェクトが持っている変数のことなんだなと理解した。

これからも作りたいと思ったものはめげずにコーディングをチャレンジしたいと思う。

以上！(^^)/