2020年6月30日20:09, プログラミング
ドメモクラスとプレイヤークラスの連携を前回行いました。
ではようやく、ドメモのシミュレーションを行っていきましょう!
新しくgame.pyを作成します。
以下のコードはドメモのシミュレーションを自動で行うことを表現しています。
これは全体の抜粋であるため、これ以外にもいくつかのメソッドを実装します。 ただ大きな流れを先に説明したいと思います。
class Game:
def play(self):
self.turn_player = self.get_turn_player()
if self.turn_player.get_hand_num() > 0:
self.call_card = self.turn_player.think(self.get_all_open_cards(self.turn_player))
self.is_card_in_the_hand = self.turn_player.call_card(self.call_card)
if self.is_card_in_the_hand:
self.domemo.add_open_card(self.call_card)
if self.turn_player.get_hand_num() == 0:
self.add_finish_player(self.turn_player)
この行は現在のターンプレイヤー、つまり数字を宣言するプレイヤーを設定しています。
self.get_turn_player()というメソッドを後で実装しますが、このメソッドは現在のターンプレイヤーを返します。
ここで、先程取得した現在のターンプレイヤーが持っている札の枚数を確認します。
プレイヤークラスはget_hand_num()という持っている札の枚数を返すメソッドを持っています。turn_playerはプレイヤークラスのインスタンスとして作られるため、このメソッドを使うことが出来ます。
もし札の枚数が0枚以上であれば、そのプレイヤーはまだドメモを続行しなければなりません。
札の枚数が0枚以上であれば、プレイヤーは思考を行い、数字を宣言します。
プレイヤークラスに実装されているthink()メソッドを用います。これはプレイヤークラスが思考を行い、その結果を返します。
思考を行った後、実際にその数字を宣言します。プレイヤークラスが持っているcall_card()メソッドで数字を宣言させます。引数にはthink()メソッドで返した数字を入れます。
call_card()メソッドの返り値は、宣言した数字がある(True)か、ない(False)かのどちらかです。それを変数へ代入します。
宣言した数字があるか、ないかを判断し、あった場合の処理を行います。
宣言した数字があった場合、その数字は公開情報になります。そのためドメモインスタンスのadd_open_card()メソッドを使用して追加します。
このメソッドも未実装のため後で加えましょう。
ターンプレイヤーが持っている札が0枚だった場合の処理を記載します。
持っている札が0枚であればゲーム終了のため、ターンプレイヤーをadd_finish_playerメソッドで終了プレイヤーとして登録します。
STEP1で紹介したplay()というメソッドを繰り返し呼び出すことで、ターンプレイヤーは巡ります。最終的にすべてのプレイヤーが持つ札が0枚になるまで呼び出せば、ドメモのシミュレーションができますね。
ここからは細かいメソッドを追加していきましょう。
class Game:
def play(self):
...
def judge_game_run(self):
if len(self.finish_player_list) < len(self.player_instances):
return True
else:
return False
def get_turn_player(self):
loop_index = 0
while loop_index < len(self.player_instances):
self.turn_player = self.player_instances[self.turn_number % len(self.player_instances)]
self.turn_number = self.turn_number + 1
if self.turn_player.get_hand_num() > 0:
break
loop_index = loop_index + 1
return self.turn_player
def add_finish_player(self, player_instance):
self.finish_player_list.append(player_instance)
def get_field_open_cards(self):
return self.domemo.open_cards
def get_field_closed_cards(self):
return self.domemo.closed_cards
def get_all_open_cards(self, player_instance):
number_list_of_card = [1, 2, 3, 4, 5, 6, 7]
closed_field_cards = self.get_field_closed_cards()
player_have_cards = player_instance.get_my_cards()
closed_card_collections = collections.Counter(closed_field_cards + player_have_cards)
for closed_card in closed_card_collections.keys():
number_list_of_card[closed_card - 1] = \
number_list_of_card[closed_card - 1] - closed_card_collections[closed_card]
open_card_list = []
for card, number in enumerate(number_list_of_card):
open_card_list = open_card_list + [card + 1] * number
return open_card_list
これはドメモの終了を判断するメソッドです。
ドメモの終了とは、すべてのプレイヤーが自身の札を当て終わった場合です。
そのためfinish_player_listの数とplayer_instancesの数を比較しています。
finish_player_listがplayer_instances未満の場合はTrueを、違う場合はFalseを返しています。
このメソッドはターンプレイヤーを返します。
行っていることは単純です。
while文はloop_index < len(player_instances)がTrueのとき、つまり最大でプレイヤー数だけ実行されます。
while文の内部は、ターンプレイヤーを決定するためのロジックです。
turn_numberをlen(player_instances)で割った余りをインデックスとして、player_instancesの要素を取り出しています。それがターンプレイヤーになります。
例を見てみましょう。
player_instances = [A, B, C, D]
len(player_instances) # A, B, C, Dの4つになりますね。
turn_number # 最初は0でこのメソッドが呼ばれるたびに1増えます。
turn_number % len(player_instances) # 0を4で割った余りなので0です。
player_instances[turn_number % len(player_instances)] # インデックスが0なのでAが取り出されます。
turn_number # 1になりました。
turn_number % len(player_instances) # 1を4で割った余りなので1です。
player_instances[turn_number % len(player_instances)] # インデックスが1なのでBが取り出されます。
上記を繰り返していくとターンプレイヤーが巡っていくのがわかるでしょう。
ターンプレイヤーの持っている札が0枚以上であればすぐにwhile文をbreakで抜けます。 0だった場合はloop_indexを1増やして、再度while文を実行します。
最終的にreturnでターンプレイヤーを返します。
これはプレイヤーインスタンスを受け取り、finish_player_listへ追加するメソッドです。
finish_player_listは終了したプレイヤーのリストで__init__で定義しておきます。
ドメモクラスにある属性open_cardsを返すメソッドです。__init__でドメモインスタンスを作成しておきます。
ドメモクラスにある属性closed_cardsを返すメソッドです。__init__でドメモインスタンスを作成しておきます。
これは、プレイヤーインスタンスを受け取り、そのプレイヤーから見えるすべての札を返すメソッドです。
特定のプレイヤーから見える札というのは、すべての札(number_list_of_cards)から場に伏せられている札(closed_field_cards)とプレイヤー自身が持っている札(player_have_cards)を除いたものです。
内部の詳しいロジックはぜひ考えてみてください。もっとわかりやすくできるかもしれないですし。
最後に__init__メソッドとimport文を追加しましょう。
import collections
import random
import copy
from domemo import Domemo
from player import Player
class Game:
def __init__(self, player_instances):
self.player_instances = player_instances
self.domemo = Domemo(self.player_instances)
self.domemo.give_card_for_player()
self.domemo.give_card_for_field()
self.turn_player = self.player_instances[0]
self.finish_player_list = []
self.turn_number = 0
self.is_card_in_the_hand = True
def play(self):
...
def judge_game_run(self):
...
def get_turn_player(self):
...
def add_finish_player(self, player_instance):
...
def get_field_open_cards(self):
...
def get_field_closed_cards(self):
...
def get_all_open_cards(self, player_instance):
...
domemo.pyへ実装していなかったメソッドも追加します。
class Domemo:
...
def add_open_card(self, card):
self.open_cards.append(card)
return None
実行してみます。
以下(>>>の行)をPyCharmのPythonコンソールへ入力します。
>>> from player import Player
>>> from game import Game
>>> game = Game([Player("A"), Player("B"), Player("C"), Player("D"), Player("E")])
>>> while True:
print(game.get_all_open_cards(game.turn_player))
game.play()
if not game.judge_game_run():
break
[1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7]
call num (1 ~ 7)>>> >? 4
[1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7]
call num (1 ~ 7)>>> >? 5
...
実行すると、call numと聞かれるので、数字を入れます。
これを繰り返していくといつかはドメモが終了しループから抜けられます。
これでシミュレーションができるようになりましたね。
これだと、人の手を使って数字を入力しなければなりません。10000回もやりたくないですよね。
次回はすべてをコンピュータで完結させましょう!