1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonでブラックジャック作ってみた(その2)

Posted at

前回投稿したブラックジャックのコードについて、コメント欄でご指摘いただいた点を修正してみました。@shiracamusさまご意見ありがとうございました。

以下、修正内容になります。

blacjack.py
from random import shuffle
import sys

class Card:

    suits = ['','','','']

    ranks = [None, "A","2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]

    def __init__(self, s, r):
        self.suit = s
        self.rank = r

    def __int__(self):
        r = self.ranks[self.rank]
        if r == "J" or r == "Q" or r == "K":
            r = 10
        elif r == "A":
            r = 1
        return int(r)

    def __str__(self):
        v = self.ranks[self.rank] + " of " + self.suits[self.suit]
        return v

class Deck:
    def __init__(self):
        self.cards = []
        for i in range(1, 14):
            for j in range (4):
                self.cards.append(Card(j, i))
        shuffle(self.cards)

    def draw(self):
        if len(self.cards) == 0:
            return
        return self.cards.pop()

class PlayerBase():
    def __init__(self, name):
        self.total = 0
        self.name = name
        self.deck = Deck()

    def draw_msg(self,card):
        d = '{} は {} を引いた'
        print(d.format(self.name,card))

    def score_msg(self):
        s = '{} の合計スコア {} '
        print(s.format(self.name,self.total))

    def match(self):
        if self.total == 21:
            s = '{} の合計スコア {} '
            print(s.format(self.name,self.total))
            w = 'BLACKJACK! {}の勝ち'
            print(w.format(self.name))
            return True
        elif self.total > 21:
            s = '{} の合計スコア {} '
            print(s.format(self.name,self.total))
            l = 'BUST! {}の負け'
            print(l.format(self.name,self.total))
            return True
        else:
            return False


class User(PlayerBase):

    def first_draw(self):
        pc1 = self.deck.draw()
        pc2 = self.deck.draw()
        self.total += int(pc1) + int(pc2)
        self.draw_msg(str(pc1))
        self.draw_msg(str(pc2))

    def hit_stand(self):
        while True:
            hs = input("ヒット or スタンド(h/s):")
            if hs == "h":
                pc = self.deck.draw()
                self.total += int(pc)
                self.draw_msg(str(pc))
                return True
            elif hs == "s":
                return False

class Dealer(PlayerBase):

    def first_draw(self):
        dc1 = self.deck.draw()
        dc2 = self.deck.draw()
        self.total += int(dc1) + int(dc2)
        self.draw_msg(str(dc1))
        self.draw_msg("(伏せ札)")
        return str(dc2)

    def auto_draw(self):
        while True:
            if self.total < 17:
                dc = self.deck.draw()
                self.total += int(dc)
                self.draw_msg(str(dc))
                return True
            else:
                return False

class Game():
    def __init__(self):
        self.u = User('あなた')
        self.d = Dealer('ディーラー')

    def game_continue(self):
        while True:
            yn = input('ゲームを続けますか?(y/n):')
            if yn == "y":
                self.u.total = 0
                self.d.total = 0
                return True
            elif yn == "n":
                print('ゲームを終了します')
                sys.exit(-1)

    def play_game(self):
        while True:
            continue_flg = False

            self.u.first_draw()
            holecard = self.d.first_draw()

            while self.u.hit_stand():
                if self.u.match():
                    continue_flg = self.game_continue()
                    break
            if continue_flg:
                continue

            self.u.score_msg()

            o = '{} の伏せ札は {}'
            print(o.format(self.d.name, holecard))

            while self.d.auto_draw():
                if self.d.match():
                    continue_flg = self.game_continue()
                    break
            if continue_flg:
                continue

            self.d.score_msg()

            if self.d.total < self.u.total:
                print('{} の勝ち'.format(self.u.name))
            elif self.d.total > self.u.total:
                print('{} の負け'.format(self.u.name))
            else:
                print('引き分け')

            if self.game_continue():
                continue



if __name__ == '__main__':
    game = Game()
    game.play_game()


前回からの変更点

大きく2点、変更しました。

ひとつは、Gameクラスからプレイヤー関連のクラスに主な関数を移したこと。

プレイヤー関連のクラスはまずPlayerBaseのクラスを作成。その後でUserクラスとDealerクラスに継承させています(このへんの構成は出題者の方が想定した形に近いようです)。

Userクラスにはカードを引くか止めるか問う関数hit_standを、Dealerクラスには17以上になるまでカードを引き続ける関数auto_drawを設定しています。その他、プレイヤーの動作のひとつひとつをなるべく関数化して、Gameクラスではその関数を実行するだけにしてみました。

もうひとつは、ゲームのコンティニューで再帰関数を使わないようにしたこと。

関数game_continueではTrue,Falseを返すだけの形にしました。加えて関数play_gameの全体をwhile文で囲んでループ化しました。この他の関数でもやたらwhile文を多用しちゃっているような気がしますが、こんなものでしょうか。それとも再帰関数を避けようと思ったら、while文に頼るしかないものなのか。他に方法が思い浮かびませんでした。

blackjack.py

       while True:
            continue_flg = False

            self.u.first_draw()
            holecard = self.d.first_draw()

            while self.u.hit_stand():
                if self.u.match():
                    continue_flg = self.game_continue()
                    break
            if continue_flg:
                continue


while文の多重ループを抜けるためにcontinue_flg変数を設定してます。シンプルそうに見えますが、この形にたどり着くまでにけっこう試行錯誤しました。うまくコンテニューできず変なところで終わったり、変なところから始まったり。

これまでごく単純な関数しか書いたことなかったので、今回いろいろチャレンジできてプラスになりました。反面「果たしてこれが正解なんだろうか」と思う部分もいくつかありました。こんなことを考えるようになったこと自体、「とりあえず動いているから良し」と思っていた以前とはえらい違いですが。「とりあえず動く」だけでも何通りもあることがわかったので、「AとBの方法があったら、どっちを採用すべきか」と考えてしまうわけです。

@shiracamusさまからいただいたご指摘から推測するに、以下のような要素が基準になると考えています。

  • オブジェクトの責務が適切であるか
    1つの関数に1つのことだけさせているか。クラス毎に関連性の高い関数が属しているか等。
  • なるべくメモリ消費の少ない、効率的な方法であるか
    無駄な動きをさせない。データを溜め込まない等。

とはいえ、では何が適切であるか、何が効率的であるか、という部分は相変わらず手探りの状況です。

そんなわけで、引き続きコメント欄にて、ご意見を広く募集しております。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?