2006年02月15日

Strategy -アルゴリズムをごっそり切り替える

  
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。


Handクラス
using System;

namespace Gushwell.Design.Patterns {

public class Hand {
public const int HANDVALUE_GUU = 0; // グーを表す値
public const int HANDVALUE_CHO = 1; // チョキを表す値
public const int HANDVALUE_PAA = 2; // パーを表す値

public static readonly Hand[] hand = new Hand[] {
new Hand(HANDVALUE_GUU),
new Hand(HANDVALUE_CHO),
new Hand(HANDVALUE_PAA)
};

private static readonly string[] name = new string[]
{ "グー", "チョキ", "パー" };
private int handvalue; // じゃんけんの手の値

private Hand(int handvalue) {
this.handvalue = handvalue;
}

// 値からインスタンスを得る
public static Hand GetHand(int handvalue) {
return hand[handvalue];
}

// thisがhより強いときtrue
public virtual bool IsStrongerThan(Hand h) {
return Fight(h) == 1;
}

// thisがhより弱いときtrue
public virtual bool IsWeakerThan(Hand h) {
return Fight(h) == -1;
}

// 引き分けは0, thisの勝ちなら1, hの勝ちなら-1
private int Fight(Hand h) {
if (this == h) {
return 0;
} else if ((this.handvalue + 1) % 3 == h.handvalue) {
return 1;
} else {
return -1;
}
}

// 文字列表現へ変換
public override string ToString() {
return name[handvalue];
}
}
}


IStrategyインターフェース
using System;

namespace Gushwell.Design.Patterns {

public interface IStrategy {
Hand NextHand();
void Study(bool win);
}
}


WinningStrategy
using System;

namespace Gushwell.Design.Patterns {

public class WinningStrategy : IStrategy {
private Random random;
private bool won = false;
private Hand prevHand;

public WinningStrategy(int seed) {
random = new Random((int)seed);
}

public virtual Hand NextHand() {
if (!won) {
prevHand = Hand.GetHand(random.Next(3));
}
return prevHand;
}

public virtual void Study(bool win) {
won = win;
}
}
}


ProbStrategyクラス
using System;

namespace Gushwell.Design.Patterns {
public class ProbStrategy : IStrategy {
private Random random;
private int prevHandValue = 0;
private int currentHandValue = 0;
private int[][] history = new int[][] { new int[] { 1, 1, 1 },
new int[] { 1, 1, 1 },
new int[] { 1, 1, 1 } };

public ProbStrategy(int seed) {
random = new Random((int)seed);
}

public virtual Hand NextHand() {
int bet = random.Next(GetSum(currentHandValue));
int handvalue = 0;
if (bet < history[currentHandValue][0]) {
handvalue = 0;
} else if (bet < history[currentHandValue][0] +
history[currentHandValue][1]) {
handvalue = 1;
} else {
handvalue = 2;
}
prevHandValue = currentHandValue;
currentHandValue = handvalue;
return Hand.GetHand(handvalue);
}

private int GetSum(int hv) {
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += history[hv][i];
}
return sum;
}

public virtual void Study(bool win) {
if (win) {
history[prevHandValue][currentHandValue]++;
} else {
history[prevHandValue][(currentHandValue + 1) % 3]++;
history[prevHandValue][(currentHandValue + 2) % 3]++;
}
}
}
}


Playerクラス
using System;

namespace Gushwell.Design.Patterns {

public class Player {
private string name;
private IStrategy strategy;
private int wincount;
private int losecount;
private int gamecount;

public Player(string name, IStrategy strategy) {
// 名前と戦略を授けられる
this.name = name;
this.strategy = strategy;
}

public virtual Hand NextHand() {
// 戦略におうかがいを立てる
return strategy.NextHand();
}

public virtual void Win() {
// 勝った
strategy.Study(true);
wincount++;
gamecount++;
}

public virtual void Lose() {
// 負けた
strategy.Study(false);
losecount++;
gamecount++;
}

public virtual void Even() {
// 引き分け
gamecount++;
}

public override string ToString() {
return "[" + name + ":" + gamecount + " games, " +
wincount + " win, " +
losecount + " lose" + "]";
}
}
}


Programクラス
using System;

namespace Gushwell.Design.Patterns {

public class Program {
[STAThread]
public static void Main(string[] args) {
if (args.Length != 2) {
Console.WriteLine("Usage: Program randomseed1 randomseed2");
Console.WriteLine("Example: Program 314 15");
return;
}

int seed1 = int.Parse(args[0]);
int seed2 = int.Parse(args[1]);
Player player1 = new Player("Taro", new WinningStrategy(seed1));
Player player2 = new Player("Hana", new ProbStrategy(seed2));
for (int i = 0; i < 10000; i++) {
Hand nextHand1 = player1.NextHand();
Hand nextHand2 = player2.NextHand();
if (nextHand1.IsStrongerThan(nextHand2)) {
Console.WriteLine("Winner:" + player1);
player1.Win();
player2.Lose();
} else if (nextHand2.IsStrongerThan(nextHand1)) {
Console.WriteLine("Winner:" + player2);
player1.Lose();
player2.Win();
} else {
Console.WriteLine("Even...");
player1.Even();
player2.Even();
}
}

Console.WriteLine("Total result:");
Console.WriteLine(player1.ToString());
Console.WriteLine(player2.ToString());
}
}
}


 

この記事へのトラックバックURL