Python　初心者の自分用メモ日記r2　ブラックジャック作成

##実行環境

* MacBook Air (11-inch, Mid 2012)
* JupyterLab (from Anaconda)
* Python 3.6.4


##今回のあらすじ
class使ってブラックジャック作った。
ルールの参考元：https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B8%E3%83%A3%E3%83%83%E3%82%AF

##思考経路
長いコードを書くときにみんなはどう書いているのだろう。
最近思うのはコードなんてものは、準備の時点、つまり設計図の比重が80％くらいなのかなぁと思ったり。なぜその設計図になったかという思考経路が重要なのでは？

練習がてら思考経路を軽くメモ：
スタートとゴールをまず決める。
今回はブラックジャック。
ゴールは決まっている。
ゴール。プレイヤーとディーラーの手札を比較して結果を出す。手札が必要。
ゴールから逆算。手札を配る必要あり。配る元の場所が必要。配ったものを受け取る場所がいる。つまり山札とプレイヤーが必要。
多分いまスタート地点。経路を紙に絵を描く。
スタート地点に到着したら、スタート地点から作り始める。
途中で思いついた追加したい情報は、別の欄にメモしていく。プレイヤー数を増やしたい。ベットさせる。連勝ボーナス。ゲームの再起動確認など。なんでもいい残しておく。

より現実に即するために情報を追加していく。←ここでよく迷う。
Class カードを例にとる。具体的な情報を付け加える。カードは52枚。4つのシンボル。数字は1-13。シャッフルして、上から一枚ずつ引いていく。(大抵見落としがある。後で追加する。)付け加えた情報を分解。基準は保存する変数か。動作か。まとめられそうなものをまとめる。どういうふうに作るか考える。複数出た場合は比較。
山札の作り方)
1. ["♡",1,2,3 ...,13]のリストをシンボルごとに作る
2. ("♡1",1)のようなタプルを52個作る。
3. {"♡":[1,2,...13]}の辞書で作る。
今回は2で作ってリストに保存した。山札を引くときの動作が最も簡単に想像できたから。そのまま山札から引く方法も思いついてコードを書く。

class Playerはとりあえず必要そうな変数を追加しまくってあとから引き算することにした。Class Playerにすると、(掛け金、口座、手札、勝利状態、名前、合計、連続勝利数、ベット倍率)の変数を作成。動作は後付で作ることにした。面倒くさかったから。

スタートから順番に細かく情報を増やしていく。エラーに遭遇しまくる。

* 予約後のsumとかlenとかをインスタンス変数、インスタンスメソッド名に使ってバグる。
* 動作持たないインスタンス変数に()つけて "変数のデータ型" is not callable
* インスタンス変数と、インスタンスメソッドを一緒の名前していまう。インスタンスメソッドを実行してTypeError
* in_placeメソッドはreturn Noneを知ってるけど、思わずやっちゃう。
* 変数の値が常に等しくないはずなのに等しい。同名の別のグローバル変数が悪さしてる。

あとはコードを書いていく。I need healing.

juypterLabはセルでコードを分けて実行し、テストを行うことができる。好き。
小さい部品を作ったら毎回テストするようにするとデバッグが楽。関数に定義して、動作を分けるとデバックが楽。





```py
#Card class

import random

class Card:

    def __init__(self):
        self.deck = [(i,x) for i in ["♡","♢","♧","♤"] for x in range(1,14)]
        self.num = list(range(52))
        random.shuffle(self.num)
        
    def shuffle(self):
        random.shuffle(self.num)
        
    def hit(self):
        if self.num:
            return self.deck[self.num.pop()]
        #山札が切れた場合
        else:
            self.__init__()
            return self.deck[self.num.pop()]
    
    def __str__(self):
        return f"トランプの山札の残り枚数は"+str(len(self.num))+"枚"
```


