2010年11月21日日曜日

第10章 Strategyパターン : アルゴリズムをごっそり切り替える



Strategyパターンは、『アルゴリズム』を入れ替えるパターン。UMLの雰囲気なんかは目新しくないが、実用性は高そう。パーサとかスクレイピングとかね。
例ではStrategyがもろ『戦略』なんだけど、これが却って混乱させるというか。練習問題のソートのやつのほうが応用という意味では適していそう。

コード書いてて、自分でじゃんけんアルゴリズムを考えたくなるのは性なんでしょうかね。でも思いつかなかったので書いてないです。

何となく、囚人のジレンマに適用できるくらいまで抽象化してみようかと考えています。


ソースコード

strategy.py
# -*- coding: utf8 -*-

class __Strategy: #interface
    def __init__(self): raise NotImplementedError
    def nextHand(self): raise NotImplementedError
    def study(self, win): raise NotImplementedError

import hand
import random as random1
class WinningStrategy(__Strategy):
    def __init__(self, seed):
        random1.seed(seed)
        self.won = False
        self.previousHand = None

    def nextHand(self):
        if not self.won:
            self.previousHand = hand.getHand(random1.randint(0,2))
        return self.previousHand

    def study(self, win):
        self.won = win


import random as random2
class ProbStrategy(__Strategy):
    def __init__(self, seed):
        random2.seed(seed)
        self.history = [[1,1,1] for i in xrange(3)]
        self.prevHandValue = 0
        self.currentHandValue = 0

    def nextHand(self):
        def listConcat(listOfLists): return reduce(lambda a,b: a+b, listOfLists,[])
        self.prevHandValue = self.currentHandValue
        #ex. [3,2,5] -> [[0,0,0],[1,1],[2,2,2,2,2]] -> [0,0,0,1,1,2,2,2,2,2] -> choice one
        self.currentHandValue = random2.choice(listConcat([[i for dummy in xrange(x)] for i,x in enumerate(self.history[self.currentHandValue])]))
        return hand.getHand(self.currentHandValue)

    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

player.py
# -*- coding: utf8 -*-

class Player:
    def __init__(self, name, strategy):
        self.name = name
        self.strategy = strategy
        self.gamecount = self.wincount = self.losecount = 0

        self.nextHand = 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 __str__(self):
        return '[%s: %d games, %d win, %d lose]' % (self.name, self.gamecount, self.wincount, self.losecount,)

main.py
#!/usr/bin/env python
# -*- coding: utf8 -*-

from player import Player
from strategy import WinningStrategy, ProbStrategy
import sys

def main():
    if not len(sys.argv) == 3:
        print "Usage: python main.py randomseed1 randomseed2"
        print "Example: python main.py 314 15"
        sys.exit(0)

    seeds = map(long, sys.argv[1:3])

    player1 = Player("Taro", WinningStrategy(seeds[0]))
    player2 = Player("Hana", ProbStrategy(seeds[1]))

    for i in xrange(10000):
        nextHand1 = player1.nextHand()
        nextHand2 = player2.nextHand()
        if nextHand1.isStrongerThan(nextHand2):
            print "Winner:", player1
            player1.win()
            player2.lose()
        elif nextHand2.isStrongerThan(nextHand1):
            print "Winner:", player2
            player1.lose()
            player2.win()
        else:
            print "Even..."
            player1.even()
            player2.even()

    print "Total result:"
    print player1
    print player2


if __name__=='__main__': main()

0 件のコメント:

コメントを投稿