Help us understand the problem. What is going on with this article?

Pythonでブラックジャック作った。

はじめに

アドベントカレンダーが空いていたのでまたまた書いていこうと思います。今回は題名の通りブラックジャックを作りました。

前回の記事はこちら→ Pythonでおみくじ作った。

環境

Ubuntu18.04LTS
Python3.6.9
vim

コード

play_bj.py
from random import shuffle


class Deck:
    """
    山札を表すクラス
    """
    def __init__(self):
        """
        山札を初期化して、シャッフルする。
        """
        suits = ['スペード', 'クラブ', 'ダイヤモンド', 'ハート']
        values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K']
        self.deck = []
        for i in suits:
            for j in values:
                self.deck.append((i, j))
        shuffle(self.deck)

    def draw(self):
        """
        山札から1枚引き、引いたカードを返す。
        """
        return self.deck.pop(0)


class Person:
    """
    プレイヤーを表すクラス
    """
    def __init__(self):
        """
        手札を初期化する。
        """
        self.hands = []
        self.point = 0

    def add(self, card):
        """
        手札にカードを1枚加える。
        """
        self.hands.append(card)

    def point_sum(self):
        """
        トランプの合計点数を求める。
        """
        self.point = 0
        for i in self.hands:
            if i[1] in ['J', 'Q', 'K']:
                self.point += 10
            else:
                self.point += i[1]

        return self.point


def BJ():
    """
    メインの処理
    """
    print('ブラックジャックへようこそ!')

    d = Deck()
    p = Person()
    c = Person()

    drawing(d, p, 'あなた')
    drawing(d, p, 'あなた')

    drawing(d, c, 'CPU')
    card = d.draw()
    c.add(card)

    player_point = p.point_sum()
    cpu_point = c.point_sum()

    if player_point == cpu_point == 21:
        print('引き分けです。')
        return
    elif player_point == 21:
        print('あなたの勝ちです!')
        return
    elif cpu_point == 21:
        print('あなたの負けです。')
        return
    # ここではAは11にならないので上の条件分岐は本当はいらない。

    while True:
        choice = input('"Hit"または"Stand"を入力してください。: ')

        while True:
            if choice.lower() == 'hit' or choice.lower() == 'stand':
                break
            else:
                choice = input('"Hit"または"Stand"を入力してください。: ')

        if choice.lower() == 'hit':
            drawing(d, p, 'あなた')
            player_point = p.point_sum()
            if player_point >= 22:
                print('あなたの負けです。')
                return
        elif choice.lower() == 'stand':
            break
        else:
            print('error')
            return

    print('CPUのもう1つのカードは{}の{}です。'.format(c.hands[1][0], c.hands[1][1]))

    while True:
        cpu_point = c.point_sum()
        if cpu_point < 17:
            drawing(d, c, 'CPU')
            cpu_point = c.point_sum()
            if cpu_point >= 22:
                print('あなたの勝ちです!')
                return
        else:
            break

    if player_point == cpu_point:
        print('引き分けです。')
        return
    elif player_point > cpu_point:
        print('あなたの勝ちです!')
        return
    elif player_point < cpu_point:
        print('あなたの負けです。')
        return


def drawing(class1, class2, name):
    """
    山札からカードを1枚引き手札に加える。
    その後、加えたカードの情報を表示する。
    """
    card = class1.draw()
    class2.add(card)
    print('{}の引いたカードは{}の{}です。'.format(name, card[0], card[1]))


if __name__ == '__main__':
    BJ()

おわりに

関数とクラスのいい勉強になりました。次は、Aに11も対応できるようにしようかな。もっと良いものができ次第、追記していきます。

追記

クラスの継承などを利用してコードを整理しました。また、Aに11も対応できるようにしました。(12/09)
エラー処理,argparseを追加しました。(12/16)
loggingを追加しました。(01/04)

import argparse
import logging
from logging import getLogger, StreamHandler, FileHandler, Formatter
import sys
from random import shuffle


logger = getLogger(__file__)
logger.setLevel(logging.DEBUG)

stream_handler = StreamHandler()
file_handler = FileHandler('debug.log')

stream_handler.setLevel(logging.DEBUG)
file_handler.setLevel(logging.DEBUG)

stream_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

stream_handler.setFormatter(stream_format)
file_handler.setFormatter(file_format)

logger.addHandler(stream_handler)
logger.addHandler(file_handler)


class Card:
    SUITS = 'スペード', 'クラブ', 'ダイヤモンド', 'ハート'
    RANKS = 'A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'

    def __init__(self, suit, rank):
        self.suit = suit
        self.rank = rank
        self.number = self.RANKS.index(rank) + 1


class Deck:
    def __init__(self):
        self.deck = [Card(suit, rank)
                     for suit in Card.SUITS
                     for rank in Card.RANKS]
        shuffle(self.deck)

    def draw(self):
        return self.deck.pop()


class Participant:
    def __init__(self, name):
        self.name = name
        self.hands = []
        self.point = []

    def drawing(self, card, display=True):
        self.hands.append(card)
        self.point.append(min(card.number, 10))
        if display:
            print('{}の引いたカードは{}の{}です。'
                  .format(self.name, card.suit, card.rank))


