Strategy
Strategyには「軍事的な戦略,策略,方略」などという意味がある.相手と戦うときにどのような戦略で進めるか,ある問題に対してどのような方法をとるか,それを決めたり,選択したりすることができるようにする.問題に対する解法を与えるときには,その方法はアルゴリズムが当てはまる.すなわち,ある問題に対するアルゴリズムを選択しやすくすることや,改良しやすくすることなどを考える.
取る戦略を選ぶ場面として,カードゲームでの戦略を考える.プレイヤーAとプレイヤーBの対戦カードゲームとし,各プレイヤーが1,2,3,4,5のカードを持ち,好きな順番に出していく.互いに出したカードの数字が大きい方が勝ちである.どのような出し方をすると勝ち数が多いか,カードの出し方の戦略を決める.以下に,5回出すうちの1回戦,2回戦までを示す.
この後の対戦も含めて,プレイヤーAが4,2,1,5,3,プレイヤーBが3,5,1,4,2の順でカードを出したとしたとき,プレイヤーAは3勝,1敗,1分で,プレイヤーBは1勝,3敗,1分である. このようなカードゲームを行うときに,勝つための戦略を決めて対戦するプログラムを作成して,Strategyパターンが活用できる様子を確認する.
クラス構造
Strategyパターンを利用した様子のUMLを以下に示す.
ソースコード
UML図に含まれるクラスのソースコードを確認する.
Strategyクラス
Strategyインターフェースを示す.出す番号を返すnextNumber,相手の出した番号を受け取って学習するlearningという,抽象メソッドを持つインターフェースとして定義する.
interface Strategy { public abstract Integer nextNumber(int i); public abstract void learning(int enemyNumber); }
Playerクラス
class Player { private String name;//プレイヤー名を記憶する private Strategy strategy;//戦略のインスタンス private int wincount;//勝った回数を記憶する private int losecount;//負けた回数を記憶する //プレイヤーをインスタンス化するときに,名前と戦略を決める Player(String name, Strategy strategy) { this.name = name; this.strategy = strategy; } public int nextNumber(int i) { return strategy.nextNumber(i); } public void learning(int enemy) { strategy.learning(enemy); } public void win() { wincount++; } public void lose() { losecount++; } public String toString() { return ( wincount + "勝" + losecount + "敗"); } public String getName() { return name; } }
プレイヤーをインスタンス化するときに,プレイヤー名と戦略のインスタンスを決める.フィールドに持つ戦略オブジェクトのメソッドを利用して,プレイヤーの数字の出し方,相手の数字を学習するメソッドの処理を定義する.さらに,プレイヤとして記憶しておくべき勝ち数,負け数,結果などを表示するメソッド,名前を返すメソッドを定義する.
Strategyを実装したクラス
Strategyインターフェイスを実装して,具体的な戦略を実装する.nextNumberメソッドを実装する.そのnextNumberで,どのような値を返すかによって,カードの勝負に勝つか負けるか決まる.そのため,カードの返し方である戦略をここで決める.
StrategyRandomクラス
戦略の1つ目として,相手が何を出したかに関わらず,ランダムに出す手を考える.カードをArrayListに順に格納し,それをシャッフルメソッドを使ってシャッフルする.learningで過去の出し手を得られるが,この戦略では活用しない.しかし,learningを実装することは必須なので,「何もしない」として実装する.
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class StrategyRandom implements Strategy { //数値を格納するリストをつくる private List<Integer> cards = new ArrayList<Integer>(); StrategyRandom() { //リストに数値を順に入れていく cards.add(1); cards.add(2); cards.add(3); cards.add(4); cards.add(5); //コレクションのシャッフル機能を使ってシャッフルする Collections.shuffle(cards);// } //リストから順番に数値を出す public Integer nextNumber(int i) { return cards.get(i); } //過去の出し手を利用しないので何も操作しない実装をする @Override public void learning(int enemy) { } }
StrategyStraight
Strategyを実装して,1から5まで順番に出す戦略をとるクラスを定義する.
class StrategyStraight implements Strategy { //配列の番号を出す順番として0から4の場所に格納されている1から5を順番に出す. private int[] cards = new int[5]; StrategyStraight() { cards[0] = 1; cards[1] = 2; cards[2] = 3; cards[3] = 4; cards[4] = 5; } @Override public Integer nextNumber(int i) { int value = cards[i]; return value; } @Override public void learning(int enemy) { } }
StrategyFollowクラス
次の戦略として,相手が前に出した数字と同じ数字を出す戦略を考える.相手が前に出した手を覚えておき,その数字のカードがあればそれを出す.もし,その数字のカードをすでに出している場合は,持っている数字で最小の数字のカードを出すことにする.
import java.util.ArrayList; import java.util.List; class StrategyFollow implements Strategy { //直前に相手が出した番号を記憶しておくフィールド private int preValue = 1; //手持ちの札を覚えておくリスト private List<Integer> cards = new ArrayList<Integer>(); StrategyFollow() { cards.add(1); cards.add(2); cards.add(3); cards.add(4); cards.add(5); } @Override public Integer nextNumber(int i) { int returnValue = 1; if(cards.indexOf(preValue) != -1)//出すカードがあるか調べる {//該当のカードが手持ちにあれば,そのカードを出す returnValue = preValue; } else {//該当するカードがない時には,0番目にあるカードを出す returnValue = cards.get(0); } int index = cards.indexOf(returnValue); cards.remove(index);//出したカードの数字を削除する return returnValue; } @Override public void learning(int enemyNumber) {//出したカードを記憶しておく preValue = enemyNumber; } }
Mainクラス
プレイヤーをインスタンス化して,カードゲームをするクラスを定義する.
public class Main { public static void main(String args[]) { //プレイヤーをインスタンス化 Player player1 = new Player("PC", new StrategyRandom()); Player player2 = new Player("P1",new StrategyFollow()); System.out.println(player1.getName() + ":" + player2.getName()); //5枚のカードで対戦する for(int i=0; i < 5; i++) { //カードの数字を出す int player1Num = player1.nextNumber(i); int player2Num = player2.nextNumber(i); //カードの数字を表示する System.out.println(player1Num + ":" +player2Num); //勝ち負けに応じて,成績を保存する if(player1Num> player2Num) { player1.win(); player2.lose(); } else if(player1Num < player2Num) { player1.lose(); player2.win(); } //対戦相手のカードの数字を学習する player1.learning(player2Num); player2.learning(player1Num); } //成績を表示する System.out.println(player1.getName() + ":" + player1); System.out.println(player2.getName() + ":" + player2); } }
実行結果
StrategyRandom vs StrategyFollow
PC:P1 4:1 5:4 1:5 3:2 2:3 PC:3勝2敗 P1:2勝3敗
StrategyRandom vs StrategyStraight
Player player1 = new Player("PC", new StrategyRandom()); Player player2 = new Player("P1",new StrategyStraight());
PC:P1 3:1 4:2 5:3 1:4 2:5 PC:3勝2敗 P1:2勝3敗
演習
追加
上記のカードゲームにおいて,自分で考案したStrategyの実装を作成せよ.ただし,相手の出した手を活用した戦略をとること.
開発
Strategyパターンを活用して,サイコロの出目が偶数(丁)か奇数(半)かを当てるゲームをするプログラムを,Strategyパターンを用いて構築せよ.
このとき,複数のプレイヤーが,それぞれの戦略で同時に賭けることができるようにするにはどうしたらよいだろうか.
すなわち,1回のサイコロふりの試行に対して,複数の客が丁半を賭けて勝ち負けを重ねられるようにする.
提出
内容:Strategyパターンの作成締切日時:2月15日19:00
提出先:WebClass
課題:Strategyパターンの演習(追加)