Strategyパターン
アルゴリズムをごっそり切り替える
Strategy(戦略):Strategy
インタフェースを規定
ConcreteStrategy(具体的戦略):WinningStrategy、ProbStrategy
Context(文脈):Player
Strategyを利用。ConcreteStrategyのインスタンスを保持。
Hand.php
class Hand { const GUU = 0; const CHO = 1; const PAA = 2; static $hand = []; static private $name = ["グー","チョキ","パー"]; private $handvalue; static function initialize() { self::$hand = [new Hand(self::GUU), new Hand(self::CHO), new Hand(self::PAA)]; } function __construct($handvalue) { $this->handvalue = $handvalue; } static function getHand($handvalue) { return self::$hand[$handvalue]; } function isStrongerThan(Hand $h) { return $this->fight($h) == 1; } function isWeekerThan(Hand $h) { return $this->fight($h) == -1; } private function fight(Hand $h) { if ($this == $h) return 0; if (($this->handvalue + 1) % 3 == $h->handvalue) return 1; return -1; } function toString() { return self::$name[$this->handvalue]; } }
Strategy.php
interface Strategy { function nextHand(): Hand; function study(bool $win); }
WinningStrategy.php
// 勝ったら同じ手を出す戦略 class WinningStrategy implements Strategy { private $won = false; private $prevHand; function nextHand(): Hand { if (!$this->won) $this->prevHand = Hand::getHand(rand(0, 2)); return $this->prevHand; } function study(bool $win) { $this->won = $win; } }
ProbStrategy.php
// 勝敗履歴から確率を計算する戦略 class ProbStrategy implements Strategy { private $prevHandValue = 0; private $currentHandValue = 0; private $history = [[1,1,1], [1,1,1], [1,1,1]]; function nextHand(): Hand { $bet = rand(0, $this->getSum($this->currentHandValue)); $handvalue = 0; if ($bet < $this->history[$this->currentHandValue][0]) { $handvalue = 0; } else if ($bet < $this->history[$this->currentHandValue][0] + $this->history[$this->currentHandValue][1]) { $handvalue = 1; } else { $handvalue = 2; } $this->prevHandValue = $this->currentHandValue; $this->currentHandValue = $handvalue; return Hand::getHand($handvalue); } private function getSum($hv) { $sum = 0; for ($i=0;$i<3;$i++) { $sum += $this->history[$hv][$i]; } return $sum; } function study($win) { if ($win) { $this->history[$this->prevHandValue][$this->currentHandValue]++; } else { $this->history[$this->prevHandValue][($this->currentHandValue + 1) % 3]++; $this->history[$this->prevHandValue][($this->currentHandValue + 2) % 3]++; } } }
Player.php
class Player { private $name; private $strategy; private $wincount = 0; private $losecount = 0; private $gamecount = 0; function __construct($name, $strategy) { $this->name = $name; $this->strategy = $strategy; } function nextHand() { return $this->strategy->nextHand(); } function win() { $this->strategy->study(true); $this->wincount++; $this->gamecount++; } function lose() { $this->strategy->study(false); $this->losecount++; $this->gamecount++; } function even() { $this->gamecount++; } function toString() { return "[" . $this->name . ":" . $this->gamecount . " games, " . $this->wincount . " win, " . $this->losecount . " lose]"; } }
Main.php
require "../autoload.php"; Hand::initialize(); $player1 = new Player("Taro", new WinningStrategy()); $player2 = new Player("Jiro", new ProbStrategy()); for ($i=0;$i<10;$i++) { $nextHand1 = $player1->nextHand(); $nextHand2 = $player2->nextHand(); if ($nextHand1->isStrongerThan($nextHand2)) { echo "Winner:" . $player1->toString() . "\n"; $player1->win(); $player2->lose(); } else if ($nextHand2->isStrongerThan($nextHand1)) { echo "Winner:" . $player2->toString() . "\n"; $player2->win(); $player1->lose(); } else { echo "Even...\n"; $player1->even(); $player2->even(); } } echo "Total result:\n"; echo $player1->toString() . "\n"; echo $player2->toString() . "\n";