```py
#Player class
class Player:
    
    def __init__(self, name="Guest", balance=100):
        self.name = name
        self.balance = 100
        self.bet = 0
        #self.win_streak = 0
        self.condition = None
        self.hand = []
        self.total = 0
        
    def __str__(self):
        return f"Name: {self.name}, Balance: {self.balance}, Bet: {self.bet}, Hand: {self.hand}"
    
    def bet_money(self):
        num = ""
        while True:
            try:
                num = int(input(f"{self.name}. Your balance is {self.balance}. How many bet?: "))
                if 0 <= num <= self.balance:
                    self.bet = num
                    break
                else:
                    print(f"Not much rish, Your balance is {self.balance}")
                    print("or Bet 0 for fun")
            except (ValueError, TypeError):
                print("Do you know Number?")
                
    def total_hand(self):
        self.total = sum(10 if x[1]>=10 else 11 if x[1]==1 else x[1] for x in self.hand)
        if self.total > 21 and 1 in [x[1] for x in self.hand]:
            self.total -= 10
            return self.total
        else:
            return self.total
        
    def result(self, rate=1):
        if self.condition:
            self.balance += self.bet * rate
            self.bet = 0
            return "Win"
        else:
            self.balance -= self.bet
            self.bet = 0
            return "Lose"
```


```py
#その他
def multi():
    num = ""
    while True:
        try:
            num = int(input("How many players? min1,max3: "))
            if 1 <= num <= 3:
                return num
            elif num == 0:
                return 0
            else:
                print("Too many friends")
                continue
        except (TypeError,ValueError):
            print("Number between 1-3 please, 0 for stop playing")
            continue
            
def make_player(player_list, num):
    if 1 <= num <=3:
        for i in range(1, num+1):
            player_list.append(Player(f"P{i}"))
        return player_list
    else:
        return []

def hit_or_stand():
    while True: 
        rq = input("Hit or Stand: ")
        if rq.lower()[0] == "h":
            return True
        elif rq.lower()[0] == "s":
            return False
        else:
            print("Can't understand, try again")
            continue
        
def bust_check(hand_total):
    return True if hand_total > 21 else False

def show_me_your_moves(p_list):
    for p in p_list:
        print(f"{p.name} Total is", p.total_hand(), p.hand)

def win_check(p_list):
    #Dealer Bust
    if p_list[0].condition == False:
        for p in p_list[1:]:
            if p.condition != False:
                p.condition = True
    else:
        dealer = p_list[0].total_hand()
        for p in [p for p in p_list[1:] if p.condition!=False]:
            if p.total_hand() > dealer:
                p.condition = True 
```

```py

card = Card()
dealer = Player("Dealer")
p_list= [dealer]
p_list = make_player(p_list, multi())

#ブラックジャックによろしく
while len(p_list) >= 2:
    for player in p_list:
        if player.name != "Dealer":
            player.bet_money()
        for i in range(2):
            player.hand.append(card.hit())

    #手札公開
    print("OPEN THE GAME and SHOW THE CARDS")
    print(f"Dealer is {p_list[0].hand[0]}, (?,?)")
    for p in p_list[1:]:
        print(f"This is {p.name}, {p.hand}")


    #ゲーム開始
    for p in p_list:
        while True:
            if p.name != "Dealer":
                print(f"{p.name} Turn, {p.hand}")
                
            #bust判定
            if bust_check(p.total_hand()):
                if p.name != "Dealer":
                    print(f"{p.name} Bust, Drop out")
                #敗北判定
                p.condition = False
                break
            elif p.name == "Dealer":
                if p.total_hand()<=17:
                    p.hand.append(card.hit())
                else:
                    break
            else:
                if hit_or_stand():
                    p.hand.append(card.hit())
                else:
                    break  

    show_me_your_moves(p_list)
    win_check(p_list)
    for p in p_list[1:]:
        print(f"{p.name} " + p.result())
        print(p)

    if input("Keep Going? y or n: ").lower()[0] == "y":
        for p in p_list:
            p.hand = []
            p.condition = None
            p.total = 0
    else:
        print("Thank you for playing")
        break
```

##あとがき
やっぱ`if __name__="__main__":`の記述つけたほうがいいんかな？
body部分をメソッドの中に入れてさ。

歩道あるいてると、そこ信号いるか？って信号ある。時間の流れで時代遅れになった設備の位置情報をgooglemapに追加してきゃまぁいろいろ便利なんじゃないの？紙のない公共トイレとか有害指定ですぞ。