Python デザインパターン サンプルコード Strategy
結城 浩「Java言語で学ぶデザインパターン入門」をPython化
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-18)Python3.11で動作確認済み
【重要な注意】本ソースコードファイルを起動するには第2引数が必要です。ターミナルからPythonを起動するとき,普通はプロンプト「'>'」の後に次のようにタイプします。
>python Strategy.py 314 15(ケースバイケースで)
ターミナルによっては'python'は'python3'になります。ソースコードファイル(スクリプトファイル)名称が第1引数です。第2引数は本プログラムに読み込むオプションです。
「PyCharm」では,次の枠に第2引数を設定します。
メニュー→run→Edit Configurations...→左下矢印と右上矢印の枠
ファイル名→Modify Run Configuration...→左下矢印と右上矢印の枠
画面によっては,Interpreter options: という枠です(左下矢印と右上矢印の枠)
引数が必要なところに「sys.argv[1]」を入れます。「Jupyter Notebook」では,自己テストに「sys.argv[1]=???」とする場合があります。そのときは「PyCharm」では,この行は必要ありません。「Jupyter Notebook」にこの行を入れるのは,ハードコードと言い,固有名詞や固有な数値をコマンドラインに埋め込むことは本来避けるべきですが,この場合はやむを得ないとしましょう。ちなみにこのコードでは第1引数のような扱いになっていますが,ターミナルのコマンドラインでは第2引数なので混乱しないように。
「sys.argv[1]」の前かまたは冒頭に「import sys」を忘れないように。
.pyではターミナルから実行されたとき自己テストが実行され,他のファイルから呼ばれたときは自己テストは無視されることも忘れないように。「Jupyter Notebook」では呼ばれる側を上側に置き,下側に参照されるようにします(本記事に関係ないかも)。
Hand Player Strategy (get Hand, (nextHand, (nextHand, study) isStrongerThan, win ↑ ↑ isWeakerThan, loose WinningStrategy ProbStrategy fight, even (nextHand, study) (nextHand, getSum, study) toString) toString)サンプルはジャンケンの試合です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import random from abc import ABCMeta, abstractmethod class Hand(): HANDVALUE_GUU = 0 HANDVALUE_CHO = 1 HANDVALUE_PAA = 2 hand = [HANDVALUE_GUU, HANDVALUE_CHO, HANDVALUE_PAA] name = ["グー", "チョキ", "パー"] def __init__(self, handvalue): self.handvalue = handvalue def getHand(self, handvalue): return self.hand[handvalue] def isStrongerThan(self, h): return self.fight(h) == 1 def isWeakerThan(self, h): return self.fight(h) == -1 def fight(self, h): if self.handvalue == h.handvalue: return 0 elif (self.handvalue + 1) % 3 == h.handvalue: return 1 else: return -1 def toString(self): return self.name[self.handvalue] class Strategy(metaclass=ABCMeta): @abstractmethod def nextHand(): pass @abstractmethod def study(win): pass class WinningStrategy(Strategy): won = False prevHand = 0 def __init__(self, _seed): random.seed(_seed) def nextHand(self): if not(self.won): self.prevHand = Hand(3).getHand(random.randint(0, 2)) return self.prevHand def study(self, win): self.won = win class ProbStrategy(Strategy): prevHandValue = 0 currentHandValue = 0 history = [[1, 1, 1], [1, 1, 1], [1, 1, 1]] def __init__(self, _seed): random.seed(_seed) def nextHand(self): bet = random.randint(0, self.getSum(self.currentHandValue)) handvalue = 0 if bet < self.history[self.currentHandValue][0]: handvalue = 0 elif bet < self.history[self.currentHandValue][0] + \ self.history[self.currentHandValue][1]: handvalue = 1 else: handvalue = 2 self.prevHandValue = self.currentHandValue self.currentHandValue = handvalue return Hand(3).getHand(handvalue) def getSum(self, hv): _sum = 0 for i in range(3): _sum += self.history[hv][i] return _sum def study(self, win): if win: self.history[self.prevHandValue][self.currentHandValue] += 1 else: self.history[ self.prevHandValue][(self.currentHandValue + 1) % 3] += 1 self.history[ self.prevHandValue][(self.currentHandValue + 2) % 3] += 1 class Player(): wincount = 0 losecount = 0 gamecount = 0 def __init__(self, name, strategy): self.name = name self.strategy = strategy def nextHand(self): return self.strategy.nextHand() def win(self): self.strategy.study(True) self.wincount += 1 self.gamecount += 1 def lose(self): self.strategy.study(False) self.losecount += 1 self.gamecount += 1 def even(self): self.gamecount += 1 def toStirng(self): return "[{0}: {1} games {2} win {3} lose]" \ .format(self.name, self.gamecount,self.wincount, self.losecount) def main(): if len(sys.argv) != 3: sys.stdout.write( "Usage: python Strategy.py randomseed1 randomseed2\n") sys.stdout.write("Example: python Strategy.py 314 15") sys.exit() seed1 = int(sys.argv[1]) seed2 = int(sys.argv[2]) player1 = Player("Taro", WinningStrategy(seed1)) player2 = Player("Hana", ProbStrategy(seed2)) for i in range(10): # turn, ex;10000 x = player1.nextHand() #x = 2 nextHand1 = Hand(x) #print(x) y = player2.nextHand() nextHand2 = Hand(y) #print(y) if nextHand1.isStrongerThan(nextHand2): sys.stdout.write("Winner : {0}\n".format(player1.toStirng())) player1.win() player2.lose() elif nextHand2.isStrongerThan(nextHand1): sys.stdout.write("Winner : {0}\n".format(player2.toStirng())) player1.lose() player2.win() else: sys.stdout.write("Even ...\n") player1.even() player2.even() sys.stdout.write("Total result:\n") sys.stdout.write(player1.toStirng()) sys.stdout.write("\n") sys.stdout.write(player2.toStirng()) if __name__ == "__main__": main() """ コマンドライン : 314 15 Even ... Winner : [Taro: 1 games 0 win 0 lose] Winner : [Hana: 2 games 0 win 1 lose] Even ... Winner : [Hana: 4 games 1 win 1 lose] Winner : [Hana: 5 games 2 win 1 lose] Even ... Even ... Winner : [Taro: 8 games 1 win 3 lose] Even ... Total result: [Taro: 10 games 2 win 3 lose] [Hana: 10 games 3 win 2 lose] """