//============================================================================= // UpperHandPlayer.java // Copyright(c) 1998-1999 Satoshi Kobayashi, All rights reserved. //============================================================================= import java.util.*; //============================================================================= // class UpperHandPlayer // コンピュータのプレーヤーを実装するクラス。 //============================================================================= public class UpperHandPlayer extends Object { //----------------------------------------------------------------------------- // クラス変数定義 //----------------------------------------------------------------------------- // 位置ごとの評価値 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]; // 手番 private int player; // 強さ private int rank; // 0 Beginer // 1 Middle Class // 2 Junior Master //----------------------------------------------------------------------------- // コンストラクタ定義 //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // public UpperHandPlayer(int player, int rank) // UpperHandPlayerのインスタンスを生成し、初期化を行う。 //----------------------------------------------------------------------------- public UpperHandPlayer(int player, int rank) { // 手番を設定する。 this.player = player; // 強さを設定する。 this.rank = rank; // 着手の優先順位をランダムに並び替える。 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; } } //----------------------------------------------------------------------------- // メソッド定義 //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // public int selectMove(UpperHandGame game) // gameから着手する位置を選択し返す。 //----------------------------------------------------------------------------- public int selectMove(UpperHandGame game) { // 先読みの深さを得る。 int depth = depth(game); if (depth == 0) { // 先読みしない場合 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]; } } } } // 先読みする場合 float maxEval = 0f; // 最高の評価値 int p = -1; // 最高の評価の手 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]) { UpperHandGame newGame = (UpperHandGame)game.clone(); // 局面を複製する。 newGame.makeMove(moves[j]); // 着手してみる。 float eval = evaluate(newGame, depth - 1); // 着手後の局面を評価する。 if (eval > maxEval) { // 評価値が最も高い場合。 maxEval = eval; // 現在の評価値を最高の評価値とする。 p = moves[j]; // 現在の手を最高の評価の手とする。 } } } } return p; // 最高の評価の手を返す。 } //----------------------------------------------------------------------------- // private int depth(UpperHandGame game) // 先読みの深さを返す。 //----------------------------------------------------------------------------- private int depth(UpperHandGame game) { return rank; } //----------------------------------------------------------------------------- // private float evaluate(UpperHandGame game) // gameで与えられたゲームの状況の自分にとっての評価値を返す。 //----------------------------------------------------------------------------- private float evaluate(UpperHandGame game) { float eval = 0f; if (game.isFinish()) { // 終局した場合 eval = (player == UpperHandGame.FIRST) ? 27.5f - game.ball(UpperHandGame.FIRST) + game.ball(UpperHandGame.SECOND) : 27.5f - game.ball(UpperHandGame.SECOND) + game.ball(UpperHandGame.FIRST); } else { // 位置ごとの評価値の総和を評価値とする。 float evals[] = new float[55]; for (int p = 0; p < 55; p++) { switch (game.boardStatus(p)) { case UpperHandGame.FIRST: case UpperHandGame.SECOND: evals[p] = (player == game.boardStatus(p)) ? 1f : 0f; // 自分の玉の場合1点、相手の玉の場合0点 break; case UpperHandGame.NUTRAL: case UpperHandGame.MOVABLE: evals[p] = 0.5f; // 0.5点 break; case UpperHandGame.UNMOVABLE: // 1つ下の位置から自分の玉となる確率を計算する。 float e1 = evals[game.downPosition[p][0]]; float e2 = evals[game.downPosition[p][1]]; float e3 = evals[game.downPosition[p][2]]; float e4 = evals[game.downPosition[p][3]]; evals[p] = e1 * e2 * e3 * e4 + (1f - e1) * e2 * e3 * e4 + e1 * (1f - e2) * e3 * e4 + e1 * e2 * (1f - e3) * e4 + e1 * e2 * e3 * (1f - e4) + (1f - e1) * (1f - e2) * e3 * e4 * 0.5f + (1f - e1) * e2 * (1f - e3) * e4 * 0.5f + (1f - e1) * e2 * e3 * (1f - e4) * 0.5f + e1 * (1f - e2) * (1f - e3) * e4 * 0.5f + e1 * (1f - e2) * e3 * (1f - e4) * 0.5f + e1 * e2 * (1f - e3) * (1f - e4) * 0.5f; break; } eval = eval + evals[p]; } } return eval; } //----------------------------------------------------------------------------- // private float evaluate(UpperHandGame game, int depth) // gameで与えられたゲームをdepth手分先読みして評価値を返す。 //----------------------------------------------------------------------------- private float evaluate(UpperHandGame game, int depth) { if (depth == 0 || game.isFinish()) { // これ以上先読みする必要のない場合 return evaluate(game); // 局面の評価値を返す。 } // さらに先読みを行う場合 float maxEval = 0f; // 最大の評価値 float minEval = 55f; // 最小の評価値 int moves[] = game.moves(); // 着手可能な手を取得する。 boolean myMove = game.nextPlayer() == player; // 着手可能な手の中から優先順位の高い順に先読みを行う。 for (int i = 0; i < priorityOfMove.length; i++) { for (int j = 0; j < moves.length; j++) { if (priorityOfMove[i] == moves[j]) { UpperHandGame newGame = (UpperHandGame)game.clone(); // 局面を複製する。 newGame.makeMove(moves[j]); // 着手してみる。 float eval = evaluate(newGame, depth - 1); // 着手後の局面を評価する。 if (myMove) { // 自分の手番の場合 maxEval = eval > maxEval ? eval : maxEval; } else { // 相手の手番の場合 minEval = eval < minEval ? eval : minEval; } } } } return myMove ? maxEval : minEval; } }