class Player(Participant):
    def choose_a_first(self):
        in_a = any(card.rank == 'A' for card in self.hands)
        while in_a and sum(self.point) <= 11:
            try:
                choice_point = input('Aを1としますか11をしますか?: ')
            except (KeyboardInterrupt, EOFError):
                print()
                print('ブラックジャックを終了しました。')
                sys.exit()
            else:
                if choice_point == '11':
                    self.point.append(10)
                    break
                if choice_point == '1':
                    break
        print('{}の合計得点は{}です。'.format(self.name, sum(self.point)))

    def choose_a(self):
        while self.hands[-1].rank == 'A' and sum(self.point) <= 11:
            try:
                choice_point = input('Aを1としますか11をしますか?: ')
            except (KeyboardInterrupt, EOFError):
                print()
                print('ブラックジャックを終了しました。')
                sys.exit()
            else:
                if choice_point == '11':
                    self.point.append(10)
                    break
                if choice_point == '1':
                    break
        print('{}の合計得点は{}です。'.format(self.name, sum(self.point)))

    def is_continue(self):
        while True:
            try:
                choice = input('Hit または Standを入力してください。: ').lower()
            except (KeyboardInterrupt, EOFError):
                print()
                print('ブラックジャックを終了しました。')
                sys.exit()
            else:
                print()
                if choice == 'hit':
                    return True
                if choice == 'stand':
                    return False


class Dealer(Participant):
    def choose_a_first(self):
        in_a = any(card.rank == 'A' for card in self.hands)
        if in_a and sum(self.point) <= 11:
            self.point.append(10)

    def choose_a(self):
        if self.hands[-1].rank == 'A' and sum(self.point) <= 11:
            self.point.append(10)
        print('{}の合計得点は{}です。'.format(self.name, sum(self.point)))

    def show_card(self):
        print('{}のもう1枚のカードは{}の{}です。'
              .format(self.name, self.hands[1].suit, self.hands[1].rank))
        print('{}の合計得点は{}です。'.format(self.name, sum(self.point)))
        print()

    def is_continue(self):
        if sum(self.point) < 17:
            return True
        else:
            return False


class BlackJack:
    def __init__(self):
        try:
            name = input('名前を入力してください。: ')
        except (KeyboardInterrupt, EOFError):
            print()
            print('ブラックジャックを終了しました。')
            sys.exit()
        else:
            self.deck = Deck()
            self.player = Player(name)
            self.dealer = Dealer('ディーラー')

    def play(self):
        print()
        self.player.drawing(self.deck.draw())
        self.player.drawing(self.deck.draw())
        self.player.choose_a_first()
        print()
        self.dealer.drawing(self.deck.draw())
        self.dealer.drawing(self.deck.draw(), False)
        self.dealer.choose_a_first()
        print()

        j_21 = self.judje_21(sum(self.player.point), sum(self.dealer.point))
        if j_21:
            logger.info('ナチュラル21')
            return

        while self.player.is_continue():
            self.player.drawing(self.deck.draw())
            self.player.choose_a()
            print()
            if sum(self.player.point) >= 22:
                print('{}の負けです。'.format(self.player.name))
                logger.info('プレイヤーバスト')
                return

        self.dealer.show_card()

        while self.dealer.is_continue():
            self.dealer.drawing(self.deck.draw())
            self.dealer.choose_a()
            print()

        self.judje(sum(self.player.point), sum(self.dealer.point))

    def judje_21(self, player_point, dealer_point):
        if player_point == dealer_point == 21:
            print('{}もナチュラル21です。'.format(self.dealer.name))
            print('引き分けです。')
            return True
        elif player_point == 21:
            print('{}の勝ちです。'.format(self.player.name))
            return True
        elif dealer_point == 21:
            print('{}はナチュラル21です。'.format(self.dealer.name))
            print('{}の負けです。'.format(self.player.name))
            return True

    def judje(self, player_point, dealer_point):
        if player_point == dealer_point:
            print('引き分けです。')
        elif player_point > dealer_point or dealer_point >= 22:
            print('{}の勝ちです。'.format(self.player.name))
        elif player_point < dealer_point:
            print('{}の負けです。'.format(self.player.name))


def get_parser():
    parser = argparse.ArgumentParser(description='ターミナル上でブラックジャックができます。')

    parser.add_argument('-q', '--qiita', action='store_true', help='ソースコードがあるQiitaのリンク')
    parser.add_argument('-r', '--rules', action='store_true', help='ブラックジャックのルールを説明')

    args = parser.parse_args()
    return args


def main():
    args = get_parser()
    if args.qiita:
        print('https://qiita.com/yoshige/items/d06382f2a8b76ce6cd79')
        return

    if args.rules:
        print('ブラックジャックのルールはこちらをご覧ください')
        print('https://ja.wikipedia.org/wiki/ブラックジャック')
        return

    logger.info('start')
    print('ブラックジャックへようこそ!')
    bj = BlackJack()
    bj.play()
    logger.info('end')

if __name__ == '__main__':
    main()
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした