4
4

More than 3 years have passed since last update.

【Python版】なぜ,あなたはJavaでオブジェクト指向開発ができないのか

Posted at

概要

オブジェクト指向再学習のために「なぜ,あなたはJavaでオブジェクト指向開発ができないのか」を購入したが、Java・Rubyでしか公式サンプルがなかったのでPythonで作成してみました。
(あくまで参考程度にしてください。私も初心者です。)

Lesson 5 まで(ジャンケンプログラム)

全体の処理進行

object_janken.py
from judge import *
from player import *
from tactics import *

# 審判(斎藤さん)のインスタンス生成
saito = Judge()

# プレイヤー1(村田さん)の生成
murata_tactics = RandomTactics()
murata = Player("村田さん",murata_tactics)

# プレイヤー2(山田さん)の生成
yamada_tactics = StoneOnlyTactics()
yamada = Player("山田さん",yamada_tactics)
# yamada = Player_STONE("山田さん")

# 村田さんと山田さんをプレイヤーとしてジャンケンを開始する
saito.start_janken(murata, yamada)

プレイヤークラス

player.py
class Player:

    # ジャンケンの手を表す定数
    STONE = 0  # グー
    SCISSORS = 1  # チョキ
    PAPER = 2  # パー

    def __init__(self, name, tactics):
        self.name = name
        self.win_count = 0
        self.tactics = tactics

    # ジャンケンの手を出す。
    def show_hand(self):
        hand = self.tactics.read_tactics()
        return hand

    # 審判から勝敗を聞く。勝ったら、引数 result は true
    def notify_result(self,result):
        if (result):
            self.win_count += 1

    # 自分の勝った回数を答える。
    def get_win_count(self):
        return self.win_count

    # 自分の名前を答える。
    def get_name(self):
        return self.name

# 継承し子クラス作成
class Player_STONE(Player):
    def show_hand(self):
        return self.STONE

ジャッジクラス

judge.py
class Judge:

    # ジャンケンを開始する。
    def start_janken(self, player1, player2):
        # ジャンケンの開始を宣言する
        print("【ジャンケン開始】\n")

        # ジャンケンを3回行う
        for cnt in range(3):
            # 何回戦目か表示する
            print("【", (cnt + 1), "回戦目】")

            # プレイヤーの手を見て、どちらが勝ちかを判定する。
            winner = self.judge_janken(player1, player2)

            if (winner != None):
                # 勝者を表示する
                print("\n", winner.get_name(), "が勝ちました!\n")
                # 勝ったプレイヤーへ結果を伝える
                winner.notify_result(True)
            else:
                # 引き分けの場合
                print("\n引き分けです!\n")

        # ジャンケンの終了を宣言する
        print("【ジャンケン終了】\n")

        # 最終的な勝者を判定する
        final_winner = self.judge_final_winner(player1, player2)
        print(player1.get_win_count(), " 対 ", player2.get_win_count(), "で")
        if (final_winner != None):
            print(final_winner.get_name(), "の勝ちです!\n")
        else:
            print("引き分けです!\n")

    # プレイヤーの手を見て、どちらが勝ちかを判定する。
    def judge_janken(self,player1, player2):
        winner = None

        hand_dict = {0: "STONE",
                     1: "SCISSORS",
                     2: "PAPER"}

        # プレイヤーの手を出す
        player1hand = player1.show_hand()
        player2hand = player2.show_hand()
        player1hand_name = hand_dict[player1hand]
        player2hand_name = hand_dict[player2hand]

        # それぞれの手を表示する
        print(f"{player1hand_name} vs. {player2hand_name} \n")

        # プレイヤー1が勝つ場合
        if (player1hand_name == "STONE" and player2hand_name == "SCISSORS") or \
            (player1hand_name == "SCISSORS" and player2hand_name == "PAPER") or \
            (player1hand_name == "PAPER" and player2hand_name == "STONE") :
            winner = player1

        # プレイヤー2が勝つ場合
        if (player1hand_name == "STONE" and player2hand_name == "PAPER") or \
            (player1hand_name == "SCISSORS" and player2hand_name == "STONE") or \
            (player1hand_name == "PAPER" and player2hand_name == "SCISSORS") :
            winner = player2

        # どちらでもない場合は引き分け(nilを返す)
        return winner

    # 最終的な勝者を判定する。
    def judge_final_winner(self,player1, player2):
        final_winner = None

        # 勝ち数を聞く
        player1_win_count = player1.get_win_count()
        player2_win_count = player2.get_win_count()

        # 勝者を決定
        if (player1_win_count > player2_win_count):
            final_winner = player1
        elif(player1_win_count < player2_win_count):
            final_winner = player2
        else:
            final_winner = None

        return final_winner

戦略クラス

tactics.py
class RandomTactics():

    def read_tactics(self):

        # 0.0以上3.0未満の小数として乱数を得る
        random_num = rm.random()* 3.0
        if (random_num < 1.0):
          hand = Player.STONE
        elif (random_num < 2.0):
          hand = Player.SCISSORS
        elif (random_num < 3.0):
          hand = Player.PAPER

        # 決定した手を戻り値として返す
        return hand


#==グーが大好き!」戦略クラス。
class StoneOnlyTactics():

  def read_tactics(self):
    # 必ずグーを出す
    return Player.STONE

Lesson 6(ババ抜きプログラム)

カードクラス

card.py

class Card():

    # ジョーカーを表す定数
    JOKER = 0
    # スペードを表す定数
    SUIT_SPADE = 1
    # ダイヤを表す定数
    SUIT_DIAMOND = 2
    # クラブを表す定数
    SUIT_CLUB = 3
    # ハートを表す定数
    SUIT_HEART = 4

    def __init__(self,suit,number):
        self.suit = suit
        self.number = number

    def getNumber(self):
        return self.number

    # toString関数をオーバーライド
    def toString(self):

        # JOKERの場合
        if self.suit == 0:
            card_code = "JK"

        # JOKER以外のカード
        else:
            suit_dict = {"SUIT_SPADE":"S", "SUIT_DIAMOND":"D",
                         "SUIT_CLUB":"C","SUIT_HEART":"H"}
            number_dict = {1:"A", 2:"2", 3:"3", 4:"4", 5:"5",
                           6:"6", 7:"7", 8:"8", 9:"9", 10:"T", 11:"J", 12:"Q", 13:"K"}
            card_code = suit_dict[self.suit] + number_dict[self.number]

        return card_code

手札クラス

hand.py

import random as rm

class Hand():

    # card クラスをリストとして保持
    def __init__(self,list):
        self.hand = list

    # カードを追加
    def add_card(self,card):
        self.hand.append(card)

    # カードを引く(先頭)
    def pick_card(self):
        picked_card = self.hand.pop(0)
        return picked_card

    # 所持枚数を返す
    def getNumberOfCard(self):
        return len(self.hand)

    # 手札をシャッフル
    def shuffle(self):
        # カードをランダムに抜き取って最後に加える動作を繰り返す
        number = self.getNumberOfCard()
        for i in range(number):
            pos = int(rm.random() * number)
            picked_card = self.hand.pop(pos)
            self.hand.append(picked_card)

    # 同じ数のカードを探し、配列で戻す
    def find_same_number_card(self):

        same_cards = None

        # 最後に追加されたカードの数字を取得する
        last_added_card = self.hand[-1]
        last_added_card_num = last_added_card.number

        for index in range(len(self.hand)-1):

            card = self.hand[index]
            if card.number == last_added_card_num:

                # 最後に追加されたカードと同じカードが見つかった場合
                # 見つかった組み合わせをsameCardsに格納し、ループを抜ける
                same_cards = [self.hand.pop(-1),self.hand.pop(index)]
                break

        return same_cards

    # 手札にあるカードを文字列で表現する。
    def toString(self):
        hand_cards = ""
        for card in self.hand:
            hand_cards += card.toString() + " "
        return hand_cards

テーブルクラス

table.py
class Table():

    def __init__(self):
        self.disposed_cards = []

    # カードを捨てる。
    def dispose_card(self,dispose_list):
        for card in dispose_list:
            print(f"{card.toString()} を捨てました")
            self.disposed_cards.append(card)

    # 捨てられたカードを文字列で表現する。
    def toString(self):
        disposed_cards = ""
        for card in self.disposed_cards:
            disposed_cards += card.toString() + " "
        return disposed_cards

プレイヤークラス

player.py

