J'ai pratiqué les modèles de conception afin de pouvoir écrire du code conscient du design. D'autres modèles de conception seront publiés fréquemment.
L'objectif principal est de comprendre quand, quoi et comment utiliser les modèles de conception. (Je suis nouveau en Java ou dans un langage à typage statique, et je n'ai pas une longue histoire de python, donc je pense qu'il y a des choses qui ne ressemblent pas à Pythonista. Si vous avez des suggestions, apprenez-moi s'il vous plaît.)
Cette fois, la stratégie de modèle concernant le comportement.
La partie qui implémente l'algorithme peut être échangée dans un aperçu. Un modèle qui permet de changer facilement d'algorithme et de résoudre le même problème différemment.
C'est parce que le modèle de stratégie sépare consciemment la partie algorithme des autres parties. Et seule l'interface (API) avec l'algorithme est spécifiée. Ensuite, l'algorithme est utilisé par délégation du programme. Vous pouvez facilement basculer entre les algorithmes en utilisant les liens lâches de la délégation.
Le modèle Stratégie peut être utilisé lorsque de nombreux comportements apparaissent sous la forme de plusieurs instructions conditionnelles.
Le programme d'exemple créé ici est pour exécuter "Janken" sur un ordinateur. J'ai pensé à deux méthodes comme la «stratégie» de Janken. L'un est "Si vous gagnez, vous ferez le même coup la prochaine fois" (WinningStrategy), et l'autre est "Calculez de manière probabiliste le coup suivant à partir du coup précédent" (ProbStrategy).
hand.py
class Hand():
HANDVALUE_ROCK = 0
HANDVALUE_SCISSORS = 1
HANDVALUE_PAPER = 2
NAMES = ['Goo', 'Choki', 'Par']
HANDS = [HANDVALUE_ROCK,
HANDVALUE_SCISSORS,
HANDVALUE_PAPER]
def __init__(self, handvalue):
self.__handvalue = handvalue
def get_hand(self, handvalue):
return self.HANDS[self.__handvalue]
def is_stronger_than(self, h):
return self.__fight(h) == 1
def is_weaker_than(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 to_string(self):
return self.NAMES[self.__handvalue]
La classe Hand est une classe qui représente la «main» de Janken. À l'intérieur de la classe, goo vaut 0, choki vaut 1 et le par est 2. Enregistrez-le en tant que champ de valeur manuelle.
Seules trois instances de la classe Hand peuvent être créées. Au départ, trois instances sont créées et stockées dans le tableau HANDS.
Vous pouvez obtenir une instance en utilisant la méthode de classe get_hand. Si vous donnez une valeur de main comme argument, l'instance sera la valeur de retour.
is_stronger_than et is_weaker_than comparent la force de la main. À utiliser lorsque vous avez deux mains, hand1 et hand2.
À l'intérieur de cette classe, c'est une méthode appelée combat qui détermine en fait la force de la main. La valeur de la main est utilisée pour juger de la force. La (self .__ handvalue + 1)% 3 == h .__ handvalue
utilisée ici est la valeur de la main de self plus 1 est la valeur de la main de h (si self est goo, h est choki, self Si est choki, h est par, et si self est par, h est goo), alors self est plus fort que h. La raison d'utiliser l'opérateur% pour prendre le reste de 3 est que nous voulons qu'il soit goo (0) lorsque 1 est ajouté à par (2).
strategy.py
from abc import ABCMeta, abstractmethod
class Strategy(metaclass=ABCMeta):
@abstractmethod
def next_hand():
pass
@abstractmethod
def study(win):
pass
L'interface de stratégie est une collection de méthodes abstraites pour la «stratégie» de Janken.
next_hand est une méthode pour "obtenir le coup suivant". Lorsque cette méthode est appelée, la classe qui implémente l'interface de stratégie décide du "prochain mouvement".
l'étude est une méthode pour apprendre "si oui ou non vous avez gagné par la main que vous venez de lancer". Si l'appel de la méthode next_hand précédent l'emporte, appelez-le comme study (True). Si vous perdez, appelez cela étude (Faux). Par conséquent, la classe qui implémente l'interface Strategy change son état interne et l'utilise comme matériau pour déterminer la valeur de retour de la méthode next_hand à partir de la prochaine fois.
winning_strategy.py
import random
from hand import Hand
from strategy import Strategy
class WinningStrategy(Strategy):
__won = False
__prev_hand = 0
def __init__(self, seed):
self.__rand = seed
def next_hand(self):
if not(self.__won):
self.__prev_hand = Hand(self.__rand).get_hand(random.randint(0, 3))
return self.__prev_hand
def study(self, win):
self.__won = win
La classe WinningStrategy est l'une des classes qui implémente l'interface Strategy. Implémenter l'interface de stratégie signifie implémenter deux méthodes, next_hand et study.
Dans cette classe, si vous gagnez la partie précédente, vous adopterez la même stratégie la prochaine fois (goo pour goo, par pour par). Si vous perdez la partie précédente, le prochain coup sera décidé en utilisant des nombres aléatoires.
Le champ rand contient les nombres aléatoires que cette classe utilise lorsqu'elle en a besoin.
Le champ gagné contient le résultat du match précédent. Si vous gagnez, ce sera Vrai, si vous perdez, ce sera Faux.
Le champ prev_hand contient la main que vous avez lancée dans le jeu précédent.
prob_strategy.py
import random
from hand import Hand
from strategy import Strategy
class ProbStrategy(Strategy):
__prev_hand_value = 0
__current_hand_value = 0
__history = [[1, 1, 1], [1, 1, 1], [1, 1, 1]]
def __init__(self, seed):
self.__rand = seed
def next_hand(self):
bet = random.randint(0, self.__get_sum(self.__current_hand_value))
hand_value = 0
if bet < self.__history[self.__current_hand_value][0]:
hand_value = 0
elif bet < self.__history[self.__current_hand_value][0] + \
self.__history[self.__current_hand_value][1]:
hand_value = 1
else:
hand_value = 2
self.__prev_hand_value = self.__current_hand_value
self.__current_hand_value = hand_value
return Hand(hand_value).get_hand(hand_value)
def __get_sum(self, hv):
total = 0
for i in range(0, 3):
total += self.__history[hv][i]
return total
def study(self, win):
if win:
self.__history[self.__prev_hand_value][self.__current_hand_value] \
+= 1
else:
self.__history[self.__prev_hand_value][(self.__current_hand_value + 1) % 3] \
+= 1
self.__history[self.__prev_hand_value][(self.__current_hand_value + 2) % 3] \
+= 1
La classe ProbStrategy est une autre «stratégie» concrète. Le coup suivant est toujours décidé par un nombre aléatoire, mais l'historique des victoires et des pertes passées est utilisé pour changer la probabilité de chaque coup.
Le champ historique est un tableau de calcul de probabilité qui reflète les victoires et les pertes passées. L'histoire est un tableau à deux dimensions d'entiers, et les indices de chaque dimension ont les significations suivantes.
histoire [Dernière main] [Cette fois] Plus la valeur de cette formule est élevée, plus le taux de victoire passé est élevé.
Si vous écrivez en détail. historique [0] [0] Goo, le nombre de victoires passées lorsque vous le sortez avec Goo histoire [0] [1] Goo, Choki et le nombre de victoires passées lorsque vous sortez historique [0] [2] Nombre de victoires passées quand Goo, Par et moi
Supposons que vous ayez mis une pâte la dernière fois. À ce moment-là, ce que je vais donner ensuite est calculé avec la probabilité à partir des valeurs ci-dessus de histroy [0] [0], history [0] [1], hisstroy [0] [2]
. En bref, ajoutez les valeurs de ces trois expressions (méthode get_sum), calculez le nombre à partir de 0, puis décidez du coup suivant en fonction de celui-ci (méthode next_hand).
Par exemple La valeur de l'historique [0] [0] est 3 La valeur de l'historique [0] [1] est 5 La valeur de l'historique [0] [2] est 7 dans le cas de. À ce moment, le rapport entre goo, choki et par est réglé à 3: 5: 7 et le prochain mouvement est décidé. Obtenir une valeur aléatoire entre 0 et moins de 15 (15 est une valeur de 3 + 5 + 7), Goo si 0 ou plus et moins de 3 Choki si 3 ou plus et moins de 8 Par si 8 ou plus et moins de 15 ça ira.
La méthode d'étude met à jour le contenu du champ d'historique en fonction du résultat de la main renvoyée par la méthode next_hand.
player.py
class Player():
__wincount = 0
__losecount = 0
__gamecount = 0
def __init__(self, name, strategy):
self.__name = name
self.__strategy = strategy
def next_hand(self):
return self.__strategy.next_hand()
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 to_stirng(self):
return '[{0}: {1} games {2} win {3} lose]'.format(self.__name,
self.__gamecount,
self.__wincount,
self.__losecount)
La classe Player est une classe qui représente la personne qui lit les fichiers indésirables. La classe Player reçoit un "nom" et une "stratégie" pour créer une instance. La méthode next_hand sert à obtenir le coup suivant, mais c'est votre «stratégie» qui détermine en fait le coup suivant. La valeur de retour de la méthode next_hand de la stratégie sera la valeur de retour de la méthode next_hand du joueur. La méthode next_hand "délègue" ce qu'elle doit faire à Strategy.
La classe Player appelle la méthode d'étude à travers le champ startegy afin d'utiliser les résultats des jeux gagnants (gagner), perdants (perdants) et tirés (pair) dans le prochain match. Utilisez la méthode d'étude pour modifier l'état interne de la stratégie. wincount, losecount et geamecount enregistrent le nombre de victoires d'un joueur.
main.py
import random
import sys
from winning_strategy import WinningStrategy
from prob_strategy import ProbStrategy
from player import Player
from hand import Hand
def main():
try:
if int(sys.argv[1]) >= 3:
seed1 = random.randint(0, 2)
else:
seed1 = int(sys.argv[1])
if int(sys.argv[2]) >= 3:
seed2 = random.randint(0, 2)
else:
seed2 = int(sys.argv[2])
player1 = Player('Taro', WinningStrategy(seed1))
player2 = Player('Hana', ProbStrategy(seed2))
for i in range(0, 10): # 10000
next_hand1 = Hand(player1.next_hand())
next_hand2 = Hand(player2.next_hand())
if next_hand1.is_stronger_than(next_hand2):
print('Winner : {0}'.format(player1.to_stirng()))
player1.win()
player2.lose()
elif next_hand2.is_stronger_than(next_hand1):
print('Winner : {0}'.format(player2.to_stirng()))
player1.lose()
player2.win()
else:
print('Even ...')
player1.even()
player2.even()
print('Total result:')
print(player1.to_stirng())
print(player2.to_stirng())
except IndexError:
print('Check args size, does not work')
print('usage: python main random_seed1 random_seed2')
print('Example: python main.py 314 15')
if __name__ == "__main__":
main()
Résultat d'exécution
python main.py 21 3
Winner : [Hana: 0 games 0 win 0 lose]
Even ...
Winner : [Hana: 2 games 1 win 0 lose]
Winner : [Taro: 3 games 0 win 2 lose]
Even ...
Winner : [Taro: 5 games 1 win 2 lose]
Even ...
Winner : [Hana: 7 games 2 win 2 lose]
Winner : [Taro: 8 games 2 win 3 lose]
Winner : [Hana: 9 games 3 win 3 lose]
Total result:
[Taro: 10 games 3 win 4 lose]
[Hana: 10 games 4 win 3 lose]
Le modèle de stratégie sépare consciemment la partie de l'algorithme des autres parties. Comme il utilise une connexion lâche appelée délégation, il est facile de changer d'algorithme si vous ne modifiez pas la partie interface.
Recommended Posts