それでは、いよいよコンピュータに相手をさせてみましょう。
コンピュータのプレーヤーを実現するUpperHandPlayer
クラスを
新規に作成します。
といっても、今回は先読みまでは行いません。
あらかじめ決めておいた優先順位にしたがって着手するだけです。
//============================================================================= // class UpperHandPlayer // コンピュータのプレーヤーを実装するクラス。 //============================================================================= public class UpperHandPlayer extends Object { }
UpperHandPlayer
クラスには以下の2つのメソッドを定義します。
メソッド名 意味 UpperHandPlayer()
UpperHandPlayer
のインスタンスを生成し、初期化を行う。int selectMove(UpperHandGame game)
game
から着手する位置を選択し返す。
まず位置ごとの評価値を定義します。
その位置に玉を置かないと玉を置くことができるようにならない位置の個数を
その位置の評価値とします。
例えば位置2に玉を置かないと下の図の赤で示した8つの位置に
玉を置くことはできないので、位置2の評価値は8となります。
位置ごとの評価値を計算で求めることもできますが、 固定の値になるのでクラス変数として定義することにしました。
//----------------------------------------------------------------------------- // クラス変数定義 //----------------------------------------------------------------------------- // 位置ごとの評価値 private final static int valueOfPosition[] = { 4, 7, 8, 7, 4, 7, 13, 15, 13, 7, 8, 15, 18, 15, 8, 7, 13, 15, 13, 7, 4, 7, 8, 7, 4, 3, 5, 5, 3, 5, 9, 9, 5, 5, 9, 9, 5, 3, 5, 5, 3, 2, 3, 2, 3, 5, 3, 2, 3, 2, 1, 1, 1, 1, 0 };
次に、位置ごとの評価値と乱数を使って着手の優先順位を決定します。 優先順位はインスタンス変数に格納します。
//----------------------------------------------------------------------------- // インスタンス変数定義 //----------------------------------------------------------------------------- // 着手の優先順位 private int priorityOfMove[] = new int[valueOfPosition.length];
乱数を使って着手の優先順位をランダムに並びかえ、位置ごとの評価値でソートします。
この処理はコンストラクタで行っています。
着手の優先順位はUpperHandPlayer
オブジェクトごとに
異なりますが、
同一のUpperHandPlayer
オブジェクトは同じ局面では
常に同じ手を選択することになります。
//----------------------------------------------------------------------------- // public UpperHandPlayer() // UpperHandPlayerのインスタンスを生成し、初期化を行う。 //----------------------------------------------------------------------------- public UpperHandPlayer() { // 着手の優先順位をランダムに並び替える。 Random random = new Random(); boolean flag[] = new boolean[priorityOfMove.length]; int i = 0; while (i < priorityOfMove.length) { int n = (int)(random.nextFloat() * priorityOfMove.length); // 位置をランダムに選ぶ。 if (! flag[n]) { // 選んだ位置が使用済みでない場合 priorityOfMove[i] = n; // 選んだ位置をi番目の着手位置とする。 flag[n] = true; // 選んだ位置を使用済みとする。 i++; } } // 位置ごとの評価値の大きい順に着手の優先順位を並び替える。 for (i = 0; i < priorityOfMove.length; i++) { int p = i; for (int j = i + 1; j < priorityOfMove.length; j++) { if (valueOfPosition[priorityOfMove[p]] < valueOfPosition[priorityOfMove[j]]) { p = j; } } int q = priorityOfMove[i]; priorityOfMove[i] = priorityOfMove[p]; priorityOfMove[p] = q; } }
乱数の発生にはRandom
クラスを使っています。
nextFloat()
を呼び出すと0.0〜1.0の間の数がランダムに返されます。
次の一手を選ぶメソッドselectMove()
を実装します。
今回は優先順位にしたがって手を選ぶので非常に簡単です。
//----------------------------------------------------------------------------- // public int selectMove(UpperHandGame game) // gameから着手する位置を選択し返す。 //----------------------------------------------------------------------------- public int selectMove(UpperHandGame game) { int moves[] = game.moves(); // 着手可能な位置を取得する。 // 着手可能な位置の中で優先順位の一番高い位置を返す。 for (int i = 0; i < priorityOfMove.length; i++) { for (int j = 0; j < moves.length; j++) { if (priorityOfMove[i] == moves[j]) { return moves[j]; } } } return 0; }
では、プレーヤーを生成しましょう。
UpperHand
クラスに先手/後手のプレーヤーを格納するインスタンス変数
player
を追加します。
//----------------------------------------------------------------------------- // インスタンス変数定義 //----------------------------------------------------------------------------- ... // プレーヤー private UpperHandPlayer player[] = new UpperHandPlayer[2];
プレーヤーの初期化を行うメソッドinitPlayer()
を追加します。
人間の手番の場合、player
にはnull
を設定します。
//----------------------------------------------------------------------------- // private void initPlayer() // コンピュータのプレーヤーを生成し、コンピュータが後手、先手の順になるよ // うに手番を決める。 //----------------------------------------------------------------------------- private void initPlayer() { // プレーヤーを初期化する。 if (player[UpperHandGame.SECOND] != null) { // 後手がコンピュータの場合 // 先手をコンピュータ、後手を人間に設定する。 player[UpperHandGame.FIRST] = new UpperHandPlayer(); player[UpperHandGame.SECOND] = null; // プレーヤーの名前を設定する。 playerView[UpperHandGame.FIRST].playerName("Duke"); playerView[UpperHandGame.SECOND].playerName("You"); } else { // 先手を人間、後手をコンピュータに設定する。 player[UpperHandGame.FIRST] = null; player[UpperHandGame.SECOND] = new UpperHandPlayer(); // プレーヤーの名前を設定する。 playerView[UpperHandGame.FIRST].playerName("You"); playerView[UpperHandGame.SECOND].playerName("Duke"); } }
最後に、UpperHand
クラスに人間とコンピュータが交互に手を打つ機能を
追加します。
単純に考えると、ゲームの初期化が終わった後に、
selectMove()
を呼び出す。
selectMove()
を呼び出す。
nextMove()
を追加し、
ゲームの初期化が終わったときと、1手打たれたときに呼び出すことにしました。
//----------------------------------------------------------------------------- // private void nextMove() // 次の一手に進む。 // 次のプレーヤーがコンピュータのときは、次に一手をコンピュータに選択 // させ、着手する。次のプレーヤーが人間のときは何もせずにリターンし、 // マウスの入力を待つ。 //----------------------------------------------------------------------------- private void nextMove() { int p = game.nextPlayer(); if (player[p] != null) { // 次のプレーヤーがコンピュータのとき // 次の一手をコンピュータに選択させ、着手する。 makeMove(player[p].selectMove(game)); } }
//----------------------------------------------------------------------------- // public void startGame() // ゲームを開始する。 //----------------------------------------------------------------------------- public void startGame() { // プレーヤーを初期化する。 initPlayer(); // ゲームを初期化する。 game.init(); // 次の一手に進む。 nextMove(); } //----------------------------------------------------------------------------- // public void makeMove(int p) // 位置pに着手する。 //----------------------------------------------------------------------------- public void makeMove(int p) { // 着手する。 game.makeMove(p); if (game.isFinish()) { // ゲームが終わったとき return; // 何もしない。 } else { nextMove(); // 次の一手に進む。 } }
Javaソースコード (Ver. 1.1a8)