class Player():

    def __init__(self,name,hand,master,table):
        self.hand = hand
        self.name = name
        self.master = master
        self.table = table

    # 順番を指名する(自分の番)
    def play(self,next_player):

        # 次のプレイヤーに手札を出してもらう
        next_hand = next_player.show_hand()

        # 相手の手札からカードを一枚引く
        picked_card = next_hand.pick_card()

        # 引いた結果を表示
        print(self.name, ":", next_player.name, "さんから ", picked_card.toString()," を引きました\n")

        # 引いたカードを自分の手札に加え、同じ数のカードがあったら捨てる
        self.deal_card(picked_card)

        # 手札がゼロになったかどうか調べる
        if self.hand.getNumberOfCard() == 0:
            # 進行役に上がりを宣言する
            self.master.declare_win(self)
        else:
            # 現在の手札を表示する
            print(self.name, ":残りの手札は ",self.hand.getNumberOfCard(), "です\n")

    # 手札をシャッフルして見せる
    def show_hand(self):
        if self.hand.getNumberOfCard() == 1:
            self.master.declare_win(self)
        self.hand.shuffle()
        return self.hand

    # カードを受け取る
    def receive_card(self,card):
        self.deal_card(card)

    # 同じカードがあったら捨てる
    def deal_card(self,card):

        # カードを自分の手札に加える
        self.hand.add_card(card)

        # 今加えたカードと同じカードを探す
        same_cards = self.hand.find_same_number_card()

        # 同じカードの組み合わせが存在した場合
        if (same_cards != None):
            # テーブルへカードを捨てる
            self.table.dispose_card(same_cards)

    # プレイヤー名を返す
    def toString(self):
        return self.name

マスタークラス

master.py

class Master():

    def __init__(self,player_list):
        self.player_list = player_list

    def prepare_game(self,hand):
        print("【カードを配ります】\n")

        # トランプをシャッフルする
        hand.shuffle()

        # トランプの枚数を取得する
        number_of_cards = hand.getNumberOfCard()

        # プレイヤーの人数を取得する
        number_of_players = len(self.player_list)

        for index in range(number_of_cards):

            # カードから一枚引く
            card = hand.pick_card()

            # 各プレイヤーに順番にカードを配る
            player = self.player_list[index % number_of_players]
            player.receive_card(card)


    # ゲームを開始する。
    def start_game(self):
        print("\n【ばば抜きを開始します】\n")

        # プレイヤーの人数を取得する
        count = 0
        while len(self.player_list) > 1:
            player_index = count % len(self.player_list)
            next_player_index = (count + 1) % len(self.player_list)

            # 指名するプレイヤーの取得
            player = self.player_list[player_index]
            # 次のプレイヤーの取得
            next_player = self.player_list[next_player_index]

            # プレイヤーを指名する
            print("\n", player.name, "さんの番です --- ({})".format(count), "\n")
            player.play(next_player)
            count += 1

        # プレイヤーが上がって残り1名になるとループを抜ける
        print("【ばば抜きを終了しました】\n")


    # 上がりを宣言する。
    def declare_win(self,winner):
        # 上がったプレイヤー
        print(winner.name, "さんが上がりました!\n")

        # 上がったプレイヤーをリストからはずす
        self.player_list.remove(winner)

        # 残りプレイヤーが1人になった時は敗者を表示する
        if len(self.player_list) == 1:
            loser = self.player_list[0]
            print(loser.name, "さんの負けです!\n")


    # ゲームに参加するプレイヤーを登録する。
    def register_player(self,player):
        self.player_list.append(player)

実行プログラム

old_maid.py

from card import *
from hand import *
from master import *
from player import *
from table import *

# ばば抜きプログラム。


# 53枚のトランプを生成する。
def create_trump():
    trump = Hand([])

    # 各スート13枚のカードを生成する
    for i in range(13):
        number = i + 1
        trump.add_card(Card("SUIT_CLUB", number))
        trump.add_card(Card("SUIT_DIAMOND", number))
        trump.add_card(Card("SUIT_HEART", number))
        trump.add_card(Card("SUIT_SPADE", number))

    # ジョーカーの作成
    trump.add_card(Card(0, 0))

    return trump

# 進行役の生成
master = Master([])

# 場の生成
field = Table()

# プレイヤーの生成
murata = Player("村田", Hand([]), master, field)
yamada = Player("山田", Hand([]), master, field)
saito = Player("斎藤", Hand([]),master, field)

# 進行役へプレイヤーを登録
master.register_player(murata)
master.register_player(yamada)
master.register_player(saito)

# トランプを生成する
trump = create_trump()

# ゲームの準備をする
master.prepare_game(trump)

# ゲームを開始する
master.start_game()

フレームワーク化(7並べ)

・読み飛ばしたため、省略。気をつけないといけないのは以下の2点

抽象クラス

・pythonで抽象クラスを作成する際はABC(Abstract Base Classes)をインポートし、抽象クラスを継承する必要がある
→参考:PythonのABC - 抽象クラスとダック・タイピング

値渡し

・テーブルクラスがテーブル情報を返すときに値渡しにする必要がある

table.py
class FantanTable(Table):

    # テーブルはnumpyで作成
    matrix = np.full((4,13),"..")

    def getCard(self):
        return self.matrix.copy()
4
4
0

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
4
4