[Next] [Prev] [Back] [Home]

■ UpperHand Gameを作る


コンピュータに相手をさせる(その1)

それでは、いよいよコンピュータに相手をさせてみましょう。 コンピュータのプレーヤーを実現する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クラスに人間とコンピュータが交互に手を打つ機能を 追加します。
単純に考えると、ゲームの初期化が終わった後に、

  1. 人間の手番の場合、マウスの入力を待つ。
  2. コンピュータの手番の場合、selectMove()を呼び出す。
という処理をループさせればいいような気がしますが、 この方法はうまくいきません。 Javaには「マウスの入力を待つ」というメソッドが存在しないからです。
そこで、
  1. 人間の手番の場合、何もしない。
  2. コンピュータの手番の場合、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に対応していません。

Javaソースコード (Ver. 1.1a8)



[Next] [Prev] [Back] [Home]
Satoshi Kobayashi (koba@yk.rim.or.jp)