2020年6月12日21:58, プログラミング
前回まではdomemo.pyへDomemoクラスを作成しました。
今回は別のファイルを使用してプレイヤークラスを作成していきます。
さて、クラスオブジェクトはどんなデータを持っていて、どんな操作が出来るかを表現するものだと説明しました。そこでドメモというゲームをプレイすることを考えると、ドメモの箱だけあってもしょうがないですね。
それをプレイするプレイヤーがどうしても必要になります。
プレイヤーも、Aさんというプレイヤーさんがいるとすれば、Zさんというプレイヤーさんもいるかもしれません。重要なのはAさんもZさんも全く異なるデータを持っていたり、操作を行ったりするわけではないということです。
ボードゲームには必ずルールがあります。ですので、プレイヤーは一定の行動を行うわけです。
例を見ましょう。
次の図はプレイヤーが持つデータを示しています。もちろん、ここには書いていないようなデータもあるでしょう。
これをPythonで表現しましょう。
player.pyを作成して以下のコードを記載します。
class Player:
def __init__(self, name):
self.player_name = name
self.my_cards = []
self.original_cards = []
self.my_call_cards = []
self.my_call_judge = [True, True, True, True, True, True, True]
self.other_call_cards = []
self.call_card_list = [1, 2, 3, 4, 5, 6, 7]
クラスオブジェクトで使用する__init__メソッドについてはその3で説明しましたね。
1点違うのは、nameという引数を受け取っているという点です。
Domemoクラスのインスタンス化はDomemo()のようにかっこ()の中には何も入れませんでした。 これは、Domemoクラスの__init__メソッドが何も(self以外)引数を受け取らないためです。
Playerクラスではnameを受け取るため、インスタンス化する場合はPlayer("player_name")と何かしらの名前を入れる必要があります。渡されたnameはself.name = nameによってインスタンス変数self.player_nameに格納されます。
このようにすることで、インスタンス化するときにプレイヤー名を渡せば、異なる名前を持つPlayerクラスを作成できるのです。
各変数の意味を記載していきます。
私がシミュレーションする過程で必要になったデータを、Playerクラスに持たせています。
これは改良することができて、実際のゲームでプレイヤーが持っている情報をPythonで表現すればよいのです。他に考えられるとすれば、ブラフを言った回数や、声の大きさ、等も加えてみると違ったシミュレーションが出来るかもしれません。
雰囲気を変数で表現できたら素晴らしいですね。
メソッドを追加していきましょう。
次のコードをplayer.pyへ追記します。
import copy
class Player:
def __init__(self, name):
....
def set_my_cards(self, cards):
self.my_cards = cards
def get_my_cards(self):
return self.my_cards
def set_original_cards(self):
self.original_cards = copy.copy(self.get_my_cards())
def get_original_cards(self):
return self.original_cards
def get_hand_num(self):
return len(self.my_cards)
def get_my_call_cards(self):
return self.my_call_cards
ここで設定したメソッドはセッター、ゲッターと一般的に呼ばれているものです。
__init__メソッドで作成したmy_cardsやoriginal_cardsへ値を代入するのがセッター、それらの値をおreturnで返すのがゲッターです。
Pythonでは別にセッター、ゲッターを使用せずとも、インスタンス名.データ名で各データを参照することができます。
ただ、データと操作を分離したほうが私は良いと考えています。
例えば、後でmy_cardsに値をセットするときは、その前にprint文でセットする値を出力したい、となったとします。
このような場合、セッターを使っていればセッターを変更するだけで済みます。
インスタンス名.データ名で参照している場合は、その前にprint文を使う必要がありますね。これが1箇所だけならばよいのですが、10箇所、20箇所で使っていたら、直すのも大変ですね。
ですので、セッター、ゲッターを使用したほうが良いかなと思います。
もちろん、どんな書き方をしても大丈夫です。私だって変な書き方をしていることもありますし。要するに動けばいいという精神を持つことが大事でしょう。言語は伝えるためにあるわけで、下手でも伝えるほうが、伝えられないより、よっぽどいいと思います。
もう1つ文法の話です。
import copyでcopyというモジュールを読み込んでいます。
これはリスト型オブジェクトのコピーを返すメソッドを使用するために読み込みました。
さて、ここでどうして=ではなくてcopyを使うのか、確認しましょう。
>>> a = [1,2,3]
>>> b = a
>>> b
[1, 2, 3] # コピーされている
>>> del b[1]
>>> b
[1, 3] # bから2を削除
>>> a
[1, 3] # aからも2が消えてしまっている!
上記の例は=を使用した場合に起こる問題を示しています。
aという変数へリスト型オブジェクト[1,2,3]を格納しました。その後、bという変数へaを格納しました。
ここで行っている変数への格納という処理が、数値型や文字列型を格納する場合と違うのです。
# a = [1,2,3]に相当
a >>>参照>>> [1,2,3]
# b = aに相当
a >>>参照>>> [1,2,3] <<<参照<<< b
# del b[1]に相当
a >>>参照>>> [1,3] <<<参照<<< b
上記のように、リスト型オブジェクトは変数へ入れられているというよりも、変数がリスト型オブジェクトを見ている(参照している)と言えます。
したがって、同じリスト型オブジェクトを見ているa, bは同じ結果を返します。
bがオブジェクトの中身を消せば、aから見た場合も消えた後のリスト型オブジェクトが見えるわけです。
続いてcopyのしていることを確認しましょう。
# a = [1,2,3]に相当
a >>>参照>>> [1,2,3]
# b = copy.copy(a)に相当
a >>>参照>>> [1,2,3]
|
|<<< コピー
v
b >>>参照>>> [1,2,3]
# del b[1]に相当
a >>>参照>>> [1,2,3]
b >>>参照>>> [1,3]
copyはリスト型オブジェクトのコピーを作成します。そのため、aとbは中身は同じでも別のオブジェクトを見ているのです。
そのため、bから2を消しても、aからは消えません。
次回はセッター、ゲッター以外のメソッドを追加していきます!